c# - 在实现 IEnumerable 的类上序列化自定义属性

标签 c# json.net

<分区>

目前,JSON.NET 忽略实现 IEnumerable 的类的所有其他属性并序列化数组。

如何让 JSON.NET 序列化自定义属性?我正在尝试序列化 PagedList<T>实现如下:

public interface IPagedList : IEnumerable
{
    int PageIndex { get; }
    int PageSize { get; }
    int TotalCount { get; }
    int TotalPages { get; }
    bool HasPreviousPage { get; }
    bool HasNextPage { get; }
}

public interface IPagedList<T> : IPagedList, IList<T>
{
}

/// <summary>
/// A tried and tested PagedList implementation
/// </summary>
public class PagedList<T> : List<T>, IPagedList<T>
{
    public PagedList(IEnumerable<T> source, int pageIndex, int pageSize) :
        this(source.GetPage(pageIndex, pageSize), pageIndex, pageSize, source.Count()) { }

    public PagedList(IEnumerable<T> source, int pageIndex, int pageSize, int totalCount)
    {
        Ensure.Argument.NotNull(source, "source");

        this.TotalCount = totalCount;
        this.TotalPages = totalCount / pageSize;

        if (totalCount % pageSize > 0)
            TotalPages++;

        this.PageSize = pageSize;
        this.PageIndex = pageIndex;

        this.AddRange(source.ToList());
    }

    public int PageIndex { get; private set; }
    public int PageSize { get; private set; }
    public int TotalCount { get; private set; }
    public int TotalPages { get; private set; }

    public bool HasPreviousPage { get { return (PageIndex > 0); } }
    public bool HasNextPage { get { return (PageIndex + 1 < TotalPages); } }
}

最佳答案

我遇到了同样的问题,最终不得不使用自定义转换器来完成。我的转换器采用类型名称列表进行操作,而不是在全局范围内工作。我上了您的课并编写了一个小型控制台应用程序,它将通过转换器运行您的类(class)。神奇之处在于设置 JsonSerializerSettings 以使用 EnumerableConverter 类。设置完成后,您必须在 SerializeObject 调用中使用设置对象。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Newtonsoft.Json;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            var pagedList = new PagedList<MyObject>(
                                new MyObject[] { 
                                    new MyObject { Foo = "Bar" }, 
                                    new MyObject { Foo = "Baz" } 
                                }, 0, 2, 2);
            Debug.WriteLine(JsonConvert.SerializeObject(pagedList));
            // Outputs: [{"Foo":"Bar"},{"Foo":"Baz"}]

            var settings = new JsonSerializerSettings
            {
                Converters = new List<JsonConverter>(
                                new[]{
                                    new EnumerableConverter(
                                       new []{"ConsoleApplication2.PagedList"}, 
                                              false)
                                })
            };
            Debug.WriteLine(JsonConvert.SerializeObject(pagedList, settings));
            // Outputs: 
            //{
            //   "PageIndex":0,
            //   "PageSize":2,
            //   "TotalCount":2,
            //   "TotalPages":1,
            //   "HasPreviousPage":false,
            //   "HasNextPage":false,
            //   "Capacity":4,
            //   "Count":2,
            //   "Items":[
            //      {
            //         "Foo":"Bar"
            //      },
            //      {
            //         "Foo":"Baz"
            //      }
            //   ]
            //}      
        }
    }

    public class MyObject
    {
        public string Foo { get; set; }
    }

    public interface IPagedList : IEnumerable
    {
        int PageIndex { get; }
        int PageSize { get; }
        int TotalCount { get; }
        int TotalPages { get; }
        bool HasPreviousPage { get; }
        bool HasNextPage { get; }
    }

    public interface IPagedList<T> : IPagedList, IList<T>
    {
    }

    /// <summary>
    /// A tried and tested PagedList implementation
    /// </summary>
    public class PagedList<T> : List<T>, IPagedList<T>
    {
        //public PagedList(IEnumerable<T> source, int pageIndex, int pageSize) :
        //    this(source.GetPage(pageIndex, pageSize), pageIndex, pageSize, source.Count()) { }

        public PagedList(IEnumerable<T> source, int pageIndex, int pageSize, int totalCount)
        {
            //Ensure.Argument.NotNull(source, "source");

            this.TotalCount = totalCount;
            this.TotalPages = totalCount / pageSize;

            if (totalCount % pageSize > 0)
                TotalPages++;

            this.PageSize = pageSize;
            this.PageIndex = pageIndex;

            this.AddRange(source.ToList());
        }

        public int PageIndex { get; private set; }
        public int PageSize { get; private set; }
        public int TotalCount { get; private set; }
        public int TotalPages { get; private set; }

        public bool HasPreviousPage { get { return (PageIndex > 0); } }
        public bool HasNextPage { get { return (PageIndex + 1 < TotalPages); } }
    }

    public class EnumerableConverter : Newtonsoft.Json.JsonConverter
    {
        private IEnumerable<string> Types { get; set; }
        private bool IsCamelCase { get; set; }

        public EnumerableConverter(IEnumerable<string> types)
            : this(types, true)
        {
        }

        public EnumerableConverter(IEnumerable<string> types, bool isCamelCase)
        {
            if (types == null) throw new ArgumentNullException("types");
            Types = types;
            IsCamelCase = isCamelCase;
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            writer.WriteStartObject();
            var foundInnerEnumerable = false;
            foreach (var property in value.GetType().GetProperties()) {
                try {
                    var propVal = property.GetValue(value, null);
                    foundInnerEnumerable |= propVal is IEnumerable;
                    writer.WritePropertyName(NameFor(property.Name));
                    serializer.Serialize(writer, propVal);
                }
                catch (System.Reflection.TargetParameterCountException) { 
                    // Ignore properties such as Item on List<T>
                }
            }
            if (!foundInnerEnumerable) {
                writer.WritePropertyName(NameFor("Items"));
                writer.WriteStartArray();
                foreach (var item in (IEnumerable)value) {
                    serializer.Serialize(writer, item);
                }
                writer.WriteEndArray();
            }
            writer.WriteEndObject();
        }

        private string NameFor(string value)
        {
            if (!IsCamelCase) return value;
            return value[0].ToString().ToLowerInvariant() + value.Substring(1);
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }

        public override bool CanConvert(Type objectType)
        {
            return Types.Any(t => objectType.FullName.StartsWith(t));
        }
    }

}

关于c# - 在实现 IEnumerable 的类上序列化自定义属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12129774/

相关文章:

C# ToList 性能和替代方法

c# - 如何在给定 UTC 时间和日期的情况下创建 DateTime 对象?

c# - 使用 .NET 创建 tar.gz

c# - 创建模型时无法使用上下文。但是我只有一个 dbcontext?

javascript - 在 C# 中反序列化 Paypal 响应的正确方法

c# - iOS 在执行过程中丢弃任务

c# - 在 C# 中反序列化 Open Street Map JSON

c# - Newtonsoft JSON - 反序列化JSON时如何使用JsonConverter.ReadJson方法进行类型转换

f# - 从 TypeProvider 引用 Newtonsoft.Json

c# - 解析值时遇到意外字符