17

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?

user2358262
  • 257
  • 3
  • 13
  • 2
    you can check that in 1 minute. Set breakpoint in constructor – Sergey Berezovskiy Jan 26 '17 at 09:17
  • Not entirely, this depends on the actual type of serialization/deserialization. XmlSerialization for example *does* use the default-constructor. However this is pointless to the actual question here. – MakePeaceGreatAgain Jan 26 '17 at 09:21
  • 3
    You know that it's open source and you can check it on github right? – mrogal.ski Jan 26 '17 at 09:24
  • The property values not appearing in the constructor will still be set by using PropertyInfo.SetValue(). https://msdn.microsoft.com/en-us/library/system.reflection.propertyinfo.setvalue(v=vs.110).aspx – svinja Jan 26 '17 at 09:34
  • 1
    See [Avoiding default constructors and public property setters](http://stackoverflow.com/a/35247524/3744182) for an explanation of what Json.NET does. Basically, it *does* use the parameterized constructor when that is the only constructor to call, or it is marked with [`[JsonConstructorAttribute]`](http://www.newtonsoft.com/json/help/html/JsonConstructorAttribute.htm), and matches the arguments to the JSON properties by name modulo case. – dbc Jan 26 '17 at 09:42

1 Answers1

27

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 a colorful version of the answer:

picking constructor

TL;DR First of all, Newtonsoft.Json creates a JsonContract of the type which you are going to deserialize. It's 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. Most interesting for us are:

  • IsInstantiable - defines whether the 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.

Anomaly
  • 932
  • 1
  • 10
  • 18
Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
  • 1
    Thank You for the great explanation. I have one question, you said _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_ in this what do you meant by the _closest match by name_ does the property name don't need to be a 100% match? for eg SomeProprty will be considered a match with SomeProperty? – user2358262 Jan 26 '17 at 17:38
  • @user2358262 it first tries to get property with 100% matching name, but if it fails, then it tries to find property ignoring case of name. I believe that is done due to different naming convention. For properties we use PascalCase naming and for parameters camelCase. You can check this code [here](https://github.com/JamesNK/Newtonsoft.Json/blob/cf6a917a46b532558578c53d34cdc4f39ec0560a/Src/Newtonsoft.Json/Serialization/JsonPropertyCollection.cs) – Sergey Berezovskiy Jan 26 '17 at 21:10