【问题标题】:Manage Xml Serialization of referenced objects with a custom Id使用自定义 ID 管理引用对象的 Xml 序列化
【发布时间】:2015-07-08 16:58:46
【问题描述】:

短版:我需要能够序列化/反序列化 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>

我没有包含任何反序列化或序列化它的代码,因为实现会根据使用的序列化程序而有所不同。但目前我正在使用带有 [XmlIgnore] 而不是 [DataContract(IsReference = True)] 的 XmlSerializer。但是,我需要将这些引用放回原处。

注意:JSON 不是一个选项,但我们可以使用任何序列化 Xml 的开源库。

【问题讨论】:

    标签: c# xml wcf xmlserializer datacontractserializer


    【解决方案1】:

    你的意思是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>
    

    【讨论】:

    • 注意:这需要 YamlDotNet 和 .NET Framework >= 4.5
    【解决方案2】:

    试试这个

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

    【讨论】:

    • 嘿,感谢您的回答,我在问题中添加了一些说明,但我基本上需要一些维护这些引用的东西,以便它实际使用反序列化后提供的类。此外,我的课程非常庞大,因此可以更改,因此最好使用这样做的工具。
    【解决方案3】:

    这里是序列化。将 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; }
        }
    }
    ​
    

    【讨论】:

    • 对不起,但这不能回答我的问题。我声明我真的需要保持参照完整性的东西。我什至提到我已经尝试过 XmlSerializer。这不会解决我的参照完整性问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-09
    相关资源
    最近更新 更多