How to use jQuery for XML parsing with namespaces

66,671

Solution 1

I got it.

Turns out that it requires \\ to escape the colon.

$.get(xmlPath, {}, function(xml) {
    $("rs\\:data", xml).find("z\\:row").each(function(i) {
        alert("found zrow");
    });
}, "xml");

As Rich pointed out:

The better solution does not require escaping and works on all "modern" browsers:

.find("[nodeName=z:row]")

Solution 2

I have spent several hours on this reading about plugins and all sorts of solutions with no luck.

ArnisAndy posted a link to a jQuery discussion, where this answer is offered and I can confirm that this works for me in Chrome(v18.0), FireFox(v11.0), IE(v9.08) and Safari (v5.1.5) using jQuery (v1.7.2).

I am trying to scrape a WordPress feed where content is named <content:encoded> and this is what worked for me:

content: $this.find("content\\:encoded, encoded").text()

Solution 3

If you are using jquery 1.5 you will have to add quotes around the node selector attribute value to make it work:

.find('[nodeName="z:row"]')

Solution 4

Although the above answer seems to be correct, it does not work in webkit browsers (Safari, Chrome). A better solution I believe would be:

.find("[nodeName=z:myRow, myRow]")    

Solution 5

In case someone needs to do this without jQuery, just with normal Javascript, and for Google Chrome (webkit), this is the only way I found to get it to work after a lot of research and testing.

parentNode.getElementsByTagNameNS("*", "name");

That will work for retrieving the following node: <prefix:name>. As you can see the prefix or namespace is omitted, and it will match elements with different namespaces provided the tag name is name. But hopefully this won't be a problem for you.

None of this worked for me (I am developping a Google Chrome extension):

getElementsByTagNameNS("prefix", "name")

getElementsByTagName("prefix:name")

getElementsByTagName("prefix\\:name")

getElementsByTagName("name")

Edit: after some sleep, I found a working workaround :) This function returns the first node matching a full nodeName such as <prefix:name>:

// Helper function for nodes names that include a prefix and a colon, such as "<yt:rating>"
function getElementByNodeName(parentNode, nodeName)
{   
    var colonIndex = nodeName.indexOf(":");
    var tag = nodeName.substr(colonIndex + 1);
    var nodes = parentNode.getElementsByTagNameNS("*", tag);
    for (var i = 0; i < nodes.length; i++)
    {
        if (nodes[i].nodeName == nodeName) return nodes[i]
    }
    return undefined;
}

It can easily be modified in case you need to return all the matching elements. Hope it helps!

Share:
66,671

Related videos on Youtube

Brian Liang
Author by

Brian Liang

iOS (Objective-C), C/C++, C#

Updated on February 04, 2020

