使用delegate类型的字段反序列化类

时间:2015-06-17 21:28:51

标签: c# serialization delegates json.net

我有一个类,其中一个字段是我想要序列化和反序列化的委托。

看起来像这样:

public delegate bool DistanceEqualityStrategy (Distance distance1, Distance distance2);

[JsonObject(MemberSerialization.Fields)]
public partial class Distance
{

    private double _intrinsicValue;

    [JsonIgnore]
    private DistanceEqualityStrategy _strategy;

    public Distance(double passedInput, DistanceEqualityStrategy passedStrategy = null)
    {
        _intrinsicValue = passedInput;
        _equalityStrategy = _chooseDefaultOrPassedStrategy(passedStrategy);
    }

    public Distance(Distance passedDistance)
    {
        _intrinsicValue = passedDistance._intrinsicValue;
        _equalityStrategy = passedDistance._equalityStrategy;
    }

    private static DistanceEqualityStrategy _chooseDefaultOrPassedStrategy(DistanceEqualityStrategy passedStrategy)
    {
        if (passedStrategy == null)
        {
            return EqualityStrategyImplementations.DefaultConstantEquality;
        }
        else
        {
            return passedStrategy;
        }
    }
}

我无法理解反序列化此对象时的实际情况。如何重建对象?它在尝试重新创建对象时是否调用该类的特定构造函数?它序列化很好,但是当重建对象时,它将委托设置为null。

这使我得出结论,我不明白这种反序列化是如何工作的,因为它似乎没有使用构造函数。

有人可以向我解释如何在不使用构造函数的情况下在反序列化时创建对象吗?

2 个答案:

答案 0 :(得分:1)

你问,有人可以向我解释如何在不使用构造函数的情况下在反序列化时创建对象吗?

简短的回答是,FormatterServices.GetUninitializedObject()可以用来做这件事。

问题中显示的POCO类型标有[JsonObject(MemberSerialization.Fields)]。将MemberSerialization.Fields应用于maps to a JSON object触发特殊构造规则的POCO类型。 Json.NET如何决定如何构造这种类型的实例的规则如下:

  1. 如果在构造函数上设置了[JsonConstructor],请使用该构造函数。

  2. 接下来,在full trust only中,当应用 MemberSerialization.Fields 时,或 [Serializable]已应用且DefaultContractResolver.IgnoreSerializableAttribute == false ,特殊方法FormatterServices.GetUninitializedObject()用于分配对象。 不调用任何类型的构造函数。

  3. 接下来,如果有一个公共无参数构造函数,请使用它。

  4. 接下来,如果有非公共无参数构造函数和JsonSerializerSettings.ConstructorHandling == ConstructorHandling.AllowNonPublicDefaultConstructor,请使用它。

  5. 接下来,如果有一个单个公共参数化构造函数,请使用该构造函数,将JSON对象属性与名称(模数字大小写)的构造函数参数匹配,然后反序列化匹配的属性构造函数参数类型。当没有匹配的JSON对象属性时传递默认值。

  6. 如果不能完成上述所有操作,则无法构造对象,并且在反序列化期间将抛出异常。

  7. 请参阅the reference source了解逻辑。 Dictionariesdynamic objects,实现ISerializable的类型,collections(映射到JSON数组的类型)和映射到JSON基元的类型可能有一些不同的规则。

    因此调用FormatterServices.GetUninitializedObject()而不是类型的构造函数,因此它的_equalityStrategy永远不会被初始化。

    作为一种解决方法,由于您还没有序列化_equalityStrategy,因此您可以添加初始化它的OnDeserialized callback

    [JsonObject(MemberSerialization.Fields)]
    public partial class Distance
    {
        [JsonIgnore]
        private DistanceEqualityStrategy _equalityStrategy;
    
        [OnDeserialized]
        void OnDeserialized(StreamingContext context)
        {
            this._equalityStrategy = _chooseDefaultOrPassedStrategy(this._equalityStrategy);
        }
    }
    

    Json.NET的这种行为没有明确记录。最相关的段落来自Serialization Guide

      

    最后,可以使用字段模式序列化类型。所有字段(包括公共字段和私有字段)都是序列化的,属性将被忽略。这可以通过在带有JsonObjectAttribute的类型上设置MemberSerialization.Fields或使用.NET SerializableAttribute并将DefaultContractResolver上的IgnoreSerializableAttribute设置为false来指定。

    来自Json.NET 4.5 Release 8 release notes

      

    更改 - 序列化程序现在在反序列化Serializable类型时使用GetUninitializedObject创建对象。

    事实上,GetUninitializedObject()用于“字段模式”,可能是为了模仿DataContractJsonSerializer的行为。如果你愿意,你可以report a documentation issue

答案 1 :(得分:0)

我认为代表们与您的问题无关,这似乎是:

  

我可以提供自定义逻辑来设置标有JsonIgnore属性的字段吗? (因为它们不包含在序列化流中)

不幸的是,JsonIgnoreAttribute文档非常缺乏,并没有解释这一点。也许它被其他地方所覆盖。

至于关于使用什么构造函数的问题,它似乎由JsonSerializer实例上的ConstructorHandling属性控制。

CustomCreationConverter class听起来也可能是相关的,因为它被描述为"提供了一种在JSON反序列化期间自定义对象创建方式的方法。