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

C#如何使用System.Text.Json深克隆包含DynamicObject的类集合?

问题原因

System.Text.Json 的默认序列化逻辑仅会序列化类型显式声明的公共属性,不会自动读取 DynamicObject 派生类的动态成员。你自定义的 DynamicDictionary 中存储键值对的 dictionary 是私有字段,默认不会被序列化,因此得到的序列化结果里 DynamicObj 就是空对象,反序列化后自然没有值。

解决方案

推荐使用自定义 JsonConverter 的方式实现,不需要修改原有类的业务逻辑,兼容和之前 Newtonsoft.Json 一致的序列化结构:

第一步:实现自定义转换器和运行时绑定器

using System.Text.Json;
using System.Text.Json.Serialization;
using System.Dynamic;

public class DynamicDictionaryConverter : JsonConverter<DynamicDictionary>
{
    public override DynamicDictionary? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if (reader.TokenType != JsonTokenType.StartObject)
        {
            throw new JsonException();
        }

        var dynamicDict = new DynamicDictionary();
        // 反射获取内部存储字典,也可以提前给DynamicDictionary加公共访问方法避免反射
        var innerDict = (Dictionary<string, object>)typeof(DynamicDictionary)
            .GetField("dictionary", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)!
            .GetValue(dynamicDict)!;

        while (reader.Read())
        {
            if (reader.TokenType == JsonTokenType.EndObject)
            {
                return dynamicDict;
            }
            if (reader.TokenType != JsonTokenType.PropertyName)
            {
                throw new JsonException();
            }

            var propName = reader.GetString()!;
            reader.Read();
            var value = JsonSerializer.Deserialize<object>(ref reader, options);
            innerDict[propName] = value;
        }
        throw new JsonException();
    }

    public override void Write(Utf8JsonWriter writer, DynamicDictionary value, JsonSerializerOptions options)
    {
        writer.WriteStartObject();
        foreach (var memberName in value.GetDynamicMemberNames())
        {
            value.TryGetMember(new RuntimeGetMemberBinder(memberName, false), out var memberValue);
            writer.WritePropertyName(memberName);
            JsonSerializer.Serialize(writer, memberValue, options);
        }
        writer.WriteEndObject();
    }
}

// 实现运行时获取成员绑定器
public class RuntimeGetMemberBinder : GetMemberBinder
{
    public RuntimeGetMemberBinder(string name, bool ignoreCase) : base(name, ignoreCase) { }
    public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject? errorSuggestion)
    {
        throw new NotImplementedException();
    }
}

第二步:序列化时传入配置

var jsonOptions = new JsonSerializerOptions
{
    Converters = { new DynamicDictionaryConverter() }
};

// 执行深克隆
var clonedCollection = JsonSerializer.Deserialize<List<SerializeExtensions>>(
    JsonSerializer.Serialize(originalCollection, jsonOptions), 
    jsonOptions
);

轻量替代方案(仅内部克隆可用)

如果不要求序列化结构和之前一致,可以直接修改 DynamicDictionary 类,给内部字典加序列化允许标记:

public class DynamicDictionary : System.Dynamic.DynamicObject
{
    [JsonInclude] // 允许序列化私有/公共字段
    public Dictionary<string, object> dictionary = new Dictionary<string, object>();
    // 其余原有代码保持不变
}

这种方式不用写转换器,但是序列化结果会多一层dictionary嵌套节点,仅适合内部克隆场景使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 04:48:03