【问题标题】:Deserialize the JSON where the values are field names with JSON.NET使用 JSON.NET 反序列化其中值是字段名称的 JSON
【发布时间】:2013-07-19 12:13:11
【问题描述】:

我有一个非常不受欢迎的情况,需要我反序列化 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 以上类的最佳方法是什么?

【问题讨论】:

    标签: c# .net json json.net


    【解决方案1】:

    您可以创建自定义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】:

      有几个选项可供您使用。您可以有一个自定义的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);
      

      【讨论】:

        【解决方案3】:

        嗯,你可以试试:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-06-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多