如何让xerces-c以最小体积开销输出含命名空间的XML?
解决Xerces-C 3.2.1中命名空间前缀重复声明与不必要元素前缀的问题
我之前在使用Xerces-C处理命名空间的时候也踩过几乎一模一样的坑,折腾了好一阵子才找到正确的姿势。结合你的需求,我给你梳理几个关键步骤,帮你生成符合理想格式的XML:
核心问题分析
你遇到的两个问题根源其实都是命名空间上下文的处理逻辑:
- 给元素加
myprefix:dim属性时,每个元素都重复声明xmlns:myprefix——这是因为Xerces默认会为每个需要命名空间的属性/元素自动声明前缀,除非它能找到上层节点已经存在的同名绑定。 - 在根节点声明前缀后,其他元素被自动加上
myprefix:前缀——这大概率是因为你创建子元素时没有正确指定它们的命名空间URI,Xerces误以为这些元素不属于默认命名空间,就用你声明的前缀来绑定了。
分步解决方案
1. 正确创建所有元素(必须指定命名空间URI)
所有属于my_namespace的元素,都必须用createElementNS创建,而不是普通的createElement。这一步是关键,否则Xerces会把无命名空间的元素和你声明的前缀绑定,导致元素被加上myprefix:前缀。
示例代码:
// 正确创建子元素,指定默认命名空间URI DOMElement* elem = doc->createElementNS(L"my_namespace", L"Elem"); DOMElement* elem1 = doc->createElementNS(L"my_namespace", L"Elem1"); DOMElement* elem2 = doc->createElementNS(L"my_namespace", L"Elem2");
2. 在根节点预先声明前缀绑定
在根节点添加xmlns:myprefix="my_namespace"的声明,这一步你之前的代码是对的,但建议在创建子元素之前完成:
// 给根节点添加前缀绑定,注意xmlns属性的命名空间是固定的http://www.w3.org/2000/xmlns/ root->setAttributeNS(L"http://www.w3.org/2000/xmlns/", L"xmlns:myprefix", L"my_namespace");
3. 配置序列化器的命名空间参数
Xerces的DOMLSSerializer有几个关键参数需要配置,确保它复用已有的前缀绑定,并且保留默认命名空间的声明,不会随意给元素加前缀:
DOMImplementationLS* lsImpl = dynamic_cast<DOMImplementationLS*>(impl); DOMLSSerializer* serializer = lsImpl->createLSSerializer(); DOMConfiguration* config = serializer->getDomConfig(); // 启用命名空间处理(默认是true,但显式设置更稳妥) config->setParameter(XMLUni::fgDOMNamespaceDeclarations, true); // 允许使用命名空间前缀,并且优先复用已声明的绑定 config->setParameter(XMLUni::fgDOMUseNamespacePrefixes, true); // 保留默认命名空间的声明,避免Xerces用前缀代替默认命名空间 config->setParameter(XMLUni::fgDOMDefaultNamespaceDeclaration, true); // 可选:控制空白字符,避免生成多余的换行/空格 config->setParameter(XMLUni::fgDOMElementContentWhitespace, false);
4. 正确设置带前缀的属性
设置属性时,仍然使用setAttributeNS,指定命名空间URI和带前缀的属性名:
elem->setAttributeNS(L"my_namespace", L"myprefix:dim", L"your_data_here");
这时候Xerces会发现根节点已经有myprefix到my_namespace的绑定,就不会在Elem元素上重复声明命名空间了。
完整示例代码
把这些步骤整合起来,完整的代码大概是这样:
#include <xercesc/dom/DOM.hpp> #include <xercesc/dom/DOMImplementationLS.hpp> #include <xercesc/util/XMLString.hpp> #include <iostream> XERCES_CPP_NAMESPACE_USE int main() { XMLPlatformUtils::Initialize(); // 创建DOM实现和文档 DOMImplementation* impl = DOMImplementationRegistry::getDOMImplementation(L"LS"); DOMDocument* doc = impl->createDocument( L"my_namespace", L"Test", nullptr ); DOMElement* root = doc->getDocumentElement(); // 添加xml、xsi命名空间和schemaLocation root->setAttributeNS(L"http://www.w3.org/2000/xmlns/", L"xmlns:xml", L"http://www.w3.org/XML/1998/namespace"); root->setAttributeNS(L"http://www.w3.org/2000/xmlns/", L"xmlns:xsi", L"http://www.w3.org/2001/XMLSchema-instance"); root->setAttributeNS(L"http://www.w3.org/2001/XMLSchema-instance", L"xsi:schemaLocation", L"my_namespace myschema.xsd"); // 预先在根节点声明myprefix绑定 root->setAttributeNS(L"http://www.w3.org/2000/xmlns/", L"xmlns:myprefix", L"my_namespace"); // 创建子元素(全部用createElementNS指定命名空间) DOMElement* elem = doc->createElementNS(L"my_namespace", L"Elem"); elem->setAttributeNS(L"my_namespace", L"myprefix:dim", L"sample_data"); root->appendChild(elem); DOMElement* elem1 = doc->createElementNS(L"my_namespace", L"Elem1"); root->appendChild(elem1); DOMElement* elem2 = doc->createElementNS(L"my_namespace", L"Elem2"); root->appendChild(elem2); // 配置序列化器 DOMImplementationLS* lsImpl = dynamic_cast<DOMImplementationLS*>(impl); DOMLSSerializer* serializer = lsImpl->createLSSerializer(); DOMConfiguration* config = serializer->getDomConfig(); config->setParameter(XMLUni::fgDOMNamespaceDeclarations, true); config->setParameter(XMLUni::fgDOMUseNamespacePrefixes, true); config->setParameter(XMLUni::fgDOMDefaultNamespaceDeclaration, true); config->setParameter(XMLUni::fgDOMElementContentWhitespace, false); // 生成并输出XML XMLCh* xmlStr = serializer->writeToString(doc); char* xmlChar = XMLString::transcode(xmlStr); std::cout << xmlChar << std::endl; // 释放资源 XMLString::release(&xmlChar); XMLString::release(&xmlStr); serializer->release(); doc->release(); XMLPlatformUtils::Terminate(); return 0; }
验证结果
运行这段代码后,生成的XML应该就是你想要的理想格式:
<?xml version="1.0" encoding="UTF-16" standalone="yes"?> <Test xmlns="my_namespace" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="my_namespace myschema.xsd" xmlns:myprefix="my_namespace"> <Elem myprefix:dim="sample_data"/> <Elem1/> <Elem2/> </Test>
内容的提问来源于stack exchange,提问作者x y
相关产品推荐
相关产品推荐

