Apache XMLBeans 5.4.1 快速入门与集成示例
本文以 Apache XMLBeans 5.4.1 为对象,在 Ubuntu 22.04 + OpenJDK 17 环境下实测 Maven 依赖引入、scomp 强类型生成、XmlObject 解析、XPath/XQuery 查询的完整快速入门链路,全部命令与回显真实可复现。
Apache XMLBeans 5.4.1 快速入门与集成示例
目标:在 Ubuntu 22.04 + OpenJDK 17 + Maven 3.6.3 环境下,实测 Apache XMLBeans 5.4.1 的依赖引入、通用解析(XmlObject)、scomp 强类型生成、XPath/XQuery 查询四条主线,全部代码与回显真实可复现。
一、环境信息
实测环境与版本基线(2026-10-15):
| 项 | 值 |
|---|---|
| 操作系统 | Ubuntu 22.04.5 LTS(x86_64) |
| JDK | OpenJDK 17.0.20.1(apt 包 openjdk-17-jdk) |
| Maven | Apache Maven 3.6.3 |
| XMLBeans | org.apache.xmlbeans:xmlbeans:5.4.1(Maven Central 最新 release) |
| Saxon | net.sf.saxon:Saxon-HE:12.10(XPath/XQuery 引擎,5.4.1 POM 中为 provided 作用域) |
| 工作目录 | /work(示例:/work/schema、/work/mvn-quick、/work/mvn-demo) |
# java -version
openjdk version "17.0.20.1" 2026-08-18
OpenJDK Runtime Environment (build 17.0.20.1+1-1~22.04)
OpenJDK 64-Bit Server VM (build 17.0.20.1+1-1~22.04, mixed mode, sharing)
# mvn -v | 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
XMLBeans 5.4.1 的字节码基线是 Java 8(jar 内 XmlObject.class 实测 major=52),JDK 8 与 17 均可运行;本文以 JDK 17 为主环境,JDK 8 兼容性见《Apache XMLBeans 5.4.1 scomp 编译与进阶实战详解》第七节实测。
贯穿全文的业务示例:一张订单(order.xml)与它的 Schema(order.xsd,内含对共享类型文件 types.xsd 的 include):
/work/schema/types.xsd:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:ord="http://example.com/order"
targetNamespace="http://example.com/order"
elementFormDefault="qualified">
<xs:complexType name="AddressType">
<xs:sequence>
<xs:element name="province" type="xs:string"/>
<xs:element name="city" type="xs:string"/>
<xs:element name="street" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
/work/schema/order.xsd:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:ord="http://example.com/order"
targetNamespace="http://example.com/order"
elementFormDefault="qualified">
<xs:include schemaLocation="types.xsd"/>
<xs:element name="order" type="ord:OrderType"/>
<xs:complexType name="OrderType">
<xs:sequence>
<xs:element name="customer" type="ord:CustomerType"/>
<xs:element name="shippingAddress" type="ord:AddressType"/>
<xs:element name="item" type="ord:ItemType" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="id" type="xs:string" use="required"/>
<xs:attribute name="date" type="xs:date" use="required"/>
</xs:complexType>
<xs:complexType name="CustomerType">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="email" type="xs:string"/>
</xs:sequence>
<xs:attribute name="vip" type="xs:boolean" default="false"/>
</xs:complexType>
<xs:complexType name="ItemType">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="price" type="xs:double"/>
</xs:sequence>
<xs:attribute name="sku" type="xs:string" use="required"/>
<xs:attribute name="qty" type="xs:int" use="required"/>
</xs:complexType>
</xs:schema>
/work/schema/order.xml:
<?xml version="1.0" encoding="UTF-8"?>
<ord:order xmlns:ord="http://example.com/order" id="A-2026-1015" date="2026-10-15">
<ord:customer vip="true">
<ord:name>张伟</ord:name>
<ord:email>zhangwei@example.com</ord:email>
</ord:customer>
<ord:shippingAddress>
<ord:province>广东省</ord:province>
<ord:city>深圳市</ord:city>
<ord:street>科技园南路 88 号</ord:street>
</ord:shippingAddress>
<ord:item sku="SKU-1001" qty="2">
<ord:name>机械键盘</ord:name>
<ord:price>399.0</ord:price>
</ord:item>
<ord:item sku="SKU-2002" qty="1">
<ord:name>27 寸显示器</ord:name>
<ord:price>1899.0</ord:price>
</ord:item>
<ord:item sku="SKU-3003" qty="3">
<ord:name>USB-C 扩展坞</ord:name>
<ord:price>259.0</ord:price>
</ord:item>
</ord:order>
二、10 分钟最小可用
2.1 工程与依赖
# mkdir -p /work/mvn-quick/src/main/java/demo
# cd /work/mvn-quick
/work/mvn-quick/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>demo</groupId>
<artifactId>xmlbeans-quick</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.release>17</maven.compiler.release>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.xmlbeans</groupId>
<artifactId>xmlbeans</artifactId>
<version>5.4.1</version>
</dependency>
<dependency>
<groupId>net.sf.saxon</groupId>
<artifactId>Saxon-HE</artifactId>
<version>12.10</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
</plugin>
</plugins>
</build>
</project>
要点:显式固定 maven-compiler-plugin 3.11.0(Maven 3.6.3 内置的旧版插件不认识 --release 参数);Saxon-HE 在 5.4.1 的 POM 里是 provided 作用域,不声明它就无法使用完整 XPath。
2.2 第一个程序
/work/mvn-quick/src/main/java/demo/GenericDemo.java:
package demo;
import org.apache.xmlbeans.SimpleValue;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import javax.xml.namespace.QName;
import java.io.File;
public class GenericDemo {
static final String NS = "http://example.com/order";
static final QName Q_ORDER = new QName(NS, "order");
static final QName Q_CUSTOMER = new QName(NS, "customer");
static final QName Q_EMAIL = new QName(NS, "email");
static final QName Q_ID = new QName("", "id");
public static void main(String[] args) throws Exception {
XmlObject doc = XmlObject.Factory.parse(new File("/work/schema/order.xml"));
System.out.println("解析完成,schemaType = " + doc.schemaType());
XmlObject order = doc.selectChildren(Q_ORDER)[0];
System.out.println("selectChildren(order) 命中 = " + doc.selectChildren(Q_ORDER).length);
System.out.println("selectAttribute(id).getStringValue() = "
+ ((SimpleValue) order.selectAttribute(Q_ID)).getStringValue());
XmlObject email = order.selectChildren(Q_CUSTOMER)[0].selectChildren(Q_EMAIL)[0];
System.out.println("customer/email 文本值 = " + ((SimpleValue) email).getStringValue());
System.out.println("缩进序列化首行 = "
+ doc.xmlText(new XmlOptions().setSavePrettyPrint()).split("\n")[0]);
}
}
2.3 编译与运行
# mvn -q -B compile
# echo $?
0
# M2=~/.m2/repository
# java -Dfile.encoding=UTF-8 -cp target/classes:$M2/org/apache/xmlbeans/xmlbeans/5.4.1/xmlbeans-5.4.1.jar:$M2/org/apache/logging/log4j/log4j-api/2.26.1/log4j-api-2.26.1.jar demo.GenericDemo
解析完成,schemaType = N=
selectChildren(order) 命中 = 1
selectAttribute(id).getStringValue() = A-2026-1015
customer/email 文本值 = zhangwei@example.com
缩进序列化首行 = <?xml version="1.0" encoding="UTF-8"?>
验证点:解析、QName 选取子元素/属性、取文本值、缩进序列化全部工作。schemaType = N= 说明当前类路径没有该文档的 Schema 类型系统(N = NoType),通用解析不依赖 XSD;第四节生成类型 jar 后这一行会变为 D=order@http://example.com/order。
依赖解析结果(确认 XMLBeans 只传递一个编译期依赖 log4j-api):
# mvn -B dependency:tree
...
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ xmlbeans-quick ---
[INFO] demo:xmlbeans-quick:jar:1.0.0
[INFO] +- org.apache.xmlbeans:xmlbeans:jar:5.4.1:compile
[INFO] | \- org.apache.logging.log4j:log4j-api:jar:2.26.1:compile
[INFO] \- net.sf.saxon:Saxon-HE:jar:12.10:compile
[INFO] \- org.xmlresolver:xmlresolver:jar:5.3.3:compile
[INFO] \- org.xmlresolver:xmlresolver:data:jar:5.3.3:compile
...
[INFO] BUILD SUCCESS
三、Maven 集成关键点与高频坑
实测在本版本(5.4.1)上集中踩到四类问题,先列结论再逐条给实测证据:
| 问题 | 现象 | 正确姿势 |
|---|---|---|
| compiler 插件过老 | Fatal error compiling: invalid flag: --release | 显式固定 maven-compiler-plugin 3.11.0 |
| 旧 API 失效 | getStringValue()/setSavePretty() 编译不过 | (SimpleValue) obj).getStringValue() / setSavePrettyPrint() |
| 漏日志门面 | NoClassDefFoundError: org/apache/logging/log4j/Logger | Maven 自动带;jar 直跑必须加 log4j-api |
| 漏 Saxon | XPath 谓词报 NoClassDefFoundError: net/sf/saxon/sxpath/XPathStaticContext | 声明 Saxon-HE 12.10 |
其中两条直接编译不过。用一个临时验证类 /work/mvn-quick/src/main/java/demo/OldApiDemo.java 复现(核心两行就是旧写法 order.getStringValue() 与 new XmlOptions().setSavePretty()):
# mvn -B compile
...
[ERROR] COMPILATION ERROR :
[ERROR] /work/mvn-quick/src/main/java/demo/OldApiDemo.java:[13,33] cannot find symbol
[ERROR] symbol: method getStringValue()
[ERROR] location: variable order of type org.apache.xmlbeans.XmlObject
[ERROR] /work/mvn-quick/src/main/java/demo/OldApiDemo.java:[14,48] cannot find symbol
[ERROR] symbol: method setSavePretty()
[ERROR] location: class org.apache.xmlbeans.XmlOptions
原因与修复:5.x 把"取简单类型文本值"放在 SimpleValue 接口(XmlObject 的兄弟子接口链上),缩进序列化选项名为 setSavePrettyPrint()。用 javap 可直接核实:
# javap -cp ~/.m2/repository/org/apache/xmlbeans/xmlbeans/5.4.1/xmlbeans-5.4.1.jar org.apache.xmlbeans.XmlObject | grep -c stringvalue
0
# javap -cp ~/.m2/repository/org/apache/xmlbeans/xmlbeans/5.4.1/xmlbeans-5.4.1.jar org.apache.xmlbeans.SimpleValue | grep getStringValue
public abstract java.lang.String getStringValue();
# javap -cp ~/.m2/repository/org/apache/xmlbeans/xmlbeans/5.4.1/xmlbeans-5.4.1.jar org.apache.xmlbeans.XmlOptions | grep -i pretty
public org.apache.xmlbeans.XmlOptions setSavePrettyPrint()
public org.apache.xmlbeans.XmlOptions setSavePrettyPrintIndent(int)
💡 技术拓展:网上大量教程基于 2.x~3.x 的 API,
XmlObject.getStringValue()是当年存在的;XMLBeans 5.x 清理了这批方法。遇到编译错误先javap实测当前 jar 的真实签名,再改写法,比搜索旧博客可靠。
四、scomp 生成强类型类并使用
4.1 生成类型 jar
scomp 编译器已内置在 xmlbeans-5.4.1.jar 中,命令行入口为主类 org.apache.xmlbeans.impl.tool.SchemaCompiler(注意:jar 内同包的 XMLBean 类是 Ant 任务、没有 main 方法,实测确认):
# cd /work
# java -cp xmlbeans-5.4.1.jar:log4j-api-2.26.1.jar org.apache.xmlbeans.impl.tool.SchemaCompiler -out order-xmlbeans.jar schema/order.xsd
Time to build schema type system: 0.387 seconds
Time to generate code: 0.048 seconds
Time to compile code: 1.0 seconds
Compiled types to: order-xmlbeans.jar
# echo $?
0
# jar tf order-xmlbeans.jar | head -8
com/
com/example/
com/example/order/
com/example/order/OrderType.class
com/example/order/OrderDocument.class
com/example/order/impl/
com/example/order/impl/ItemTypeImpl.class
com/example/order/impl/OrderDocumentImpl.class
# jar tf order-xmlbeans.jar | wc -l
52
关键事实:命名空间 http://example.com/order 自动映射为 Java 包 com.example.order;order.xsd 通过 xs:include 引用的 types.xsd 被自动带入(jar 的元数据目录里能看到两个 xsd 源)。类名对应关系:
| Schema | 生成的 Java |
|---|---|
| 全局元素 order | OrderDocument(文档接口) + OrderDocument.Order(元素内容) |
| 命名类型 OrderType/CustomerType/ItemType/AddressType | 同名接口 + impl 包实现类 |
(scomp 完整选项与失败样例见《Apache XMLBeans 5.4.1 scomp 编译与进阶实战详解》。)
4.2 安装到本地仓库并引入工程
# cd /work/mvn-quick
# mvn -q -B install:install-file -Dfile=/work/order-xmlbeans.jar -DgroupId=com.example -DartifactId=order-xmlbeans -Dversion=1.0.0 -Dpackaging=jar -DgeneratePom=true
# echo $?
0
在 pom 的 <dependencies> 里追加:
<dependency>
<groupId>com.example</groupId>
<artifactId>order-xmlbeans</artifactId>
<version>1.0.0</version>
</dependency>
4.3 强类型读写与校验
/work/mvn-quick/src/main/java/demo/TypedDemo.java:
package demo;
import com.example.order.AddressType;
import com.example.order.CustomerType;
import com.example.order.ItemType;
import com.example.order.OrderDocument;
import com.example.order.OrderType;
import org.apache.xmlbeans.XmlError;
import org.apache.xmlbeans.XmlOptions;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class TypedDemo {
public static void main(String[] args) throws Exception {
OrderDocument doc = OrderDocument.Factory.parse(new File("/work/schema/order.xml"));
OrderType order = doc.getOrder();
System.out.println("id = " + order.getId()
+ " date = " + order.getDate().getTime());
CustomerType customer = order.getCustomer();
System.out.println("customer = " + customer.getName()
+ " <" + customer.getEmail() + "> vip = " + customer.getVip());
AddressType addr = order.getShippingAddress();
System.out.println("地址 = " + addr.getProvince() + " " + addr.getCity()
+ " " + addr.getStreet());
double total = 0;
for (ItemType item : order.getItemList()) {
System.out.println("item " + item.getSku() + " qty=" + item.getQty()
+ " price=" + item.getPrice() + " name=" + item.getName());
total += item.getQty() * item.getPrice();
}
System.out.println("合计 = " + total);
System.out.println("---- 修改并校验 ----");
order.setId("B-2026-1101");
order.getItemList().get(0).setQty(3);
ItemType extra = order.addNewItem();
extra.setSku("SKU-4004");
extra.setQty(1);
extra.setName("无线鼠标");
extra.setPrice(199.0);
List<XmlError> errors = new ArrayList<>();
XmlOptions opts = new XmlOptions();
opts.setErrorListener(errors);
boolean ok = doc.validate(opts);
System.out.println("validate = " + ok + " errors = " + errors.size());
doc.save(new File("/work/schema/order-typed-modified.xml"));
System.out.println("已保存 /work/schema/order-typed-modified.xml");
System.out.println("---- 校验非法实例 ----");
OrderDocument bad = OrderDocument.Factory.parse(new File("/work/schema/order-invalid.xml"));
List<XmlError> badErrors = new ArrayList<>();
XmlOptions badOpts = new XmlOptions();
badOpts.setErrorListener(badErrors);
System.out.println("validate = " + bad.validate(badOpts)
+ " errors = " + badErrors.size());
for (XmlError e : badErrors) {
System.out.println(" line " + e.getLine() + ", col " + e.getColumn()
+ ": " + e.getMessage());
}
}
}
非法实例 /work/schema/order-invalid.xml(缺必填属性 id,且 qty="abc"):
<?xml version="1.0" encoding="UTF-8"?>
<ord:order xmlns:ord="http://example.com/order" date="2026-10-15">
<ord:customer vip="true">
<ord:name>张伟</ord:name>
<ord:email>zhangwei@example.com</ord:email>
</ord:customer>
<ord:shippingAddress>
<ord:province>广东省</ord:province>
<ord:city>深圳市</ord:city>
<ord:street>科技园南路 88 号</ord:street>
</ord:shippingAddress>
<ord:item sku="SKU-1001" qty="abc">
<ord:name>机械键盘</ord:name>
<ord:price>399.0</ord:price>
</ord:item>
</ord:order>
把本系列示例类(GenericDemo、CursorDemo、TypedDemo、QNameSchemaDemo、QueryDemo,其中两类详见《XmlObject 与 Schema API 详解》)放入工程后编译并运行:
# mvn -B clean compile
...
[INFO] --- maven-compiler-plugin:3.11.0:compile (default-compile) @ xmlbeans-quick ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 5 source files with javac [debug release 17] to target/classes
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.371 s
[INFO] Finished at: 2026-10-15T12:00:28Z
[INFO] ------------------------------------------------------------------------
# M2=~/.m2/repository
# java -Dfile.encoding=UTF-8 -cp target/classes:$M2/org/apache/xmlbeans/xmlbeans/5.4.1/xmlbeans-5.4.1.jar:$M2/org/apache/logging/log4j/log4j-api/2.26.1/log4j-api-2.26.1.jar:$M2/com/example/order-xmlbeans/1.0.0/order-xmlbeans-1.0.0.jar demo.TypedDemo
id = A-2026-1015 date = Thu Oct 15 00:00:00 GMT 2026
customer = 张伟 <zhangwei@example.com> vip = true
地址 = 广东省 深圳市 科技园南路 88 号
item SKU-1001 qty=2 price=399.0 name=机械键盘
item SKU-2002 qty=1 price=1899.0 name=27 寸显示器
item SKU-3003 qty=3 price=259.0 name=USB-C 扩展坞
合计 = 3474.0
---- 修改并校验 ----
validate = true errors = 0
已保存 /work/schema/order-typed-modified.xml
---- 校验非法实例 ----
validate = false errors = 2
line -1, col -1: Expected attribute: id in element order@http://example.com/order
line -1, col -1: Invalid decimal value: unexpected char '97'
验证点全部达成:强类型 getter/setter、addNewItem() 追加、合计 3474.0(3×399+1899+3×259)、合法修改校验通过、非法实例精确报出两类错误(缺必填属性、qty 含非法字符 97 即 'a')。line/col 为 -1 是 validate() 错误监听器的已知行为,需要行列号时改用 CLI 校验器(见 scomp 进阶篇第六节,实测能给出 2:66、12:37)。
五、XPath 与 XQuery 查询
5.1 引擎分层:内置引擎与 Saxon 的边界
5.4.1 内置一个 XPath 子集引擎,无谓词的简单路径不需要 Saxon;带谓词([])的路径才走 Saxon。用下面的 QueryDemo 实测边界——先从类路径里去掉 Saxon:
/work/mvn-quick/src/main/java/demo/QueryDemo.java:
package demo;
import org.apache.xmlbeans.SimpleValue;
import org.apache.xmlbeans.XmlObject;
import java.io.File;
public class QueryDemo {
static final String NS_DECL =
"declare namespace ord='http://example.com/order'; ";
public static void main(String[] args) throws Exception {
XmlObject doc = XmlObject.Factory.parse(new File("/work/schema/order.xml"));
System.out.println("== selectPath(XPath 2.0,Saxon) ==");
XmlObject[] emails = doc.selectPath(
NS_DECL + "$this/ord:order/ord:customer/ord:email");
System.out.println("customer/email 命中 = " + emails.length
+ " 值 = " + ((SimpleValue) emails[0]).getStringValue());
XmlObject[] skus = doc.selectPath(
NS_DECL + "$this/ord:order/ord:item/@sku");
System.out.print("全部 item/@sku 命中 " + skus.length + " 个: ");
for (XmlObject sku : skus) {
System.out.print(((SimpleValue) sku).getStringValue() + " ");
}
System.out.println();
XmlObject[] dear = doc.selectPath(
NS_DECL + "$this//ord:item[ord:price > 300]/ord:name");
System.out.print("价格 > 300 的 item name 命中 " + dear.length + " 个: ");
for (XmlObject d : dear) {
System.out.print(((SimpleValue) d).getStringValue() + " ");
}
System.out.println();
System.out.println("== execQuery(XQuery 1.0,Saxon) ==");
XmlObject[] result = doc.execQuery(
NS_DECL + "for $i in $this//ord:item "
+ "where xs:double($i/ord:price) > 300 "
+ "order by xs:double($i/ord:price) descending "
+ "return $i/ord:name");
System.out.println("XQuery 返回条数 = " + result.length);
for (XmlObject r : result) {
System.out.println(" " + ((SimpleValue) r).getStringValue());
}
}
}
类路径只有 xmlbeans + log4j-api 时(无 Saxon):
# java -Dfile.encoding=UTF-8 -cp target/classes:$M2/org/apache/xmlbeans/xmlbeans/5.4.1/xmlbeans-5.4.1.jar:$M2/org/apache/logging/log4j/log4j-api/2.26.1/log4j-api-2.26.1.jar:$M2/com/example/order-xmlbeans/1.0.0/order-xmlbeans-1.0.0.jar demo.QueryDemo
== selectPath(XPath 2.0,Saxon) ==
customer/email 命中 = 1 值 = zhangwei@example.com
全部 item/@sku 命中 3 个: SKU-1001 SKU-2002 SKU-3003
Exception in thread "main" java.lang.NoClassDefFoundError: net/sf/saxon/sxpath/XPathStaticContext
at org.apache.xmlbeans.impl.xpath.XPathFactory.getCompiledPathSaxon(XPathFactory.java:142)
at org.apache.xmlbeans.impl.xpath.XPathFactory.getCompiledPath(XPathFactory.java:97)
at org.apache.xmlbeans.impl.xpath.XPathFactory.getCompiledPath(XPathFactory.java:59)
at org.apache.xmlbeans.impl.store.Cursor._selectPath(Cursor.java:716)
...
Caused by: java.lang.ClassNotFoundException: net.sf.saxon.sxpath.XPathStaticContext
... 23 more
前两条简单路径命中(内置引擎),第三条带 [ord:price > 300] 谓词的路径在编译期回退到 Saxon 引擎并因缺类而失败——这就是"必须引入 Saxon"的实测边界。
5.2 补齐 Saxon 后完整运行
Maven 方式声明 net.sf.saxon:Saxon-HE:12.10 即可,其 POM 会传递引入 org.xmlresolver:xmlresolver:5.3.3(实测 dependency:tree 见第二节)。若像 jar 直跑那样手工拼类路径,必须连 xmlresolver 一起加,否则 Saxon 初始化时报错(实测复现):
Exception in thread "main" java.lang.NoClassDefFoundError: org/xmlresolver/Resolver
at net.sf.saxon.lib.CatalogResourceResolver.<init>(CatalogResourceResolver.java:46)
at net.sf.saxon.Configuration.init(Configuration.java:387)
at net.sf.saxon.Configuration.<init>(Configuration.java:234)
at org.apache.xmlbeans.impl.xpath.saxon.SaxonXPath.selectNodes(SaxonXPath.java:92)
...
Caused by: java.lang.ClassNotFoundException: org.xmlresolver.Resolver
补齐 xmlresolver-5.3.3.jar 后完整运行:
# M2=~/.m2/repository
# java -Dfile.encoding=UTF-8 -cp target/classes:$M2/org/apache/xmlbeans/xmlbeans/5.4.1/xmlbeans-5.4.1.jar:$M2/org/apache/logging/log4j/log4j-api/2.26.1/log4j-api-2.26.1.jar:$M2/com/example/order-xmlbeans/1.0.0/order-xmlbeans-1.0.0.jar:$M2/net/sf/saxon/Saxon-HE/12.10/Saxon-HE-12.10.jar:$M2/org/xmlresolver/xmlresolver/5.3.3/xmlresolver-5.3.3.jar demo.QueryDemo
== selectPath(XPath 2.0,Saxon) ==
customer/email 命中 = 1 值 = zhangwei@example.com
全部 item/@sku 命中 3 个: SKU-1001 SKU-2002 SKU-3003
价格 > 300 的 item name 命中 2 个: 机械键盘 27 寸显示器
== execQuery(XQuery 1.0,Saxon) ==
XQuery 返回条数 = 2
27 寸显示器
机械键盘
验证点:XPath 属性选取(@sku)、谓词过滤(price>300 命中 2 条)、XQuery FLWOR(where + order by descending 得到 1899 元的显示器在前)全部正确。
5.3 实测踩坑:无类型文档的排序按字符串比较
本例第一版查询写的是 order by $i/ord:price descending,实测输出顺序是机械键盘(399.0)在前、显示器(1899.0)在后——因为文档未绑定 Schema 类型(N= 解析),price 节点是无类型原子值,排序退化为字符串比较("399.0" > "1899.0")。修复:显式转换 xs:double($i/ord:price)(本文 QueryDemo 即修复后版本),where 子句同理。绑定类型 jar 后由 Schema 声明(xs:double)也能获得正确的数值排序。
六、方案选型建议
| 场景 | 建议 |
|---|---|
| 有 XSD、要强类型、XML 结构频繁变动需编译期检查 | XMLBeans 5.4.1 + scomp(本文第四节) |
| 只要读写不校验、无 XSD | 通用 XmlObject + XmlCursor(N= 解析,零依赖 XSD) |
| 仅做轻量路径查询 | XPath 子集即可,可不引 Saxon(见 5.1 边界) |
| 需要完整 XPath 2.0 / XQuery / 排序 | 必须加 Saxon-HE 12.10 + xmlresolver 5.3.3 |
| 已在 JDK 8 遗留系统 | XMLBeans 5.4.1 兼容(字节码 major=52),《scomp 进阶篇》第七节实测 |
💡 技术拓展:与 JAXB 相比,XMLBeans 强项是保留完整游标与运行期 Schema 自省、对未绑定文档也能解析;弱项是生成类体积与学习曲线。JDK 11+ 项目若只需"XSD→类 + 校验",两者皆可;需要游标式增量修改(XmlCursor)时选 XMLBeans。
七、参考链接
- 官方入门指南:https://xmlbeans.apache.org/docs/getting_started_guide.html
- XmlObject Javadoc(5.4.1):https://xmlbeans.apache.org/apidocs/5.4.1/org/apache/xmlbeans/XmlObject.html
- XmlCursor Javadoc(5.4.1):https://xmlbeans.apache.org/apidocs/5.4.1/org/apache/xmlbeans/XmlCursor.html
- Maven Central:https://central.maven.org/maven2/org/apache/xmlbeans/xmlbeans/
- 本系列姊妹篇:《Ubuntu 22.04 安装 Apache XMLBeans 指南》《Apache XMLBeans 5.4.1 XmlObject 与 Schema API 详解》《Apache XMLBeans 5.4.1 scomp 编译与进阶实战详解》