自定义 JsonConverter 获取 TokenType 为“EndObject”的读取器,而不应

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

提问人:akbiggs 提问时间:8/27/2015 更新时间:8/27/2015 访问量:1608

问:

我有一个包含 JSON 对象数组的流,这些对象有两种不同的格式,但包含相同类型的数据,我想将这两种格式反序列化为相同的类型,这样我的视图就不需要两种格式的数据的自定义逻辑。目前,我正在使用自定义JsonConverter处理此问题。

这是我的模型:

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

这是我的自定义 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
    // ...
}

但是,当我反序列化第一种格式的对象时,我收到一个错误,说它无法加载 JObject,因为 JsonReader 具有 TokenType “EndToken”。我不确定为什么会发生这种情况,我正在加载的数据格式正确。关于我应该注意什么的任何建议?

C# .NET 序列化 json.net

评论


答:

0赞 akbiggs 8/27/2015 #1

在写这篇文章时弄清楚了,调用再次递归到我的转换器中,此时读取器将从加载到 JObject 中到达结束令牌,使 TokenType 等于 EndToken。我应该更仔细地检查堆栈跟踪。现在,我将为这两种格式编写自定义初始化逻辑,使我的模型类与格式无关。serializer.Deserialize<MyCustomObject>(reader)

评论

0赞 akbiggs 8/27/2015
作为后续,有没有办法在不使用模型的自定义转换器的情况下将 JSON 对象转换为模型?即只使用默认的
2赞 dbc 8/27/2015
当您想要回退到标准行为时,避免递归的习惯方法是自己分配对象,然后调用序列化程序。填充(jObject.CreateReader(), result)。ReadJson()
3赞 dbc 8/27/2015 #2

但是,在读取 时,您希望回退到默认的反序列化:MyCommonObject

  • 正如你所观察到的,对的调用已经将读者推进到对象之外,并且JObject.Load(reader)
  • 无论如何,调用都会导致无限递归。serializer.Deserialize<MyCustomObject>(reader)

在这种情况下,避免不必要的递归的习惯方法是手动分配结果,然后调用序列化程序。填充(jObject.CreateReader(), result)。即:ReadJson()

    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;
    }