I've got a Newtonsoft JSON.NET JsonConverter to help deserialize a property whose type is an abstract class. The gist of it looks like this:

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

Here are the classes it handles:

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

当反序列化具有非空PetPerson时,这可以很好地工作.但是如果Pet为空,那么ReadJson方法在第一行用JsonReaderException中断:

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

其他信息:从JsonReader读取JObject时出错.当前JsonReader项目不是对象:Null.路径‘Pet’,第1行,位置11.

我查阅了the Custom JsonConverter份文档,但它只是一个writing转换器.我试过以下方法:

if (reader.Value == null) return null; // this inverts the [Test] results

But then I get:

JsonSerializationException: Additional text found in JSON string after finishing deserializing object.

如果该房产有is人居住.

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


For completeness, here are some unit tests that demonstrate the issue at hand:

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

推荐答案

While writing the question, specifically while writing the "what have I tried" bit, I find one possible solution:

if (reader.TokenType == JsonToken.Null) return null;

我发布这篇文章有两个原因:

  1. If it's good enough, it might help someone else with the same question.
  2. I might learn of a better, competing solution from someone else's answer.

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

Json相关问答推荐

您可以使用Jolt对JSON执行OR条件吗

将嵌套的json中的字符串强制转换为数字

如何在数组抖动中按值分组

Flutter -控制器问题-JSON API

当有2个嵌套数组时展平复杂的JSON

序列化从/到空值

如何使用模式注册中心创建从主题中取消本地化的ks qlDB表?

使用JQ将JSON输出转换为CSV复杂 struct

无法访问id的第三级json

jq 对特定键进行过滤并将值整理到单个 csv 单元格中

json 字符串到 Postgres 14 中的表视图

如何强制仅有一个元素的数组在JSON中生成方括号

数据清理设计不良的 JSON 数据 - 需要有关最佳策略的建议

嵌套 JSON 到 CSV(多级)

如何使用 Serde 使用顶级数组反序列化 JSON?

.NET CORE 3 升级 CORS 和 Json(cycle) XMLHttpRequest 错误

嵌套 JSON:如何向对象添加(推送)新项目?

如何在 Django JSONField 数据上聚合(最小/最大等)?

使用适用于 Python 的 Google API - 我从哪里获取 client_secrets.json 文件?

如何使用 SwiftyJSON 遍历 JSON?