杰瑞科技汇

Java XPath如何高效解析XML?

Java 标准库自 JDK 5.0 开始就内置了对 XPath 的支持,主要位于 javax.xml.xpath 包中。

Java XPath如何高效解析XML?-图1
(图片来源网络,侵删)

准备工作:XML 文件

我们先准备一个示例 XML 文件 books.xml,后续所有的示例都将基于这个文件。

books.xml

<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
    <book category="FICTION">
        <title lang="en">Great Gatsby</title>
        <author>F. Scott Fitzgerald</author>
        <year>1925</year>
        <price>10.99</price>
    </book>
    <book category="CHILDREN">
        <title lang="en">Harry Potter</title>
        <author>J.K. Rowling</author>
        <year>2005</year>
        <price>29.99</price>
    </book>
    <book category="WEB">
        <title lang="en">Learning XML</title>
        <author>Erik T. Ray</author>
        <year>2003</year>
        <price>39.95</price>
    </book>
</bookstore>

核心步骤

使用 Java XPath 解析 XML 通常遵循以下四个核心步骤:

  1. 获取 XPathFactory 实例:这是所有 XPath 操作的入口点。
  2. 创建 XPath 对象:通过工厂对象创建一个 XPath 实例。
  3. 编译 XPath 表达式:将你的 XPath 查询字符串编译成一个 XPathExpression 对象,这样可以提高重复查询的效率。
  4. 评估表达式并获取结果:使用 XPathExpression 对象去解析 XML 文档(如 DocumentInputSource 等)并获取结果。

代码实现示例

下面我们通过一个完整的 Java 类来演示如何执行各种 XPath 查询。

Java XPath如何高效解析XML?-图2
(图片来源网络,侵删)

