繁体   English   中英

对派生类型的派生类型属性进行序列化

[英]Serialize array of derived type property of derived type

我有一个简单的.Net framework C#控制台应用程序,该应用程序序列化了一个派生类型的类,其中一个属性也是一个派生类型。

派生类的名称与基类相同,但在不同的名称空间中以防止它们冲突。 似乎XmlSerializer使用的反射不能很好地解决这个问题。 也许有一些方法可以解决这些属性,但我仍然可以使用漂亮的名称(因为使用时它将是DLL接口)来结束基类,而XML也使用漂亮的名称(因为它将是可人工编辑的)。不需要派生类的漂亮名称(尽管这是一个奖励)。

XML希望如下所示:

<?xml version="1.0" encoding="utf-8"?>
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Details>
    <Detail>
      <Description>bald</Description>
    </Detail>
    <Detail>
      <Description>red tie</Description>
    </Detail>
  </Details>
</Person>

但是我可以毫无例外地得到的最接近的是<Detail>元素所在的位置

    <Detail xsi:type="DerivedDetail"> ... </Detail>

必须添加此xs:type属性对于人类可编辑的XML并不是最好的选择。

这是通过以下C#代码实现的。 如果我删除了标记的XmlType属性,则该元素应在没有xsi:type属性的情况下进行序列化,但会出现异常:

InvalidOperationException:类型'Test.Detail'和'Test.Xml.Detail'都使用来自名称空间''的XML类型名称'Detail'。 使用XML属性为类型指定唯一的XML名称和/或名称空间。

我尝试将派生的Xml.Detail类标记为匿名XML类型,但是异常显示为:

InvalidOperationException:无法包含匿名类型'Test.Xml.Detail'。

我读过许多类似的问题,但还没有遇到任何可以解决这个问题的东西。

在下面的代码中, Person是一个抽象类,它具有一个属性,该属性是一个抽象类型Detail的数组。 这些类型分别由Xml.PersonXml.Detail派生。 该程序创建一个测试Xml.Person对象并尝试对其进行序列化:

using System;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            // Create test details array
            var TestDetails = new Xml.Detail[] 
            {
                new Xml.Detail
                {
                    Description = "bald"
                },
                new Xml.Detail
                {
                    Description = "red tie"
                }
            };

            // create test person object that holds details array
            var TestBar = new Xml.Person()
            {
                Details = TestDetails
            };

            // serialize the person object
            var s = new Xml.Serializer();
            var TestOutput = s.Serialize(TestBar);

            Console.WriteLine(TestOutput);
        }
    }

    // base classes
    public abstract class Person
    {
        public abstract Detail[] Details { get; set; }
    }

    public abstract class Detail
    {
        public abstract string Description { get; set; }
    }

    namespace Xml
    {
        // derived classes
        [Serializable]
        [XmlType(AnonymousType = true)]
        [XmlRoot(IsNullable = false)]
        public class Person : Test.Person
        {
            [XmlArrayItem("Detail", typeof(Detail))]
            [XmlArray(IsNullable = false)]
            public override Test.Detail[] Details { get; set; }
        }

        // This attribute makes serialization work but also adds the xsi:type attribute
        [XmlType("DerivedDetail")]
        [Serializable]
        public class Detail : Test.Detail
        {
            public override string Description { get; set; }
        }

        // class that does serializing work
        public class Serializer
        {
            private static XmlSerializer PersonSerializer = 
                new XmlSerializer(typeof(Person), new Type[] { typeof(Detail) });

            public string Serialize(Test.Person person)
            {
                string Output = null;
                var Stream = new MemoryStream();
                var Encoding = new UTF8Encoding(false, true);

                using (var Writer = new XmlTextWriter(Stream, Encoding))
                {
                    Writer.Formatting = Formatting.Indented;
                    PersonSerializer.Serialize(Writer, person);
                    Output = Encoding.GetString(Stream.ToArray());
                }
                Stream.Dispose();
                return Output;
            }
        }
    }
}

