如何使用System.Text.Json按类定制属性与字典键的序列化规则
核心问题说明
- System.Text.Json默认序列化继承
Dictionary<,>的类型时,仅输出字典内的键值对,不会序列化类自身定义的公共属性 - 全局
PropertyNamingPolicy无法针对不同类型实现差异化的键命名规则
实现方案
通过为Product和Store分别定义专属的JsonConverter,同时处理自有属性序列化、差异化键命名两个需求。
步骤1:定义两个自定义转换器
using System.Reflection; using System.Text.Json; using System.Text.Json.Serialization; public class ProductJsonConverter : JsonConverter<Product> { // 不需要反序列化逻辑直接抛出异常 public override Product Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { throw new NotImplementedException(); } public override void Write(Utf8JsonWriter writer, Product value, JsonSerializerOptions options) { writer.WriteStartObject(); // 序列化类自有属性,过滤Dictionary自带的Keys/Values/Count等属性 var selfProperties = typeof(Product).GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(p => !typeof(Dictionary<string, object>).GetProperties().Select(dp => dp.Name).Contains(p.Name) && p.GetMethod != null); foreach (var prop in selfProperties) { var propValue = prop.GetValue(value); if (propValue == null) continue; // 自有属性名转全小写 writer.WritePropertyName(prop.Name.ToLowerInvariant()); JsonSerializer.Serialize(writer, propValue, prop.PropertyType, options); } // 序列化字典内的键值对,键转全小写 foreach (var kvp in value) { writer.WritePropertyName(kvp.Key.ToLowerInvariant()); JsonSerializer.Serialize(writer, kvp.Value, options); } writer.WriteEndObject(); } } public class StoreJsonConverter : JsonConverter<Store> { // 字典键需要拼接的前缀,可按需修改 private const string KeyPrefix = "prefix_"; public override Store Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { throw new NotImplementedException(); } public override void Write(Utf8JsonWriter writer, Store value, JsonSerializerOptions options) { writer.WriteStartObject(); // 序列化类自有属性 var selfProperties = typeof(Store).GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(p => !typeof(Dictionary<string, object>).GetProperties().Select(dp => dp.Name).Contains(p.Name) && p.GetMethod != null); foreach (var prop in selfProperties) { var propValue = prop.GetValue(value); if (propValue == null) continue; writer.WritePropertyName(prop.Name.ToLowerInvariant()); JsonSerializer.Serialize(writer, propValue, prop.PropertyType, options); } // 序列化字典内的键值对,键转全小写后拼接前缀 foreach (var kvp in value) { writer.WritePropertyName($"{KeyPrefix}{kvp.Key.ToLowerInvariant()}"); JsonSerializer.Serialize(writer, kvp.Value, options); } writer.WriteEndObject(); } }
步骤2:给对应类绑定转换器
直接在类定义上添加JsonConverter特性,序列化时会自动触发对应转换器逻辑:
[JsonConverter(typeof(ProductJsonConverter))] public class Product : Dictionary<string, object> { public string ProductId { get; set; } public IList<Store> Stores { get; set; } } [JsonConverter(typeof(StoreJsonConverter))] public class Store : Dictionary<string, object> { public string StoreId { get; set; } }
步骤3:直接执行序列化
不需要额外配置全局命名策略,直接正常序列化即可得到符合要求的输出:
IEnumerable<Product> products = GetProducts(); var serializeOptions = new JsonSerializerOptions { WriteIndented = true // 可选,格式化输出JSON }; string resultJson = JsonSerializer.Serialize(products, serializeOptions);
注意事项
- 转换器中已经过滤了
Dictionary自带的Keys、Values、Count等内置属性,不会输出冗余内容 - 若后续给
Product或Store新增自有属性,不需要修改转换器代码,会自动兼容处理
内容的提问来源于stack exchange,提问作者Develorem
相关产品推荐
相关产品推荐