XPathParser.java

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.*;
import org.w3c.dom.*;
import org.xml.sax.InputSource;
import java.io.File;
public class XPathParser {
    public static void main(String[] args) {
        try {
            // 1. 创建 XPathFactory 和 XPath 对象
            XPathFactory xPathFactory = XPathFactory.newInstance();
            XPath xpath = xPathFactory.newXPath();
            // 2. 加载 XML 文档
            // DocumentBuilderFactory 用于将 XML 文件解析为 DOM 对象
            DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
            Document doc = dBuilder.parse(new File("books.xml"));
            // 设置为不忽略空白节点,这对于精确匹配很重要
            doc.getDocumentElement().normalize();
            System.out.println("--- 示例 1: 获取所有书名 ---");
            // 3. 编译 XPath 表达式
            XPathExpression expr = xpath.compile("//book/title/text()");
            // 4. 评估表达式,获取结果
            NodeList nodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
            for (int i = 0; i < nodes.getLength(); i++) {
                System.out.println("书名: " + nodes.item(i).getNodeValue());
            }
            System.out.println("\n--- 示例 2: 获取第一本书的作者 ---");
            expr = xpath.compile("/bookstore/book[1]/author/text()");
            String author = (String) expr.evaluate(doc, XPathConstants.STRING);
            System.out.println("作者: " + author);
            System.out.println("\n--- 示例 3: 获取类别为 'WEB' 的书的价格 ---");
            expr = xpath.compile("//book[@category='WEB']/price/text()");
            String price = (String) expr.evaluate(doc, XPathConstants.STRING);
            System.out.println("价格: " + price);
            System.out.println("\n--- 示例 4: 获取所有书的属性(节点本身) ---");
            expr = xpath.compile("//book");
            NodeList bookNodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
            for (int i = 0; i < bookNodes.getLength(); i++) {
                Node bookNode = bookNodes.item(i);
                // 获取节点的属性
                NamedNodeMap attributes = bookNode.getAttributes();
                Node categoryAttr = attributes.getNamedItem("category");
                System.out.println("书的类别: " + categoryAttr.getNodeValue());
                // 遍历子节点获取更多信息
                NodeList childNodes = bookNode.getChildNodes();
                for (int j = 0; j < childNodes.getLength(); j++) {
                    Node child = childNodes.item(j);
                    if (child.getNodeType() == Node.ELEMENT_NODE) {
                        System.out.println("  " + child.getNodeName() + ": " + child.getTextContent());
                    }
                }
                System.out.println("----------------------");
            }
            System.out.println("\n--- 示例 5: 使用函数计算书的总数 ---");
            expr = xpath.compile("count(//book)");
            double bookCount = (Double) expr.evaluate(doc, XPathConstants.NUMBER);
            System.out.println("书的总数: " + (int) bookCount);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

代码解析

  1. 加载 XML:我们使用 javax.xml.parsers.DocumentBuilderbooks.xml 文件解析成一个 org.w3c.dom.Document 对象。Document 对象是内存中 XML 文档的树形表示,是 XPath 操作的基础。
  2. xpath.compile():将字符串形式的 XPath 查询(如 "//book/title/text()")编译成一个 XPathExpression 对象,编译后的表达式可以被多次重用,效率更高。
  3. expr.evaluate():这是执行查询的核心方法。
    • 第一个参数 doc 是要查询的 XML 源(在这里是 Document 对象)。
    • 第二个参数 XPathConstants 指定了你期望的返回类型,常用的类型有:
      • XPathConstants.NODESET:返回一个 NodeList,通常用于匹配多个节点(如 //book)。
      • XPathConstants.NODE:返回一个单独的 Node
      • XPathConstants.STRING:返回匹配节点的字符串值(通常是文本内容)。
      • XPathConstants.NUMBER:返回数字(如使用 count() 函数)。
      • XPathConstants.BOOLEAN:返回布尔值。
  4. 处理结果
    • 如果返回类型是 NODESET,我们会得到一个 NodeList,然后遍历它来访问每个节点。
    • 如果返回类型是 STRING,我们直接强制类型转换即可。
    • 对于单个节点,我们可以调用 getNodeValue() 获取其值,或者 getTextContent() 获取其包含的所有文本内容(包括子节点的文本)。getAttributes() 用于获取节点的属性。

常用 XPath 表达式速查表

XPath 表达式 描述 示例 (针对 books.xml)
nodename 选取此节点的所有直接子节点 /bookstore 选取根节点的子节点 bookstore
从根节点选取 /bookstore 选取根元素 bookstore
从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置 //book 选取所有 book 元素,无论它们在文档中的哪个位置
选取当前节点 ./book 选取当前节点的直接子节点 book
选取当前节点的父节点 ../price 选取当前节点的父节点的 price 节点
选取属性 //book[@category] 选取所有拥有 category 属性的 book 元素
text() 选取文本节点 //title/text() 选取所有 title 元素的文本内容
匹配任何元素节点 bookstore/* 选取 bookstore 的所有子元素
匹配任何属性节点 //book/@* 选取所有 book 元素的所有属性
[] 用于谓词,用来查找某个特定的节点或包含某个值的节点 /bookstore/book[1] 选取第一个 book 元素
//book[@category='WEB'] 选取所有 category 属性为 WEBbook 元素
选择多个路径 //book/title | //book/author 选取所有的 titleauthor 元素
count() 计算节点数量 count(//book) 计算所有 book 元素的数量

使用 InputSource 直接解析字符串或URL

除了从文件加载,你还可以直接从字符串或 URL 加载 XML。

import javax.xml.xpath.*;
import org.xml.sax.InputSource;
// ... 其他代码 ...
// 从字符串加载 XML
String xmlString = "<root><item>hello</item></root>";
InputSource sourceFromString = new InputSource(new StringReader(xmlString));
// 使用 xpath.evaluate(sourceFromString, ...) 进行查询
// 从 URL 加载 XML
String urlString = "http://example.com/data.xml";
InputSource sourceFromUrl = new InputSource(urlString);
// 使用 xpath.evaluate(sourceFromUrl, ...) 进行查询

Java 内置的 XPath 功能非常强大且易于使用,尤其适合处理已经加载到内存中的 DOM 文档,对于更复杂的 XML 处理需求,你还可以结合其他 XML API,如 SAX(用于流式解析,节省内存)或 StAX(也用于流式解析,性能更高)。

希望这份详细的指南能帮助你掌握 Java 中使用 XPath 解析 XML 的技能!

Java XPath如何高效解析XML?-图3
(图片来源网络,侵删)
分享:
扫描分享到社交APP
上一篇
下一篇