2009-06-17 3 views
3

Exemple de schéma:En désérialisation XML .NET, comment puis-je autoriser l'utilisation polymorphique des types Array?

<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> 

En l'utilisant wsdl.exe de .NET génère un code similaire à ce qui suit:

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

public partial class Dog { ... } 

public partial class Cat { ... } 

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

Il semble que wsdl.exe essaie d'être « intelligent » et se rendre compte que mon ArrayOfDog est vraiment juste un type de wrapper qui peut être encodé comme un tableau C#. Cela fonctionne correctement lorsque ArrayOfDog est explicitement référencé dans un autre type de données. Cependant, lorsque ArrayOfDog est utilisé de façon polymorphe (par exemple, en remplacement de xsd: anyType), cela se brise. Il semble rompre parce que le runtime .NET ne sait rien sur le complexType nommé "ArrayOfDog" - il a fondamentalement jeté cette information en faveur de l'utilisation de tableaux C# natifs.

Document XML Exemple 1:

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

Document XML Exemple 2:

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

Document n ° 1 est désérialisée correctement par le moteur d'exécution. Je reçois un objet de type Foo avec des champs correctement désérialisés pour Bar et Baz.

Le document 2 est désérialisé incorrectement par le runtime. J'obtiens un objet de type Foo avec un champ correctement désérialisé pour Bar, mais pour le champ Baz je reçois System.XML.XMLNode []. Ma conjecture est parce que le temps d'exécution ne sait rien au sujet d'une liaison de type pour une entité appelée "ArrayOfDog". Vous pourriez penser que la directive XmlInclude "XmlIncludeAttribute (typeof (Dog []))" gérera cela, mais cela ne semble pas fonctionner.

Est-ce que quelqu'un est tombé dessus?

Existe-t-il une solution élégante ici? La solution de contournement que je pense utiliser est d'envelopper mon type "ArrayOf" dans un autre type et l'inclure dans la substitution pour le xsd: anyType.

+0

Quel est le contexte ici? Un service Web ASMX? Quel type revient-il? Tapez Foo? Essayez d'utiliser un nom comme "BunchOfDogs" au lieu de "ArrayOfDog", qui est un nom que .NET aurait généré à partir de Dog []. –

+0

L'erreur se produit dans le client C# d'un service Web. Le WSDL pour le service Web est défini manuellement avec des types de données définis comme je l'ai mentionné ci-dessus. Le point de terminaison du service est implémenté à l'aide de Java. – Eric

Répondre

1

Je ne pense pas que cela ait quelque chose à voir avec le polymorphisme. Je pense que c'est un bogue dans le XML Serializer, en supposant que tout type nommé "ArrayOfDog", contenant une séquence de "Dog" est censé représenter un Dog [].En tant que test de cette théorie, essayez de changer le WSDL pour utiliser le nom "BunchOfDogs" à la place, et voir si cela change le code du proxy dans le client.

Si vous voulez un polymorphisme en XML, alors ArrayOfDog et Cat devront être des extensions du même type de base (autre que xsd: any). Si c'était le cas, alors je m'attendrais à .NET à générer Baz comme étant du type de base.

Schémas avec xsd: tout problème peut causer des problèmes en général. Il pourrait y avoir presque n'importe quoi, et certaines combinaisons n'auront tout simplement pas de sens.

Vous n'avez pas dit si ce service Java venait d'Axis, ou de quelle version il s'agit. J'ai vu Axis se comporter comme si xsi: type étaient un substitut à un schéma valide. Soyez prudent avec les schémas qui nécessitent une utilisation "correcte" de xsi: type.

+1

Je suis d'accord. Je pense que c'est un bug dans le générateur .NET C#. Si le générateur de liaisons .NET veut être mignon et utilise des tableaux natifs pour les types complexes "ArrayOf" qui ont une séquence avec un seul élément avec minOccurs = 0 maxOccurs = unbounded, c'est bien. Cependant, il ne devrait pas désavouer toutes les connaissances du type complexe "ArrayOf". S'il voit ce type "ArrayOf" dans un attribut "xsi: type =", il est toujours responsable de l'unmarshalling correctement. Veuillez noter cependant que je n'utilise pas "xsd: any", plutôt "xsd: anyType". Il existe une différence (xsd: anyType correspond à "Object" dans C# et java) – Eric

+0

Re: "polymorphism". Probablement pas le meilleur terme, mais je vois l'utilisation de xsd: anyType comme un moyen d'obtenir un comportement générique/polymorphique de XML. En termes de substituabilité, ce n'est pas différent d'utiliser des extensions de type. L'avantage par rapport à xsd: extension est que mes types ne doivent pas tous s'étendre d'un ancêtre commun. Je suis d'accord cependant, si j'avais modélisé "ArrayOfDog" en utilisant xsd: extension, .NET n'aurait probablement pas essayé d'appliquer son astuce d'oubli sur le type et en utilisant simplement des tableaux natifs partout. – Eric

+0

@Eric: Il pense qu'une autre instance du XML Serializer a généré "ArrayOfDog" à partir de Dog [], donc c'est juste l'inverse. S'il vous plaît essayez l'expérience de changer le nom du type - pas comme une solution de contournement, mais comme un diagnostic. –

1

Avec quoi voulez-vous commencer? Si vous commencez avec un type défini comme ceci:

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; } 
    } 
} 

Ensuite, vous pouvez sérialiser et documents désérialiser comme votre document n ° 2.

On dirait que vous voulez commencer avec le WSDL et le XSD. Dans ce cas, pouvez-vous généraliser votre schéma à ressembler à quelque chose comme ceci:

<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> 

Le schéma ci-dessus génère ce code:

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; 
     } 
    } 
} 

Si vous souhaitez inclure d'autres types, puis ajouter des éléments à la xsd: choix selon le cas.

Puisque vous voulez autoriser xsd: any à l'intérieur, Foo.Baz est un tableau d'objets. Le modèle de programmation sur elle, vous oblige à tester ou à lancer chaque élément avec quelque chose comme (foo.Baz [i] comme Type1).

Questions connexes