很少用在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.&lt;2&gt;&lt;2&gt;</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++里面,我还很少见到。


Jeason Zhao (沈胜衣,斛律光) ------雪饮再现,一个人的江湖
我知道我是谁,我是沈胜衣,默默的活着,就像空气。