使用JsonReader而不是JsonSerializer时,自定义JsonConverter无法正常工作

时间:2017-09-03 15:18:02

标签: c# json.net

我有一个课程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

虽然我可以在FooConverter中使用serializer.Deserialize<string>(reader)修复ReadJson,但我仍然想了解reader.ReadAsStringFooConverter.ReadJson失败的原因。

2 个答案:

答案 0 :(得分:2)

根据documentationJsonReader.ReadAsString()

,问题在于此
  

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

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

此外,在ReadJson()结束时,您的代码必须将阅读器定位在与要转换的对象相对应的最后一个JSON令牌上。因此,在JSON只是原始的情况下,读者根本不应该先进。

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

来自 Json.Net: Serialize/Deserialize property as a value, not as an object

StringIdConverter举例说明了这一点。您可以按如下方式修改它:

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

答案 1 :(得分:0)

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

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

{ Something: "a value" }

在C#中会像这样转换:

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