1

In the following code, I serialize an object using Json.Net. This Json has type names embedded. I then change one of the type names to induce an error (this is a test, I am dealing with a real issue in an existing project). When I deserialize the Json, I expect to get an object back that has a null value for the property with the fiddled type name. Instead the serializer craps out and returns null. Are my expectations correct? Can I change the settings somehow so that I will get a non-null object for my root object? Note that the second error that I get suggests that there is a bug in the serializer.

static public class JsonTest
{
    static public void Test()
    {
        // Create test object
        A a = new A
        {
            MyTest = new MyTest(),
        };

        // Serialize it. 
        string json = JsonConvert.SerializeObject(a, new JsonSerializerSettings
        {
            TypeNameHandling = TypeNameHandling.Auto
        });

        // Fiddle class name to induce error
        json = json.Replace("+MyTest", "+MyTest2"); 

        // Before: {"MyTest":{"$type":"<Namespace>.JsonTest+MyTest, <Assembly>"}}
        // After: {"MyTest":{"$type":"<Namespace>.JsonTest+MyTest2, <Assembly>"}}

        // Deserialize
        A a2 = JsonConvert.DeserializeObject<A>(json, new JsonSerializerSettings
        {
            TypeNameHandling = TypeNameHandling.Auto,
            Error = (object sender, ErrorEventArgs e) =>
            {
                e.ErrorContext.Handled = true; // Should have only one error: the unrecognized Type
            }
        });

        // A second error occurs: Error = {Newtonsoft.Json.JsonSerializationException: Additional text found in JSON string after finishing deserializing object....
        // a2 is null
    }

    public class A
    {
        public ITest MyTest { get; set; }
    }

    public interface ITest { }
    public class MyTest : ITest { }
}

1 Answer 1

1

Update

This issue has been fixed in Json.NET 10.0.2 in this submission.

Original Answer

This looks to be a bug in Json.NET. If I set JsonSerializerSettings.MetadataPropertyHandling = MetadataPropertyHandling.ReadAhead then the problem goes away:

// Deserialize
A a2 = JsonConvert.DeserializeObject<A>(json, new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.Auto,
    MetadataPropertyHandling = MetadataPropertyHandling.ReadAhead,
    Error = (object sender, Newtonsoft.Json.Serialization.ErrorEventArgs e) =>
    {
        Debug.WriteLine(e.ErrorContext.Path);
        e.ErrorContext.Handled = true; // Should have only one error: the unrecognized Type
    }
});

Debug.Assert(a2 != null); // No assert. 

However, it should not be necessary to turn on this setting, which enables reading metadata properties including "$type" located anywhere in a JSON object, rather than just as the first property. Most likely it coincidentally fixes the bug since it requires pre-loading the entire JSON object before beginning deserialization.

You could report an issue if you want.

Debugging a bit, the problem seems to be that, because the inner MyTest object cannot be constructed, the exception is caught and handled by JsonSerializerInternalReader.PopulateObject() while populating the outer object A. Because of this, the JsonReader does not get advanced past the inner, nested object, leaving the reader and serializer in an inconsistent state. This accounts for the second exception and the eventual Additional text found in JSON string after finishing deserializing object. Path '' exception.

Sign up to request clarification or add additional context in comments.

1 Comment

I reported this as an issue, and it has been fixed.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.