[英]DataContract serialization of circular references works too deep for the first item of collection and too finely for the rest
I have two classes which refer on each other: 我有两个相互引用的类:
[DataContract(IsReference = true)]
public class Student
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public string Surname { get; set; }
[DataMember]
public List<Course> Courses { get; set; }
public Student()
{
Courses = new List<Course>();
}
}
[DataContract(IsReference = true)]
public class Course
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public List<Student> Students { get; set; }
public Course()
{
Students = new List<Student>();
}
}
I want to serialize 4 Student entities, each of them has some courses attached. 我想序列化4个Student实体,每个实体都有一些课程。 after serialization only the first Student entity appears not empty.
序列化后,只有第一个Student实体显示为非空。 What is more, it has too many reference levels.
而且,它具有太多的参考水平。 And all others Student entities are empty:
所有其他学生实体均为空:
<GetStudentsResult
xmlns:a="http://schemas.datacontract.org/2004/07/WcfService2"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:Student xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/"
z:Id="i1">
<a:Courses>
<a:Course z:Id="i2">
<a:Id>1</a:Id>
<a:Name>Operation Systems</a:Name>
<a:Students>
<a:Student z:Ref="i1"/>
<a:Student z:Id="i3">
<a:Courses>
<a:Course z:Ref="i2"/>
<a:Course z:Id="i4">
<a:Id>2</a:Id>
<a:Name>Algorithmes and data structures</a:Name>
<a:Students>
<a:Student z:Ref="i3"/>
<a:Student z:Id="i5">
<a:Courses>
<a:Course z:Ref="i4"/>
<a:Course z:Id="i6">
<a:Id>3</a:Id>
<a:Name>Basics of HTML and CSS</a:Name>
<a:Students>
<a:Student z:Ref="i1"/>
<a:Student z:Id="i7">
<a:Courses>
<a:Course z:Ref="i2"/>
<a:Course z:Ref="i6"/>
</a:Courses>
<a:Id>3</a:Id>
<a:Name>Oleg</a:Name>
<a:Surname>Kuznetsov</a:Surname>
</a:Student>
<a:Student z:Ref="i5"/>
</a:Students>
</a:Course>
</a:Courses>
<a:Id>4</a:Id>
<a:Name>Olga</a:Name>
<a:Surname>Petrova</a:Surname>
</a:Student>
</a:Students>
</a:Course>
</a:Courses>
<a:Id>2</a:Id>
<a:Name>Maria</a:Name>
<a:Surname>Vasilyeva</a:Surname>
</a:Student>
<a:Student z:Ref="i7"/>
</a:Students>
</a:Course>
<a:Course z:Ref="i6"/>
</a:Courses>
<a:Id>1</a:Id>
<a:Name>Egor</a:Name>
<a:Surname>Ivanov</a:Surname>
</a:Student>
<a:Student xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/"
z:Ref="i3"/>
<a:Student xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/"
z:Ref="i7"/>
<a:Student xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/"
z:Ref="i5"/>
</GetStudentsResult>
Here is my code, filling Student and Course collections: 这是我的代码,填充了学生和课程集合:
Student s1 = new Student { Id = 1, Name = "Egor", Surname = "Ivanov" };
Student s2 = new Student { Id = 2, Name = "Maria", Surname = "Vasilyeva" };
Student s3 = new Student { Id = 3, Name = "Oleg", Surname = "Kuznetsov" };
Student s4 = new Student { Id = 4, Name = "Olga", Surname = "Petrova" };
context.Students.Add(s1);
context.Students.Add(s2);
context.Students.Add(s3);
context.Students.Add(s4);
Course c1 = new Course
{
Id = 1,
Name = "Operation Systems",
Students = new List<Student>() { s1, s2, s3 }
};
Course c2 = new Course
{
Id = 2,
Name = "Algorithmes and data structures",
Students = new List<Student>() { s2, s4 }
};
Course c3 = new Course
{
Id = 3,
Name = "Basics of HTML and CSS",
Students = new List<Student>() { s3, s4, s1 }
};
context.Courses.Add(c1);
context.Courses.Add(c2);
context.Courses.Add(c3);
Please advise. 请指教。
What you are seeing is not a bug. 您所看到的不是错误。 The
IsReference = true
functionality of the data contract serializers never creates forward declarations in the XML file, only backwards references. 数据协定序列化程序的
IsReference = true
功能从不在XML文件中创建前向声明 ,而仅向后引用。 Let's take a simple case: 我们来看一个简单的例子:
[DataContract(IsReference = true, Namespace="")]
public class Item
{
[DataMember]
public int Id { get; set; }
[DataMember]
public Item SubItem { get; set; }
}
public class TestClass
{
public static void TestSimple()
{
var item1 = new Item { Id = 1 };
var item2 = new Item { Id = 2 };
item1.SubItem = item2;
var xml = DataContractSerializerHelper.GetXml(new[] { item1, item2 });
Debug.WriteLine(xml);
}
}
The XML created by this test case is looks like: 该测试用例创建的XML如下所示:
<ArrayOfItem xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Item z:Id="i1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
<Id>1</Id>
<SubItem z:Id="i2">
<Id>2</Id>
<SubItem i:nil="true" />
</SubItem>
</Item>
<Item z:Ref="i2" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" />
</ArrayOfItem>
What this means is: 这意味着:
z:Id="i1"
z:Id="i1"
z:Id="i2"
z:Id="i2"
z:Id="i2"
. z:Id="i2"
。 Ie, when serializing, the first time the data contract serializer encounters a reference object, it assigns it a reference id, saves that id in a dictionary, and proceeds to serialize the object. 即,在序列化时,数据协定序列化程序第一次遇到参考对象时,会为其分配参考ID,然后将该ID保存在字典中,然后继续序列化对象。 Then, on subsequent encounters, it serializes a reference to that id, and nothing else.
然后,在随后的相遇中,它会序列化对该ID的引用,而别无其他。 Then, when deserializing, it does the reverse: when a reference id is encountered as an attribute of an element, the object created for that element is put in a dictionary, then is deserialized.
然后,在反序列化时,它会执行相反的操作:当引用ID作为元素的属性遇到时,为该元素创建的对象将放入字典中,然后反序列化。 On subsequent encounters to that id, the saved object is taken from the dictionary and inserted in the object graph.
在随后遇到该ID时,已保存的对象将从字典中取出并插入到对象图中。
This simple one-pass algorithm does not allow for forward declarations. 这种简单的一遍算法不允许进行前向声明。
It appears that you would prefer that reference objects be serialized at the highest possible level in the XML with forwards or backwards references inserted as needed in nested elements, like so: 似乎您希望引用对象在XML中以尽可能高的级别进行序列化,并在嵌套元素中根据需要插入向前或向后引用,如下所示:
<ArrayOfItem xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Item z:Id="i1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
<Id>1</Id>
<SubItem z:Ref="i2"/> <!-- FORWARD DECLARATION OF z:Id="i2" -->
</Item>
<Item z:Id="i2" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
<Id>2</Id>
<SubItem i:nil="true" />
</Item>
</ArrayOfItem>
Unfortunately, the data contact serializer doesn't do that. 不幸的是,数据联系人序列化程序无法做到这一点。
You can see that, by indenting your XML, all students are properly included -- all but the first as nested elements of the list of courses of the first student. 您可以看到,通过缩进 XML,可以正确地包含所有学生-除第一个学生外,所有学生都作为第一个学生课程列表中的嵌套元素。 Then subsequent occurrences are just references:
然后后续出现的只是参考:
<GetStudentsResult xmlns:a="http://schemas.datacontract.org/2004/07/WcfService2" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:Student xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" z:Id="i1">
<a:Courses>
<a:Course z:Id="i2">
<a:Id>1</a:Id>
<a:Name>Operation Systems</a:Name>
<a:Students>
<a:Student z:Ref="i1" />
<a:Student z:Id="i3">
<a:Courses>
<a:Course z:Ref="i2" />
<a:Course z:Id="i4">
<a:Id>2</a:Id>
<a:Name>Algorithmes and data structures</a:Name>
<a:Students>
<a:Student z:Ref="i3" />
<a:Student z:Id="i5">
<a:Courses>
<a:Course z:Ref="i4" />
<a:Course z:Id="i6">
<a:Id>3</a:Id>
<a:Name>Basics of HTML and CSS</a:Name>
<a:Students>
<a:Student z:Ref="i1" />
<a:Student z:Id="i7">
<a:Courses>
<a:Course z:Ref="i2" />
<a:Course z:Ref="i6" />
</a:Courses>
<a:Id>3</a:Id>
<a:Name>Oleg</a:Name>
<a:Surname>Kuznetsov</a:Surname>
</a:Student>
<a:Student z:Ref="i5" />
</a:Students>
</a:Course>
</a:Courses>
<a:Id>4</a:Id>
<a:Name>Olga</a:Name>
<a:Surname>Petrova</a:Surname>
</a:Student>
</a:Students>
</a:Course>
</a:Courses>
<a:Id>2</a:Id>
<a:Name>Maria</a:Name>
<a:Surname>Vasilyeva</a:Surname>
</a:Student>
<a:Student z:Ref="i7" />
</a:Students>
</a:Course>
<a:Course z:Ref="i6" />
</a:Courses>
<a:Id>1</a:Id>
<a:Name>Egor</a:Name>
<a:Surname>Ivanov</a:Surname>
</a:Student>
<a:Student xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" z:Ref="i3" />
<a:Student xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" z:Ref="i7" />
<a:Student xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" z:Ref="i5" />
</GetStudentsResult>
If you deserialize that XML with DataContractSerializer
, you will find your graph of students and courses correctly restored with the same topology as the original graph. 如果使用
DataContractSerializer
对XML进行反序列化,则会发现您的学生和课程图已正确还原,并具有与原始图相同的拓扑。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.