问题
Consider these two classes:
public Class Base {
public string Id {get; set;}
public string Name {get; set;}
public string LastName {get; set;}
}
And the derived class:
public Class Derived : Base {
public string Address {get; set;}
public DateTime DateOfBirth {get; set;}
}
When serializing the Derived class using Json.Net:
Derived record = new Derived record(); {// Initialize here...}
JsonConvert.SerializeObject(record);
By default, the properties of the Derived class appear first:
{
"address": "test",
"date_of_birth" : "10/10/10",
"id" : 007,
"name" : "test name",
"last_name": "test last name"
}
What I need:
{
"id" : 007,
"name" : "test name",
"last_name": "test last name"
"address": "test",
"date_of_birth" : "10/10/10",
}
Question
Is it possible to have the base class properties come first, when serializing the derived class (without using [JsonProperty(Order=)]
for each property of both classes)?
回答1:
According to the JSON standard, a JSON object is an unordered set of name/value pairs. So my recommendation would be to not worry about property order. Nevertheless you can get the order you want by creating your own ContractResolver inheriting from one of the standard contract resolvers, and then overriding 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;
}
}
}
And then use it like:
var settings = new JsonSerializerSettings { ContractResolver = BaseFirstContractResolver.Instance };
var json = JsonConvert.SerializeObject(derived, Formatting.Indented, settings);
回答2:
Just as a complement, another approach different than the accepted answer is using [JsonProperty(Order = -2)]
; You can modify your base class as follow:
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; }
}
The reason of setting Order values to -2 is that every property without an explicit Order value has a value of -1 by default. So you need to either give all child properties an Order value, or just set your base class' properties to -2.
回答3:
If you're using ASP.NET Core, don't override important contract resolver settings provided by default. Following from @dbc's answer, you can do this:
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;
}
}
}
}
来源:https://stackoverflow.com/questions/32571695/order-of-fields-when-serializing-the-derived-class-in-json-net