我有一个类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);
}
}
Run Code Online (Sandbox Code Playgroud)
序列化工作正常。但是在反序列化时:
var foo = JsonConvert.DeserializeObject<Foo>("\"something\"");
Run Code Online (Sandbox Code Playgroud)
它抛出,JsonSerializationException因为它reader.ReadAsString是空的。
但我不明白为什么它必须是null......reader.ReadAsString如果我像这样手动执行它,它可以完美地找到:
var reader = new JsonTextReader(new StringReader("\"something\""));
var str = reader.ReadAsString(); // str is now `something` NOT null
Run Code Online (Sandbox Code Playgroud)
虽然我可以FooConverter通过使用serializer.Deserialize<string>(reader)in修复ReadJson,但我仍然想了解为什么reader.ReadAsString在FooConverter.ReadJson.
Your problem is that, according to the documentation, JsonReader.ReadAsString():
Reads the next JSON token from the source as a String.
However, when JsonConverter.ReadJson() is called, the reader is already positioned on the first JSON token corresponding to the object being deserialized. Thus, by calling ReadAsString() you discard that value and try to read the next token in the stream -- but there is none, so you throw an exception.
Further, At the end of ReadJson() your code must have positioned the reader at the last JSON token corresponding to the object being converted. So, in the case where the JSON is simply a primitive, the reader should not get advanced at all.
A simple way to guarantee that the reader is always correctly positioned by ReadJson() is to call JToken.Load(). This always leaves the reader positioned at the end of the token that was loaded. Afterwards, you can check to see that what was loaded was as expected. E.g., if the JSON has an object where a string was expected, rather than leaving the reader incorrectly positioned, the converter should throw an exception rather than leave the reader incorrectly positioned.
StringIdConverter from Json.Net: Serialize/Deserialize property as a value, not as an object gives an example of this. You could modify it as follows:
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 };
}
Run Code Online (Sandbox Code Playgroud)