Extension method to serialize generic objects as a SOAP formatted stream

Viewed 7017

I'm having a hard time trying to figure out a generic extension method that would serialize a given object as SOAP formatted. The actual implementation looks somewhat like this:

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

EXCEPTION

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)

On the other hand both XML and JSON serialization (using XmlSerializer and DataContractJsonSerializer respectively) is working fine with the following expected results:

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

Any advice will be sincerely appreciated. Thanks much in advance.

UPDATE

As one of the commenters remarked, there's a SoapFormatter class but given that I was aware it can't deal with generic types haven't included that snippet. So anyway this would be the code for that scenario:

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

Which throws the following exception:

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

UPDATE 2

Following the lead given by Merlyn Morgan-Graham I've been tried to feed the SoapFormatter with a non-generic object, so after a little juggling with MemoryStream I've ended up with this:

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

Which surprisingly enough, character entities aside, outputs a decent SOAP message:

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