web-dev-qa-db-ja.com

JSON.NETで派生クラスをシリアル化するときのフィールドの順序

次の2つのクラスを検討してください。

_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);
_

デフォルトでは、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=)] for両方のクラスの各プロパティ)?

23
A-Sharabiani

JSON標準 によると、JSONオブジェクトは名前と値のペアの順序付けされていないセットです。したがって、私の推奨は、プロパティの順序について心配しないことです。それでも、独自の ContractResolver を作成して、標準の contract resolvers の1つを継承し、 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);
20
dbc

補足として、受け入れられた回答とは異なる別のアプローチは[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に設定する必要があります。

28
Moshtaf

ASP.NET Coreを使用している場合は、重要なコントラクトリゾルバー設定 デフォルトで提供される を上書きしないでください。 @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;
            }
        }
    }
}
2
HappyNomad