免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: iunknown
打印 上一主题 下一主题

[C] SPDataPickle: C语言的结构体和 xml/json/protobuf 的自动转化(移植到 win32 平台) [复制链接]

论坛徽章:
0
11 [报告]
发表于 2009-10-11 11:43 |只看该作者
原帖由 yulihua49 于 2009-10-10 16:05 发表

在网络上,各种不同系统,二进制不方便。
实际上是用模板映射结构,要很仔细保持模板和结构的关系。
使用xml格式做模板效率较低,在C语言里我更喜欢用结构做模板,其他语言可以用xml。

我也搞了一个JSON ...



这个自动序列化的关键,就在于要在 c/c++ 实现类似 java 的反射机制。
java 的反射机制,是由于在语言级别为每个类生成了 metainfo ,我们可以直接使用。
c/c++ 中,对于 structure 默认没有生成 metainfo ,因此需要我们自己来生成。

spdatapickle 中,xml 文件在运行时是不需要的,只是在编译期需要。
通过 xml 文件的描述,自动生成 structure ,同时生成 structure 的 metainfo ,
可以确保 structure 和 metainfo 是对应的。这些 metainfo 就在 dp_xyzaddrbook.cpp 文件中。

这些 metainfo ,主要信息包含 structure 中各个字段的类型,名称,偏移位置,编号。
有了这些信息,就可以遍历一个 structure 的所有字段,这就相当于具有了 java 中的反射机制。

有了这套反射机制,很多 java 中能做的事情,在 c/c++ 中都是可以做的。比如 java 的自动序列化,或者 ORM 。


关于 DTO ,从下面的连接来看
http://space.itpub.net/8804348/viewspace-478471

里面提到的映射模板,是要人工手写么?这个映射模板,就相当于上面提到的 metainfo 。
在 spdatapickle 里面,这个 metainfo 是通过从 xml 描述中自动生成的,同时由于 structure 也是从 xml 描述生成的,
因此可以保证 structure 和 metainfo 是完全一致的。


  1. SRM的映射模板,相当于iBates的映像文件:
  2. T_PkgType seat_type[] = {                /* 席位表 */
  3.         {CH_DATE,YEAR_TO_DAY_LEN,"start_date",YEAR_TO_DAY,-1}, /* 始发日期 YYYY-MM-DD*/
  4.         {CH_CHAR,5,"beg_station"},        /* 上车站 */
  5.         {CH_CHAR,13,"Train_no"},        /* 始发车次 */
  6.         {CH_CHAR,7,"run_train"},        /* 运行车次 */
  7. ......
  8.         {-1,0,0}
  9. };
复制代码


对应 spdatapickle 里面,生成的 metainfo 类似是这样



  1. 自动生成的结构体

  2. typedef struct tagXYZPhoneNumber {
  3.     char mType[10];
  4.     int mPrimary;
  5.     char * mContent;
  6. } XYZPhoneNumber_t;

  7. 自动生成的 metainfo

  8. static SP_DPMetaField_t gMetaXYZPhoneNumber [] = {
  9.     { sizeof(SP_DPMetaField_t), 1, "Type",                             
  10.        // 第一个字段是这个 metafield 本身的大小,为以后扩展留下余地。 第二个字段是 编号,在 protobuf 中用到。第三个是字段名。
  11.         SP_DP_FIELD_OFFSET(XYZPhoneNumber_t, mType), eTypeSPDPChar, 1, 1, 10,
  12.        // 第四个字段是这个字段在结构体中的偏移位置。第五个字段是字段类型。第六个字段表明是否指针。
  13.        // 第七个字段表示是否为必要字段。第8个字段表明是否定为数组。
  14.         "", sizeof(char[10]), sizeof(char), 0 },
  15.         // 第九个字段用于指针类型中,指明指针指向的内容的长度。第十个字段表明这个字段的总大小。第十一个字段表明这个字段每个元素的大小。
  16.         // 第十二个字段表明,这个字段是否为用于指明另外一个字段长度的。
  17.     { sizeof(SP_DPMetaField_t), 2, "Primary",
  18.         SP_DP_FIELD_OFFSET(XYZPhoneNumber_t, mPrimary), eTypeSPDPInt32, 0, 1, 0,
  19.         "", sizeof(int), sizeof(int), 0 },
  20.     { sizeof(SP_DPMetaField_t), 3, "Content",
  21.         SP_DP_FIELD_OFFSET(XYZPhoneNumber_t, mContent), eTypeSPDPChar, 1, 1, 0,
  22.         "", sizeof(char *), sizeof(char), 0 }
  23. };
