Apache Xerces-J 在 Ubuntu 22.04 集成使用指南

在 Ubuntu 22.04 上以 Maven 与手动 jar 两种方式集成 Apache Xerces-J 2.12.2,完整跑通 SAX 解析、DOM 解析、XML Schema 校验三个最小示例并展示全部实测输出。

Apache XercesApache XercesJavaXMLUbuntuUbuntu 22.042026/10/16

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需要什么下载什么(最小化)
升级成本改版本号重跑重新下载替换
运行 classpathdependency: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() 直接抛出,必须自己计数并事后判断,否则违例文档也会"安静地"解析完。

六、注意事项

  1. 运行时工作目录:parse("books.xml") 按相对路径在工作目录找文件,java 命令必须在与数据文件同一目录执行,否则抛 FileNotFoundException(实测报 /root/xerces-lab/src/books.xml (No such file or directory) 形式的堆栈)。
  2. 源码编码:示例含中文,javac 必须带 -encoding UTF-8,否则中文注释与输出乱码;运行时若终端乱码,加环境变量 LANG=C.UTF-8。
  3. 编译期不要用旧的 setAttribute 写法:javax.xml.parsers.SAXParserFactory 没有 setAttribute 方法(实测 javac 报 cannot find symbol: method setAttribute),schemaLanguage/schemaSource 要设置在 SAXParser 实例的 setProperty 上。
  4. 确认生效的版本:运行时打印 org.apache.xerces.impl.Version.getVersion()(实测输出 Xerces-J 2.12.2)是确认独立版生效的最直接手段;类路径排查用 jar tf 或 mvn dependency:tree。
  5. 不要混装多个解析器 jar:dom4j 等库可能传递带入旧版 xercesImpl,先用 mvn dependency:tree | grep -i xerces 检查冲突。

七、参考链接