您在XML中看到的
xsi:type
属性是标准的W3C XML Schema属性,允许元素明确指定其类型;详情请参见
此处。正如
Xsi:type Attribute Binding Support中所解释的那样,
XmlSerializer
支持通过
XmlIncludeAttribute
的使用来反序列化多态类型的机制。
首先,按照以下方式定义一个抽象基类
FieldValue
:
public static class XmlNamespaces
{
public const string Crsoftwareinc = "http://www.crsoftwareinc.com/xml/ns/titanium/common/v1_0";
}
[XmlRoot("field-value", Namespace = XmlNamespaces.Crsoftwareinc)]
[XmlType("field-value", Namespace = XmlNamespaces.Crsoftwareinc)]
[XmlInclude(typeof(TextFieldValue)),
XmlInclude(typeof(DateFieldValue))]
public abstract partial class FieldValue
{
public abstract object GetValue();
}
接下来,对于每一个可能的 xsi:type="XXX"
的值,定义一个由 FieldValue
派生而来的类型,其 XmlTypeAttribute.TypeName
与 xsi:type
的值相匹配。对于每个已经显示的派生类型,在基类上使用 [XmlInclude(typeof(TDerivedFieldValue))]
属性进行装饰:
[XmlRoot("field-text-valueType", Namespace = XmlNamespaces.Crsoftwareinc)]
[XmlType("field-text-valueType", Namespace = XmlNamespaces.Crsoftwareinc)]
public class TextFieldValue : FieldValue
{
[XmlElement("value")]
public string Value { get; set; }
public override object GetValue() { return Value; }
}
[XmlRoot("field-date-valueType", Namespace = XmlNamespaces.Crsoftwareinc)]
[XmlType("field-date-valueType", Namespace = XmlNamespaces.Crsoftwareinc)]
public class DateFieldValue : FieldValue
{
[XmlElement("value", DataType = "date")]
public DateTime Value { get; set; }
public override object GetValue() { return Value; }
}
接下来,按照以下方式定义与<field>
和其他更高级元素对应的包含类型:
[XmlRoot("field", Namespace = XmlNamespaces.Crsoftwareinc)]
[XmlType("field", Namespace = XmlNamespaces.Crsoftwareinc)]
public class Field
{
[XmlAttribute("name")]
public string Name { get; set; }
[XmlElement("field-value")]
public FieldValue FieldValue { get; set; }
}
[XmlRoot("user-defined-data-row", Namespace = XmlNamespaces.Crsoftwareinc)]
[XmlType("user-defined-data-row", Namespace = XmlNamespaces.Crsoftwareinc)]
public class UserDefinedDataRow
{
[XmlElement("field")]
public List<Field> Fields { get; set; }
}
[XmlRoot("root", Namespace = XmlNamespaces.Crsoftwareinc)]
[XmlType("root", Namespace = XmlNamespaces.Crsoftwareinc)]
public class RootObject
{
[XmlElement("user-defined-data-row")]
public List<UserDefinedDataRow> Rows { get; set; }
}
Notes:
注意:
If the base class FieldValue
has a namespace specified in via XmlTypeAttribute.Namespace
, then the derived classes must also, or else an error will get thrown by XmlSerializer
.
Once an [XmlType]
namespace is defined, it automatically applies to all serialized properties, so it isn't necessary to specify the same namespace via [XmlElement(Namespace = "http://www.crsoftwareinc.com/xml/ns/titanium/common/v1_0")]
attributes.
I got tired of repeatedly typing the namespace "http://www.crsoftwareinc.com/xml/ns/titanium/common/v1_0"
and so I extracted it into a constant.
Other derived types of FieldType
can be added easily, e.g.:
[XmlRoot("field-decimal-valueType", Namespace = XmlNamespaces.Crsoftwareinc)]
[XmlType("field-decimal-valueType", Namespace = XmlNamespaces.Crsoftwareinc)]
public class DecimalFieldValue : FieldValue
{
[XmlElement("value")]
public decimal Value { get; set; }
public override object GetValue() { return Value; }
}
[XmlInclude(typeof(DecimalFieldValue))]
public abstract partial class FieldValue { }
Don't forget to add [XmlInclude(typeof(DecimalFieldValue))]
when doing so.
If you have been given an XSD for the XML you are trying to deserialize that specifies the possible types of <field-value>
via e.g. an <xsd:extension>
element as shown in Generating XML Documents from XML Schemas: Abstract Types, then xsd.exe
will generate classes that include an appropriate type hierarchy. But if you only have the XML, then xsd.exe
and Paste XML as Classes will not generate a correct type hierarchy using whatever xsi:type
attributes are present.
For more about this limitation see xsi:type attribute messing up C# XML deserialization.
Your XML is not well-formed because it omits a declaration for the xsi:
namespace. Also, a default namespace xmlns="http://www.crsoftwareinc.com/xml/ns/titanium/common/v1_0"
is not defined so none of the elements are actually in this namespace. Thus I assume your XML is a fragment of some larger document that is valid, e.g.
<root
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.crsoftwareinc.com/xml/ns/titanium/common/v1_0">
<user-defined-data-row>
</user-defined-data-row>
</root>
这里有一个示例工作的.Net fiddle,点击这里查看。