Как можно разрешить полиморфное использование типов Array в десериализации .NET XML? - PullRequest
3 голосов
/ 17 июня 2009

Пример схемы:

<complexType name="Dog">...</complexType>
<complexType name="Cat">...</complexType>

<complexType name="ArrayOfDog">
    <sequence>
        <element name="Dog" type="tns:Dog minOccurs="0" maxOccurs="unbounded" />
    </sequence>
</complexType>

<complexType name="Foo">
    <sequence>
        <element name="Bar" type="string"/>          
        <element name="Baz" type="anyType"/>
    </sequence>
</complexType>

Выполнение этого через .NET wsdl.exe создает код, подобный следующему:

[System.Xml.Serialization.XmlIncludeAttribute(typeof(Dog[]))]

public partial class Dog { ... }

public partial class Cat { ... }

public partial class Foo {
    private string barField;
    private object bazField;
}

Похоже, что wsdl.exe пытается быть "умным" и понимает, что мой ArrayOfDog действительно является просто типом-оберткой, который может быть закодирован как массив C #. Это прекрасно работает, когда на ArrayOfDog явно ссылаются в другом типе данных. Однако, когда ArrayOfDog используется полиморфно (например, в качестве замены xsd: anyType), это нарушается. Кажется, он сломался, потому что среда выполнения .NET ничего не знает о complexType с именем «ArrayOfDog» - она ​​в основном отбросила эту информацию в пользу только использования собственных массивов C #.

Пример XML-документа 1:

<Foo>
    <Bar>Hello</Bar>
    <Baz xsi:type="Cat">
        ...
    </Baz>
</Foo>

Пример XML-документа 2:

<Foo>
    <Bar>Hello</Bar>
    <Baz xsi:type="ArrayOfDog">
        <Dog>...</Dog>
        <Dog>...</Dog>
    </Baz>
</Foo>

Документ № 1 правильно десериализован во время выполнения. Я получаю объект типа Foo с правильно десериализованными полями для Bar и Baz.

Документ № 2 десериализован неправильно во время выполнения. Я получаю объект типа Foo с правильно десериализованным полем для Bar, но для поля Baz я получаю System.XML.XMLNode []. Я думаю, потому что среда выполнения ничего не знает о каких-либо привязках типов для объекта с именем "ArrayOfDog". Вы можете подумать, что директива XmlInclude "XmlIncludeAttribute (typeof (Dog []))" справится с этим, но, похоже, она не работает.

Кто-нибудь сталкивался с этим?

Есть ли здесь элегантное решение? Обходной путь, который я собираюсь использовать, - это обернуть мой тип «ArrayOf» в другой тип и включить его в замену для xsd: anyType.

Ответы [ 2 ]

1 голос
/ 17 июня 2009

Я не думаю, что это имеет какое-либо отношение к полиморфизму. Я думаю, что это ошибка в XML Serializer, предполагая, что любой тип с именем «ArrayOfDog», содержащий последовательность «Dog», предназначен для представления Dog []. В качестве проверки этой теории попробуйте изменить WSDL, чтобы вместо него использовать имя «BunchOfDogs», и посмотрите, не изменит ли это код прокси в клиенте.

Если вы хотите полиморфизм в XML, то и ArrayOfDog, и Cat, должны быть расширениями одного базового типа (кроме xsd: any). Если бы это было так, я бы ожидал, что .NET сгенерирует Baz как базовый тип.

Схемы с xsd: проблемы с любой причиной в общем. Там может быть что угодно, и некоторые комбинации просто не имеют смысла.

Вы не сказали, является ли этот Java-сервис Axis или какой это версия. Я видел, как Axis ведет себя так, как будто xsi: type заменяет действительную схему. Будьте осторожны со схемами, которые требуют «правильного» использования xsi: type.

1 голос
/ 17 июня 2009

С чего вы хотите начать? Если вы начинаете с типа, определенного следующим образом:

public partial class Foo
{
    private string _bar;

    private object[] _baz;

    public string Bar
    {
        get { return _bar; }
        set { _bar= value; }
    }

    [XmlArray("Baz")]
    [XmlArrayItem("Type1", typeof(Type1))]
    public object[] Baz
    {
        get { return _baz; }
        set { _baz= value; }
    }
}

Затем вы можете сериализовать и десериализовать документы, такие как ваш документ № 2.

Похоже, вы хотите начать с WSDL и XSD. В этом случае, вы можете обобщить вашу схему, чтобы она выглядела примерно так:

<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="Foo" nillable="true" type="Foo" />
  <xs:complexType name="Foo">
    <xs:sequence>
      <xs:element minOccurs="0" maxOccurs="1" name="Bar" type="xs:string" />
      <xs:element minOccurs="0" maxOccurs="1" name="Baz" type="UntypedArray" />
    </xs:sequence>
  </xs:complexType>


  <xs:complexType name="UntypedArray">
    <xs:choice minOccurs="1" maxOccurs="unbounded">
      <xs:element name="Type1" type="Type1" minOccurs="1" maxOccurs="1"/>
      <xs:any namespace="##other" processContents="lax" minOccurs="1" maxOccurs="1"/>
    </xs:choice>
  </xs:complexType>


  <xs:complexType name="Type1" mixed="true">
    <xs:sequence>
      <xs:element minOccurs="0" maxOccurs="1" name="Child" type="xs:string" />
    </xs:sequence>
  </xs:complexType>
</xs:schema>

Приведенная схема генерирует этот код:

public partial class Foo {

    private string barField;

    private object[] bazField;

    /// <remarks/>
    public string Bar {
        get {
            return this.barField;
        }
        set {
            this.barField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlArrayItemAttribute("", typeof(System.Xml.XmlElement), IsNullable=false)]
    [System.Xml.Serialization.XmlArrayItemAttribute(typeof(Type1), IsNullable=false)]
    public object[] Baz {
        get {
            return this.bazField;
        }
        set {
            this.bazField = value;
        }
    }
}

Если вы хотите включить другие типы, добавьте элементы к выбору xsd: соответственно.

Поскольку вы хотите разрешить xsd: any внутри него, Foo.Baz является массивом объектов. Модель программирования на нем требует от вас тестирования или приведения каждого элемента к чему-то вроде (foo.Baz [i] как Type1).

...