嵌入式全栈开发学习笔记---Linux常用库(json)

2024-09-01 12:28

本文主要是介绍嵌入式全栈开发学习笔记---Linux常用库(json),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

入门级问题

为什么使用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()
{//创建socketint 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格式的字符串类型才能传给sendconst 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);//关闭socketreturn 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()
{//创建socketint 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是指针,只有四个字节,我们直接写1024int 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!

本篇就到这里,下篇继续!欢迎点击下方订阅本专栏↓↓↓

这篇关于嵌入式全栈开发学习笔记---Linux常用库(json)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1126929

相关文章

Linux使用nload监控网络流量的方法

《Linux使用nload监控网络流量的方法》Linux中的nload命令是一个用于实时监控网络流量的工具,它提供了传入和传出流量的可视化表示,帮助用户一目了然地了解网络活动,本文给大家介绍了Linu... 目录简介安装示例用法基础用法指定网络接口限制显示特定流量类型指定刷新率设置流量速率的显示单位监控多个

ElasticSearch+Kibana通过Docker部署到Linux服务器中操作方法

《ElasticSearch+Kibana通过Docker部署到Linux服务器中操作方法》本文介绍了Elasticsearch的基本概念,包括文档和字段、索引和映射,还详细描述了如何通过Docker... 目录1、ElasticSearch概念2、ElasticSearch、Kibana和IK分词器部署

C#中读取XML文件的四种常用方法

《C#中读取XML文件的四种常用方法》Xml是Internet环境中跨平台的,依赖于内容的技术,是当前处理结构化文档信息的有力工具,下面我们就来看看C#中读取XML文件的方法都有哪些吧... 目录XML简介格式C#读取XML文件方法使用XmlDocument使用XmlTextReader/XmlTextWr

Linux流媒体服务器部署流程

《Linux流媒体服务器部署流程》文章详细介绍了流媒体服务器的部署步骤,包括更新系统、安装依赖组件、编译安装Nginx和RTMP模块、配置Nginx和FFmpeg,以及测试流媒体服务器的搭建... 目录流媒体服务器部署部署安装1.更新系统2.安装依赖组件3.解压4.编译安装(添加RTMP和openssl模块

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

linux下多个硬盘划分到同一挂载点问题

《linux下多个硬盘划分到同一挂载点问题》在Linux系统中,将多个硬盘划分到同一挂载点需要通过逻辑卷管理(LVM)来实现,首先,需要将物理存储设备(如硬盘分区)创建为物理卷,然后,将这些物理卷组成... 目录linux下多个硬盘划分到同一挂载点需要明确的几个概念硬盘插上默认的是非lvm总结Linux下多

Java深度学习库DJL实现Python的NumPy方式

《Java深度学习库DJL实现Python的NumPy方式》本文介绍了DJL库的背景和基本功能,包括NDArray的创建、数学运算、数据获取和设置等,同时,还展示了如何使用NDArray进行数据预处理... 目录1 NDArray 的背景介绍1.1 架构2 JavaDJL使用2.1 安装DJL2.2 基本操

linux进程D状态的解决思路分享

《linux进程D状态的解决思路分享》在Linux系统中,进程在内核模式下等待I/O完成时会进入不间断睡眠状态(D状态),这种状态下,进程无法通过普通方式被杀死,本文通过实验模拟了这种状态,并分析了如... 目录1. 问题描述2. 问题分析3. 实验模拟3.1 使用losetup创建一个卷作为pv的磁盘3.

CSS弹性布局常用设置方式

《CSS弹性布局常用设置方式》文章总结了CSS布局与样式的常用属性和技巧,包括视口单位、弹性盒子布局、浮动元素、背景和边框样式、文本和阴影效果、溢出隐藏、定位以及背景渐变等,通过这些技巧,可以实现复杂... 一、单位元素vm 1vm 为视口的1%vh 视口高的1%vmin 参照长边vmax 参照长边re

轻松上手MYSQL之JSON函数实现高效数据查询与操作

《轻松上手MYSQL之JSON函数实现高效数据查询与操作》:本文主要介绍轻松上手MYSQL之JSON函数实现高效数据查询与操作的相关资料,MySQL提供了多个JSON函数,用于处理和查询JSON数... 目录一、jsON_EXTRACT 提取指定数据二、JSON_UNQUOTE 取消双引号三、JSON_KE