免费注册 查看新帖 |

Chinaunix

广告
  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 6787 | 回复: 2
打印 上一主题 下一主题

抛砖引玉,开源的XML Parser expat的介绍和使用心得 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-09-16 12:08 |只看该作者 |倒序浏览
刚刚借了别人的帖子,发了一篇开源SQL数据库SQlite3的文章,顺便发表下,我的另一篇,介绍开源XML Parser expat的文章。

以下内容是从我的人人主页直接复制来的:

----------------------------------------------------------------------------------------------------------------

expat是使用C所写的XML解释器,采用流的方式来解析XML文件,并且基于事件通知型来调用分析到的数据,并不需要把所有XML文件全部加载到内存里,这样可以分析非常大的XML文件。由于expat库是由XML的主要负责人James Clark来实现的,因此它是符合W3C的XML标准的。

---------------------------以上为转载-------------------------------------

正因为源码全部是纯C所写,因此,非常容易移植,尤其是适用于嵌入式平台,我在往联芯的手机平台上移植时,几乎没改任何东西。

不过,优点也带来了缺点,因为是采用流的方式解析XML,所以不会像TinyXML那样在一块内存中生成基于DOM的树。

虽然这样解析起来略显麻烦,但是基于回调的机制,在我看来还是蛮方便的。

下面就说使用方法:

首先是用XML_ParserCreate(const XML_Char *encodingName),参数一般为NULL,函数返回一个XML_Parser类型指针,我们就当他是一个句柄吧,类似于Windows里的内核对象,一般需要保存在一个全局的指针里。



然后调用XML_SetElementHandler(XML_Parser parser,
                                                  XML_StartElementHandler start,
                                                  XML_EndElementHandler end)



         第一个参数是那个Parser句柄,第二个和第三个参数则是整个Parser的核心,类型为CallBack的函数,不了解CallBack函数的,我在这里简单说下,函数调用一般分为两种,一种是主调,即编写代码者,自己调用的函数,还一种成为Callback函数,编码者写好,但他自己却不主动调用,而是在某些条件下(编码者并不清楚具体时间和流程),由其他函数调用,比如简单的,如设备驱动,操作系统提供了一组某个设备的函数指针,比如LCD屏驱动,由一组画点,画线,画块等函数组成,当更换LCD时,只需要把操作系统开放的函数指针,指向你提供的接口即可,操作系统再需要时,会自动调用你的驱动函数,这就是回调函数一个典型的例子。

       这二个回调分别是对应于解析<>和</>, 下面分别详细介绍这个2个回调函数。

       typedef void (XMLCALL *XML_StartElementHandler) (void *userData,
                                                 const XML_Char *name,
                                                 const XML_Char **atts);

       其中第一个参数userData, 可以由函数XML_SetUserData(XML_Parser parser, void *p)设置,参数就不用说了吧?

       后面两个参数,我用个具体的列子说明下,这样更好理解:

       比如有个标准XML,某个标签属性如下:

       <feed version="2.0" ctxt-id="9212" template-id="default" feed-type="ftti">

       那么StartElementHandler回调返回的name就是标签"feed", **atts是一个指针数组,分别指向标签的一组属性,atts[0]就是"version", atts[1]就是"2.0", 以此类推。应该很清楚了吧?呵呵。

       这时候必然有个对应的</feed>,

       typedef void (XMLCALL *XML_EndElementHandler) (void *userData,
                                               const XML_Char *name);

       就是处理标签结束的,name就是"feed”了,这个回调一般是用户设置自己的状态机的。

       最后一个函数就是XML_SetCharacterDataHandler(XML_Parser parser,XML_CharacterDataHandler handler)

       这个函数是设置处理一个<>和</>之间的字段的回调。

       回调原型如下:

       typedef void (XMLCALL *XML_CharacterDataHandler) (void *userData,
                                                  const XML_Char *s,
                                                  int len);

       其中第二个参数是一块Buffer的指针,如果你单步DEBUG后,你会发现expat用的就是你传入的那块Buffer(这块Buffer下面讲解),比如:

       <title>天气</title>
       <summary>28日08时至29日08时,陕西中南部、山西西南部、河南中南部、湖北北部、四川中东部、重庆西部和北部、贵州西部等地的部分地区有大雨或暴雨,河南南部、湖北北部等地局部有大暴雨。【点击“更多”查询其他城市天气】</summary>

       假设目前解析到天气这个charData, 如果你看那个指针的所有内容的话,实际上是这样的:

       天气</title>
       <summary>28日08时至29日08时,陕西中南部、山西西南部、河南中南部、湖北北部、四川中东部、重庆西部和北部、贵州西部等地的部分地区有大雨或暴雨,河南南部、湖北北部等地局部有大暴雨。【点击“更多”查询其他城市天气】</summary>

       所有要根据第三个参数len来确定正确的数据。

       但这里有个非常隐晦的问题,如果不知道的话,会带来很大麻烦,下面说。

       最后就是parse,调用

       XML_Parse(XML_Parser parser, const char *s, int len, int isFinal)

       第二个参数是用户指定的Buffer指针, 第三个是这块Buffer中实际内容的字节数,最后参数代表是否这块Buffer已经结束。比如要解析的XML文件太大,但内存比较吃紧,Buffer比较小,则可以循环读取文件,然后丢给Parser,  在文件读取结束前,isFinal参数为FALSE,反之为TRUE。

      这里的Buffer如果太小则会造成上面提到那个隐晦的问题,

      XML_CharacterDataHandler一次返回的可能并不是完整的CharData,比如这个charData的Len大于你的Buffer大小,那这是会连续调用2次XML_CharacterDataHandler,我们需要将2次结果拼接起来,以得到正确结果,因此我们的状态机一定要考虑到这点。

      顺便说下XML_ParserReset(XML_Parser parser, const XML_Char *encodingName)函数,在某些时候,如果你不确定前后2次XML是否一样的情况下,比如网络上投递的XML,在一次解析后最好调用一次本函数,否则会出现意料之外的结果。比如前后两次XML完全一样,可这你并不知情,那么XML_Parse()会返回失败。

      好了,开源的XML解析器expat就讲到这,下次说开源的SQL数据库sqlite3的移植和使用心得,经过上次日志被毁的经历,我不敢一次码太多的字了,88!

