使用Newtonsoft Json.Net反序列化为IEnumerable类

26

我有一个项目,目前正在使用Json.Net进行Json反序列化,类似于这样的类:

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


贴一个自包含可编译代码怎么样?因为我无法复现它。 - L.B
@L.B 添加了示例代码和示例项目。 - redent84
1
你不需要实现 IEnumerable<Bar>。只需将以下方法放进 EnumerableFoo 里(移除其他的 GetEnumerators):public IEnumerator GetEnumerator() { return Bars.GetEnumerator(); }这样就可以在 foreach 中使用了。 - L.B
JSON.net文档(http://james.newtonking.com/json/help/index.html?topic=html/SerializationGuide.htm)讨论了序列化IEnumerables的限制。 - Jason
2个回答

46

根据Json.Net文档

IEnumerable、列表和数组

.NET列表(继承自IEnumerable的类型)和.NET数组将被转换为JSON数组。因为JSON数组只支持一系列值,而不是属性,所以在.NET集合上声明的任何其他属性和字段都不会被序列化。在不需要JSON数组的情况下,可以在实现IEnumerable的.NET类型上放置JsonObjectAttribute以将类型强制序列化为JSON对象。

换句话说,由于您的类实现了IEnumerable<T>,Json.Net认为它是一个列表。要解决这个问题,只需在类上添加[JsonObject]属性即可。这将强制Json.Net将其序列化和反序列化为普通类,这正是您此时想要的。

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

1

如果有一个需要序列化/反序列化的私有属性,应该使用[JsonProperty]

例如:

[JsonProperty]
private Sth[] sthArray;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接