c# - 使用多种类型反序列化 XML
问题描述
我正在尝试反序列化某些相同名称标签具有不同 xsi 类型的 XML:
<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 中的类型:
字段文本值类型
字段日期值类型
如何让 C# 类使用类似的东西解释这两种类型
[XmlType("field-text-valueType")]
编辑:反序列化不序列化
解决方案
xsi:type
您在 XML 中看到的属性是标准的 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
{
// 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
值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; }
}
// 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
在 via 中指定了命名空间XmlTypeAttribute.Namespace
,那么派生类也必须如此,否则会抛出错误XmlSerializer
。定义命名空间后,它会自动
[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))]
。如果您已经为您尝试反序列化的 XML 提供了一个 XSD,它指定了可能的类型,
<field-value>
例如从 XML 模式生成 XML 文档:抽象类型<xsd:extension>
中所示的元素,那么将生成包含适当类型层次结构的类。但是,如果您只有 XML,那么将 XML粘贴为类将不会使用存在的任何属性 生成正确的类型层次结构。xsd.exe
xsd.exe
xsi:type
有关此限制的更多信息,请参阅xsi:type attribute messing up C# XML deserialization。
您的 XML 格式不正确,因为它省略了
xsi:
命名空间的声明。xmlns="http://www.crsoftwareinc.com/xml/ns/titanium/common/v1_0"
此外,未定义默认命名空间,因此实际上没有任何元素位于此命名空间中。因此,我假设您的 XML 是一些有效的较大文档的片段,例如<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>
示例工作 .Net fiddle here。
推荐阅读
- javascript - 将对象转换为 MongoDB 中用户的数组
- javascript - JavaScript 实时时钟未运行
- git - 使用jenkins管道将代码推送到git开发服务器时如何触发Jenkins测试作业?
- java - android_app\app\src\main\java\ApiServices.java:49: 错误:注解类型不适用于这种声明 @Query("artist_id") int Artist_id
- if-statement - 如何在 IF Vlookup 和 ISNA 公式中放置 AND 语句
- python - Selenium Python 脚本的 Cron SyntaxError
- python - 使用值从字典列表中访问字典
- visual-studio - Visual Studio 中 CPU 使用情况分析器中的类别是什么?
- java - Intellij中如何导入另一个java项目包并使用
- themes - 你能用 Python-pptx 改变现有 powerpoint 的主题吗?