嵌入式linux对xml文件的操作(expat篇)

前段时间在项目中用到了涉及到了xml文件的解析,环境是嵌入式linux,在此简单总结一下留个记录,也跟大家分享一下。

        我在项目中用的是expat解析库,还有很多其它的库可以使用,比如libxml、minixml等,它们之间有些差别,下面是从网上粘来的说法。

        目前的xml解析器一般有两种解析的形式.一种是DOM模型的,一种是SAX2模型的.DOM模型是讲xml文件中的结构解析成一棵树,然后再进行各种操作;而SAX2模型是类似与事件处理的方式从头到位解析xml文件.两种方式各有优劣,不过在嵌入式设备上用DOM模型的解析器似乎太耗内存,所以一般都用SAX2的解析器。

        expat就是SAX2模型的解析器,下面简单说说安装和使用方法:

1.下载、解压expat库(网上资源很多,自己搜)

2.执行configure文件和make install、make,注意执行 configure时要把编译指令改为交叉编译指令(交叉编译指令视情况而定)如下:

   ./configure CC=arm-linux-gcc --host=arm-linux --prefix=$PWD/_install

3.使用方法:

  expat库要包含的头文件是expat.h,如果是集成开发环境,如eclipse,需要包含动态库或者静态库(libexpat.a,libexpat.so)的路径,

expat要使用的函数有4个:

  XML_ParserCreate()

扫描二维码关注公众号,回复: 4811202 查看本文章

  XML_SetUserData()

  XML_SetElementHandler()

  XML_ParserFree()


粘一段代码

[html]  view plain  copy
  1. #include "readconfig.h"  
  2. #include "expat.h"  
  3.   
  4. #define MAX_XML_CONFIG_FILE_SIZE   1024000  
  5.   
  6. static Shm_Data_Cache *config_shm_data_cache = NULL;  
  7. static NAYU_INT32 g_channel_offset = 0;  
  8. static NAYU_INT32 g_device_offset = 0;  
  9. static NAYU_INT32 g_iopoint_offset = 0;  
  10.   
  11. void startElement(void *userData, const char *name, const char **atts)  
  12. {  
[html]  view plain  copy
  1.     /*这个函数中name就是属性名,*atts[奇数]是字段名,*atts[偶数]是字段值*/  
  2. }  
  3.   
  4. void endElement(void *userData, const char *name)  
  5. {  
  6.     /*这个函数中判断name的结尾并做出相应的处理*/  
  7. }  
  8.   
  9. int Get_Total_Config(Shm_Data_Cache * shm)//自定义函数  
  10. {  
  11.     config_shm_data_cache = shm;  
  12.     NAYU_INT8 buf[MAX_XML_CONFIG_FILE_SIZE];  
  13.     XML_Parser parser = XML_ParserCreate(NULL);  
  14.     NAYU_INT32 depth = 0;  
  15.     FILE* pf = fopen("/home/nayu/config/db.xml","r");  
  16.     if(pf == NULL)  
  17.     {  
  18.         printf("Open xml file error!\n");  
  19.         return -1;  
  20.     }  
  21.     XML_SetUserData(parser, &depth);  
  22.     XML_SetElementHandler(parser, startElement, endElement);  
  23.     size_t len = 0;  
  24.     do{  
  25.         len  = fread(buf, 1, sizeof(buf), pf);  
  26.         XML_Parse(parser, buf, len, 1);  
  27.     }while(len>0);  
  28.     XML_ParserFree(parser);  
  29.     fclose(pf);  
  30.     return 0;  
  31. }  

expat

介绍 

    expat是使用C所写的XML解释器,采用流的方式来解析XML文件,并且基于事件通知型来调用分析到的数据,并不需要把所有XML文件全加载到内存里,这样可以分析非常大的XML文件。expat库是由XML的主要负责人James Clark来实现的,符合W3C的XML标准。
    expat默认只支持UTF-8 UTF-16 ISO-8859-1 US-ASCII ,其它的字符集需要自己定义UnknownEncodingHandler的实现,不然分析器会报错。

使用

首先是用XML_ParserCreate(const XML_Char *encodingName),参数一般为NULL,函数返回一个XML_Parser类型指针,

我们就当他是一个句柄吧,类似于Windows里的内核对象,一般需要保存在一个全局的指针里。

然后调用XML_SetElementHandler(XML_Parser parser, XML_StartElementHandler start, XML_EndElementHandler end), 第一个参数是那个Parser句柄,第二个和第三个参数则是整个Parser的核心,类型为CallBack的函数,分别对应于解析<>和</>, 下面分别详细介绍这个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的指针,就是你传入的那块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()会返回失败。

TinyXML 

开源:http://sourceforge.net/projects/tinyxml/

介绍

TinyXML is a simple, small, minimal, C++ XML parser that can be easily integrating into other programs. It reads XML and creates C++ objects representing the XML document. The objects can be manipulated, changed, and saved again as XML。

通过解析XML文件,然后在内存中生成DOM模型,从而让我们很方便的遍历这棵XML树。可用于Windows、Linux平台。

在TinyXML中,根据XML的各种元素来定义了一些类:
           TiXmlBase:整个TinyXML模型的基类。   

TiXmlAttribute:对应于XML中的元素的属性。

TiXmlNode:对应于DOM结构中的节点。

TiXmlComment:对应于XML中的注释

TiXmlDeclaration:对应于XML中的申明部分,<?versiong="1.0" ?>。

TiXmlDocument:对应于XML的整个文档。

TiXmlElement:对应于XML的元素。

TiXmlText:对应于XML的文字部分

TiXmlUnknown:对应于XML的未知部分。 

TiXmlHandler:定义了针对XML的一些操作。 

安装和配置

TinyXML使用了多线程环境,因此需要设置多线程的环境,win下vc中选择Debug MultiThreaed DLL。     

使用 

可对xml文件及xml 字符流(非xml文件)进行解析:

xml文件解析方法 myDocument->LoadFile("Students.xml");

字符流解析方法 myDocument->Parse(xmlString.c_str());


猜你喜欢

转载自blog.csdn.net/gnnulzy/article/details/79398456