使用自定义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的开源库。

使用自定义Id管理引用对象的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; }
    }
}
​