诸如“为什么用 XPath 的表达式进行查询,却没有返回所期望的结果?”的问题通常都与命名空间(NameSpace)有关,而且绝大多数是与缺省命名空间(Default Namespace)有关。本文试图解释这个问题并针对三种流行的 XPath 实现给出解决方法:Jaxen、JAXP XPPathFactory 以及 XSLT。
内容列表
- 问题描述
- “前缀-命名空间”映射
- Jaxen 和 Dom4J
- Jaxen 和 XOM
- Jaxen 和 JDOM
- JAXP XPathFactory
- XSLT
- 结束语
- 资源
问题描述
看下述 XML:
当我们使用与上文相同的 XPath “//cd”,将得不到任何元素。这是因为指定的 XPath 返回的是所有不属于任何命名空间的“cd”节点,而本例中,所有的“cd”元素都属于缺省的命名空间“
<catalog>
<cd>
<artist>Sufjan Stevens</artist>
<title>Illinois</title>
<src>[url]http://www.sufjan.com/</[/url]src>
</cd>
<cd>
<artist>Stoat</artist>
<title>Future come and get me</title>
</cd>
<cd>
<artist>The White Stripes</artist>
<title>Get behind me satan</title>
</cd>
</catalog>
你可以使用“//cd”来得到没有在任何命名空间中定义的“cd”节点。
现在让我们来改造这个 XML,让它的所有元素都属于
'http://www.edankert.com/examples/'
命名空间中。 为了避免在每个不同的元素前都要加个前缀,我们在根元素上定义通常所说的缺省命名空间。改造后的 XML 如下:
<catalog xmlns="http://www.edankert.com/examples/">
<cd>
<artist>Sufjan Stevens</artist>
<title>Illinois</title>
<src>[url]http://www.sufjan.com/</[/url]src>
</cd>
<cd>
<artist>Stoat</artist>
<title>Future come and get me</title>
</cd>
<cd>
<artist>The White Stripes</artist>
<title>Get behind me satan</title>
</cd>
</catalog>
[url]http://www.edankert.com/examples/[/url]”。
“前缀-命名空间”映射
为了取出命名空间“[url]http://www.edankert.com/examples/[/url]”中的所有“cd”元素,我们需要对 XPath 表达式做一些额外的工作。
为了解决这个问题,XPath 规范允许我们使用 QName 来指定元素或者属性。QName 可以是元素的直接名称(形如“element”),或者包含一个前缀(形如“pre:element”)。这个前缀需要映射到一个命名空间的 URI 上。例如,如果把“pre”前缀映射到“[url]http://www.edankert.com/test[/url]”上,则通过“pre:element”可以查找出属于命名空间“[url]http://www.edankert.com/test[/url]”的所有 “element”元素。
在本例中,我们把“edx”映射到“'http://www.edankert.com/examples/”命名空间上。通过 XPath“//edx:cd”就可以查找出属于“'http://www.edankert.com/examples/”命名空间的所有“cd”元素。
XPath 处理器允许设置“前缀-命名空间”的映射,但是,如何去映射,却要依赖于具体的实现。下文举例说明 Jaxen (JDOM/dom4j/XOM)、JAXP 以及 XSLT 中是如何进行“前缀-命名空间”的映射的。
Jaxen 和 Dom4J
下述代码从文件系统读入一个 XML 文件到 org.dom4j.Document 对象中,并且在 Document 中查找属于“[url]http://www.edankert.com/examples/[/url]”命名空间的所有“cd”元素。
try {
SAXReader reader = new SAXReader();
Document document = reader.read( "file:catalog.xml");
HashMap map = new HashMap();
map.put( "edx", "http://www.edankert.com/examples/");
XPath xpath = new Dom4jXPath( "//edx:cd");
xpath.setNamespaceContext( new SimpleNamespaceContext( map));
List nodes = xpath.selectNodes( document);
...
} catch ( JaxenException e) { // An error occurred parsing or executing the XPath ... } catch ( DocumentException e) {
// the document is not well-formed.
...
}
SAXReader reader = new SAXReader();
Document document = reader.read( "file:catalog.xml");
HashMap map = new HashMap();
map.put( "edx", "http://www.edankert.com/examples/");
XPath xpath = new Dom4jXPath( "//edx:cd");
xpath.setNamespaceContext( new SimpleNamespaceContext( map));
List nodes = xpath.selectNodes( document);
...
} catch ( JaxenException e) { // An error occurred parsing or executing the XPath ... } catch ( DocumentException e) {
// the document is not well-formed.
...
}
第一步,创建一个 SAXReader,用来从文件系统中读取“catalog.xml”并创建一个特定于 Dom4j 的 Document 对象。
第二步,对于所有 Jaxen 实现都一样,就是创建一个 HashMap 对象,用于保存“前缀-命名空间的 URI”的映射。
为了能通过 Dom4j 使用 Jaxen 的 XPath 功能,需要创建一个与 Dom4j 相关的 XPath 对象:Dom4jXPath。创建方法是把 XPath 的表达式(即“//edx:cd”)传给 Dom4jXPath 的构造方法。
现在,我们已经创建了 XPath 对象,接下来可以把“前缀-命名空间”的映射表传递给 XPath 引擎:把这个 HashMap 映射表用 SimpleNamespaceContext 包装起来。SimpleNamespaceContext 是 Jaxen 的 NamespaceContext 接口的默认实现类。
最后一步就是调用 XPath 对象的 selectNodes() 方法进行查找。并把完整的 Dom4j Document 对象作为参数传递进去。实际上,Document 中的任何结点都可以作为参数。
Jaxen 和 XOM
XOM 是基于简单的 Java DOM APIs 之上的最新工具,它的设计初衷是提供简单和易学易用的接口。
try {
Builder builder = new Builder();
Document document = builder.build( "file:catalog.xml");
HashMap map = new HashMap();
map.put( "edx", "http://www.edankert.com/examples/");
XPath xpath = new XOMXPath( "//edx:cd");
xpath.setNamespaceContext( new SimpleNamespaceContext( map));
List nodes = xpath.selectNodes( document);
...
} catch ( JaxenException e) { // An error occurred parsing or executing the XPath ... } catch ( IOException e) {
// An error occurred opening the document
...
} catch ( ParsingException e) {
// An error occurred parsing the document
...
}
Builder builder = new Builder();
Document document = builder.build( "file:catalog.xml");
HashMap map = new HashMap();
map.put( "edx", "http://www.edankert.com/examples/");
XPath xpath = new XOMXPath( "//edx:cd");
xpath.setNamespaceContext( new SimpleNamespaceContext( map));
List nodes = xpath.selectNodes( document);
...
} catch ( JaxenException e) { // An error occurred parsing or executing the XPath ... } catch ( IOException e) {
// An error occurred opening the document
...
} catch ( ParsingException e) {
// An error occurred parsing the document
...
}
下一步创建出包含了“前缀-命名空间”映射关系的 HashMap 对象。
我们需要创建一个特定于 XOM 的 XPath 对象:
XOMXPath
。创建方法是把 XPath 表达式传递给构造方法,然后就可以通过 XOM 使用 Jaxen 的 XPath 功能了。 创建完 XPath 对象后,同样,我们把“前缀-命名空间”的映射表用 SimpleNamespaceContext 对象封装后,传递给 XPath 引擎。
最后调用 XPath 对象的“selectNodes()”方法进行查找,把 XOM Document 对象作为本方法的参数。
Jaxen 和 JDOM
JDOM 是第一个提供简单的 XML 访问 API 的工具。
try {
SAXBuilder builder = new SAXBuilder();
Document document = builder.build( "file:catalog.xml");
HashMap map = new HashMap();
map.put( "edx", "http://www.edankert.com/examples/");
XPath xpath = new JDOMXPath( "//edx:cd");
xpath.setNamespaceContext( new SimpleNamespaceContext( map));
List nodes = xpath.selectNodes( document);
...
} catch ( JaxenException e) { // An error occurred parsing or executing the XPath ... } catch ( IOException e) {
// An error occurred opening the document
...
} catch ( JDOMException e) {
// An error occurred parsing the document
...
}
SAXBuilder builder = new SAXBuilder();
Document document = builder.build( "file:catalog.xml");
HashMap map = new HashMap();
map.put( "edx", "http://www.edankert.com/examples/");
XPath xpath = new JDOMXPath( "//edx:cd");
xpath.setNamespaceContext( new SimpleNamespaceContext( map));
List nodes = xpath.selectNodes( document);
...
} catch ( JaxenException e) { // An error occurred parsing or executing the XPath ... } catch ( IOException e) {
// An error occurred opening the document
...
} catch ( JDOMException e) {
// An error occurred parsing the document
...
}
首先,通过
SAXBuilder
创建了一个特定于 JDom 的 Document
对象。 接着创建一个特定于 JDOM 的 XPath 对象:
JDOMXPath
。 然后,把“前缀-命名空间”的映射表(HashMap)用
SimpleNamespaceContext
对象封装起来,传递给 XPath 引擎。 最后调用 XPath 对象的“selectNodes()”方法来进行查找,并把 JDOM 的
Document
对象作为本方法的输入参数。JAXP XPathFactory
从 1.3 版起, JAXP 还提供了一种在 XML Object Models 上进行查询的通用机制。
try {
DocumentBuilderFactory domFactory =DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware( true);
DocumentBuilder builder = domFactory.newDocumentBuilder();Document document = builder.parse( new InputSource( "file:catalog.xml"));
XPathFactory factory =XPathFactory.newInstance();
XPath xpath = factory.newXPath();
xpath.setNamespaceContext( new NamespaceContext() {
public String getNamespaceURI(String prefix) {
if ( prefix.equals( "edx")) {
return "http://www.edankert.com/examples/";
} else if ...
...
}
return XPathConstants.NULL_NS_URI;
}
public String getPrefix(String namespaceURI) {
if ( namespaceURI.equals( "http://www.edankert.com/examples/")) {
return "edx";
} else if ...
...
}
return null;
}
public Iterator getPrefixes(String namespaceURI) {
ArrayList list = new ArrayList();
if ( namespaceURI.equals( "http://www.edankert.com/examples/")) {
list.add( "edx");
} else if ...
...
}
return list.iterator();
}
});
Object nodes = xpath.evaluate( "//edx:cd", document.getDocumentElement(),
XPathConstants.NODESET);
...
} catch (ParserConfigurationException e) {
...
} catch (XPathExpressionException e) {
...
} catch (SAXException e) {
...
} catch (IOException e) {
...
}
DocumentBuilderFactory domFactory =DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware( true);
DocumentBuilder builder = domFactory.newDocumentBuilder();Document document = builder.parse( new InputSource( "file:catalog.xml"));
XPathFactory factory =XPathFactory.newInstance();
XPath xpath = factory.newXPath();
xpath.setNamespaceContext( new NamespaceContext() {
public String getNamespaceURI(String prefix) {
if ( prefix.equals( "edx")) {
return "http://www.edankert.com/examples/";
} else if ...
...
}
return XPathConstants.NULL_NS_URI;
}
public String getPrefix(String namespaceURI) {
if ( namespaceURI.equals( "http://www.edankert.com/examples/")) {
return "edx";
} else if ...
...
}
return null;
}
public Iterator getPrefixes(String namespaceURI) {
ArrayList list = new ArrayList();
if ( namespaceURI.equals( "http://www.edankert.com/examples/")) {
list.add( "edx");
} else if ...
...
}
return list.iterator();
}
});
Object nodes = xpath.evaluate( "//edx:cd", document.getDocumentElement(),
XPathConstants.NODESET);
...
} catch (ParserConfigurationException e) {
...
} catch (XPathExpressionException e) {
...
} catch (SAXException e) {
...
} catch (IOException e) {
...
}
首先用 JAXP 的
DocumentBuilderFactory
创建一个org.w3c.dom.Document
对象,确保启用了 namespace 处理功能。 现在可以通过 XPathFactory 来创建 XPath 对象,并通过 XPath 对象对文档进行查询。
为了创建“前缀-命名空间”映射并传递给 XPath 引擎,我们需要实现
NamespaceContext
接口,该接口目前还没有默认实现类。这就意味着要实现 getNamespaceURI、getPrefix 和getPrefixes 方法,并确保这些方法能返回正确的值,包括“xmlns”和“xml”前缀所对应的命名空间的 URI 值。 把我们自己实现的
NamespaceContext
对象传递给 XPath
引擎后,就可以通过 evaluate 方法来查询 XPath 表达式所对应的元素:使用上文中提到的 XPath 表达式,并使用 Document 的根节点作为输入入参数,并接收一个 NodeList
对象作为返回结果。XSLT
XPath 设计的初衷是用于 XSLT。这也许能解释“为什么在 XSLT 中定义命名空间的前缀是一件很平常的事”(也许因为 XSLT 也是一个 XML 名词的缘故吧)。
<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="//edx:cd" xmlns:edx="http://www.edankert.com/examples/">
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
<xsl:template match="//edx:cd" xmlns:edx="http://www.edankert.com/examples/">
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
只需要使用 XML 本身的机制,简单地为 edx 前缀赋予一个命名空间的 URI 值。
通过与我们的 XPath 表达式“
//edx:cd
”相匹配的 xsl:template,能得到与上文其他例子相同的输出结果。结束语
为了在(缺省)命名空间上使用 XPath 表达式,我们需要指定一个“前缀-命名空间”映射。正如我们所看到的,具体使用什么样的前缀名称,是无关紧要的。
同样的方法,也可以用于查询那些用其他前缀修饰的元素。这意味着上面的例子对下述 XML 也有效。下述 XML 没有使用缺省命名空间,而是使用了 examples 作命名空间的前缀:
<examples:catalog xmlns:examples="http://www.edankert.com/examples/">
<examples:cd>
<examples:artist>Sufjan Stevens</examples:artist>
<examples:title>Illinois</examples:title>
<examples:src>[url]http://www.sufjan.com/</[/url]examples:src>
</examples:cd>
<examples:cd>
<examples:artist>Stoat</examples:artist>
<examples:title>Future come and get me</examples:title>
<examples:src>[url]http://www.stoatmusic.com/</[/url]examples:src>
</examples:cd>
<examples:cd>
<examples:artist>The White Stripes</examples:artist>
<examples:title>Get behind me satan</examples:title>
<examples:src>[url]http://www.whitestripes.com/</[/url]examples:src>
</examples:cd>
</examples:catalog>
<examples:cd>
<examples:artist>Sufjan Stevens</examples:artist>
<examples:title>Illinois</examples:title>
<examples:src>[url]http://www.sufjan.com/</[/url]examples:src>
</examples:cd>
<examples:cd>
<examples:artist>Stoat</examples:artist>
<examples:title>Future come and get me</examples:title>
<examples:src>[url]http://www.stoatmusic.com/</[/url]examples:src>
</examples:cd>
<examples:cd>
<examples:artist>The White Stripes</examples:artist>
<examples:title>Get behind me satan</examples:title>
<examples:src>[url]http://www.whitestripes.com/</[/url]examples:src>
</examples:cd>
</examples:catalog>
使用“//edx:cd”作为 XPath 表达式,使用与前文例子相同的“前缀-命名空间”映射,在这个 XML 上同样能查询出属于“[url]http://www.edankert.com/examples/[/url]”命名空间的所有“cd”元素。
资源
- Extensible Markup Language (XML) 1.0 (Third Edition)
[url]http://www.w3.org/TR/REC-xml/[/url] - Namespaces in XML
[url]http://www.w3.org/TR/REC-xml-names/[/url] - XML Path Language (XPath) Version 1.0
[url]http://www.w3.org/TR/xpath[/url] - XSL Transformations (XSLT) Version 1.0
[url]http://www.w3.org/TR/xslt[/url] - dom4j
[url]http://www.dom4j.org/[/url] - XOM
[url]http://www.xom.nu/[/url] - JDOM
[url]http://www.jdom.org/[/url] - Jaxen
[url]http://www.jaxen.org/[/url] - Java 5.0
[url]http://java.sun.com/j2se/1.5.0/[/url]
相关推荐
在处理具有命名空间的XML文档时,XPath的使用会变得稍微复杂,因为命名空间为元素和属性提供了唯一的标识,防止了名称冲突。DOM4J是一个流行的Java库,用于处理XML、HTML和DOM文档,它支持XPath查询,使得处理带有...
你可以使用这些方法结合XPath表达式来定位和操作带有命名空间的元素。 5. **创建和写入带有命名空间的新XML**: 如果需要创建新的XML元素并附加命名空间,可以使用`Element`类的`addNamespace()`方法,然后创建和...
.NET Framework 是微软开发的一个全面的开发平台,它包含了大量的命名空间,这些命名空间组织了各种类和接口,方便开发者在不同领域进行编程。本文将详细解释一些常见的命名空间及其核心功能。 1. **System....
在XML中,命名空间(Namespaces)是解决元素名称冲突的关键机制,尤其在处理多个XML文档交互时。本实例主要涉及如何在XML文档中进行带有命名空间的操作,包括增、删、改、查等常见操作。 首先,理解XML命名空间的...
在实际应用中,你可能还需要处理命名空间,因为XML文档中可能存在多个前缀和对应的URI。DOM4J提供了注册命名空间的方法,以正确解析带有前缀的XPath表达式。 总结来说,Demo4j虽然不直接支持XPath,但通过结合DOM4J...
XML是一种广泛使用的数据交换格式,`System.Xml`命名空间提供了处理XML文档的类,如`XmlDocument`和`XmlNode`,支持DOM和XPath查询。 ### 12. System.Media 多媒体支持是许多应用的需求,`System.Media`提供了播放...
XPath 将一个 XML 文档建模成为一棵...XPath 充分支持 XML 命名空间[XML Names]。这样,节点的名字被建模成由一个局城部分和可能为空的命名空间 URI 组成的对;这被称为扩展名。5 数据模型描述了数据模型详细细节。
XPath 节点是一个重要的概念,在 XPath 中,有七种类型的节点:元素、属性、文本、命名空间、处理指令、注释以及文档(根)节点。XML 文档是被作为节点树来对待的。树的根被称为文档节点或者根节点。 XPath 语法是 ...
4. 变量和命名空间:XPath允许使用变量来存储值,并支持命名空间以解决元素和属性的命名冲突问题。 在Java中使用XPath,你需要引入如`jaxen`或`jaxp`等Jar包。这些包提供了API,如`org.jaxen.dom.DOMXPath`和`javax...
4. **命名空间处理**:在处理包含命名空间的XML时,需正确处理命名空间前缀和声明,以避免选取错误的节点。 5. **函数和运算符**:熟练运用XPath提供的各种函数和运算符,如字符串处理、数值计算和逻辑判断。 使用...
- **节点类型**:XPath处理的节点包括元素(Element)、属性(Attribute)、文本(Text)、命名空间(Namespace)、处理指令(Processing Instruction)、注释(Comment)和文档(Document)节点。 - **路径表达式...
1. **基本概念**:理解XPath和Css选择器的基本语法和用法,包括各种选择器的含义和应用场景。 2. **选择器的使用**:学习如何编写XPath和Css表达式来定位页面上的特定元素,例如,通过id、class、tag name、属性等...
1. **节点类型**:Xpath中的节点包括元素(element)、属性(attribute)、文本(text)、命名空间(namespace)、处理指令(processing-instruction)、注释(comment)以及文档节点(document node)。 2. **路径...
在C#中,可以使用`System.Xml`命名空间下的`XmlDocument`类来解析和操作XML文档。 2. **选取节点**:小工具会提供一个交互界面,用户可以通过选择文档中的元素来获取其XPath路径。这可能通过鼠标点击事件实现,当...
5. **XPath选择器**:如果只是处理已返回的XML数据,而不改变WebAPI的行为,可以使用XPath选择器来处理XML节点,忽略或移除命名空间。 以上方法中,自定义XML序列化器和媒体类型格式化器是最直接的解决方案,但可能...
此外,DOM4J还支持命名空间、XPath表达式以及转换为其他XML格式(如SAX或DOM)的能力。 XPath是W3C制定的一种查询语言,用于在XML文档中查找信息。它可以定位到XML文档中的特定节点,如元素、属性、文本等。XPath...
XPath 节点有七种类型:元素、属性、文本、命名空间、处理指令、注释以及文档节点(或称根节点)。这些节点之间存在一定的关系,如父子关系、兄弟关系等。XPath 语法包括了 XPath 路径表达式、XPath 标准函数、XPath...
1. **节点类型**:XPath支持元素(element)、属性(attribute)、文本(text)、命名空间(namespace)、处理指令(processing-instruction)和注释(comment)六种基本节点类型。 2. **轴(Axis)**:轴定义了...
XPath表达式是XPath的核心,它可以选取XML文档中的元素、属性、文本、命名空间等。例如,"/bookstore/book"选择所有书店里的书元素,而"//title"选择文档中的所有title元素。XPath还支持条件查询,如"//book[price>...