【问题标题】:Json.Net Deserialization Constructor vs. Property RulesJson.Net 反序列化构造函数与属性规则
【发布时间】:2015-10-13 16:21:44
【问题描述】:

我正在使用 Json.Net 对以下类的(反)序列化问题进行故障排除:

public class CoinsWithdrawn
{
    public DateTimeOffset WithdrawlDate { get; private set; }
    public Dictionary<CoinType, int> NumberOfCoinsByType { get; private set; }

    public CoinsWithdrawn(DateTimeOffset withdrawDate, Dictionary<CoinType, int> numberOfCoinsByType)
    {
        WithdrawlDate = withdrawDate;
        NumberOfCoinsByType = numberOfCoinsByType;
    }
}

问题在于构造函数参数“withdrawDate”的命名与属性名称“WithDrawlDate”不同。使名称匹配(即使忽略大小写)解决了这个问题。

但是,我想更好地理解这一点,所以我在公开两个 setter 后恢复了代码并进行了测试。这也解决了问题。

最后,我从自动属性切换到带有支持字段的属性,以便完全调试并查看实际发生的情况:

public class CoinsWithdrawn
{
    private DateTimeOffset _withdrawlDate;
    private Dictionary<CoinType, int> _numberOfCoinsByType;

    public DateTimeOffset WithdrawlDate
    {
        get { return _withdrawlDate; }
        set { _withdrawlDate = value; }
    }

    public Dictionary<CoinType, int> NumberOfCoinsByType
    {
        get { return _numberOfCoinsByType; }
        set { _numberOfCoinsByType = value; }
    }

    public CoinsWithdrawn(DateTimeOffset withdrawDate, Dictionary<CoinType, int> numberOfCoinsByType)
    {
        WithdrawlDate = withdrawDate;
        NumberOfCoinsByType = numberOfCoinsByType;
    }
}

我尝试了使用和不使用默认构造函数(显示的代码省略了默认构造函数)。

使用默认构造函数:调用默认构造函数,然后调用两个属性设置器。

没有默认构造函数:调用非默认构造函数,然后调用WithDrawlDate setter。永远不会调用 NumberOfCoinsByType 设置器。

我最好的猜测是,反序列化器会跟踪可以通过构造函数设置哪些属性(按照某种约定,因为似乎忽略了大小写),然后在可能的情况下使用属性设置器来填补空白。

这是它的工作方式吗?反序列化的操作顺序/规则是否记录在某处?

【问题讨论】:

    标签: c# json serialization json.net


    【解决方案1】:

    我最好的猜测是反序列化器会跟踪可以通过构造函数设置哪些属性(按照某种约定,因为似乎忽略了大小写),然后在可能的情况下使用属性设置器来填补空白。 这就是它的工作方式吗?

    是的,这就是要点。如果您查看source code,您可以自己查看。在JsonSerializerInternalReader 类中有一个方法CreateObjectUsingCreatorWithParameters 使用非默认构造函数处理对象的实例化。我已经复制了下面的相关部分。

    ResolvePropertyAndCreatorValues 方法从 JSON 中获取数据值,然后循环尝试将它们与构造函数参数匹配。那些不匹配的1 被添加到remainingPropertyValues 字典中。然后使用匹配的参数实例化该对象,使用空值/默认值来填补任何空白。稍后在该方法中的第二个循环(此处未显示)然后尝试为该字典中的其余属性调用对象上的设置器。

    IDictionary<JsonProperty, object> propertyValues = 
        ResolvePropertyAndCreatorValues(contract, containerProperty, reader, objectType, out extensionData);
    
    object[] creatorParameterValues = new object[contract.CreatorParameters.Count];
    IDictionary<JsonProperty, object> remainingPropertyValues = new Dictionary<JsonProperty, object>();
    
    foreach (KeyValuePair<JsonProperty, object> propertyValue in propertyValues)
    {
        JsonProperty property = propertyValue.Key;
    
        JsonProperty matchingCreatorParameter;
        if (contract.CreatorParameters.Contains(property))
        {
            matchingCreatorParameter = property;
        }
        else
        {
            // check to see if a parameter with the same name as the underlying property name exists and match to that
            matchingCreatorParameter = contract.CreatorParameters.ForgivingCaseSensitiveFind(p => p.PropertyName, property.UnderlyingName);
        }
    
        if (matchingCreatorParameter != null)
        {
            int i = contract.CreatorParameters.IndexOf(matchingCreatorParameter);
            creatorParameterValues[i] = propertyValue.Value;
        }
        else
        {
            remainingPropertyValues.Add(propertyValue);
        }
    
        ...
    } 
    ...
    
    object createdObject = creator(creatorParameterValues);
    
    ...
    

    1参数匹配算法本质上是一种不区分大小写的搜索,如果找到多个匹配项,则返回到区分大小写。如果您有兴趣,请查看ForgivingCaseSensitiveFind 实用方法。

    是否在某处记录了反序列化的操作顺序/规则?

    据我所知。官方文档是here,但没有详细介绍。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-05-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-08
      • 1970-01-01
      相关资源
      最近更新 更多