BinaryFormatter已弃用,如何用System.Text.Json反序列化完整对象图?
问题分析与解决方案
1. "仅根节点被加载"是假象
你的反序列化其实已经成功加载了完整对象树,问题出在Node类没有重写ToString()方法。当执行Console.WriteLine(loadFile)时,调用的是object基类的默认ToString(),只会输出类型名称Node,而非对象的实际内容,导致你误以为子节点未加载。
验证方式:给Node类添加ToString()重写:
public override string ToString() { return $"Name: {Name}, 子节点数量: {Nodes.Count}"; }
或者手动遍历输出节点结构:
Console.WriteLine($"根节点: {loadFile.Name}, 子节点数: {loadFile.Nodes.Count}"); foreach (var aNode in loadFile.Nodes) { Console.WriteLine($" 子节点: {aNode.Name}, 孙节点数: {aNode.Nodes.Count}"); }
执行后就能看到所有节点都已正确反序列化。
2. System.Text.Json完全可以实现完整对象树的序列化/反序列化
你的示例是树结构(无循环引用),System.Text.Json默认就能处理,无需额外特性。如果实际DOM存在循环引用(比如子节点引用父节点),只需在配置中启用引用处理:
循环引用处理配置
修改JsonSerializerOptions,启用引用保留:
var options = new JsonSerializerOptions { WriteIndented = true, ReferenceHandler = ReferenceHandler.Preserve // 启用后可处理循环引用 };
Node类无需额外特性,System.Text.Json会自动完成对象引用的序列化与还原。
完整可运行示例
using System.Text.Json; var root = new Node("root"); var a1 = new Node("a1"); var a2 = new Node("a2"); var b11 = new Node("b11"); var b12 = new Node("b12"); var b21 = new Node("b21"); var b22 = new Node("b22"); root.Nodes.Add(a1); root.Nodes.Add(a2); a1.Nodes.Add(b11); a1.Nodes.Add(b12); a2.Nodes.Add(b21); a2.Nodes.Add(b22); var options = new JsonSerializerOptions { WriteIndented = true, ReferenceHandler = ReferenceHandler.Preserve }; string saveFile = JsonSerializer.Serialize(root, options); Console.WriteLine("序列化结果:"); Console.WriteLine(saveFile); var loadFile = JsonSerializer.Deserialize<Node>(saveFile, options)!; Console.WriteLine(); Console.WriteLine("反序列化后结构:"); Console.WriteLine($"根节点: {loadFile.Name}, 子节点数: {loadFile.Nodes.Count}"); foreach (var child in loadFile.Nodes) { Console.WriteLine($" 子节点: {child.Name}, 孙节点数: {child.Nodes.Count}"); foreach (var grandchild in child.Nodes) { Console.WriteLine($" 孙节点: {grandchild.Name}"); } } public class Node { public Node(string name) { Name = name; } public string Name { get; set; } public List<Node> Nodes { get; set; } = new List<Node>(); public override string ToString() { return $"Node(Name: {Name}, 子节点数: {Nodes.Count})"; } }
3. 保存DOM到磁盘的其他建议
- 无需人类可读场景:可以选择高效二进制序列化方案,比如MessagePack(紧凑高效)、Protobuf-net(跨平台、性能优异)。
- 需要人类可读场景:继续使用JSON即可,System.Text.Json是官方原生库,性能更优;Newtonsoft.Json功能更丰富,对旧代码兼容性更好,两者都能处理对象树/图。
- 循环引用注意事项:如果DOM存在循环引用,必须启用对应引用处理配置,否则序列化会直接失败。
内容的提问来源于stack exchange,提问作者Zoidboom
相关产品推荐
相关产品推荐

