[原创] help2416_JSON_Transmit_如何使用开源代码

lyzhangxiang   2014-8-10 09:02 楼主
一、JSON入门
JSON即JavaScript Object Natation,它是一种轻量级的数据交换格式,非常适合于服务器与JavaScript的交互.

尽管有许多宣传关于XML如何拥有跨平台,跨语言的优势.
然而,除非应用于Web Services,否则在普通的Web应用中,开发者经常为XML的解析伤透了脑筋,无论是服务器端生成或处理XML,还是客户端用JavaScript解析XML,都常常导致复杂的代码,极低的开发效率.
实际上,对于大多数Web应用来说,他们根本不需要复杂的XML来传输数据,XML的扩展性很少具有优势,许多AJAX应用甚至直接返回HTML片段来构建动态Web页面.
和返回XML并解析它相比,返回HTML片段大大降低了系统的复杂性,但同时缺少了一定的灵活性.
现在,JSON为 Web应用开发者提供了另一种数据交换格式.让我们来看看JSON到底是什么,同XML或HTML片段相比.JSON提供了更好的简单性和灵活性.


二、JSON数据格式解析
和XML一样,JSON也是基于纯文本的数据格式. 由于JSON天生是为JavaScript准备的,因此JSON的数据格式非常简单,您可以用JSON传输一个简单的String, Number, Boolean, 也可以传输一个数组,或者一个复杂的Object对象.
String, Number和Boolean用JSON表示非常简单. 例如,用JSON表示一个简单的String "abc" 其格式为"abc" .
除了字符 ", \, /和一些控制符(\b, \f, \n, \r, \t)需要编码外,其他Unicode字符可以直接输出.


JSON语法是JavaScript对象表示语法的子集
  1. 数据在名称/值对中
  2.         数据由逗号分隔
  3.         花括号保存对象
  4.         方括号保存数组
  5.         JSON名称/值对



JSON 数据的书写格式是:名称/值对
  1. 名称/值对组合中的名称写在前面(在双引号中),值对写在后面(同样在双引号中),中间用冒号隔开:
  2.         "firstName":"John"
  3.         这很容易理解,等价于这条JavaScript语句:
  4.         firstName="John"


JSON值可以是:
  1. 数字(整数或浮点数)
  2.         字符串(在双引号中)
  3.         逻辑值(true或false)
  4.         数组(在方括号中)
  5.         对象(在花括号中)
  6.         null



JSON结构有两种结构
        JSON简单说就是javascript中的对象和数组,所以这两种结构就是对象和数组两种结构,通过这两种结构可以表示各种复杂的结构
        1、对象:对象在js中表示为"{}"括起来的内容,数据结构为 {key:value, key:value,...}的键值对的结构,在面向对象的语言中,key为对象的属性,value为对应的属性值.
        所以很容易理解,取值方法为对象.key获取属性值,这个属性值的类型可以是数字、字符串、数组、对象几种.

        2、数组:数组在js中是中括号"[]"括起来的内容,数据结构为 ["java","javascript","c++",...],取值方式和所有语言中一样,使用索引获取,字段值的类型可以是数字、字符串、数组、对象几种.
        经过对象、数组2种结构就可以组合成复杂的数据结构了.
       
       
       
三、JSON格式举例

中国省份和城市用JSON表示如下:
  1. {
  2.         "name":"中国",
  3.         "province":[
  4.                 {
  5.                 "name":"黑龙江",
  6.                         "cities":{
  7.                         "city":["哈尔滨","大庆"]
  8.                         }
  9.                 },
  10.                 {
  11.                 "name":"广东",
  12.                         "cities":{
  13.                         "city":["广州","深圳","珠海"]
  14.                         }
  15.                 },
  16.                 {
  17.                 "name":"台湾",
  18.                 "cities":{
  19.                         "city":["台北","高雄"]
  20.                         }
  21.                 },
  22.                 {
  23.                 "name":"新疆",
  24.                         "cities":{
  25.                         "city":["乌鲁木齐"]
  26.                         }
  27.                 }
  28.         ]
  29. }



四、使用开源cJSON

github地址:https://github.com/openxc/cJSON

请自行下载源码,阅读README、LICENCE,API使用参考源码中函数说明和test程序.



五、使用开源cJSON

我做了一个与之前ttyS关联的测试程序,使用前面介绍的调用动态库方式调用ttyS相关函数,当然你可以可以把cJSON做成库的方式,大家可以动手实践一下.

