在自定义JsonConverter的ReadJson方法中处理null对象

Jer*_*oen 23 c# json json.net

我有一个Newtonsoft JSON.NET JsonConverter来帮助反序列化其类型是抽象类的属性.它的要点看起来像这样:

public class PetConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(Animal);
    }

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

        if (jsonObject["Lives"] != null) return jsonObject.ToObject<Cat>(serializer);
        if (jsonObject["StopPhrase"] != null) return jsonObject.ToObject<Parrot>(serializer);

        return null;
    }

    public override bool CanWrite { get { return false; } }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    { throw new NotImplementedException(); }
}
Run Code Online (Sandbox Code Playgroud)

以下是它处理的类:

public abstract class Animal 
{ }

public class Cat : Animal
{
    public int Lives { get; set; }
}

public class Parrot : Animal
{
    public string StopPhrase { get; set; }
}

public class Person
{
    [JsonConverter(typeof(PetConverter))]
    public Animal Pet { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

这在反序列化Person具有非null的方法时工作正常Pet.但是如果Pet为null,那么该ReadJson方法在第一行打破了这个JsonReaderException:

Newtonsoft.Json.dll中出现"Newtonsoft.Json.JsonReaderException"类型的异常,但未在用户代码中处理

附加信息:从JsonReader读取JObject时出错.当前的JsonReader项不是对象:Null.路径'宠物',第1行,第11位.

我已经检查了Custom JsonConverter文档,但它只是一个写入转换器.我尝试过以下方法:

if (reader.Value == null) return null; // this inverts the [Test] results
Run Code Online (Sandbox Code Playgroud)

但后来我得到:

JsonSerializationException:完成反序列化对象后在JSON字符串中找到的附加文本.

对于填充属性的情况.

简而言之,处理这种情况的正确方法是什么?


为了完整起见,这里有一些单元测试可以证明手头的问题:

[TestFixture]
public class JsonConverterTests
{
    [Test]
    public void Cat_survives_serialization_roundtrip()
    {
        var person = new Person { Pet = new Cat { Lives = 9 } };
        var serialized = JsonConvert.SerializeObject(person);
        var deserialized = JsonConvert.DeserializeObject<Person>(serialized);
        Assert.That(deserialized.Pet, Is.InstanceOf<Cat>());
        Assert.That((deserialized.Pet as Cat).Lives, Is.EqualTo(9));
    }

    [Test]
    public void Parrot_survives_serialization_roundtrip()
    {
        var person = new Person { Pet = new Parrot { StopPhrase = "Lorrie!" } };
        var serialized = JsonConvert.SerializeObject(person);
        var deserialized = JsonConvert.DeserializeObject<Person>(serialized);
        Assert.That(deserialized.Pet, Is.InstanceOf<Parrot>());
        Assert.That((deserialized.Pet as Parrot).StopPhrase, Is.EqualTo("Lorrie!"));
    }

    [Test]
    public void Null_property_does_not_break_converter()
    {
        var person = new Person { Pet = null };
        var serialized = JsonConvert.SerializeObject(person);
        var deserialized = JsonConvert.DeserializeObject<Person>(serialized);
        Assert.That(deserialized.Pet, Is.Null);
    }
}
Run Code Online (Sandbox Code Playgroud)

Jer*_*oen 37

在写这个问题时,特别是在编写"我试过的东西"位时,我找到了一个可能的解决方案:

if (reader.TokenType == JsonToken.Null) return null;
Run Code Online (Sandbox Code Playgroud)

我发布这个有两个原因:

  1. 如果它足够好,它可能会帮助其他人有同样的问题.
  2. 我可能会从其他人的答案中了解到更好的竞争解决方案.

FWIW,这里是完整的JsonConverter,用于对类型为抽象类的属性进行非常基本的处理反序列化:

public class PetConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(Animal);
    }

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

        JObject jsonObject = JObject.Load(reader);

        if (jsonObject["Lives"] != null) return jsonObject.ToObject<Cat>(serializer);
        if (jsonObject["StopPhrase"] != null) return jsonObject.ToObject<Parrot>(serializer);

        return null;
    }

    public override bool CanWrite { get { return false; } }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    { 
        throw new NotImplementedException(); 
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 和/或您可以将您的 JSON 作为 `JToken` 加载并检查 `token.Type == JTokenType.Null`。尽管实际上您的解决方案似乎是最好的。 (2认同)
  • 嗨,Jeroen,只是写信说你的建议帮助解决了我的问题,尽管说实话,我不知道为什么。我最初的代码与您的代码类似,在 ReadJson 中我尝试简单地说“if (reader.Value is Animal)”,但是 reader.Value 始终为 null(尽管它实际上具有值)。最后我只是按照你的想法,并使用“jsonObject[MyValue] != null”,就像你对 Lives/StopPhrase 所做的那样。 (2认同)