5

値がJSON.NETのフィールド名であるJSONを逆シリアル化する必要がある非常に望ましくない状況があります。非常に適切に構造化された次の 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 は配列ではありません。これは、フィールド名として一意のキーを持つオブジェクトとして他の値を含むオブジェクトです (これは恐ろしいことです)。このJSONをUserJSON.NETで上記のクラスに逆シリアル化する最良の方法は何ですか?

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

利用可能なオプションがいくつかあります。カスタムJsonConverterを作成して、手動でシリアル化できます。これを書いている時点で、この fero はこれに基づいた回答を提供しているので、2 つのサロゲート クラスを必要とする別の方法を紹介します。

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 に答える