Showing posts with label XDocument. Show all posts
Showing posts with label XDocument. Show all posts

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!

Wednesday, February 23, 2011

HOWTO: find descendant XElement in XDocument by its name recursively using LINQ with namespaces

If you want to use LINQ to locate XML nodes by their names and your XML contains namespace notations, be aware to specify namespaces looking for descendants.

XML Example:

<?xml version="1.0" encoding="utf-8"?>
<
MyDataStruct xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <
DataRoot xmlns="http://mydata.org/2011/1">
    <
Dispatch xmlns="http://mydata.org/2011/1">
      <
Target>
        <
Organisation>
          <
Comment></Comment>
          <
ID></ID>
        </
Organisation>
        <
System>
          <
ID>100</ID>
          <
Comment></Comment>
        </
System>
      </
Target>
      <
Source>
        <
Organisation>
          <
Comment></Comment>
          <
ID></ID>
        </
Organisation>
        <
System>
          <
ID>101</ID>
          <
Comment></Comment>
        </
System>
      </
Source>
    </
Dispatch>
  </
DataRoot>
</
MyDataStruct>

LINQ based recursive search operations:

XDocument xDoc = XDocument.Load(xmlFileName);

XElement xe1 = xDoc.Descendants("{http://mydata.org/2011/1}Target").First(); // locate first “Target” node
   XElement xe1 = xDoc.Descendants("{http://mydata.org/2011/1}Source").First()
                                  .Descendants("{http://mydata.org/2011/1}ID").First(); // locate “ID” subnode of“Source” node

UPDATE: seen also here -

http://stackoverflow.com/questions/566167/query-an-xdocument-for-elements-by-name-at-any-depth

Enjoy!