Apache Xerces 配置参数详解
逐项解析 Xerces-J 2.12.2 的 SAX 标准特性、扩展特性与扩展属性、JAXP 系统属性及 ErrorHandler 错误处理,全部配置项均附 OpenJDK 17 下的实测默认值与运行回显。
Apache Xerces 配置参数详解
一、配置体系总览
Xerces-J(本文实测版本 2.12.2)的配置分为四层,作用范围从大到小:
| 层次 | 机制 | 典型示例 | 优先级 |
|---|---|---|---|
| JAXP 工厂选择 | 系统属性 / ServiceLoader | -Djavax.xml.parsers.DocumentBuilderFactory=... | 最高 |
| JAXP 工厂开关 | 工厂对象的方法 | factory.setNamespaceAware(true)、factory.setFeature(...) | 次之 |
| 解析器特性 | URI 字符串标识的布尔开关 | reader.setFeature("http://apache.org/xml/features/validation/schema", true) | 再次 |
| 解析器属性 | URI 字符串标识的对象注入 | reader.setProperty(".../external-schemaLocation", "...") | 最低(同层按调用顺序) |
特性(feature)与属性(property)的 URI 遵循两个命名空间:
- SAX 标准前缀:
http://xml.org/sax/features/、http://xml.org/sax/properties/ - Xerces 扩展前缀:
http://apache.org/xml/features/、http://apache.org/xml/properties/
所有实测结论均来自 OpenJDK 17(Ubuntu 22.04 与 CentOS 8 双端一致)下的探测程序:对 XMLReader 逐项调用 getFeature/getProperty 并打印结果。
二、SAX 标准特性
以 http://xml.org/sax/features/ 为前缀。对 JAXP 工厂产出的解析器(org.apache.xerces.jaxp.SAXParserImpl$JAXPSAXParser)实测默认值:
| 特性 URI | 实测默认值 | 说明 |
|---|---|---|
http://xml.org/sax/features/namespaces | false(JAXP 工厂)/ true(原生 SAXParser 类) | 开启后回调中的 uri、localName 生效。这就是默认 SAX 输出 uri/localName 为空串、而 Xerces 原生类有值的原因 |
http://xml.org/sax/features/namespace-prefixes | true(JAXP 工厂)/ false(原生 SAXParser 类) | 是否报告 xmlns:* 与 xmlns 属性 |
http://xml.org/sax/features/validation | false | DTD 校验开关。只开它对 XSD 文档无效,实测报 "no grammar found" |
与 setNamespaceAware(true) 的关系:工厂方法内部会同步 namespaces 特性;直接操作 XMLReader 时用 setFeature 即可,两者等价。
三、Xerces 扩展特性
以 http://apache.org/xml/features/ 为前缀,为 Xerces 专属能力。逐项实测结果(两 OS 一致):
3.1 校验类
| 特性 URI(完整) | 实测默认值 | 说明 |
|---|---|---|
http://apache.org/xml/features/validation/schema | false | XSD 校验总开关。与 http://xml.org/sax/features/validation 同时开启才会执行 Schema 校验 |
http://apache.org/xml/features/validation/dynamic | false | 动态校验:文档声明了 grammar 才校验 |
http://apache.org/xml/features/validation/schema-full-checking | 可识别 | 完整 Schema 约束检查,更严格但更慢 |
http://apache.org/xml/features/honour-all-schemaLocations | false | 校验 xsi:schemaLocation 声明的全部 schema。注意:没有 /validation/ 段,写成 .../features/validation/honour-all-schemaLocations 会实测抛 SAXNotRecognizedException |
http://apache.org/xml/features/validation/warn-on-duplicate-attdef | 可识别 | DTD 重复属性定义告警 |
http://apache.org/xml/features/validation/warn-on-undeclared-elemdef | false | DTD 未声明元素告警。注意是 elemdef,写成 warn-on-undeclared-element 实测不被识别 |
3.2 DTD 与实体加载类
| 特性 URI(完整) | 实测默认值 | 说明 |
|---|---|---|
http://apache.org/xml/features/nonvalidating/load-external-dtd | true | 即使不校验也加载外部 DTD。生产环境建议关闭以防外部实体注入 |
http://apache.org/xml/features/nonvalidating/load-dtd-grammar | false | 加载 DTD 作为 grammar |
http://apache.org/xml/features/disallow-doctype-decl | false | 禁止 DOCTYPE 声明,防御 XXE 的首选开关 |
disallow-doctype-decl 的实测拦截效果——对含 <!DOCTYPE books [<!ENTITY xxe SYSTEM "file:///etc/passwd">]> 的文档开启该特性后解析:
已开启 disallow-doctype-decl=true,开始解析...
[Fatal Error] :2:10: DOCTYPE is disallowed when the feature "http://apache.org/xml/features/disallow-doctype-decl" set to true.
解析被拦截,异常原文: 行=2 列=10 DOCTYPE is disallowed when the feature "http://apache.org/xml/features/disallow-doctype-decl" set to true.
3.3 解析行为类
| 特性 URI(完整) | 实测默认值 | 说明 |
|---|---|---|
http://apache.org/xml/features/standard-uri-conformant | false | 严格 URI 合规检查 |
http://apache.org/xml/features/continue-after-fatal-error | 可识别 | 致命错误后是否继续 |
http://apache.org/xml/features/allow-java-encodings | 可识别 | 允许 Java 风格编码名 |
http://apache.org/xml/features/generate-synthetic-annotations | 可识别 | Schema 综合注解生成 |
http://apache.org/xml/features/validate-annotations | 可识别 | 校验 Schema 注解本身 |
四、Xerces 扩展属性
以 http://apache.org/xml/properties/ 为前缀,值是对象而非布尔值。实测可读(未设置时返回 null):
| 属性 URI(完整) | 实测值 | 说明 |
|---|---|---|
http://apache.org/xml/properties/schema/external-schemaLocation | null | 外部指定 XSD(带命名空间文档),键值格式为"命名空间 URI + 空格 + schema 路径",多对用空格分隔 |
http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation | null | 外部指定 XSD(无命名空间文档),值为单个路径 |
实测一(用 external-schemaLocation 对违规文档校验):
XMLReader reader = new org.apache.xerces.parsers.SAXParser();
reader.setFeature("http://xml.org/sax/features/validation", true);
reader.setFeature("http://apache.org/xml/features/validation/schema", true);
reader.setProperty("http://apache.org/xml/properties/schema/external-schemaLocation",
"http://www.example.com/books /root/xerces-demo/src/main/resources/books.xsd");
reader.setErrorHandler(handler);
reader.parse("src/main/resources/books-invalid.xml");
实测回显:
---- 校验 books-invalid.xml(通过扩展属性指定 schema)----
[ERROR] 行 8 列 11: cvc-complex-type.4: Attribute 'id' must appear on element 'book'.
[ERROR] 行 11 列 36: cvc-datatype-valid.1.2.1: 'not-a-number' is not a valid value for 'decimal'.
[ERROR] 行 11 列 36: cvc-type.3.1.3: The value 'not-a-number' of element 'price' is not valid.
解析结束
实测二(路径错误提示):把属性值写成相对路径 src/main/resources/books.xsd 时,该路径会基于实例文档的 base URI 解析(而非进程工作目录),实测报:
[WARNING] 行 3: schema_reference.4: Failed to read schema document 'src/main/resources/books.xsd', because 1) could not find the document; 2) the document could not be read; 3) the root element of the document is not <xsd:schema>.
结论:属性值一律使用绝对路径或绝对 URL。另外两个实测细节:对带默认命名空间的文档误用 external-noNamespaceSchemaLocation 会报 TargetNamespace.2: Expecting no namespace, but the schema document has a target namespace of 'http://www.example.com/books'。
标准词法处理器应使用 SAX 标准属性 http://xml.org/sax/properties/lexical-handler(Xerces 扩展名下的 lexical-handler 实测不被识别);错误处理器与实体解析器则通过 setErrorHandler()、setEntityResolver() 方法注入,不走 property。
五、JAXP 系统属性
5.1 支持实现切换的系统属性
| 系统属性 | Xerces-J 提供的实现类(实测可用) |
|---|---|
javax.xml.parsers.DocumentBuilderFactory | org.apache.xerces.jaxp.DocumentBuilderFactoryImpl |
javax.xml.parsers.SAXParserFactory | org.apache.xerces.jaxp.SAXParserFactoryImpl |
javax.xml.validation.SchemaFactory:http://www.w3.org/2001/XMLSchema | org.apache.xerces.jaxp.validation.XMLSchemaFactory |
org.xml.sax.driver | org.apache.xerces.parsers.SAXParser |
注意:xercesImpl-2.12.2.jar 的 META-INF/services 实测包含 DatatypeFactory、DocumentBuilderFactory、SAXParserFactory、XMLEventFactory、SchemaFactory、DOMImplementationSourceList、org.xml.sax.driver 七个声明,不含 TransformerFactory(转换器需另配 Xalan)。
5.2 三种选择机制的实测对比
对同一个探测程序 JaxpSwitchDemo 三种运行方式(OpenJDK 17,双 OS 结果一致):
场景 1,classpath 不含 xercesImpl:
javax.xml.parsers.DocumentBuilderFactory = (未设置)
实际 DocumentBuilderFactory 实现: com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl
场景 2,classpath 含 xercesImpl(未设任何系统属性):
javax.xml.parsers.DocumentBuilderFactory = (未设置)
实际 DocumentBuilderFactory 实现: org.apache.xerces.jaxp.DocumentBuilderFactoryImpl
场景 3,显式系统属性:
java -Djavax.xml.parsers.DocumentBuilderFactory=org.apache.xerces.jaxp.DocumentBuilderFactoryImpl \
-Djavax.xml.parsers.SAXParserFactory=org.apache.xerces.jaxp.SAXParserFactoryImpl \
-cp target/classes com.example.xerces.JaxpSwitchDemo
javax.xml.parsers.DocumentBuilderFactory = org.apache.xerces.jaxp.DocumentBuilderFactoryImpl
实际 DocumentBuilderFactory 实现: org.apache.xerces.jaxp.DocumentBuilderFactoryImpl
javax.xml.parsers.SAXParserFactory = org.apache.xerces.jaxp.SAXParserFactoryImpl
实际 SAXParserFactory 实现: org.apache.xerces.jaxp.SAXParserFactoryImpl
结论:优先级为 系统属性 > classpath ServiceLoader 声明 > JDK 内置默认。把 xercesImpl 放进 classpath 这一动作本身就会切换全部 JAXP 工厂的默认实现,升级 JDK 或引入新依赖时要意识到这一点。
5.3 SchemaFactory 的带冒号语法
javax.xml.validation.SchemaFactory 的系统属性名以 schema 语言 URI 为后缀,冒号分隔:
java -Djavax.xml.validation.SchemaFactory:http://www.w3.org/2001/XMLSchema=org.apache.xerces.jaxp.validation.XMLSchemaFactory ...
实测在不设置该属性、仅把 xercesImpl 放进 classpath 时,SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI) 也已返回 org.apache.xerces.jaxp.validation.XMLSchemaFactory(ServiceLoader 生效)。
六、ErrorHandler 错误处理
6.1 三类错误的语义
| 回调 | 触发条件 | 实测示例 |
|---|---|---|
| warning | 可恢复告警 | schema_reference.4 找不到 schema 文档 |
| error | 违反 Schema 约束,可继续解析 | cvc-complex-type.4、cvc-datatype-valid.1.2.1 |
| fatalError | 格式错误(not well-formed),解析被迫中断 | XML document structures must start and end within the same entity. |
6.2 实测回显(XSD 校验违规文档)
Validator validator = schema.newValidator();
validator.setErrorHandler(new ErrorHandler() {
public void warning(SAXParseException e) {
System.out.println("[WARNING] 行 " + e.getLineNumber() + " 列 " + e.getColumnNumber() + ": " + e.getMessage());
}
public void error(SAXParseException e) {
errors[0]++;
System.out.println("[ERROR] 行 " + e.getLineNumber() + " 列 " + e.getColumnNumber() + ": " + e.getMessage());
}
public void fatalError(SAXParseException e) throws SAXParseException {
errors[0]++;
System.out.println("[FATAL] 行 " + e.getLineNumber() + " 列 " + e.getColumnNumber() + ": " + e.getMessage());
throw e; // 中断解析
}
});
validator.validate(new StreamSource(new File("src/main/resources/books-invalid.xml")));
[ERROR] 行 8 列 11: cvc-complex-type.4: Attribute 'id' must appear on element 'book'.
[ERROR] 行 11 列 36: cvc-datatype-valid.1.2.1: 'not-a-number' is not a valid value for 'decimal'.
[ERROR] 行 11 列 36: cvc-type.3.1.3: The value 'not-a-number' of element 'price' is not valid.
结果: 校验失败,共报告 3 处错误
格式不合法文档的实测回显(fataError 抛出后 validate 抛 SAXParseException):
[FATAL] 行 9 列 1: XML document structures must start and end within the same entity.
结果: 校验失败(致命错误已中断解析)
6.3 两个必须注意的实测行为
- error() 不抛异常时校验器会跑完整个文档并正常返回。 实测只在回调里打印、不计数不抛出时,validate() 正常结束,程序误报"校验通过"。正确做法是维护错误计数或直接抛出 SAXParseException。
- DOM 路径同理。
DocumentBuilder.parse()遇到格式错误时先由默认 handler 打印一行[Fatal Error] 文件名:9:1: ...再抛出异常,实测回显:
[Fatal Error] books-badwf.xml:9:1: XML document structures must start and end within the same entity.
捕获 SAXParseException:
systemId = file:/root/xerces-demo/src/main/resources/books-badwf.xml
行=9 列=1
message = XML document structures must start and end within the same entity.
七、配置组合的推荐基线
结合实测,给出生产环境推荐配置(防御 XXE 并显式声明意图):
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true);
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
factory.setFeature("http://xml.org/sax/features/validation", true);
factory.setFeature("http://apache.org/xml/features/validation/schema", true);
配套 JVM 参数(256–512 MB 堆)写入 /etc/profile.d/xerces-maven.sh:
export MAVEN_OPTS="-Xms256m -Xmx512m"
实测验证(后台运行解析进程后采样):
# jps -lv
7857 com.example.xerces.BigXmlDom -Xms256m -Xmx512m
# jstat -gcutil 7857 1000 3
S0 S1 E O M CCS YGC YGCT FGC FGCT CGC CGCT GCT
0.00 100.00 10.53 99.86 91.58 62.69 44 0.518 0 0.000 12 0.014 0.532
0.00 100.00 21.05 99.89 91.05 61.42 70 0.779 1 0.012 18 0.020 0.811
0.00 0.00 17.54 24.64 91.05 61.42 103 1.025 3 0.035 25 0.028 1.089
八、常见问题
Q1:setFeature 抛 SAXNotRecognizedException 一定是版本不支持吗?
不一定。实测两类"假性不识别":.../features/validation/honour-all-schemaLocations 正确 URI 没有 /validation/ 段(应为 http://apache.org/xml/features/honour-all-schemaLocations);warn-on-undeclared-element 应为 warn-on-undeclared-elemdef。先核对 URI 再怀疑版本。
Q2:JAXP 包装类与 Xerces 原生解析器类配置结果为何不同?
实测 org.apache.xerces.jaxp.SAXParserImpl$JAXPSAXParser 默认 namespaces=false、namespace-prefixes=true,而原生 org.apache.xerces.parsers.SAXParser 默认 namespaces=true、namespace-prefixes=false。业务代码建议统一走 JAXP 工厂,避免两套默认值造成困惑。
Q3:设置了 external-noNamespaceSchemaLocation 却报 TargetNamespace.2?
该属性只适用于无命名空间文档。实测对带 xmlns="http://www.example.com/books" 的文档使用时报 Expecting no namespace, but the schema document has a target namespace of 'http://www.example.com/books',应改用 external-schemaLocation。
Q4:属性值里的相对路径为什么找不到文件? 外部 schema 属性值按实例文档的 base URI 解析。实测相对路径报 schema_reference.4,改为绝对路径后正常。
Q5:只开 http://xml.org/sax/features/validation 为什么对 XSD 无效?
该特性仅指 DTD 校验。实测只开它解析带 xsi:noNamespaceSchemaLocation 的文档报 Document is invalid: no grammar found.,必须再加 http://apache.org/xml/features/validation/schema(或用 SchemaFactory/Validator 路径)。
Q6:错误回调打印了 ERROR 却得到"校验通过"结论? error() 抛不抛异常决定 validate() 是否中断。实测只打印不抛出时校验器跑完全文并正常返回,需要在回调中计数并在校验后判断计数,或直接抛出异常。
九、参考链接
- Xerces2 Java 官方站点:https://xerces.apache.org/xerces2-j/
- 特性(features)官方清单:https://xerces.apache.org/xerces2-j/features.html
- 属性(properties)官方清单:https://xerces.apache.org/xerces2-j/properties.html
- Maven Central 构件页:https://central.sonatype.com/artifact/xerces/xercesImpl
- 源码仓库:https://github.com/apache/xerces-j