自定义JsonConverter WriteJson不会更改子属性的序列化 [英] Custom JsonConverter WriteJson Does Not Alter Serialization of Sub-properties

查看:98
本文介绍了自定义JsonConverter WriteJson不会更改子属性的序列化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我总是给人以JSON序列化程序实际上遍历整个对象树的印象,并在遇到的每个接口类型的对象上执行自定义JsonConverter的WriteJson函数-并非如此.

I always had the impression that the JSON serializer actually traverses your entire object's tree, and executes the custom JsonConverter's WriteJson function on each interface-typed object that it comes across - not so.

我有以下类和接口:

public interface IAnimal
{
    string Name { get; set; }
    string Speak();
    List<IAnimal> Children { get; set; }
}

public class Cat : IAnimal
{
    public string Name { get; set; }
    public List<IAnimal> Children { get; set; }        

    public Cat()
    {
        Children = new List<IAnimal>();
    }

    public Cat(string name="") : this()
    {
        Name = name;
    }

    public string Speak()
    {
        return "Meow";
    }       
}

 public class Dog : IAnimal
 {
    public string Name { get; set; }
    public List<IAnimal> Children { get; set; }

    public Dog()
    {
        Children = new List<IAnimal>();   
    }

    public Dog(string name="") : this()
    {
        Name = name;
    }

    public string Speak()
    {
        return "Arf";
    }

}

为了避免JSON中的$ type属性,我编写了一个自定义JsonConverter类,其WriteJson为

To avoid the $type property in the JSON, I've written a custom JsonConverter class, whose WriteJson is

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
    JToken t = JToken.FromObject(value);

    if (t.Type != JTokenType.Object)
    {
        t.WriteTo(writer);                
    }
    else
    {
        IAnimal animal = value as IAnimal;
        JObject o = (JObject)t;

        if (animal != null)
        {
            if (animal is Dog)
            {
                o.AddFirst(new JProperty("type", "Dog"));
                //o.Find
            }
            else if (animal is Cat)
            {
                o.AddFirst(new JProperty("type", "Cat"));
            }

            foreach(IAnimal childAnimal in animal.Children)
            {
                // ???
            }

            o.WriteTo(writer);
        }
    }
}

在此示例中,是的,狗可以养猫给孩子,反之亦然.在转换器中,我想插入"type"属性,以便将其保存到序列化中.我有以下设置. (Zoo只有一个名称和一个IAnimals列表.为了简洁和懒惰,我在这里没有包括它;))

In this example, yes, a dog can have cats for children and vice-versa. In the converter, I want to insert the "type" property so that it saves that to the serialization. I have the following setup. (Zoo has only a name and a list of IAnimals. I didn't include it here for brevity and laziness ;))

Zoo hardcodedZoo = new Zoo()
            {   Name = "My Zoo",               
                Animals = new List<IAnimal> { new Dog("Ruff"), new Cat("Cleo"),
                    new Dog("Rover"){
                        Children = new List<IAnimal>{ new Dog("Fido"), new Dog("Fluffy")}
                    } }
            };

            JsonSerializerSettings settings = new JsonSerializerSettings(){
                ContractResolver = new CamelCasePropertyNamesContractResolver() ,                    
                Formatting = Formatting.Indented
            };
            settings.Converters.Add(new AnimalsConverter());            

            string serializedHardCodedZoo = JsonConvert.SerializeObject(hardcodedZoo, settings);

serializedHardCodedZoo序列化后具有以下输出:

serializedHardCodedZoo has the following output after serialization:

{
  "name": "My Zoo",
  "animals": [
    {
      "type": "Dog",
      "Name": "Ruff",
      "Children": []
    },
    {
      "type": "Cat",
      "Name": "Cleo",
      "Children": []
    },
    {
      "type": "Dog",
      "Name": "Rover",
      "Children": [
        {
          "Name": "Fido",
          "Children": []
        },
        {
          "Name": "Fluffy",
          "Children": []
        }
      ]
    }
  ]
}

type属性显示在Ruff,Cleo和Rover上,但不适用于Fido和Fluffy.我猜WriteJson没有递归调用.我如何在那里获得该类型属性?

The type property shows up on Ruff, Cleo, and Rover, but not for Fido and Fluffy. I guess the WriteJson isn't called recursively. How do I get that type property there?

顺便说一句,为什么它不像我期望的那样驼峰式IAnimals?

As an aside, why does it not camel-case IAnimals like I expect it to?

推荐答案

未将转换器应用到子对象的原因是JToken.FromObject()在内部使用了序列化程序的新实例,该实例不了解您的转换器.有一个重载,允许您传递序列化程序,但是如果这样做,您将遇到另一个问题:由于您位于转换器内部,并且您正在使用JToken.FromObject()尝试序列化父对象,因此您将进入无限递归循环. (JToken.FromObject()调用序列化程序,该序列化程序调用您的转换器,调用JToken.FromObject()等)

The reason that your converter is not getting applied to your child objects is because JToken.FromObject() uses a new instance of the serializer internally, which does not know about your converter. There is an overload that allows you to pass in the serializer, but if you do so here you will have another problem: since you are inside a converter and you are using JToken.FromObject() to try to serialize the parent object, you will get into an infinite recursive loop. (JToken.FromObject() calls the serializer, which calls your converter, which calls JToken.FromObject(), etc.)

要解决此问题,必须手动处理父对象.您可以使用一点反射来枚举父属性,而不会遇到很多麻烦:

To get around this problem, you must handle the parent object manually. You can do this without much trouble using a bit of reflection to enumerate the parent properties:

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
    JObject jo = new JObject();
    Type type = value.GetType();
    jo.Add("type", type.Name);

    foreach (PropertyInfo prop in type.GetProperties())
    {
        if (prop.CanRead)
        {
            object propVal = prop.GetValue(value, null);
            if (propVal != null)
            {
                jo.Add(prop.Name, JToken.FromObject(propVal, serializer));
            }
        }
    }
    jo.WriteTo(writer);
}

提琴: https://dotnetfiddle.net/sVWsE4

这篇关于自定义JsonConverter WriteJson不会更改子属性的序列化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