复制代码

[ 本帖最后由 iunknown 于 2009-10-11 12:02 编辑 ]

论坛徽章:
0
12 [报告]
发表于 2009-10-11 11:52 |只看该作者
原帖由 sbc19861004 于 2009-10-10 16:14 发表
序列化是个说大就大,说小就小的问题,和兼容性,异构度有关,好像json比较受吹捧



类似 google protobuf 定义的 wire format ,在兼容性方面其实是很好的。
它的格式设计,已经充分考虑了异构的平台。

二进制的格式,对于传输大量的数据,请求非常频繁的场景下,相对于文本还是有一些性能上的好处的。

json 相对于 xml ,在有效负载方面的确有明显的优势。
劣势可能就在于不像 xml 那样,有 ms,ibm 这些大公司在后面推广。

论坛徽章:
0
13 [报告]
发表于 2009-11-06 23:14 |只看该作者

发布 0.5 版本,移植到 win32 平台

发布 0.5 版本,包括
1.protobuf 的一些 bugfix
2.移植到 win32 平台

http://spdatapickle.googlecode.c ... ckle-0.5.src.tar.gz

论坛徽章:
0
14 [报告]
发表于 2009-11-07 13:17 |只看该作者
原帖由 iunknown 于 2009-1-22 07:06 发表
SPDataPickle 是一个用于在 C语言的结构体和 xml/json 之间做自动转化的库。

http://code.google.com/p/spdatapickle
http://spdatapickle.googlecode.c ... ckle-0.5.src.tar.gz

大家可能对 ...


大概看了一下代码。有个疑问。
假如用你的代码来实现RPC。传入的是序列化的一个结构体,但是反序列化的机器上假如对应的描述文件比较旧,多了一个不用的field或者少一个新加的field,反序列化还能成功吗(去掉不关心的field)?

论坛徽章:
0
15 [报告]
发表于 2009-11-07 14:22 |只看该作者
请问lz

这个库可以支持数组或则c++stl容器类型吗, 结构嵌套, 容器嵌套, 结构容器嵌套等复杂类型, 都支持吗?

论坛徽章:
0
16 [报告]
发表于 2009-11-07 16:30 |只看该作者
原帖由 emacsnw 于 2009-11-7 13:17 发表


