Json.Net反序列化构造函数与属性规则 [英] Json.Net Deserialization Constructor vs. Property Rules

查看:150
本文介绍了Json.Net反序列化构造函数与属性规则的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Json.Net来解决以下类的(反序列化)问题:

I was troubleshooting a (de)serialization issue with the following class using 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"的名称不同.使名称匹配(甚至忽略大小写)可以解决此问题.

The problem is that the constructor argument "withdrawDate" is named differently than the property name "WithDrawlDate". Making the names match (even ignoring case) fixed the issue.

但是,我想更好地理解这一点,因此在将两个设置者都公开之后,我还原了代码并进行了测试.这也解决了这个问题.

However, I wanted to understand this a little better, so I reverted the code and tested after making both the setters public. This also fixed the problem.

最后,我从自动属性切换为具有后备字段的属性,以便可以完全调试并查看实际情况:

Finally, I switched from auto-properties to properties with backing fields so that I could fully debug and see what was actually going on:

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;
    }
}

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

I tried this with and without a default constructor (code shown omits the default constructor).

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

With the default constructor: default constructor is called, then both property setters are called.

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

Without the default constructor: non-default constructor is called, then WithDrawlDate setter is called. NumberOfCoinsByType setter is never called.

我最好的猜测是,解串器正在跟踪可以通过构造函数设置哪些属性(按照惯例,由于似乎忽略了大小写),然后在可能的情况下使用属性设置器来填充空白.

My best guess is that the deserializer is keeping track of which properties can be set via the constructor (by some convention, since casing seems to be ignored), and then uses property setters where possible to fill in the gaps.

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

Is this the way it works? Are the order of operations/rules for deserialization documented somewhere?

推荐答案

我的最佳猜测是,反序列化器一直在跟踪可以通过构造函数设置哪些属性(根据惯例,由于似乎忽略了大小写),然后在可能的情况下使用属性设置器. 是这样的吗?

My best guess is that the deserializer is keeping track of which properties can be set via the constructor (by some convention, since casing seems to be ignored), and then uses property setters where possible to fill in the gaps. Is this the way it works?

是的,这就是要点.如果您查看源代码,您会发现自己.在JsonSerializerInternalReader类中,有一种方法

Yes, that's pretty much the gist. If you take a look at the source code you can see for yourself. In the JsonSerializerInternalReader class there is a method CreateObjectUsingCreatorWithParameters which handles the instantiation of objects using a non-default constructor. I've copied the relevant bits below.

ResolvePropertyAndCreatorValues方法从JSON获取数据值,然后循环尝试将其与构造函数参数匹配.与 1 不匹配的那些被添加到remainingPropertyValues词典中.然后使用匹配的参数实例化该对象,并使用空值/默认值填充所有间隙.该方法后面的第二个循环(此处未显示)随后尝试为该字典中的其余属性调用对象上的setter.

The ResolvePropertyAndCreatorValues method grabs the data values from the JSON, then the loop tries to match them up to the constructor parameters. Those that don't match1 are added to the remainingPropertyValues dictionary. The object is then instantiated using the matched parameters, with null/default values used to fill any gaps. A second loop later in the method (not shown here) then attempts to call setters on the object for the remaining properties in this dictionary.

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 参数匹配算法本质上是不区分大小写的搜索,如果找到多个匹配项,则回落为区分大小写.看看 实用程序方法.

1The parameter matching algorithm is essentially a case insensitive search that falls back to being case sensitive if there are multiple matches found. Take a look at the ForgivingCaseSensitiveFind utility method if you're interested.

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

Are the order of operations/rules for deserialization documented somewhere?

据我所知.官方文档位于此处,但它不涉及这一详细级别

Not to my knowledge. Official documentation is here, but it does not go into this level of detail.

这篇关于Json.Net反序列化构造函数与属性规则的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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