How JSON deserialization works in C #

I am trying to understand how JsonConvert.DeserializeObject<X>(someJsonString) can set values ​​using a 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 the above code, I want to understand how JsonConvert.DeserializeObject is able to deserialize it correctly. Does json serialize use this public X(string someProperty, string someProperty2) constructor public X(string someProperty, string someProperty2) ? If so, what is the name and use of this constructor?

What happens is that parseThisJson contains more value key pairs in addition to some_Property and some_Property_2?

+6
source share
1 answer

Having rummaged in Newtonsoft.Json sources, I can tell you the algorithm for creating an instance of the object that is used there. And yes, the constructor is almost always called (*). The only question is, "which?" Here is a colorful version of the answer:

construction collector

TL DR First of all, Newtonsoft.Json creates a JsonContract the type you are about to deserialize. This is an abstract class. And it has different implementations for dictionaries, arrays, objects, etc. In your case, a JsonObjectContract will be created. The contract contains various metadata about the deserialized type. The most interesting for us are:

  • IsInstantiable - determines if the deserialized type is available (see below)
  • Properties is a set of properties of an object.
  • DefaultCreator - The DefaultCreator creation method used to create the Func<object>
  • DefaultCreatorNonPublic - Determines whether the default constructor is open.
  • OverrideCreator - non-default creator used if <<28> is applied to the constructor of the object
  • ParametrizedCreator - the creator that calls the parameterized constructor is used if we have no default or overriding creators
  • CreatorParameters - a collection of properties that are used to override the creator or creator of the parameterization
  • MemberSerialization - This value determines how properties and fields are serialized. The default value is OptOut - that is, all public members are serialized. If you want to exclude some, you should use the JsonIgnore attribute. But there is also the Fields option, which says that all public and private fields should be serialized. There are several options for enabling this option. But by default it was disabled.

Some of this metadata can be obtained by reflecting type metadata. For instance. IsInstantiable computed by checking if the deserialized type is not abstract, not an interface. Some metadata is added by DefaultContractResolver . In particular, it determines the method of constructing an object. 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, prioirty is sent to the constructor marked with the JsonConstructorAttribute attribute. You will also get an error if there is more than one such constructor.

(*) The following is the only case where an object can be created without calling the constructor. For instance. if you mark a class with the [JsonObject(MemberSerialization = MemberSerialization.Fields)] attribute for serializing private fields.

Then we check if we have a standard constructor without parameters, which is not private. If so, then we go for another constructor that has parameters and should be publicly available. If there is more than one such constructor, you will also get an error.

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

+7
source

Source: https://habr.com/ru/post/1014450/


All Articles