JSON.net: how to deserialize without using the default constructor?

I have a class that has a default constructor, as well as an overloaded constructor that takes a set of parameters. These parameters correspond to the fields of the object and are assigned during construction. At the moment, I need a default constructor for other purposes, so I would like to save it if I can.

My problem: if I delete the default constructor and pass the JSON string, the object is deserialized correctly and passes the constructor parameters without any problems. In the end, I return to the object filled as I expected. However, as soon as I add a default constructor to the object, when I call JsonConvert.DeserializeObject<Result>(jsontext) , the properties no longer populate.

At this point, I tried to add new JsonSerializerSettings(){CheckAdditionalContent = true} to the deserialization call. that did nothing.

One more note. constructor parameters exactly match field names, except that parameters begin with a lowercase letter. I would not think that it would matter, because, as I mentioned, deserialization works fine without a default constructor.

Here is an example of my constructors:

  public Result() { } public Result(int? code, string format, Dictionary<string, string> details = null) { Code = code ?? ERROR_CODE; Format = format; if (details == null) Details = new Dictionary<string, string>(); else Details = details; } 
+114
json c #
Apr 11 '14 at 16:16
source share
5 answers

Json.Net prefers to use the default constructor (without parameters) for the object, if any. If there are several constructors and you want Json.Net to use a non-standard one, you can add the [JsonConstructor] attribute to the constructor that you want to call Json.Net.

 [JsonConstructor] public Result(int? code, string format, Dictionary<string, string> details = null) { ... } 

It is important that the constructor parameter names match the corresponding JSON object property names (ignoring the case) so that this works correctly. However, it is not necessary to have a constructor parameter for each property of the object. For those properties of the JSON object that are not covered by constructor parameters, Json.Net will try to use public accessors (or properties / fields marked with [JsonProperty] ) to populate the object after its creation.

