Xerces-C++ によるXMLの読み取り(EXCEL, Word フォーマット)

Apache ProjectによるXerces-C++を用いたXMLのパーサの利用例です。Windows環境ではMSXML、UNIX環境では、gnome等で使われているlibXMLの利用を考えると思いますが、cygwin 等でプログラムを組んでUNIX、Windows間のマルチプラットフォームで動作するプログラムを組もうとすると両環境で利用できるライブラリがあると便利です。Xerces-C++はJavaやPHP等、多言語でも多く扱われ、多言語でXML等を扱う際にもDOMやSAXの考え方が役に立つはずです。

cygwin でXerces-C++ を扱う場合は、セットアップの際にすでにインストールされていて、/usr/lib/libxerces-c.dll.a が存在していれば下記のオプションで利用できますが、インストールされていないようであれば、Xerces-C++のサイトからダウンロードし、configure, make でインストールして下さい。しかし、私も一度試してみましたが、非常に多くのエラーが発生し、挫折しました。その場合は、Windows用のライブラリをダウンロードし、Microsoft Visual Studio でインクルードパス、ライブラリパスを設定、コンパイル、する方が簡単に利用できます。もちろん、ソースはそのままで大丈夫です(現時点では、Visual Stdio 98では上手くコンパイルできましたが、VC2005ではライブラリが上手くリンクできませんでした。解決すればまた報告します。)。

コンパイル時のオプションには、
% g++ foo.cpp -lxerces-c.dll

のように -lxerces-c.dll を追加して下さい。

今回XMLの読み取り用に用いたXMLファイルは excel.xml(EXCELスプレッドシート) です。内容を見ていただくと分かる方は分かる?と思いますが、Excelで用いられているXML スプレッドシートでの記述になっています。このXMLファイルの拡張子を .xls に変えるか、そのままExcelで開くと、Excel上に内容が表示されます。XMLの記述はWordの場合でもXML Documentとして開くことができます(word.xml: XML Document)。読み込み時にテキストとして読み込まれる場合、InfoPath 2003 が必要です。Open Officeでは標準でサポートしていますので、そちらでも確認できます(XML表現の違いか、Bold, Italic, Underline 等が表示されなかったりしますが。)。

 ダウンロード

(cppxml.cpp)
#include <stdio.h>
#include <xercesc/parsers/XercesDOMParser.hpp>
#include <xercesc/dom/DOM.hpp>
#include <xercesc/dom/DOMNode.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/PlatformUtils.hpp>
#include <xercesc/sax/HandlerBase.hpp>
#include <xercesc/framework/LocalFileFormatTarget.hpp>

#include "DOMPrintFilter.hpp"

XERCES_CPP_NAMESPACE_USE

//-----------------------------------------------------------------
void traceNode(DOMElement& root,int depth);
int saveXML(DOMNode* node,char* filename);
//-----------------------------------------------------------------
/** XMLCh to char **/
class Char2X{
    XMLCh* fUnicodeForm;
public:
    Char2X(const char* const toTranscode){ fUnicodeForm = XMLString::transcode(toTranscode); }
    ~Char2X(){ delete [] fUnicodeForm; }
    const XMLCh* unicodeForm() const{ return fUnicodeForm; }
};

/** char to XMLCh **/
class X2Char{
    char* fcharForm;
public:
    X2Char(const XMLCh* const toTranscode){ fcharForm = XMLString::transcode(toTranscode); }
    ~X2Char(){ delete [] fcharForm; }
    const char* charForm() const{ return fcharForm; }
};

#define C2X(str) Char2X(str).unicodeForm()
#define X2C(str) X2Char(str).charForm()
//-----------------------------------------------------------------
class SimpleErrorHandler : public DOMErrorHandler
{
public:
    bool handleError(const DOMError& domError)
    {
        printf("%s, line %s, char %s: %s\n",
               X2C(domError.getLocation()->getURI()),
               (char*)domError.getLocation()->getLineNumber(),
               (char*)domError.getLocation()->getColumnNumber(),
               X2C(domError.getMessage()));
        return domError.getSeverity() != DOMError::DOM_SEVERITY_FATAL_ERROR;
    }
};
//-----------------------------------------------------------------

int main(int argc,char* args[])
{
    try {
        XMLPlatformUtils::Initialize();
    }
    catch(const XMLException& e) {
        char* message = XMLString::transcode(e.getMessage());
        fprintf(stderr,"Error during initialization! : %s\n",message);
        XMLString::release(&message);
        return 1;
    }

    XercesDOMParser* parser = new XercesDOMParser();
    parser->setValidationScheme(XercesDOMParser::Val_Always); // optional
    parser->setDoNamespaces(true); // optional
    
    ErrorHandler* errHandler = (ErrorHandler*) new HandlerBase();
    parser->setErrorHandler(errHandler);
    
    char* xmlFile = "input.xml";

    try {
        parser->parse(xmlFile);
        
        DOMDocument* doc = parser->getDocument();
        DOMElement* root = doc->getDocumentElement();
        
        // trace and show node
        traceNode(*root,0);
        // copy node    
        saveXML((DOMNode*)root,"output.xml");
        
    } catch(const XMLException& e) {
        char* message = XMLString::transcode(e.getMessage());
        fprintf(stderr,"Exception message is: %s\n",message);
        XMLString::release(&message);
        return -1;
    } catch(const DOMException& e) {
        char* message = XMLString::transcode(e.msg);
        fprintf(stderr,"Exception message is: %s\n",message);
        XMLString::release(&message);
        return -1;
    } catch(...) {
        fprintf(stderr,"Unexpected Exception\n");
        return -1;
    }

    delete parser;
    delete errHandler;

    XMLPlatformUtils::Terminate();
    
    return 0;
}

