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

JSON Serialization/Contract Resolvers:将内嵌键值对列表转为同级键值对

实现方案

针对你需要将CustomFields中的键值对与固定属性(如part、quant)平级序列化的需求,以下是几种最优实现方式:

一、Json.NET(Newtonsoft.Json)自定义ContractResolver(推荐)

这种方式无需修改原有类结构,直接在序列化阶段动态展开CustomFields,同时支持反序列化,适合长期使用。

1. 原有类定义

public class OrderItem
{
    public string Part { get; set; }
    public int Quant { get; set; }
    public Dictionary<string, object> CustomFields { get; set; } = new Dictionary<string, object>();
}

2. 自定义ContractResolver和ValueProvider

public class CustomFieldsResolver : DefaultContractResolver
{
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        var properties = base.CreateProperties(type, memberSerialization);
        var customFieldsProp = properties.FirstOrDefault(p => p.PropertyName == nameof(OrderItem.CustomFields));
        
        if (customFieldsProp != null)
        {
            // 移除原CustomFields属性
            properties.Remove(customFieldsProp);
            
            // 动态添加CustomFields中的键作为序列化属性
            var dummyInstance = Activator.CreateInstance(type);
            var customFields = dummyInstance.GetType().GetProperty(nameof(OrderItem.CustomFields))
                .GetValue(dummyInstance) as Dictionary<string, object>;
            
            if (customFields != null)
            {
                foreach (var kvp in customFields)
                {
                    properties.Add(new JsonProperty
                    {
                        PropertyName = kvp.Key,
                        Readable = true,
                        Writable = true,
                        ValueProvider = new CustomFieldValueProvider(kvp.Key)
                    });
                }
            }
        }
        return properties;
    }
}

public class CustomFieldValueProvider : IValueProvider
{
    private readonly string _key;

    public CustomFieldValueProvider(string key) => _key = key;

    public object GetValue(object target)
    {
        var customFields = target.GetType().GetProperty(nameof(OrderItem.CustomFields))
            .GetValue(target) as Dictionary<string, object>;
        customFields.TryGetValue(_key, out var value);
        return value;
    }

    public void SetValue(object target, object value)
    {
        var customFields = target.GetType().GetProperty(nameof(OrderItem.CustomFields))
            .GetValue(target) as Dictionary<string, object>;
        customFields[_key] = value;
    }
}

3. 使用示例

var item = new OrderItem
{
    Part = "A123",
    Quant = 5,
    CustomFields = new Dictionary<string, object>
    {
        ["Color"] = "Red",
        ["Size"] = "XL"
    }
};

var settings = new JsonSerializerSettings
{
    ContractResolver = new CustomFieldsResolver(),
    Formatting = Formatting.Indented
};

var json = JsonConvert.SerializeObject(item, settings);

输出结果

{
  "Part": "A123",
  "Quant": 5,
  "Color": "Red",
  "Size": "XL"
}

二、System.Text.Json 自定义JsonConverter

如果你使用的是.NET Core/.NET 5+自带的System.Text.Json,可通过自定义转换器实现同样效果:

转换器代码

public class OrderItemConverter : JsonConverter<OrderItem>
{
    public override OrderItem Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        var item = new OrderItem();
        using var doc = JsonDocument.ParseValue(ref reader);
        var root = doc.RootElement;

        // 读取固定属性
        if (root.TryGetProperty(nameof(OrderItem.Part), out var partProp))
            item.Part = partProp.GetString();
        if (root.TryGetProperty(nameof(OrderItem.Quant), out var quantProp))
            item.Quant = quantProp.GetInt32();

        // 读取剩余属性到CustomFields
        foreach (var prop in root.EnumerateObject())
        {
            if (prop.Name != nameof(OrderItem.Part) && prop.Name != nameof(OrderItem.Quant))
            {
                item.CustomFields[prop.Name] = JsonSerializer.Deserialize<object>(prop.Value.GetRawText(), options);
            }
        }
        return item;
    }

    public override void Write(Utf8JsonWriter writer, OrderItem value, JsonSerializerOptions options)
    {
        writer.WriteStartObject();
        // 写入固定属性
        writer.WriteString(nameof(OrderItem.Part), value.Part);
        writer.WriteNumber(nameof(OrderItem.Quant), value.Quant);

        // 写入CustomFields中的键值对
        foreach (var kvp in value.CustomFields)
        {
            writer.WritePropertyName(kvp.Key);
            JsonSerializer.Serialize(writer, kvp.Value, options);
        }
        writer.WriteEndObject();
    }
}

使用示例

var options = new JsonSerializerOptions
{
    Converters = { new OrderItemConverter() },
    WriteIndented = true
};

var json = JsonSerializer.Serialize(item, options);

三、ExpandoObject 临时转换(适合一次性场景)

如果只是临时序列化需求,无需支持反序列化,可将对象转换为ExpandoObject后再序列化:

public static ExpandoObject ConvertToExpando(OrderItem item)
{
    var expando = new ExpandoObject();
    var expandoDict = (IDictionary<string, object>)expando;

    // 复制固定属性
    var props = item.GetType().GetProperties().Where(p => p.Name != nameof(OrderItem.CustomFields));
    foreach (var prop in props)
    {
        expandoDict[prop.Name] = prop.GetValue(item);
    }

    // 添加CustomFields中的键值对
    foreach (var kvp in item.CustomFields)
    {
        expandoDict[kvp.Key] = kvp.Value;
    }

    return expando;
}

使用示例

var expandoItem = ConvertToExpando(item);
var json = JsonConvert.SerializeObject(expandoItem, Formatting.Indented);

方案对比

  • ContractResolver/JsonConverter:最优长期方案,无需修改业务类结构,支持正反序列化,集成度高。
  • ExpandoObject:代码量少,适合临时序列化场景,但反序列化需要额外处理,不适合复杂业务流程。

内容的提问来源于stack exchange,提问作者Martian99

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 07:48:27