I'm using the Newtonsoft JSON serializer and it works for most objects.
Unfortunately I get a JsonSerializationException
when I try to serialize a large object, one of whose members throws a NullReferenceException
.
Is there anyway to ignore the offending member and serialize the rest of the object?
I'm thinking perhaps in the JsonSerializerSettings
?
Here's a simplified version of what I want to do:
private class TestExceptionThrowingClass
{
public string Name { get { return "The Name"; } }
public string Address { get { throw new NullReferenceException(); } }
public int Age { get { return 30; } }
}
[Test]
public void CanSerializeAClassWithAnExceptionThrowingMember()
{
// Arrange
var toSerialize = new TestExceptionThrowingClass();
// Act
var serializerSettings = new Newtonsoft.Json.JsonSerializerSettings();
serializerSettings.MaxDepth = 5;
serializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
serializerSettings.MissingMemberHandling = Newtonsoft.Json.MissingMemberHandling.Ignore;
serializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
serializerSettings.ObjectCreationHandling = Newtonsoft.Json.ObjectCreationHandling.Reuse;
serializerSettings.DefaultValueHandling = Newtonsoft.Json.DefaultValueHandling.Ignore;
var result = Newtonsoft.Json.JsonConvert.SerializeObject(toSerialize);
// Assert
Assert.That(result, Is.EqualTo(@"{""Name"":""The Name"",""Age"":30}"));
}
And here's the stack trace:
at Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue(Object target)
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CalculatePropertyValues(JsonWriter writer, Object value, JsonContainerContract contract, JsonProperty member, JsonProperty property, JsonContract& memberContract, Object& memberValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter jsonWriter, Object value, Type objectType)
at Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter jsonWriter, Object value, Type objectType)
at Newtonsoft.Json.JsonConvert.SerializeObject(Object value, Type type, Formatting formatting, JsonSerializerSettings settings)
at Newtonsoft.Json.JsonConvert.SerializeObject(Object value)
at AspectsProject.Aspects.CachingPolicy.CachingPolicyCacheKeyCreatorTests.CanSerializeAClassWithAnExceptionThrowingMember() in D:\Dev\test.cs:line 169
--NullReferenceException
at AspectsProject.Aspects.CachingPolicy.CachingPolicyCacheKeyCreatorTests.TestExceptionThrowingClass.get_Address() in D:\Dev\test.cs:line 149
at GetAddress(Object )
at Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue(Object target)
I'm happy to use a different JSON serializer if someone knows one that will do this.
If you don't control the source code, you can use a custom
ContractResolver
to inject a "ShouldSerialize" method for the problematic property during serialization. You can have that method always return false, or optionally, implement some logic which will detect the situations where the property will throw and return false only in that case.For example, let's say that your class looks like this:
Clearly, if we try to serialize the above, it will not work because the
Offender
property will always throw an exception when the serializer tries to access it. Since we know the class and property name that causes the problem, we can write a custom ContractResolver (derived from the DefaultContractResolver) to suppress the serialization of that specific member.Here's a demo showing how to use it:
Output:
A more generic solution
In your comments you indicated that you have many kinds of objects that might throw an exception when any of the properties are accessed. To that end, we need something more generic. Here is a resolver that might work for that case, but you'll need to test it extensively in your own environment. It does not depend on any particular class or property name, but creates a ShouldSerialize predicate for every property that comes its way. In that predicate it uses reflection to get the value of the property inside a try/catch; if successful it returns true, otherwise false.
Here is a demo:
Output:
A simpler way to ignore errors:
or