2017-03-15 1 views
2

J'ai du mal à désérialiser ce segment de code XML correspondant à une section Détail FAULT du service WCF SOAP à cause de l'attribut xsi: type = "p: OUTPUT-HEADER":XmlSerializer et xsi: deserialization

<p:OUTPUT-HEADER xsi:type="p:OUTPUT-HEADER" xmlns:p="http://aaa.bbb.ccc/v2" xmlns:ns0="http://aaa.bbb.ccc/v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
    <FAULT> 
    <p:COD-ERROR>2951</p:COD-ERROR> 
    <p:COD-SEV>8</p:COD-SEV> 
    <p:MSG-ERROR>Error message</p:MSG-ERROR> 
    </FAULT> 
    <CNL-OUT>xxx</CNL-OUT> 
</p:OUTPUT-HEADER> 

Ce sont les classes que je utilise:

[XmlInclude(typeof(OutputHeader))] 
public abstract class FaultDetail 
{ 
    [XmlElement(ElementName = "FAULT", Namespace = "")] 
    public Fault FaultSection{ get; set; } 

    [XmlElement(ElementName = "CNL-OUT", Namespace = "")] 
    public string ClnOut{ get; set; } 
} 

[XmlRoot(ElementName = "OUTPUT-HEADER", Namespace = "http://aaa.bbb.ccc/v2")] 
public class OutputHeader : FaultDetail 
{ 
} 

public class Fault 
{ 
    [XmlElement(ElementName = "COD-ERROR")] 
    public int CodigoError { get; set; } 

    [XmlElement(ElementName = "COD-SEV")] 
    public int Severidad { get; set; } 

    [XmlElement(ElementName = "MSG-ERROR")] 
    public string Mensaje { get; set; } 

} 

XmlSerializer:

XmlSerializer x = new XmlSerializer(typeof(OutputHeader)); 

Et t il erreur que je reçois lorsque vous appelez la méthode deserialize:

« Le type spécifié n'a pas été reconnu: nom = 'SORTIE HEADER', espace de nom = 'http://aaa.bbb.ccc/v2', à < SORTIE-HEADER xmlns = 'http://aaa.bbb.ccc/v2' >. "

Est-il possible de décorer les classes désérialiser ce XML correctement? Toutes les pensées sont grandement appréciées, merci!

+0

WCF sérialise et désérialise automatiquement le contenu envoyé à travers le câble. Y a-t-il une raison spécifique pour laquelle vous voulez le faire manuellement? –

+0

Je comprends que la méthode 'auto' devrait utiliser FaultException.GetDetail , mais aucune classe OutputHeader n'a été générée car le wsdl ne contient aucune information liée à cela. Donc mon prochain mouvement est d'obtenir le flux FaultException.GetReaderAtDetailContents() et d'essayer de le désérialiser. Ou est-ce que je manque quelque chose ici? Merci! – mack

Répondre

0

Plutôt que XmlSerializer, il semble que vous devez utiliser DataContractSerializer pour désérialiser ce code XML. Ce sérialiseur est le default serializer for WCF donc vous devrez simplement supprimer le code où vous spécifiez l'utilisation de XmlSerializer.

Concevez vos types comme suit:

[DataContract(Namespace = "")] 
public abstract class OutputHeaderBase 
{ 
    [DataMember(Name = "FAULT", Order = 1)] 
    public Fault FaultSection { get; set; } 

    [DataMember(Name = "CNL-OUT", Order = 2)] 
    public string ClnOut { get; set; } 
} 

[DataContract(Name = "OUTPUT-HEADER", Namespace = "http://aaa.bbb.ccc/v2")] 
public class OutputHeader : OutputHeaderBase 
{ 
} 

[DataContract(Name = "FAULT", Namespace = "http://aaa.bbb.ccc/v2")] 
public class Fault 
{ 
    [DataMember(Name = "COD-ERROR", Order = 1)] 
    public int CodigoError { get; set; } 

    [DataMember(Name = "COD-SEV", Order = 2)] 
    public int Severidad { get; set; } 

    [DataMember(Name = "MSG-ERROR", Order = 3)] 
    public string Mensaje { get; set; } 
} 

Ensuite, votre contrat déclare d'opération de retour (ou accepter) un objet de type OutputHeader (pas OutputHeaderBase).

Enfin, revenez à la sérialisation du contrat de données en supprimant [XmlSerializerFormat] de votre contrat de service et/ou d'exploitation, et vous devriez tous être définis. Pour plus de détails de commutation voir Using the XmlSerializer Class

(Notez également les propriétés de Fault doivent être placés dans l'espace de noms correct.)

Pourquoi ce travail fait?

L'attribut "xsi:type" est un attribut standard W3C qui permet à un élément d'affirmer explicitement son type. Les deux XmlSerializer utilisation et DataContractSerializer cet attribut pour transmettre des informations de type réel lors de la sérialisation d'un type polymorphes. Cependant, l'élément suivant:

<p:OUTPUT-HEADER 
    xsi:type="p:OUTPUT-HEADER" 
    xmlns:p="http://aaa.bbb.ccc/v2" 
    xmlns:ns0="http://aaa.bbb.ccc/v2" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" > 
</p:OUTPUT-HEADER> 

a le type de base dans l'espace de noms OUTPUT-HEADERhttp://aaa.bbb.ccc/v2 et le sous-espace de nom OUTPUT-HEADER dans http://aaa.bbb.ccc/v2 - à savoir les informations de type et sous-type sont identiques et si l'attribut xsi:type est redondante.

Mais, si elle est redondant, il devrait être inoffensif, non? Vous pouvez concevoir une hiérarchie de type pour XmlSerializer comme suit:

[XmlRoot(ElementName = "OUTPUT-HEADER", Namespace = "http://aaa.bbb.ccc/v2")] 
[XmlInclude(typeof(OutputHeaderSubclass))] // Artificial subtype to trigger handling of the `xsi:type` attribute. 
[XmlInclude(typeof(OutputHeader))] 
public class OutputHeader 
{ 
    [XmlElement(ElementName = "FAULT", Namespace = "")] 
    public Fault FaultSection { get; set; } 

    [XmlElement(ElementName = "CNL-OUT", Namespace = "")] 
    public string ClnOut { get; set; } 
} 

[XmlRoot(ElementName = "OUTPUT-HEADER-SUBCLASS", Namespace = "http://aaa.bbb.ccc/v2")] 
public class OutputHeaderSubclass : OutputHeader 
{ 
} 

désérialisation Puis OutputHeader pourrait bien fonctionner. Malheureusement, ce n'est pas le cas.XmlSerializer déclenche une exception pour l'attribut redondant plutôt que de le gérer. Inversement DataContractSerializer n'a pas, c'est donc celui à utiliser.

+0

Impressionnant, très bien expliqué et documenté. J'utilisais XmlSerializer car je pensais que DataContractSerializar ne pouvait pas gérer les différents espaces de noms dans la hierachie, mais je vois maintenant que je ne comprenais pas le XML. Merci beaucoup dbc! – mack