目录
入门级问题
为什么使用json?
什么是json?
json-c库
json源码
安装方法
json-c API
Json类型
C-API
将一个字符串转换成符合json格式的字符串(json对象)
定义一个字符串数组
定义一个json_object结构体指针
把一个字符串转换成一个json对象
将转换成json对象的字符串的地址给结构体指针
组合json格式的json对象
第一步,创建空json对象
第二步,往空的json对象中填键值对
第三步,将C字符串转换成json字符串格式的对象
第四步,将整数转换成json格式的对象
解析json对象
第一步,根据键名,从json对象获取对应数据的json对象
第二步,根据数据类型,将数据对应的json对象转化为对应类型的数据
json数组
创建json数组对象
往数组里面填充元素
把数组对象嵌套到json对象中
从对象中把数组对象中每一个元素对象解析出来
Json-C/S架构json数据传输
目前我们学习完了Linux系统编程和网络编程,现在开始学习linux常用库!
json是一个非常常用的一种库,它跟语言是没有关系的,不仅仅是在C语言里面, 很多语言里面多可以用,它是一种数据包的格式。
入门级问题
为什么使用json?
以QQ注册的场景为例。
当我们注册QQ的时候,我们需要输入很多信息,比如账号,密码还有一些以防之后忘记密码需要找回密码时所需的邮箱账号,或者密保问题等等,这些信息有将放在一个结构体里面,注册成功后,我们登录的时候,就只需要用到账号和密码,那么之前注册的结构体里的其他成员就用不上了,为了节约内存空间只好又重新创建一个登录时用的结构体,这个结构体只放账号和密码,但是这样如果每次一个任务都要重开一个结构体,就太麻烦了。
如果不开新的结构体,之后什么信息都往注册时用的结构体里面添加,这个结构体的体量会变得越来越大,在执行不同的任务时,有可能结构体里面的很多成员是用不上的,这样很浪费资源。
json就是为了解决以上这些问题的。Json不像结构体一旦创建,里面的成员就固定了,Json的格式是灵活的。这就是我们在Linux开发里面为什么要使用json的原因,尤其是在CS架构里面,在客户端和服务器进行交互数据的时候,json比结构体更加实用。
什么是json?
JSON(JavaScript Object Notation, JS对象简谱)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。
json其实就是一个键值对
一些合法的Json实例:
{“name”: ”Jack”, ”sex” : ”male”}
{“name”: ”Jack”, ”age” : 18, ”address”: {“country”: “china”, “zip-code”: “10000”}} //json嵌套
{“a”: 1, “b”: [1, 2, 3]}
以上比如“name”: ”Jack”就是一个json对象,也就是一个键值对,json允许没有“键”,只有“值”,也就是说只有”Jack”,没有“name”也是可以的。不同的json对象用逗号隔开。一个Json对象内部是用冒号:隔开的,json里面嵌套对象用花括号{ }隔开。
json里面无论是字符串还是字符都是用双引号,没有单引号。
json-c库
json支持很多种语言
json-c 库中是在嵌入式开发中常用的库。因为很多地方都以json数据数据交互协议, 尤其嵌入式web数据交互时通常会用到json格式, 因此如果需要在产品端进行json数据解析 , json-c 是一个比较不错的选择。
以智能家居的智能开关为例,智能开关和普通的开关相比,它的特别之处在于可以通过手机来控制开/关。手机先把数据发给服务器,服务器再把数据发送到开关上面,开关作为一个嵌入式产品,它要跟服务器不停地进行数据的交互。在嵌入式端我们用到json-c,因为嵌入式端一般都是用C语言来写的,所以需要移植json-c库
json源码
链接:https://pan.baidu.com/s/19LJNLTYj--yKWfoYUjt83g
提取码:asn5
安装方法
把上面源码拉到虚拟机上的一个文件夹下,随便一个文件夹都行,路径不指定。然后执行这四句命令就安装成功了。
./autogen.sh
./configure
make
make install
json-c API
Json类型
安装好Json-c之后可以在这个头文件中找到json的类型
支持的对象类型在这个枚举里面
我们常用的就是这个json对象(含有键值对的json对象)
比如这就是一个object类型,
如果只有值,没有键,也可以作为一个json对象,只不过这个json的类型是这个,也就是字符串类型的
还有int,也可以作为json的类型
比如这个json对象的类型就是int,
还有array数组类型
比如
C-API
接下来测试一下将一个字符串转换成符合json格式的json对象
将一个字符串转换成符合json格式的字符串(json对象)
定义一个字符串数组
先定义一个字符串数组
注意,{ }里面的双引号前都要加上转义字符\
定义一个json_object结构体指针
然后要定义一个json_object结构体指针
这个结构体的原型可以在json_object_private.h这个头文件中找到
用这个结构体可以表示一个json对象。
把一个字符串转换成一个json对象
把一个字符串转换成一个json对象,我们需要用到这个函数
参数就是要转换成json对象的字符串
将转换成json对象的字符串的地址给结构体指针
注意我们要包含一个头文件:#include <json-c/json.h>,这个头文件间接包含了其他头文件
编译的时候要加上-ljson-c,因为这个库是我们自己装的,是个外来库。
注意:如果以上编译错误就把头文件#include <json-c/json.h>改成#include <json/json.h>,另外再加上#include <stdlib.h> (int64_t这个类型编译器没有识别。这个类型在stdlib.h头文件里面有定义,所以只要在第一行,#include <stdlib.h>就行,这行代码一定要放在json.h的前面),并且编译的时候加上-ljson。
接下来把json对象转换成json格式的字符串输出
用到这个函数:
参数就是Json对象
注意:此时编译运行后如果出现这个错误
就执行这些操作:
先输入命令:find /usr -name "libjson.so.0"
回车后得到一个路径,则复制路径,然后输入:cp 得到的路径 /lib
回车后再编译运行就没问题了
接下来我们来学习自己组合json格式的json对象
组合json格式的json对象
第一步,创建空json对象
用到这个函数
第二步,往空的json对象中填键值对
要用到这个函数
这个函数可以在json_oject.h这个头文件里找到详细解释
第一个参数是json对象,第二个参数是“键”,第三个参数是“值”。
第三步,将C字符串转换成json字符串格式的对象
但是我们前面说过一个json对象可以没有键,只有值,所以一个值就是一个json对象,因此第三个参数不是直接填上字符串类型的值就行了,还得把这个值先转换成json对象,此时要用到这个函数:
然后我们继续添加键和值
第四步,将整数转换成json格式的对象
如果值是int类型的数值,我们就需要用到这个函数来讲整型转换成对象
这样我们就完成了键值对的添加
最后可以打印出来看看对不对
编译运行
接下来学习解析,比如客户端通过socket将json对象发给了服务器,服务器收到这个object就要进行解析
解析json对象
我们刚刚添加的这个,里面每一对都是一个json对象
我们解析要做的事情就是把里面每一对都提取出来
比如我们根据”name”从json对象{ "name": "jack", "age": 11, "sex": "male" }中获取”jack”这个json对象,然后再将”jack”这个json对象转换成字符串输出就行了。
第一步,根据键名,从json对象获取对应数据的json对象
需要用到这个函数:
网上很多人会用这个函数
但是现在都推荐使用这个函数json_object_object_get_ex()
它的第一个参数是从哪个json对象里解析,第二个参数是“键”,第二个参数是存放了解析出来的Json对象的地址的指针的地址(二级指针)
但是由于虚拟机上只有json_object_object_get这个,所以这里我的代码就直接使用了json_object_object_get这个,它的第一个参数是json对象,第二个参数是键,返回值是解析出来的json对象(值)
第二步,根据数据类型,将数据对应的json对象转化为对应类型的数据
要先获取对象类型
获取对象类型需要用到这个函数:
返回值是这个枚举类型:
参数是json对象
然后我们对这个函数的返回值进行判断
如果在已经知道json对象的类型,我们可以不用获取类型和判断了,直接解析和调用对应的函数打印出来就行。
运行结果:
这就是我们解析出来的json对象
接下来学习json数组
json数组
创建json数组对象
需要用到这个函数
往数组里面填充元素
第一个参数是刚刚创建的数组对象,第二个参数是值(要转换成对象)
把数组对象嵌套到json对象中
数组在json对象类型中也是一个数组,所以就不需要转换了,直接把数组名(即数组的地址)传给json_object_object_add函数就可以了
运行结果
接下来就从这个obj这个对象中把数组对象中每一个元素对象解析出来
从对象中把数组对象中每一个元素对象解析出来
解析的时候我们需要用到这个函数获取Json数组的长度
参数是一个json对象
json数组对象里面的每一个元素也是一个单独的json对象,我们可以通过下标来获取这些元素对象,要用到这个函数
第一个参数是解析出来的数组对象,第二个参数是要获取的元素对象的下标
运行结果
这就是我们解析出来的数组对象里的每个元素对象
Json-C/S架构json数据传输
接下来我们来模拟一下客户端向服务器发送json格式的数据的过程
代码演示:
这里我们要用到之前我们写的TCP服务器的代码
在客户端的代码中,在发送数据之前先搞好json对象,然后再发送给服务器
完整代码:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>//inet_addr的头文件
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>//close的头文件
#include <stdlib.h>
#include <json/json.h>
int main()
{
//创建socket
int sockfd=socket(PF_INET,SOCK_STREAM,0);
if(-1==sockfd)
{
perror("socket");
exit(1);
}
//发送连接请求
struct sockaddr_in server_info;//保存服务器的信息
bzero(&server_info,sizeof(server_info));
server_info.sin_family=PF_INET;
server_info.sin_port=htons(7000);
server_info.sin_addr.s_addr=inet_addr("192.168.0.163");
if(connect(sockfd,(struct sockaddr*)&server_info,sizeof(server_info))==-1)
{
perror("connect");
exit(2);
}
//发送数据
//创建json对象
struct json_object *json=json_object_new_object();
//填充对象
json_object_object_add(json,"name",json_object_new_string("jack"));//记得将json格式的字符串转成json格式的对象
json_object_object_add(json,"age",json_object_new_int(11));
json_object_object_add(json,"sex",json_object_new_string("male"));
//发送对象
//将json格式的对象转成json格式的字符串类型才能传给send
const char*buf=json_object_to_json_string(json);
if(send(sockfd,buf,strlen(buf),0)==-1)//最后一个参数写成0默认就行
{
perror("send");
}
printf("字符串%s发送成功! 长度 %ld \n",buf,strlen(buf));
close(sockfd);//关闭socket
return 0;
}
在服务器端的代码中,当收到客户端的连接请求时,接收客户端的数据,然后解析并打印输出
完整代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <sys/socket.h>//inet_addr的头文件
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>//bzero的头文件
#include <unistd.h>
#include <stdlib.h>
#include <json/json.h>
int main()
{
//创建socket
int sockfd=socket(PF_INET,SOCK_STREAM,0);//地址族:IPV4协议,套接字类型:流式套接字
if(-1==sockfd)
{
perror("socket");
exit(1);
}
//绑定信息
struct sockaddr_in server_info;//用于保存服务器的信息:IP,PORT,(还有个地址族)
bzero(&server_info,sizeof(struct sockaddr_in));//清空
server_info.sin_family=PF_INET;//地址族
server_info.sin_port=htons(7000);//端口号,大于1024都行,记得转换字节序
//server_info.sin_addr.s_addr=inet_addr("127.0.0.1");//这个地址每一台电脑都有回环IP地址用于测试,记得将字符串转换成长整形,并且要记得包含头文件
server_info.sin_addr.s_addr=inet_addr("192.168.0.163");//对外通信
if(bind(sockfd,(struct sockaddr*)&server_info,sizeof(server_info))==-1)//记得将结构体类型强转一下
{
perror("bind");
exit(2);
}
//设置监听队列
if(listen(sockfd,10)==-1)//队列大小填10用于测试
{
perror("listen");
exit(3);
}
//程序停在这里监听......
printf("等待客户端的连接...\n");
//接受连接(阻塞),一旦有客户端向服务器发起连接就调用函数接收
struct sockaddr_in client_info;//用于保存客户端的信息
int length=sizeof(client_info);
int fd=accept(sockfd,(struct sockaddr*)&client_info,(socklen_t *)&length);
if(-1==fd)
{
perror("accept");
exit(4);
}
printf("接受客户端的连接 %d\n",fd);
//连接后,服务器端要用一个buf接收数据
char*buf=(char*)malloc(sizeof(char)*1024);
//接受数据放在buf里面,注意,recv里面不能直接写sizeof(buf),这样buf是指针,只有四个字节,我们直接写1024
int size=recv(fd,buf,1024,0);//从哪读,读到哪,读多少,属性写成0就行
if(size==-1)
{
perror("recv");
exit(1);
}
//从客户端接受过来的是一个json格式的字符串数据,所以要解析
//先将json格式的字符串转换成json格式的对象
struct json_object*obj=json_tokener_parse(buf);
//解析
struct json_object*json;
json=json_object_object_get(obj,"name");
printf("name:%s\n",json_object_get_string(json));
json=json_object_object_get(obj,"age");
printf("age:%d\n",json_object_get_int(json));
json=json_object_object_get(obj,"sex");
printf("sex:%s\n",json_object_get_string(json));
close(fd);//关闭TCP连接,不能再接收数据
close(sockfd);//关闭socket,不能再处理客户端的请求
//sockfd用于处理客户端连接 fd用于处理客户端的消息
return 0;
}
运行结果:
综上,我们用json格式发送数据比用结构体要好。
下节开始学习libevent!
本篇就到这里,下篇继续!欢迎点击下方订阅本专栏↓↓↓