Tuesday, January 15, 2008

Is that default namespace in XML bothering you?

I had one of my dev team members walk up to me some days back with this really interesting requirement. He had these XML files that contained similar information except that the elements were in different namespaces on those files. If you find yourself caught up in a situation like this, probably this blog entry might be useful. Once again, I will show a few ways of getting this done, pick the right one based on your requirements.

For starters, I will create an XML file that contains a Person node at the root and three nodes - Name that lies in the n1 namespace, Age that lies in the n2 namespace and Gender that is part of the default namespace.

A listing of the Sample.xml file

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

<Person xmlns:n1="http://codeedifice.com/N1" xmlns:n2="http://codeedifice.com/N2" xmlns="http://codeedifice.com/N">

  <n1:Name>Alvin</n1:Name>

  <n2:Age>10</n2:Age>

  <Gender>Male</Gender>

</Person>



The example that I am listing here lists the top three approaches taken by developers who are still getting their hands dirty with XML that contains namespaces. The next three approaches are alternatives that you must work out for yourself and pick the one that suits your actual requirement

  Sub Main()

    Dim xdoc As XmlDocument = New XmlDocument

    xdoc.Load("Sample.xml")

 

    'Fails due to lack of namespace

    Console.WriteLine("Looking for Person Node(s) without a namespace manager...")

    Dim xNodLst1 As Xml.XmlNodeList = xdoc.SelectNodes("/Person")

    Console.WriteLine(Space(4) & "Found " & xNodLst1.Count & " matche(s)")

 

    Dim nsMgr As XmlNamespaceManager = New XmlNamespaceManager(xdoc.NameTable)

    nsMgr.AddNamespace("n1", "http://codeedifice.com/N1")

    nsMgr.AddNamespace("n2", "http://codeedifice.com/N2")

 

    'Fails since the namespaces do not include the default one

    Console.WriteLine("Looking for Person Node(s) with the namespace manager not including the default namespace...")

    Dim xNodLst2 As Xml.XmlNodeList = xdoc.SelectNodes("/Person", nsMgr)

    Console.WriteLine(Space(4) & "Found " & xNodLst1.Count & " matche(s)")

 

    nsMgr.AddNamespace("", "http://codeedifice.com/N")

    'Fails since the empty string namespace is not useful (quite contrary to popular vote)

    Console.WriteLine("Looking for Person Node(s) with the namespace manager including the default namespace as an empty string...")

    Dim xNodLst3 As Xml.XmlNodeList = xdoc.SelectNodes("/Person", nsMgr)

    Console.WriteLine(Space(4) & "Found " & xNodLst3.Count & " matche(s)")

 

    nsMgr.RemoveNamespace("", "http://codeedifice.com/N"'take the useless one out

    nsMgr.AddNamespace("ALVIN", "http://codeedifice.com/N")

    'Finally a working solution

    Console.WriteLine("Looking for Person Node(s) with the namespace manager including the default namespace as a unique value - ALVIN...")

    Dim xNodLst4 As Xml.XmlNodeList = xdoc.SelectNodes("/ALVIN:Person", nsMgr)  'Note how the dummy namespace is used

    Console.WriteLine(Space(4) & "Found " & xNodLst4.Count & " matche(s)")

 

    'A slightly more elegant solution that does not rely on the unique value ALVIN to be created by the developer

    Console.WriteLine("Looking for Person Node(s) without the namespace manager using the localName XPath function...")

    Dim xNodLst5 As Xml.XmlNodeList = xdoc.SelectNodes("/*[local-name()='Person']")

    Console.WriteLine(Space(4) & "Found " & xNodLst5.Count & " matche(s)")

 

    'Accessing the Name and Age nodes correctly will require the use of the namespace

    Console.WriteLine("Looking for Name Node(s) with the namespace manager using the localName XPath function...")

    Dim xNodLst6 As Xml.XmlNodeList = xdoc.SelectNodes("/*[local-name()='Person']/n1:Name", nsMgr)

    Console.WriteLine(Space(4) & "Found " & xNodLst6.Count & " matche(s)")

 

    'Accessing the Name and Age nodes in a namespace agnostic fashion

    'This implies that you are assuming that you do not care about the namespace and can be dangerous

    Console.WriteLine("Looking for Name Node(s) without the namespace manager using the localName XPath function...")

    Dim xNodLst7 As Xml.XmlNodeList = xdoc.SelectNodes("/*[local-name()='Person']/*[local-name()='Name']")

    Console.WriteLine(Space(4) & "Found " & xNodLst7.Count & " matche(s)")

 

    Console.ReadLine()

  End Sub



And a look at the results

No comments: