Apache Xerces-C++ 3.3.0 配置参数详解

逐项实测 Xerces-C++ 3.3.0 的 setFeature/setProperty 参数(validation/dynamic/Schema/schemaFullChecking/namespaces 等)、ErrorHandler 错误分级与 CMake 构建选项换装。

Apache XercesApache XercesC/C++XMLUbuntu2026/10/16

Apache Xerces-C++ 3.3.0 配置参数详解

目标:把 Xerces-C++ 3.3.0 解析器的 feature(特性开关)与 property(属性)逐项实测一遍,搞清每个参数的默认值、生效方式与组合行为,并覆盖错误处理器与 CMake 构建期选项。全部命令在 Ubuntu 22.04 服务器实测,回显如实转录。

一、环境信息

  • 操作系统:Ubuntu 22.04(x86_64),gcc 11.4.0,cmake 3.22.1
  • Xerces-C++ 3.3.0(源码 CMake 编译安装到 /usr/local,过程见《Apache Xerces-C++ 在 Ubuntu 22.04 集成使用指南》)
  • 解析器配置摘要(configure 实测):File Manager=POSIX,Mutex Manager=standard,Transcoder=gnuiconv,NetAccessor=socket,Message Loader=inmemory,XMLCh type=char16_t
  • 测试数据与自写程序目录:/root/xtest

二、前置条件:程序与数据准备

本章探测工具是自写的两个小程序 feature_test(feature 查询/设置 + 实际解析)与 property_test(property 设置/回读 + 实际解析),编译方式实测如下。

⚠️ 实测发现的链接坑:装过 apt 版 libxerces-c-dev 后,裸 -lxerces-c 会先命中 /lib/x86_64-linux-gnu/libxerces-c-3.2.so(链接器搜索序 /lib/x86_64-linux-gnu 先于 /usr/local/lib,ld --verbose 实测确认),而头文件仍命中 /usr/local/include 的 3.3 版,两端命名空间不一致(xercesc_3_2 vs xercesc_3_3)会报大量 undefined reference。统一用 pkg-config --cflags --libs xerces-c 显式带上 -L/usr/local/lib 即可避免:

cd /root/xtest
g++ -std=c++14 feature_test.cpp  -o feature_test  $(pkg-config --cflags --libs xerces-c)
g++ -std=c++14 property_test.cpp -o property_test $(pkg-config --cflags --libs xerces-c)
g++ -std=c++14 error_handler.cpp -o error_handler $(pkg-config --cflags --libs xerces-c)
g++ -std=c++14 caf_demo.cpp      -o caf_demo      $(pkg-config --cflags --libs xerces-c)
g++ -std=c++14 dynamic_demo.cpp  -o dynamic_demo  $(pkg-config --cflags --libs xerces-c)

测试数据三个文件(均在 /root/xtest):

  • employee.xsd:定义 employee 根元素(name/age 子元素、必填 id 属性,age 为 xs:integer);
  • employee.xml / employee_bad.xml:合法/非法实例,均带 xsi:noNamespaceSchemaLocation="employee.xsd" 引用;
  • employee_noxsi.xml:内容非法(<age>thirty</age>)但不带任何语法引用,用于验证 dynamic 组合行为。

两个探测程序的用法(实测 -h 会当普通文件名,程序用参数个数校验):

Usage: feature_test <xml-file> <feature-uri> [true|false]
Usage: property_test <xml-file> <property-uri> <value>

💡 技术拓展:feature 与 property 的区别——feature 是布尔开关(bool getFeature / void setFeature(const XMLCh*, bool)),property 是值型配置(void* getProperty / void setProperty(const XMLCh*, void*))。名字都是 URI 字符串:http://xml.org/sax/features/... 前缀是 SAX 标准特性,http://apache.org/xml/features/... 与 http://apache.org/xml/properties/... 前缀是 Xerces 扩展;Xerces-J 使用同一套 URI,经验可直接互通。头文件 util/XMLUni.hpp 里预定义了全部 URI 常量(如 fgSAX2CoreValidation、fgXercesSchema)。

