Search code examples
c#xmlxmppxmlwriter

XmlWriter leave element open


I am looking at the XMPP protocol and testing some basic interaction with a server. XMPP interacts by exchanging XML forward and backwards, but this XML is part of an ongoing stream, effectively building a document as it goes.

I have tried to reproduce this using an XmlWriter and although I can create the Xml almost exactly as required, I can't seem to prevent the writer from closing the Xml Element. For example to open the connection you send a <stream:stream ... > element (to be closed at some later point in the interaction). Using an XmlWriter will always close this element such as: <stream:stream ... /> which I don't want because sending the close /> will cause the server to close the stream.

Is there a way to get an XmlWriter to leave the element "open" but still retrieve the data contained within? Of course I can just do a string replace on the element to get rid of the '/' but I'd prefer to find a neater method of doing this if possible.

The initial message to send to the server looks like this: <?xml version="1.0" encoding="utf-8"?><stream:stream from="[email protected]" to="address.com" version="1.0" xml:lang="en" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" >

So far my code is:

private string CreateStreamHeader(string from, string to, double version)
{
    Encoding encoding = new UTF8Encoding(false);
    var sb = new StringBuilder();
    var settings = new XmlWriterSettings();
    settings.Encoding = encoding;
    using(var output = new StringWriterWithEncoding(sb, encoding))
    {
        using(XmlWriter writer = XmlWriter.Create(output, settings))
        {
            writer.WriteStartDocument();
            writer.WriteStartElement("stream", "stream", "http://etherx.jabber.org/streams");
            writer.WriteAttributeString("from", from);
            writer.WriteAttributeString("to", to);
            writer.WriteAttributeString("version", version.ToString("0.0"));
            writer.WriteAttributeString("xml", "lang", null, "en");
            writer.WriteAttributeString("xmlns", "jabber:client");
        }
    }
    return sb.ToString();
}

And this code produces: <?xml version="1.0" encoding="utf-8"?><stream:stream from="[email protected]" to="address.com" version="1.0" xml:lang="en" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" /> <-- note the /> here

Note: the StringWriterWithEncoding is the one explained in this post


Solution

  • You should probably consider one of the existing libraries linked to at xmpp.org. Treating namespace declarations as if they are attributes will come back to haunt you later.

    As to your question, manually serialize the stream:stream start tag, and only use XmlWriter for full stanzas.