You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 22:23:36