很少用在C++下面的XML,虽然买了一本书,但是尘封数年了,没看。大多数都是用JDOM之类非常简单明快的东西——嗯,最近受到Websphere的XML解析器的困扰,我已经开始自己抽取一份XML解析器了。所以在C++上的SAX,多少熟悉一些,但是代码却有些陌生,尤其是那个转义的XMLCh,令人有些哭笑不得。
问题出自于一个在用的系统,原先的XML节点中的字符串都是规范的字符串,没有特殊字符,目前客户(万恶的China Unicom)要在这些节点中加入”<”和”>”导致解析的时候出现错误。原先的解析机制大致如下
startElement->读取节点属性
characters->读取XML节点中的文本
endElement->处理上面读取到的文本
在没有特殊字符的时候,一切正常,但是一旦出现了,则会出现characters读取不完整的错误。纵观文档终于恍然大悟,的确高深,某未知为何这样设计,但是存在必有道理的原则下,姑且信之。http://xerces.apache.org/xerces2-j/javadocs/api/org/xml/sax/ContentHandler.html
原先读取的时候characters直接只用XMLString::transcode即可转换出内部的文本,但是实际情况下,characters在处理单个节点中的文本的时候,可能会被调用多次,也就是说,俺们的逻辑应该是
startElement
characters
characters
…
endElement
如此才是合理
所以只好对程序进行改造,改造结果如下
#include <xercesc/sax2/SAX2XMLReader.hpp>
#include <xercesc/sax2/XMLReaderFactory.hpp>
#include <xercesc/sax2/DefaultHandler.hpp>
#include <xercesc/util/XMLString.hpp>
#if defined(XERCES_NEW_IOSTREAMS)
#include <iostream>
#else
#include <iostream.h>
#endif
XERCES_CPP_NAMESPACE_USE
using namespace std;
class MySAX2Handler : public DefaultHandler
{
public:
string xtemp;
void endElement(
const XMLCh* const uri,
const XMLCh* const localname,
const XMLCh* const qname
)
{
char* message = XMLString::transcode(localname);
cout << "End Element: "<< message << endl;
cout<<"Result \""<<xtemp<<"\""<<endl;
XMLString::release(&message);
};
void startElement(
const XMLCh* const uri,
const XMLCh* const localname,
const XMLCh* const qname
, const Attributes& a
)
{
char* message = XMLString::transcode(localname);
cout << "Start Element: "<< message << endl;
xtemp="";
XMLString::release(&message);
};
void characters(const XMLCh* const localname
,const unsigned int i)
{
char* message = XMLString::transcode(localname);
cout << "Get Value : "<<i<<" \""<< message<<"\"" << endl;
xtemp+=message;
XMLString::release(&message);
};
};
int main (int argc, char* args[])
{
try
{
XMLPlatformUtils::Initialize();
}
catch (const XMLException& toCatch)
{
char* message = XMLString::transcode(toCatch.getMessage());
cout << "Error during initialization! :\n";
cout << "Exception message is: \n"
<< message << "\n";
XMLString::release(&message);
return 1;
}
char* xmlFile = "x1.xml";
SAX2XMLReader* parser = XMLReaderFactory::createXMLReader();
parser->setFeature(XMLUni::fgSAX2CoreValidation, true);
parser->setFeature(XMLUni::fgXercesIgnoreAnnotations, true); // optional
DefaultHandler* defaultHandler = new MySAX2Handler();
parser->setContentHandler(defaultHandler);
parser->setErrorHandler(defaultHandler);
try
{
parser->parse(xmlFile);
}
catch (const XMLException& toCatch)
{
char* message = XMLString::transcode(toCatch.getMessage());
cout << "Exception message is: \n"
<< message << "\n";
XMLString::release(&message);
return -1;
}
catch (const SAXParseException& toCatch)
{
char* message = XMLString::transcode(toCatch.getMessage());
cout << "Exception message is: \n"
<< message << "\n";
XMLString::release(&message);
return -1;
}
catch (...)
{
cout << "Unexpected Exception \n" ;
return -1;
}
delete parser;
delete defaultHandler;
return 0;
}
测试使用的XML源文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<A xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<B name="TestVC">
Begin B
<C>CCCCCCCCCCC.<2><2></C>
End B
</B>
</A>
检查输出结果,有些大跌眼镜
Start Element: A
Get Value:""
Start Element: B
Get Value:"Begin B"
Start Element: C
Get Value:"CCCCCCCCCCC."
Get Value:"<2"
Get Value:">"
Get Value:"<2"
Get Value:">"
End Element: C
Result "CCCCCCCCCCC.<2><2>"
Get Value:"End B"
End Element: B
Result "End B"
Get Value:""
End Element: A
Result ""
对于节点B中的内容,拆分了三次来读,也就是说,我们目前的解决方案还需要改进,借用程序设计的一个名词,这个设计方案必须允许函数重入。
另外,这个库的函数名怎么都是小写开头,这个在C++里面,我还很少见到。