views:

49

answers:

2

What is the proper way to post an XmlDocument to a web-server? Here is the skeleton function:

public static void PostXml(XmlDocument doc, String url)
{ 
   //TODO: write this
}

Right now i use:

//Warning: Do not use this PostXml implmentation
//It doesn't adjust the Xml to match the encoding used by WebClient
public static void PostXml(XmlDocument doc, String url)
{
   using (WebClient wc = new WebClient())
   {
      wc.UploadString(url, DocumentToStr(doc));
   }
}

Where DocumentToStr is a perfectly valid and correct method:

/// <summary>
/// Convert an XmlDocument to a String
/// </summary>
/// <param name="doc">The XmlDocument to be converted to a string</param>
/// <returns>The String version of the XmlDocument</returns>
private static String DocumentToStr(XmlDocument doc)
{
    using (StringWriter writer = new StringWriter())
    {
       doc.Save(writer);
       return writer.ToString();
    }
}

The problem with my implementation of PostXml is that it posts the String exactly as is. This means that (in my case) the http request is:

POST http://stackoverflow.com/upload.php HTTP/1.1
Host: stackoverflow.com
Content-Length: 557
Expect: 100-continue

<?xml version="1.0" encoding="utf-16"?>
<AccuSpeedData MACAddress="00252f21279e" Date="2010-10-07 10:49:41:768">
  <Secret SharedKey="1234567890abcdefghijklmnopqr" />
  <RegisterSet TimeStamp="2010-10-07 10:49:41:768">
    <Register Address="total:power" Type="Analog" Value="485" />
    <Register Address="total:voltage" Type="Analog" Value="121.4" />
    <Register Address="total:kVA" Type="Analog" Value="570" />
  </RegisterSet>
</AccuSpeedData>

You'll notice that the xml declaration has an incorrect encoding:

<?xml version="1.0" encoding="utf-16"?>

The WebClient is not sending the request in utf-16 unicode, that was how Strings in .NET are stored. i don't even know the encoding used by the WebClient.


The http post of the xml needs to be properly encoded, which normally happens during a call to:

Save(textWriter)

During a call to Save the XmlDocument object will adjust the xml-declaration based on Encoding of the TextWriter it is being asked to save to. Unfortunately WebClient doesn't expose a TextWriter that i can save the XmlDocument to.

See also

A: 

Is this a web server under your control? Can you provide more info regarding what you are actually trying to do and the environment situation>

Mike Cheel
The web-server isn't under my control. The protocol is to send the xml in the post body of http request. i am trying to post xml to a url on a web-server.
Ian Boyd
Why don't you follow the instructions found here: http://netomatix.com/Development/XmlWebRequest.aspx
Mike Cheel
Three problems with that code: it puts the xml into an encoding that doesn't necessarily match the encoding of the `HttpWebRequest`, it omits the xml-declaration, and it wastes memory by storing data in an intermediate byte array. First two are deal breakers, the 3rd is just bad design.
Ian Boyd
You don't have to get the innerxml. You can just say ToString() and get the declaration. I don't know if I can agree that converting characters to bytes is such an wasteful operation. Lastly, as far as the code, I have seen it used in plenty of places very similar to what was in the article and it works great.
Mike Cheel
Calling `xmlDocument.ToString()` returns `"System.Xml.XmlDocument"`
Ian Boyd
I meant xmlDocument.OuterXML for retaining the xml declaration =P While the declaration is supposed to be there to be well formed it isn't always validated and so you might not even need it.
Mike Cheel
i still have the problem where i am returned a UTF-16 string, with *who knows* what kind of xml-declaration. Both need to be converted to the encoding that will be used by the `POST`.
Ian Boyd
Can you tell me what you mean by 'i am returned a UTF-16'? If you are just concerned with making sure that you are transmitting the xml in UF-8 then when you convert to the byte array using UTF8.GetBytes() then you are.
Mike Cheel
A: 

There's this solution. Subclass StringWriter:

public class StringWriterWithEncoding : StringWriter
{
  Encoding encoding;

  public StringWriterWithEncoding (StringBuilder builder, Encoding encoding) :base(builder)
  {
    this.encoding = encoding;
  }

  public override Encoding Encoding
  {
    get { return encoding; }
  }
}
Bryce Fischer
That guy is intentionally mangling the string, then wondering why the string is invalid. Following the link the he includes, one of the responders is correct when he says, "Why do you need UTF-8 encoded string? In-memory strings are unicode (UTF-16); I don't know any other way to make them UTF-8 without writing them to file or a memory stream." A `String` *is* UTF-16, trying to stuff UTF-8 bytes into it is completely invalid.
Ian Boyd