[ 本帖最后由 ExclusivePig 于 2009-9-16 12:47 编辑 ]

论坛徽章:
0
2 [报告]
发表于 2009-09-16 13:56 |只看该作者
不用XML,<>比虱子都多。

论坛徽章:
14
巨蟹座
日期:2013-11-19 14:09:4615-16赛季CBA联赛之青岛
日期:2016-07-05 12:36:0515-16赛季CBA联赛之广东
日期:2016-06-29 11:45:542015亚冠之全北现代
日期:2015-07-22 08:09:472015年辞旧岁徽章
日期:2015-03-03 16:54:15巨蟹座
日期:2014-12-29 08:22:29射手座
日期:2014-12-05 08:20:39狮子座
日期:2014-11-05 12:33:52寅虎
日期:2014-08-13 09:01:31巳蛇
日期:2014-06-16 16:29:52技术图书徽章
日期:2014-04-15 08:44:01天蝎座
日期:2014-03-11 13:06:45
3 [报告]
发表于 2009-09-16 17:21 |只看该作者
楼上,俺一直是你的粉丝^_^
XML实在是太那个啥了,人难看懂,机器也难看懂(手工修改xml是一种噩梦)。既没有讨好到人,也没能讨好机器(各种解析xml的库,都是bug多,运行效率低)。
我都是自己写一个二进制的树结构,用于程序读写;然后写一个带界面的解析小工具,用于人工的读写。
另外,有人建议我用google的那个啥二进制XML类

[ 本帖最后由 bruceteen 于 2009-9-16 17:24 编辑 ]
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP