如何使用System.Text.Json将IEnumerable序列化为字典形式
在.NET 6中用System.Text.Json实现主键作为JSON对象键的序列化
首先需要纠正一个小问题:你想要的目标JSON格式不能是数组([]),因为JSON数组不支持键值对结构,正确的目标格式应该是一个JSON对象({}),示例如下:
{ "0": { "Salary": 5000.00 }, "1": { "Salary": 6000.00 } }
下面提供两种实用的实现方案:
方案一:直接转换为Dictionary序列化(简单快速)
如果只需要处理特定类型(比如Employee),可以直接在业务代码中将集合转换为Dictionary,再序列化。这种方式不需要自定义转换器,代码简洁:
// 假设已有Employee集合 var employeeList = new List<Employee> { new Employee { Id = 0, Salary = 5000.00m }, new Employee { Id = 1, Salary = 6000.00m } }; // 转换为Dictionary,键是Id,值是排除Id的匿名对象 var keyedDictionary = employeeList.ToDictionary( e => e.Id, e => new { e.Salary } ); // 序列化得到目标JSON var json = JsonSerializer.Serialize(keyedDictionary, new JsonSerializerOptions { WriteIndented = true });
方案二:自定义通用JsonConverter(适用于多类型场景)
如果多个类都需要类似的序列化逻辑,可以通过自定义转换器+特性标记的方式实现通用处理:
步骤1:定义标记主键的特性
创建一个特性类,用来标记类中的主键属性:
[AttributeUsage(AttributeTargets.Property)] public class JsonPrimaryKeyAttribute : Attribute { }
步骤2:标记实体类的主键
在Employee类的主键属性上添加特性:
public class Employee { [JsonPrimaryKey] public int Id { get; set; } public decimal Salary { get; set; } }
步骤3:实现通用JsonConverter
编写泛型转换器,自动识别标记了特性的主键,并将集合序列化为键值对对象:
public class PrimaryKeyDictionaryConverter<T> : JsonConverter<IEnumerable<T>> { public override IEnumerable<T> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { // 若不需要反序列化,可直接抛出异常或返回空集合 throw new NotImplementedException("反序列化逻辑未实现"); } public override void Write(Utf8JsonWriter writer, IEnumerable<T> value, JsonSerializerOptions options) { // 获取标记了JsonPrimaryKeyAttribute的主键属性 var primaryKeyProp = typeof(T).GetProperties() .FirstOrDefault(p => p.GetCustomAttribute<JsonPrimaryKeyAttribute>() != null); if (primaryKeyProp == null) throw new InvalidOperationException($"类型 {typeof(T).Name} 未标记 [JsonPrimaryKey] 属性"); writer.WriteStartObject(); foreach (var item in value) { // 获取主键值并转为字符串作为JSON键 var keyValue = primaryKeyProp.GetValue(item)?.ToString(); if (string.IsNullOrEmpty(keyValue)) continue; writer.WritePropertyName(keyValue); // 序列化时排除主键属性 var serializeOptions = new JsonSerializerOptions(options); // 移除当前转换器避免递归调用 serializeOptions.Converters.Remove(this); // 添加忽略主键特性的策略 serializeOptions.TypeInfoResolver = new DefaultJsonTypeInfoResolver { Modifiers = { typeInfo => { if (typeInfo.Type == typeof(T)) { var keyProp = typeInfo.Properties.FirstOrDefault(p => p.Name == primaryKeyProp.Name); if (keyProp != null) keyProp.Ignored = true; } }} }; // 序列化当前对象(已排除主键) JsonSerializer.Serialize(writer, item, serializeOptions); } writer.WriteEndObject(); } }
步骤4:使用转换器
可以通过特性标记在属性上,或者在序列化选项中添加转换器:
方式A:通过特性标记属性
public class ApiResponse { [JsonConverter(typeof(PrimaryKeyDictionaryConverter<Employee>))] public IEnumerable<Employee> Employees { get; set; } } // 序列化ApiResponse对象 var response = new ApiResponse { Employees = employeeList }; var json = JsonSerializer.Serialize(response, new JsonSerializerOptions { WriteIndented = true });
方式B:全局添加转换器
var options = new JsonSerializerOptions { WriteIndented = true, Converters = { new PrimaryKeyDictionaryConverter<Employee>() } }; var json = JsonSerializer.Serialize(employeeList, options);
内容的提问来源于stack exchange,提问作者Joqsun
相关产品推荐
相关产品推荐

