Converting rootless XmlReader to XmlDocument in .NET

Share on facebook
Share on twitter
Share on linkedin
Share on reddit

MS SQL has the ability to retrieve data as XML by using the “FOR XML [RAW | AUTO [ELEMENTS] | EXPLICIT]” parameters, which is very useful especially when used in a web service that communicates with a Flash / Flex client as the results don’t have to be converted, but just output to the client.

Your code might look something like this:

  1. public static XmlDocument ExecuteSelectXmlCmd(string sQuery, bool nodes)
  2. {
  3. 	if (sQuery.ToLower().IndexOf("for xml") < 0)
  4. 	{
  5. 		sQuery += " FOR XML AUTO";
  6. 		if (nodes == true)
  7. 		sQuery += ", ELEMENTS";
  8. 	}
  9.  
  10. 	string sConn = ConfigurationManager.AppSettings["ConnectionString"] /* get the connection string from web.config */
  11. 	SqlConnection oConn = new SqlConnection(sConn);
  12. 	try
  13. 	{
  14. 		oConn.Open();
  15. 	}
  16. 	catch { throw new Exception(sConn); }
  17. 	if (oConn.State != ConnectionState.Open)
  18. 	throw new Exception("Connection unavailable");
  19.  
  20. 	SqlCommand cmd = new SqlCommand();
  21.  
  22. 	// this can be a security issue!
  23. 	cmd.CommandText = sQuery;
  24.  
  25. 	cmd.Connection = oConn;
  26.  
  27. 	cmd.CommandType = CommandType.Text;
  28. 	XmlReader xmlRead = cmd.ExecuteXmlReader();
  29. 	oConn.Close();
  30. }

Besides the possible security issues (it’d be much better to use parametrized queries), there’s a problem with this code: it returns an XmlReader without a root node, so you can’t return that to your client.

There’s no straightforward way to convert a XmlReader that does not have a root element to a XmlDocument – most of the code samples I’ve seen simply read sequentially through the XmlReader, appending data to a string. Obviously this is inneficient from a performance standpoint.

The better way to handle this is via a rather obscure method, mentioned on MSDN:

It involves using an XPathNavigator, like this:

XPathDocument xp = new XPathDocument(xmlRead);
XPathNavigator xn = xp.CreateNavigator();

so you end up with an XmlNode.

Then you can create the XmlDocument like usual, with a root node

XmlDocument xd = new XmlDocument();
XmlNode root = xd.CreateElement("root");

and dump the node inside:

root.InnerXml = xn.OuterXml;
xd.AppendChild(root);

So the whole method might look like this:

  1. public static XmlDocument ExecuteSelectXmlCmd(string sQuery, string sRootName, bool nodes)
  2. {
  3. 	if (sQuery.ToLower().IndexOf("for xml") < 0)
  4. 	{
  5. 		sQuery += " FOR XML AUTO";
  6. 		if (nodes == true)
  7. 		sQuery += ", ELEMENTS";
  8. 	}
  9. 	string sConn = ConfigurationManager.AppSettings["ConnectionString"] /* get the connection string from web.config */
  10. 	SqlConnection oConn = new SqlConnection(sConn);
  11. 	try
  12. 	{
  13. 		oConn.Open();
  14. 	}
  15. 	catch { throw new Exception(sConn); }
  16. 	if (oConn.State != ConnectionState.Open)
  17. 	throw new Exception("Connection unavailable");
  18.  
  19. 	SqlCommand cmd = new SqlCommand();
  20. 	// this can be a security issue!
  21. 	cmd.CommandText = sQuery;
  22. 	cmd.Connection = oConn;
  23. 	cmd.CommandType = CommandType.Text;
  24. 	XmlReader xmlRead = cmd.ExecuteXmlReader();
  25.  
  26. 	XPathDocument xp = new XPathDocument(xmlRead);
  27. 	XPathNavigator xn = xp.CreateNavigator();
  28. 	XmlDocument xd = new XmlDocument();
  29. 	XmlNode root = xd.CreateElement(sRootName);
  30. 	root.InnerXml = xn.OuterXml;
  31. 	xd.AppendChild(root);
  32. 	oConn.Close();
  33. 	return xd;
  34. }

SQLXML

If you don’t want to add the root tag by yourself or if you need to apply a XSL transformation on the returned XML, you can use SQLXML. In case you don’t have it installed, you can download it from Microsoft and install it; then you can add it as reference in your project.

To use it, add this in your class:

using Microsoft.Data.SqlXml;

then, modify the query part of the method like this:

string sConn = ConfigurationManager.AppSettings["ConnectionString"];
SqlXmlCommand cmd = new SqlXmlCommand(sConn);
cmd.RootTag = sRootName;
cmd.CommandText = sQuery;
XmlReader xmlRead = cmd.ExecuteXmlReader;
XmlDocument xd = new XmlDocument();
xd.Load(xmlRead);
oConn.Close();
return xd;

The rest stays the same.

Futher reading

Armand Niculescu

Armand Niculescu

As the Senior Project manager, Armand is one of the rare kind of developers that can do both design and programming with equal skill. This, coupled with a solid background and many years of experience, enables him to see the big picture and plan for the small details.

7 Responses

  1. To quote you “There’s no straightforward way to convert a XmlReader to a XmlDocument – most of the code samples I’ve seen simply read sequentially through the XmlReader, appending data to a string. Obviously this is inneficient.”.

    Isn’t much easier to use the “Load” method on the XmlDocument class? One of the 4 overloads accept an XmlReader as aparameter.

  2. This looked to be exactly what I needed for converting data from an sql server db to a xmldoc. Unfortunately, I’m using .net 1.1 and don’t have the option to upgrade and this has only been added in .net 2.0. Is there any other, elegant way of doing this in 1.1 without having to sequentially read through the XmlReader

  3. Sam, if you’re using MSSQL 2005, you can specify the root node in the query itself, like this:
    ...FOR XML AUTO, ROOT('doc')

    I don’t know of any other way.

  4. Great Article. I wish I had find it sooner.

    Here is another way of doing the same with an XMLDocumentFragment.
    It works, but it could use some cleanup, specially around the ugly way I’m parsing each node.

    Just go to my blog look at my post.

    Thanks! Sebastian

Comments are closed.