web-dev-qa-db-ja.com

Newtonsoft Json.Netを使用してIEnumerableクラスに逆シリアル化する

次のようなJson逆シリアル化クラスに現在Json.Netを使用しているプロジェクトがあります。

public class Foo {
    public Guid FooGuid { get; set; }
    public string Name { get; set; }
    public List<Bar> Bars { get; set; }
}

public class Bar {
    public Guid BarGuid { get; set; }
    public string Description { get; set; }
}

これまでのところ、それは正常に動作します。

ある時点で反復を簡単にするために、Fooクラスを実装しましたIEnumerable<Bar> このような:

public class Foo : IEnumerable<Bar> {
    public Guid FooGuid { get; set; }
    public string Name { get; set; }
    public List<Bar> Bars { get; set; }

    public IEnumerator<Bar> GetEnumerator()
    {
        return Bars.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

public class Bar {
    public Guid BarGuid { get; set; }
    public string Description { get; set; }
}

ただし、オブジェクトのデシリアライズに失敗します。 FooGuidフィールドを逆シリアル化することはできませんが、IEnumerableインターフェイスを削除すると再び機能するようになっているため、エラーは混乱を招きます。

問題は、シミュレータまたはデバイスのMonoTouch環境とMonoDroid環境の両方で同じです。

それを機能させる方法についての手がかりはありますか?


この問題を再現するコードを追加しました:

public static class Tests {
    public static void SerializeAndDeserializeFoo() {
        // Serialize and deserialize Foo class
        var element = new Foo
        {
            FooGuid = Guid.NewGuid(),
            Name = "Foo name",
            Bars = new List<Bar> {
                new Bar { BarGuid = Guid.NewGuid(), Description = "Bar description" },
                new Bar { BarGuid = Guid.NewGuid(), Description = "Bar description" },
                new Bar { BarGuid = Guid.NewGuid(), Description = "Bar description" }
            }
        };

        var serializedObject = JsonConvert.SerializeObject(element);
        Console.WriteLine("Serialized Foo element: {0}", serializedObject);

        // Exception if Foo implements IEnumerable
        var deserializedObject = JsonConvert.DeserializeObject<Foo>(serializedObject);
        Console.WriteLine("Foo deserialization worked!");
    }

    public static void SerializeAndDeserializeEnumerableFoo() {
        // Serialize and deserialize Foo class
        var element = new EnumerableFoo
        {
            FooGuid = Guid.NewGuid(),
            Name = "Foo name",
            Bars = new List<Bar> {
                new Bar { BarGuid = Guid.NewGuid(), Description = "Bar description" },
                new Bar { BarGuid = Guid.NewGuid(), Description = "Bar description" },
                new Bar { BarGuid = Guid.NewGuid(), Description = "Bar description" }
            }
        };

        var serializedObject = JsonConvert.SerializeObject(element);
        Console.WriteLine("Serialized EnumerableFoo element: {0}", serializedObject);

        try {
            // Exception if Foo implements IEnumerable
            var deserializedObject = JsonConvert.DeserializeObject<EnumerableFoo>(serializedObject);
            Console.WriteLine("EnumerableFoo deserialization worked!");
        }
        catch (Exception e){
            Console.WriteLine("EnumerableFoo deserialization failed!");
            throw;
        }
    }
}

public class EnumerableFoo : IEnumerable<Bar> {
    public Guid FooGuid { get; set; }
    public string Name { get; set; }
    public List<Bar> Bars { get; set; }

    public IEnumerator<Bar> GetEnumerator()
    {
        return Bars.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

public class Foo {
    public Guid FooGuid { get; set; }
    public string Name { get; set; }
    public List<Bar> Bars { get; set; }
}

public class Bar {
    public Guid BarGuid { get; set; }
    public string Description { get; set; }
}

サンプルプロジェクト: https://www.dropbox.com/s/27i58aiz71dylkw/IEnumerableJson.Zip

21
redent84

Json.Netドキュメント から:

IEnumerable、リストおよび配列

.NETリスト(IEnumerableから継承するタイプ)と.NET配列はJSON配列に変換されます。 JSON配列は値の範囲のみをサポートし、プロパティはサポートしないため、.NETコレクションで宣言された追加のプロパティおよびフィールドはシリアル化されません。 JSON配列が不要な状況では、JsonObjectAttributeをIEnumerableを実装する.NET型に配置して、代わりに型をJSONオブジェクトとしてシリアル化することができます。

つまり、クラスがIEnumerable<T>を実装しているため、Json.Netはそれをリストであると見なします。これを回避するには、[JsonObject]属性を使用してクラスを装飾するだけです。これにより、Json.Netは通常のクラスとしてシリアル化および逆シリアル化するように強制されます。これは、この場合に必要なことです。

[JsonObject]
public class EnumerableFoo : IEnumerable<Bar>
{
    ...
}
32
Brian Rogers