一、JAXP对XPath的支持
XPath是从JAXP1.3开始被支持的,在这些API中,其核心接口有XPath和XPathExpression,它们都在javax.xml.xpath包中,分别表示XPath对象以及被预编译后的表达式对象。
例如,对于如下的示例文件(schema_test_copy.xml)
<?xml version="1.0" encoding="UTF-8"?> <templet> <bean name="user" class="com.daniele.appdemo.test.domain.User"></bean> <bean name="company" class="com.daniele.appdemo.test.domain.Company"></bean> <description date="2013-06-07">This is a XML Schema example.</description> </templet>
我们可以利用上述两个和XPath相关的接口进行一系列的节点访问操作。
public static void main(String[] args) throws XPathExpressionException, IOException { // 返回默认的DOM对象模型的XPath工厂 XPathFactory factory = XPathFactory.newInstance(); XPath xPath = factory.newXPath(); // 获取templet下name属性值为user的bean元素的class属性 String expression = "/templet/bean[@name='user']/@class"; InputSource source = new InputSource(); source.setByteStream(new FileInputStream(new File("resource/xml/schema/schema_test_copy.xml"))); // 预编译表达式,这种方式对于对同一个表达式多次求值来说,可提高运行效率,并且是线程安全的 XPathExpression compiledExpression = xPath.compile(expression); System.out.println(compiledExpression.evaluate(source)); }
上述代码执行后将会正确的得到schema_test_copy.xml文件中第一个bean元素的class的属性值。
现在,把xml文档的内容做如下修改(schema_test.xml):
<templet xmlns="http://www.appdemo.daniele.com/schema/templet" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.appdemo.daniele.com/schema/templet ../../../schema/schame_test.xsd"> <bean name="user" class="com.daniele.appdemo.test.domain.User"></bean> <bean name="company" class="com.daniele.appdemo.test.domain.Company"></bean> <description date="2013-06-07">This is a XML Schema example.</description> </templet>
注意对比文件中根节点的变化,它在schema_test_copy.xml的基础上增加了两个与命名空间相关的属性xmlns和xmlns:xsi,其中xmlns是我们自定义文档结构规范的命名空间,是使用XPath时需要考虑的重点。
此时我们再次运行上面的main()方法后会发现将得不到任何输出结果。这主要是由于XPath不支持默认命名空间造成的,因此我们需要将前缀和相关的URI关联起来。
为达到这个目的,JAVA为我们提供了一个javax.xml.namespace.NamespaceContext接口,它的目的在于让我们可以在XPath接口对象中设置这个context,当XPath进行编译时,会根据前缀和URI的映射关系来匹配实际的节点。
NamespaceContext接口定义有如下三个方法:
1)public String getNamespaceURI(String prefix):获取绑定到当前范围中前缀的命名空间 URI;
2)public String getPrefix(String namespaceURI):获取绑定到当前范围命名空间 URI的前缀;
3)public Iterato getPrefixes(String namespaceURI):获取绑定到当前范围命名空间 URI的所有前缀。
下面来自定义实现一个NamespaceContext:
package com.daniele.appdemo.xml.jaxp.xpath; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import javax.xml.XMLConstants; import javax.xml.namespace.NamespaceContext; /** * <p>默认的命名空间处理器</p> * @author <a href="mailto:code727@gmail.com">Daniele</a> * @version 1.0.0, 2013-7-11 * @see * @since AppDemo1.0.0 */ public class DefaultNamespaceContext implements NamespaceContext { /** 维持前缀与命名空间URI的映射关系(K:前缀,V:命名空间URI) */ private Map<String,String> namespaceMap = new HashMap<String,String>(); public DefaultNamespaceContext() { addNamespace("xsi", XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI); } public void addNamespace(String prefix, String namespaceURI) { namespaceMap.put(prefix, namespaceURI); } /** * <p>获取绑定到当前范围中前缀的命名空间 URI。</p> * @author <a href="mailto:code727@gmail.com">Daniele</a> * @param prefix * @return * @since AppDemo1.0.0 */ public String getNamespaceURI(String prefix) { String namespaceURI = namespaceMap.get(prefix); return namespaceURI != null ? namespaceURI : XMLConstants.NULL_NS_URI; } /** * <p>获取绑定到当前范围命名空间 URI的前缀。</p> * @author <a href="mailto:code727@gmail.com">Daniele</a> * @param namespaceURI * @return * @since AppDemo1.0.0 */ public String getPrefix(String namespaceURI) { Set<String> keySet = namespaceMap.keySet(); for (String key : keySet) { if (namespaceMap.get(key).equals(namespaceURI)) return key; } return XMLConstants.DEFAULT_NS_PREFIX; } /** * <p>获取绑定到当前范围命名空间 URI的所有前缀。</p> * @author <a href="mailto:code727@gmail.com">Daniele</a> * @param namespaceURI * @return * @since AppDemo1.0.0 */ public Iterator<?> getPrefixes(String namespaceURI) { List<String> prefixes = new ArrayList<String>(); Set<String> keySet = namespaceMap.keySet(); for (String key : keySet) { if (namespaceMap.get(key).equals(namespaceURI)) prefixes.add(key); } return prefixes.iterator(); } }
完成后利用这个实现类,将前面的main()方法做如下修改(注意第5,11,12,15行的代码):
public static void main(String[] args) throws IOException, XPathExpressionException { XPathFactory factory = XPathFactory.newInstance(); XPath xPath = factory.newXPath(); DefaultNamespaceContext defaultNamespaceContext = new DefaultNamespaceContext(); /* * 设置前缀与命名空间URI的映射关系 * XPath表达式中使用的前缀与文档中实际的前缀并没有关联,这里只是为了在XPath中增加一个前缀标识所有。 * 但前缀对应的命名空间URI必须与文档中实际的命名空间URI保持一致 */ defaultNamespaceContext.addNamespace("t", "http://www.appdemo.daniele.com/schema/templet"); xPath.setNamespaceContext(defaultNamespaceContext); InputSource source = new InputSource(new FileInputStream(new File("resource/xml/schema/schema_test.xml"))); String expression = "/t:templet/t:bean[@name='user']/@class"; XPathExpression compiledExpression = xPath.compile(expression); System.out.println(compiledExpression.evaluate(source)); }
再次运行main()方法将重新得到前面相同的运行结果。
二、Dom4j对XPath的支持
除了JAXP外,JDOM和Dom4j也提供了对XPath的支持。其中在Dom4j中,与JAXP的XPathExpression作用类似的接口为org.dom4j.XPath,这是在dom4j-1.x.x.jar中,因此在实际的使用过程中需将此包加入到classpath中。
例如,利用XPath获取上述schema_test_copy.xml中无命名空间的节点:
package com.daniele.appdemo.xml.dom4j; import java.io.File; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.DocumentFactory; import org.dom4j.DocumentHelper; import org.dom4j.Element; import org.dom4j.Node; import org.dom4j.XPath; import org.dom4j.io.SAXReader; /** * <p>Dom4j对XPath支持的测试类</p> * @author <a href="mailto:code727@gmail.com">Daniele</a> * @version 1.0.0, 2013-7-18 * @see * @since AppDemo1.0.0 */ public class Dom4jXPathTester { public static void main(String[] args) throws DocumentException { DocumentFactory factory = DocumentFactory.getInstance(); SAXReader reader = new SAXReader(factory); // 解析无命名空间的XML文档 Document document = reader.read(new File("./resource/xml/schema/schema_test_copy.xml")); Element root = document.getRootElement(); String subNodeName = "bean"; /* * 从工厂(DocumentFactory)对象或DocumentHelper中创建XPath对象, * 与JAXP的XPathExpression类似,XPath的作用在于避免对同一个XPath求值时进行重复编译的问题 */ XPath rootSubNodePath = factory.createXPath(root.getPath() + "/" + subNodeName); System.out.println("根元素" + root.getName() + "内共有" + rootSubNodePath.selectNodes(document).size() + "个" + subNodeName + "子节点"); subNodeName = "description"; rootSubNodePath = DocumentHelper.createXPath(root.getPath() + "/" + subNodeName); Node subNode = rootSubNodePath.selectSingleNode(document); System.out.println("根元素" + root.getName() + "子节点" + subNode.getName() + "的文本值为:" + subNode.getText()); } }运行后将得到如下结果:
根元素templet内共有2个bean子节点
根元素templet子节点description的文本值为:This is a XML Schema example.
与JAXP的情况一样,如果上述代码用来获取schema_test.xml文件中带命名空间的节点,则同样不会得到预期结果。为此,Dom4j也提供了与JAXP类似的解决方案,实际使用情况甚至比JAXP的还要方便和灵活。
Dom4j也提供了一个名为NamespaceContext的接口(与javax.xml.namespace.NamespaceContext没有任何关系),它可以根据某一个前缀来得到对应的URI,它在Dom4j的依赖包jaxen-1.x.jar中,因此在使用时需要将此包导入到classpath中。
jaxen提供了一个NamespaceContext的默认实现类SimpleNamespaceContext。在这个实现类中有一个叫namespaces的Map成员属性,它是用来维护命名空间前缀和URI的映射关系的。当进行XPath求值时,首先会将表达式中的前缀标识提取出来,再根据这个标识,到它所关联的NamespaceContext的映射中获取对应的URI,最终再调用NamespaceContext接口方法translateNamespacePrefixToUri(String prefix)返回URI。
对于SimpleNamespaceContext来说,它有如下两种方式来添加命名空间关系:
1)利用构造函数来添加——public SimpleNamespaceContext(Map namespaces), 这种方式传入一个已经在外部设置好了的映射关系即可;
2)调用SimpleNamespaceContext实例的addNamespace(String prefix, String URI)方法来逐条添加。
完成后,再调用XPath实例的setNamespaceContext(namespaceContext)方法,将Context实例传入即可。例如:
package com.daniele.appdemo.xml.dom4j; import java.io.File; import java.util.HashMap; import java.util.Map; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.DocumentFactory; import org.dom4j.DocumentHelper; import org.dom4j.XPath; import org.dom4j.io.SAXReader; import org.jaxen.NamespaceContext; import org.jaxen.SimpleNamespaceContext; /** * <p>Dom4j利用XPath获取具有命名空间的节点的测试类</p> * @author <a href="mailto:code727@gmail.com">Daniele</a> * @version 1.0.0, 2013-7-18 * @see * @since AppDemo1.0.0 */ public class Dom4jNamespaceXPathTester { public static void main(String[] args) throws DocumentException { DocumentFactory factory = DocumentFactory.getInstance(); SAXReader reader = new SAXReader(factory); // 解析具有命名空间的XML文档 Document document = reader.read(new File("./resource/xml/schema/schema_test.xml")); // 命名空间前缀与URI的映射 Map<String, String> namespaceMap = new HashMap<String, String>(); namespaceMap.put("t", "http://www.appdemo.daniele.com/schema/templet"); XPath xPath = null; // 根据映射关系构建NamespaceContext实例 NamespaceContext namespaceContext = new SimpleNamespaceContext(namespaceMap); xPath = DocumentHelper.createXPath("/t:templet/t:bean"); xPath.setNamespaceContext(namespaceContext); System.out.println(xPath.selectNodes(document).size()); xPath = factory.createXPath("/t:templet/t:bean"); xPath.setNamespaceContext(namespaceContext); System.out.println(xPath.selectNodes(document).size()); } }
对于XPath的实例来说,可以完成摒弃对NamespaceContext实例的创建和设置,可以调用它的另外一个方法setNamespaceURIs(Map map),直接将映射关系传递给这个方法就可以了。例如:
// 命名空间前缀与URI的映射 Map<String, String> namespaceMap = new HashMap<String, String>(); namespaceMap.put("t", "http://www.appdemo.daniele.com/schema/templet"); XPath xPath = null; xPath = DocumentHelper.createXPath("/t:templet/t:bean"); xPath.setNamespaceURIs(namespaceMap); System.out.println(xPath.selectNodes(document).size()); xPath = factory.createXPath("/t:templet/t:bean"); xPath.setNamespaceURIs(namespaceMap); System.out.println(xPath.selectNodes(document).size());在setNamespaceURIs()方法的内部,它会创建一个SimpleNamespaceContext来维护映射关系(具体实现在org.dom4j.xpath.DefaultXPath中)。
对于上述两种方式,都有一个比较明显的缺陷,即对于每一个新建的XPath实例来说,在求值之前都需要设置一次context或映射关系,这是比较繁琐的。
为此,Dom4j也提供了一个类似JAXP那样的全局设置方式,即调用DocumentFactory实例的setXPathNamespaceURIs(Map map)方法。
public static void main(String[] args) throws DocumentException { DocumentFactory factory = DocumentFactory.getInstance(); SAXReader reader = new SAXReader(factory); // 解析具有命名空间的XML文档 Document document = reader.read(new File("./resource/xml/schema/schema_test.xml")); // 命名空间前缀与URI的映射 Map<String, String> namespaceMap = new HashMap<String, String>(); namespaceMap.put("t", "http://www.appdemo.daniele.com/schema/templet"); // 设置全局的映射关系 factory.setXPathNamespaceURIs(namespaceMap); XPath xPath = null; xPath = DocumentHelper.createXPath("/t:templet/t:bean"); System.out.println(xPath.selectNodes(document).size()); xPath = factory.createXPath("/t:templet/t:bean"); System.out.println(xPath.selectNodes(document).size()); }
回顾第一部分中最后一块代码片段,JAXP的javax.xml.xpath.XPath并不代表某一个具体的表达式对象(Dom4j中的XPath更像是javax.xml.xpath.XPathExpression),因此在javax.xml.xpath.XPath实例中设置的NamespaceContext就表示为全局的,即后续所有与编译好了的XPathExpression(对于Dom4j来说是XPath)都使用同一个全局的NamespaceContext来将表达式中的前缀标识替换成对应的URI。
相关推荐
要使用DOM4J的XPath功能,首先需要引入DOM4J库和JAXP(Java API for XML Processing),后者提供了XPath的实现。在Java项目中,这通常通过添加对应的依赖完成。例如,在Maven项目中,可以在pom.xml文件中加入以下...
**DOM4J**是一款由dom4j.org开发的开源XML解析库,专为Java平台设计,它不仅支持DOM和SAX这两种标准的XML解析方式,还兼容JAXP(Java API for XML Processing)。DOM4J以其简洁易用而闻名,只要对基本的XML-DOM模型...
在实际应用中,"dom4j和xpath必备jar包"通常包含了DOM4J库和XPath相关的类库,如Java的JAXP(Java API for XML Processing)的一部分,这些库文件是Java项目中解析和操作XML所必需的依赖。将这些jar包添加到项目的类...
通过以上步骤,你可以利用Dom4J和XPath进行XML文档的深度操作。这个教程提供的源代码和示例将帮助你更好地理解和实践这些概念,进一步提升你的XML处理能力。在实际开发中,根据具体需求调整XPath表达式,可以实现更...
DOM4J 是一个开源的 XML 解析包,由 dom4j.org 出品,应用于 Java 平台,采用了 Java 集合框架并完全支持 DOM、SAX 和 JAXP。DOM4J 的主要特点是使用大量的接口,所有主要接口都在 org.dom4j 里面定义。 一、DOM4J ...
DOM4J的XPath支持允许开发者通过简洁的表达式来查找、过滤和导航XML文档的结构,大大简化了数据提取和操作的过程。 此外,DOM4J还整合了Java集合框架,这意味着它可以无缝地与ArrayLists、Maps等Java内置数据结构...
4. **XPath和XSLT转换**:DOM4J不仅能够执行XPath查询,还可以结合JAXP(Java API for XML Processing)进行XSLT转换,将XML文档转换成其他格式。 5. **DOM4J与JDOM兼容**:DOM4J设计时考虑到了与流行的JDOM库的...
- 和JDOM相比,DOM4J支持更广泛的XML特性,如命名空间和XPath。 - StAX和SAX提供更高效的流式处理,但在复杂操作上不如DOM4J方便。 综上所述,DOM4J是一个强大而全面的XML处理库,提供的包和依赖包确保了其在各种...
Dom4j是Java平台中一个功能强大的开源XML解析库,它支持DOM、SAX和JAXP标准。由于其易于使用的接口和Java集合框架的集成,Dom4j广泛应用于需要处理XML文档的Java应用程序中。首先,Dom4j的应用非常简单,只需要对XML...
### Dom4j与XPath解析XML #### 一、Dom4j概述 Dom4j是一个功能强大的XML解析库,由dom4j.org开发并维护。它主要用于Java平台上的XML解析任务,支持DOM、SAX和JAXP等多种XML解析标准。Dom4j的主要特点在于其丰富的...
**DOM4J使用简介** DOM4J 是一个强大的 Java 库,专为处理 XML 文档而设计。...通过其简洁的 API 和对 DOM、SAX、XPath 和 XSLT 的全面支持,DOM4J 提供了高效、易用的解决方案,是 Java 开发者处理 XML 的得力助手。
作为开源软件,DOM4J在SourceForge上可以获取,并且它完全支持DOM、SAX和JAXP,这使得它在Java平台上处理XML时具有高度的灵活性和兼容性。 DOM4J的主要优势在于它的易用性和高性能。与其他Java XML API相比,如JDOM...
- **高性能**:DOM4J通过优化内存使用和提高解析速度,使其在处理大型XML文档时表现优秀。 - **强大的API**:丰富的类库提供了创建、修改、遍历和查询XML文档的各种功能。 - **XPath支持**:内建的XPath引擎允许...
在Java中,XPath通常与JAXP(Java API for XML Processing)一起使用,但DOM4J还支持使用XPath,无需引入额外的库。这是因为DOM4J包含了对XPath的内置支持,这使得开发者可以方便地在DOM4J解析的XML文档上应用XPath...
DOM4J是一个由dom4j.org开发的开源XML解析包,专为Java平台设计,它不仅支持DOM、SAX和JAXP标准,还巧妙地融入了Java集合框架,使其成为Java开发者在处理XML数据时的强大工具。DOM4J的最大亮点在于其简洁易用的API...
3. **XPath支持**:DOM4J提供了对XPath表达式的强大支持,可以方便地定位XML文档中的任何节点。 4. **轻量级**:DOM4J的设计目标是轻便且高效,它不依赖于大型的JDK库,如JAXP,使得其可以在各种环境下游刃有余。 *...
3. **XPath支持**:DOM4J内置了XPath支持,这使得通过表达式快速定位XML文档中的特定节点成为可能。XPath是XML中强大的查询语言,可以方便地查找、选择和修改XML数据。 4. **XML Schema和DTD处理**:DOM4J可以验证...
4. **XPath支持**:DOM4J内置了XPath支持,而DOM需要额外的库如JAXP来实现XPath查询。 **使用场景** DOM4J适合于需要频繁操作XML结构,或者需要高性能和低内存占用的场景。而DOM更适合那些对内存使用不太敏感,但...