总目录
一、什么是 ReferenceHandling
?
1. 概述
ReferenceHandling
是 System.Text.Json 中用于处理对象引用(循环引用或重复引用)的选项。它允许开发者在序列化和反序列化时控制如何处理对象之间的引用关系。
默认情况下,System.Text.Json
不支持循环引用或重复引用的处理,直接序列化会导致堆栈溢出或数据丢失。通过启用 ReferenceHandling
,可以解决以下问题:
- 循环引用:对象 A 引用对象 B,对象 B 又引用对象 A。
- 重复引用:多个属性引用同一个对象实例。
2. ReferenceHandling
的两种模式
ReferenceHandling
提供了两种模式:
- Preserve 模式:保留对象引用,使用
$id
和$ref
标记引用关系。 - IgnoreCycles 模式:忽略循环引用,避免堆栈溢出。
3. 使用场景
- 循环引用:例如,树形结构或双向关联的对象。
- 重复引用:例如,多个属性引用同一个对象实例。
- 复杂对象图:需要完整保留对象引用关系的场景。
二、如何使用ReferenceHandling
1. 循环引用(Preserve 模式)
假设我们有一个简单的类,包含循环引用:
public class Node
{public string Name { get; set; }public Node Next { get; set; }
}// 创建循环引用
var nodeA = new Node { Name = "NodeA" };
var nodeB = new Node { Name = "NodeB" };
nodeA.Next = nodeB;
nodeB.Next = nodeA;// 配置 JsonSerializerOptions
var options = new JsonSerializerOptions
{ReferenceHandler = ReferenceHandler.Preserve, // 保留引用WriteIndented = true // 美化输出
};// 序列化
string json = JsonSerializer.Serialize(nodeA, options);
Console.WriteLine(json);
输出结果:
{"$id": "1","Name": "NodeA","Next": {"$id": "2","Name": "NodeB","Next": {"$ref": "1"}}
}
解释:
$id
:唯一标识对象实例。$ref
:引用其他对象实例。
2. 循环引用(IgnoreCycles 模式)
如果不想保留引用关系,可以选择忽略循环引用:
var options = new JsonSerializerOptions
{ReferenceHandler = ReferenceHandler.IgnoreCycles, // 忽略循环引用WriteIndented = true // 美化输出
};// 序列化
string json = JsonSerializer.Serialize(nodeA, options);
Console.WriteLine(json);
输出结果:
{"Name": "NodeA","Next": {"Name": "NodeB","Next": null}
}
解释:
- 循环引用的部分被设置为
null
,避免堆栈溢出。
3. 重复引用
假设我们有一个对象被多个属性引用:
public class Employee
{public string Name { get; set; }public Employee Manager { get; set; }
}// 创建重复引用
var manager = new Employee { Name = "Alice" };
var employee = new Employee { Name = "Bob", Manager = manager };
manager.Manager = manager; // 自己是自己的经理// 配置 JsonSerializerOptions
var options = new JsonSerializerOptions
{ReferenceHandler = ReferenceHandler.Preserve, // 保留引用WriteIndented = true // 美化输出
};// 序列化
string json = JsonSerializer.Serialize(employee, options);
Console.WriteLine(json);
输出结果:
{"$id": "1","Name": "Bob","Manager": {"$id": "2","Name": "Alice","Manager": {"$ref": "2"}}
}
解释:
$id
和$ref
确保重复引用不会导致无限递归。
4. 反序列化(Preserve 模式)
反序列化时,ReferenceHandler.Preserve
会自动恢复对象引用关系。
var json = @"
{""$id"": ""1"",""Name"": ""Bob"",""Manager"": {""$id"": ""2"",""Name"": ""Alice"",""Manager"": {""$ref"": ""2""}}
}";var options = new JsonSerializerOptions
{ReferenceHandler = ReferenceHandler.Preserve // 保留引用
};var employee = JsonSerializer.Deserialize<Employee>(json, options);// 验证引用关系
Console.WriteLine(employee.Name); // 输出:Bob
Console.WriteLine(employee.Manager.Name); // 输出:Alice
Console.WriteLine(employee.Manager.Manager == employee.Manager); // 输出:True
解释:
- 反序列化后,
Manager
的引用关系正确恢复。
三、注意事项
- 性能开销:
- 启用
ReferenceHandler.Preserve
会增加一定的性能开销,因为需要维护$id
和$ref
。
- 启用
- 兼容性:
- 并非所有 JSON 解析器都支持
$id
和$ref
,需确保目标系统兼容。
- 并非所有 JSON 解析器都支持
- 默认行为:
- 默认情况下,
System.Text.Json
不支持循环引用,必须显式启用ReferenceHandler
。
- 默认情况下,
结语
回到目录页:C#/.NET 知识汇总
希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。
参考资料:
- .NET 官方文档:ReferenceHandler
- System.Text.Json 完全指南