Levi Wallach
Levi Wallach

Reputation: 433

Very confused about how to parse xml with namespace prefixes

So, I need to be able to parse xml files that could include namespace prefixes. I've tried doing this with a sample file and it gives me back null when trying to get a nodelist, even when I specify a node that has no attribute prefixes.

I've been trying to research this and it keeps coming back to the fact that without the namespace prefix defined, it won't work, so I've added code that I thought would do this, but it's still giving the same results. Here's some code I've added:

protected void Page_Load(object sender, EventArgs e)
{
    xml.Load(Server.MapPath("~/SomeLesson/imsmanifest.xml"));
    populateBaseNodes();
}

private void populateBaseNodes()
{
    treeViewMenu.Nodes.Clear(); // Clear any existing items
    TreeNode treenode = new TreeNode();
    treenode.Text = "organizations";
    XmlNodeList baseNodeList;

    string xmlns = xml.DocumentElement.Attributes["xmlns"].Value;
    XmlNamespaceManager nsmgr = new XmlNamespaceManager(xml.NameTable);
    nsmgr.AddNamespace("adlcp", "http://www.adlnet.org/xsd/adlcp_v1p3");
    nsmgr.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
    nsmgr.AddNamespace("imscp", "http://www.w3.org/2001/XMLSchema-instance");
    nsmgr.AddNamespace("imsss", "http://www.w3.org/2001/XMLSchema-instance");
    nsmgr.AddNamespace("schemaLocation", "http://www.w3.org/2001/XMLSchema-instance");

    baseNodeList = xml.SelectNodes("/manifest/organizations/organization/item", nsmgr);

    TextBox1.Text = baseNodeList.Count.ToString();

    foreach (XmlNode xmlnode in baseNodeList)
    {
        TreeNode treeNodeCatalog = new TreeNode();
        treeNodeCatalog.Text = xmlnode.Attributes["identifier"].Value;
        treeNodeCatalog.SelectAction = TreeNodeSelectAction.Expand;
        treeViewMenu.Nodes.Add(treeNodeCatalog);
    }

    treeViewMenu.CollapseAll();
}

(marc_s) Here's the XML in question that needs to be parsed:

<manifest identifier="Navigating_in_QuickBooks_-_Introduction_MANIFEST" version="1.3"
  xmlns="http://www.imsglobal.org/xsd/imscp_v1p1"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:imscp="http://www.imsglobal.org/xsd/imscp_v1p1"
    xmlns:adlcp="http://www.adlnet.org/xsd/adlcp_v1p3" 
    xmlns:imsss="http://www.imsglobal.org/xsd/imsss"
    xsi:schemaLocation=" http://www.imsglobal.org/xsd/imscp_v1p1 imscp_v1p1.xsd
                         http://www.imsglobal.org/xsd/imsss imsss_v1p0.xsd
                         http://www.adlnet.org/xsd/adlcp_v1p3 adlcp_v1p3.xsd 
                         http://www.adlnet.org/xsd/adlseq_v1p3 adlseq_v1p3.xsd
                         http://www.adlnet.org/xsd/adlnav_v1p3 adlnav_v1p3.xsd">

  <metadata>  
     <!-- not relevant here ... -->
  </metadata>
  <organizations default="TOC1">
     <organization identifier="TOC1">
        <title>Navigating in QuickBooks - Introductory Lesson</title>
        <item identifier="I_SCO0" identifierref="SCO0"> 
            <title>Navigating in QuickBooks - Introductory Lesson</title>
        </item>
     </organization>
   </organizations>
   <resources>
     <!-- not relevant here ... -->
   </resources>
</manifest>

Upvotes: 3

Views: 3195

Answers (2)

pmartin
pmartin

Reputation: 2741

In the XML you posted the default namespace controlling all of the elements in your sample file is:

xmlns="http://www.imsglobal.org/xsd/imscp_v1p1" 

This namespace does not define a prefix so you must add this namespace to your namespace manager using a blank prefix. I think you should be able to use this code to define the default namespace (using String.Empty to specify a blank prefix):

nsmgr.AddNamespace(String.Empty, "http://www.imsglobal.org/xsd/imscp_v1p1");

Upvotes: 0

marc_s
marc_s

Reputation: 755531

You're not showing us what your XML looks like - but two comments:

  1. you don't need to add the xsi prefix, and I'm not sure what the schemaLocation prefix is supposed to do ....

  2. when you've defined the schema prefixes, you also need to use those prefixes in your XPath, of course!

Again, not knowing what your XML structure looks like, I cannot really tell what you need - but something along the lines of:

 baseNodeList = xml.SelectNodes("/adlcp:manifest/adlcp:organizations/adlcp:organization/imscp:item", nsmgr);

or whatever other XML namespace prefixes your source XML requires.

Update: seeing your XML makes it clearer: see the root node - it has a default XML namespace (the one with xmlns="...." and no explicit prefix):

<manifest identifier="Navigating_in_QuickBooks_-_Introduction_MANIFEST" version="1.3"
     xmlns="http://www.imsglobal.org/xsd/imscp_v1p1"  <=== DEFAULT namespace!!!
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xmlns:imscp="http://www.imsglobal.org/xsd/imscp_v1p1"
     xmlns:adlcp="http://www.adlnet.org/xsd/adlcp_v1p3" 
     xmlns:imsss="http://www.imsglobal.org/xsd/imsss"
    ................>

That means: ALL your subsequent nodes that don't have a specific XML prefix will be in that default namespace.

Unfortunately, .NET XML parsing has problem with defining a default namespace without prefix - so my best solution is to create a namespace with a prefix for the default namespace, and then use it:

XmlNamespaceManager nsmgr = new XmlNamespaceManager(xml.NameTable);

// add default namespace, with a prefix for .NET 
nsmgr.AddNamespace("ns", "http://www.imsglobal.org/xsd/imscp_v1p1");

baseNodeList = 
   xml.SelectNodes("/ns:manifest/ns:organizations/ns:organization/ns:item", nsmgr);

Do you get any results now??

Upvotes: 4

Related Questions