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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 15:55:17