2

When I use newtosoft.json in netcore 2.1, I come across a problem. When I add a Constructor in my ApiErrorConverter class which inherits from JsonConverter. It's warning me

“No parameterless constructor defined for this object.”

When I remove the Constructor in my ApiErrorConverter class, it will work well. But I want to pass some value into the JsonConverter to implement converting JSON map to C# object flexibly. Here is my code:

Payload

string payload2 ="{\"statusCode\":123,\"name\":\"error_code\",\"description\":\"The Message\",\"item\":{\"ahah\":\"cl\"}}"; 

Model

public class ApiError { [JsonProperty("error")] public string Error { get; set; } [JsonProperty("errorCode")] public string ErrorCode { get; set; } [JsonProperty("message")] public string Message { get; set; } [JsonProperty("statusCode")] public int StatusCode { get; set; } } 

Model converter

public class ApiErrorConverter : JsonConverter { Dictionary<string, string> _propertyMappings = new Dictionary<string, string>(); public ApiErrorConverter(Dictionary<string, string> dataparam) { propertyMappings = dataparam; } public override bool CanWrite => false; public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } public override bool CanConvert(Type objectType) { return objectType.GetTypeInfo().IsClass; } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { object instance = Activator.CreateInstance(objectType); var props = objectType.GetTypeInfo().DeclaredProperties.ToList(); JObject jo = JObject.Load(reader); foreach (JProperty jp in jo.Properties()) { if (!_propertyMappings.TryGetValue(jp.Name, out var name)) name = jp.Name; PropertyInfo prop = props.FirstOrDefault(pi => pi.CanWrite && pi.GetCustomAttribute<JsonPropertyAttribute>().PropertyName == name); prop.SetValue(instance, jp.Value.ToObject(prop.PropertyType, serializer)); } return instance; } } 

Usage

static void Main(string[] args) { Dictionary<string, string> test = new Dictionary<string, string> { {"name", "error"}, {"code", "errorCode"}, {"description", "message"} }; var apiError2 = JsonConvert.DeserializeObject<ApiError>(payload2, new ApiErrorConverter(test)); } 

Anyway, I just want to convert json map to C# object. How can I pass the dictionary into the ApiErrorConverter without this error?

How can i map the json to C# object with newtosoft.json flexibly like I'm trying to do?

2
  • 4
    Please consider formatting your code so it is readable. Commented May 16, 2019 at 21:19
  • @Ron Beyer I will be noticed nexttime,thanks Commented May 17, 2019 at 5:23

1 Answer 1

2

The problem is not that your ApiErrorConverter has a constructor, but how you call the json property value to object method. Because you pass the serializer, it calls the ReadJson method again for the property. This fails on the second property (a string) as a string doesn't have a parameterless constructor. If you use the ToObject overload that just specifies type, it will work. Given the data sample supplied, you also need to check for null on the PropertyInfo before using it.

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { object instance = Activator.CreateInstance(objectType); var props = objectType.GetTypeInfo().DeclaredProperties.ToList(); JObject jo = JObject.Load(reader); foreach (JProperty jp in jo.Properties()) { if (!_propertyMappings.TryGetValue(jp.Name, out var name)) name = jp.Name; PropertyInfo prop = props.FirstOrDefault(pi => pi.CanWrite && pi.GetCustomAttribute<JsonPropertyAttribute>().PropertyName == name); prop?.SetValue(instance, jp.Value.ToObject(prop.PropertyType)); } return instance; } 

Answer to extra comments:

The question was asked, given a different json structure:

string payload2 = "{\"statusCode\":123,\"code\":\"error_code\",\"name\":\"hahaha\",\"description\":\"The Message\",\"qqqitem\":[{\"ahah\":\"cl\",\"ahahah\":\"item\"}]}"; 

How to get the array named qqqitem into my deserialised object?

Firstly, the array is an object with two properties, ahah and ahahah, we need to make a class for this. I'll call it Item.

public class Item { public string ahah { get; set; } public string ahahah { get; set; } } 

Then we need our ApiError class to contain an array of these objects. I'll create it as a list.

public class ApiError { [JsonProperty("error")] public string Error { get; set; } [JsonProperty("errorCode")] public string ErrorCode { get; set; } [JsonProperty("message")] public string Message { get; set; } [JsonProperty("statusCode")] public int StatusCode { get; set; } [JsonProperty("items")] public List<Item> Items { get; set; } } 

Finally, since the names dont match I'll add a mapping

Dictionary<string, string> test = new Dictionary<string, string> { {"name", "error"}, {"code", "errorCode"}, {"description", "message"}, {"qqqitem", "items"} }; 
Sign up to request clarification or add additional context in comments.

5 Comments

it worked well!I will use jsonschema to check my data,thanks for your advice
I have another problem,if my data string contains JsonArray.Then my ApiErrorConverter will not get the values in the JsonArray.the data like this:string payload2 = "{\"statusCode\":123,\"code\":\"error_code\",\"name\":\"hahaha\",\"description\":\"The Message\",\"qqqitem\":[{\"ahah\":\"cl\",\"ahahah\":\"item\"}]}"; it will not get the JsonArray qqqitem values.What should i do to solve the problem .thanks @cjmurph
I've updated my answer to answer your extra question. It probably should have been a new question though.
Thanks for your replay.if the class Item field is different from my data string payload2 = "{\"statusCode\":123,\"code\":\"error_code\",\"name\":\"hahaha\",\"description\":\"The Message\",\"qqqitem\":[{\"ahah\":\"cl\",\"ahahah\":\"item\"}]}";ex:the class Item like this public class Item { [JsonProperty("message")] public string Message { get; set; } [JsonProperty("hahaha")] public string hahaha { get; set; } }.I want to map the ahah to message.Should I add a branch in function ReadJson to judge the JProperty is JsonArray ?@cjmurph
I think you should ask a new question (link it from here if you like) as this has now moved on from the original problem.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.