If you do not want to add attributes to your class or otherwise do not control the source code for the class you are trying to deserialize, then another alternative is to create a custom JsonConverter to instantiate and populate your object. For example:

 class ResultConverter : JsonConverter { public override bool CanConvert(Type objectType) { return (objectType == typeof(Result)); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { // Load the JSON for the Result into a JObject JObject jo = JObject.Load(reader); // Read the properties which will be used as constructor parameters int? code = (int?)jo["Code"]; string format = (string)jo["Format"]; // Construct the Result object using the non-default constructor Result result = new Result(code, format); // (If anything else needs to be populated on the result object, do that here) // Return the result return result; } public override bool CanWrite { get { return false; } } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } } 

Then add the converter to your serializer settings and use the settings when deserializing:

 JsonSerializerSettings settings = new JsonSerializerSettings(); settings.Converters.Add(new ResultConverter()); Result result = JsonConvert.DeserializeObject<Result>(jsontext, settings); 
+174
Apr 11 '14 at 16:26
source share
— -

It’s a bit late here and doesn’t quite fit here, but I am going to add my solution here because my question was closed as a duplicate of this, and because this solution is completely different.

I need a general way to instruct Json.NET to prefer the most specific constructor for a user-defined type of structure, so I can omit the JsonConstructor attributes that would add a dependency to the project where each such structure is defined.

I changed the design a bit and implemented a custom contract resolver, where I redefined the CreateObjectContract method to add my own creation logic.

 public class CustomContractResolver : DefaultContractResolver { protected override JsonObjectContract CreateObjectContract(Type objectType) { var c = base.CreateObjectContract(objectType); if (!IsCustomStruct(objectType)) return c; IList<ConstructorInfo> list = objectType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).OrderBy(e => e.GetParameters().Length).ToList(); var mostSpecific = list.LastOrDefault(); if (mostSpecific != null) { c.OverrideCreator = CreateParameterizedConstructor(mostSpecific); c.CreatorParameters.AddRange(CreateConstructorParameters(mostSpecific, c.Properties)); } return c; } protected virtual bool IsCustomStruct(Type objectType) { return objectType.IsValueType && !objectType.IsPrimitive && !objectType.IsEnum && !objectType.Namespace.IsNullOrEmpty() && !objectType.Namespace.StartsWith("System."); } private ObjectConstructor<object> CreateParameterizedConstructor(MethodBase method) { method.ThrowIfNull("method"); var c = method as ConstructorInfo; if (c != null) return a => c.Invoke(a); return a => method.Invoke(null, a); } } 

I use it like this.

 public struct Test { public readonly int A; public readonly string B; public Test(int a, string b) { A = a; B = b; } } var json = JsonConvert.SerializeObject(new Test(1, "Test"), new JsonSerializerSettings { ContractResolver = new CustomContractResolver() }); var t = JsonConvert.DeserializeObject<Test>(json); tAShouldEqual(1); tBShouldEqual("Test"); 
+32
Mar 08 '16 at 10:35
source share

Based on some answers here, I wrote a CustomConstructorResolver for use in the current project, and I thought this might help someone else.

It supports the following resolution mechanisms, all customizable:

  • Select one private constructor so that you can define one private constructor without having to mark it with an attribute.
  • Choose the most specific private constructor so that you can have multiple overloads without using attributes.
  • Choose a constructor marked with an attribute with a specific name - for example, the default recognizer, but without depending on the Json.Net package, since you need to reference Newtonsoft.Json.JsonConstructorAttribute .
 public class CustomConstructorResolver : DefaultContractResolver { public string ConstructorAttributeName { get; set; } = "JsonConstructorAttribute"; public bool IgnoreAttributeConstructor { get; set; } = false; public bool IgnoreSinglePrivateConstructor { get; set; } = false; public bool IgnoreMostSpecificConstructor { get; set; } = false; protected override JsonObjectContract CreateObjectContract(Type objectType) { var contract = base.CreateObjectContract(objectType); // Use default contract for non-object types. if (objectType.IsPrimitive || objectType.IsEnum) return contract; // Look for constructor with attribute first, then single private, then most specific. var overrideConstructor = (this.IgnoreAttributeConstructor ? null : GetAttributeConstructor(objectType)) ?? (this.IgnoreSinglePrivateConstructor ? null : GetSinglePrivateConstructor(objectType)) ?? (this.IgnoreMostSpecificConstructor ? null : GetMostSpecificConstructor(objectType)); // Set override constructor if found, otherwise use default contract. if (overrideConstructor != null) { SetOverrideCreator(contract, overrideConstructor); } return contract; } private void SetOverrideCreator(JsonObjectContract contract, ConstructorInfo attributeConstructor) { contract.OverrideCreator = CreateParameterizedConstructor(attributeConstructor); contract.CreatorParameters.Clear(); foreach (var constructorParameter in base.CreateConstructorParameters(attributeConstructor, contract.Properties)) { contract.CreatorParameters.Add(constructorParameter); } } private ObjectConstructor<object> CreateParameterizedConstructor(MethodBase method) { var c = method as ConstructorInfo; if (c != null) return a => c.Invoke(a); return a => method.Invoke(null, a); } protected virtual ConstructorInfo GetAttributeConstructor(Type objectType) { var constructors = objectType .GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) .Where(c => c.GetCustomAttributes().Any(a => a.GetType().Name == this.ConstructorAttributeName)).ToList(); if (constructors.Count == 1) return constructors[0]; if (constructors.Count > 1) throw new JsonException($"Multiple constructors with a {this.ConstructorAttributeName}."); return null; } protected virtual ConstructorInfo GetSinglePrivateConstructor(Type objectType) { var constructors = objectType .GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic); return constructors.Length == 1 ? constructors[0] : null; } protected virtual ConstructorInfo GetMostSpecificConstructor(Type objectType) { var constructors = objectType .GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) .OrderBy(e => e.GetParameters().Length); var mostSpecific = constructors.LastOrDefault(); return mostSpecific; } } 

Here is the full version with XML documentation in the form of an entity: https://gist.github.com/maverickelementalch/80f77f4b6bdce3b434b0f7a1d06baa95

Feedback is welcome.

+1
Feb 14 '19 at 19:11
source share

The default behavior in Newtonsoft.Json will look for public constructors. If your default constructor is used only in the containing class or the same assembly, you can reduce the access level to protected or internal so that Newtonsoft.Json selects the desired public constructor.

Admittedly, this decision is very limited to specific cases.

  internal Result() { } public Result(int? code, string format, Dictionary<string, string> details = null) { Code = code ?? ERROR_CODE; Format = format; if (details == null) Details = new Dictionary<string, string>(); else Details = details; } 
0
Jul 08 '19 at 14:59
source share

Decision:

 public Response Get(string jsonData) { var json = JsonConvert.DeserializeObject<modelname>(jsonData); var data = StoredProcedure.procedureName(json.Parameter, json.Parameter, json.Parameter, json.Parameter); return data; } 

Model:

 public class modelname { public long parameter{ get; set; } public int parameter{ get; set; } public int parameter{ get; set; } public string parameter{ get; set; } } 
0
Jul 30 '19 at 12:08
source share



All Articles