如何配置Poco SAX解析器以保留XML原始字符?
如何让Poco SAX解析器保留XML原始编码字符
当然可以实现这个需求,但得先明确一点:标准SAX解析器的核心职责之一就是解析XML中的实体引用(比如<)并将其转换为对应的原始字符(<),这是符合XML规范的默认行为。所以要保留原始的编码字符,我们需要绕开默认的实体解析逻辑,或者使用更底层的XML处理工具。
下面是两种可行的方案:
方案一:使用Poco XMLTokenizer(推荐)
Poco提供了XMLTokenizer这个底层工具,它会把XML分解成一个个token(比如起始标签、字符数据、实体引用等),并且不会自动解码实体引用。这样你可以直接捕获到原始的<字符串,同时还能处理XML的结构。
举个简单的代码示例:
#include "Poco/XML/XMLTokenizer.h" #include "Poco/XML/InputSource.h" #include <iostream> using namespace Poco::XML; int main() { // 示例XML内容 std::string xmlContent = "<root>Example text with < encoded character</root>"; InputSource input(xmlContent); XMLTokenizer tokenizer(input); Token currentToken; while ((currentToken = tokenizer.nextToken()).type() != Token::TOKEN_EOF) { switch (currentToken.type()) { case Token::TOKEN_START_TAG: std::cout << "Found start tag: " << currentToken.asString() << "\n"; break; case Token::TOKEN_CHARACTERS: std::cout << "Found text: " << currentToken.asString() << "\n"; break; case Token::TOKEN_ENTITY_REFERENCE: // 这里直接拿到原始的实体引用字符串 std::cout << "Found entity reference: " << currentToken.asString() << "\n"; break; case Token::TOKEN_END_TAG: std::cout << "Found end tag: " << currentToken.asString() << "\n"; break; // 可以根据需求处理其他token类型(比如注释、DOCTYPE等) } } return 0; }
运行这段代码后,你会看到输出里直接保留了<,而不是被解码成<。
方案二:自定义EntityResolver(仅适用于自定义实体,预定义实体有限制)
如果你坚持要用标准SAX的ContentHandler,那可以尝试自定义EntityResolver,但要注意:预定义实体(<、>等)是XML规范内置的,Poco的SAX解析器会自动处理它们,不会调用EntityResolver。所以这个方案只适用于你自己定义的实体,对预定义实体无效。
示例代码(仅作参考,不解决预定义实体问题):
#include "Poco/XML/SAXParser.h" #include "Poco/XML/ContentHandler.h" #include "Poco/XML/EntityResolver.h" #include "Poco/XML/InputSource.h" #include <iostream> using namespace Poco::XML; class CustomEntityResolver : public EntityResolver { public: InputSource* resolveEntity(const XMLString* publicId, const XMLString& systemId) override { // 自定义实体处理逻辑,比如返回原始的实体字符串 if (systemId == "my-custom-entity") { return new InputSource("&my-custom-entity;"); } return nullptr; // 让解析器使用默认处理 } }; class CustomContentHandler : public ContentHandler { // 实现必要的ContentHandler方法... void characters(const XMLChar ch[], int start, int length) override { std::string text(ch + start, length); std::cout << "Characters: " << text << "\n"; } // 其他方法(startDocument、endDocument等)可以留空或简单实现 void startDocument() override {} void endDocument() override {} void startElement(const XMLString& uri, const XMLString& localName, const XMLString& qName, const Attributes& attrs) override {} void endElement(const XMLString& uri, const XMLString& localName, const XMLString& qName) override {} }; int main() { SAXParser parser; CustomContentHandler handler; CustomEntityResolver resolver; parser.setContentHandler(&handler); parser.setEntityResolver(&resolver); std::string xml = "<root>&my-custom-entity;</root>"; InputSource input(xml); try { parser.parse(input); } catch (SAXException& e) { std::cerr << "Error: " << e.what() << "\n"; } return 0; }
总结
如果你需要保留预定义实体的原始编码形式,方案一的XMLTokenizer是最可靠的选择,因为它直接在底层捕获token,不会自动解码实体引用。标准SAX解析器的ContentHandler回调拿到的都是解码后的字符,这是规范要求的,无法通过简单配置改变。
内容的提问来源于stack exchange,提问作者Takoua
相关产品推荐
相关产品推荐

