使用多种类型

时间:2018-05-23 09:05:07

标签: c# xml xml-serialization xmlserializer xsi

我试图反序列化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")]

编辑:反序列化不序列化

1 个答案:

答案 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"值,定义FieldValuexsi: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:typexsd.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