不知道没有成员字段时为什么要使用基类而不是接口。 无论如何,我都假定您希望Xml.Person是抽象Person或从抽象Person派生的任何类的具体实例,而不用XML属性装饰抽象Person 我通过强制抽象Person在序列化之前成为Xml.Person的具体实例来实现这一点。 请用Test替换XmlSerializationProject

using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace XmlSerializationProject
{
    class Program
    {
        static void Main(string[] args)
        {
            // Create test details array
            var TestDetails = new Xml.Detail[]
            {
                new Xml.Detail
                {
                    Description = "bald"
                },
                new Xml.Detail
                {
                    Description = "red tie"
                }
            };

            // create test person object that holds details array
            var TestBar = new Xml.Person()
            {
                Details = TestDetails
            };

            // serialize the person object
            var s = new Xml.Serializer();
            var TestOutput = s.Serialize(TestBar);

            Console.WriteLine(TestOutput);
            Console.ReadKey();
        }
    }

    // base classes
    public abstract class Person
    {
        public abstract Detail[] Details { get; set; }
    }

    public abstract class Detail
    {
        public abstract string Description { get; set; }
    }

    namespace Xml
    {
        [Serializable]
        [XmlType(AnonymousType = true)]
        [XmlRoot(IsNullable = false)]
        public class Person : XmlSerializationProject.Person
        {
            public Person()
            { }

            public Person(XmlSerializationProject.Person person)
            {
                // Deep copy
                if (person.Details == null) return;
                this.Details = new Detail[person.Details.Length];
                for (int i = 0; i < person.Details.Length; i++)
                {
                    this.Details[i] = new Detail { Description = person.Details[i].Description };
                }
            }

            [XmlArray(ElementName = "Details")]
            [XmlArrayItem("Detail", typeof(Detail))]
            [XmlArrayItem("ODetail", typeof(XmlSerializationProject.Detail))]
            public override XmlSerializationProject.Detail[] Details
            {
                get;
                set;
            }
        }

        [Serializable]
        public class Detail : XmlSerializationProject.Detail
        {
            public override string Description { get; set; }
        }

        // class that does serializing work
        public class Serializer
        {
            private static readonly XmlSerializer PersonSerializer;

            private static Serializer()
            {
                var xmlAttributeOverrides = new XmlAttributeOverrides();
                // Change original "Detail" class's element name to "AbstractDetail"
                var xmlAttributesOriginalDetail = new XmlAttributes();
                xmlAttributesOriginalDetail.XmlType = new XmlTypeAttribute() { TypeName = "AbstractDetail" };
                xmlAttributeOverrides.Add(typeof(XmlSerializationProject.Detail), xmlAttributesOriginalDetail);

                // Ignore Person.Details array
                var xmlAttributesOriginalDetailsArray = new XmlAttributes();
                xmlAttributesOriginalDetailsArray.XmlIgnore = true;
                xmlAttributeOverrides.Add(typeof(XmlSerializationProject.Person), "Details", xmlAttributesOriginalDetailsArray);

                PersonSerializer = new XmlSerializer(
                    typeof(Person), xmlAttributeOverrides, new Type[] { typeof(Detail) }, new XmlRootAttribute(), "default");
            }

            public string Serialize(XmlSerializationProject.Person person)
            {
                return Serialize(new Person(person));
            }

            public string Serialize(Person person)
            {
                string Output = null;
                var Stream = new MemoryStream();
                var Encoding = new UTF8Encoding(false, true);

                using (var Writer = new XmlTextWriter(Stream, Encoding))
                {
                    Writer.Formatting = Formatting.Indented;
                    PersonSerializer.Serialize(Writer, person);
                    Output = Encoding.GetString(Stream.ToArray());
                }
                Stream.Dispose();
                return Output;
            }
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM