Symbian——解析XML文件

来源:互联网 发布:罗技游戏鼠标 知乎 编辑:程序博客网 时间:2024/05/16 04:58

ZT:http://www.mobpub.net/archiver/?tid-28.html
Symbian学习笔记(15)——解析XML文件(上)
今天想分享的是如何在Symbian平台上解析XML文件,不需要第三方的东西,Symbian已经为我们提供了这个类CParser。

网上也有这方面的资料,建议参考:
[url]http://wiki.forum.nokia.com/index.php/How_to_parse_XML_file_using_CParser_class[/url]

不过,要注意的是Symbian中的CParser是基于SAX方式来解析的也就是说它是基于事件流方式,对于SAX,如果做过JAVA开发的一般不会陌生了。与DOM相比SAX方式在操作上会有点麻烦而且显得没那么好理解。

简要比较一下吧,DOM是将XML在内存中展开成一个树的模型,我们可以方便地访问它的每个子节点,可读可写。但是SAX呢?我们只能通过一个单向文本流去解析XML,在过程中有多个事件回调(开始某个元素处理、结束某个元素处理等等),它是单向只读的。

下面我们来详细说明一下如何实现吧。
首先,我们建立一个解析器派生于CActive,由它负责整个解析过程(因为这是一个异步操作)。
#include <xmlparser.h>
#include <xmlcontenthandler.h>

using namespace Xml;

class MXMLHandlerObserver
...{
public:
    virtual void OnParseCompleted( TInt aError ) = 0;
};

class CXMLActiveParser : public CActive ...{
public:
    ~CXMLActiveParser();
    static CXMLActiveParser* NewL(MXMLHandlerObserver& aObserver,MContentHandler& aHandler);
    static CXMLActiveParser* NewLC(MXMLHandlerObserver& aObserver,MContentHandler& aHandler);

public:
    void StartL(const TDesC& aFileName);

private:
    CXMLActiveParser(MXMLHandlerObserver& aObserver,MContentHandler& aHandler);
    void ConstructL();

private:
    void RunL();
    void DoCancel();
    TInt RunError(TInt aError);

private:
   
    CParser*            iParser;
    HBufC8*             iBuffer;
    RFile               iFile;
   
    MContentHandler        *iHandler;
    MXMLHandlerObserver *iObserver;

    RFs                    iFs;
};

除了CActive所必需的东西以外,我们增加了 iParser 成员负责解析,iBuffer保存文件内容以供给iParser去解析,而iHandler是SAX所特有的回调处理类(后面详述),iObserver 则是自定义的一个接口,其实是一个Notifer,就是在解析完成后调用它的OnParseCompleted方法。

实现的部分主要功能在Construct、Start和Run三个函数中:
void CXMLActiveParser::ConstructL() ...{   
    CActiveScheduler::Add( this); // Add to scheduler
    iParser = CParser::NewL( KXmlMimeType, *iHandler );   
    iFs.Connect();
}

void CXMLActiveParser::StartL(const TDesC& aFileName) ...{
    Cancel();
   
    User::LeaveIfError( iFile.Open( /**//*CCoeEnv::Static()->FsSession()*/iFs, aFileName,
            EFileRead ) );
    delete iBuffer;
    iBuffer = 0;
   
    iBuffer = HBufC8::NewL( KFileBufferSize );
    TPtr8 bufferPtr( iBuffer->Des() );
    iFile.Read( bufferPtr, KFileBufferSize, iStatus );
    SetActive();
 
    iParser->ParseBeginL();
}

void CXMLActiveParser::RunL() ...{
    if ( KErrNone == iStatus.Int() )...{
        if ( iBuffer->Length() == 0)...{
            iParser->ParseEndL();
            iFile.Close();
            delete iBuffer;
            iBuffer = 0;
           
            iObserver->OnParseCompleted(KErrNone);
        }
        else ...{
            iParser->ParseL( *iBuffer );
            TPtr8 bufferPtr( iBuffer->Des() );
            iFile.Read( bufferPtr, KFileBufferSize, iStatus );
            SetActive();
        }
    }
    else ...{
        //error handler.
        iObserver->OnParseCompleted(iStatus.Int());
    }
}

注意CParser在NewL时告诉它文档类型是 _LIT8( KXmlMimeType, "text/xml" ) ,以及它需要的回调处理器是iHandler。然后在StartL时读入XML文件,准备解析。在RunL中如果未完成则开始解析,真到完成后则调用 iObserver的onParseCompleted通知观察者“我处理完了,请拿走结果吧”。

Symbian学习笔记(16)——解析XML文件(下)
书接上回,这篇介绍那个MContentHandler的实现,这是SAX解析方法的核心所在。

