Destroy JSON where values ​​are field names using JSON.NET

I have a very undesirable situation that requires me to deserialize JSON, where the values ​​are the names of the fields with JSON.NET. Assuming I have the following JSON, which is very properly structured:

{ "name": "tugberk", "roles": [ { "id": "1", "name": "admin" }, { "id": "2", "name": "guest" } ] } 

It is very easy to deserialize this with JSON.NET for the CLR object:

 class Program { static void Main(string[] args) { var camelCaseSettings = new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }; var text = File.ReadAllText("user_normal.txt"); var obj = JsonConvert.DeserializeObject<User>(text, camelCaseSettings); } } public class User { public string Name { get; set; } public Role[] Roles { get; set; } } public class Role { public int Id { get; set; } public string Name { get; set; } } 

However, in my current case, I have the following terrible JSON, which is equivalent to the above JSON in terms of values:

 { "name": "tugberk", "roles": { "1": { "name": "admin" }, "2": { "name": "guest" } } } 

As you can see, the roles field is not an array; this is an object that contains other values ​​as objects with unique keys as their field names (which is horrible). What is the best way to deserialize this JSON above the User class using JSON.NET?

+4
source share
3 answers

You can create a custom JsonConverter that serializes / deserializes Role[] . You can then decorate your Roles property with JsonConverterAttribute as follows:

 public class User { public string Name { get; set; } [JsonConverter(typeof(RolesConverter))] public Role[] Roles { get; set; } } 

In your converter class, you can read an object and return an array. The converter class may look like this:

 class RolesConverter : JsonConverter { public override bool CanConvert(Type objectType) { return objectType == typeof(Role[]); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { // deserialize as object var roles = serializer.Deserialize<JObject>(reader); var result = new List<Role>(); // create an array out of the properties foreach (JProperty property in roles.Properties()) { var role = property.Value.ToObject<Role>(); role.Id = int.Parse(property.Name); result.Add(role); } return result.ToArray(); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } } 
+6
source

Hm you can try:

 dynamic jObject = JObject.Parse(text); List<User> users = new List<User>(); foreach(dynamic dUser in jObject) { List<Role> roles = new List<Role>(); User user = new User(); user.Name = dUser.name; foreach(PropertyInfo info in dUser.GetType().GetProperties()) { Role role = new Role(); role.Id = info.Name; role.Name = dUser[info.Name].name; roles.Ad(role); } user.Roles = roles.ToArray(); } 
+2
source

Several options are available. You can have a custom JsonConverter and serialize it manually. Since, at the time of this writing, fero has provided an answer based on this, I will give you an alternative requiring two surrogate classes:

 public class JsonUser { public string Name { get; set; } public Dictionary<int, JsonRole> Roles { get; set; } } public class JsonRole { public string Name { get; set; } } 

And in your Role class:

 public static implicit operator User(JsonUser user) { return new User { Name = user.Name, Roles = user.Roles .Select(kvp => new Role { Id = kvp.Key, Name = kvp.Value.Name}) .ToArray() }; } 

What can be used as follows:

 User jsonUser = JsonConvert.DeserializeObject<JsonUser>(json); 

Now this is done by creating an intermediate object and is probably not suitable for most cases.

For completeness, I will include my version of the JsonConverter solution:

 public class UserRolesConverter : JsonConverter { public override bool CanConvert(Type objectType) { return objectType == typeof (Role[]); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { return serializer.Deserialize<JObject>(reader) .Properties() .Select(p => new Role { Id = Int32.Parse(p.Name), Name = (string) p.Value["name"] }) .ToArray(); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } } public class User { public string Name { get; set; } [JsonConverter(typeof(UserRolesConverter))] public Role[] Roles { get; set; } } var jsonUser = JsonConvert.DeserializeObject<User>(json); 
+2
source

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


All Articles