一尘不染

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

json

我有一个类型节点的对象。 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."

我必须如何设计反序列化调用?


阅读 230

收藏
2020-07-27

共1个答案

一尘不染

Json.NET不会序列化事件,因此存储库基本类型中的public event PropertyChangedEventHandler PropertyChangedin
(序列化)期间不应引起问题。PropertyChangedBaseHousePlan

但是,该存储库中的至少一种类型具有一个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;
}

目前尚不清楚为什么为此目的使用委托而不是事件,但是System.ActionJson.NET不能反序列化。实际上,序列化和反序列化这些委托没有任何意义,因为它们在构造函数中分配给Node

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

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

    [JsonIgnore]
    public Action ValueChanged;

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

    public event EventHandler ValueChanged;

如果出于某种原因无法更改这些类型,则可以创建一个自定义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属性将不再被序列化或反序列化。

2020-07-27