Подтвердить что ты не робот

Порядок полей при сериализации производного класса в 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;}
}

При сериализации класса Derived с помощью Json.Net:

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

По умолчанию свойства класса Derived появляются первыми:

{ 
  "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=)] для каждого свойства обоих классов)?

4b9b3361

Ответ 1

Согласно стандарту JSON, объект JSON является неупорядоченным набором пар имя/значение. Поэтому моя рекомендация состояла бы в том, чтобы не беспокоиться о порядке собственности. Тем не менее вы можете получить желаемый заказ, создав собственный ContractResolver, наследующий от одного из стандартных контрактные преобразователи, а затем переопределение 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);

Ответ 2

В качестве дополнения используется другой подход, отличный от принятого ответа [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.

Ответ 3

Если вы используете ядро ​​ASP.NET, не переопределяйте важные параметры разрешающей способности контракта предоставленные по умолчанию. Следуя ответу @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;
            }
        }
    }
}