【问题标题】:Order of fields when serializing the derived class in JSON.NET在 JSON.NET 中序列化派生类时的字段顺序
【发布时间】:2015-12-10 20:40:52
【问题描述】:

考虑这两个类:

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=)])? p>

【问题讨论】:

  • 是否值得问“为什么需要不同的顺序?”这个问题?
  • @TimBarrass 只是为了在手动测试和调试时更有条理。
  • 根据JSON standard,JSON 对象是一组*无序的名称/值对*。所以我的建议是不要担心这个。
  • 对于 JSON 的测试,我发现JToken.DeepEquals 非常有用,它消除了纯粹由于格式造成的差异。
  • 我不认为“订单”编号必须是连续的。也许你可以为每个孩子分配“乐队”(即,基数是 1-10,孩子 A 是 11-20,孩子 B 是 21-30,等等)。

标签: c# json serialization json.net


【解决方案1】:

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

public class BaseFirstContractResolver : DefaultContractResolver
{
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization) =>
        base.CreateProperties(type, memberSerialization)
            ?.OrderBy(p => p.DeclaringType.BaseTypesAndSelf().Count()).ToList();
}

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

然后像这样使用它:

// Cache an instance of the resolver for performance
static IContractResolver baseFirstResolver = new BaseFirstContractResolver { /* Set any required properties here e.g.  NamingStrategy = new CamelCaseNamingStrategy() */ };

// And use the cached instance when serializing and deserializing
var settings = new JsonSerializerSettings 
{ 
    ContractResolver = baseFirstResolver, 
    // Add your other settings here.
    TypeNameHandling = TypeNameHandling.Objects 
};
var json = JsonConvert.SerializeObject(derived, typeof(Base), Formatting.Indented, settings);

注意事项:

  • 这种方法特别适用于多级类型层次结构,因为它可以自动对层次结构中所有级别的属性进行正确排序。

  • Newtonsoft 推荐 caching instances of contract resolvers 以获得最佳性能。

演示小提琴here.

【讨论】:

  • 缓存类型级别可能是有意义的,因为您要为同一类中的每个属性计算它
【解决方案2】:

我还考虑采用“dbc”的答案,但将“OrderBy()”表达式替换为以下(-不依赖于基类比派生类具有更多属性):

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

然后你不需要“TypeExtensions”,但它正在重复“p.Order”排序(这已经在基类中发生了);考虑到时间和思想,可能会有更好/更有效的方法来做到这一点。

【讨论】:

  • 我没有按属性数量排序我按类型层次结构中DeclaringType的深度排序.
  • 但是你在 'OrderBy()' 调用中得到了对 '.Count()' 的(最终)调用。
  • DeclaringType.BaseTypesAndSelf().Count() 计算声明类型的继承层次结构中的基本类型的数量。较小的计数意味着该属性在继承层次结构中被声明得更高(即来自基类型),而较大的计数意味着该属性来自更派生的类型。
【解决方案3】:

作为补充,与公认答案不同的另一种方法是使用[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; }
}

将 Order 值设置为 -2 的原因是每个没有显式 Order 值的属性默认值为 -1。因此,您需要给所有子属性一个 Order 值,或者只需将基类的属性设置为 -2。

【讨论】:

  • 我认为[JsonProperty(Order = int.MinValue)] 是不言自明的(-2 似乎是一个任意值)
  • 是的,但在问题中,我提到两个类的每个属性都没有使用 [JsonProperty(Order=)]。原因是我正在处理一个无法使用 [JsonProperty] 属性的多平台项目。
  • 你是对的。正如我所提到的,对于那些可能仅通过标题找到这个问题的人来说,这只是一个补充答案,这个问题更笼统。所以这两种方法都可以在一个地方找到。
  • 另外,我的派生类只向基类添加了一个属性。我发现将 [JsonProperty(Order = 1)] 添加到派生类中的该属性更容易、更简洁
【解决方案4】:

如果您使用的是 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;
            }
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-03-31
    • 2011-03-20
    • 2017-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多