ASP.NET 2.0 XmlDataSource's XPath doesn't support namespaces
I'm working (again) on the XML Chapter to our upcoming book. The book is all about ASP.NET 2.0, but XML is such an important part of ASP.NET that this chapter gets bigger and bigger. I've been updating it from the original Beta 1 version this last few months and noticed that the namespace qualification for the XmlDataSource is still broken/incomplete as it was last year in September. I talked to a bunch of people at TechEd including a number of very helpful devs and PMs who were very much interested in resolving this issue. However, unfortunately it looks like this'll be one of those features that won't make it into the final, which means one of us will have to write our own.
The basic problem is this (from the book draft):
One unfortunate caveat of the new XmlDataSource is its XPath attribute does not support documents that use namespace qualification. Examples in this chapter use the Books.xml file with a default namespace of http://examples.books.com. It is very common for XML files to use multiple namespaces, including a default namespace. As you learned when you created an XPathDocument and queried it with XPath, the namespace in which an element exists is very important.
The regrettable reality is, there is no way use a namespace qualified XPath expression or to make the XmlDataSource Control aware of a list of prefix/namespace pairs via the XmlNamespaceManager class. However, the XPath function used in the ItemTemplate of the templated DataList control can take a XmlNamespaceManager as its second parameter and query XML returned from the XmlDataSource - as long as the control does not include an XPath attribute with namespace qualification or you can just omit it all together. That said, in order for these examples to work, you must remove the namespaces from your source XML and use XPath queries that include no namespace qualification, as shown in Listing xx-xx.
I was hoping to avoid having any caveats like this in the book, but this one will stay until there's a solution to the problem. It'd be nice if someone (Oleg, kzu, Don, me, you?) could add a namespace-aware XmlDataSource control to the Mvp.Xml project and have it ready by 2.0 ship.
As it is currently, you do this:
<asp:datalist id="DataList1" DataSourceID="XmlDataSource1" runat="server">
<ItemTemplate>
<p><b><%# XPath("author/first-name") %>
<%# XPath("author/last-name")%></b>
wrote <%# XPath("title") %></p>
</ItemTemplate>
</asp:datalist>
<asp:xmldatasource id="XmlDataSource1" runat="server"
datafile="~/Books.xml"
xpath="//bookstore/book"/>
And the root problem is that the boldfaced xpath expression can't use namespace qualified XPath expressions like //b:bookstore/b:book, because there's no way to pass in an XmlNamespaceManager to the XmlDataSource. Note that this doesn't apply to the TemplatedControl.XPath expression. You CAN pass in an XmlNamespaceManager like this: <%# XPath("b:author/b:first-name", myNamespaceMgr) %>. The only bummer is that there's no completely declarative way to do this; you have to have the XmlNamespaceManager in the code behind.
In an ideal world, there'd be a number of ways to let the XmlDataSource know about namespaces and prefix. Here's some ideas in order of preference:
-
"Infer" the namespace/prefixes and create an XmlNamespaceManager instance and associate it with the control automatically. Perhaps this calls for an XmlNamespaceInferringReader that creates an XmlNamespaceManager as a side effect (this wouldn't be hard, methinks)?
-
Pass in the prefixes/namespaces declaratively like Christopf wants:
<asp:xmldatasource runat="server" id="xds1"
datafile="~/app_data/namespacebooks.xml"
xpath="/ba:store/dx:book">
<asp:namespace prefix="ba" name=”http://bracketangles.net/names” />
<asp:namespace prefix="dx" name=”http://donxml.com/names” />
<asp:namespace prefix="ha" name=”http://hanselman.com/names” />
</asp:xmldatasource> -
Have an event in the code behind like this, where you can help in an associated a NamespaceManager:
private void OnXmlDataSource1ExecutingXPath(object sender, XmlDataSourceXPathEventArgs e) {
NameTable table = new NameTable();
e.NamespaceManager = new XmlNamespaceManager(table);
e.NamespaceManager.AddNamespace("a", "b");
e.NamespaceManager.AddNamespace("myns1", "http://www.example.org/namespace1");
e.NamespaceManager.AddNamespace("myns2", "http://www.example.org/namespace2");
}
Any of these would be preferrable to the alternatives, which are, as I seem them:
- Not using documents with namespaces
- Not using the XPath attribute of the XmlDataSource
- Using an XSLT Transformation to strip out namespaces before using the result in the XmlDataSource. Yikes.
It's a bummer, because, in my opinion, if you're using Xml without namespaces then you're just pushing around less-than/greater-than delimited files. Considering that so much effort was put into making schemas for most (all?) the ASP.NET config files and such, it's a shame if a control shipped without support for Xml Namespaces.
We're making the book very approachable for the beginner and intermediate dev, but there will be call-outs with gotchas like this that will hopefully save the advanced developer a lot of time. Also, if you're an advanced 1.1 dev, there is a lot of direct "it used to work like this, be careful because now..." exploration. I hope it'll save you time. It should be in bookstores just before ASP.NET 2.0 itself is.
About Scott
Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.
About Newsletter
I dig what you're saying. We have to deal with stuff like this all the time in BizTalk, because in many places it can't deal with qualified xpath expressions.
Of course, this does not mean it is totally unusable, one can still do the old trick of:
//*[local-name()='Bookstore' and namespace-uri='...']/[local-name()='Book' and namespace-uri='...']
Not pretty, but it certainly works...
Comments are closed.
Ideal for my taste would be just
<asp:xmldatasource runat="server" id="xds1"
datafile="~/app_data/namespacebooks.xml"
xpath="/ba:store/dx:book" xmlns:ba="bla" xmlns:dx="blah">
</asp:xmldatasource>
That would be so natural and familiar for anybody ever working with XSLT. Is it what you mean by "infer" approach?
Oh and if it won't be fixed (is there a bug we can vote for?), we could definitely provide enhanced version in the Mvp.Xml lib.