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
包含 BaseMemberType
,DerivedType
限制 BaseType
使用 DerivedMemberType
作为成员。注意 属性 上的 XmlIgnore
使用 new
关键字隐藏基 class 中的 XmlIgnore
,并使用单独的虚拟 属性对于序列化,为了解决 new
-ed 属性不能与 .NET XML 序列化代码一起工作的问题。
在使用代理进行 [反] 序列化时,您应该牢记的一个警告是,如果您有一个集合而不是单个对象(List<MyType>
与 MyType
),则允许maxOccurs > 1
,那么您将需要比单个对象情况下更深入的代理。这是因为 XML 序列化程序将添加到代理 属性 的 getter 返回的集合中,而不是直接在代理 属性 上设置。一个解决方案是安排您的代理 属性 与其代理的 属性 交换责任。这可以通过为代理数据和 "real" 面向应用程序的数据设置私有成员来完成,但它是由代理和非代理属性强制执行的,在给定时间只设置一个或另一个,并且它们之间的转换在需要时发生。
我正在尝试采用 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
包含 BaseMemberType
,DerivedType
限制 BaseType
使用 DerivedMemberType
作为成员。注意 属性 上的 XmlIgnore
使用 new
关键字隐藏基 class 中的 XmlIgnore
,并使用单独的虚拟 属性对于序列化,为了解决 new
-ed 属性不能与 .NET XML 序列化代码一起工作的问题。
在使用代理进行 [反] 序列化时,您应该牢记的一个警告是,如果您有一个集合而不是单个对象(List<MyType>
与 MyType
),则允许maxOccurs > 1
,那么您将需要比单个对象情况下更深入的代理。这是因为 XML 序列化程序将添加到代理 属性 的 getter 返回的集合中,而不是直接在代理 属性 上设置。一个解决方案是安排您的代理 属性 与其代理的 属性 交换责任。这可以通过为代理数据和 "real" 面向应用程序的数据设置私有成员来完成,但它是由代理和非代理属性强制执行的,在给定时间只设置一个或另一个,并且它们之间的转换在需要时发生。