三、Feature 参数逐项实测

以下每条命令都真实执行过;getFeature -> 是默认值,setFeature(...) 后回读确认,然后立即解析文档观察行为差异。

3.1 namespaces(命名空间处理)

./feature_test notes.xml http://xml.org/sax/features/namespaces
getFeature -> true
parse -> OK

SAX2 解析器默认开启命名空间处理,qname/uri/localname 三元组在回调中可用。设为 false 会让 SAX2 回调只走 SAX1 兼容路径(文档元素名合并处理),一般保持默认。

3.2 validation(校验总开关)

./feature_test employee_bad.xml http://xml.org/sax/features/validation
./feature_test employee_bad.xml http://xml.org/sax/features/validation true
getFeature -> false
parse -> OK

getFeature -> false
setFeature(true) -> OK, readback true
ERROR /root/xtest/employee_bad.xml:5:20 value 'thirty' does not match regular expression facet '[+\-]?[0-9]+'
parse -> OK

默认 false——不校验,schema 引用形同虚设(第一条命令对内容非法文档直接放行)。设为 true 后,解析器顺着实例文档里的 xsi:noNamespaceSchemaLocation 找到 employee.xsd 并校验,报出 <age>thirty</age> 不匹配 integer 的正则词面错误(行 5 列 20)。注意 parse -> OK:这是 ERROR 级错误,不中断解析(与 fatalError 的区别见 3.7)。

3.3 dynamic(按需校验)

./feature_test employee_bad.xml http://apache.org/xml/features/validation/dynamic
getFeature -> false
parse -> OK

dynamic 需与 validation 组合才有意义,单独设置无效果。组合行为用 dynamic_demo(固定 validation=true,dynamic 可变)在不带 xsi 引用的 employee_noxsi.xml 上实测:

./dynamic_demo employee_noxsi.xml false
./dynamic_demo employee_noxsi.xml true
ERROR /root/xtest/employee_noxsi.xml:2:21 no declaration found for element 'employee'
ERROR /root/xtest/employee_noxsi.xml:2:21 attribute 'id' is not declared for element 'employee'
ERROR /root/xtest/employee_noxsi.xml:3:9 no declaration found for element 'name'
ERROR /root/xtest/employee_noxsi.xml:4:8 no declaration found for element 'age'
parse done, errors=4
exit=0

parse done, errors=0
exit=0

结论实测成立:validation=true + dynamic=false 时,文档没有语法也强行校验,产生一批 "no declaration found" 噪音错误;validation=true + dynamic=true 时,找不到语法(无 DTD/XSD 引用)就跳过校验安静通过。混源环境(合法引用与无引用文档同池)推荐 dynamic=true。

3.4 Schema(XML Schema 校验)

./feature_test employee.xml http://apache.org/xml/features/validation/schema
./feature_test employee.xml http://apache.org/xml/features/validation/schema false
getFeature -> true
parse -> OK

getFeature -> true
setFeature(false) -> OK, readback false
parse -> OK

SAX2Reader 上 schema 特性默认 true;关闭后即使文档带 xsi 引用也不再加载 XSD(与 validation=true 组合时若实例无 DTD 会进入 3.3 的 no-declaration 噪音场景)。XSD 校验的开关组合实测矩阵:validation + schema 都 true 才走 Schema 校验。

3.5 schemaFullChecking(全量约束检查)

./feature_test employee.xml http://apache.org/xml/features/validation/schema-full-checking
./feature_test employee.xml http://apache.org/xml/features/validation/schema-full-checking true
getFeature -> false
parse -> OK

getFeature -> false
setFeature(true) -> OK, readback true
parse -> OK

默认 false。它是 Schema 组件层的严格检查开关(UPA 约束、类型派生合法性等 Schema Part 1 深层规则),开启后 Schema 本身也要被严格审一遍。本文的简单 schema 无深层违规,两种取值均解析通过;对复杂 schema(尤其第三方提供的)开启能提前暴露 schema 自身缺陷,代价是校验耗时增加。

