使用自定义Id管理引用对象的Xml序列化
本文关键字:对象 Xml 序列化 引用 管理 自定义 Id | 更新日期: 2023-09-27 18:01:34
简短版:我需要能够序列化/反序列化XML,并根据我设置的自定义id维护引用。我需要一些通用的东西,可以通过在序列化后构建引用来实现这一点,或者最好自定义一个c#序列化器来处理这个问题。
我正在创建一个需要与各种应用程序通信的WCF应用程序。我们基本上是在构建一个非常复杂的计算器。我们确实有一个公共数据库,但是,用户对象的状态可能与数据库中的状态不同,我们不想存储这种中间状态。
我需要能够在XML(参见WCF)中传递利用自定义引用构建器的复杂相关对象。构建器需要根据每个对象的Id将对象重新组合在一起。我希望能够指定id,以便另一个应用程序(比如c++)可以通过正确构建XML调用我们的应用程序。
我知道XmlSerializer将只复制整个引用,而DataContractSerializer将保持引用的完整性,但是它在现场创建了一个标识符。如何在序列化期间或序列化之后解析这些引用?
如果答案是"你不能",那么我提出以下问题。
目前我们已经构建了一个自定义序列化器,但它很慢(XmlSerializer根据文件大小快8-30倍)。我们的自定义序列化器可以处理下面的XML,但是它还有第二步,即解析引用(在反序列化之后)。但是,为了让XmlSerializer处理这个问题,我将不得不进行一些主要的返工和定制,因为我们需要XmlSerializer的性能。(即使用id列表并在所有引用类上使用XmlIgnore解析这些引用)。是否有工具或库已经在解析步骤或序列化步骤中为XML做了这些?
编辑:当我反序列化时,我需要维护这些引用。如果我换了老师。班级内的名字应该换老师。在学校的名字。
我已经把我正在谈论的内容的简化版本和我想用来解析它的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的开源库。
你的意思是像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>
试试这个
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; }
}
}