将通用对象序列化为SOAP格式化流的扩展方法

Nan*_*ada 6 c# serialization soap datacontractserializer xmlserializer

我很难找到一个通用扩展方法,它将给定对象序列化为SOAP格式.实际的实现看起来像这样:

Foobar.cs

[Serializable, XmlRoot("foobar"), DataContract]
public class Foobar
{
    [XmlAttribute("foo"), DataMember]
    public string Foo { get; set; }

    [XmlAttribute("bar"), DataMember]
    public string Bar { get; set; }

    public Foobar() {}
}
Run Code Online (Sandbox Code Playgroud)

Lipsum.cs

[Serializable, XmlRoot("lipsum"), XmlType("lipsum"), DataContract]
public class Lipsum
{
    private List<Foobar> lipsum = new List<Foobar>();

    [XmlElement("foobar"), DataMember]
    public List<Foobar> Lipsum { get { return lipsum; } }
}
Run Code Online (Sandbox Code Playgroud)

}

Extensions.cs

public static void SerializeToSoap<T>(this Stream target, T source)
{
    XmlTypeMapping xmlTypeMapping = (new SoapReflectionImporter().ImportTypeMapping(typeof(T)));
    XmlSerializer xmlSerializer = new XmlSerializer(xmlTypeMapping);
    xmlSerializer.Serialize(target, source);
}
Run Code Online (Sandbox Code Playgroud)

Program.cs中

static void Main()
{
    Lipsum lipsum = new Lipsum();
    lipsum.Lipsum.Add(
        new Foobar()
        {
            Foo = "Lorem",
            Bar = "Ipsum"
        }
    );

    using (MemoryStream persistence = new MemoryStream())
    {
        persistence.SerializeToSoap<Lipsum>(lipsum);
        Console.WriteLine(Encoding.Default.GetString(persistence.ToArray()));
        Console.WriteLine(Environment.NewLine);
    }
}
Run Code Online (Sandbox Code Playgroud)

例外

System.InvalidOperationException: Token StartElement in state Epilog would result in an invalid XML document.
   at System.Xml.XmlTextWriter.AutoComplete(Token token)
   at System.Xml.XmlTextWriter.WriteStartElement(String prefix, String localName, String ns)
   at System.Xml.Serialization.XmlSerializationWriter.WriteStartElement(String name, String ns, Object o, Boolean writePrefixed, XmlSerializerNamespaces xmlns)
   at System.Xml.Serialization.XmlSerializationWriter.WriteArray(String name, String ns, Object o, Type type)
   at System.Xml.Serialization.XmlSerializationWriter.WriteReferencedElement(String name, String ns, Object o, Type ambientType)
   at System.Xml.Serialization.XmlSerializationWriter.WriteReferencedElements()
   at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriter1.
   Write4_Lipsum(Object o)
Run Code Online (Sandbox Code Playgroud)

另一方面,XMLJSON序列化(分别使用XmlSerializerDataContractJsonSerializer)可以正常工作,具有以下预期结果:

<?xml version="1.0"?>
<lipsum xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <foobar foo="Lorem" bar="Ipsum" />
</lipsum>

{"Lipsum":[{"Foo":"Lorem","Bar":"Ipsum"}]}
Run Code Online (Sandbox Code Playgroud)

任何建议都将得到真诚的感谢.非常感谢提前.

UPDATE

正如其中一位评论者所说,有一个SoapFormatter类,但鉴于我知道它不能处理泛型类型没有包含该片段.所以无论如何这将是该场景的代码:

public static void SerializeToSoap<T>(this Stream target, T source)
{
    SoapFormatter soapFormatter = new SoapFormatter();
    soapFormatter.Serialize(target, source);
}
Run Code Online (Sandbox Code Playgroud)

这会引发以下异常:

Exception caught: Soap Serializer does not support serializing Generic Types : System.Collections.Generic.List`1[Foobar].
Run Code Online (Sandbox Code Playgroud)

更新2

在Merlyn Morgan-Graham给出的领导之后,我已经尝试SoapFormatter用一个非通用的对象来提供,所以经过一点点玩杂耍之后,MemoryStream我最终得到了这个:

using (MemoryStream xmlStream = new MemoryStream())
{
    XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
    xmlSerializer.Serialize(xmlStream, lipsum);

    using (MemoryStream soapStream = new MemoryStream()) 
    {
        SoapFormatter soapFormatter = new SoapFormatter();
        soapFormatter.Serialize(soapStream, Encoding.Default.GetString(xmlStream.ToArray()));
        Console.WriteLine(Encoding.Default.GetString(soapStream.ToArray()));
        Console.WriteLine(Environment.NewLine);
    }
}
Run Code Online (Sandbox Code Playgroud)

令人惊讶的是,除了字符实体之外,还输出了一个不错的SOAP消息:

<SOAP-ENV:Envelope
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:clr="http://schemas.microsoft.com/soap/encoding/clr/1.0"
    SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    <SOAP-ENV:Body>
        <SOAP-ENC:string id="ref-1">
            &#60;?xml version=&#34;1.0&#34;?&#62;&#60;lipsum
            xmlns:xsi=&#34;http://www.w3.org/2001/XMLSchema-instance&#34;
            xmlns:xsd=&#34;http://www.w3.org/2001/XMLSchema&#34;&#62;&#60;
            foobar foo=&#34;Lorem&#34; bar=&#34;Ipsum&#34;/&#62;&#60;/lipsum&#62;
        </SOAP-ENC:string>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Run Code Online (Sandbox Code Playgroud)

Mer*_*ham 5

我(一半)通过与其他元素包装系列化,阿拉这个论坛的帖子解决了这个问题:http://forums.asp.net/p/1510998/3607468.aspx,而这个博客帖子:HTTP://sandblogaspnet.blogspot .COM/2009/07 /系列化,在净3.html

public static void SerializeToSoap<T>(this Stream target, T source)
{
    XmlTypeMapping xmlTypeMapping = (new SoapReflectionImporter().ImportTypeMapping(typeof(T)));
    XmlSerializer xmlSerializer = new XmlSerializer(xmlTypeMapping);
    using (var xmlWriter = new XmlTextWriter(target, Encoding.UTF8))
    {
        xmlWriter.WriteStartDocument();
        xmlWriter.WriteStartElement("root");
        xmlSerializer.Serialize(xmlWriter, source);
        xmlWriter.WriteFullEndElement();
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,该文档看起来很奇怪,并且不包含SOAP信封.不可否认,我对SOAP知之甚少,所以也许你知道如何解决这些问题:)

编辑:

查看反射源System.Web.Services.Protocols.SoapHttpClientProtocol,它看起来像SoapReflectionImporterXmlSerializer使用,但SOAP信封和主体直接在序列化代码中生成.没有特殊帮助者暴露给用户.所以你可能不得不用自定义代码包装消息的那一部分.

从好的方面来说,代码看起来相当简单 - 只需使用正确的名称空间/属性编写正确的元素即可.