これはそのままでは実装されていません。 JsonSerializerInternalWriter.SerializeList()
のソースを調べると、いくつかのフィルターに基づいてコレクション項目をスキップするロジックがないことがわかります。
ただし、Json.NETにはrobust exception handlingがあります。例外がスローされた場合は、[OnError]
コールバックでキャッチされ、飲み込まオブジェクトシリアル化するために始めたときに:配列のエントリを書いている場合
- を、配列のエントリがは(あなたの希望の動作)をスキップされます。
- ルートオブジェクトを書き込んだ場合、例外がキャッチされません(おそらくバグですか?)
- それ以外の場合は
null
が書き込まれます。
このように、あなたの所望の機能を達成するための一つの可能性は、人工的なコールバックからの例外をスローすることですが、その後、ハンドラで例外をキャッチして飲み込むがJsonContract.OnErrorCallbacks
に追加し、カスタム契約リゾルバによってJsonContract.OnSerializingCallbacks
に加えます。すでに行っているようにプロパティ値のフィルタリングと組み合わせると、この方法は、ルートオブジェクトである場合でも、辞書、動的オブジェクト、または多次元配列に含まれている場合でも、シークレットオブジェクトを直列化できないことを保証するという利点があります。このアプローチはPreserveReferencesHandling.Arrays
を妨げません。次のようにこれを行い
一つの契約リゾルバは次のとおりです。
sealed class JsonSkipObjectException : JsonException
{
}
public class ShouldSerializeContractResolver : DefaultContractResolver
{
readonly Predicate<object> shouldSerialize;
readonly SerializationCallback serializationCallback;
readonly SerializationErrorCallback onErrorCallback;
public ShouldSerializeContractResolver(Predicate<object> shouldSerialize)
: base()
{
this.shouldSerialize = shouldSerialize;
this.serializationCallback = (o, context) =>
{
if (shouldSerialize != null && !this.shouldSerialize(o))
throw new JsonSkipObjectException();
};
this.onErrorCallback = (o, context, errorContext) =>
{
if (errorContext.Error is JsonSkipObjectException)
{
errorContext.Handled = true;
}
};
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (shouldSerialize != null)
{
if (property.Readable)
{
var oldShouldSerialize = property.ShouldSerialize;
property.ShouldSerialize = (o) =>
{
if (oldShouldSerialize != null && !oldShouldSerialize(o))
return false;
var value = property.ValueProvider.GetValue(o);
if (!this.shouldSerialize(value))
return false;
return true;
};
}
}
return property;
}
protected override JsonContract CreateContract(Type objectType)
{
var contract = base.CreateContract(objectType);
contract.OnSerializingCallbacks.Add(serializationCallback);
contract.OnErrorCallbacks.Add(onErrorCallback);
return contract;
}
}
そして、一つの可能な用途は次のようになります。
public interface IConditionalSerialization
{
bool ShouldSerialize();
}
public class ConditionalSerializationObject : IConditionalSerialization
{
public bool IsSecret { get; set; }
public string SecretProperty { get { return "should not see me"; } }
// Ensure "normal" conditional property serialization is not broken
public bool ShouldSerializeSecretProperty()
{
return false;
}
#region IConditionalSerialization Members
bool IConditionalSerialization.ShouldSerialize()
{
return !IsSecret;
}
#endregion
}
public class TestClass
{
public static void Test()
{
Predicate<object> filter = (o) =>
{
var conditional = o as IConditionalSerialization;
return conditional == null || conditional.ShouldSerialize();
};
var settings = new JsonSerializerSettings
{
ContractResolver = new ShouldSerializeContractResolver(filter),
};
var ok = new ConditionalSerializationObject { IsSecret = false };
var notOk = new ConditionalSerializationObject { IsSecret = true };
Test(ok, settings);
Test(new { Public = ok, Private = notOk }, settings);
Test(new [] { ok, notOk, ok, notOk }, settings);
Test(new[,] {{ ok, notOk, ok, notOk }}, settings);
Test(new { Array = new[,] { { ok, notOk, ok, notOk } } }, settings);
try
{
Test(notOk, settings);
}
catch (Exception ex)
{
Console.WriteLine("Exception thrown and not caught serializing root object " + notOk.GetType());
Console.WriteLine(ex);
}
}
static void Test<T>(T value, JsonSerializerSettings settings)
{
Console.WriteLine("Unfiltered object: ");
Console.WriteLine(JToken.FromObject(value));
var serializer = JsonSerializer.CreateDefault(settings);
var token = JToken.FromObject(value, serializer);
Console.WriteLine("Filtered object: ");
Console.WriteLine(token);
if (!token.SelectTokens("..IsSecret").All(t => JToken.DeepEquals(t, (JValue)false)))
{
throw new InvalidOperationException("token.SelectTokens(\"..IsSecret\").All(t => JToken.DeepEquals(t, (JValue)true))");
}
if (token.SelectTokens("..SecretProperty").Any())
{
throw new InvalidOperationException("token.SelectTokens(\"..SecretProperty\").Any()");
}
Console.WriteLine("Secret objects and properties were successfully filtered.");
Console.WriteLine("");
}
}
プロトタイプfiddle。
多数の例外をスローしてキャッチすることは、パフォーマンスに影響を与える可能性があることに注意してください。 How expensive are exceptions in C#?を参照してください。これが問題かどうかを判断するには、Webアプリケーションのプロファイルを作成する必要があります。また、「秘密」のルートオブジェクトを直列化しようとする際に、Webサービスが例外を返すかどうかを決定する必要があります。
これはそのままでは実装されていません。 ['JsonSerializerInternalWriter.SerializeList()'](https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs#L663)のソースを参照してください。しかし、何かが可能かもしれません。あなたはあなたのセットアップの[mcve]を与えることができますか?シリアライズ対デシリアライズ時に異なる契約リゾルバを使用できますか?あなたのルートオブジェクトはフィルタリングを必要とするコレクションですか、それともプロパティコレクションをフィルタリングする必要がありますか? – dbc