Friday, January 06, 2012

XMLWriter ignores XMLWriterSettings: generated XML is “UTF-16” instead of “UTF-8”

Once you need to generate a XML document in your ASP.NET application (for example, to offer it for download), you will sooner or later decide to use the XMLWriter class of .NET Fx.
So you’ll write a piece of code like: 
  
string fileName = buildDownloadFilename();
XDocument xmlDocument = generateXML();
if (xmlDocument != null)
   {
     StringBuilder sb = new StringBuilder();
     XmlWriter xmlWriter = XmlWriter.Create(sb);
     xmlDocument.Save(xmlWriter);
     xmlWriter.Close();
     string strContent = sb.ToString();
     Response.ContentType = "text/xml";
     Response.AppendHeader("content-disposition", String.Format("attachment;filename={0}", fileName));
     Response.AppendHeader("Content-Length", strContent.Length.ToString());
     Response.Write(strContent);
And you’ll be definitely surprised seeing the output XML declaration with encoding “UTF-16”.

Well, extending the code snippet with explicit XMLWriterSettings, you’ll land on:

string fileName = buildDownloadFilename();
XDocument xmlDocument = generateXML();
if (xmlDocument != null)
   {
     StringBuilder sb = new StringBuilder();
     XmlWriterSettings settings = new XmlWriterSettings();
     settings.Encoding = Encoding.UTF8;
     XmlWriter xmlWriter = XmlWriter.Create(sb, settings);
     xmlDocument.Save(xmlWriter);
     xmlWriter.Close();
     string strContent = sb.ToString();
     Response.ContentType = "text/xml";
     Response.AppendHeader("content-disposition", String.Format("attachment;filename={0}", fileName));
     Response.AppendHeader("Content-Length", strContent.Length.ToString());
     Response.Write(strContent);
And you’ll be still surprised seeing the output XML declaration unchanged with encoding “UTF-16”.

The reason is, the settings of XMLWriter target (in this sample – StringBuilder) override the XMLWriterSettings parameters already in XMLWriter constructor.

The way out is, use an alternate target for XMLWriter, supporting the encoding you need. For example:

string fileName = buildDownloadFilename();
XDocument xmlDocument = generateXML();
if (xmlDocument != null)
   {
    XmlWriterSettings settings = new XmlWriterSettings();
    settings.Encoding = Encoding.UTF8;
    MemoryStream ms = new MemoryStream();
    XmlWriter xmlWriter = XmlWriter.Create(ms, settings);
    xmlDocument.Save(xmlWriter);
    xmlWriter.Close();
    string strContent = Encoding.UTF8.GetString(ms.GetBuffer());
    Response.ContentType = "text/xml";
    Response.AppendHeader("content-disposition", String.Format("attachment;filename={0}", fileName));
    Response.AppendHeader("Content-Length", strContent.Length.ToString());
    Response.Write(strContent);
Also check the solution here: http://blogs.msdn.com/b/kaevans/archive/2008/08/11/xmlwritersettings-encoding-being-ignored.aspx

Enjoy!

No comments: