Атрибуты xsi:type
, которые вы видите в своем XML, являются стандартными атрибутами XML-схемы W3C, которые позволяют элементу явно указывать его тип;подробности см. здесь .Как объяснено в Xsi: Поддержка связывания атрибутов типа , 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
имеет пространство имен, указанное в параметре 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))]
при этом.
Если вам дали XSD дляXML, который вы пытаетесь десериализовать, который определяет возможные типы <field-value>
, например, <xsd:extension>
Элемент , как показано в Генерация XML-документов из схем XML: абстрактные типы , тогда xsd.exe
создаст классы, которые включают соответствующую иерархию типов.Но если у вас есть только XML, то xsd.exe
и Вставить XML как классы будет не сгенерировать правильную иерархию типов с использованием любых атрибутов xsi:type
,
Подробнее об этом ограничении см. xsi: сбой атрибута типа C # XML десериализация .
Ваш 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 скрипки здесь .