我试图反序列化XML,其中一些相同的名称标签具有不同的xsi类型:
<user-defined-data-row>
<field name="entity">
<field-value xsi:type="field-text-valueType">
<value>Test</value>
</field-value>
</field>
<field name="expiry_date">
<field-value xsi:type="field-date-valueType">
<value>2001-10-07</value>
</field-value>
</field>
</user-defined-data-row>
通过将xml反序列化为此模型很容易实现:
[XmlRoot(ElementName = "field-value", Namespace = "http://www.crsoftwareinc.com/xml/ns/titanium/common/v1_0")]
[XmlType("field-text-valueType")]
public class Fieldvalue
{
[XmlElement(ElementName = "value", Namespace = "http://www.crsoftwareinc.com/xml/ns/titanium/common/v1_0")]
public string Value { get; set; }
}
唯一不同的是XML中的类型:
字段-文本VALUETYPE
字段-日期VALUETYPE
如何使C#类使用类似
的内容来解释这两种类型[XmlType("field-text-valueType")]
编辑:反序列化不序列化
答案 0 :(得分:3)
您在XML中看到的xsi:type
属性是标准的W3C XML Schema属性,允许元素明确指定其类型;有关详细信息,请参阅here。正如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
{
// It's not necessary to have this in the base class but I usually find it convenient.
public abstract object GetValue();
}
接下来,针对每个可能的xsi:type="XXX"
值,定义FieldValue
与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; }
}
// The XML for the root object is not shown so this is just a stub
[XmlRoot("root", Namespace = XmlNamespaces.Crsoftwareinc)]
[XmlType("root", Namespace = XmlNamespaces.Crsoftwareinc)]
public class RootObject
{
[XmlElement("user-defined-data-row")]
public List<UserDefinedDataRow> Rows { get; set; }
}
和其他更高元素对应的包含类型,如下所示:
FieldValue
注意:
如果基类XmlSerializer
在via XmlTypeAttribute.TypeName
中指定了命名空间,那么派生类也必须,否则[XmlType]
会抛出错误。
定义[XmlElement(Namespace = "http://www.crsoftwareinc.com/xml/ns/titanium/common/v1_0")]
命名空间后,它会自动应用于所有序列化属性,因此无需通过"http://www.crsoftwareinc.com/xml/ns/titanium/common/v1_0"
属性指定相同的命名空间。
我厌倦了反复键入命名空间FieldType
,因此我将其提取为常量。
可以轻松添加[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 { }
的其他派生类型,例如:
[XmlInclude(typeof(DecimalFieldValue))]
这样做时不要忘记添加<field-value>
。
如果您已为您尝试反序列化的XML提供了XSD,则会指定xsd.exe
的可能类型,例如:如XmlTypeAttribute.Namespace
中所示的<xsd:extension>
元素,然后Generating XML Documents from XML Schemas: Abstract Types将生成包含适当类型层次结构的类。但是,如果您只拥有XML,那么xsi:type
和xsd.exe
将不使用任何xsi:
属性生成正确的类型层次结构。
有关此限制的详情,请参阅 Paste XML as Classes 。
您的XML格式不正确,因为它省略了xmlns="http://www.crsoftwareinc.com/xml/ns/titanium/common/v1_0"
命名空间的声明。此外,未定义默认名称空间<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>
<!-- Remainder as shown in the question -->
</user-defined-data-row>
</root>
,因此没有任何元素实际位于此名称空间中。因此,我假设您的XML是一些有效的较大文档的片段,例如
FAILURE: Build failed with an exception.
* Where:
Build file '/home/travis/build/ir2pid/AndroidPOC2/app/build.gradle' line: 1
* What went wrong:
A problem occurred evaluating project ':app'.
> Failed to apply plugin [id 'com.android.application']
> Minimum supported Gradle version is 4.1. Current version is 4.0.1. If using the gradle wrapper, try editing the distributionUrl in /home/travis/build/ir2pid/AndroidPOC2/gradle/wrapper/gradle-wrapper.properties to gradle-4.1-all.zip
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
BUILD FAILED in 40s
travis_time:end:06d6b207:start=1527276803217824665,finish=1527276844181358996,duration=40963534331
[0K
[31;1mThe command "gradle wrapper --gradle-version 4.1" failed and exited with 1 during .[0m
Your build has been stopped.
示例工作.Net小提琴xsi:type attribute messing up C# XML deserialization。