简体   繁体   English

循环引用的DataContract序列化对于集合的第一项来说作用太深,而对其余项的作用太细

[英]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: 这意味着:

  • The first time item1 is encountered is in the root list. 一次遇到item1是在根列表中。 It gets assigned a reference id z:Id="i1" 它被分配了参考ID z:Id="i1"
  • The first time item2 is encountered is as a child of item1 . 一次遇到item2是item1子项 It gets assigned a reference id z:Id="i2" 它被分配了参考ID z:Id="i2"
  • The second time item2 is encountered is in the root list. 第二次遇到item2是在根列表中。 In that case, it appears only as an indirect reference 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM