【问题标题】:Custom JsonConverter not working when using JsonReader instead of JsonSerializer使用 JsonReader 而不是 JsonSerializer 时自定义 JsonConverter 不起作用
【发布时间】:2017-09-03 15:18:02
【问题描述】:

我有一个类Foo 及其FooConverter,定义如下:

[JsonConverter(typeof(FooConverter))]
public class Foo
{
    public string Something { get; set; }
}

public class FooConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        writer.WriteValue(((Foo)value).Something);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var str = reader.ReadAsString();
        if (str == null)
        {
            throw new JsonSerializationException();
        }    
        // return new Foo {Something = serializer.Deserialize<string>(reader)};
        return new Foo {Something = str};
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(Foo);
    }
}

序列化工作正常。但是反序列化时:

var foo = JsonConvert.DeserializeObject<Foo>("\"something\"");

它抛出 JsonSerializationException 因为 reader.ReadAsString 为空。
但我不明白为什么它必须是 null...reader.ReadAsString 如果我像这样手动执行它,它可以完美地找到:

var reader = new JsonTextReader(new StringReader("\"something\""));
var str = reader.ReadAsString(); // str is now `something` NOT null

虽然我可以通过在ReadJson 中使用serializer.Deserialize&lt;string&gt;(reader) 来修复FooConverter,但我仍然想了解为什么reader.ReadAsStringFooConverter.ReadJson 中失败。

【问题讨论】:

标签: c# json.net


【解决方案1】:

你的问题是,根据documentationJsonReader.ReadAsString()

从源中读取 下一个 JSON 令牌作为字符串。

但是,当调用JsonConverter.ReadJson() 时,读取器已经定位在与被反序列化的对象对应的第一个 JSON 令牌上。因此,通过调用ReadAsString(),您丢弃该值并尝试读取流中的下一个标记——但没有,因此您抛出异常。

此外,在ReadJson() 的末尾,您的代码必须将阅读器定位在与正在转换的对象相对应的最后一个 JSON 标记处。因此,在 JSON 只是一个原始的情况下,读者根本不应该变得更先进。

保证阅读器始终被ReadJson() 正确定位的一种简单方法是调用JToken.Load()。这总是使阅读器位于已加载令牌的末尾。之后,您可以检查加载的内容是否符合预期。例如,如果 JSON 中有一个对象需要字符串,而不是让阅读器定位不正确,转换器应该抛出异常而不是让阅读器定位不正确。

StringIdConverter from Json.Net: Serialize/Deserialize property as a value, not as an object 给出了一个例子。你可以修改如下:

public class FooConverter : JsonConverter
{
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;
        var token = JToken.Load(reader);
        if (!(token is JValue))
            throw new JsonSerializationException("Token was not a primitive");
        return new Foo { Something = (string)token };
    }

【讨论】:

    【解决方案2】:

    您的 "\"something\"" 不是有效的 JSON,它只是一个转义字符串。 JSON = JavaScript Object Notation,其中一个空对象被定义为{}

    具有状态的对象只是一个字典结构,其中键有值。您的Foo 类型有一个属性Something,它是键,需要为其分配一个值。所以试试这样的 JSON 对象:

    { Something: "a value" }
    

    在 C# 中会这样转换:

    var foo = JsonConvert.DeserializeObject<Foo>("{\"Something\":\"a value\"}");
    

    【讨论】:

    • 是的,这就是问题所在。我怀疑 JsonReader 专门寻找正确定义的 JSON 属性。
    • @DanielMay 那么为什么当我手动使用 JsonReader 时它工作正常? (从问题上看)
    • @Dvs 我不能使用它,因为我需要手动反序列化。这就是我使用 JsonConverter 的原因。
    • @WingerSendon 你能解释一下(1)为什么你需要“手动”转换 JSON(有点代码味道)和(2)你为什么试图通过继承 @987654329 来做到这一点@,我假设是 Newtonsoft.Json 类型,它需要有效的 JSON?
    • @Dvs 我有一个类型,它是一个字符串的包装器(更具体地说,是从this 问题中接受的答案中看到的“字符串枚举”)。因此需要将其转换为 Json 字符串而不是 Json 对象。而且这个对象没有直接翻译成 json。它在另一个对象内,所以它在 J​​son 中可能看起来像这样:{"MyStringEnum":"a value"} (2) 不知道你的意思是什么。字符串被转换为 json 字符串。数组转换为 json 数组。我希望将我的类转换为 json 字符串。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-12-27
    • 1970-01-01
    • 1970-01-01
    • 2012-09-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多