// trace and show node
void traceNode(DOMElement& root,int depth){
    DOMNodeList* n1 = root.getChildNodes();
    if(depth == 0){
        DOMNode* r = (DOMNode*)&root;
        char *name = XMLString::transcode(r->getNodeName());
        printf("**%s\n",name);
        XMLString::release(&name);
    }
    for(int i=0;i<(signed)n1->getLength();i++){
        DOMElement* c1 = (DOMElement*)n1->item(i);
        if(((DOMNode*)c1)->getNodeType() == c1->ELEMENT_NODE){
            traceNode(*c1,depth+1);
        } else if(((DOMNode*)c1)->getNodeType() == c1->TEXT_NODE){
            DOMNode* r = c1->getParentNode();
            char *snode = XMLString::transcode(r->getNodeName());
            char *value = XMLString::transcode(c1->getTextContent());
            if(value[0] != '\n'){
                printf("%d> %s:\t%s\n",depth,snode,value);
            }
            XMLString::release(&snode);
            XMLString::release(&value);
        }
    }
}

// from xml.apache.org/xerces-c: dom serialize
int saveXML(DOMNode* node,char* filename)
{
    XMLCh tempStr[100];
    XMLString::transcode("LS",tempStr,99);
    DOMImplementation *impl = DOMImplementationRegistry::getDOMImplementation(tempStr);
    DOMWriter* theSerializer = ((DOMImplementationLS*)impl)->createDOMWriter();

    // optionally you can set some features on this serializer
    if(theSerializer->canSetFeature(XMLUni::fgDOMWRTDiscardDefaultContent,true))
        theSerializer->setFeature(XMLUni::fgDOMWRTDiscardDefaultContent,true);

    if(theSerializer->canSetFeature(XMLUni::fgDOMWRTFormatPrettyPrint,true))
        theSerializer->setFeature(XMLUni::fgDOMWRTFormatPrettyPrint,true);

    // writer filter
    DOMWriterFilter* myFilter = new DOMPrintFilter();
    theSerializer->setFilter(myFilter);
    
    // error handler
    DOMErrorHandler* errHandler = new SimpleErrorHandler();
    theSerializer->setErrorHandler(errHandler);
    
    // StdOutFormatTarget prints the resultant XML stream
    // to stdout once it receives any thing from the serializer.
    XMLFormatTarget* myFormatTarget = new LocalFileFormatTarget(filename);

    try { // serialization
        theSerializer->writeNode(myFormatTarget,*node);
    } catch(const XMLException& e) {
        char* message = XMLString::transcode(e.getMessage());
        fprintf(stderr,"Exception message is: %s\n",message);
        XMLString::release(&message);
        return -1;
    } catch(const DOMException& toCatch) {
        char* message = XMLString::transcode(toCatch.msg);
        fprintf(stderr,"Exception message is: %s\n",message);
        XMLString::release(&message);
        return -1;
    } catch(...) {
        fprintf(stderr,"Unexpected Exception\n");
        return -1;
    }
    
    theSerializer->release();
    delete errHandler;
    delete myFilter;
    delete myFormatTarget;
    return 0;
}

実行例
  • excel.xls に対する処理 Gami[244]% ./cppxml.exe **Workbook 5> Data: 1 5> Data: M 5> Data: A 5> Data: 19781003 5> Data: 男性 5> Data: xxxx@gmail.com 5> Data: 0xx-xxxx-xxxx 5> Data: 2 5> Data: S 5> Data: B 5> Data: 19700000 5> Data: 男性 5> Data: xxx@gmail.com 5> Data: 0xx-xxxx-xxxx Gami[245]%
  • word.xls に対する処理 Gami[289]% ./cppxml.exe **w:wordDocument 4> w:t: 1 4> w:t: , 4> w:t: M 4> w:t: , 4> w:t: A 4> w:t: , 4> w:t: 19781003 4> w:t: , 4> w:t: 男性 4> w:t: , 4> w:t: xxxx@gmail.com 4> w:t: , 4> w:t: 0xx-xxxx-xxxx 4> w:t: 2 4> w:t: , 4> w:t: S 4> w:t: , 4> w:t: B 4> w:t: , 4> w:t: 19700000 4> w:t: , 4> w:t: 男性 4> w:t: , 4> w:t: xxx@gmail.com 4> w:t: , 4> w:t: 0xx-xxxx-xxxx Gami[290]%
  • inserted by FC2 system