XML 限制派生类型的对象绑定

XML object binding of types derived by restriction

我正在尝试采用 XML 架构并将关系转换为 C# classes。我运行的情况如下:

我在架构中有一个复杂的类型,它有 4 个元素,都是某种类型:

<xs:complexType name="ReportingBaseType" abstract="true">
    <xs:sequence>
        <xs:element name="Category" type="ent:ReportingCategoryConceptType" minOccurs="0" maxOccurs="0"/>
        <xs:element name="Frequency" type="ent:ReportingFrequencyType"/>
        <xs:element name="AdjustmentFrequency" type="ent:ReportingAdjustmentFrequencyType"/>
        <xs:element name="FirstReportDueDate" type="ent:CXMLDateType" minOccurs="0" maxOccurs="0"/>
    </xs:sequence>
</xs:complexType>

接下来我有多个复杂类型可以限制或者扩展这个类型:

<xs:complexType name="ReportingClassA">
   <xs:restriction base="ReportingBaseType">
    <xs:sequence>
        <xs:element name="Category" type="ent:ReportingClassAType" minOccurs="0" maxOccurs="0"/>
        <xs:element name="Frequency" type="ent:FequencyForThisClassType"/>
    </xs:sequence>
   </restriction>
</xs:complexType>

<xs:complexType name="ReportingClassB">
   <xs:restriction base="ReportingBaseType">
    <xs:sequence>
        <xs:element name="Category" type="ent:NewTypeForThisClass" minOccurs="0" maxOccurs="0"/>
        <xs:element name="Frequency" type="ent:AnotherNewType"/>
        <xs:element name="AdjustmentFrequency" type="ent:ThisIsADifferntTypeThenParent"/>
        <xs:element name="FirstReportDueDate" type="ent:AndAnotherNewType" minOccurs="0" maxOccurs="
    </xs:sequence>
  </restriction>
</xs:complexType>

我如何(在 C# 中)从父 class (ReportingBaseType) 派生但具有不同类型的属性,就像它们在架构中定义的那样?

我曾考虑过在派生的 classes 上为 "hide" 继承成员定义我的属性时使用 new 关键字,但我听说在序列化此对象时XML 序列化程序可能无法正确处理此问题。

有没有人有任何建议如何正确地将我的模式与 C# classes 相关联并且仍然能够序列化我的 C# 对象以更正将传递 XML 的 XML 对象架构验证?

编辑: 我尝试使用 Xsd2Code 和内置的 xsd.exe 工具来处理正确的 C# classes 但它们没有生成详细信息我需要的。特别是在我上面描述的情况下。

.NET XML 当您想做一些更复杂的事情时,序列化会变得混乱。

在这些情况下,您可以采用的一种方法是使用代理 class 进行 XML 序列化和反序列化,而不是直接在您想要处理的类型上申请。

在您的情况下,由于您正在限制类型,基础 class 可能已经是 [de] 序列化的适当代理 class,因为 XML 数据您可以适合受限类型也应该适合基本类型。下面的示例使用这种方法,但如果这对您不起作用,那么您可以定义一个单独的代理类型,该代理类型仅用于 XML [反] 将基本类型和受限类型序列化为它们对应的C# classes.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;

namespace XmlTest
{
    public class BaseMemberType
    {
        public string SomeValue
        {
            get;
            set;
        }
    }

    public abstract class BaseType
    {
        [XmlElement("Member", Type=typeof(BaseMemberType))]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public virtual BaseMemberType _MemberSerializer
        {
            get
            {
                return Member;
            }
            set
            {
                this.Member = (BaseMemberType)value;
            }
        }

        [XmlIgnore()]
        public BaseMemberType Member
        {
            get;
            set;
        }
    }

    public class DerivedMemberType
    {
        public int SomeValue
        {
            get;
            set;
        }
    }

    public class DerivedType : BaseType
    {
        [XmlIgnore()]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public override BaseMemberType _MemberSerializer
        {
            get
            {
                return new BaseMemberType()
                {
                    SomeValue = this.Member.SomeValue.ToString()
                };
            }
            set
            {
                this.Member = new DerivedMemberType()
                {
                    SomeValue = int.Parse(value.SomeValue)
                };
            }
        }

        [XmlIgnore()]
        public new DerivedMemberType Member
        {
            get;
            set;
        }
    }

    public class AnotherDerivedType : BaseType
    {
    }

    public class RootElement
    {
        public DerivedType First
        {
            get;
            set;
        }

        public AnotherDerivedType Second
        {
            get;
            set;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var serializer = new System.Xml.Serialization.XmlSerializer(typeof(RootElement));
            RootElement rootElement = null;
            string xml = @"
<RootElement>
<First>
<Member><SomeValue>1234</SomeValue></Member>
</First>
<Second>
<Member><SomeValue>Some string</SomeValue></Member>
</Second>
</RootElement>
";
            using(var reader = new System.IO.StringReader(xml))
            {
                rootElement = (RootElement)serializer.Deserialize(reader);
            }

            Console.WriteLine("First.Member.SomeValue: {0}", rootElement.First.Member.SomeValue);
            Console.WriteLine("Second.Member.SomeValue: {0}", rootElement.Second.Member.SomeValue);

            using (var writer = new System.IO.StringWriter())
            {
                serializer.Serialize(writer, rootElement);
                string serialized = writer.ToString();
                Console.WriteLine("Deserialized: ");
                Console.WriteLine(serialized);
            }
        }
    }
}

你会看到 BaseMemberType 表示一个基类型,它被 DerivedMemberType 限制,因此字符串成员被限制为 int。 BaseType 包含 BaseMemberTypeDerivedType 限制 BaseType 使用 DerivedMemberType 作为成员。注意 属性 上的 XmlIgnore 使用 new 关键字隐藏基 class 中的 XmlIgnore,并使用单独的虚拟 属性对于序列化,为了解决 new-ed 属性不能与 .NET XML 序列化代码一起工作的问题。

在使用代理进行 [反] 序列化时,您应该牢记的一个警告是,如果您有一个集合而不是单个对象(List<MyType>MyType),则允许maxOccurs > 1,那么您将需要比单个对象情况下更深入的代理。这是因为 XML 序列化程序将添加到代理 属性 的 getter 返回的集合中,而不是直接在代理 属性 上设置。一个解决方案是安排您的代理 属性 与其代理的 属性 交换责任。这可以通过为代理数据和 "real" 面向应用程序的数据设置私有成员来完成,但它是由代理和非代理属性强制执行的,在给定时间只设置一个或另一个,并且它们之间的转换在需要时发生。