Apache Xerces-J 在 Ubuntu 22.04 集成使用指南
在 Ubuntu 22.04 上以 Maven 与手动 jar 两种方式集成 Apache Xerces-J 2.12.2,完整跑通 SAX 解析、DOM 解析、XML Schema 校验三个最小示例并展示全部实测输出。
Apache Xerces-J 在 Ubuntu 22.04 集成使用指南
目标:在 Ubuntu 22.04 上用 Maven 与手动 jar+classpath 两种方式把 Apache Xerces-J 2.12.2 集成进 Java 工程,并完整跑通 SAX 解析、DOM 解析、XML Schema 校验三个最小示例。文中全部命令与回显均在该环境实测通过。
一、环境信息
本文实测环境如下,读者复现时版本可以略有差异,但 JDK 建议 17 及以上。
- 操作系统:Ubuntu 22.04.5 LTS(x86_64,PRETTY_NAME 实测输出
Ubuntu 22.04.5 LTS) - JDK:OpenJDK 17.0.20.1(openjdk-17-jdk,apt 安装)
- 构建工具:Apache Maven 3.6.3(apt 安装)
- Xerces-J:2.12.2(Maven Central 当前最新稳定版)
- 工作目录:/root/xerces-lab
- 网络直连 Maven Central 正常;如下载慢可走代理 http://192.168.1.62:7897
二、前置条件
2.1 安装 JDK 与 Maven
安装 openjdk-17-jdk 与 maven(Ubuntu 22.04 官方仓库自带这两个包):
apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y openjdk-17-jdk maven
验证安装结果:
java -version
实测回显:
openjdk version "17.0.20.1" 2026-08-18
OpenJDK Runtime Environment (build 17.0.20.1+1-1-22.04-Ubuntu)
OpenJDK 64-Bit Server VM (build 17.0.20.1+1-1-22.04-Ubuntu, mixed mode, sharing)
mvn -version | head -3
实测回显:
Apache Maven 3.6.3
Maven home: /usr/share/maven
Java version: 17.0.20.1, vendor: Ubuntu, runtime: /usr/lib/jvm/java-17-openjdk-amd64
2.2 建立工作目录
mkdir -p /root/xerces-lab/{src,classes,libs,logs}
cd /root/xerces-lab
💡 技术拓展:Java 生态里"引入一个库"主流有三种方式——
方式 原理 适用场景 Maven/Gradle 坐标依赖 声明 groupId:artifactId:version,构建工具自动下载 常规工程项目,推荐 手动 jar + classpath 自己下载 jar,编译运行时显式指定 无构建工具的服务器脚本、排查依赖冲突 模块路径(JPMS) JDK 9+ 模块化 --module-path 库带 module-info 时使用;xercesImpl 未提供 module-info,不适用 本文实测 Maven 与手动 jar 两种;xercesImpl 2.12.2 没有 module-info.class,不建议走模块路径。
三、依赖引入
3.1 方式 A:Maven 坐标依赖
先核实坐标与版本(Xerces-J 在 Central 的正式坐标是历史遗留的 xerces:xercesImpl;网上流传的 org.apache.xerces:xercesImpl 在 Central 并不存在):
curl -sI https://repo1.maven.org/maven2/xerces/xercesImpl/2.12.2/xercesImpl-2.12.2.jar | head -1
实测回显:
HTTP/1.1 200 OK
curl -sI https://repo1.maven.org/maven2/org/apache/xerces/xercesImpl/maven-metadata.xml | head -1
实测回显:
HTTP/1.1 404 Not Found
创建 Maven 工程,pom.xml 全文如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.quzuo.demo</groupId>
<artifactId>xerces-j-demo</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
<version>2.12.2</version>
</dependency>
</dependencies>
</project>
逐项说明:groupId 是 xerces(遗留坐标);无需显式声明 xml-apis,它会作为 xercesImpl 的传递依赖自动进来(下一步实测确认);source/target 17 对应本机 JDK。
编译并查看依赖树:
cd /root/xerces-lab/maven-proj
mvn -B compile
mvn -B dependency:tree | grep -E "xerces|xml-apis"
实测回显(dependency:tree 部分):
[INFO] net.quzuo.demo:xerces-j-demo:jar:1.0.0
[INFO] \- xerces:xercesImpl:jar:2.12.2:compile
[INFO] \- xml-apis:xml-apis:jar:1.4.01:compile
xml-apis 1.4.01 确实作为传递依赖被自动引入。它是 JAXP/SAX/DOM 的标准接口包,在 JDK 17 上与 java.xml 模块功能重叠,一般不碍事;如遇类冲突可加 exclusions 排除。
生成运行时 classpath 供后续 java 命令使用:
mvn -B dependency:build-classpath -Dmdep.outputFile=cp.txt -q
cat cp.txt
实测回显:
/root/.m2/repository/xerces/xercesImpl/2.12.2/xercesImpl-2.12.2.jar:/root/.m2/repository/xml-apis/xml-apis/1.4.01/xml-apis-1.4.01.jar
3.2 方式 B:手动 jar + classpath
不使用构建工具时,从 Maven Central 直接下载 jar:
cd /root/xerces-lab/libs
curl -sS -O https://repo1.maven.org/maven2/xerces/xercesImpl/2.12.2/xercesImpl-2.12.2.jar
curl -sS -O https://repo1.maven.org/maven2/xml-apis/xml-apis/1.4.01/xml-apis-1.4.01.jar
curl -sS -O https://repo1.maven.org/maven2/xml-resolver/xml-resolver/1.2/xml-resolver-1.2.jar
sha256sum *.jar
实测回显:
6fc991829af1708d15aea50c66f0beadcd2cfeb6968e0b2f55c1b0909883fe16 xercesImpl-2.12.2.jar
a840968176645684bb01aed376e067ab39614885f9eee44abe35a5f20ebe7fad xml-apis-1.4.01.jar
47dcde8986019314ef78ae7280a94973a21d2ed95075a40a000b42da956429e1 xml-resolver-1.2.jar
编译全部示例(只需 xercesImpl 在编译期 classpath 上):
cd /root/xerces-lab
javac -encoding UTF-8 -cp libs/xercesImpl-2.12.2.jar -d classes src/*.java
实测回显:无任何输出(编译成功)。
3.3 两种方式对比
| 维度 | Maven 方式 | 手动 jar 方式 |
|---|---|---|
| 依赖声明 | pom.xml 一处声明 | 自行下载、自行核对 sha256 |
| 传递依赖 | 自动带出 xml-apis | 需要什么下载什么(最小化) |
| 升级成本 | 改版本号重跑 | 重新下载替换 |
| 运行 classpath | dependency:build-classpath 生成 | 手写 libs/xercesImpl-2.12.2.jar |
| 适用 | 常规工程 | 服务器脚本、最小依赖环境 |
选择建议:有工程结构用 Maven;临时验证或最小化部署用手动 jar(本文两个示例代码完全共用,只是 classpath 来源不同)。
四、核心 API 与实现切换机制
Xerces-J 通过 JAXP(javax.xml.parsers)工厂 API 使用。关键机制:xercesImpl-2.12.2.jar 内部有 META-INF/services 注册文件,把它放上 classpath 后,JAXP 工厂会自动发现并切换到独立版 Xerces 实现。用下面程序观察三种运行形态:
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.SAXParserFactory;
public class SaxWho {
public static void main(String[] args) throws Exception {
SAXParserFactory sf = SAXParserFactory.newInstance();
DocumentBuilderFactory df = DocumentBuilderFactory.newInstance();
System.out.println("SAXParserFactory 实现类: " + sf.getClass().getName());
System.out.println("DocumentBuilderFactory 实现类: " + df.getClass().getName());
}
}
形态一:classpath 不带 xercesImpl(纯 JDK 内置实现):
java -cp classes SaxWho
实测回显:
SAXParserFactory 实现类: com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl
DocumentBuilderFactory 实现类: com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl
形态二:classpath 带上 xercesImpl(自动切换为独立版):
java -cp classes:libs/xercesImpl-2.12.2.jar SaxWho
实测回显:
SAXParserFactory 实现类: org.apache.xerces.jaxp.SAXParserFactoryImpl
DocumentBuilderFactory 实现类: org.apache.xerces.jaxp.DocumentBuilderFactoryImpl
形态三:系统属性强制指定(效果与形态二一致):
java -Djavax.xml.parsers.SAXParserFactory=org.apache.xerces.jaxp.SAXParserFactoryImpl -cp classes:libs/xercesImpl-2.12.2.jar SaxWho
实测回显:
SAXParserFactory 实现类: org.apache.xerces.jaxp.SAXParserFactoryImpl
DocumentBuilderFactory 实现类: org.apache.xerces.jaxp.DocumentBuilderFactoryImpl
验证结论:引入依赖即生效,但工程上建议显式设系统属性或直接 new org.apache.xerces.jaxp.SAXParserFactoryImpl() 把实现"钉死",避免隐式切换。
五、集成示例
5.1 测试数据文件
books.xml(合法文档):
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
<book id="1001">
<title>Java 核心技术</title>
<author>凯·霍斯特曼</author>
<price>149.00</price>
</book>
<book id="1002">
<title>深入理解 Java 虚拟机</title>
<author>周志明</author>
<price>129.00</price>
</book>
<book id="1003">
<title>Effective Java</title>
<author>Joshua Bloch</author>
<price>99.00</price>
</book>
</bookstore>
books.xsd(Schema 定义):
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="bookstore">
<xs:complexType>
<xs:sequence>
<xs:element name="book" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="title" type="xs:string"/>
<xs:element name="author" type="xs:string"/>
<xs:element name="price" type="xs:decimal"/>
</xs:sequence>
<xs:attribute name="id" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
books-invalid.xml(故意违反 Schema:price 不是 decimal、第二本书缺 author):
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
<book id="1001">
<title>Java 核心技术</title>
<author>凯·霍斯特曼</author>
<price>免费</price>
</book>
<book id="1002">
<title>深入理解 Java 虚拟机</title>
<price>129.00</price>
</book>
</bookstore>
5.2 示例一:SAX 流式解析
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.SAXParserFactory;
public class SaxParseDemo {
public static void main(String[] args) throws Exception {
SAXParserFactory factory = SAXParserFactory.newInstance();
System.out.println("SAXParserFactory 实现类: " + factory.getClass().getName());
DefaultHandler handler = new DefaultHandler() {
@Override
public void startElement(String uri, String localName, String qName, Attributes atts) {
System.out.println("开始元素: qName=" + qName + ", localName=" + localName
+ ", uri=" + (uri.isEmpty() ? "(无)" : uri));
}
@Override
public void endElement(String uri, String localName, String qName) {
System.out.println("结束元素: " + qName);
}
@Override
public void characters(char[] ch, int start, int length) {
String text = new String(ch, start, length).trim();
if (!text.isEmpty()) {
System.out.println("文本内容: " + text);
}
}
};
factory.newSAXParser().parse("books.xml", handler);
System.out.println("SAX 解析完成,无错误。");
}
}
Maven 方式运行(工作目录为 maven-proj,books.xml 需在同目录):
cd /root/xerces-lab/maven-proj
java -cp "target/classes:$(cat cp.txt)" SaxParseDemo
实测回显(输出节选,首尾完整保留,中间为重复的 book 事件):
SAXParserFactory 实现类: org.apache.xerces.jaxp.SAXParserFactoryImpl
开始元素: qName=bookstore, localName=, uri=(无)
开始元素: qName=book, localName=, uri=(无)
开始元素: qName=title, localName=, uri=(无)
文本内容: Java 核心技术
结束元素: title
开始元素: qName=author, localName=, uri=(无)
文本内容: 凯·霍斯特曼
结束元素: author
开始元素: qName=price, localName=, uri=(无)
文本内容: 149.00
结束元素: price
结束元素: book
开始元素: qName=book, localName=, uri=(无)
开始元素: qName=title, localName=, uri=(无)
文本内容: 深入理解 Java 虚拟机
结束元素: title
开始元素: qName=author, localName=, uri=(无)
文本内容: 周志明
结束元素: author
开始元素: qName=price, localName=, uri=(无)
文本内容: 129.00
结束元素: price
结束元素: book
开始元素: qName=book, localName=, uri=(无)
开始元素: qName=title, localName=, uri=(无)
文本内容: Effective Java
结束元素: title
开始元素: qName=author, localName=, uri=(无)
文本内容: Joshua Bloch
结束元素: author
开始元素: qName=price, localName=, uri=(无)
文本内容: 99.00
结束元素: price
结束元素: book
结束元素: bookstore
SAX 解析完成,无错误。
手动 jar 方式运行(结果完全一致,仅 classpath 来源不同):
cd /root/xerces-lab/src
java -cp ../classes:../libs/xercesImpl-2.12.2.jar SaxParseDemo | head -3
实测回显:
SAXParserFactory 实现类: org.apache.xerces.jaxp.SAXParserFactoryImpl
开始元素: qName=bookstore, localName=, uri=(无)
开始元素: qName=book, localName=, uri=(无)
5.3 示例二:DOM 树解析
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilderFactory;
public class DomParseDemo {
public static void main(String[] args) throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
System.out.println("DocumentBuilderFactory 实现类: " + dbf.getClass().getName());
Document doc = dbf.newDocumentBuilder().parse("books.xml");
Element root = doc.getDocumentElement();
System.out.println("根元素: " + root.getTagName());
NodeList books = doc.getElementsByTagName("book");
System.out.println("book 元素个数: " + books.getLength());
for (int i = 0; i < books.getLength(); i++) {
Element book = (Element) books.item(i);
System.out.println("第" + (i + 1) + "本: id=" + book.getAttribute("id")
+ ", 《" + book.getElementsByTagName("title").item(0).getTextContent() + "》"
+ ", 作者=" + book.getElementsByTagName("author").item(0).getTextContent()
+ ", 价格=" + book.getElementsByTagName("price").item(0).getTextContent());
}
System.out.println("DOM 解析完成,无错误。");
}
}
Maven 方式运行:
cd /root/xerces-lab/maven-proj
java -cp "target/classes:$(cat cp.txt)" DomParseDemo
实测回显:
DocumentBuilderFactory 实现类: org.apache.xerces.jaxp.DocumentBuilderFactoryImpl
根元素: bookstore
book 元素个数: 3
第1本: id=1001, 《Java 核心技术》, 作者=凯·霍斯特曼, 价格=149.00
第2本: id=1002, 《深入理解 Java 虚拟机》, 作者=周志明, 价格=129.00
第3本: id=1003, 《Effective Java》, 作者=Joshua Bloch, 价格=99.00
DOM 解析完成,无错误。
手动 jar 方式运行:
cd /root/xerces-lab/src
java -cp ../classes:../libs/xercesImpl-2.12.2.jar DomParseDemo
实测回显与 Maven 方式完全一致(首行实现类为 org.apache.xerces.jaxp.DocumentBuilderFactoryImpl,书单三行相同,末行 DOM 解析完成,无错误。)。
5.4 示例三:XML Schema 校验(含错误处理器输出)
走 JAXP 可移植路线:SAXParser.setProperty 设置 schema 语言与 Schema 来源(注意:schemaLanguage/schemaSource 这两个 property 设置在 SAXParser 实例上,不是 SAXParserFactory 上):
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
public class SchemaValidateDemo {
public static void main(String[] args) throws Exception {
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setValidating(true);
factory.setNamespaceAware(true);
SAXParser parser = factory.newSAXParser();
parser.setProperty("http://java.sun.com/xml/jaxp/properties/schemaLanguage",
"http://www.w3.org/2001/XMLSchema");
parser.setProperty("http://java.sun.com/xml/jaxp/properties/schemaSource", "books.xsd");
final int[] errors = {0};
DefaultHandler handler = new DefaultHandler() {
@Override
public void warning(SAXParseException e) {
System.out.println("[警告] 第" + e.getLineNumber() + "行: " + e.getMessage());
}
@Override
public void error(SAXParseException e) {
errors[0]++;
System.out.println("[错误] 第" + e.getLineNumber() + "行: " + e.getMessage());
}
@Override
public void fatalError(SAXParseException e) {
errors[0]++;
System.out.println("[致命错误] 第" + e.getLineNumber() + "行: " + e.getMessage());
}
};
parser.parse(args[0], handler);
if (errors[0] > 0) {
System.out.println("校验结果: 未通过,共发现 " + errors[0] + " 处校验错误。");
} else {
System.out.println("校验结果: 通过," + args[0] + " 符合 books.xsd 定义的规则。");
}
}
}
对合法文档校验:
cd /root/xerces-lab/maven-proj
java -cp "target/classes:$(cat cp.txt)" SchemaValidateDemo books.xml
实测回显:
校验结果: 通过,books.xml 符合 books.xsd 定义的规则。
对故意违例的文档校验(错误处理器输出是重点观察对象):
java -cp "target/classes:$(cat cp.txt)" SchemaValidateDemo books-invalid.xml
实测回显:
[错误] 第6行: cvc-datatype-valid.1.2.1: '免费' is not a valid value for 'decimal'.
[错误] 第6行: cvc-type.3.1.3: The value '免费' of element 'price' is not valid.
[错误] 第10行: cvc-complex-type.2.4.a: Invalid content was found starting with element 'price'. One of '{author}' is expected.
校验结果: 未通过,共发现 3 处校验错误。
三条报错分别对应:数据类型不合法(cvc-datatype-valid)、元素值与类型不符(cvc-type.3.1.3)、内容模型出现未预期的元素(cvc-complex-type.2.4.a)。手动 jar 方式运行:
cd /root/xerces-lab/src
java -cp ../classes:../libs/xercesImpl-2.12.2.jar SchemaValidateDemo books-invalid.xml
实测回显与 Maven 方式逐行一致(同样三行 cvc-* 错误,末行 校验结果: 未通过,共发现 3 处校验错误。)。
💡 说明:错误处理器里 error() 回调收到的是"可恢复校验错误"(解析继续),fatalError() 收到的是良构性致命错误(解析中止)。上面的输出说明 error() 不会被 parse() 直接抛出,必须自己计数并事后判断,否则违例文档也会"安静地"解析完。
六、注意事项
- 运行时工作目录:
parse("books.xml")按相对路径在工作目录找文件,java 命令必须在与数据文件同一目录执行,否则抛 FileNotFoundException(实测报/root/xerces-lab/src/books.xml (No such file or directory)形式的堆栈)。 - 源码编码:示例含中文,javac 必须带
-encoding UTF-8,否则中文注释与输出乱码;运行时若终端乱码,加环境变量 LANG=C.UTF-8。 - 编译期不要用旧的 setAttribute 写法:
javax.xml.parsers.SAXParserFactory没有setAttribute方法(实测 javac 报cannot find symbol: method setAttribute),schemaLanguage/schemaSource 要设置在SAXParser实例的setProperty上。 - 确认生效的版本:运行时打印
org.apache.xerces.impl.Version.getVersion()(实测输出Xerces-J 2.12.2)是确认独立版生效的最直接手段;类路径排查用jar tf或mvn dependency:tree。 - 不要混装多个解析器 jar:dom4j 等库可能传递带入旧版 xercesImpl,先用
mvn dependency:tree | grep -i xerces检查冲突。
七、参考链接
- Maven Central 坐标:https://repo1.maven.org/maven2/xerces/xercesImpl/
- JAXP 文档:https://docs.oracle.com/javase/tutorial/jaxp/index.html
- Xerces-J 官方页:https://xerces.apache.org/xerces2-j/
- 本文配套篇目:《Apache Xerces-J 介绍与选型指南》《Apache Xerces-J 2.12.2 配置参数详解》《Apache Xerces-J 2.12.2 命令行工具详解》