在Java开发中,处理XML格式的数据是常见需求,当XML结构复杂、层级较深时,手动遍历节点树的方式不仅代码冗余,还容易出错。XPath是一门在XML文档中查找信息的语言,通过简洁的路径表达式就能快速定位到目标节点,大幅提升XML解析效率。Java标准库已经内置了对XPath的支持,不需要额外引入第三方依赖就能完成相关操作。

准备工作
Java中使用XPath需要依赖两个核心类:DocumentBuilder用于解析XML文件得到文档对象,XPath用于执行XPath表达式。这两个类都来自Java标准库,不需要额外添加依赖,只需要导入对应的包即可。
需要导入的包如下:
import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathFactory; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import java.io.File;
基础使用步骤
Java中使用XPath查询XML节点的整体流程分为四步:解析XML得到文档对象、创建XPath对象、编写XPath表达式、执行表达式获取结果。下面通过一个示例XML文件演示完整流程。
示例XML文件内容如下,保存为test.xml:
<?xml version="1.0" encoding="UTF-8"?>
<students>
<student id="1">
<name>张三</name>
<age>20</age>
<course>Java</course>
</student>
<student id="2">
<name>李四</name>
<age>22</age>
<course>Python</course>
</student>
<student id="3">
<name>王五</name>
<age>20</age>
<course>Java</course>
</student>
</students>
第一步:解析XML得到Document对象
使用DocumentBuilderFactory和DocumentBuilder解析XML文件,得到代表整个XML文档的Document对象,后续XPath查询都是基于这个对象进行。
public static Document parseXml(String filePath) throws Exception {
// 创建文档构建器工厂
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// 创建文档构建器
DocumentBuilder builder = factory.newDocumentBuilder();
// 解析XML文件得到Document对象
return builder.parse(new File(filePath));
}
第二步:创建XPath对象
通过XPathFactory创建XPath实例,这个实例用于执行后续的XPath表达式。
public static XPath createXPath() {
// 创建XPath工厂
XPathFactory xPathFactory = XPathFactory.newInstance();
// 创建XPath对象
return xPathFactory.newXPath();
}
第三步:编写XPath表达式并执行查询
XPath表达式的语法和文件路径类似,/代表根节点,//代表任意位置的节点,@用于获取属性。下面演示几个常见的查询场景。
场景1:查询所有学生节点的姓名
表达式//student/name表示查找所有student节点下的name子节点。
public static void queryAllStudentNames(Document document, XPath xPath) throws Exception {
// 编写XPath表达式
String expression = "//student/name";
// 执行表达式,返回节点列表
NodeList nodeList = (NodeList) xPath.evaluate(expression, document, XPathConstants.NODESET);
// 遍历结果
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
System.out.println("学生姓名:" + node.getTextContent());
}
}
场景2:查询id为2的学生信息
表达式//student[@id='2']表示查找所有带有id属性且值为2的student节点,[@属性名='值']是属性筛选的语法。
public static void queryStudentById(Document document, XPath xPath) throws Exception {
String expression = "//student[@id='2']";
Node node = (Node) xPath.evaluate(expression, document, XPathConstants.NODE);
if (node != null) {
// 获取子节点内容
String name = xPath.evaluate("name", node);
String age = xPath.evaluate("age", node);
String course = xPath.evaluate("course", node);
System.out.println("id为2的学生信息:姓名=" + name + ",年龄=" + age + ",课程=" + course);
}
}
场景3:查询年龄为20岁的学生姓名
表达式//student[age='20']/name表示先筛选age子节点值为20的student节点,再获取这些节点的name子节点。
public static void queryStudentByAge(Document document, XPath xPath) throws Exception {
String expression = "//student[age='20']/name";
NodeList nodeList = (NodeList) xPath.evaluate(expression, document, XPathConstants.NODESET);
System.out.println("年龄为20岁的学生姓名:");
for (int i = 0; i < nodeList.getLength(); i++) {
System.out.println(nodeList.item(i).getTextContent());
}
}
常见问题处理
处理XML命名空间
如果XML文档带有命名空间,直接写节点名会查询不到结果,需要在XPath中设置命名空间上下文。示例如下:
import javax.xml.namespace.NamespaceContext;
import java.util.Iterator;
// 自定义命名空间上下文
class CustomNamespaceContext implements NamespaceContext {
@Override
public String getNamespaceURI(String prefix) {
if ("ns".equals(prefix)) {
return "http://www.example.org/student";
}
return null;
}
@Override
public String getPrefix(String namespaceURI) {
return null;
}
@Override
public Iterator<String> getPrefixes(String namespaceURI) {
return null;
}
}
// 使用命名空间查询
public static void queryWithNamespace(Document document, XPath xPath) throws Exception {
// 设置命名空间上下文
xPath.setNamespaceContext(new CustomNamespaceContext());
// 表达式中使用命名空间前缀
String expression = "//ns:student/ns:name";
NodeList nodeList = (NodeList) xPath.evaluate(expression, document, XPathConstants.NODESET);
for (int i = 0; i < nodeList.getLength(); i++) {
System.out.println(nodeList.item(i).getTextContent());
}
}
处理表达式中的特殊字符
如果节点名或者属性值包含空格、连字符等特殊字符,需要用单引号包裹,或者使用concat函数处理,避免表达式语法错误。
完整测试代码
将上述方法整合,编写主函数测试所有查询场景:
public static void main(String[] args) {
try {
// 解析XML
Document document = parseXml("test.xml");
// 创建XPath对象
XPath xPath = createXPath();
// 测试查询所有学生姓名
System.out.println("所有学生姓名:");
queryAllStudentNames(document, xPath);
// 测试查询id为2的学生
queryStudentById(document, xPath);
// 测试查询年龄为20的学生
queryStudentByAge(document, xPath);
} catch (Exception e) {
e.printStackTrace();
}
}
运行上述代码就能看到对应的查询结果,开发者可以根据实际的XML结构和查询需求调整XPath表达式,实现更灵活的节点查询。