我一直觉得 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/