C#反序列化派生并具有引用的对象

时间:2017-03-04 17:50:43

标签: c# json serialization json.net

我有一个Type Node对象。 Node.cs

当我按以下方式拨打电话时序列化有效:

var nodeSer = JsonConvert.SerializeObject(mynode, Formatting.Indented,
   new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects });

我的问题是以下调用不起作用。

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."

如何设计反序列化调用?

1 个答案:

答案 0 :(得分:2)

Json.NET不会序列化事件,因此PropertyChangedBase存储库的HousePlan基类型中的public event PropertyChangedEventHandler PropertyChanged不应在(反)序列化期间导致问题。

但是,该存储库中至少有一种类型具有System.Action委托,而不是在值发生变化时要处理的事件,特别是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;
}

不清楚为什么代理而不是事件被用于此目的,但Json.NET无法对System.Action进行反序列化。实际上,序列化和反序列化这些委托是没有意义的,因为它们是在Node的构造函数中分配的:

public class Node: DiagramObject
{
    public Node()
    {
        Size.ValueChanged = RecalculateSnaps;
        Location.ValueChanged = RecalculateSnaps;
    }

一个简单的解决方案是使用[JsonIgnore]

标记这些属性
    [JsonIgnore]
    public Action ValueChanged;

第二个简单的解决方案是用适当的事件替换委托,Json.NET现在将忽略该事件:

    public event EventHandler ValueChanged;

如果由于某种原因您无法更改这些类型,您可以创建一个custom ContractResolver来自动忽略所有委托类型属性:

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

现在使用以下设置进行序列化:

var settings = new JsonSerializerSettings
{
    PreserveReferencesHandling = PreserveReferencesHandling.Objects,
    ContractResolver = IgnorePropertiesOfTypeContractResolver<System.Delegate>.Instance,
};

ValueChanged属性将不再被序列化或反序列化。