C#反序列化一个派生并具有引用的对象 [英] C# Deserialize an object which is derived and has references
问题描述
我有一个类型为Node的对象. Node.cs
I have a object of Type Node. Node.cs
当我拨打电话时,序列化工作如下:
The serialization works when I make the call as following:
var nodeSer = JsonConvert.SerializeObject(mynode, Formatting.Indented,
new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects });
我的问题是以下呼叫无法正常工作.
My problem is that the following call does not work.
var n = JsonConvert.DeserializeObject<Node>(nodeSer,
new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects, TypeNameHandling = TypeNameHandling.Auto });
该调用导致以下错误:
Newtonsoft.Json.JsonSerializationException: "ISerializable type 'System.Action' does not have a valid constructor. To correctly implement ISerializable a constructor that takes SerializationInfo and StreamingContext parameters should be present. Path 'Size.ValueChanged', line 35, position 5."
如何设计反序列化调用?
How do I have to design the the deserialization call?
推荐答案
Json.NET不会序列化事件,因此 HousePlan
存储库在反序列化期间不会引起问题.
Json.NET does not serialize events so the public event PropertyChangedEventHandler PropertyChanged
in the PropertyChangedBase
base type of the HousePlan
repository should not cause problems during (de)serialization.
但是,该存储库中的至少一种类型具有一个System.Action
委托,而不是一个值更改时要处理的事件,特别是
However, at least one of the types in that repository has a System.Action
delegate rather than an event to handle when a value changes, specifically BindablePoint
:
public class BindablePoint: PropertyChangedBase
{
public double X
{
get { return Value.X; }
set { Value = new Point(value, Value.Y); }
}
public double Y
{
get { return Value.Y; }
set { Value = new Point( Value.X, value); }
}
private Point _value;
public Point Value
{
get { return _value; }
set
{
_value = value;
OnPropertyChanged("Value");
OnPropertyChanged("X");
OnPropertyChanged("Y");
if (ValueChanged != null)
ValueChanged();
}
}
// This property is causing problems for Json.NET
public Action ValueChanged;
}
目前尚不清楚为什么为此目的使用委托而不是事件,但是System.Action
不能由Json.NET反序列化.确实,序列化和反序列化这些委托没有任何意义,因为它们在构造器中分配给
It's not clear why a delegate rather than an event is used for this purpose, however System.Action
cannot be deserialized by Json.NET. Indeed, serializing and deserializing these delegates makes no sense since they are assigned in the constructor for Node
:
public class Node: DiagramObject
{
public Node()
{
Size.ValueChanged = RecalculateSnaps;
Location.ValueChanged = RecalculateSnaps;
}
一个简单的解决方案是使用 [JsonIgnore]
One simple solution is to mark these properties with [JsonIgnore]
[JsonIgnore]
public Action ValueChanged;
第二种简单的解决方案是用适当的事件替换委托,而Json.NET现在将忽略该事件:
A second simple solution would be to replace the delegate with a proper event, which Json.NET will now ignore:
public event EventHandler ValueChanged;
如果出于某种原因无法更改这些类型,则可以创建自定义ContractResolver
自动忽略所有委托类型属性:
If for whatever reason you cannot change these types, you can create a custom ContractResolver
that automatically ignores all delegate type properties:
public class IgnorePropertiesOfTypeContractResolver<T> : IgnorePropertiesOfTypeContractResolver
{
// As of 7.0.1, Json.NET suggests using a static instance for "stateless" contract resolvers, for performance reasons.
// http://www.newtonsoft.com/json/help/html/ContractResolver.htm
// http://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Serialization_DefaultContractResolver__ctor_1.htm
// "Use the parameterless constructor and cache instances of the contract resolver within your application for optimal performance."
static IgnorePropertiesOfTypeContractResolver<T> instance;
static IgnorePropertiesOfTypeContractResolver() { instance = new IgnorePropertiesOfTypeContractResolver<T>(); }
public static IgnorePropertiesOfTypeContractResolver<T> Instance { get { return instance; } }
public IgnorePropertiesOfTypeContractResolver() : base(new[] { typeof(T) }) { }
}
/// <summary>
/// Contract resolver to ignore properties of any number of given types.
/// </summary>
public class IgnorePropertiesOfTypeContractResolver : DefaultContractResolver
{
readonly HashSet<Type> toIgnore;
public IgnorePropertiesOfTypeContractResolver(IEnumerable<Type> toIgnore)
{
if (toIgnore == null)
throw new ArgumentNullException();
this.toIgnore = new HashSet<Type>(toIgnore);
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (property.PropertyType.BaseTypesAndSelf().Any(t => toIgnore.Contains(t)))
{
property.Ignored = true;
}
return property;
}
}
public static class TypeExtensions
{
public static IEnumerable<Type> BaseTypesAndSelf(this Type type)
{
while (type != null)
{
yield return type;
type = type.BaseType;
}
}
}
现在使用以下设置进行序列化:
Now serialize with the following settings:
var settings = new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
ContractResolver = IgnorePropertiesOfTypeContractResolver<System.Delegate>.Instance,
};
ValueChanged
属性将不再被序列化或反序列化.
The ValueChanged
property will no longer be serialized or deserialized.
这篇关于C#反序列化一个派生并具有引用的对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!