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

时间:2015-01-21 16:52:37

标签: c# .net xml serialization xml-serialization

我正在尝试使用XML Schema并将关系转换为C#类。我遇到的情况如下:

我在模式中有一个复杂的类型,它有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#中)从父类(ReportingBaseType)派生,但是对于属性有不同的类型,如在Schema中定义的那样?

我想到在派生类上定义我的属性时使用new关键字来隐藏&#34;隐藏&#34;继承的成员,但我听说在序列化这个对象时,XML序列化程序可能无法正确处理这个。

有没有人有任何建议如何正确地将我的架构与C#类关联起来,并且仍然能够序列化我的c#对象以纠正将传递XML Schema Validation的XML对象?

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

1 个答案:

答案 0 :(得分:2)

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

您可以在这些场景中采用的一种方法是使用代理类进行XML序列化和反序列化,而不是直接在应用程序中处理的类型。

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

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的使用,以及为序列化使用单独的虚拟属性,以解决{ {1}} - ed属性不能使用.NET XML序列化代码。

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