具有显式默认命名空间的 XML 文档的 XPath 和命名空间规范
Posted
技术标签:
【中文标题】具有显式默认命名空间的 XML 文档的 XPath 和命名空间规范【英文标题】:XPath and namespace specification for XML documents with an explicit default namespace 【发布时间】:2014-09-17 06:04:07 【问题描述】:我正在努力按照包XML
(参数namespaces
)对于具有显式xmlns
命名空间的XML 文档的要求,获得XPath 表达式和命名空间规范的正确组合 在顶部元素中定义。
更新
多亏了 har07,我才能把它放在一起:
查询命名空间后,ns
的第一个条目还没有名称,这就是问题所在:
nsDefs <- xmlNamespaceDefinitions(doc)
ns <- structure(sapply(nsDefs, function(x) x$uri), names = names(nsDefs))
> ns
omegahat r
"http://something.org" "http://www.omegahat.org" "http://www.r-project.org"
所以我们只需分配一个名称作为前缀(这可以是任何有效的 R 名称):
names(ns)[1] <- "xmlns"
现在我们要做的就是在 XPath 表达式中使用默认的命名空间前缀 everywhere:
getNodeSet(doc, "/xmlns:doc//xmlns:b[@omegahat:status='foo']", ns)
对于那些对基于name()
和namespace-uri()
(以及其他)的替代解决方案感兴趣的人,this post 可能会有所帮助。
仅供参考:这是我们找到解决方案之前的试错代码:
考虑?xmlParse
的例子:
require("XML")
doc <- xmlParse(system.file("exampleData", "tagnames.xml", package = "XML"))
> doc
<?xml version="1.0"?>
<doc>
<!-- A comment -->
<a xmlns:omegahat="http://www.omegahat.org" xmlns:r="http://www.r-project.org">
<b>
<c>
<b/>
</c>
</b>
<b omegahat:status="foo">
<r:d>
<a status="xyz"/>
<a/>
<a status="1"/>
</r:d>
</b>
</a>
</doc>
nsDefs <- xmlNamespaceDefinitions(getNodeSet(doc, "/doc/a")[[1]])
ns <- structure(sapply(nsDefs, function(x) x$uri), names = names(nsDefs))
getNodeSet(doc, "/doc//b[@omegahat:status='foo']", ns)[[1]]
然而,在我的文档中,命名空间已经在 <doc>
标记中定义,因此我相应地修改了示例 XML 代码:
xml_source <- c(
"<?xml version=\"1.0\"?>",
"<doc xmlns:omegahat=\"http://www.omegahat.org\" xmlns:r=\"http://www.r-project.org\">",
"<!-- A comment -->",
"<a>",
"<b>",
"<c>",
"<b/>",
"</c>",
"</b>",
"<b omegahat:status=\"foo\">",
"<r:d>",
"<a status=\"xyz\"/>",
"<a/>",
"<a status=\"1\"/>",
"</r:d>",
"</b>",
"</a>",
"</doc>"
)
write(xml_source, file="exampleData_2.xml")
doc <- xmlParse("exampleData_2.xml")
nsDefs <- xmlNamespaceDefinitions(doc)
ns <- structure(sapply(nsDefs, function(x) x$uri), names = names(nsDefs))
getNodeSet(doc, "/doc", namespaces = ns)
getNodeSet(doc, "/doc//b[@omegahat:status='foo']", namespaces = ns)[[1]]
一切仍然正常。不过,更重要的是,我的 XML 代码还明确定义了默认命名空间 (xmlns
):
xml_source <- c(
"<?xml version=\"1.0\"?>",
"<doc xmlns=\"http://something.org\" xmlns:omegahat=\"http://www.omegahat.org\" xmlns:r=\"http://www.r-project.org\">",
"<!-- A comment -->",
"<a>",
"<b>",
"<c>",
"<b/>",
"</c>",
"</b>",
"<b omegahat:status=\"foo\">",
"<r:d>",
"<a status=\"xyz\"/>",
"<a/>",
"<a status=\"1\"/>",
"</r:d>",
"</b>",
"</a>",
"</doc>"
)
write(xml_source, file="exampleData_3.xml")
doc <- xmlParse("exampleData_3.xml")
nsDefs <- xmlNamespaceDefinitions(doc)
ns <- structure(sapply(nsDefs, function(x) x$uri), names = names(nsDefs))
以前有效的方法现在失败了:
> getNodeSet(doc, "/doc", namespaces = ns)
list()
attr(,"class")
[1] "XMLNodeSet"
Warning message:
using http://something.org as prefix for default namespace http://something.org
> getNodeSet(doc, "/xmlns:doc", namespaces = ns)
XPath error : Undefined namespace prefix
XPath error : Invalid expression
Error in xpathApply.XMLInternalDocument(doc, path, fun, ..., namespaces = namespaces, :
error evaluating xpath expression /xmlns:doc
In addition: Warning message:
using http://something.org as prefix for default namespace http://something.org
getNodeSet(doc, "/xmlns:doc",
namespaces = matchNamespaces(doc, namespaces="xmlns", nsDefs = nsDefs)
)
这似乎让我更接近:
> getNodeSet(doc, "/xmlns:doc",
+ namespaces = matchNamespaces(doc, namespaces="xmlns", nsDefs = nsDefs)
+ )[[1]]
<doc xmlns="http://something.org" xmlns:omegahat="http://www.omegahat.org" xmlns:r="http://www.r-project.org">
<!-- A comment -->
<a>
<b>
<c>
<b/>
</c>
</b>
<b omegahat:status="foo">
<r:d>
<a status="xyz"/>
<a/>
<a status="1"/>
</r:d>
</b>
</a>
</doc>
attr(,"class")
[1] "XMLNodeSet"
然而,现在我不知道如何进行才能到达子节点:
> getNodeSet(doc, "/xmlns:doc//b[@omegahat:status='foo']", ns)[[1]]
XPath error : Undefined namespace prefix
XPath error : Invalid expression
Error in xpathApply.XMLInternalDocument(doc, path, fun, ..., namespaces = namespaces, :
error evaluating xpath expression /xmlns:doc//b[@omegahat:status='foo']
In addition: Warning message:
using http://something.org as prefix for default namespace http://something.org
> getNodeSet(doc, "/xmlns:doc//b[@omegahat:status='foo']",
+ namespaces = c(
+ matchNamespaces(doc, namespaces="xmlns", nsDefs = nsDefs),
+ matchNamespaces(doc, namespaces="omegahat", nsDefs = nsDefs)
+ )
+ )
list()
attr(,"class")
[1] "XMLNodeSet"
【问题讨论】:
请注意,ns
指的是 R 中的自然样条函数。这将覆盖该基本函数。
【参考方案1】:
没有前缀 (xmlns="..."
) 的命名空间定义是默认命名空间。如果 XML 文档具有默认命名空间,则在上述默认命名空间中考虑声明了默认命名空间的元素及其所有没有前缀且没有不同默认命名空间声明的后代。
因此,在您的情况下,您需要在 XPath 中所有元素的开头使用为默认命名空间注册的前缀,例如:
/xmlns:doc//xmlns:b[@omegahat:status='foo']
更新:
实际上我不是r
的用户,但是在网上查看一些参考资料可能会起作用:
getNodeSet(doc, "/ns:doc//ns:b[@omegahat:status='foo']", c(ns="http://something.org"))
【讨论】:
啊哈!谢谢你的信息!但是,我仍在努力将它们放在一起:getNodeSet(doc, "/xmlns:doc//xmlns:b[@omegahat:status='foo'")[[1]]
和 getNodeSet(doc, "/xmlns:doc//xmlns:b[@omegahat:status='foo'", namespaces = ns)[[1]]
返回错误。我还缺少什么?
好的,现在明白了:将ns
中的第一个条目的名称更改为xmlns
(或任何其他前缀)就可以了:names(ns)[1] <- "xmlns"
然后getNodeSet(doc, "/xmlns:doc//xmlns:b[@omegahat:status='foo']", ns)
。谢谢大佬!
;-) 抱歉,我来晚了【参考方案2】:
我认为@HansHarhoff 提供了一个非常好的解决方案。
对于仍在寻找解决方案的其他人,根据我的经验,我认为以下方法更普遍,因为单个 XML 文档可以有多个命名空间。
doc <- xmlInternalTreeParse(xml_source)
ns <- getDefaultNamespace(doc)[[1]]$uri
names(ns)[1] <- "xmlns"
getNodeSet(doc, "//xmlns:Parameter", namespaces = ns)
【讨论】:
【参考方案3】:我遇到了类似的问题,但就我而言,我并不关心命名空间,并且想要一个忽略命名空间的解决方案。
假设我们在变量 myxml 中有以下 XML:
<root xmlns="uri:someuri.com:schema">
<Parameter>Test
</Parameter>
</root>
在 R 中,我们想读取这个,所以我们运行:
myxml <- '
<root xmlns="uri:someuri.com:schema">
<Parameter>Test
</Parameter>
</root>
'
myxmltop <- xmlParse(myxml)
ns <- xmlNamespaceDefinitions(myxmltop, simplify = TRUE)
在这里,我通过使用 simple=TRUE 参数简化了 Rappster 的代码。 现在我们可以像 Rappster 的代码一样添加命名空间的名称/前缀:
names(ns)[1] <- "xmlns"
现在我们可以通过以下方式引用这个命名空间:
getNodeSet(myxmltop, "//xmlns:Parameter", namespaces =ns)
更简单的解决方案(忽略命名空间)
我们还可以通过以下方式匹配任何命名空间来更加灵活:
myxmltop <- xmlParse(myxml)
getNodeSet(myxmltop, "//*[local-name() = 'Parameter']")
这个解决方案的灵感来自this SO answer。
【讨论】:
以上是关于具有显式默认命名空间的 XML 文档的 XPath 和命名空间规范的主要内容,如果未能解决你的问题,请参考以下文章
SimpleXML 中的 XPath 用于默认命名空间,无需前缀