且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何在 JSON.NET 中实现自定义 JsonConverter?

更新时间:2023-02-17 20:59:45

使用标准的 CustomCreationConverter,我一直在努力研究如何生成正确的类型(PersonEmployee),因为为了确定这一点,您需要分析 JSON,并且没有使用 Create 方法来执行此操作的内置方法.

我找到了一个关于类型转换的讨论线程,结果提供了答案.这是一个链接:类型转换.

所需要的是对 JsonConverter 进行子类化,覆盖 ReadJson 方法并创建一个新的抽象 Create 方法,该方法接受一个 JObject代码>.

JObject 类提供了一种加载 JSON 对象和提供对该对象内数据的访问.

重写的 ReadJson 方法创建一个 JObject 并调用 Create 方法(由我们的派生转换器类实现),传入 JObject 实例.

然后可以分析此 JObject 实例,通过检查某些字段的存在来确定正确的类型.

示例

string json = "[{"部门": "部门1","JobTitle": "JobTitle1","FirstName": "FirstName1","姓氏":"姓氏1"},{"部门": "部门2","JobTitle": "JobTitle2","FirstName": "FirstName2","姓氏":"姓氏2"},{"技能": "画家","FirstName": "FirstName3","LastName": "LastName3"}]";列表人 =JsonConvert.DeserializeObject>(json, new PersonConverter());...公共类 PersonConverter : JsonCreationConverter{protected override Person Create(Type objectType, JObject jObject){如果(FieldExists(技能",jObject)){返回新艺术家();}否则如果(FieldExists(部门",jObject)){返回新员工();}别的{返回新人();}}private bool FieldExists(string fieldName, JObject jObject){返回 jObject[fieldName] != null;}}公共抽象类 JsonCreationConverter;: JsonConverter{//////创建一个objectType的实例,基于JSON对象中的属性///</总结>///<param name="objectType">预期的对象类型</param>//////将被反序列化的 JSON 对象的内容///</param>///<returns></returns>protected abstract T Create(Type objectType, JObject jObject);public override bool CanConvert(Type objectType){返回 typeof(T).IsAssignableFrom(objectType);}公共覆盖布尔 CanWrite{得到 { 返回假;}}公共覆盖对象 ReadJson(JsonReader reader,类型对象类型,对象现有值,JsonSerializer 序列化器){//从流中加载 JObjectJObject jObject = JObject.Load(reader);//基于JObject创建目标对象T target = Create(objectType, jObject);//填充对象属性serializer.Populate(jObject.CreateReader(), 目标);返回目标;}}

I am trying to extend the JSON.net example given here http://james.newtonking.com/projects/json/help/CustomCreationConverter.html

I have another sub class deriving from base class/Interface

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class Employee : Person
{
    public string Department { get; set; }
    public string JobTitle { get; set; }
}

public class Artist : Person
{
    public string Skill { get; set; }
}

List<Person> people  = new List<Person>
{
    new Employee(),
    new Employee(),
    new Artist(),
};

How do I deserialize following Json back to List< Person >

[
  {
    "Department": "Department1",
    "JobTitle": "JobTitle1",
    "FirstName": "FirstName1",
    "LastName": "LastName1"
  },
  {
    "Department": "Department2",
    "JobTitle": "JobTitle2",
    "FirstName": "FirstName2",
    "LastName": "LastName2"
  },
  {
    "Skill": "Painter",
    "FirstName": "FirstName3",
    "LastName": "LastName3"
  }
]

I don't want to use TypeNameHandling JsonSerializerSettings. I am specifically looking for custom JsonConverter implementation to handle this. The documentation and examples around this are pretty sparse on the net. I can't seem to get the the overridden ReadJson() method implementation in JsonConverter right.

Using the standard CustomCreationConverter, I was struggling to work how to generate the correct type (Person or Employee), because in order to determine this you need to analyse the JSON and there is no built in way to do this using the Create method.

I found a discussion thread pertaining to type conversion and it turned out to provide the answer. Here is a link: Type converting.

What's required is to subclass JsonConverter, overriding the ReadJson method and creating a new abstract Create method which accepts a JObject.

The JObject class provides a means to load a JSON object and provides access to the data within this object.

The overridden ReadJson method creates a JObject and invokes the Create method (implemented by our derived converter class), passing in the JObject instance.

This JObject instance can then be analysed to determine the correct type by checking existence of certain fields.

Example

string json = "[{
        "Department": "Department1",
        "JobTitle": "JobTitle1",
        "FirstName": "FirstName1",
        "LastName": "LastName1"
    },{
        "Department": "Department2",
        "JobTitle": "JobTitle2",
        "FirstName": "FirstName2",
        "LastName": "LastName2"
    },
        {"Skill": "Painter",
        "FirstName": "FirstName3",
        "LastName": "LastName3"
    }]";

List<Person> persons = 
    JsonConvert.DeserializeObject<List<Person>>(json, new PersonConverter());

...

public class PersonConverter : JsonCreationConverter<Person>
{
    protected override Person Create(Type objectType, JObject jObject)
    {
        if (FieldExists("Skill", jObject))
        {
            return new Artist();
        }
        else if (FieldExists("Department", jObject))
        {
            return new Employee();
        }
        else
        {
            return new Person();
        }
    }

    private bool FieldExists(string fieldName, JObject jObject)
    {
        return jObject[fieldName] != null;
    }
}

public abstract class JsonCreationConverter<T> : JsonConverter
{
    /// <summary>
    /// Create an instance of objectType, based properties in the JSON object
    /// </summary>
    /// <param name="objectType">type of object expected</param>
    /// <param name="jObject">
    /// contents of JSON object that will be deserialized
    /// </param>
    /// <returns></returns>
    protected abstract T Create(Type objectType, JObject jObject);

    public override bool CanConvert(Type objectType)
    {
        return typeof(T).IsAssignableFrom(objectType);
    }

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

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

        // Create target object based on JObject
        T target = Create(objectType, jObject);

        // Populate the object properties
        serializer.Populate(jObject.CreateReader(), target);

        return target;
    }
}