Dies ist nicht im Lieferumfang enthalten. Wenn Sie die Quelle für JsonSerializerInternalWriter.SerializeList()
überprüfen, werden Sie sehen, dass keine Logik vorhanden ist, um Auflistungseinträge basierend auf einem Filter zu überspringen.
Allerdings hat Json.NET robust exception handling. Wenn eine Ausnahme ausgelöst wird, wenn ein Objekt zu serialisiert beginnt dann in einem [OnError]
Rückruf gefangen und verschlungen:
- Wenn ein Array-Eintrag schreiben, der Array-Eintrag ist übersprungen (Ihr gewünschtes Verhalten).
- Wenn das Root-Objekt geschrieben wird, wird die Ausnahme nicht abgefangen (möglicherweise ein Fehler?)
- Sonst wird
null
geschrieben.
So ist eine Möglichkeit, Ihre gewünschte Funktionalität zu erreichen, wäre eine Ausnahme von einem künstlichen Rückruf JsonContract.OnSerializingCallbacks
durch Ihren benutzerdefinierten Vertrag Resolver hinzugefügt zu werfen, dann fangen und die Ausnahme schluckte mit einem Handler JsonContract.OnErrorCallbacks
hinzugefügt. In Kombination mit der Filterung von Eigenschaftswerten, wie Sie es bereits tun, hat dieser Ansatz den Vorteil, dass ein geheimes Objekt nicht serialisiert werden kann, selbst wenn es das Stammobjekt ist oder in einem Wörterbuch, einem dynamischen Objekt oder einem mehrdimensionalen Array enthalten ist. Dieser Ansatz stört PreserveReferencesHandling.Arrays
nicht.
Ein Vertrag Resolver, der dies tut, ist wie folgt:
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;
}
}
Dann eine mögliche Verwendung wäre:
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("");
}
}
Prototype fiddle.
Beachten Sie, dass das Werfen und Abfangen einer großen Anzahl von Ausnahmen Auswirkungen auf die Leistung haben kann. Siehe How expensive are exceptions in C#?. Sie müssen Ihre Webanwendung profilieren, um festzustellen, ob dies ein Problem ist.Sie müssen auch entscheiden, ob Ihr Web-Service eine Exception zurückgeben soll, wenn Sie versuchen, ein "geheimes" Root-Objekt zu serialisieren oder etwas anderes zu tun.
Dies ist nicht im Lieferumfang enthalten. Siehe die Quelle für ['JsonSerializerInternalWriter.SerializeList()'] (https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs#L663). Aber etwas kann möglich sein. Kannst du ein [mcve] deiner Einrichtung geben? Können Sie beim Serialisieren und Deserialisieren verschiedene Vertragsresolver verwenden? Ist Ihr Stammobjekt jemals eine Sammlung, die gefiltert werden muss, oder müssen Sie nur die Objektgruppen filtern? – dbc