C#反序列化一个派生并具有引用的对象 [英] C# Deserialize an object which is derived and has references

查看:119
本文介绍了C#反序列化一个派生并具有引用的对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个类型为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不会序列化事件,因此

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