使用JSON.NET反序列化值为字段名称的JSON

我有一个非常不受欢迎的情况,需要我反序列化JSON,其中值是使用JSON.NET的字段名称。 假设我有以下JSON,结构非常合理:

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

使用JSON.NET将其反序列化为CLR对象非常容易:

 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(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; } } 

但是,在我目前的情况下,我有以下可怕的JSON,相当于以上JSON的值:

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

如您所见, roles字段不是数组; 它是一个包含其他值作为对象的对象,它的唯一键是它们的字段名称(这很糟糕)。 使用JSON.NET将此JSON反序列化到上述User类的最佳方法是什么?

您可以创建一个自定义JsonConverter ,用于序列化/反序列化Role[] 。 然后,您可以使用JsonConverterAttribute来装饰您的Roles属性,如下所示:

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

在转换器类中,您可以读取对象并返回数组。 您的转换器类可能如下所示:

 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(reader); var result = new List(); // create an array out of the properties foreach (JProperty property in roles.Properties()) { var role = property.Value.ToObject(); 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(); } } 

您可以选择几种方式。 你可以有一个自定义的JsonConverter并手动序列化它。 因为在撰写本文时,这个fero提供了一个基于此的答案,我会给你一个替代方案,需要两个代理类:

 public class JsonUser { public string Name { get; set; } public Dictionary Roles { get; set; } } public class JsonRole { public string Name { get; set; } } 

在您的Role类中:

 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() }; } 

哪个可以这样使用:

 User jsonUser = JsonConvert.DeserializeObject(json); 

现在,这是以创建中间对象为代价完成的,并且可能不适合大多数情况。

为了完整起见,我将包含我的JsonConverter解决方案版本:

 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(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(json); 

嗯,你可以尝试:

 dynamic jObject = JObject.Parse(text); List users = new List(); foreach(dynamic dUser in jObject) { List roles = new List(); 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(); }