大概看了一下代码。有个疑问。
假如用你的代码来实现RPC。传入的是序列化的一个结构体,但是反序列化的机器上假如对应的描述文件比较旧,多了一个不用的field或者少一个新加的field,反序列化还能成功吗( ...


这个问题主要和反序列化的时候,以什么为依据有关。
一个是以数据包为依据,数据包里面有什么数据,就一定要在 struct 中找到对应的 field;
一个是以struct为依据,struct 有什么字段,就从数据包中找对应的数据。

spdatapickle 采用的是后面这种方法,以 struct 为依据。

因此对于反序列化的时候,struct少一个字段,数据包中多一个字段的情况,
由于是以 struct 为依据,因此数据包中多出来的数据自动被忽略。

对于 struct 多一个字段,数据包少一个字段的情况,如果这个字段被设置为是必须的,那么反序列化就会报错。
如果这个字段不是必须的,就不会报错,字段默认是被 memset 为 0 值的。

在 field 元素中,加上 required="0" ,表示字段不是必须的。


  1.         <struct name="Email">
  2.                 <field id="1" name="Type"    type="char" arraysize="10" />
  3.                 <field id="2" name="Address" type="*char" />
  4.                 <field id="3" name="Nickname" type="*char" required="0" />
  5.         </struct>
复制代码


这个 required 的功能,就是为了接口升级而设计的。
如果不要求 spdatapickle 做严格的校验,可以把所有字段设置为 非必须的,然后由应用程序自行做校验。
如果要求 spdatapickle 做严格的校验,那么可以在升级过程中,把字段设置为非必须,在全部程序更新了之后,再把字段设置为必须的。

[ 本帖最后由 iunknown 于 2009-11-7 16:34 编辑 ]

论坛徽章:
0
17 [报告]
发表于 2009-11-07 16:39 |只看该作者
原帖由 iunknown 于 2009-11-7 00:30 发表


这个问题主要和反序列化的时候,以什么为依据有关。
一个是以数据包为依据,数据包里面有什么数据,就一定要在 struct 中找到对应的 field;
一个是以struct为依据,struct 有什么字段,就从数据包中找对 ...


谢谢解释。我对protocol buffer这部分比较感兴趣。
简单看了一下你的解码代码,好像解码的时候没有对不认识的field进行处理,直接丢弃了?
这样的话,wire过来的bytes你如果用了一个比如是旧版本的解码器,那么数据里有几个fields不认识,忽略,使用的时候没问题。
如果你再把刚解码得到的结构体wire出去,别人受到的就不是原来的。
google protocol buffer是通过unknown_field_set来存储这些fields,这样wire回去的时候可以附上,不会损失信息。
这样是不是比较好一点?

论坛徽章:
0
18 [报告]
发表于 2009-11-07 16:39 |只看该作者
原帖由 xhl 于 2009-11-7 14:22 发表
请问lz

这个库可以支持数组或则c++stl容器类型吗, 结构嵌套, 容器嵌套, 结构容器嵌套等复杂类型, 都支持吗?


这个库只支持纯 C 的struct ,支持 struct 嵌套,支持数组。
不支持 C++ 的类,不支持 stl 容器。

要支持 C++ 的类,或者 stl 容器,可以看看 google 的 protobuf 。
spdatapickle 可以认为是 google 的 protobuf 的一个轻量版。
在我看来,google 的 protobuf 功能强大,但是也比较庞大,不够轻量。

论坛徽章:
0
19 [报告]
发表于 2009-11-07 16:48 |只看该作者
原帖由 emacsnw 于 2009-11-7 16:39 发表


简单看了一下你的解码代码,好像解码的时候没有对不认识的field进行处理,直接丢弃了?
这样的话,wire过来的bytes你如果用了一个比如是旧版本的解码器,那么数据里有几个fields不认识,忽略,使用的时候没问题。
如果你再把刚解码得到的结构体wire出去,别人受到的就不是原来的。
google protocol buffer是通过unknown_field_set来存储这些fields,这样wire回去的时候可以附上,不会损失信息。
这样是不是比较好一点?


受教,多谢指点。google 的 protobuf 没有仔细看,原来还有这样的机制。有这样的功能的确非常灵活。

猜想这个功能的作用是这样的:
接受一个包,把包解码为结构体,对结构体部分的字段做修改,然后重新编码,再发送出去。
这个功能可以用于 proxy 的场合。

这个功能不错,以后如果有需要,可以考虑加上去。

论坛徽章:
0
20 [报告]
发表于 2014-11-06 22:38 |只看该作者
那么多年过去了,不知道楼主还在么嘿嘿

最近因为项目需求,我也需要一个类似protobuf的东西。但是因为协议是我们自己的,所以没法直接用protobuf,于是就自己写了。在搜索资料的过程中看到了这篇帖子。

我觉得把整个类似protobuf的东西开源出来可能意义不大,别人直接选protobuf或者像我一样有自定义的需求,因此我觉得比较有用的是给C结构体加反射以及实现与Json互转的这部分代码。所以我把这部分代码封装了一下,开源在github上了,叫cobj,有兴趣的朋友也可以看看:https://github.com/xphh/cobj

通过cobj的规则生成的object目前支持:

* int(整型)
* CSTR(一种自定义的常量字符串,你可以看作是Java的String,不过得记得要自己释放内存。)
* BOOL(从int直接typedef的,但是转成Json是true/false)
* 可以嵌套子结构体
* 支持int、CSTR、结构体的list(相当于支持泛型),实现为ArrayList

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP