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

时间:2015-11-08 17:00:48

标签: c# serialization datacontract

我有两个相互引用的课程:

[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>();
    }
}

我想序列化4个学生实体,每个实体都附有一些课程。序列化后,只有第一个Student实体显示为空。更重要的是,它有太多的参考水平。而所有其他学生实体都是空的:

<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>

这是我的代码,填写学生和课程集:

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);

请告知。

1 个答案:

答案 0 :(得分:1)

您所看到的不是错误。数据协定序列化程序IsReference = true功能永远不会在XML文件中创建forward declarations ,只会向后引用。我们来看一个简单的案例:

[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);
    }
}

此测试用例创建的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>

这意味着:

  • 遇到的第一个时间项1位于根列表中。它会被分配一个引用ID z:Id="i1"
  • 遇到第一个时间项2是item1的子项。它会被分配一个引用ID z:Id="i2"
  • 遇到的第二个时间项2位于根列表中。在这种情况下,它仅作为间接引用z:Id="i2"出现。

即,在序列化时,数据契约序列化程序遇到引用对象的第一时间,它为其分配引用ID,将该id保存在字典中,然后继续序列化该对象。然后,在随后的遭遇中,它序列化对该id的引用,而不是其他任何内容。然后,在反序列化时,它执行相反的操作:当作为元素的属性遇到引用id时,为该元素创建的对象被放入字典中,然后被反序列化。在随后遇到该id时,保存的对象从字典中获取并插入到对象图中。

这种简单的一次通过算法不允许前向声明。

您似乎更希望在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>

不幸的是,数据联系人序列化程序并没有这样做。

您可以看到,通过缩进您的XML,所有学生都被正确包含 - 除了第一个学生的课程列表中的第一个嵌套元素之外的所有学生。然后后续出现只是引用:

<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>

如果您使用DataContractSerializer反序列化该XML,您会发现使用与原始图形相同的拓扑正确还原的学生和课程图表。