3.6 identity-constraint-checking(身份约束检查)

./feature_test employee.xml http://apache.org/xml/features/validation/identity-constraint-checking
getFeature -> true
parse -> OK

实测默认 true(key/keyref/unique 身份约束默认生效)。注意不同版本对该默认值有过调整,升级后建议显式回读确认。

3.7 continue-after-fatal-error(致命错误续扫)

./feature_test bad1.xml http://apache.org/xml/features/continue-after-fatal-error
./caf_demo bad1.xml false
./caf_demo bad1.xml true
getFeature -> false
FATAL /root/xtest/bad1.xml:6:3 expected end of tag 'body'
parse -> aborted

FATAL /root/xtest/bad1.xml:6:3 expected end of tag 'body'
parse() returned normally: fatals=1 errors=0

FATAL /root/xtest/bad1.xml:6:3 expected end of tag 'body'
FATAL /root/xtest/bad1.xml:7:1 input ended before all started tags were ended; last tag started is 'note'
parse() returned normally: fatals=2 errors=0

三段实测把语义说清:默认 false 时,遇到第一个致命错误解析器停止扫描;设 true 后解析器恢复扫描继续报错(实测在行 7 列 1 又发现一处致命错误"输入在所有起始标签结束前结束")。caf_demo 的 Handler 故意不抛异常,因此 parse() 正常返回——是否继续由该特性决定,是否中断由 Handler 决定:若 Handler 在 fatalError 里抛出异常(如 error_handler.cpp 的实现),无论该特性取值都会中断。

3.8 未知 feature 的异常行为

./feature_test notes.xml http://apache.org/xml/features/validation/nonexistent true; echo "exit=$?"
getFeature -> SAXNotRecognizedException: Unknown Feature
exit=1

不存在的 feature URI 抛 SAXNotRecognizedException(消息 Unknown Feature),这是运行期识别参数拼错的兜底手段。存在但当前实现不支持的则抛 SAXNotSupportedException。

四、Property 参数实测

4.1 external-noNamespaceSchemaLocation(外部指定 XSD)

该属性把 Schema 从「文档内 xsi 引用」改为「程序显式指定」,常用于文档不可改、校验规则由部署方控制的场景。

NSLOC="http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation"
./property_test employee_bad.xml "$NSLOC" employee.xsd; echo "exit=$?"
./property_test notes.xml "$NSLOC" employee.xsd; echo "exit=$?"
setProperty -> OK, readback: employee.xsd
ERROR employee_bad.xml:5:20 value 'thirty' does not match regular expression facet '[+\-]?[0-9]+'
parse -> OK
exit=0

setProperty -> OK, readback: employee.xsd
ERROR /root/xtest/notes.xml:2:2 no declaration found for element 'notes'
ERROR /root/xtest/notes.xml:3:3 no declaration found for element 'note'
...(输出节选:每个未声明的元素/属性各一条)
parse -> OK
exit=0

第一条:程序层指定的 employee.xsd 同样校验出 thirty 类型错误,证明外部指定与文档内 xsi 引用等价生效(双保险场景两者都写亦可,程序侧优先级更高)。第二条故意把不匹配的 schema 强加给 notes.xml,每个未声明的元素/属性都报 no-declaration 错误——反向验证属性确实生效。对应的有命名空间版本属性为 http://apache.org/xml/properties/schema/external-schemaLocation,取值格式为「URI=XSD 路径」空格分隔对。

4.2 未知 property 的异常行为

./property_test notes.xml http://apache.org/xml/properties/nonexistent anything; echo "exit=$?"
setProperty -> SAXNotRecognizedException: Unknown Property
exit=1

与 feature 一致,未知 property 抛 SAXNotRecognizedException。

五、ErrorHandler 错误处理器实现

