这些设置最好用例子来解释.比如说,我们想要代表一家公司的员工等级制度.我们制作了一个简单的类,如下所示:
class Employee
{
public string Name { get; set; }
public List<Employee> Subordinates { get; set; }
}
这是一家小公司,目前只有三名员工:安吉拉、鲍勃和查尔斯.安吉拉是老板,鲍勃和查尔斯是她的下属.让我们设置数据来描述这种关系:
Employee angela = new Employee { Name = "Angela Anderson" };
Employee bob = new Employee { Name = "Bob Brown" };
Employee charles = new Employee { Name = "Charles Cooper" };
angela.Subordinates = new List<Employee> { bob, charles };
List<Employee> employees = new List<Employee> { angela, bob, charles };
如果我们将员工列表序列化为JSON.
string json = JsonConvert.SerializeObject(employees, Formatting.Indented);
Console.WriteLine(json);
...我们得到这个输出:
[
{
"Name": "Angela Anderson",
"Subordinates": [
{
"Name": "Bob Brown",
"Subordinates": null
},
{
"Name": "Charles Cooper",
"Subordinates": null
}
]
},
{
"Name": "Bob Brown",
"Subordinates": null
},
{
"Name": "Charles Cooper",
"Subordinates": null
}
]
到现在为止,一直都还不错.但是,您会注意到,在JSON中重复了Bob和Charles的信息,因为代表他们的对象被主要员工列表和Angela的下属列表引用.也许现在还可以.
现在假设我们还希望有一种方法来跟踪每个员工的主管以及他或她的下属.所以我们改变了我们的Employee
型号,增加了Supervisor
属性.
class Employee
{
public string Name { get; set; }
public Employee Supervisor { get; set; }
public List<Employee> Subordinates { get; set; }
}
...在我们的设置代码中再添加几行,表示Charles和Bob向Angela报告:
Employee angela = new Employee { Name = "Angela Anderson" };
Employee bob = new Employee { Name = "Bob Brown" };
Employee charles = new Employee { Name = "Charles Cooper" };
angela.Subordinates = new List<Employee> { bob, charles };
bob.Supervisor = angela; // added this line
charles.Supervisor = angela; // added this line
List<Employee> employees = new List<Employee> { angela, bob, charles };
但现在我们有点问题.因为对象图中有引用循环(例如,angela
个引用bob
,而bob
个引用angela
),所以当我们try 序列化员工列表时,会得到JsonSerializationException
.解决这个问题的一种方法是将ReferenceLoopHandling
设置为Ignore
,如下所示:
JsonSerializerSettings settings = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
Formatting = Formatting.Indented
};
string json = JsonConvert.SerializeObject(employees, settings);
有了这个设置,我们得到了以下JSON:
[
{
"Name": "Angela Anderson",
"Supervisor": null,
"Subordinates": [
{
"Name": "Bob Brown",
"Subordinates": null
},
{
"Name": "Charles Cooper",
"Subordinates": null
}
]
},
{
"Name": "Bob Brown",
"Supervisor": {
"Name": "Angela Anderson",
"Supervisor": null,
"Subordinates": [
{
"Name": "Charles Cooper",
"Subordinates": null
}
]
},
"Subordinates": null
},
{
"Name": "Charles Cooper",
"Supervisor": {
"Name": "Angela Anderson",
"Supervisor": null,
"Subordinates": [
{
"Name": "Bob Brown",
"Subordinates": null
}
]
},
"Subordinates": null
}
]
如果您判断JSON,应该很清楚这个设置的作用:每当序列化程序遇到一个已经在序列化过程中的对象的引用时,它都会跳过该成员.(这可以防止序列化程序进入无限循环.)你可以看到,在JSON顶部Angela的下属列表中,Bob和Charles都没有显示主管.在JSON的底部,Bob和Charles都显示Angela是他们的主管,但请注意,她的下属列表此时不包括Bob和Charles.
虽然可以使用这个JSON,甚至可以通过一些工作从它重构原始对象层次 struct ,但这显然不是最优的.通过改用PreserveReferencesHandling
设置,我们可以消除JSON中的重复信息,同时仍然保留对象引用:
JsonSerializerSettings settings = new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
Formatting = Formatting.Indented
};
string json = JsonConvert.SerializeObject(employees, settings);
现在我们得到以下JSON:
[
{
"$id": "1",
"Name": "Angela Anderson",
"Supervisor": null,
"Subordinates": [
{
"$id": "2",
"Name": "Bob Brown",
"Supervisor": {
"$ref": "1"
},
"Subordinates": null
},
{
"$id": "3",
"Name": "Charles Cooper",
"Supervisor": {
"$ref": "1"
},
"Subordinates": null
}
]
},
{
"$ref": "2"
},
{
"$ref": "3"
}
]
注意,现在JSON中的每个对象都被分配了一个顺序的$id
值.对象第一次出现时,它被完全序列化,而随后的引用被特殊的$ref
属性替换,该属性引用回具有相应$id
的原始对象.有了这个设置,JSON就更简洁了,假设您使用的库能够理解Json.Net/Web API生成的$id
和$ref
表示法,那么JSON就可以反序列化为原始的对象层次 struct ,而不需要额外的工作.
那么,你为什么要 Select 一种或另一种设置呢?当然,这取决于你的需要.如果JSON将被不理解$id
/$ref
格式的客户机使用,并且它可以容忍某些地方存在不完整的数据,那么您可以 Select 使用ReferenceLoopHandling.Ignore
.如果你想要更紧凑的JSON,你会使用JSON.Net或Web API(或其他兼容库)来反序列化数据,然后您可以 Select 使用PreserveReferencesHandling.Objects
.如果数据是无重复引用的有向无环图,则不需要这两种设置.