先看看我要解析的XML文件如下所示,其实很简单,因为它除了Element和Attribute以外没有其它东西了。
<?xml version="1.0" encoding="utf-8" ?>
<channels>
<channel id="10" title="时政" >
<content id="1001" title="广东牛奶中毒事件污染源调查结果1周后公布"/>
<content id="1002" title="河南淅川公安局因儿童被拐案设'局耻日'"/>
<content id="1003" title="深圳大学135名师生感染病毒引发腹泻"/>
</channel>
<channel id="11" title="国际">
<content id="1101" title="巴以将于4月7日恢复领导人级和谈"/>
<content id="1102" title="古巴解除长期禁令允许国民入住涉外酒店"/>
<content id="1103" title="联合国决定继续对刚果(金)实行武器禁运"/>
<content id="1104" title="俄拒绝接受美国进攻性战略武器问题建议"/>
</channel>
<channel id="12" title="财经">
<content id="1201" title="大飞机公司拟定名中国商用飞机有限公司"/>
<content id="1202" title="大部制新部委定编制方案6月底前上报"/>
</channel>
</channels>

我们的解析处理器的声明如下:
#include <xmlcontenthandler.h>
#include <xmldocumentparameters.h>

using namespace Xml;

class TNewsChannel
...{
public:
    TInt id;
    HBufC16 * title;
};

class TNewsContent
...{
public:
    TInt id;
    TInt pid;
    HBufC16 * title;
};

class CChannelXmlHandler : public MContentHandler ...{
public:
    // Constructors and destructor
    ~CChannelXmlHandler();
    static CChannelXmlHandler* NewL();
    static CChannelXmlHandler* NewLC();
   
    RArray<TNewsChannel>* GetChannels();
    RArray<TNewsContent>* GetContents();
    TInt    GetContent(TInt pid,TInt index);
    TInt    ContentCount(TInt pid);

private:

    CChannelXmlHandler();
    void ConstructL();

private: // from MContentHandler

    void OnStartDocumentL( const RDocumentParameters &aDocParam,
        TInt aErrorCode );
   
    void OnEndDocumentL( TInt aErrorCode );
   
    void OnStartElementL( const RTagInfo &aElement,
        const RAttributeArray &aAttributes, TInt aErrorCode );
       
    void OnEndElementL( const RTagInfo &aElement, TInt aErrorCode );
   
    void OnContentL( const TDesC8 &aBytes, TInt aErrorCode );
   
   // ... ...
    
private:
    TInt iCurPID;
    RArray<TNewsChannel> iChannels;
    RArray<TNewsContent> iContents;
   
};

大多数是MContentHandler所声明的方法,这就是SAX事件解析模式的关键了,我们只需要在这些方法中做相应的处理即可。

除此之外,iChannels和iContents是我们定义了用来保存解析结果的成员,它的类型是RArray,关于RArray可以参考我的别一篇笔记:[url]http://blog.csdn.net/sharetop/archive/2008/03/21/2203450.aspx[/url]

因为我们的XML比较简单,所以在CPP中只要处理OnStartElementL就可以了:
void CChannelXmlHandler::OnStartElementL( const Xml::RTagInfo &aElement,
        const Xml::RAttributeArray &aAttributes, TInt aErrorCode )
...{
    if(aElement.LocalName().DesC().Compare(KChannelName)==0)...{       
        TNewsChannel chn;
        for(TInt i=0;i<aAttributes.Count();i++)...{
            if(aAttributes[i].Attribute().LocalName().DesC().Compare(KTitleName)==0)...{
                chn.title=CnvUtfConverter::ConvertToUnicodeFromUtf8L(aAttributes[i].Value().DesC());
            }
            else if(aAttributes[i].Attribute().LocalName().DesC().Compare(KIdName)==0)...{
                TLex8 lex;
                lex.Assign(aAttributes[i].Value().DesC());
                lex.Val(chn.id);
            }
        }
        iChannels.Append(chn);
        iCurPID=chn.id;
    }
    else if(aElement.LocalName().DesC().Compare(KContentName)==0)...{
        TNewsContent cnt;
        cnt.pid=iCurPID;
        for(TInt i=0;i<aAttributes.Count();i++)...{
            if(aAttributes[i].Attribute().LocalName().DesC().Compare(KIdName)==0)...{
                TLex8 lex;
                lex.Assign(aAttributes[i].Value().DesC());
                lex.Val(cnt.id);
            }
            else if(aAttributes[i].Attribute().LocalName().DesC().Compare(KTitleName)==0)...{
                cnt.title=CnvUtfConverter::ConvertToUnicodeFromUtf8L(aAttributes[i].Value().DesC());
            }
        }
        iContents.Append(cnt);
    }
}

这个回调会在解析器遇到元素头时进入,然后我们就可以根据传入的参数取到当前元素的信息,如元素名称、属性值等,将它们保存在我们定义的数据成员中以备将来使用即可。

在使用这个解析器的地方,比如我们的AppView负责解析XML文件,那它应该包含一个MContentHandler的成员,并且它实现接口MXMLHandlerObserver。

于是,这样启动解析过程:
    iChannelHandler=CChannelXmlHandler::NewL();
    iXmlParser=CXMLActiveParser::NewL(*this,*iChannelHandler);
    iXmlParser->StartL(KChannelXMLFile);

然后在它的OnParseCompleted方法中去iChannelHandler中取出解析结果,展示出来或者随便怎么用了。

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/Mirage520/archive/2010/01/14/5189350.aspx

原创粉丝点击