Apache Xerces-C++ 在 Ubuntu 22.04 集成使用指南
在 Ubuntu 22.04 上从零完成 Xerces-C++ 3.3.0 的 CMake 源码编译安装(含 apt 3.2.3 对照与多版本共存),并实测 SAX2 计数与 DOM 解析两个完整 C++ 程序的编译运行。
Apache Xerces-C++ 在 Ubuntu 22.04 集成使用指南
目标:在 Ubuntu 22.04 服务器上从 0 到 1 完成 Xerces-C++ 3.3.0 的 CMake 源码编译安装,写通 SAX2 与 DOM 两个可直接编译运行的 C++ 集成程序,并给出 apt 版 3.2.3 的对照与多版本共存方案。全文命令与回显均实测取得。
一、环境信息
本文实测环境如下,所有命令在 /root 下执行:
- 操作系统:Ubuntu 22.04(x86_64)
- 内核工具链:gcc/g++ 11.4.0、cmake 3.22.1(本文安装)
- Xerces-C++ 主线版本:3.3.0(源码编译,安装前缀
/usr/local) - Xerces-C++ 对照版本:3.2.3+debian-3ubuntu0.1(Ubuntu 22.04 仓库 libxerces-c-dev)
- 工作目录:
/root/xerces-c-3.3.0(源码)、/root/xerces-c-3.3.0/build(构建目录)、/root/xtest(集成示例) - 官方源码包:https://dlcdn.apache.org/xerces/c/3/sources/xerces-c-3.3.0.tar.gz
💡 技术拓展:Xerces-C++ 是库(无独立进程、无端口、无守护服务),"部署"形态就是把
.so与头文件放进系统路径、把-lxerces-c接入编译命令。因此本篇的主线是「编译安装 → 编译链接 → 运行验证」,与中间件类软件的 systemd 托管流程不同。
二、前置条件
先装齐编译工具链与构建工具,并核对版本。安装 build-essential(含 gcc/g++/make)、cmake、pkg-config、curl:
apt-get update
DEBIAN_FRONTEND=noninteractive apt-get install -y build-essential cmake pkg-config curl
安装完成后核对版本(实测输出):
gcc --version | head -1
cmake --version | head -1
g++ --version | head -1
gcc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0
cmake version 3.22.1
g++ (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0
cmake 3.22.1 满足 Xerces-C++ 3.3.0 的最低要求(其 CMakeLists 要求 cmake_minimum_required(VERSION 3.12.0)),gcc 11.4 支持 C++14,满足其默认 CMAKE_CXX_STANDARD 14。
三、依赖引入:两种安装方案
Xerces-C++ 3.3.0 官方仅提供源码分发,Ubuntu 22.04 仓库提供 3.2.3 旧版。本文以「CMake 源码编译 3.3.0」为主线方案,apt 仓库 3.2.3 作为对照方案,两方案实测对比:
| 方案 | 版本 | 是否需联网编译 | 安装位置 | 适用 |
|---|---|---|---|---|
| A:CMake 源码编译(主线) | 3.3.0 | 需编译环境与源码下载 | /usr/local(可自定前缀) | 要最新版/定制构建选项 |
| B:apt 仓库安装(对照) | 3.2.3+debian | 否,apt 直装 | /usr/lib/x86_64-linux-gnu | 内网/快速原型/仅用系统包 |
关键差异解读:两版 soname 不同(libxerces-c-3.3.so vs libxerces-c-3.2.so),C++ 符号命名空间也随之不同(xercesc_3_3 vs xercesc_3_2),头文件与库必须同套来源,混用会在链接期报大量 undefined reference(见第八章实测)。
3.1 方案 A:CMake 源码编译安装 Xerces-C++ 3.3.0
3.1.1 下载与校验
cd /root
curl -s -o xerces-c-3.3.0.tar.gz https://dlcdn.apache.org/xerces/c/3/sources/xerces-c-3.3.0.tar.gz
ls -l xerces-c-3.3.0.tar.gz
sha256sum xerces-c-3.3.0.tar.gz
实测输出:
-rw-r--r-- 1 root root 7568765 Oct 16 00:23 xerces-c-3.3.0.tar.gz
9555f1d06f82987fbb4658862705515740414fd34b4db6ad2ed76a2dc08d3bde xerces-c-3.3.0.tar.gz
官方下载页同时提供 .sha256 校验文件可交叉核对;tar -tzf xerces-c-3.3.0.tar.gz | head -5 实测可列出包内顶层目录 xerces-c-3.3.0/。
3.1.2 解压源码
cd /root
tar -xzf xerces-c-3.3.0.tar.gz
ls /root/xerces-c-3.3.0 | head -12
实测输出(源码包含库源码、样例与测试):
CMakeLists.txt CREDITS INSTALL KEYS LICENSE Makefile.am Makefile.in
NOTICE README aclocal.m4 cmake config
顶层 samples/、tests/、src/ 三个目录都会参与主构建(CMakeLists 第 167~170 行实测 add_subdirectory(doc/src/tests/samples)),一次 make 同时产出库、16 个样例程序与测试程序。
3.1.3 CMake 配置
独立 build 目录配置,指定安装前缀 /usr/local、Release 构建、动态库:
cd /root/xerces-c-3.3.0
mkdir -p build && cd build
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local ..
实测配置摘要(节选自完整配置输出,完整输出约 320 行,含各探测项 Success/Failed 明细):
-- Xerces-C++ configuration summary
-- --------------------------------
--
-- Version: 3.3.0
-- Library interface version: 3.3
--
-- Installation directory: /usr/local
-- C compiler: /usr/bin/cc
-- C++ compiler: /usr/bin/c++
--
-- Build shared libraries: ON
-- Path delimiters: "/"
-- File Manager: POSIX
-- Mutex Manager: standard
-- Transcoder: gnuiconv
-- NetAccessor: socket
-- Message Loader: inmemory
-- XMLCh type: char16_t
-- Configuring done
-- Generating done
-- Build files have been written to: /root/xerces-c-3.3.0/build
逐项说明:XMLCh type: char16_t 表示内部字符是 C++11 char16_t(见第五章转码陷阱);Transcoder: gnuiconv 用 glibc 的 iconv 做编码转换;Message Loader: inmemory 把错误消息编译进库;NetAccessor: socket 支持解析时拉取远程 DTD/Schema。这些组件均可换装,选项明细见《Apache Xerces-C++ 3.3.0 配置参数详解》。
3.1.4 编译与安装
cd /root/xerces-c-3.3.0/build
make -j$(nproc)
time make -j$(nproc) # 实测机器为 8 核,如需单独计时可再执行
make install
ldconfig
实测(8 核并行):
[100%] Linking CXX executable XSValueTest
[100%] Built target XSValueTest
real 0m15.035s
user 1m25.684s
sys 0m12.004s
make install 末尾实测(节选):
-- Installing: /usr/local/bin/SEnumVal
-- Set runtime path of "/usr/local/bin/SEnumVal" to ""
-- Installing: /usr/local/bin/StdInParse
-- Set runtime path of "/usr/local/bin/StdInParse" to ""
-- Installing: /usr/local/bin/XInclude
-- Set runtime path of "/usr/local/bin/XInclude" to ""
⚠️ 注意:
make install后必须执行ldconfig刷新动态库缓存,否则运行时可能找不到libxerces-c-3.3.so。Ubuntu 默认/etc/ld.so.conf.d/libc.conf已含/usr/local/lib,无需手工加路径。
3.1.5 安装结果验证
pkg-config --modversion xerces-c
ls -l /usr/local/lib/ | grep xerces
ls /usr/local/include/xercesc/
cat /usr/local/lib/pkgconfig/xerces-c.pc
实测输出:
3.3.0
-rw-r--r-- 1 root root 5163280 Oct 16 00:24 /usr/local/lib/libxerces-c-3.3.so
lrwxrwxrwx 1 root root 18 Oct 16 00:27 /usr/local/lib/libxerces-c.so -> libxerces-c-3.3.so
dom framework internal parsers sax sax2 util validators xinclude
prefix=/usr/local
exec_prefix=/usr/local
libdir=/usr/local/lib
includedir=/usr/local/include
Name: Xerces-C++
Description: Validating XML parser library for C++
Version: 3.3.0
Libs: -L/usr/local/lib -lxerces-c
Libs.private:
Cflags: -I/usr/local/include
布局解读:头文件按模块分目录(sax2/、dom/、parsers/ 等),动态库主版本号进文件名(libxerces-c-3.3.so),libxerces-c.so 是给链接器用的无版本软链。pkg-config 文件让编译命令可写成 g++ xxx.cpp $(pkg-config --cflags --libs xerces-c),与手写 -I/usr/local/include -L/usr/local/lib -lxerces-c 等价(本文示例统一用手写形式,两条路均实测可用)。
3.2 方案 B:apt 仓库安装(3.2.3 对照)
DEBIAN_FRONTEND=noninteractive apt-get install -y libxerces-c-dev
dpkg -l | grep libxerces-c
实测输出:
ii libxerces-c-dev:amd64 3.2.3+debian-3ubuntu0.1 amd64 ...
ii libxerces-c3.2:amd64 3.2.3+debian-3ubuntu0.1 amd64 ...
apt 版头文件与库位置(实测):
$ ls /usr/include/xercesc/ | head -6
dom
framework
internal
parsers
sax
sax2
$ ls /usr/lib/x86_64-linux-gnu/libxerces-c*
/usr/lib/x86_64-linux-gnu/libxerces-c-3.2.so
/usr/lib/x86_64-linux-gnu/libxerces-c.a
/usr/lib/x86_64-linux-gnu/libxerces-c.so
$ PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig pkg-config --modversion xerces-c
3.2.3
apt 版 API 与 3.3.0 基本兼容,本文第五、六章示例在 3.2.3 下同样可编译(第八章给出实测);差异主要在个别新 feature 与缺陷修复。
3.3 两种方案的卸载方式
方案 A 源码安装无独立卸载目标,用 CMake 生成的 install_manifest.txt(实测 1708 行清单)反查删除:
cd /root/xerces-c-3.3.0/build
head -3 install_manifest.txt # 先抽查清单内容
cat install_manifest.txt | xargs rm -f # 确认无误后删除安装文件
rm -f /usr/local/lib/libxerces-c.so # 软链如残留单独删
ldconfig
实测 manifest 首几条:
/usr/local/lib/pkgconfig/xerces-c.pc
/usr/local/share/doc/xerces-c/html/createdoc-3.html
/usr/local/share/doc/xerces-c/html/mailing-lists.html
方案 B 用 apt 卸载(实测 dry-run 确认动作):
apt-get remove --dry-run libxerces-c-dev
# 实测确认输出: Remv libxerces-c-dev [3.2.3+debian-3ubuntu0.1]
apt-get remove -y libxerces-c-dev
四、核心 API 与初始化流程
所有 Xerces-C++ 程序都遵循同一生命周期:Initialize → 构造解析器 → 解析 → 释放 → Terminate。本节用最小片段说明,完整可编译程序见第五、六章。
XMLPlatformUtils::Initialize(); // 全局初始化(进程内一次)
// ... 创建解析器、解析、处理 ...
XMLPlatformUtils::Terminate(); // 全局清理,与 Initialize 成对
初始化失败时抛 XMLException,消息是 XMLCh*,转成 char* 打印后必须 release:
try {
XMLPlatformUtils::Initialize();
} catch (const XMLException& e) {
char* msg = XMLString::transcode(e.getMessage());
std::cerr << "Initialize failed: " << msg << std::endl;
XMLString::release(&msg);
return 1;
}
4.1 XMLCh 转码陷阱(实测踩坑记录)
Xerces-C++ 内部字符类型 XMLCh 在 3.x 就是 char16_t(UTF-16 编码单元),API 出入参全部是 const XMLCh*。char* 字符串不会隐式转换,必须用 XMLString::transcode() 显式互转。这是新接 Xerces-C++ 的第一大坑,本节全部为真实编译/运行记录。
坑 1:char 直接传给 API,编译失败。* 实测把 argv[2](char*)直接传 getFeature():
bool cur = parser->getFeature(argv[2]); // 错误写法
编译报错(g++ 11.4.0 实测):
error: cannot convert 'char*' to 'const XMLCh*' {aka 'const char16_t*'}
135 | virtual bool getFeature(const XMLCh* const name) const = 0;
正确写法是先转码、用完释放:
XMLCh* featureName = XMLString::transcode(argv[2]);
bool cur = parser->getFeature(featureName);
XMLString::release(&featureName);
坑 2:transcode 返回的指针忘记 release,内存泄漏。 XMLString::transcode 在堆上分配内存(可传入自定义 MemoryManager),每次调用都要配对一次 release;release 会把传入指针置空,正好用来确认释放。专项演示程序 transcode_demo.cpp(完整源码见第六章清单)实测输出:
step1: transcode("employee") -> XMLCh* ok, len=8
step2: native = employee
step3: after release xmlName=0 native=0
step4: 1000000 paired transcode/release done, total len=4000000
step3 打印 0 证明 release 后指针已置空;step4 循环 100 万次成对 transcode/release 验证泄漏在循环场景同样可控(每轮分配即释放)。
坑 3:DOM 侧同型陷阱。 doc->getElementsByTagName(transcode("note")) 这类调用若写成内联 getElementsByTagName(XMLString::transcode("note")),返回后没有指针可 release,直接泄漏。正确写法(第六章 dom_parse.cpp 实测采用):
XMLCh* tag = XMLString::transcode("note");
DOMNodeList* notes = doc->getElementsByTagName(tag);
XMLString::release(&tag);
五、集成示例一:SAX2 计数程序
SAX2 是事件推送模型:解析器边扫边回调 Handler 的 startElement/characters/endElement,适合大文件与只需提取信息的场景。本例实现一个统计元素/属性/字符数的完整程序,先给测试数据,再给源码、编译与三种输入的运行结果。
5.1 准备测试数据
mkdir -p /root/xtest && cd /root/xtest
cat > notes.xml <<'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<notes>
<note id="1">
<to>Alice</to>
<from>Bob</from>
<body>Meeting at 10am</body>
</note>
<note id="2">
<to>Carol</to>
<from>Dave</from>
<body>Lunch tomorrow</body>
</note>
</notes>
EOF
再准备一份格式不良文档(<body> 未闭合)用于触发 fatal error:
cat > bad1.xml <<'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<notes>
<note id="3">
<to>Eve</to>
<body>Missing closing tag
</notes>
EOF
5.2 完整源码 sax2_count.cpp
// SAX2 计数解析示例:统计元素、属性与字符数据
#include <xercesc/sax2/SAX2XMLReader.hpp>
#include <xercesc/sax2/XMLReaderFactory.hpp>
#include <xercesc/sax2/DefaultHandler.hpp>
#include <xercesc/sax2/Attributes.hpp> // 不带会报 incomplete type
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/PlatformUtils.hpp>
#include <iostream>
XERCES_CPP_NAMESPACE_USE
class CountHandler : public DefaultHandler {
public:
unsigned long elements = 0;
unsigned long attributes = 0;
unsigned long charCount = 0; // 注意:不要命名成 characters,会与方法冲突
void startElement(const XMLCh* const uri, const XMLCh* const localname,
const XMLCh* const qname, const Attributes& attrs) override {
(void)uri; (void)localname; (void)qname;
++elements;
attributes += attrs.getLength();
}
void characters(const XMLCh* const chars, const XMLSize_t length) override {
(void)chars;
charCount += length;
}
void fatalError(const SAXParseException& e) override {
char* msg = XMLString::transcode(e.getMessage());
std::cerr << "Fatal: line " << e.getLineNumber()
<< ", column " << e.getColumnNumber()
<< ": " << msg << std::endl;
XMLString::release(&msg);
throw e; // 抛出中断解析;吞掉则配合 continue-after-fatal-error 继续
}
};
int main(int argc, char* argv[]) {
if (argc < 2) {
std::cerr << "Usage: " << argv[0] << " <xml-file>" << std::endl;
return 1;
}
try {
XMLPlatformUtils::Initialize();
} catch (const XMLException& e) {
char* msg = XMLString::transcode(e.getMessage());
std::cerr << "Initialize failed: " << msg << std::endl;
XMLString::release(&msg);
return 1;
}
int rc = 0;
{
SAX2XMLReader* parser = XMLReaderFactory::createXMLReader();
CountHandler handler;
parser->setContentHandler(&handler);
parser->setErrorHandler(&handler);
try {
parser->parse(argv[1]);
std::cout << "elements=" << handler.elements
<< " attributes=" << handler.attributes
<< " characters=" << handler.charCount << std::endl;
} catch (const XMLException& e) {
char* msg = XMLString::transcode(e.getMessage());
std::cerr << "XMLException: " << msg << std::endl;
XMLString::release(&msg);
rc = 1;
} catch (const SAXParseException& e) {
char* msg = XMLString::transcode(e.getMessage());
std::cerr << "SAXParseException: " << msg << std::endl;
XMLString::release(&msg);
rc = 1;
}
delete parser;
}
XMLPlatformUtils::Terminate();
return rc;
}
源码要点:DefaultHandler 是 ContentHandler/ErrorHandler 的空实现集合,只覆写关心的回调;XMLReaderFactory::createXMLReader() 返回裸指针,用完 delete;characters() 的长度类型是 XMLSize_t。
5.3 编译与运行
cd /root/xtest
g++ -std=c++14 sax2_count.cpp -o sax2_count -lxerces-c
./sax2_count notes.xml
./sax2_count bad1.xml; echo "exit=$?"
ldd ./sax2_count | grep xerces
实测输出:
$ g++ -std=c++14 sax2_count.cpp -o sax2_count -lxerces-c
$ ./sax2_count notes.xml
elements=9 attributes=2 characters=89
$ ./sax2_count bad1.xml; echo "exit=$?"
Fatal: line 6, column 3: expected end of tag 'body'
SAXParseException: expected end of tag 'body'
exit=1
$ ldd ./sax2_count | grep xerces
libxerces-c-3.3.so => /usr/local/lib/libxerces-c-3.3.so (0x0000715221a6f000)
结果解读:notes.xml 有 9 个元素(notes + 2×note + 每组 3 个子元素)、2 个 id 属性、89 个字符数据;bad1.xml 报出精确行列(行 6 列 3)并以非 0 退出。首次编译实际踩过的两个坑,已在上文源码注释中标出——成员变量命名 characters 与虚函数同名冲突(报错 conflicts with a previous declaration),以及漏包含 sax2/Attributes.hpp(报错 invalid use of incomplete type 'const class xercesc_3_3::Attributes')。
六、集成示例二:DOM 解析程序
DOM 把整棵树留在内存,适合需要随机访问、修改、回写的场景。本例用 XercesDOMParser 加载 notes.xml,枚举全部 note 元素并输出 id 属性与 <to> 文本。
6.1 完整源码 dom_parse.cpp
// DOM 解析示例:加载 XML、遍历节点树、读取属性与文本
#include <xercesc/parsers/XercesDOMParser.hpp>
#include <xercesc/dom/DOM.hpp>
#include <xercesc/sax/ErrorHandler.hpp>
#include <xercesc/sax/SAXParseException.hpp> // ErrorHandler.hpp 只有前向声明
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/PlatformUtils.hpp>
#include <iostream>
#include <string>
XERCES_CPP_NAMESPACE_USE
// XMLCh* -> std::string 的小工具:转码 + release 成对出现
static std::string toStr(const XMLCh* in) {
std::string out;
if (in != nullptr) {
char* t = XMLString::transcode(in);
out = t;
XMLString::release(&t);
}
return out;
}
class DOMErrorLogger : public ErrorHandler {
public:
void warning(const SAXParseException& e) override { report("WARNING", e); }
void error(const SAXParseException& e) override { report("ERROR", e); }
void fatalError(const SAXParseException& e) override{ report("FATAL", e); }
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::cerr << level << " at " << (sid ? sid : "(null)")
<< ":" << e.getLineNumber()
<< ":" << e.getColumnNumber() << " - " << msg << std::endl;
XMLString::release(&msg);
XMLString::release(&sid);
}
};
int main(int argc, char* argv[]) {
if (argc < 2) {
std::cerr << "Usage: " << argv[0] << " <xml-file>" << std::endl;
return 1;
}
try {
XMLPlatformUtils::Initialize();
} catch (const XMLException& e) {
char* msg = XMLString::transcode(e.getMessage());
std::cerr << "Initialize failed: " << msg << std::endl;
XMLString::release(&msg);
return 1;
}
int rc = 0;
{
XercesDOMParser parser;
parser.setValidationScheme(XercesDOMParser::Val_Auto); // 有 DTD/Schema 就校验
parser.setDoNamespaces(false);
DOMErrorLogger logger;
parser.setErrorHandler(&logger);
try {
parser.parse(argv[1]);
DOMDocument* doc = parser.getDocument();
DOMElement* root = doc->getDocumentElement();
std::cout << "root element: " << toStr(root->getTagName()) << std::endl;
XMLCh* tag = XMLString::transcode("note");
DOMNodeList* notes = doc->getElementsByTagName(tag);
XMLString::release(&tag);
const XMLSize_t n = notes->getLength();
std::cout << "note count: " << n << std::endl;
for (XMLSize_t i = 0; i < n; ++i) {
DOMElement* note = static_cast<DOMElement*>(notes->item(i));
XMLCh* idName = XMLString::transcode("id");
const XMLCh* id = note->getAttribute(idName);
XMLString::release(&idName);
std::string to;
DOMNode* child = note->getFirstChild();
while (child != nullptr) {
if (child->getNodeType() == DOMNode::ELEMENT_NODE &&
toStr(child->getNodeName()) == "to") {
to = toStr(child->getTextContent());
break;
}
child = child->getNextSibling();
}
std::cout << "note[" << i << "] id=" << toStr(id)
<< " to=" << to << std::endl;
}
} catch (const XMLException& e) {
char* msg = XMLString::transcode(e.getMessage());
std::cerr << "XMLException: " << msg << std::endl;
XMLString::release(&msg);
rc = 1;
} catch (const DOMException& e) {
char* msg = XMLString::transcode(e.msg);
std::cerr << "DOMException: " << msg << std::endl;
XMLString::release(&msg);
rc = 1;
}
}
XMLPlatformUtils::Terminate();
return rc;
}
6.2 编译与运行
cd /root/xtest
g++ -std=c++14 dom_parse.cpp -o dom_parse -lxerces-c
./dom_parse notes.xml
实测输出:
root element: notes
note count: 2
note[0] id=1 to=Alice
note[1] id=2 to=Carol
实测排障记录:dom_parse.cpp 首次编译报 invalid use of incomplete type 'const class xercesc_3_3::SAXParseException'——sax/ErrorHandler.hpp 只前向声明了 SAXParseException,覆写其回调并调用 e.getMessage() 的翻译单元必须显式包含 sax/SAXParseException.hpp,补上后编译通过。
本文自写示例程序统一存放在 /root/xtest、按同一模式编译(g++ -std=c++14 xxx.cpp -o xxx -lxerces-c),实测全部通过:sax2_count(SAX2 计数)、dom_parse(DOM 遍历)、transcode_demo(转码规范)、error_handler(错误分级 + 外部 XSD)、feature_test/property_test(特性探测,用于《Apache Xerces-C++ 3.3.0 配置参数详解》)、caf_demo(fatal 错误续扫对比)。
七、多版本共存与升级
生产机器常同时存在 apt 版(3.2.3)与源码版(3.3.0)。本章为实测矩阵,先看动态库注册结果(ldconfig -p | grep -i xerces):
libxerces-c-3.3.so (libc6,x86-64) => /usr/local/lib/libxerces-c-3.3.so
libxerces-c-3.2.so (libc6,x86-64) => /lib/x86_64-linux-gnu/libxerces-c-3.2.so
7.1 失败案例:头文件与库跨版本混链(实测)
3.3 头文件编译 + 错误地链接 3.2 库(g++ -std=c++14 sax2_count.cpp -o t -l:libxerces-c-3.2.so),链接器实测报错(节选):
/usr/bin/ld: sax2_count.cpp:(.text+0xa9): undefined reference to `xercesc_3_3::XMLUni::fgXercescDefaultLocale'
/usr/bin/ld: sax2_count.cpp:(.text+0xc2): undefined reference to `xercesc_3_3::XMLPlatformUtils::fgMemoryManager'
/usr/bin/ld: sax2_count.cpp:(.text+0xcf): undefined reference to `xercesc_3_3::XMLReaderFactory::createXMLReader(xercesc_3_3::MemoryManager*, xercesc_3_3::XMLGrammarPool*)'
collect2: error: ld returned 1 exit status
原因:Xerces-C++ 把版本号编进了 C++ 命名空间(xercesc_3_3 / xercesc_3_2),头文件与库版本不一致时符号全部对不上。且头文件查找顺序 /usr/local/include 先于 /usr/include,装了源码版后默认命中的永远是 3.3 头文件——pkg-config 的 --cflags 会因 /usr/include 是默认目录而将其过滤掉,指望它改不了顺序(实测确认)。
7.2 正确姿势 A:源码版装独立前缀(推荐多版本场景)
重建一例装到 /opt/xerces-c-iconv2(同时演示换 Transcoder 为 iconv):
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) && make install
(实测 make exit=0、install exit=0。)用它的头文件库文件单独编译,加 rpath 固定运行期查找:
cd /root/xtest
g++ -std=c++14 sax2_count.cpp -o sax2_count_iconv2 \
-I/opt/xerces-c-iconv2/include -L/opt/xerces-c-iconv2/lib \
-lxerces-c -Wl,-rpath,/opt/xerces-c-iconv2/lib
./sax2_count_iconv2 notes.xml
ldd ./sax2_count_iconv2 | grep xerces
实测输出:
elements=9 attributes=2 characters=89
libxerces-c-3.3.so => /opt/xerces-c-iconv2/lib/libxerces-c-3.3.so (0x00007dad0ad2e000)
两个 3.3.0 安装(/usr/local 与 /opt 前缀)互不干扰,运行期由 rpath 决定加载谁。
7.3 正确姿势 B:只用 apt 版时移开源码版头文件
若某程序必须用 apt 3.2.3 的头文件与库(命名空间 xercesc_3_2),把 /usr/local/include/xercesc 暂时移开再用 pkg-config 编译,实测通过:
mv /usr/local/include/xercesc /usr/local/include/xercesc.hidden
cd /root/xtest
g++ -std=c++14 sax2_count.cpp -o sax2_count_32 \
$(PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig pkg-config --cflags --libs xerces-c)
ldd ./sax2_count_32 | grep xerces
./sax2_count_32 notes.xml
mv /usr/local/include/xercesc.hidden /usr/local/include/xercesc # 用完还原
实测输出:
compile 通过
libxerces-c-3.2.so => /lib/x86_64-linux-gnu/libxerces-c-3.2.so (0x00007a45f886a000)
elements=9 attributes=2 characters=89
7.4 升级建议
升级到新版(如未来 3.3.1)遵循同一路径:官网确认版本 → 下载校验 → 新 build 目录 configure(前缀可保持 /usr/local 或换独立前缀)→ make install → ldconfig → 用 5.2/6.2 的程序回归验证。旧库 soname 不同即可共存回滚;同一 soname 内升级(3.3.0→3.3.x)建议先卸旧(见 3.3 节 manifest 法)再装新。
八、常见问题
Q1:编译报 cannot convert 'char*' to 'const XMLCh*' {aka 'const char16_t*'}?
API 全部要求 const XMLCh*(char16_t),对 char* 用 XMLString::transcode() 转入、对返回的 XMLCh* 用 XMLString::transcode() 转出,各配一次 XMLString::release()(见 4.1 节实测)。
Q2:链接报大量 undefined reference to xercesc_3_3::...?
头文件与库版本不匹配。实测有一个隐蔽触发方式:先装了源码版、又补装 apt 版 libxerces-c-dev 后,裸 -lxerces-c 会命中 /lib/x86_64-linux-gnu/libxerces-c-3.2.so(ld --verbose 实测链接器搜索序 /lib/x86_64-linux-gnu 先于 /usr/local/lib),而头文件仍命中 /usr/local/include 的 3.3 版,两端命名空间(xercesc_3_2 vs xercesc_3_3)对不上。确认 -I 与 -L/-l 来自同一安装(ldd 看实际加载路径),或统一改用 g++ xxx.cpp $(pkg-config --cflags --libs xerces-c) 显式带上 -L/usr/local/lib,也可按第七章 A/B 两种姿势隔离两套版本。
Q3:程序运行报 Cannot load message domain / Could not open catalog: XercesMessages_en_US.cat 后退出(exit=255)?
该现象在「Message Loader 换成 iconv」的构建里实测出现:iconv 消息加载器运行期依赖消息目录文件(构建产物中位于 build/src/XercesMessages_en_US.cat),且未随默认环境就位。生产构建保持默认 -Dmessage-loader=inmemory(消息编译进库)即可避免,详细选项见《Apache Xerces-C++ 3.3.0 配置参数详解》。
Q4:make install 后运行程序提示找不到 libxerces-c-3.3.so?
未刷新库缓存。执行 ldconfig 后重试;若安装到非默认前缀(如 /opt/xxx),需把该前缀的 lib 目录加入 ld.so.conf.d 再 ldconfig,或编译时加 -Wl,-rpath,<prefix>/lib(7.2 实测姿势)。
Q5:invalid use of incomplete type 'const class Attributes' 或 SAXParseException?
只包含了前向声明的头。补 #include <xercesc/sax2/Attributes.hpp>、#include <xercesc/sax/SAXParseException.hpp>(5.2/6.1 实测踩坑后已写入示例)。
Q6:成员变量命名 characters 编译报 conflicts with a previous declaration?
与 DefaultHandler 的虚函数 characters(const XMLCh*, XMLSize_t) 同名冲突,换名(如 charCount)即可,见 5.2 源码注释。
九、参考链接
- 官方安装文档(INSTALL/README 随源码包分发):https://xerces.apache.org/xerces-c/install-3.html
- 官方下载页:https://xerces.apache.org/xerces-c/download.cgi
- 官方编程指南(初始化/转码/DOM/SAX2):https://xerces.apache.org/xerces-c/program-3.html
- 本文配套篇:《Apache Xerces-C++ 介绍与选型指南》《Apache Xerces-C++ 3.3.0 配置参数详解》《Apache Xerces-C++ 常用 CLI 命令与工具详解》