下面看一下我的测试mian程序吧

  1. /**
  2. * brief  main
  3. * note   JSON测试
  4. * param  none
  5. * retval none
  6. */
  7. int main(int argc, char **argv)
  8. {
  9.         char ttyDev[20];
  10.         int  ttysFD = -1;
  11.         int  ttyRealRX = 0;
  12.        
  13.         cJSON *root, *attr;
  14.         char *jsonStr;
  15.        
  16.         if (argc == 2) {       
  17.                 strcpy(ttyDev, argv[1]);
  18.         } else {
  19.                 strcpy(ttyDev, "/dev/ttySAC1");
  20.         }
  21.        
  22.         ttyS_dbgLOG("#TTYS\n");
  23.         ttysFD = ttyS_Open(ttyDev);
  24.         if (ttysFD == -1) {
  25.                 ttyS_dbgLOG("ttyDev Open Failure\n");
  26.         }

  27.         if ( ttyS_Init(ttysFD, 115200, 'N', 8, 1, 'N') == FALSE ) {
  28.                 ttyS_dbgLOG("ttyDev Init Failure\n");
  29.         } else {
  30.                 ttyS_dbgLOG("ttyDev Init Success\n");
  31.         }

  32.         ttyS_Send( ttysFD, welcomeMsg, strlen(welcomeMsg) );
  33.        
  34.        
  35.         root = cJSON_CreateObject();       
  36.         cJSON_AddItemToObject( root, "name", cJSON_CreateString("helper2416") );
  37.         cJSON_AddItemToObject( root, "attr", attr = cJSON_CreateObject() );       
  38.        
  39.         cJSON_AddStringToObject(attr, "cpu",                        "s3c2416");
  40.         cJSON_AddStringToObject(attr, "memery",                        "64m");
  41.         cJSON_AddStringToObject(attr, "kernel",                        "linux 3.2.50");
  42.         cJSON_AddStringToObject(attr, "ip-addr",                "192.168.2.1");               
  43.         cJSON_AddNumberToObject(attr, "freq",                        534);
  44.         cJSON_AddNumberToObject(attr, "temperature",        32);
  45.         cJSON_AddFalseToObject (attr, "uboot");
  46.         cJSON_AddNumberToObject(attr, "server port",        8888);
  47.        
  48.        
  49.         while (1) {
  50.        
  51.                 bzero(&ttyRX, TTYS_BUF_MAX);
  52.                 ttyRealRX = ttyS_Receive( ttysFD, ttyRX, TTYS_BUF_MAX );

  53.                 if (ttyRX[0] == 'A') {
  54.                         ttyS_Send( ttysFD, jsonContext, strlen(jsonContext) );
  55.                         jsonStr = cJSON_Print(root);               
  56.                         ttyS_Send(ttysFD, jsonStr, strlen(jsonStr));
  57.                         ttyS_Send( ttysFD, "\n\n",     strlen("\n\n") );
  58.                         free(jsonStr);
  59.                 } else if (ttyRX[0] == 'B') {
  60.                         int _Port;
  61.                         cJSON *_attr;
  62.                         char svrPort[16];
  63.                         memset(svrPort, '\0', 16);
  64.                        
  65.                         _attr = cJSON_GetObjectItem(root,  "attr");
  66.                         _Port = cJSON_GetObjectItem(_attr, "server port")->valueint;
  67.                         sprintf(svrPort, "%d", _Port);
  68.                        
  69.                         ttyS_Send( ttysFD, attrPort, strlen(attrPort) );
  70.                         ttyS_Send( ttysFD, svrPort,  strlen(svrPort) );
  71.                         ttyS_Send( ttysFD, "\n\n",   strlen("\n\n") );
  72.                 }


主要功能,通过板子COM2进行交互,输入A之后板子会通过COM2发送JSON格式的字符串,输入B之后板子会发送JSON格式中最后一条的内容,这里是板子服务器的端口号.
这些都是用于演示如何使用它,你可以随便定义你要的功能,当然JSON需要配合js才能发挥它的魅力,不过用于交互传输数据也是很不错的,免了协议的东西.
我使用JSON传输数据的主要原因是直观,占用资源小,同时它的格式是人和机器都能够很好的理解,不像传统的F7(帧头) + 02(长度) + 01(数据一) + 02(数据二) + xx(校验) + FE(帧尾)这种不容易被人直接理解的东西.

JSON格式创建使用cJSON提供的函数,细节参考源码,提供了很多函数,基本上够用了.

cJSON_AddItemToObject                ---添加一个ITEM到对象
cJSON_AddStringToObject                ---添加一个字符串到对象
cJSON_AddNumberToObject                ---添加一个整数到对象

Makefile内容如下:
  1. #general Makefile

  2. CROSS                                        := arm-linux-
  3. CC                                                 := $(CROSS)gcc
  4. LDFLAGS                                        := -L./ -ldl -lm -lstdc++ -lpthread -lttyS
  5. CFLAGS                                        := -Wall -g

  6. objects                                 := main.o cJSON.o
  7. target                                        := cJSON_ttyS

  8. all : $(target)

  9. $(target) : $(objects)
  10.         $(CC) $(LDFLAGS) -o $(target) $(objects)

  11. cJSON.o :                         cJSON.h
  12. main.o :                        ttyS.h cJSON.h


  13. scp :
  14.         scp -P 22 ./$(target) root@192.168.1.200:/root

  15. scpdel :
  16.         ssh-keygen -f "/home/camelshoko/.ssh/known_hosts" -R [192.168.1.200]:22
  17.        
  18. .PHONY : clean

  19. clean :
  20.         -rm -rf $(target) $(objects)


注意这里的LDFLAGS中指定了需要依赖的动态库和库路径,我这里还包含了标准c++库、线程库这些在这里都是用不上的.

六、运行效果图

RUN
RUN.jpg

CONTEXT
CONTEXT.jpg

PORT
PORT.jpg

DEBUG
DEBUG.jpg


游客,如果您要查看本帖隐藏内容请回复


ttyS_JSON.zip (47.03 KB)
(下载次数: 1, 2014-8-10 09:02 上传)



电工

回复评论 (18)

JSON对我来说,还是一个高级的东西,不会。。。
My dreams will go on... http://www.jyxtec.com
点赞  2014-8-10 09:59
引用: spacexplorer 发表于 2014-8-10 09:59
JSON对我来说,还是一个高级的东西,不会。。。

我也不玩web,不过我喜欢用JSON格式来传输数据,免了协议的东西.
使用JSON格式传输数据的主要原因是直观,占用资源小,同时它的格式是人和机器都能够很好的理解,不像传统的F7(帧头) + 02(长度) + 01(数据一) + 02(数据二) + xx(校验) + FE(帧尾)这种不容易被人直接理解的东西.





电工
点赞  2014-8-10 13:04
学习!!!!
点赞  2014-8-10 18:03
这样的话也可以传输一个数组,或者一个复杂的Object对象的吧.................
中空板|防静电中空板www.cheng-sen.com
点赞  2014-8-11 09:47
引用: lyzhangxiang 发表于 2014-8-10 13:04
我也不玩web,不过我喜欢用JSON格式来传输数据,免了协议的东西.
使用JSON格式传输数据的主要原因是直观 ...

楼主的libttyS是从哪里下载的?
点赞  2014-8-11 10:16
电工
点赞  2014-8-11 11:03
引用: hbwangliang 发表于 2014-8-11 09:47
这样的话也可以传输一个数组,或者一个复杂的Object对象的吧.................

呵呵  随便你传什么,很直观哦
能玩web的搞起来传个数据什么的很easy
电工
点赞  2014-8-11 11:04
引用: wcabcd 发表于 2014-8-10 18:03
学习!!!!

有问题,可以跟帖讨论哦
电工
点赞  2014-8-11 11:04
引用: lyzhangxiang 发表于 2014-8-11 11:03
请参考我之前的帖子

https://bbs.eeworld.com.cn/thread-444517-1-1.html

参考楼主的上述两个帖子,没有找到ttyS.c这个文件,是否能共享一下。
点赞  2014-8-11 13:56
引用: wcabcd 发表于 2014-8-11 13:56
参考楼主的上述两个帖子,没有找到ttyS.c这个文件,是否能共享一下。

有的啊 在文字中啊
电工
点赞  2014-8-11 19:21
引用: lyzhangxiang 发表于 2014-8-11 19:21
有的啊 在文字中啊

没有啊,以下这两个帖子没有ttyS.c这个文件,只有ttyS_Open这些的函数,也没有ttyS_init函数,楼主把完整的ttyS.c文件发上来吧。https://bbs.eeworld.com.cn/thread-444681-1-1.html https://bbs.eeworld.com.cn/thread-444517-1-1.html



点赞  2014-8-11 21:02
引用: wcabcd 发表于 2014-8-11 21:02
没有啊,以下这两个帖子没有ttyS.c这个文件,只有ttyS_Open这些的函数,也没有ttyS_init函数,楼主把完整 ...

sorry,这个函数发漏掉了,其他的send和receive都有的啊,不只是open和close啊

  1. /**
  2. * brief  ttyS_Init
  3. * note   ttyS参数设置函数
  4. * param  None
  5. * retval None
  6. */
  7. int ttyS_Init(int fd, int Speed, int flowCtrl, int dataBits, int stopBits, int Parity)
  8. {
  9.         int i;
  10.         struct termios options;
  11.        
  12.         #define ENABLE_TCGET_ATTR                                                1
  13.        
  14.         /**
  15.          * tcgetattr(fd, &options)得到与fd指向对象的相关参数
  16.          * 并将它们保存于options,该函数,还可以测试配置是否正确,该串口是否可用等
  17.          */
  18. #if ENABLE_TCGET_ATTR
  19.         if ( tcgetattr( fd, &options) != 0 ) {
  20.                 ttyS_dbgLOG("Setup ttyS\n");
  21.                 return(FALSE);
  22.         }
  23. #endif

  24.         /* 设置串口输入波特率和输出波特率 */
  25.         for (i=0;  i<sizeof(ttys_Speedargs)/sizeof(int); i++) {
  26.                 if (Speed == ttys_Speedname[i]) {
  27.                         cfsetispeed(&options, ttys_Speedargs[i]);
  28.                         cfsetospeed(&options, ttys_Speedargs[i]);
  29.                 }
  30.         }

  31.         /* 修改控制模式,保证程序不会占用串口 */
  32.         options.c_cflag |= CLOCAL;

  33.         /* 修改控制模式,使得能够从串口中读取输入数据 */
  34.         options.c_cflag |= CREAD;

  35.         /* 设置数据流控制 */
  36.         switch(flowCtrl)
  37.         {
  38.                 //不使用流控制
  39.         case 'n' :
  40.         case 'N' :
  41.                 options.c_cflag &= ~CRTSCTS;
  42.                 break;

  43.                 //使用硬件流控制
  44.         case 'h' :
  45.         case 'H' :
  46.                 options.c_cflag |= CRTSCTS;
  47.                 break;

  48.                 //使用软件流控制
  49.         case 's' :
  50.         case 'S' :
  51.                 options.c_cflag |= IXON | IXOFF | IXANY;
  52.                 break;
  53.         }

  54.         /* 设置数据位---屏蔽其他标志位 */
  55.         options.c_cflag &= ~CSIZE;

  56.         switch (dataBits)
  57.         {
  58.         case 5:
  59.                 options.c_cflag |= CS5;
  60.                 break;

  61.         case 6:
  62.                 options.c_cflag |= CS6;
  63.                 break;

  64.         case 7:
  65.                 options.c_cflag |= CS7;
  66.                 break;
  67.         case 8:
  68.                 options.c_cflag |= CS8;
  69.                 break;

  70.         default:
  71.                 ttyS_dbgLOG("Unsupported data size\n");
  72.                 return (FALSE);
  73.         }

  74.         /* 设置校验位 */
  75.         switch (Parity)
  76.         {
  77.                 //无奇偶校验位
  78.         case 'n':
  79.         case 'N':
  80.                 options.c_cflag &= ~PARENB;
  81.                 options.c_iflag &= ~INPCK;
  82.                 break;

  83.                 //设置为奇校验
  84.         case 'o':
  85.         case 'O':
  86.                 options.c_cflag |= (PARODD | PARENB);
  87.                 options.c_iflag |= INPCK;
  88.                 break;

  89.                 //设置为偶校验
  90.         case 'e':
  91.         case 'E':
  92.                 options.c_cflag |= PARENB;
  93.                 options.c_cflag &= ~PARODD;
  94.                 options.c_iflag |= INPCK;
  95.                 break;

  96.                 //设置为空格
  97.         case 's':
  98.         case 'S':
  99.                 options.c_cflag &= ~PARENB;
  100.                 options.c_cflag &= ~CSTOPB;
  101.                 break;

  102.         default:
  103.                 ttyS_dbgLOG("Unsupported parity\n");
  104.                 return (FALSE);
  105.         }

  106.         /* 设置停止位 */
  107.         switch (stopBits)
  108.         {
  109.         case 1:
  110.                 options.c_cflag &= ~CSTOPB;
  111.                 break;

  112.         case 2:
  113.                 options.c_cflag |= CSTOPB;
  114.                 break;

  115.         default:
  116.                 ttyS_dbgLOG("Unsupported stop bits\n");
  117.                 return (FALSE);
  118.         }

  119.         /* 修改输出模式,原始数据输出 */
  120.         options.c_oflag &= ~OPOST;

  121.         /*
  122.          * 非标准模式
  123.          * ICANON---代表规范化方式
  124.          * ISIG---代表CTRL+C,否则0x03不能显示
  125.          * ICRNL和IGNCR----会导致0x0D变成0x0A
  126.          */
  127.         options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
  128.         options.c_iflag &= ~(ICRNL|IGNCR);
  129.        
  130.         options.c_iflag &= ~(BRKINT | INPCK | ISTRIP | IXON);

  131.         /*
  132.          * 设置等待时间和最小接收字符
  133.          *
  134.          * VTIME定义要求等待的时间量(取值不能大于cc_t)
  135.          * VMIN定义了要求等待的最小字节数
  136.          * options.c_cc[VTIME] = X;           //设置从获取到1个字节后开始计时的超时时间
  137.          * options.c_cc[VMIN]  = Y;             //设置要求等待的最小字节数
  138.          *
  139.          * 在原始模式下对read()函数的影响:
  140.          * X=0        Y!=0        函数read()只有在读取了Y个字节的数据或者收到一个信号的时候才返回
  141.          * X!=0        Y=0                即使没有数据可以读取,read()函数等待X时间量后返回
  142.          * X!=0        Y!=0        第一个字节数据到时开始,最先满足收到Y个字节或达超时时间X任意一个条件,read()返回
  143.          * X=0        Y=0                即使读取不到任何数据,函数read也会立即返回
  144.          */
  145.         options.c_cc[VTIME] = 0;       
  146.         options.c_cc[VMIN]         = 1;


  147.         /* 如果发生数据溢出,接收数据,但是不再读取 */
  148.         tcflush(fd, TCIFLUSH);

  149.         /* 激活配置(将修改后的termios数据设置到串口中) */
  150.         if ( tcsetattr(fd, TCSANOW, &options) != 0 ) {
  151.                 ttyS_dbgLOG("ttyS set failure\n");
  152.                 return (FALSE);
  153.         } else {
  154.                 return (TRUE);
  155.         }
  156. }



电工
点赞  2014-8-11 21:33
感谢分享,我编译试试,有问题还要再请教楼主
点赞  2014-8-11 22:42
引用: lyzhangxiang 发表于 2014-8-11 21:33 sorry,这个函数发漏掉了,其他的send和receive都有的啊,不只是open和close啊
哥们,编译报错,ttys_Speedargs ttys_Speedname 没定义,把这两个的定义发上来吧。 本帖最后由 wcabcd 于 2014-8-12 09:26 编辑
点赞  2014-8-12 09:16
引用: wcabcd 发表于 2014-8-12 09:16
哥们,编译报错,ttys_Speedargs  ttys_Speedname 没定义,把这两个的定义发上来吧。

sorry我笔记本没带

就是类似这种的啊 ,网上给你找的

int speed_arr[] = { B38400, B19200, B9600, B4800, B2400, B1200, B300,

                   B38400, B19200, B9600, B4800, B2400, B1200, B300, };

int name_arr[] = {38400,  19200,  9600,  4800,  2400,  1200,  300, 38400,

                            19200,  9600, 4800, 2400, 1200,  300, };


电工
点赞  2014-8-12 09:37
引用: lyzhangxiang 发表于 2014-8-12 09:37
sorry我笔记本没带

就是类似这种的啊 ,网上给你找的

好的,感谢楼主,已经编译通过,我将添加了ttyS.c的源码打包放在这里吧,方便其他网友使用学习,已经修改Makefile,将ttyS.c编译连接,没有制作成库。

    ttyS_JSON1.rar (2014-8-12 10:03 上传)

    13.68 KB, 下载次数: 4

    添加了ttyS.c文件直接编译链接,不使用ttyS库

点赞  2014-8-12 10:04
cJSON每次调用ADD或create函数,包括Print函数都会malloc一部分空间,头痛
点赞  2014-11-29 13:16
多谢分享
淘宝:https://viiot.taobao.com/Q群243090717 多年专业物联网行业经验,个人承接各类物联网外包项目
点赞  2016-4-27 14:32
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复