.net XML序列化 – 存储引用而不是对象复制

  • 在.Net / C#应用程序中,我有相互引用的数据结构。
  • 当我序列化它们时.Net将所有引用与单独的对象副本序列化。
  • 在下面的例子中,我试图序列化到'人'
  • “人”可能会提及另一个人。

    public class Person { public string Name; public Person Friend; } Person p1 = new Person(); p1.Name = "John"; Person p2 = new Person(); p2.Name = "Mike"; p1.Friend = p2; Person[] group = new Person[] { p1, p2 }; XmlSerializer ser = new XmlSerializer(typeof(Person[])); using (TextWriter tw = new StreamWriter("test.xml")) ser.Serialize(tw,group ); //above code generates following xml <ArrayOfPerson> <Person> <Name>John</Name> <Friend> <Name>Mike</Name> </Friend> </Person> <Person> <Name>Mike</Name> </Person> </ArrayOfPerson> 
  • 在上面的代码中,同一个“Mike”对象在两个地方存在,因为同一个对象有两个引用。

  • 在反序列化的过程中,它们成为两个不同的对象,当它们被序列化时,这不是确切的状态。
  • 我想避免这种情况,只有在序列化的XML中的对象的副本,所有的引用应该引用这个副本。 反序列化后,我想回来,相同的旧数据结构。
  • 可能吗 ?

XmlSerializer是不可能的。 您可以使用PreserveObjectReferences属性通过DataContractSerializer实现此目的。 你可以看看这个post ,解释的细节。

这是一个示例代码:

 public class Person { public string Name; public Person Friend; } class Program { static void Main(string[] args) { Person p1 = new Person(); p1.Name = "John"; Person p2 = new Person(); p2.Name = "Mike"; p1.Friend = p2; Person[] group = new Person[] { p1, p2 }; var serializer = new DataContractSerializer(group.GetType(), null, 0x7FFF /*maxItemsInObjectGraph*/, false /*ignoreExtensionDataObject*/, true /*preserveObjectReferences : this is where the magic happens */, null /*dataContractSurrogate*/); serializer.WriteObject(Console.OpenStandardOutput(), group); } } 

这会产生以下XML:

 <ArrayOfPerson z:Id="1" z:Size="2" xmlns="http://schemas.datacontract.org/2004/07/ToDelete" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/"> <Person z:Id="2"> <Friend z:Id="3"> <Friend i:nil="true"/> <Name z:Id="4">Mike</Name> </Friend> <Name z:Id="5">John</Name> </Person> <Person z:Ref="3" i:nil="true"/> </ArrayOfPerson> 

现在在构造函数中将PreserveObjectReferences设置为false ,您将会看到:

 <ArrayOfPerson xmlns="http://schemas.datacontract.org/2004/07/ToDelete" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <Person> <Friend> <Friend i:nil="true"/> <Name>Mike</Name> </Friend> <Name>John</Name> </Person> <Person> <Friend i:nil="true"/> <Name>Mike</Name> </Person> </ArrayOfPerson> 

值得一提的是,以这种方式生成的XML不能互操作,只能用DataContractSerializer进行反序列化(与BinaryFormatter相同的注释)。

您可以使用ExtendedXmlSerializer。 这是一个序列化对象引用和循环引用的例子

如果你有一个class级:

 public class Person { public int Id { get; set; } public string Name { get; set; } public Person Boss { get; set; } } public class Company { public List<Person> Employees { get; set; } } 

那么你用循环引用创build对象,如下所示:

 var boss = new Person {Id = 1, Name = "John"}; boss.Boss = boss; //himself boss var worker = new Person {Id = 2, Name = "Oliver"}; worker.Boss = boss; var obj = new Company { Employees = new List<Person> { worker, boss } }; 

您必须将Person类configuration为参考对象:

 public class PersonConfig : ExtendedXmlSerializerConfig<Person> { public PersonConfig() { ObjectReference(p => p.Id); } } 

那么你必须注册你的PersonConfig类。 你可以用两种方法做到这一点。 使用简单的工厂或Autofac。 这是一个简单的例子:

 var toolsFactory = new SimpleSerializationToolsFactory(); // Register your config class toolsFactory.Configurations.Add(new TestClassConfig()); ExtendedXmlSerializer serializer = new ExtendedXmlSerializer(toolsFactory); 

最后你可以序列化你的对象:

 var xml = serializer.Serialize(obj); 

输出xml将如下所示:

 <?xml version="1.0" encoding="UTF-8"?> <Company type="Samples.Company"> <Employees> <Person type="Samples.Person" id="2"> <Id>2</Id> <Name>Oliver</Name> <Boss type="Samples.Person" ref="1" /> </Person> <Person type="Samples.Person" id="1"> <Id>1</Id> <Name>John</Name> <Boss type="Samples.Person" ref="1" /> </Person> </Employees> </Company> 

ExtendedXmlSerializer支持.net 4.5和.net Core。 您可以将其与WebApi和AspCore集成。