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

标签 c# json serialization json.net

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

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

最佳答案

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

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

不清楚为什么使用委托(delegate)而不是事件来实现此目的,但是 System.Action 不能被 Json.NET 反序列化。事实上,序列化和反序列化这些委托(delegate)是没有意义的,因为它们是在 Node 的构造函数中分配的。 :

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

一个简单的解决方案是用 [JsonIgnore] 标记这些属性

    [JsonIgnore]
    public Action ValueChanged;

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

    public event EventHandler ValueChanged;

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

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 属性将不再被序列化或反序列化。

关于C#反序列化派生并具有引用的对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42599259/

相关文章:

java - 带有 @FormParam 的 PUT 方法

json - 是否可以使用 $resource.save 将数据保存到本地磁盘上的 json 文件而不使用任何服务器端实现?

java - 如何在java中通过引用复制和反序列化对象列表?

java - 反序列化问题

.net - 反序列化时如何从json中删除k__BackingField

c# - 如何根据 NEST Elasticsearch 中的命中分数对结果进行排序

c# - 当我调用 ThrowIfCancellationRequested() 时,OperationCanceledException 未被用户代码处理

c# - ASP.NET 中的灵活 ACL

c# - 如何使用php将UWP中的图像保存到MYsql中

asp.net - 基于 Asp Web 应用程序创建电话应用程序