以文本方式查看主题

-  中文XML论坛 - 专业的XML技术讨论区  (http://bbs.xml.org.cn/index.asp)
--  『 XML工具及XML开发环境 』  (http://bbs.xml.org.cn/list.asp?boardid=7)
----  XML映射c语言结构源代码自动化工具  (http://bbs.xml.org.cn/dispbbs.asp?boardid=7&rootid=&id=76044)


--  作者:BetonArmEE
--  发布时间:7/15/2009 11:44:00 PM

--  XML映射c语言结构源代码自动化工具

XML映射c语言结构源代码自动化工具
使用文档

calvin.xml2c@gmail.com
2009.7

谨以此文献给我亲爱的莫莫,她和她哥哥对网店的投入意外给了我写本文的时间。

版本修订
版本号 修订人 修订日期 修订内容
0.1.0 calvin 2009-07-13 创建

目录索引
版本修订 3
目录索引 4
1.概述 5
1.1.XML和映射c语言结构 5
1.2.xml2c是什么 6
1.3.感受一下XML新开发风格 6
2.使用教程 10
2.1.编写xmd定义文件 10
2.2.用xml2c处理后生成的源代码文件 11
2.3.嵌入你的应用代码 13
3.附录 14
I.关于作者和本文 14
II.xmd中已实现的数据类型描述 14

1.概述
1.1.XML和映射c语言结构
 XML(Extensible Markup Language)即可扩展标记语言由于其跨平台性、自描述性、多层次化等优点目前被广泛使用。
XML处理过程代码风格比较成熟,各个函数库使用大同小异,甚至函数名也一样。主流操作无非就是读入外部XML文件或缓冲区,创建XML树,遍历处理树,导出树为外部XML文件或缓冲区。其中遍历处理树普遍采用硬编码方式,增加了应用代码复杂性风险性和编码负担。
 XML和c语言结构都具备多层次化数据表达,不妨把两者建立映射关系,XML标记和c结构成员变量做元数据映射,类似于下例:
 XML数据:
  <userinfo>
   <username>calvin.xml2c@gmail.com</username>
   <password>123456</password>
   <regdate>2009-01-01</regdate>
   <logindate>2009-07-13</logindate>
   <extinfo>
    <age>30</age>
    <income>160000.00</income>
    <marriage>FALSE</marriage>
   </ extinfo>
  </userinfo>
 对应c语言结构:
  struct userinfo
  {
   char  username[ 64 + 1 ] ;
   char  password[ 8 + 1 ] ;
   char  regdate[ 10 + 1 ] ;
   char  logindate[ 10 + 1 ] ;
   struct _extinfo
   {
    int  age ;
    double income ;
    BOOL marriage ;
   } extinfo ;
  }
 以上映射关系可以用XML定义格式(xmd)来描述:
  <userinfo>
   <username>char64</username>
   <password>char8</password>
   <regdate>date</regdate>
   <logindate>date</logindate>
   <extinfo>
    <age>int</age>
    <income>money</income>
    <marriage>BOOL</marriage>
   </extinfo >
  </userinfo >

1.2.xml2c是什么
xml2c是基于libxml2函数库的预处理XML定义描述文件生成c语言源代码的自动化工具。开发人员针对要处理的XML,改造成xmd,用工具xml2c处理后自动生成对应的c语言结构、XML格式-c结构格式相互转换函数以及周边源代码资源。
xml2c是跨平台的,这意味着你所能接触的几乎所有主流操作系统上都能使用它。
xml2c最新版本是v0.9.1(试验版)。

1.3.感受一下XML新开发风格
 以1.2.为例,实现userinfo的文件读写处理。
 稍作修改的XML定义描述文件(XML_test.xmd):(为什么要稍作修改呢?因为xml2c目前还不十分完善)
  <userinfo>
   <username>char64</username>
   <password>char8</password>
   <regdate>date</regdate>
   <logindate>date</logindate>
   <extinfo>
    <age>int</age>
    <income>double</income>
    <marriage>char5</marriage>
   </extinfo >
  </userinfo >
 执行命令 “xml2c XML_test.xmd”,用工具xml2c处理XML_test.xmd后,你会找到一堆自动生成的c源代码文件:
  XML_test.XMLSTRUCT.h 对应c语言结构定义
  XML_test.XMLDUMP.c c语言结构导出为外部XML文件或缓冲区格式转换函数
  XML_test.XMLPARSE.c 外部XML文件或缓冲区映射为c语言结构格式转换函数
  XML_test.XMLFREE.c 对变长元数据和变size数组的free操作函数
  XML_test.XMLTRIM.c 裁剪各字符串变量右边白字符函数
 (然后开发人员卷着袖子上场了)test.c:
  #include "libxml2c.h"
  #include "XML_test.XMLSTRUCT.h"
  
  int write_xmlfile()
  {
   struct XML_userinfo userinfo ;
   
   memset( & userinfo , 0x00 , sizeof(struct XML_userinfo) );
   strcpy( userinfo.username , "calvin.xml2c@gmail.com" );
   strcpy( userinfo.password , "123456" );
   strcpy( userinfo.regdate , "2009-01-01" );
   strcpy( userinfo.logindate , "2009-07-13" );
   userinfo.extinfo.age = 30 ;
   userinfo.extinfo.income = 160000.00 ;
   strcpy( userinfo.extinfo.marriage , "FALSE" );
   
   XMLDUMPFILE_userinfo( & userinfo , "test.xml" );
   
   return 0;
  }
  
  int main()
  {
   return write_xmlfile();
  }
  
 编译链接执行,生成外部XML文件:
  <?xml version="1.0" encoding="GBK"?>
  <userinfo>
    <username>calvin.xml2c@gmail.com</username>
    <password>123456</password>
    <regdate>2009-01-01</regdate>
    <logindate>2009-07-13</logindate>
    <extinfo>
      <age>30</age>
      <income>160000.000000</income>
      <marriage>FALSE</marriage>
    </extinfo>
  </userinfo>
 okay,导出完成,再试试读入
  int read_xmlfile()
  {
   struct XML_userinfo userinfo ;
   
   memset( & userinfo , 0x00 , sizeof(struct XML_userinfo) );
   XMLPARSEFILE_userinfo( "test.xml" , & userinfo );
   
   printf( "userinfo.username[%s]\n" , userinfo.username );
   printf( "userinfo.password[%s]\n" , userinfo.password );
   printf( "userinfo.regdate[%s]\n" , userinfo.regdate );
   printf( "userinfo.logindate[%s]\n" , userinfo.logindate );
   printf( "userinfo.extinfo.age[%d]\n" , userinfo.extinfo.age );
   printf( "userinfo.extinfo.income[%lf]\n" , userinfo.extinfo.income );
   printf( "userinfo.extinfo.marriage[%s]\n" , userinfo.extinfo.marriage );
   
   return 0;
  }
 编译链接执行,显示:
  userinfo.username[calvin.xml2c@gmail.com]
  userinfo.password[123456]
  userinfo.regdate[2009-01-01]
  userinfo.logindate[2009-07-13]
  userinfo.extinfo.age[30]
  userinfo.extinfo.income[160000.000000]
  userinfo.extinfo.marriage[FALSE]
 Well done , that’s all ~
 总结一下流程:首先编写或者改造现有的XML文件为定义文件,用工具xml2c处理生成c结构定义和格式转换函数,在你的应用代码中声明结构变量,调用自动生成的函数们做结构变量和XML文件或缓冲区格式相互转化。
 对于变长字符串类型映射成c语言字符指针,重复相同层次结构的XML子树映射成c结构数组。
 您老感觉如何?如果吓着您了,就当浪费您时间不用往下看了,如果您眼光变绿口水直淌——欢迎进入第二章^_^
 
2.使用教程
2.1.编写xmd定义文件
 xmd文件就是一个普通的XML文件,原有层次结构保持不变,只不过数据部分用类型描述替代,比如把“<age>30</age>” 替代成“<age>int</age>”,把“<username>calvin.xml2c@gmail.com</username>”替代成“<username>char64</username>”,完整的类型描述参见附录II。
 目前版本xml2c支持变长字符串类型和重复XML相同层次结构,前者映射成c语言字符指针,由XML读函数内部在堆中alloc内存空间,后者映射成定长或变长结构数组,声明_count变量和_size变量跟踪该结构数组的大小。一个典型的案例如下:
  <?xml version="1.0" encoding="GBK"?>
  <tree>
   <branch11>char64</branch11>
   <branch12>char*</branch12>
   <branch21>
    <leaf211>short</leaf211>
    <leaf212>int</leaf212>
    <branch213>
     <leaf2131>long</leaf2131>
    </branch213>
   </branch21>
   <branch22 iterative="2">
    <leaf221>double</leaf221>
   </branch22>
   <branch23 iterative="*">
    <leaf231>datetime</leaf231>
   </branch23>
  </tree>
 这里要注意的是如果你的xmd有XML头(“<?xml version="1.0" encoding="GBK"?>”),工具xml2c会提取出编码代码影响后面生成的所有源代码。
xmd定义文件最原始的来源渠道就是手工写,有些人喜欢用XML编辑器,比如XMLSpy(目前本人还没下载到完美破解的版本),有些人习惯干净,喜欢用记事本或者UltraEdit书写,更多的懒人则喜欢直接把XML文件拿来,把数据部分改成类型描述即可。

2.2.用xml2c处理后生成的源代码文件
 定义好了xmd文件后,把xml2c拿出来处理一下它,如果你是一个严谨仔细的人,那么在标准输出最后一行出现的应该是“ok!\n”,如果没有出现,一定是哪里出现了错误,返回修改你的xmd直到pass为止。(推荐xmd文件命名规则为“XML_xxxxxx.xmd”,其中xxxxxx你可以自由命名)
你所在的目录里出现了很多c语言源代码文件,假如用的是1.3.的xmd文件,应该生成如下源代码文件:
  --- XML_test.XMLSTRUCT.h ---------
  #ifndef _H_XML_test_XMLSTRUCT_
  #define _H_XML_test_XMLSTRUCT_
  
  #include "libxml2c.h"
  
  struct XML_userinfo
  {
   char username[ 64 + 1 ] ;
   char password[ 8 + 1 ] ;
   char regdate[ 10 + 1 ] ;
   char logindate[ 10 + 1 ] ;
   struct _extinfo
   {
    int age ;
    double income ;
    char marriage[ 5 + 1 ] ;
   } extinfo ;
  } ;
  
  int XMLDUMPBUFFER_userinfo( struct XML_userinfo *pst , char *pbuffer , int *pbuflen );
  int XMLDUMPFILE_userinfo( struct XML_userinfo *pst , char *xmlfilename );
  int XMLPARSEBUFFER_userinfo( char *pbuffer , int *pbuflen , struct XML_userinfo *pst );
  int XMLPARSEFILE_userinfo( char *xmlfilename , struct XML_userinfo *pst );
  
  int XMLTRIM_userinfo( struct XML_userinfo *pst );
  int XMLFREE_userinfo( struct XML_userinfo *pst );
  
  #endif
头文件里包含对应c结构定义,以及其它几个.c文件里实现的函数原型。
函数XMLDUMPBUFFER_userinfo和XMLDUMPFILE_userinfo用于导出c结构变量数据到你申请好的缓冲区或外部XML文件,函数实现自动生成放在XML_test.XMLDUMP.c。
  --- XML_test.XMLDUMP.c ---------
  #include "libxml2c.h"
  #include "XML_test.XMLSTRUCT.h"
  
  int XMLDUMP_userinfo( struct XML_userinfo *pst , xmlDoc *pXmlDoc , xmlNode **ppXmlNode )
  {...}
  int XMLDUMPBUFFER_userinfo( struct XML_userinfo *pst , char *pbuffer , int *pbuflen )
  {...}
  int XMLDUMPFILE_userinfo( struct XML_userinfo *pst , char *xmlfilename )
  {...}
函数XMLPARSEBUFFER_userinfo和XMLPARSEFILE_userinfo用于读入缓冲区或外部XML文件映射成c结构数据,函数实现自动生成放在XML_test.XMLPARSE.c。
  --- XML_test.XMLPARSE.c ---------
  #include "libxml2c.h"
  #include "XML_test.XMLSTRUCT.h"
  
  int XMLPARSE_userinfo( xmlDoc *pXmlDoc , xmlNode **ppXmlNode , struct XML_userinfo *pst )
  {...}
  int XMLPARSEBUFFER_userinfo( char *pbuffer , int *pbuflen , struct XML_userinfo *pst )
  {...}
  int XMLPARSEFILE_userinfo( char *xmlfilename , struct XML_userinfo *pst )
  {...}
函数XMLTRIM_userinfo用于裁剪元数据类型为字符串的右边白字符,函数实现自动生成放在XML_test.XMLTRIM.c。
  --- XML_test.XMLTRIM.c ---------
  #include "libxml2c.h"
  #include "XML_test.XMLSTRUCT.h"
  
  int XMLTRIM_userinfo( struct XML_userinfo *pst )
  {...}
函数XMLFREE_userinfo用于释放变长字符串和变长结构数组在堆上申请的内存空间,函数实现自动生成放在XML_test.XMLFREE.c。
  --- XML_test.XMLFREE.c ---------
  #include "libxml2c.h"
  #include "XML_test.XMLSTRUCT.h"
  
  int XMLFREE_userinfo( struct XML_userinfo *pst )
  {...}

2.3.嵌入你的应用代码
 有了所有素材,你的应用代码编码变得简单。
 用前面生成的头文件里的结构体定义声明结构变量,也就是XML层次数据对应的结构,并用memset把它洗干净了。

 如果从缓冲区里读进来,用前面自动生成的函数,原型为:
  int XMLPARSEBUFFER_userinfo( char *pbuffer , int *pbuflen , struct XML_userinfo *pst );
 char *pbuffer和int *pbuflen为缓冲区首地址和有效长度,struct XML_userinfo *pst为数据映射到的c结构变量。
 如果从XML文件里读进来,用前面自动生成的函数,原型为:
  int XMLPARSEFILE_userinfo( char *xmlfilename , struct XML_userinfo *pst );
 char *xmlfilename为外部XML文件名,struct XML_userinfo *pst为数据映射到的c结构变量。

 如果写到缓冲区里去,用前面自动生成的函数,原型为:
  int XMLDUMPBUFFER_userinfo( struct XML_userinfo *pst , char *pbuffer , int *pbuflen );
 struct XML_userinfo *pst为数据源c结构变量,char *pbuffer和int *pbuflen为目标存放缓冲区首地址和缓冲区size。
 如果写到XML文件里,用前面自动生成的函数,原型为:
  int XMLDUMPFILE_userinfo( struct XML_userinfo *pst , char *xmlfilename );
 struct XML_userinfo *pst为数据源c结构变量,char *xmlfilename为要保存的外部XML文件名。

编译链接你的应用代码,看看最终效果,并爱上她。

3.附录
I.关于作者和本文
 作者目前供职于某城市商业银行,崇尚创意,工作之余搞点儿研究自得其乐,在银行系统架构、基于FastCGI的现代CGI平台、源代码自动化理论、跨平台函数库等都有较深的研究和实现,并著有三部散文诗集供退休后赏玩。此外还是狂热级休闲wow玩家,国际惯例:二区(月神殿)Maua,一区(铜龙军团)暗影战刃。
 你可以通过电子邮箱calvin.xml2c@gmail.com联系我,欢迎和我交流探讨共同提高^_^

(本工具目前为试验版本,因本工具造成的一切后果由使用者自负)
以上这句话有点儿吓人,呵呵呵^_^

II.xmd中已实现的数据类型描述
XML元数据类型(xmd内表达) c语言结构成员变量类型 类型描述
short short 短整型
int int 整型
long long 长整型
float float 浮点型
double double 双浮点型
char?? char[ ?? + 1 ] 定长字符串型
char* char* 变长字符串型①

date char[ 10 + 1 ] 日期
time char[ 9  + 1 ] 时间
datetime char[ 19 + 1 ] 日期时间
money double 金额②

BOOL BOOL 布尔值②

YESNO YESNO 是非值②

ONOFF ONOFF 开关值②

定个数XML子树结构 struct { } XXXXXX[ ?? ] ; 结构数组①

不定个数XML子树结构 struct { } *XXXXXX ;
long XXXXXX_count ;
long XXXXXX_size ; 结构数组指针①

①: PARSE系列函数自动调用alloc在堆上创建空间,DUMP列表函数则需要用户自己负责分配好空间并赋值_count和_size变量;应用处理完需要用户显式调用FREE系列函数释放空间。
②:待实现数据类型。

------------------------------------------------------
下载包地址 : http://bbs3.chinaunix.net/thread-1506492-1-2.html


W 3 C h i n a ( since 2003 ) 旗 下 站 点
苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
105.469ms