Json是一个单遍深度优先的串行化程序.当启用reference preservation时,对象将在遇到first time时被序列化.只有"$ref": "N"
会在随后的遭遇中被序列化.因此,更改属性顺序可能会导致超过MaxDepth
.
例如,考虑下面的模型,它包含一个父级列表和一个所有子级列表.父母有子元素,子元素有朋友:
public class Model
{
public List<Child> AllChildren { get; set; }= new();
public List<Parent> Parents { get; set; }= new();
}
public class Parent
{
public List<Child> Children { get; set; } = new();
}
public class Child
{
public List<Child> Friends { get; set; } = new ();
}
如果我构造一个包含2个父代和2个子代的实例,其中每个子代都是朋友:
var child1 = new Child { };
var child2 = new Child { Friends = { child1 } };
child1.Friends.Add(child2);
var model = new TModel
{
AllChildren = { child1, child2 },
Parents = { new Parent { Children = { child1 } }, new Parent { Children = { child2 } } },
};
则该模型将在MaxDepth = 10
处成功串行化,并在MaxDepth = 9
处失败.演示小提琴#1here.
现在假设我修改了Model
以首先序列化父级:
public class Model
{
public List<Parent> Parents { get; set; }= new();
public List<Child> AllChildren { get; set; }= new();
}
然后是serialization will fail at 101 10, 11 and 12.它将仅从MaxDepth = 13
开始成功串行化.演示小提琴#2here.
造成差异的原因是前面提到的深度优先序列化.在第一个版本中,第二个子元素被序列化为AllChildren
列表中第一个子元素的朋友:
{
"$id": "1",
"AllChildren": {
"$id": "2",
"$values": [
{
"$id": "3",
"Friends": {
"$id": "4",
"$values": [
{
"$id": "5",
"Friends": {
"$id": "6", // Here is where the second child is serialized.
"$values": [
{
"$ref": "3"
}
]
}
}
]
}
},
{
"$ref": "5"
}
]
},
"Parents": {
"$id": "7",
"$values": [
{
"$id": "8",
"Children": {
"$id": "9",
"$values": [
{
"$ref": "3"
}
]
}
},
{
"$id": "10",
"Children": {
"$id": "11",
"$values": [
{
"$ref": "5"
}
]
}
}
]
}
}
但在第二个版本中,第二个子级在Parents
列表内被序列化,导致最大深度增加:
Testing MaxDepth = 13:
{
"$id": "1",
"Parents": {
"$id": "2",
"$values": [
{
"$id": "3",
"Children": {
"$id": "4",
"$values": [
{
"$id": "5",
"Friends": {
"$id": "6",
"$values": [
{
"$id": "7",
"Friends": {
"$id": "8", // Here is where the second child is serialized. Notice it's deeper in the graph.
"$values": [
{
"$ref": "5"
}
]
}
}
]
}
}
]
}
},
{
"$id": "9",
"Children": {
"$id": "10",
"$values": [
{
"$ref": "7"
}
]
}
}
]
},
"AllChildren": {
"$id": "11",
"$values": [
{
"$ref": "5"
},
{
"$ref": "7"
}
]
}
}
To prevent this,您可以将JsonPropertyOrderAttribute
应用于您的属性,以强制在父级之前序列化子级:
public class Model
{
[JsonPropertyOrder(2)]
public List<Parent> Parents { get; set; }= new();
[JsonPropertyOrder(1)]
public List<Child> AllChildren { get; set; }= new();
}
如果这样做,序列化将不再依赖于C#属性顺序.演示#3 here.