Comments

  • Brian Liang
    Brian Liang over 4 years

    I'm new to jQuery and would like to parse an XML document.

    I'm able to parse regular XML with the default namespaces but with XML such as:

    <xml xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882" xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882" xmlns:rs="urn:schemas-microsoft-com:rowset" xmlns:z="#RowsetSchema">
       <s:Schema id="RowsetSchema">
         <s:ElementType name="row" content="eltOnly" rs:CommandTimeout="30">
           <s:AttributeType name="ows_ID" rs:name="ID" rs:number="1">
            <s:datatype dt:type="i4" dt:maxLength="4" />
          </s:AttributeType>
           <s:AttributeType name="ows_DocIcon" rs:name="Type" rs:number="2">
            <s:datatype dt:type="string" dt:maxLength="512" />
          </s:AttributeType>
           <s:AttributeType name="ows_LinkTitle" rs:name="Title" rs:number="3">
            <s:datatype dt:type="string" dt:maxLength="512" />
          </s:AttributeType>
           <s:AttributeType name="ows_ServiceCategory" rs:name="Service Category" rs:number="4">
            <s:datatype dt:type="string" dt:maxLength="512" />
          </s:AttributeType>
        </s:ElementType>
      </s:Schema>
       <rs:data>
        <z:row ows_ID="2" ows_LinkTitle="Sample Data 1" />
        <z:row ows_ID="3" ows_LinkTitle="Sample Data 2" />
        <z:row ows_ID="4" ows_LinkTitle="Sample Data 3" />
      </rs:data>
    </xml>
    

    All I really want are the <z:row>.

    So far, I've been using:

    $.get(xmlPath, {}, function(xml) {
        $("rs:data", xml).find("z:row").each(function(i) {
            alert("found zrow");
        });
    }, "xml");
    

    with really no luck. Any ideas?

  • fmalina
    fmalina almost 15 years
    Yup, it works! Thank you for uncovering this hidden gem. Very useful for parsing Google Product RSS feeds.
  • Richard Clayton
    Richard Clayton almost 15 years
    Completely disagree. jQuery makes handling response XML easy, the only complication you will encounter is using xml namespaces.
  • Richard Clayton
    Richard Clayton almost 15 years
    Appreciate this. I wanted to use jQuery with SOAP but was afraid of the namespace issue. By the way, this also works for element names with periods: <element.name /> = $("element\\.name")
  • gnarf
    gnarf over 14 years
    $('[nodeName=rs:data]', xml).find('[nodeName=z:row]') - works with 1.3.2 under WebKit (where the escaped colon method apparently does not)
  • Josh Pearce
    Josh Pearce over 13 years
    this seems to have stopped working in jQuery version 1.4.4, which I think means jQuery has better XML namespace support. So to be safe, this works $('[nodeName=rs:data],data')
  • Josh Pearce
    Josh Pearce over 13 years
    this seems to have stopped working in jQuery version 1.4.4, which I think means jQuery has better XML namespace support. So to be safe, this works $('[nodeName=rs:data],data')
  • Gary
    Gary about 13 years
    Hi Josh I am using your method as above, many thanks. the nodename is media:thumbnail and am trying to retrieve the url for the image. Would you know how to reach it as $('[nodeName=media:thumbnail],url') is returning a javascript object. Thanks Again
  • Tim Down
    Tim Down about 13 years
    @Richard: When using Ajax, jQuery does use the responseXML property of the built-in XMLHttpRequest object, which is indeed an XML document. However, jQuery (until 1.5, when parseXML was introduced) had no way of parsing XML, so Chris was right.
  • Drew
    Drew almost 13 years
    Lots of syntax problems in this answer: The one that works in Chrome 13 and FF7 .find("[nodeName='namespace:name']") The single quotes are important or it will throw errors. You can use ,name as above, not sure that it offers anything.
  • Gapipro
    Gapipro over 12 years
    Now jQuery 1.7 is out and this last solution doesn't work anymore. What is the new way?
  • Matt Ryan
    Matt Ryan over 12 years
    using nodeName now needs the colon escaped aswell
  • Dominic K
    Dominic K almost 12 years
    This was the only one that reliably worked for me using the latest jQuery (same version) so thank you!
  • Miere
    Miere almost 12 years
    In jQuery 1.8.x it doesn't works anymore. It should accomplished with a custom pseudo class compatibility workaround, as explained here.
  • Admin
    Admin almost 12 years
    If the performance is important, then the best solution is to select the tags without jQuery. For a comparison, see: jsperf.com/node-vs-double-select/13
  • MatteoSp
    MatteoSp about 11 years
    given the jQuery version issues, this is clearly the best solution
  • mitaka
    mitaka almost 10 years
    Even though this answers the question for the given XML doc, I'd like to remind people that the prefixes like rs, dt or s are really not the namespaces. The namespaces are the URNs at the top of the file. The prefixes are just aliases chosen by the document author to keep things short. The same document, matching the same namespaces could be created with totally different prefixes. I'd encourage everyone to look for APIs that understand namespaces instead of assuming prefixes in your queries. E.g., in the browser DOM API you can use getElementByTagNameNS() and getAttributeNS().
  • Fillip Peyton
    Fillip Peyton over 9 years
    This worked for me while I used an .each() loop to iterate through item elements: $('dc\\:creator, creator', this).text(). Though, I'm not sure why the extra , creator was needed, and dc\\:creator didn't just work.
  • Mike Grace
    Mike Grace about 9 years
    Glad I was able to help :)
  • Baryon Lee
    Baryon Lee over 7 years
    In the latest safari, it don't support the usage. it works only previous version.
  • Gilman
    Gilman almost 7 years
    Thank you for snippet - this is extremely helpful / solves the issue.
  • cdauth
    cdauth over 2 years
    My understanding is that the first argument of getElementsByTagNameNS() should be the namespace, not the prefix. So the value of the xmlns:prefix attribute on the root element.