2013-07-19 41 views
4

我有一个非常不受欢迎的情况,它要求我反序列化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<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; } 
} 

然而,在我目前的情况下,我具有以下可怕JSON其等同于上述的在JSON值方面:

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

正如你可以看到,roles字段不是阵列;它是一个包含其他值作为对象的唯一键作为其字段名称的对象(这太可怕了)。使用JSON.NET将此JSON反序列化到User类的最佳方法是什么?

回答

6

您可以创建一个自定义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<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(); 
    } 
} 
2

嗯,你可以尝试:

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

有提供给你几个选择。您可以自定义JsonConverter并手动序列化它。由于本FERO提供基于此答案写作的时候,我给你一个选择,需要两个代理类:

public class JsonUser 
{ 
    public string Name { get; set; } 
    public Dictionary<int, JsonRole> 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<JsonUser>(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<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);