将通用对象序列化为SOAP格式的流的扩展方法
我很难想出一个将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() {} }
Lipsum.cs
[Serializable, XmlRoot("lipsum"), XmlType("lipsum"), DataContract] public class Lipsum { private List lipsum = new List(); [XmlElement("foobar"), DataMember] public List Lipsum { get { return lipsum; } } }
}
Extensions.cs
public static void SerializeToSoap(this Stream target, T source) { XmlTypeMapping xmlTypeMapping = (new SoapReflectionImporter().ImportTypeMapping(typeof(T))); XmlSerializer xmlSerializer = new XmlSerializer(xmlTypeMapping); xmlSerializer.Serialize(target, source); }
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); Console.WriteLine(Encoding.Default.GetString(persistence.ToArray())); Console.WriteLine(Environment.NewLine); } }
例外
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)
另一方面, XML和JSON序列化(分别使用XmlSerializer
和DataContractJsonSerializer
)可以正常工作,具有以下预期结果:
{"Lipsum":[{"Foo":"Lorem","Bar":"Ipsum"}]}
任何建议都将得到真诚的感谢。 非常感谢提前。
UPDATE
正如其中一位评论者所说,有一个SoapFormatter
类,但考虑到我知道它不能处理generics类型没有包含该片段。 所以无论如何这将是该场景的代码:
public static void SerializeToSoap(this Stream target, T source) { SoapFormatter soapFormatter = new SoapFormatter(); soapFormatter.Serialize(target, source); }
这会引发以下exception:
Exception caught: Soap Serializer does not support serializing Generic Types : System.Collections.Generic.List`1[Foobar].
更新2
在Merlyn Morgan-Graham给出的带领下,我已经尝试用非generics对象提供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); } }
令人惊讶的是,除了字符实体之外,还输出了一个不错的SOAP消息:
<?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>
我(一半)通过使用另一个元素包装序列化来解决问题,ala这个论坛post: http://forums.asp.net/p/1510998/3607468.aspx ,以及这篇博文: http://sandblogaspnet.blogspot .COM / 2009/07 /系列化,在净3.html
public static void SerializeToSoap(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(); } }
但是,该文档看起来很奇怪,并且不包含SOAP信封。 不可否认,我对SOAP知之甚少,所以也许你知道如何解决这些问题:)
编辑:
查看System.Web.Services.Protocols.SoapHttpClientProtocol
中的reflection源,看起来使用了SoapReflectionImporter
和XmlSerializer
,但SOAP信封和正文直接在序列化代码中生成。 没有特殊帮助者暴露给用户。 因此,您可能必须使用自定义代码包装该部分消息。
从好的方面来说,代码看起来相当简单 – 只需使用正确的命名空间/属性编写正确的元素。