C# 中的 JSON 反序列化如何工作 [英] How does JSON deserialization in C# work

查看:14
本文介绍了C# 中的 JSON 反序列化如何工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图了解 JsonConvert.DeserializeObject<X>(someJsonString) 如何使用构造函数设置值.

使用 Newtonsoft.json公共课 X {[JsonProperty("some_Property")]公共字符串 SomeProperty {get;}[JsonProperty("some_Property_2")]公共字符串 SomeProperty2 {get;}公共 X(字符串 someProperty,字符串 someProperty2){某些属性 = 某些属性;SomeProperty2 = someProperty2;}公共静态 X parseObject(字符串 parseThisJson){JsonConvert.DeserializeObject(someJsonString);}}

在上面的代码中,我想了解 JsonConvert.DeserializeObject 是如何正确反序列化它的.json 序列化是否使用此 public X(string someProperty, string someProperty2) 构造函数?如果是这样,这个构造函数是如何调用和使用的?

如果 parseThisJson 除了 some_Property 和 some_Property_2 之外还有更多的键值对会发生什么?

解决方案

深入

TL;DR 首先,Newtonsoft.Json 创建 JsonContract.它的抽象类.它对字典、数组、对象等有不同的实现.在您的情况下,将创建 JsonObjectContract.合约包含关于反序列化类型的各种元数据.对我们来说最有趣的是:

  • IsInstantiable - 定义反序列化类型是否可实例化(见下文)
  • Properties - 它的对象属性集合
  • DefaultCreator - 用于创建对象的默认创建方法 Func
  • DefaultCreatorNonPublic - 定义默认构造函数是否非公开
  • OverrideCreator - 非默认创建者,在 JsonConstructorAttribute 应用于对象的构造函数时使用
  • ParametrizedCreator - 调用参数化构造函数的创建者,如果我们既没有默认创建者也没有覆盖创建者,则使用它
  • CreatorParameters - 用于覆盖创建者或参数化创建者的属性集合
  • MemberSerialization - 这个值定义属性和字段的序列化方式.默认情况下,它设置为 OptOut - 即所有公共成员都被序列化.如果你想排除一些,你应该使用 JsonIgnore 属性.但是也有 Fields 选项,它表示所有公共 和私有 字段都应该被序列化.有几个可以打开此选项.但默认情况下它是禁用的.
  • 可以通过反映类型元数据来检索其中一些元数据.例如.IsInstantiable 是通过检查反序列化类型是否非抽象非接口来计算的.DefaultContractResolver 添加了一些元数据.特别是,它定义了对象的构造方式.在伪代码中:

    if (contract.IsInstantiable){if(类型具有默认构造函数或其值类型){contract.DefaultCreator = 获取默认(无参数)构造函数;contract.DefaultCreatorNonPublic = 检查默认构造函数是否公开}if(我们有用 JsonConstructorAttribute 标记的构造函数){contract.OverrideCreator = 带有属性标记的构造函数contract.CreatorParameters = 获取与构造函数参数匹配的属性}else if (contract.MemberSerialization == MemberSerialization.Fields){//仅当复制完全受信任时contract.DefaultCreator = FormatterServices.GetUninitializedObject}else if (contract.DefaultCreator == null || contract.DefaultCreatorNonPublic){if(我们有一个带参数的公共构造函数){contract.ParametrizedCreator = 带参数的构造函数;contract.CreatorParameters = 获取匹配 ctor 参数的属性}}}

    所以,正如您所见,优先级转到标有 JsonConstructorAttribute 属性的构造函数.如果有多个这样的构造函数,你也会得到错误.

    (*) 接下来是唯一可以在不调用构造函数的情况下创建对象的情况.例如.如果您将使用 [JsonObject(MemberSerialization = MemberSerialization.Fields)] 属性标记类以序列化私有字段.

    然后我们检查我们是否有默认的非私有无参数构造函数.如果是这样,那么我们选择其他构造函数 - 一个具有参数并且应该是公共的.如果这样的构造函数不止一个,也会报错.

    最后要注意的是 - CreatorParameters.Newtonsoft.Json 使用反射来获取构造函数参数,然后尝试通过这些构造函数参数的名称找到与对象属性最接近的匹配项.它还检查要匹配的属性和参数的类型.如果未找到匹配项,则将默认值传递给此参数化构造函数.

    I am trying to understand how JsonConvert.DeserializeObject<X>(someJsonString) is able to set the values by using the constructor.

    using Newtonsoft.json
    
    public class X {
    
        [JsonProperty("some_Property")]
        public string SomeProperty {get;}
    
        [JsonProperty("some_Property_2")]
        public string SomeProperty2 {get;}
    
        public X(string someProperty, string someProperty2) {
            SomeProperty = someProperty;
            SomeProperty2 = someProperty2;
        }
    
        public static X parseObject(string parseThisJson) {
          JsonConvert.DeserializeObject<X>(someJsonString);
        }
    }
    

    In above code I want to understand how is JsonConvert.DeserializeObject is able to deserialize it correctly. Does the json serialize uses this public X(string someProperty, string someProperty2) constructor? If so how is this constructor called and used?

    What will happen is parseThisJson have more key value pairs in addition to some_Property and some_Property_2?

    解决方案

    After digging into Newtonsoft.Json sources I can tell you algorithm of object instantiation which is used there. And yes, constructor is almost always called (*). The question is only "which one?". Here is colorful version of answer:

    TL;DR First of all, Newtonsoft.Json creates JsonContract of the type which you are going to deserialize. Its abstract class. And it has different implementations for dictionaries, arrays, objects etc. In your case JsonObjectContract will be created. Contract contains various metadata about deserialized type. Most interesting for us are:

    • IsInstantiable - defines whether deserialized type is instantiable (see below)
    • Properties - its collection of object properties
    • DefaultCreator - default creation method used to create object Func<object>
    • DefaultCreatorNonPublic - defines whether default constructor is non public
    • OverrideCreator - non-default creator, used if JsonConstructorAttribute is applied to object's constructor
    • ParametrizedCreator - creator which calls paramterized constructor, it is used if we don't have neither default nor override creators
    • CreatorParameters - collection of properties which are used for override creator or parametrized creator
    • MemberSerialization - this value defines the way how properties and fields are serialized. By default it is set to OptOut - i.e. all public members are serialized. If you want to exclude some, you should use JsonIgnore attribute. But there is also Fields option, which says that all public and private fields should be serialized. There is several to turn-on this option. But by default its disabled.

    Some of this metadata can be retrieved by reflecting type metdata. E.g. IsInstantiable is calculated by checking whether deserialized type is not abstract and not interface. Some metadata are added by DefaultContractResolver. In particular, it defines the way how object should be constructed. In pseudo-code:

    if (contract.IsInstantiable)
    {
       if (type has default constructor or its a value type)
       {
           contract.DefaultCreator = get default (parameterless) constructor;
           contract.DefaultCreatorNonPublic = check if default constructor public
       }
    
       if (we have constructor marked with JsonConstructorAttribute)
       {
           contract.OverrideCreator = constructor marked with attribute
           contract.CreatorParameters = get properties which match constructor parameters
       }
       else if (contract.MemberSerialization == MemberSerialization.Fields)
       {
           // only if the upplication if fully trusted
           contract.DefaultCreator = FormatterServices.GetUninitializedObject 
       }
       else if (contract.DefaultCreator == null || contract.DefaultCreatorNonPublic)
       {
             if (we have one public constructor with parameters)
             {
                  contract.ParametrizedCreator = constructor with parameters;
                  contract.CreatorParameters = get properties which match ctor parameters
             }
       }
    }
    

    So, as you can see priority goes to constructor marked with JsonConstructorAttribute attribute. You will also get error if there is more than one such constructor.

    (*) Next goes the only case when object can be created without calling constructor. E.g. if you'll mark class with [JsonObject(MemberSerialization = MemberSerialization.Fields)] attribute to serialize private fields.

    Then we check if we have default parameterless constructor which is not private. If so, then we go for other constructor - one which has parameters and should be public. If there is more than one such constructor, you will also get error.

    And last thing to note - CreatorParameters. Newtonsoft.Json uses reflection to get constructor parameters and then tries to find closest match by name of these constructor parameters to object's properties. It also checks type of property and parameters to match. If there is no match found, then default value will be passed to this parameterized constructor.

    这篇关于C# 中的 JSON 反序列化如何工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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