It is not implemented out of the box. If you check the source for JsonSerializerInternalWriter.SerializeList() , you will see that there is no logic to skip collection entries based on some filter.
However, Json.NET has robust exception handling . If an exception occurs when the object is serialized, then it is caught and swallowed in the [OnError] :
- If you record an array record, the array record is skipped (your desired behavior).
- If you write the root object, the exception does not get caught (maybe an error?)
- Otherwise,
null written.
So one way to achieve the desired functionality would be to throw an exception from the artificial callback added to JsonContract.OnSerializingCallbacks your custom contract agreement, then catch and catch the exception using a handler added to JsonContract.OnErrorCallbacks . Combined with filtering property values โโthat you are already running, this approach has the advantage of ensuring that the secret object cannot be serialized even if it is the root object or when it is contained in a dictionary, dynamic object, or multidimensional array . This approach will not interfere with PreserveReferencesHandling.Arrays .
One contract resolver that does this is as follows:
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; } }
Then one of the possible use cases:
public interface IConditionalSerialization { bool ShouldSerialize(); } public class ConditionalSerializationObject : IConditionalSerialization { public bool IsSecret { get; set; } public string SecretProperty { get { return "should not see me"; } }
Prototype fiddle .
Note that throwing and catching a large number of exceptions can have performance implications. See How expensive are exceptions in C #? . You will need to profile your web application to determine if this is a problem. You will also need to decide if your web service should throw an exception when trying to serialize a "secret" root object or do something else.
dbc
source share