使用自定义 ID 管理 Xml 引用对象的序列化
Manage Xml Serialization of referenced objects with a custom Id
简短版本:我需要能够 serialize/deserialize XML 并根据我设置的自定义 ID 维护引用。我需要一些通用的东西,可以通过构建引用来做到这一点 post 序列化,或者最好自定义一个 C# 序列化程序来处理这个。
我正在创建一个需要与各种应用程序通信的 WCF 应用程序。我们基本上是在构建一个非常复杂的计算器。我们确实有一个公共数据库,但是,用户对象的状态可能与数据库中的状态不同,我们不想存储这个中间状态。
我需要能够在 XML(参见 WCF)中传递复杂的相关对象,这些对象使用自定义的引用生成器。构建器需要根据每个对象具有的 Id 将对象放回一起。我希望能够指定 id,以便另一个应用程序(比如 C++)可以通过正确构建 XML 来调用我们的应用程序。
我知道 XmlSerializer 只会复制整个引用,而 DataContractSerializer 会保持引用完整性,但它当场创建一个标识符。 如何在序列化期间或序列化后解析这些引用?
如果答案是 "You can't" 那么我提出以下问题。
目前我们已经构建了一个自定义序列化程序,但速度很慢(Xml序列化程序快 8-30 倍,具体取决于文件大小)。我们的自定义序列化程序可以处理以下 XML,但它还有一个解析引用的第二步(在反序列化之后)。但是我将不得不做一些重大的返工和定制以使 XmlSerializer 能够处理它,因为我们需要 XmlSerializer 的性能。 (即使用 ID 列表并在我们所有的引用 classes 上使用 XmlIgnore 解析这些引用)。 是否有任何工具或库已经为 XML 解决步骤或序列化步骤执行此操作?
编辑:当我反序列化它时,我需要维护这些引用。如果我在 class 中更改 teacher.Name,它应该更改学校中的 teacher.Name。
我已经将我正在谈论的内容和我想用来解析它的 XML 放在一起的简化版本。
public class Teacher
{
//This is a made up annotation that represents what I'd like to happen.
[DataMember(IsReferenceId = True)]
public int Id { get; set; }
public string Name { get; set; }
//This is a made up annotation that represents what I'd like to happen.
[DataMember(IsReference = True)]
public List<Class> Classes { get; set; }
public Teacher()
{
Classes = new List<Class>();
}
public Teacher(int id)
{
Classes = new List<Class>();
Id = id;
}
}
public class Class
{
//This is a made up annotation that represents what I'd like to happen.
[DataMember(IsReferenceId = True)]
public int Id { get; set; }
public string Subject { get; set; }
//This is a made up annotation that represents what I'd like to happen.
[DataMember(IsReference = True)]
public Teacher Teacher { get; set; }
public Class()
{}
public Class(int id)
{
Id = id;
}
}
public class School
{
public string Name { get; set; }
public List<Class> Classes { get; set; }
public List<Teacher> Teachers { get; set; }
}
我希望能够像这样解析 XML。
<School>
<Classes>
<Class>
<Id>1</Id>
<Subject>Biology</Subject>
<Teacher><Id>1</Id></Teacher>
</Class>
<Class>
<Id>2</Id>
<Subject>Advanced Biology</Subject>
<Teacher><Id>1</Id></Teacher>
</Class>
<Class>
<Id>3</Id>
<Subject>Algebra</Subject>
<Teacher><Id>2</Id></Teacher>
</Class>
<Class>
<Id>4</Id>
<Subject>Trigonometry</Subject>
<Teacher><Id>2</Id></Teacher>
</Class>
</Classes>
<Teachers>
<Teacher>
<Id>1</Id>
<Name>Biology Teacher</Name>
<Classes>
<Class><Id>1</Id></Class>
<Class><Id>2</Id></Class>
</Classes>
</Teacher>
<Teacher>
<Id>2</Id>
<Name>Biology Teacher</Name>
<Classes>
<Class><Id>3</Id></Class>
<Class><Id>4</Id></Class>
</Classes>
</Teacher>
</Teachers>
</School>
我没有包含任何对此进行反序列化或序列化的代码,因为实现会因使用的序列化程序而异。但目前我正在使用 XmlSerializer 和 [XmlIgnore] 而不是 [DataContract(IsReference = True)]。但是,我需要将这些引用放回原处。
注意:JSON不是一个选项,但是我们可以使用任何序列化Xml的开源库。
试试这个
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication34
{
class Program
{
static void Main(string[] args)
{
string input =
"<School>" +
"<Classes>" +
"<Class>" +
"<Id>1</Id>" +
"<Subject>Biology</Subject>" +
"<Teacher><Id>1</Id></Teacher>" +
"</Class>" +
"<Class>" +
"<Id>2</Id>" +
"<Subject>Advanced Biology</Subject>" +
"<Teacher><Id>1</Id></Teacher>" +
"</Class>" +
"<Class>" +
"<Id>3</Id>" +
"<Subject>Algebra</Subject>" +
"<Teacher><Id>2</Id></Teacher>" +
"</Class>" +
"<Class>" +
"<Id>4</Id>" +
"<Subject>Trigonometry</Subject>" +
"<Teacher><Id>2</Id></Teacher>" +
"</Class>" +
"</Classes>" +
"<Teachers>" +
"<Teacher>" +
"<Id>1</Id>" +
"<Name>Biology Teacher</Name>" +
"<Classes>" +
"<Class><Id>1</Id></Class>" +
"<Class><Id>2</Id></Class>" +
"</Classes>" +
"</Teacher>" +
"<Teacher>" +
"<Id>2</Id>" +
"<Name>Biology Teacher</Name>" +
"<Classes>" +
"<Class><Id>3</Id></Class>" +
"<Class><Id>4</Id></Class>" +
"</Classes>" +
"</Teacher>" +
"</Teachers>" +
"</School>";
XDocument doc = XDocument.Parse(input);
var results = doc.Elements().Select(x => new {
classes = x.Element("Classes").Elements("Class").Select(y => new {
id = y.Element("Id").Value,
subject = y.Element("Subject").Value,
teacherId = y.Element("Teacher").Element("Id").Value
}).ToList(),
teachers = x.Element("Teachers").Elements("Teacher").Select(y => new {
id = y.Element("Id").Value,
name = y.Element("Name").Value,
classIds = y.Element("Classes").Elements("Class").Select(z => z.Element("Id").Value).ToList()
}).ToList()
}).FirstOrDefault();
}
}
}
这里是连载。将 xml 标识添加到 xml 的第 1 行:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = @"c:\temp\test.xml";
static void Main(string[] args)
{
XmlSerializer xs = new XmlSerializer(typeof(School));
XmlTextReader reader = new XmlTextReader(FILENAME);
School school = (School)xs.Deserialize(reader);
}
}
[XmlRoot("Teachers")]
public class Teachers
{
[XmlElement("Teacher")]
public List<Teacher> teacher { get; set; }
}
[XmlRoot("Teacher")]
public class Teacher
{
//This is a made up annotation that represents what I'd like to happen.
[XmlElement("Id")]
public int Id { get; set; }
[XmlElement("Name")]
public string Name { get; set; }
//This is a made up annotation that represents what I'd like to happen.
[XmlElement("Classes")]
public List<Classes> Classes { get; set; }
}
[XmlRoot("Classes")]
public class Classes
{
[XmlElement("Class")]
public List<Class> c_class {get; set;}
}
[XmlRoot("Class")]
public class Class
{
//This is a made up annotation that represents what I'd like to happen.
[XmlElement("Id")]
public int Id { get; set; }
[XmlElement("Subject")]
public string Subject { get; set; }
//This is a made up annotation that represents what I'd like to happen.
[XmlElement("Teacher")]
public Teacher Teacher { get; set; }
}
[XmlRoot("School")]
public class School
{
public string Name { get; set; }
public Classes Classes { get; set; }
public Teachers Teachers { get; set; }
}
}
你的意思是像 Persist ?:
using System;
using elios.Persist;
using System.Collections.Generic;
using System.IO;
public class Program
{
public static void Main()
{
var students = new List<Student>();
students.Add(new Student {Name = "Alfred"});
students.Add(new Student {Name = "Ben"});
students.Add(new Student {Name = "Camila"});
students.Add(new Student {Name = "Denise"});
var alfred = students[0];
var ben = students[1];
var camila = students[2];
var denise = students[3];
alfred.AddFriend(ben);
alfred.AddFriend(camila);
ben.AddFriend(alfred);
ben.AddFriend(denise);
camila.AddFriend(alfred);
camila.AddFriend(ben);
camila.AddFriend(denise);
denise.AddFriend(camila);
var archive = new XmlArchive(typeof(List<Student>));
string xml;
using (var s = new MemoryStream())
{
archive.Write(s,students,"Students");
s.Position = 0;
using (var reader = new StreamReader(s))
{
xml = reader.ReadToEnd();
}
}
Console.WriteLine(xml);
}
}
public class Student
{
[Persist("Friends",IsReference = true, ChildName = "Friend")]
private readonly List<Student> m_friends;
public string Name { get; set; }
public Student()
{
m_friends = new List<Student>();
}
public void AddFriend(Student friend)
{
m_friends.Add(friend);
}
}
产生:
<Students>
<Student Name="Alfred" id="4">
<Friends>
<Friend id="1" />
<Friend id="2" />
</Friends>
</Student>
<Student Name="Ben" id="1">
<Friends>
<Friend id="4" />
<Friend id="5" />
</Friends>
</Student>
<Student Name="Camila" id="2">
<Friends>
<Friend id="4" />
<Friend id="1" />
<Friend id="5" />
</Friends>
</Student>
<Student Name="Denise" id="5">
<Friends>
<Friend id="2" />
</Friends>
</Student>
</Students>
简短版本:我需要能够 serialize/deserialize XML 并根据我设置的自定义 ID 维护引用。我需要一些通用的东西,可以通过构建引用来做到这一点 post 序列化,或者最好自定义一个 C# 序列化程序来处理这个。
我正在创建一个需要与各种应用程序通信的 WCF 应用程序。我们基本上是在构建一个非常复杂的计算器。我们确实有一个公共数据库,但是,用户对象的状态可能与数据库中的状态不同,我们不想存储这个中间状态。
我需要能够在 XML(参见 WCF)中传递复杂的相关对象,这些对象使用自定义的引用生成器。构建器需要根据每个对象具有的 Id 将对象放回一起。我希望能够指定 id,以便另一个应用程序(比如 C++)可以通过正确构建 XML 来调用我们的应用程序。
我知道 XmlSerializer 只会复制整个引用,而 DataContractSerializer 会保持引用完整性,但它当场创建一个标识符。 如何在序列化期间或序列化后解析这些引用?
如果答案是 "You can't" 那么我提出以下问题。
目前我们已经构建了一个自定义序列化程序,但速度很慢(Xml序列化程序快 8-30 倍,具体取决于文件大小)。我们的自定义序列化程序可以处理以下 XML,但它还有一个解析引用的第二步(在反序列化之后)。但是我将不得不做一些重大的返工和定制以使 XmlSerializer 能够处理它,因为我们需要 XmlSerializer 的性能。 (即使用 ID 列表并在我们所有的引用 classes 上使用 XmlIgnore 解析这些引用)。 是否有任何工具或库已经为 XML 解决步骤或序列化步骤执行此操作?
编辑:当我反序列化它时,我需要维护这些引用。如果我在 class 中更改 teacher.Name,它应该更改学校中的 teacher.Name。
我已经将我正在谈论的内容和我想用来解析它的 XML 放在一起的简化版本。
public class Teacher
{
//This is a made up annotation that represents what I'd like to happen.
[DataMember(IsReferenceId = True)]
public int Id { get; set; }
public string Name { get; set; }
//This is a made up annotation that represents what I'd like to happen.
[DataMember(IsReference = True)]
public List<Class> Classes { get; set; }
public Teacher()
{
Classes = new List<Class>();
}
public Teacher(int id)
{
Classes = new List<Class>();
Id = id;
}
}
public class Class
{
//This is a made up annotation that represents what I'd like to happen.
[DataMember(IsReferenceId = True)]
public int Id { get; set; }
public string Subject { get; set; }
//This is a made up annotation that represents what I'd like to happen.
[DataMember(IsReference = True)]
public Teacher Teacher { get; set; }
public Class()
{}
public Class(int id)
{
Id = id;
}
}
public class School
{
public string Name { get; set; }
public List<Class> Classes { get; set; }
public List<Teacher> Teachers { get; set; }
}
我希望能够像这样解析 XML。
<School>
<Classes>
<Class>
<Id>1</Id>
<Subject>Biology</Subject>
<Teacher><Id>1</Id></Teacher>
</Class>
<Class>
<Id>2</Id>
<Subject>Advanced Biology</Subject>
<Teacher><Id>1</Id></Teacher>
</Class>
<Class>
<Id>3</Id>
<Subject>Algebra</Subject>
<Teacher><Id>2</Id></Teacher>
</Class>
<Class>
<Id>4</Id>
<Subject>Trigonometry</Subject>
<Teacher><Id>2</Id></Teacher>
</Class>
</Classes>
<Teachers>
<Teacher>
<Id>1</Id>
<Name>Biology Teacher</Name>
<Classes>
<Class><Id>1</Id></Class>
<Class><Id>2</Id></Class>
</Classes>
</Teacher>
<Teacher>
<Id>2</Id>
<Name>Biology Teacher</Name>
<Classes>
<Class><Id>3</Id></Class>
<Class><Id>4</Id></Class>
</Classes>
</Teacher>
</Teachers>
</School>
我没有包含任何对此进行反序列化或序列化的代码,因为实现会因使用的序列化程序而异。但目前我正在使用 XmlSerializer 和 [XmlIgnore] 而不是 [DataContract(IsReference = True)]。但是,我需要将这些引用放回原处。
注意:JSON不是一个选项,但是我们可以使用任何序列化Xml的开源库。
试试这个
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication34
{
class Program
{
static void Main(string[] args)
{
string input =
"<School>" +
"<Classes>" +
"<Class>" +
"<Id>1</Id>" +
"<Subject>Biology</Subject>" +
"<Teacher><Id>1</Id></Teacher>" +
"</Class>" +
"<Class>" +
"<Id>2</Id>" +
"<Subject>Advanced Biology</Subject>" +
"<Teacher><Id>1</Id></Teacher>" +
"</Class>" +
"<Class>" +
"<Id>3</Id>" +
"<Subject>Algebra</Subject>" +
"<Teacher><Id>2</Id></Teacher>" +
"</Class>" +
"<Class>" +
"<Id>4</Id>" +
"<Subject>Trigonometry</Subject>" +
"<Teacher><Id>2</Id></Teacher>" +
"</Class>" +
"</Classes>" +
"<Teachers>" +
"<Teacher>" +
"<Id>1</Id>" +
"<Name>Biology Teacher</Name>" +
"<Classes>" +
"<Class><Id>1</Id></Class>" +
"<Class><Id>2</Id></Class>" +
"</Classes>" +
"</Teacher>" +
"<Teacher>" +
"<Id>2</Id>" +
"<Name>Biology Teacher</Name>" +
"<Classes>" +
"<Class><Id>3</Id></Class>" +
"<Class><Id>4</Id></Class>" +
"</Classes>" +
"</Teacher>" +
"</Teachers>" +
"</School>";
XDocument doc = XDocument.Parse(input);
var results = doc.Elements().Select(x => new {
classes = x.Element("Classes").Elements("Class").Select(y => new {
id = y.Element("Id").Value,
subject = y.Element("Subject").Value,
teacherId = y.Element("Teacher").Element("Id").Value
}).ToList(),
teachers = x.Element("Teachers").Elements("Teacher").Select(y => new {
id = y.Element("Id").Value,
name = y.Element("Name").Value,
classIds = y.Element("Classes").Elements("Class").Select(z => z.Element("Id").Value).ToList()
}).ToList()
}).FirstOrDefault();
}
}
}
这里是连载。将 xml 标识添加到 xml 的第 1 行:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = @"c:\temp\test.xml";
static void Main(string[] args)
{
XmlSerializer xs = new XmlSerializer(typeof(School));
XmlTextReader reader = new XmlTextReader(FILENAME);
School school = (School)xs.Deserialize(reader);
}
}
[XmlRoot("Teachers")]
public class Teachers
{
[XmlElement("Teacher")]
public List<Teacher> teacher { get; set; }
}
[XmlRoot("Teacher")]
public class Teacher
{
//This is a made up annotation that represents what I'd like to happen.
[XmlElement("Id")]
public int Id { get; set; }
[XmlElement("Name")]
public string Name { get; set; }
//This is a made up annotation that represents what I'd like to happen.
[XmlElement("Classes")]
public List<Classes> Classes { get; set; }
}
[XmlRoot("Classes")]
public class Classes
{
[XmlElement("Class")]
public List<Class> c_class {get; set;}
}
[XmlRoot("Class")]
public class Class
{
//This is a made up annotation that represents what I'd like to happen.
[XmlElement("Id")]
public int Id { get; set; }
[XmlElement("Subject")]
public string Subject { get; set; }
//This is a made up annotation that represents what I'd like to happen.
[XmlElement("Teacher")]
public Teacher Teacher { get; set; }
}
[XmlRoot("School")]
public class School
{
public string Name { get; set; }
public Classes Classes { get; set; }
public Teachers Teachers { get; set; }
}
}
你的意思是像 Persist ?:
using System;
using elios.Persist;
using System.Collections.Generic;
using System.IO;
public class Program
{
public static void Main()
{
var students = new List<Student>();
students.Add(new Student {Name = "Alfred"});
students.Add(new Student {Name = "Ben"});
students.Add(new Student {Name = "Camila"});
students.Add(new Student {Name = "Denise"});
var alfred = students[0];
var ben = students[1];
var camila = students[2];
var denise = students[3];
alfred.AddFriend(ben);
alfred.AddFriend(camila);
ben.AddFriend(alfred);
ben.AddFriend(denise);
camila.AddFriend(alfred);
camila.AddFriend(ben);
camila.AddFriend(denise);
denise.AddFriend(camila);
var archive = new XmlArchive(typeof(List<Student>));
string xml;
using (var s = new MemoryStream())
{
archive.Write(s,students,"Students");
s.Position = 0;
using (var reader = new StreamReader(s))
{
xml = reader.ReadToEnd();
}
}
Console.WriteLine(xml);
}
}
public class Student
{
[Persist("Friends",IsReference = true, ChildName = "Friend")]
private readonly List<Student> m_friends;
public string Name { get; set; }
public Student()
{
m_friends = new List<Student>();
}
public void AddFriend(Student friend)
{
m_friends.Add(friend);
}
}
产生:
<Students>
<Student Name="Alfred" id="4">
<Friends>
<Friend id="1" />
<Friend id="2" />
</Friends>
</Student>
<Student Name="Ben" id="1">
<Friends>
<Friend id="4" />
<Friend id="5" />
</Friends>
</Student>
<Student Name="Camila" id="2">
<Friends>
<Friend id="4" />
<Friend id="1" />
<Friend id="5" />
</Friends>
</Student>
<Student Name="Denise" id="5">
<Friends>
<Friend id="2" />
</Friends>
</Student>
</Students>