c# - 自定义 JsonConverter WriteJson 不会改变子属性的序列化

标签 c# json serialization json.net converter

我一直觉得 JSON 序列化程序实际上遍历了整个对象的树,并在它遇到的每个接口(interface)类型对象上执行自定义 JsonConverter 的 WriteJson 函数 - 并非如此。

我有以下类和接口(interface):

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 是

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

在这个例子中,是的,一只狗可以为 child 养猫,反之亦然。在转换器中,我想插入“类型”属性,以便将其保存到序列化中。我有以下设置。 (动物园只有一个名称和一个 IAnimals 列表。为了简洁和懒惰,我没有在此处包括它;))

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 序列化后输出如下:

{
  "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": []
        }
      ]
    }
  ]
}

类型属性出现在 Ruff、Cleo 和 Rover 上,但不出现在 Fido 和 Fluffy 上。我想 WriteJson 不是递归调用的。我如何在那里获得该类型的属性(property)?

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

最佳答案

您的转换器未应用于您的子对象的原因是因为 JToken.FromObject() 在内部使用序列化程序的新实例,它不知道您的转换器。有一个允许您传入序列化器的重载,但是如果您在这里这样做,您将遇到另一个问题:因为您在一个转换器中并且您正在使用 JToken.FromObject() 来尝试序列化父对象,就会陷入无限递归循环。 (JToken.FromObject() 调用序列化程序,后者调用您的转换器,后者调用 JToken.FromObject(),等等)

要解决这个问题,您必须手动处理父对象。使用一些反射来枚举父属性,您可以毫不费力地做到这一点:

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

fiddle :https://dotnetfiddle.net/sVWsE4

关于c# - 自定义 JsonConverter WriteJson 不会改变子属性的序列化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29267030/

相关文章:

c# - 在 PHP/Bash/C# 中从 PDF 中删除图层/背景#

java - php json_encode 安卓的欧元符号

java - 让 Jackson XMLMapper 在代码中设置根元素名称

json - Golang 服务器,如何接收 TCP JSON 数据包?

java - 可选字段的 Avro 序列化

c# - 如何检查 (c#) ControlCollection.Find() 是否返回结果

c# - PagedList 在第二页和之后的页面上丢失搜索过滤器

传递到下一个 Activity 时 Parcelable 中的 Android 问题

ios - 通过 AirDrop 发送 NSArray

c# - 当 DrawMode 不是 Normal 时,我的 ComboBox 看起来很糟糕