【问题标题】:How do I get json.net to serialize members of a class deriving from List<T>?如何让 json.net 序列化从 List<T> 派生的类的成员?
【发布时间】:2026-02-06 07:20:06
【问题描述】:

我创建了一个类PagedResult&lt;T&gt; : List&lt;T&gt;,其中包含一些添加的成员,以便使用我们的一个组件。但是,当我运行 json 反序列化器时,它只会序列化列表。如果我用[JsonObject][JsonProperty] 标记派生类,那么它只会序列化派生类的成员而不是列表。我如何获得两者?

【问题讨论】:

    标签: c# serialization json.net


    【解决方案1】:

    默认情况下,Json.Net 会将任何实现IEnumerable 的类视为一个数组。您可以通过使用[JsonObject] 属性装饰类来覆盖此行为,但如您所见,只有对象属性会被序列化。列表本身不会被序列化,因为它没有通过公共属性公开(而是通过GetEnumerator() 方法公开)。

    如果您想要两者,您可以按照@Konrad 的建议进行操作,并在派​​生类上提供公共属性以公开列表,或者您可以编写自定义JsonConverter 以按照您认为合适的方式序列化整个事物。下面是后一种方法的一个示例。

    假设您的 PagedResult&lt;T&gt; 类看起来像这样:

    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&lt;T&gt;,而我实现了IList&lt;T&gt;,一次在基类中,一次在序列化对象中,直接得到我的IEnumerable 实现。在这两种情况下,只要我用[JsonObject] 装饰,我就会得到添加的属性和集合对象。可能是更新版本的 Json.NET (12.0.0)。
    【解决方案2】:

    我想到的最简单的解决方法是将内部元素公开为派生类的另一个属性:

    [JsonProperty]
    public IEnumerable<T> Elements {
        get 
        { 
           return this; 
        }
    }
    

    【讨论】:

    • 我在尝试此选项时收到错误 Self referencing loop detected for property 'Elements' with type "T" error。
    【解决方案3】:

    如果您不想编写自定义 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
             });
        }
    }
    

    【讨论】:

      最近更新 更多