.NET Core中如何控制List内DTO对象的JSON属性输出顺序?
控制.NET Core中DTO序列化JSON的属性输出顺序
针对你需要固定或自定义List<SearchResultPersonDto>序列化JSON时的属性顺序问题,以下是两种常用序列化库的解决方案:
一、使用特性固定顺序(简单直接)
1. 针对System.Text.Json(.NET 5+默认)
给DTO的每个属性添加[JsonPropertyOrder]特性,通过指定数字控制顺序(数字越小,序列化时越靠前):
public class SearchResultPersonDto { [JsonPropertyOrder(1)] public string LastName { get; set; } [JsonPropertyOrder(2)] public string FirstName { get; set; } // 自定义顺序:把TaxYear放在最前面 [JsonPropertyOrder(0)] public int TaxYear { get; set; } [JsonPropertyOrder(3)] public string Position { get; set; } [JsonPropertyOrder(4)] public string IncomeBracket { get; set; } }
如果需要同时重命名属性,配合[JsonPropertyName]使用即可:
[JsonPropertyName("last_name"), JsonPropertyOrder(1)] public string LastName { get; set; }
2. 针对Newtonsoft.Json(第三方库)
使用[JsonProperty(Order = n)]特性控制顺序,规则和System.Text.Json一致:
public class SearchResultPersonDto { [JsonProperty(Order = 1)] public string LastName { get; set; } [JsonProperty(Order = 2)] public string FirstName { get; set; } [JsonProperty(Order = 0)] public int TaxYear { get; set; } [JsonProperty(Order = 3)] public string Position { get; set; } [JsonProperty(Order = 4)] public string IncomeBracket { get; set; } }
二、动态自定义顺序(适合多场景不同顺序需求)
如果不同接口需要输出不同的属性顺序,无需修改DTO特性,可通过自定义解析器/转换器实现:
1. Newtonsoft.Json 动态顺序
创建自定义契约解析器:
public class CustomPropertyOrderResolver : DefaultContractResolver { private readonly Dictionary<Type, List<string>> _propertyOrderMap; public CustomPropertyOrderResolver(Dictionary<Type, List<string>> propertyOrderMap) { _propertyOrderMap = propertyOrderMap; } protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization) { var properties = base.CreateProperties(type, memberSerialization); if (_propertyOrderMap.TryGetValue(type, out var orderedProps)) { // 按指定顺序排序,未指定的属性放最后 properties = properties.OrderBy(p => orderedProps.IndexOf(p.PropertyName)).ToList(); } return properties; } }
全局配置(影响所有该类型序列化)
在Program.cs或Startup.cs中配置:
builder.Services.AddControllers() .AddNewtonsoftJson(options => { var orderMap = new Dictionary<Type, List<string>> { { typeof(SearchResultPersonDto), new List<string> { "TaxYear", "Position", "LastName", "FirstName", "IncomeBracket" } } }; options.SerializerSettings.ContractResolver = new CustomPropertyOrderResolver(orderMap); });
单个接口动态配置
在Action中单独指定顺序:
[HttpGet("custom-order")] public IActionResult GetPersonsCustomOrder() { var persons = GetPersonList(); // 你的数据获取逻辑 var customOrder = new List<string> { "TaxYear", "IncomeBracket", "Position", "LastName", "FirstName" }; var settings = new JsonSerializerSettings { ContractResolver = new CustomPropertyOrderResolver(new Dictionary<Type, List<string>> { { typeof(SearchResultPersonDto), customOrder } }) }; var json = JsonConvert.SerializeObject(persons, settings); return Content(json, "application/json"); }
2. System.Text.Json 动态顺序
创建自定义转换器:
public class CustomPropertyOrderConverter<T> : JsonConverter<T> { private readonly List<string> _propertyOrder; public CustomPropertyOrderConverter(List<string> propertyOrder) { _propertyOrder = propertyOrder; } public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return JsonSerializer.Deserialize<T>(ref reader, options); } public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) { writer.WriteStartObject(); var type = typeof(T); // 先写入指定顺序的属性 foreach (var propName in _propertyOrder) { var prop = type.GetProperty(propName); if (prop != null) { writer.WritePropertyName(propName); JsonSerializer.Serialize(writer, prop.GetValue(value), prop.PropertyType, options); } } // 写入剩余未指定的属性 foreach (var prop in type.GetProperties().Where(p => !_propertyOrder.Contains(p.Name))) { writer.WritePropertyName(prop.Name); JsonSerializer.Serialize(writer, prop.GetValue(value), prop.PropertyType, options); } writer.WriteEndObject(); } }
在Action中使用:
[HttpGet("dynamic-order")] public IActionResult GetPersonsDynamicOrder() { var persons = GetPersonList(); // 你的数据获取逻辑 var options = new JsonSerializerOptions(); options.Converters.Add(new CustomPropertyOrderConverter<SearchResultPersonDto>( new List<string> { "TaxYear", "Position", "IncomeBracket", "LastName", "FirstName" })); return Json(persons, options); }
注意事项
- 如果使用了属性重命名(如
[JsonPropertyName]或[JsonProperty]),动态顺序配置中要使用重命名后的名称而非DTO属性名。 - 全局配置会作用于所有该类型的JSON序列化,动态配置仅影响当前接口。
内容的提问来源于stack exchange,提问作者Shai
相关产品推荐
相关产品推荐

