5

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

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

4

3 回答 3

6

您可以创建一个JsonConverter序列化/反序列化的自定义Role[]。然后你可以用这样的方式装饰你的Roles财产JsonConverterAttribute

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();
    }
}
于 2013-07-19T12:36:39.990 回答
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();
}
于 2013-07-19T12:45:03.960 回答
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);
于 2013-07-19T12:47:42.260 回答