Xerces-C++ 把解析错误分三级回调:warning()(警告)、error()(校验错误,默认不中断)、fatalError()(致命错误,格式不良等,默认中断)。必须给解析器 setErrorHandler 一个自定义实现,否则错误会被吞掉(DefaultHandler 默认空实现)。实测实现 error_handler.cpp(节选自完整可编译源码):

class MyErrorHandler : public DefaultHandler {
public:
    int warnings = 0, errors = 0, fatals = 0;

    void warning(const SAXParseException& e) override   { ++warnings; report("WARNING", e); }
    void error(const SAXParseException& e) override     { ++errors;   report("ERROR", e); }
    void fatalError(const SAXParseException& e) override {
        ++fatals; report("FATAL", e);
        throw e;   // 抛出即中断;吞掉则配合 continue-after-fatal-error 继续扫
    }
    void resetErrors() override {}
private:
    void report(const char* level, const SAXParseException& e) {
        char* msg = XMLString::transcode(e.getMessage());
        char* sid = XMLString::transcode(e.getSystemId());
        std::cout << level << " | " << (sid ? sid : "(null)")
                  << ":" << e.getLineNumber() << ":" << e.getColumnNumber()
                  << " | " << msg << std::endl;
        XMLString::release(&msg);
        XMLString::release(&sid);
    }
};
// 装配:parser->setContentHandler(&handler); parser->setErrorHandler(&handler);

三种输入的实测输出(SAX2 + validation/schema 全开,外部 XSD 由 xsi 引用提供):

./error_handler employee.xml employee.xsd       # 合法文档
./error_handler employee_bad.xml employee.xsd   # XSD 数据类型非法
./error_handler bad1.xml                        # 格式不良
external noNamespaceSchemaLocation = employee.xsd
parse done: warnings=0 errors=0 fatals=0

external noNamespaceSchemaLocation = employee.xsd
ERROR | /root/xtest/employee_bad.xml:5:20 | value 'thirty' does not match regular expression facet '[+\-]?[0-9]+'
parse done: warnings=0 errors=1 fatals=0

FATAL | /root/xtest/bad1.xml:6:3 | expected end of tag 'body'
parse aborted by fatalError: warnings=0 errors=5 fatals=1

行为解读:合法文档全零;数据类型错误走 ERROR 通道(计数后继续解析完毕);未闭合标签走 FATAL 通道并因抛异常中断(此前的 no-grammar ERROR 已累计 5 条,因为该文档没有 xsi 引用而校验全开——再次呼应 3.3 的组合规则)。

六、CMake 构建选项实测

解析器还有一类「编译期参数」——CMake 选项。3.3.0 源码 cmake/ 目录实测定义了这些缓存变量:network(BOOL,默认 ON)、network-accessor(STRING,默认 socket)、message-loader(STRING,默认 inmemory)、transcoder(STRING,默认 gnuiconv)、file-manager(STRING,默认 POSIX)、mutex-manager(STRING,默认 standard)、threads/sse2(BOOL)等。

6.1 默认构建摘要(Ubuntu 22.04 实测)

cd /root/xerces-c-3.3.0 && mkdir -p build && cd build
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local ..
-- Xerces-C++ configuration summary
--   Version:                   3.3.0
--   Library interface version: 3.3
--   Installation directory:    /usr/local
--   Build shared libraries:    ON
--   File Manager:              POSIX
--   Mutex Manager:             standard
--   Transcoder:                gnuiconv
--   NetAccessor:               socket
--   Message Loader:            inmemory
--   XMLCh type:                char16_t

