反序列化具有委托类型的字段的类

本文关键字:字段 类型 反序列化 | 更新日期: 2023-09-27 17:56:53

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

它看起来像这样:

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。

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

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

反序列化具有委托类型的字段的类

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

简短的回答是,FormatterServices.GetUninitializedObject()可以使用这样做。

问题中显示的 POCO 类型已标记为 [JsonObject(MemberSerialization.Fields)] 。 将MemberSerialization.Fields应用于映射到 JSON 对象的 POCO 类型会触发特殊的构造规则。 Json.NET 如何决定如何构造此类类型的实例的规则如下:

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

  2. 接下来,仅在完全信任的情况下,当应用MemberSerialization.Fields应用[Serializable]DefaultContractResolver.IgnoreSerializableAttribute == false时,使用特殊方法 FormatterServices.GetUninitializedObject() 来分配对象。 不调用该类型的构造函数。

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

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

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

  6. 如果上述所有操作都失败,则无法构造对象,并且在反序列化期间将引发异常。

有关逻辑,请参阅参考源。 字典、动态对象、实现ISerializable的类型、集合(映射到 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 的这种行为没有明确的记录。 最相关的段落来自序列化指南:

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

从 Json.NET 4.5 第 8 版发行说明中:

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

事实上,GetUninitializedObject()用于"字段模式",可能是为了模拟DataContractJsonSerializer的行为。 如果您愿意,可以报告文档问题。

我认为

代表们与您的问题没有任何关系,该问题似乎是:

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

不幸的是,JsonIgnoreAttribute文档非常稀缺,无法解释这一点。 也许它在其他地方有所涉及。

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

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