在JSON.NET中序列化派生类时的字段顺序

时间:2015-09-14 18:38:42

标签: c# json serialization json.net

考虑这两个类:

public Class Base {
    public string Id {get; set;}
    public string Name {get; set;}
    public string LastName {get; set;}
}

派生类:

public Class Derived : Base {
    public string Address {get; set;}
    public DateTime DateOfBirth {get; set;}
}

使用 Json.Net 序列化派生类时:

Derived record = new Derived record(); {// Initialize here...}
JsonConvert.SerializeObject(record);

默认情况下,首先显示派生类的属性:

{ 
  "address": "test", 
  "date_of_birth" : "10/10/10",
  "id" : 007,
  "name" : "test name",
  "last_name": "test last name"
 }

我需要什么:

{ 
  "id" : 007,
  "name" : "test name",
  "last_name": "test last name"
  "address": "test", 
  "date_of_birth" : "10/10/10",      
 }

问题

在序列化派生类时,是否可以首先使用基类属性(对两个类的每个属性不使用[JsonProperty(Order=)] )?

4 个答案:

答案 0 :(得分:16)

根据JSON standard,JSON对象是一组无序的名称/值对。所以我的建议是不要担心房产秩序。不过,您可以通过创建自己的ContractResolver继承标准contract resolvers之一,然后覆盖CreateProperties来获得所需的订单:

public class BaseFirstContractResolver : DefaultContractResolver
{
    // As of 7.0.1, Json.NET suggests using a static instance for "stateless" contract resolvers, for performance reasons.
    // http://www.newtonsoft.com/json/help/html/ContractResolver.htm
    // http://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Serialization_DefaultContractResolver__ctor_1.htm
    // "Use the parameterless constructor and cache instances of the contract resolver within your application for optimal performance."
    static BaseFirstContractResolver instance;

    static BaseFirstContractResolver() { instance = new BaseFirstContractResolver(); }

    public static BaseFirstContractResolver Instance { get { return instance; } }

    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        var properties = base.CreateProperties(type, memberSerialization);
        if (properties != null)
            return properties.OrderBy(p => p.DeclaringType.BaseTypesAndSelf().Count()).ToList();
        return properties;
    }
}

public static class TypeExtensions
{
    public static IEnumerable<Type> BaseTypesAndSelf(this Type type)
    {
        while (type != null)
        {
            yield return type;
            type = type.BaseType;
        }
    }
}

然后使用它:

        var settings = new JsonSerializerSettings { ContractResolver = BaseFirstContractResolver.Instance };
        var json = JsonConvert.SerializeObject(derived, Formatting.Indented, settings);

答案 1 :(得分:16)

作为补充,另一种不同于接受答案的方法是使用[JsonProperty(Order = -2)];您可以按照以下方式修改基类:

public class Base
{
    [JsonProperty(Order = -2)]
    public string Id { get; set; }

    [JsonProperty(Order = -2)]
    public string Name { get; set; }

    [JsonProperty(Order = -2)]
    public string LastName { get; set; }
}

将顺序设置为-2的原因是没有显式顺序的任何属性默认情况下的顺序为-1。因此,您必须为所有子项属性提供订单,或者只是设置您的基类&#39;属性为-2。

答案 2 :(得分:1)

如果您使用的是ASP.NET Core,请勿覆盖重要的合约解析程序设置provided by default。从@ dbc的回答开始,你可以这样做:

class DataContractJsonResolver : DefaultContractResolver
{
    public DataContractJsonResolver()
    {
        NamingStrategy = new CamelCaseNamingStrategy();
    }

    protected override IList<JsonProperty> CreateProperties( Type type, MemberSerialization memberSerialization )
    {
        return base.CreateProperties( type, memberSerialization )
            .OrderBy( p => BaseTypesAndSelf( p.DeclaringType ).Count() ).ToList();

        IEnumerable<Type> BaseTypesAndSelf( Type t )
        {
            while ( t != null ) {
                yield return t;
                t = t.BaseType;
            }
        }
    }
}

答案 3 :(得分:0)

我也考虑采用'dbc'的答案,但是用以下命令替换-(OrderBy()'表达式,该表达式(-不依赖基类具有比派生类更多的属性): / p>

                    .OrderBy(
                        p =>
                            p.DeclaringType == type
                                ? 2
                                : 1)
                    .ThenBy(
                        p =>
                            p.Order ?? -1)

然后您不需要“ TypeExtensions”,但它会重复“ p.Order”排序(已经在基类中进行了);在有时间和思想的情况下,可能会有更好/更有效的方法来完成此操作。