简体   繁体   English

XmlSerializer和xsi:反序列化

[英]XmlSerializer and xsi: deserialization

I'm having a hard time trying to deserialize this chunk of XML code corresponding to a WCF SOAP service FAULT detail section because of the xsi:type="p:OUTPUT-HEADER" attribute: 由于xsi:type =“ p:OUTPUT-HEADER”属性,我很难尝试反序列化对应于WCF SOAP服务FAULT详细信息部分的XML代码块:

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

These are the classes I'm using: 这些是我正在使用的类:

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

}

The XmlSerializer: XmlSerializer:

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

And the error I'm getting when calling the deserialize method: 而且我在调用反序列化方法时遇到的错误:

"The specified type was not recognized: name ='OUTPUT-HEADER', namespace=' http://aaa.bbb.ccc/v2 ', at <OUTPUT-HEADER xmlns=' http://aaa.bbb.ccc/v2 '>." “无法识别指定的类型:名称='OUTPUT-HEADER',名称空间=' http://aaa.bbb.ccc/v2 ',位于<OUTPUT-HEADER xmlns =' http://aaa.bbb.ccc/ v2 '>。”

Is it possible to decorate the classes to deserialize this XML correctly? 是否可以装饰类以正确反序列化此XML? Any thoughts are greatly appreciated, thanks! 任何想法都将不胜感激,谢谢!

Rather than XmlSerializer , it seems you must use DataContractSerializer to deserialize this XML. 似乎您必须使用DataContractSerializer而不是XmlSerializer来反序列化此XML。 This serializer is the default serializer for WCF so you would simply need to remove the code where you specify use of XmlSerializer . 该序列化程序是WCF默认序列化程序,因此您只需要删除指定使用XmlSerializer的代码。

Design your types as follows: 设计您的类型,如下所示:

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

Then declare your operation contract as returning (or accepting) an object of type OutputHeader ( not OutputHeaderBase ). 然后声明您的操作合同为返回(或接受) OutputHeader类型的对象( 而不是OutputHeaderBase )。

Finally switch back to data contract serialization by removing [XmlSerializerFormat] from your service and/or operation contract, and you should be all set. 最后,通过从服务和/或操作合同中删除[XmlSerializerFormat] ,切换回数据合同序列化,您应该已经准备就绪。 For details of switching see Using the XmlSerializer Class 有关切换的详细信息,请参见使用XmlSerializer类。

(Note also the properties of Fault need to be placed into the correct namespace.) (还要注意, Fault的属性需要放置在正确的名称空间中。)

Why does this work ? 为什么这样做

The "xsi:type" attribute is a w3c standard attribute that allows an element to explicitly assert its type. "xsi:type"属性是w3c标准属性,它允许元素显式声明其类型。 Both XmlSerializer and DataContractSerializer use this attribute to convey actual type information when serializing a polymorphic type. XmlSerializerDataContractSerializer都在序列化多态类型时使用此属性来传达实际的类型信息。 However, the following element: 但是,以下元素:

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

Has base type OUTPUT-HEADER in namespace http://aaa.bbb.ccc/v2 and subtype OUTPUT-HEADER in namespace http://aaa.bbb.ccc/v2 -- ie the type and subtype information are identical and so the xsi:type attribute is redundant. 在名称空间http://aaa.bbb.ccc/v2具有基本类型OUTPUT-HEADER在名称空间http://aaa.bbb.ccc/v2子类型OUTPUT-HEADER http://aaa.bbb.ccc/v2即类型和子类型信息相同,因此xsi:type属性是多余的。

But, if it's redundant, it should be harmless, right? 但是,如果有多余的话,它应该是无害的,对吗? You could design a type hierarchy for XmlSerializer as follows: 您可以为XmlSerializer设计类型层次结构,如下所示:

[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
{
}

Then deserializing to OutputHeader might well work. 然后反序列化为OutputHeader可能很好。 Unfortunately, it doesn't. 不幸的是,事实并非如此。 XmlSerializer throws an exception for the redundant attribute rather than handling it. XmlSerializer为冗余属性引发异常,而不是对其进行处理。 Conversely DataContractSerializer does not, so that's the one to use. 相反, DataContractSerializer则没有,所以这是要使用的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM