如何让json.net序列化继承自List<T>的类的成员?

12

我创建了一个类PagedResult<T> : List<T>,其中添加了一些成员以便与我们的组件配合使用。但是,当我运行JSON反序列化时,它只对列表进行序列化。如果我使用[JsonObject][JsonProperty]标记派生类,那么它只会序列化派生类的成员而不是列表。如何同时得到两者?

3个回答

12
默认情况下,Json.Net会将任何实现IEnumerable接口的类视为数组。您可以通过在类上使用[JsonObject]属性来覆盖此行为,但是这样只有对象属性将被序列化,正如您所看到的那样,列表本身不会被序列化,因为它没有通过公共属性公开(而是通过GetEnumerator()方法公开)。
如果您想要同时得到两者,您可以按照@Konrad的建议,在派生类中提供一个公共属性来公开列表,或者编写自定义JsonConverter以按照您所需的方式序列化整个内容。以下是后一种方法的示例。
假设您的PagedResult类看起来像这样:
class PagedResult<T> : List<T>
{
    public int PageSize { get; set; }
    public int PageIndex { get; set; }
    public int TotalItems { get; set; }
    public int TotalPages { get; set; }
}

您可以像这样制作一个转换器:
class PagedResultConverter<T> : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(PagedResult<T>));
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        PagedResult<T> result = (PagedResult<T>)value;
        JObject jo = new JObject();
        jo.Add("PageSize", result.PageSize);
        jo.Add("PageIndex", result.PageIndex);
        jo.Add("TotalItems", result.TotalItems);
        jo.Add("TotalPages", result.TotalPages);
        jo.Add("Items", JArray.FromObject(result.ToArray(), serializer));
        jo.WriteTo(writer);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject jo = JObject.Load(reader);
        PagedResult<T> result = new PagedResult<T>();
        result.PageSize = (int)jo["PageSize"];
        result.PageIndex = (int)jo["PageIndex"];
        result.TotalItems = (int)jo["TotalItems"];
        result.TotalPages = (int)jo["TotalPages"];
        result.AddRange(jo["Items"].ToObject<T[]>(serializer));
        return result;
    }
}

(注意,这种方法不需要使用[JsonObject][JsonProperty]属性,因为序列化的知识封装在转换器类中。)
以下是演示转换器操作的示例:
class Program
{
    static void Main(string[] args)
    {
        PagedResult<string> result = new PagedResult<string> { "foo", "bar", "baz" };
        result.PageIndex = 0;
        result.PageSize = 10;
        result.TotalItems = 3;
        result.TotalPages = 1;

        JsonSerializerSettings settings = new JsonSerializerSettings();
        settings.Converters.Add(new PagedResultConverter<string>());
        settings.Formatting = Formatting.Indented;

        string json = JsonConvert.SerializeObject(result, settings);
        Console.WriteLine(json);
    }
}

输出:

{
  "PageSize": 10,
  "PageIndex": 0,
  "TotalItems": 3,
  "TotalPages": 1,
  "Items": [
    "foo",
    "bar",
    "baz"
  ]
}

Json.Net会将任何实现IEnumerable接口的类视为数组。您可以通过在类上添加[JsonObject]属性来覆盖此行为,但这样只有对象属性会被序列化。我的经验略有不同:OP是从List<T>派生的,而我已经在基类中实现了IList<T>,并且直接在序列化对象中实现了它以获取我的IEnumerable实现。在两种情况下,只要使用[JsonObject]进行装饰,我就可以获得添加的属性和集合对象。可能是由于Json.NET的新版本(12.0.0)。 - Bob Sammers

3
我想到的最简单的解决方法是将内部元素作为派生类的另一个属性公开:
[JsonProperty]
public IEnumerable<T> Elements {
    get 
    { 
       return this; 
    }
}

当我尝试使用此选项时,出现了“检测到类型为'T'的属性'Elements'的自引用循环”错误。 - Abhinav Galodha

0
如果您不想编写自定义的 JsonConverter 或使用 JSON 属性(JsonObjectAttribute),那么您可以使用扩展方法:
class PagedResult<T> : List<T>
{
    public int PageSize { get; set; }
    public int PageIndex { get; set; }
    public int TotalItems { get; set; }
    public int TotalPages { get; set; }
}

public static class Extensions
{
    public static string ToPagedJson<T>(this PagedResult<T> pagedResult)
    {
         return JsonConvert.SerializeObject(new
         {
             PageSize = pagedResult.PageSize,
             PageIndex = pagedResult.PageIndex,
             TotalItems = pagedResult.TotalItems,
             TotalPages = pagedResult.TotalPages
             Items = pagedResult
         });
    }
}

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