逐项含义:Transcoder 决定外部编码↔UTF-16 转换的实现(gnuiconv 走 glibc iconv,无外部依赖;icu 版支持更多编码但引入 ICU 依赖);Message Loader 决定错误消息来源;NetAccessor 决定能否在解析期拉取远程 DTD/Schema(socket 即可用 http:// 引用);File/Mutex Manager 是文件与锁的平台层,Linux 保持默认。

6.2 换装实测:transcoder=iconv + network=OFF 变体

cd /root/xerces-c-3.3.0
mkdir build-iconv2 && cd build-iconv2
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/opt/xerces-c-iconv2 \
      -Dtranscoder=iconv -Dmessage-loader=inmemory -Dnetwork=OFF ..
make -j$(nproc); echo "make exit=$?"
make install; echo "install exit=$?"

实测 configure 摘要变为 Transcoder: iconv / NetAccessor: none,make/install 均返回 0;用该前缀编译运行集成示例(-I/opt/xerces-c-iconv2/include -L/opt/xerces-c-iconv2/lib -Wl,-rpath,/opt/xerces-c-iconv2/lib)实测输出与默认版一致(elements=9 attributes=2 characters=89),证明变体功能等价且与 /usr/local 主版本可共存。

6.3 失败案例:message-loader=iconv 不能开箱即用(如实记录)

按同样方式构建 message-loader=iconv 变体(安装到 /opt/xerces-c-iconv),编译与安装成功,但运行时实测直接失败:

$ LD_LIBRARY_PATH=/opt/xerces-c-iconv/lib ./sax2_count notes.xml
Error during initialization! Message:
Cannot load message domain
exit=255

原因实测:iconv 消息加载器运行期要读消息目录文件 XercesMessages_en_US.cat(构建产物中位于 build/src/ 下),没有随系统环境就位时 XMLPlatformUtils::Initialize() 直接抛异常退出;实测即便把工作目录切到 .cat 所在位置仍不能自动找到(其检索路径依赖系统 catopen/NLSPATH 机制配置)。结论:生产构建保持默认 inmemory;确需本地化消息时另行部署目录文件并配置环境。

七、生产配置模板

按实测行为给出三个可复制的特性组合(SAX2 场景,均 setFeature 逐项设置):

① 大文件流式提取(不校验,最快):

parser->setFeature("http://xml.org/sax/features/validation", false);
parser->setFeature("http://apache.org/xml/features/validation/schema", false);
// namespaces 保持默认 true

② 混源严格校验(推荐默认档):validation=true + dynamic=true + schema=true + schemaFullChecking=true + 自定义 ErrorHandler(error 累计、fatal 中断)。实测行为:有语法引用的文档被校验,无引用的文档安静通过(3.3 节实测),坏数据报行列与具体词面错误(3.2 节实测)。

③ 容错修复工具(尽量多报错):在 ② 基础上 continue-after-fatal-error=true + fatalError 不抛异常(5 章/3.7 节实测:能收集到全部致命错误位置)。

性能侧经验(实测观察):本文所有 8 核并行编译约 15 秒、样例解析毫秒级,说明该库 CPU 密集段在转码与校验;大文件场景优先 SAX2 而非 DOM(DOM 建树内存与文档大小成正比),并把 schemaFullChecking 仅用于校验管线的构建期而非请求期。

八、常见问题

Q1:setFeature 后解析行为没变化? 先确认三个总开关的组合:校验生效需 validation + schema 同时为 true(XSD 场景);再确认文档确实携带 xsi 引用或已用 external-noNamespaceSchemaLocation 属性指定(4.1 实测的反例可以验证属性是否真的生效)。

Q2:错误信息一条都没看到? 没有 setErrorHandler 自定义实现,DefaultHandler 的空实现会吞掉 warning/error。参照第五章实现并在三个回调里输出。

Q3:怎么知道某个 feature/property 在当前版本存在? 头文件 util/XMLUni.hpp 有全部 URI 常量;运行期用 getFeature/getProperty 试探,不存在的会抛 SAXNotRecognizedException(3.8/4.2 实测);拼错 URI 同样抛该异常。

Q4:官方文档说 identity-constraint-checking 默认值与本文不一致? 以实测为准:3.3.0 在 Ubuntu 22.04 实测 getFeature 返回 true。该默认值历史上随版本调整过,升级版本后应回读确认。

九、参考链接