Метод расширения для сериализации общих объектов в виде потока в формате SOAP - PullRequest
6 голосов
/ 19 июня 2011

Я с трудом пытаюсь найти общий метод расширения, который бы сериализовал данный объект в формате 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<Foobar> lipsum = new List<Foobar>();

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

}

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

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

ИСКЛЮЧЕНИЕ

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 соответственно) работает нормально со следующими ожидаемыми результатами:

<?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"}]}

Любой совет будет искренне оценен.Заранее большое спасибо.

ОБНОВЛЕНИЕ

Как заметил один из комментаторов, есть класс SoapFormatter, но, учитывая, что я знал, что он не может работать с универсальнымтипы не включали этот фрагмент.Так или иначе, это будет код для этого сценария:

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

, который выдает следующее исключение:

Exception caught: Soap Serializer does not support serializing Generic Types : System.Collections.Generic.List`1[Foobar].

ОБНОВЛЕНИЕ 2

Следующийлидерство, данное Мерлином Морганом-Грэмом, я пытался накормить 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:

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

1 Ответ

5 голосов
/ 19 июня 2011

Я (наполовину) решил проблему, обернув сериализацию другим элементом, как это сообщение на форуме: http://forums.asp.net/p/1510998/3607468.aspx, и это сообщение в блоге: http://sandblogaspnet.blogspot.com/2009/07/serialization-in-net-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();
    }
}

Документ выглядит действительностранно, однако, и не содержит конверт SOAP.По общему признанию, я очень мало знаю о SOAP, так что, возможно, вы знаете, как решить эти проблемы:)

Редактировать:

Глядя на отраженный источник в System.Web.Services.Protocols.SoapHttpClientProtocol, выглядитнапример, используются SoapReflectionImporter и XmlSerializer, но конверт и тело SOAP генерируются непосредственно в коде сериализации.Никакие специальные помощники не выставляются пользователю.Поэтому вам, вероятно, придется обернуть эту часть сообщения пользовательским кодом.

С другой стороны, код выглядит довольно просто - просто напишите правильные элементы с соответствующими пространствами имен / атрибутами.

...