26

I have a project that is currently using Json.Net for Json deserialization classes like these:

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

So far it works fine.

To make iteration simpler at one point I made Foo class implement IEnumerable<Bar> like this:

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

But then it fails deserializing the object. The error is confusing, as it said that it cannot deserialize FooGuid field, but removing the IEnumerable interface works again.

The problem is the same in both MonoTouch and MonoDroid environments in simulator or device.

Any clue about how to make it work?


Added code to reproduce this issue:

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

Sample project: https://www.dropbox.com/s/27i58aiz71dylkw/IEnumerableJson.zip

redent84
  • 18,901
  • 4
  • 62
  • 85
  • How about posting a self-contained compilable code? Since i can't reproduce it. – L.B Sep 29 '13 at 16:38
  • @L.B Added sample code and sample project. – redent84 Sep 29 '13 at 17:27
  • 1
    You don't need to implement `IEnumerable`. Just put this method `public IEnumerator GetEnumerator() { return Bars.GetEnumerator(); }` to `EnumerableFoo` (removing other GetEnumerators) , it will work in **foreach** – L.B Sep 29 '13 at 17:50
  • the JSON.net docs (http://james.newtonking.com/json/help/index.html?topic=html/SerializationGuide.htm) discuss the limitations of serializing IEnumerables. – Jason Sep 29 '13 at 21:28

2 Answers2

46

From the Json.Net documentation:

IEnumerable, Lists and Arrays

.NET lists (types that inherit from IEnumerable) and .NET arrays are converted to JSON arrays. Because JSON arrays only support a range of values and not properties, any additional properties and fields declared on .NET collections are not serialized. In situations where a JSON array is not wanted the JsonObjectAttribute can be placed on a .NET type that implements IEnumerable to force the type to be serialized as a JSON object instead.

In other words, since your class implements IEnumerable<T>, Json.Net thinks it is a list. To get around this, simply decorate your class with the [JsonObject] attribute. This will force Json.Net to serialize and deserialize it as a normal class, which is what you want in this case.

[JsonObject]
public class EnumerableFoo : IEnumerable<Bar>
{
    ...
}
Brian Rogers
  • 125,747
  • 31
  • 299
  • 300
1

If there is a private property to be serialized/deserialized [JsonProperty] should be used.

Example:

[JsonProperty]
private Sth[] sthArray;
Peter Csala
  • 17,736
  • 16
  • 35
  • 75