简体   繁体   中英

Custom JsonConverter gets reader with TokenType “EndObject” when it shouldn't be

I have a stream with an array of JSON objects which come in two different formats but contain the same sort of data, and I want to deserialize both of these formats into the same type so my view doesn't need custom logic for both formats of data. Currently I am handling this using a custom JsonConverter.

Here is my model:

[JsonObject]
[JsonConverter(typeof(MyCommonObjectJsonConverter))]
public class MyCommonObject {
    // some common fields, e.g.
    public String Id { get; set; }
    public string Text { get; set; }
}

Here is my custom JsonConverter:

public class MyCommonObjectJsonConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        // don't need to worry about serialization in this case, only
        // reading data
        throw new NotImplementedException();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject jObject = JObject.Load(reader);

        MyCustomObject result;
        if (IsFormatOne(jObject))
        {
            // the structure of the object matches the first format,
            // so just deserialize it directly using the serializer
            result = serializer.Deserialize<MyCustomObject>(reader);
        }
        else if (IsFormatTwo(jObject))
        {
            result = new MyCustomObject();

            // initialize values from the JObject
            // ... 
        }
        else
        {
            throw new InvalidOperationException("Unknown format, cannot deserialize");
        }

        return result;
    }

    public override bool CanConvert(Type objectType)
    {
        return typeof(MyCustomObject).IsAssignableFrom(objectType);
    }

    // Definitions of IsFormatOne and IsFormatTwo
    // ...
}

However, when I deserialize the first format's objects, I get an error saying that it can't load the JObject, since the JsonReader has TokenType "EndToken". I'm not sure why this is happening, the data I am loading in is correctly formatted. Any suggestions on what I should be looking out for?

You want to fall back to the default deserialization when reading your MyCommonObject , however:

  • The call to JObject.Load(reader) has already advanced the reader past the object, as you have observed, and
  • calling serializer.Deserialize<MyCustomObject>(reader) would in any event result in infinite recursion.

The customary way to avoid unwanted recursion in ReadJson() in cases like this is to allocate your result manually then call serializer.Populate(jObject.CreateReader(), result) . Ie:

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;

        JObject jObject = JObject.Load(reader);

        MyCommonObject result;
        if (IsFormatOne(jObject))
        {
            result = (existingValue as MyCommonObject ?? (MyCommonObject)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator()); // Reuse existingValue if present
            // the structure of the object matches the first format,
            // so just deserialize it directly using the serializer
            using (var subReader = jObject.CreateReader())
                serializer.Populate(subReader, result);
        }
        else if (IsFormatTwo(jObject))
        {
            result = (existingValue as MyCommonObject ?? (MyCommonObject)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator());

            // initialize values from the JObject
            // ... 
        }
        else
        {
            throw new InvalidOperationException("Unknown format, cannot deserialize");
        }

        return result;
    }

Figured it out while writing this up, calling serializer.Deserialize<MyCustomObject>(reader) was recursing into my converter again, at which point the reader will have reached an end token from being loaded into the JObject, making the TokenType equal to EndToken. I should have checked the stack trace more carefully. For now I'm going to write custom initialization logic for both formats, making my model class format-agnostic.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM