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
相关产品推荐
相关产品推荐

