先决条件和示例
在本文中,我假设您熟悉Brett McLaughlin的“从Java™平台评估XPath”中描述的技术细节。 如果你不知道如何使用XPath运行Java程序,请参阅Brett的文章(请参阅相关主题的文章链接。)同样是真实的加载一个XML文件,并评估XPath表达式所需的API 。
所有示例都将使用以下XML文件:
清单1.示例XML
<?xml version="1.0" encoding="UTF-8"?>
<books:booklist
xmlns:books="http://univNaSpResolver/booklist"
xmlns="http://univNaSpResolver/book"
xmlns:fiction="http://univNaSpResolver/fictionbook">
<science:book xmlns:science="http://univNaSpResolver/sciencebook">
<title>Learning XPath</title>
<author>Michael Schmidt</author>
</science:book>
<fiction:book>
<title>Faust I</title>
<author>Johann Wolfgang von Goethe</author>
</fiction:book>
<fiction:book>
<title>Faust II</title>
<author>Johann Wolfgang von Goethe</author>
</fiction:book>
</books:booklist>
这个XML示例在根元素中声明了三个命名空间,在结构中更深的元素中声明了一个命名空间。 您将看到此设置导致的差异。
关于此XML示例的第二件有趣的事情是,元素booklist
具有三个子级,均名为book
。 但是第一个子级具有命名空间science
,而第二个子级具有命名空间fiction
。 这意味着这些元素与XPath完全不同。 您将在下面的示例中看到结果。
关于示例源代码的一点说明:该代码不是为维护而优化的,而是为了提高可读性。 这意味着它具有一些冗余。 通过System.out.println()
以最简单的方式产生输出。 与输出有关的所有代码行均在本文中缩写为“ ...”。 另外,我不在本文中介绍辅助方法,但它们包含在下载文件中(请参阅下载 )。
理论背景
命名空间的含义是什么,为什么要关心它们? 名称空间是元素或属性的标识符的一部分。 您可以具有具有相同本地名称但具有不同名称空间的元素或属性。 他们是完全不同的。 请参见上面的示例( science:book
和fiction:book
)。 如果合并来自不同来源的XML文件,则需要命名空间来解决命名冲突。 以一个XSLT文件为例。 它由XSLT命名空间的元素,您自己的命名空间的元素和XHTML命名空间的元素(通常)组成。 使用名称空间,可以避免有关具有相同本地名称的元素的歧义。
名称空间由URI定义(在本示例中为http://univNaSpResolver/booklist
)。 为了避免使用此长字符串,请定义与此URI关联的前缀(在示例中为books
)。 请记住,前缀就像一个变量:其名称无关紧要。 如果两个前缀引用相同的URI,则带前缀的元素的名称空间将相同(有关此示例,请参见清单5中的示例1)。
XPath表达式使用前缀(例如, books:booklist/science:book
),并且您必须提供与每个前缀关联的URI。 这就是NamespaceContext进入的地方。它正是这样做的。
本文介绍了在前缀和URI之间提供映射的不同方法。
在XML文件中,映射由xmlns
属性提供,例如: xmlns:books="http://univNaSpResolver/booklist"
或xmlns="http://univNaSpResolver/book"
(默认名称空间)。
提供名称空间解析的必要性
如果您具有使用名称空间的XML,那么如果不提供NamespaceContext,则XPath表达式将失败。 清单2中的示例0显示了这种情况。 XPath对象是在加载的XML文档上构造和评估的。 首先,尝试写一个表达式没有任何名称空间前缀( result1
)。 在第二部分中,使用名称空间前缀( result2
)编写表达式。
清单2.没有名称空间解析的示例0
private static void example0(Document example)
throws XPathExpressionException, TransformerException {
sysout("\n*** Zero example - no namespaces provided ***");
XPath xPath = XPathFactory.newInstance().newXPath();
...
NodeList result1 = (NodeList) xPath.evaluate("booklist/book", example,
XPathConstants.NODESET);
...
NodeList result2 = (NodeList) xPath.evaluate(
"books:booklist/science:book", example, XPathConstants.NODESET);
...
}
这将导致以下输出。
清单3.示例0的输出
*** Zero example - no namespaces provided ***
First try asking without namespace prefix:
--> booklist/book
Result is of length 0
Then try asking with namespace prefix:
--> books:booklist/science:book
Result is of length 0
The expression does not work in both cases.