C# 自定义JsonConverter WriteJson不会更改子属性的序列化
我一直觉得JSON序列化程序实际上遍历了整个对象的树,并在遇到的每个接口类型的对象上执行自定义JsonConverter的WriteJson函数——事实并非如此 我有以下类和接口:C# 自定义JsonConverter WriteJson不会更改子属性的序列化,c#,json,serialization,json.net,converter,C#,Json,Serialization,Json.net,Converter,我一直觉得JSON序列化程序实际上遍历了整个对象的树,并在遇到的每个接口类型的对象上执行自定义JsonConverter的WriteJson函数——事实并非如此 我有以下类和接口: public interface IAnimal { string Name { get; set; } string Speak(); List<IAnimal> Children { get; set; } } public class Cat : IAnimal {
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";
}
}
在这个例子中,是的,狗可以给孩子养猫,反之亦然。在转换器中,我想插入“type”属性,以便将其保存到序列化中。我有以下设置。(动物园只有一个名字和一个动物列表。为了简洁和懒惰,我没有把它放在这里;)
type属性显示在Ruff、Cleo和Rover上,但不显示在Fido和Fluffy上。我想WriteJson不是递归调用的。如何在那里获取该类型属性
顺便说一句,为什么它不像我期望的那样使用camel case 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:这里有一个想法,不是对每个属性进行反射,而是迭代通常序列化的JObject,然后更改您感兴趣的属性的标记 这样,您仍然可以利用所有的“JsonIgnore”属性和其他有吸引力的内置功能
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JToken jToken = JToken.FromObject(value);
if (jToken.Type == JTokenType.Object)
{
JObject jObject = (JObject)jToken;
...
AddRemoveSerializedProperties(jObject, val);
...
}
...
}
然后
private void AddRemoveSerializedProperties(JObject jObject, MahMan baseContract)
{
jObject.AddFirst(....);
foreach (KeyValuePair<string, JToken> propertyJToken in jObject)
{
if (propertyJToken.Value.Type != JTokenType.Object)
continue;
JToken nestedJObject = propertyJToken.Value;
PropertyInfo clrProperty = baseContract.GetType().GetProperty(propertyJToken.Key);
MahMan nestedObjectValue = clrProperty.GetValue(baseContract) as MahMan;
if(nestedObj != null)
AddRemoveSerializedProperties((JObject)nestedJObject, nestedObjectValue);
}
}
private void AddRemoveSerializedProperties(JObject JObject,基于MahMan的合同)
{
jObject.AddFirst(…);
foreach(jObject中记录的KeyValuePair属性)
{
if(propertyJToken.Value.Type!=JTokenType.Object)
继续;
JToken nestedJObject=propertyJToken.Value;
PropertyInfo clrProperty=baseContract.GetType().GetProperty(propertyJToken.Key);
MahMan nestedObjectValue=clrProperty.GetValue(baseContract)作为MahMan;
if(nestedObj!=null)
AddRemoveSerializedProperties((JObject)nestedJObject,nestedObjectValue);
}
}
对于父类型和子类型,我使用两个自定义转换器时遇到了这个问题。我发现的一个更简单的方法是,由于JToken.FromObject()
的重载将序列化程序作为参数,因此可以传递WriteJson()中给出的序列化程序。但是,您需要从序列化程序中删除转换器,以避免对其进行递归调用(但在之后将其添加回):
但值得注意的是,这可能会导致忽略其他基于序列化的属性。例如,如果您将[JsonIgnore]放在属性上,它仍然会与上面的代码一起添加。这里有类似的问题吗?您能提供帮助吗?关于实现此功能的方法的任何更新都尊重这些属性吗?jObject来自何方?嗨,李,很抱歉,如果jToken类型为“Object”,那么jToken可以转换为jObject。我添加了缺少的行,如果您有任何其他问题,那么这是一种糟糕的做法:1。这不是安全威胁2。它将无法以与此父节点相同的方式序列化相同类型的子节点(因为缺少当前序列化程序)3。如果FormObject失败,则seralizer的状态已更改4。如果没有失败,应用转换器的顺序可能已经改变。
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);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JToken jToken = JToken.FromObject(value);
if (jToken.Type == JTokenType.Object)
{
JObject jObject = (JObject)jToken;
...
AddRemoveSerializedProperties(jObject, val);
...
}
...
}
private void AddRemoveSerializedProperties(JObject jObject, MahMan baseContract)
{
jObject.AddFirst(....);
foreach (KeyValuePair<string, JToken> propertyJToken in jObject)
{
if (propertyJToken.Value.Type != JTokenType.Object)
continue;
JToken nestedJObject = propertyJToken.Value;
PropertyInfo clrProperty = baseContract.GetType().GetProperty(propertyJToken.Key);
MahMan nestedObjectValue = clrProperty.GetValue(baseContract) as MahMan;
if(nestedObj != null)
AddRemoveSerializedProperties((JObject)nestedJObject, nestedObjectValue);
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Converters.Remove(this);
JToken jToken = JToken.FromObject(value, serializer);
serializer.Converters.Add(this);
// Perform any necessary conversions on the object returned
}