如何使用jQuery解析带有命名空间的XML?

87

我刚接触jQuery,想要解析一个XML文档。

我可以使用默认命名空间解析常规的XML,但对于以下这种XML:

<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>

我真正需要的只是<z:row>

到目前为止,我一直在使用:

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

一直都没有什么运气。有什么想法吗?


省略命名空间前缀对我有用。请参见此答案:https://dev59.com/8oLba4cB1Zd3GeqPZxpL#25089647 - Vincil Bishop
20个回答

139

我明白了。

原来需要使用\\转义冒号。

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

正如Rich指出的:

更好的解决方案不需要转义,适用于所有“现代”浏览器:

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

2
$('[nodeName=rs:data]', xml).find('[nodeName=z:row]') - 在 WebKit 的 1.3.2 版本下工作(在那里,转义冒号的方法显然不起作用)。 - gnarf
2
这似乎在jQuery 1.4.4版本中停止工作,我认为这意味着jQuery具有更好的XML命名空间支持。因此为了安全起见,这个代码可以使用:$('[nodeName=rs:data],data') - Josh Pearce
15
现在jQuery 1.7已经发布,这个最后的解决方案不再适用。新方法是什么? - Gapipro
3
在 jQuery 1.8.x 中,它不再起作用。可以通过自定义伪类兼容性解决方法来实现,如此处所述:http://www.h-online.com/open/news/item/jQuery-1-8-1-has-compatibility-workaround-for-custom-pseudo-selectors-1696774.html。 - Miere
5
尽管这个答案回答了给定的XML文档的问题,但是我想提醒人们,像rsdts这样的前缀实际上不是命名空间。命名空间是文件顶部的URNs。前缀只是文档作者选择的别名,以保持简短。完全不同的前缀可以创建匹配相同命名空间的同一文档。我鼓励每个人寻找了解命名空间的API,而不是假设查询中的前缀。例如,在浏览器DOM API中,您可以使用getElementByTagNameNS()getAttributeNS() - sergiopereira
显示剩余5条评论

35

我花了几个小时阅读有关插件和各种解决方案,但都没有成功。

ArnisAndy在jQuery的一个讨论中发布了一个链接,其中提供了这个答案,我可以确认在使用jQuery(v1.7.2)的 Chrome(v18.0)、FireFox(v11.0)、IE(v9.08) 和 Safari (v5.1.5) 中,这对我有效。

我正在尝试抓取名为<content:encoded>的WordPress feed内容,以下是对我有效的代码:

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

3
这是唯一一个在使用最新版本的jQuery(相同版本)时对我可靠地起作用的,所以谢谢你! - Dominic K
2
当我使用.each()循环遍历item元素时,这对我起作用:$('dc\\:creator, creator', this).text()。虽然我不确定为什么需要额外的, creator,而dc\\:creator没有起作用。 - Fillip Peyton

19
如果您正在使用jquery 1.5,您需要在节点选择器属性值周围添加引号才能使其正常工作:
.find('[nodeName="z:row"]')

19

尽管以上答案似乎是正确的,但在 WebKit 浏览器(Safari、Chrome)中无效。我认为更好的解决方案应该是:

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

5
这似乎在jQuery版本1.4.4中停止工作,我认为这意味着jQuery具有更好的XML命名空间支持。因此为了安全起见,这个代码可以使用 $('[nodeName=rs:data],data') - Josh Pearce

17
假如有人需要使用普通的JavaScript(无需jQuery)并且只在Google Chrome(Webkit)中工作,那么在进行了大量研究和测试后,我发现以下是唯一可用的方法:

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

这将适用于检索以下节点:<prefix:name>。可以看到前缀或命名空间被省略,它将匹配具有不同命名空间但标记名称为name的元素。但希望这对您不会造成问题。

对我来说,以上所有都没有奏效(我正在开发一个Google Chrome扩展程序):

getElementsByTagNameNS("prefix", "name")
getElementsByTagName("prefix:name")
getElementsByTagName("prefix\\:name")
getElementsByTagName("name")
编辑:经过一些睡眠后,我找到了一种有效的解决方法 :) 此函数将返回与完整的nodeName匹配的第一个节点,例如<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;
}

如果需要返回所有匹配的元素,它很容易被修改。希望能对你有所帮助!


我的理解是 getElementsByTagNameNS() 的第一个参数应该是命名空间,而不是前缀。因此,在根元素上,应该使用 xmlns:prefix 属性的值。 - cdauth

14

以上的解决方案都不是很好。我找到了这个方法,并已经进行了速度优化。只需要添加这个,就能轻松解决:

$.fn.filterNode = function(name) {
    return this.find('*').filter(function() {
       return this.nodeName === name;
    });
};

使用方法:

var ineedthatelementwiththepsuedo = $('someparentelement').filterNode('dc:creator');

来源:http://www.steveworkman.com/html5-2/javascript/2011/improving-javascript-xml-node-finding-performance-by-2000/

该文介绍如何通过使用纯JavaScript代码,在XML文件中查找节点的性能提高了2000倍。作者尝试了不同的方法,最终找到了一种有效的方法来增强性能,并在文章中详细描述了实现步骤。

感谢您提供的代码片段 - 这非常有帮助/解决了问题。 - Gilman

9
"\\\\" 转义并不能保证完全无误,简单的

需要注意。"
.find('[nodeName="z:row"]')

在Jquery 1.7版本之后,该方法似乎已经失效。我找到了一个针对1.7版本的解决方案,使用了一个过滤函数,链接在这里:Improving Javascript XML Node Finding Performance


3

在评论中找到了解决方案:使用jQuery $().find解析带命名空间的XML

对于我来说,使用冒号后面的节点名称的后半部分就可以了。使用.find("lat")而不是.find("geo\:lat")就可以了。


我的设置:

  • Chrome 42
  • jQuery 2.1.3

示例XML(从Google联系人API中提取的片段):

<entry>
  <id>http://www.google.com/m8/feeds/contacts/mstefanow%40gmail.com/base/0</id>
  <gd:email rel="http://schemas.google.com/g/2005#other" address="email@example.com" primary="true"/>
</entry>

解析代码:

var xmlDoc = $.parseXML( xml );
var $xml = $( xmlDoc );
var $emailNode = $xml.find( "email" );
$("#email").html($emailNode.attr("address"));

Plnkr: http://plnkr.co/edit/l8VzyDq1NHtn5qC9zTjf?p=preview

可以访问这个链接来查看 Plnkr。它是一个在线代码编辑器,您可以使用它来创建、编辑和共享 Web 应用程序的示例。

很高兴我能帮到你 :) - Mike Grace

3

如果性能很重要,那么最好的解决方案是选择不带jQuery的标签。进行比较,请参见:http://jsperf.com/node-vs-double-select/13 - user699082

2

jQuery 1.7不支持以下内容:

$(xml).find("[nodeName=a:IndexField2]")

我找到了一种解决方案,可以在Chrome、Firefox和IE中使用。这种方法是基于一个事实:在IE中使用的选择器与在Chrome中使用的选择器不同。我们可以同时使用两种选择器,让它们各自在不同浏览器中工作。

$(xml).find('a\\\\:IndexField2, IndexField2')

在IE中,这将返回使用命名空间的节点(Firefox和IE需要命名空间),而在Chrome中,选择器根据非命名空间选择器返回节点。我没有在Safari中测试过,但应该可以工作,因为它在Chrome中工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接