cjson

1. 引言

这段时间做个项目,要加一个配置文件。配置文件之前有用过ini、yaml、lua,这次正好碰巧项目里还要加MQTT,要用到json,就直接拿json来做配置文件了。
其实json很多人是不建议做配置文件的,毕竟没有注释,格式对于手写也不是很友善,但是就当尝试一下新事物嘛,感受一下总是好的。
OK,开始。

2. Json格式

Json相比与ini文件来说,格式要复杂一些。虽然基本单元也是键值对。

要搞清楚几个名词:

  • 键值
  • 对象
  • 数组

从参考链接1里摘了一段,基本上记住and理解这些就够了

JSON对象是一个无序的"名称/值"键值对的集合

  • 以"{“开始,以”}"结束,允许嵌套使用;
  • 每个名称和值成对出现,名称和值之间使用":"分隔;
  • 键值对之间用","分隔
  • 在这些字符前后允许存在无意义的空白符;

对于键值value,可以有如下值,也是json的基本数据类型:

  • 一个新的json对象:使用"{“和”}"表示
  • 数组:使用"[“和”]"表示
  • 数字:直接表示,可以是整数,也可以是浮点数
  • 字符串:使用引号"表示
  • 布尔值:false、true中的一个(必须是小写)
  • null

对于数组 array,就是键值(value)(注意不是键值对!)的集合

虽然就这么几句,真的理解还挺不容易的,写起来有时候也很容易犯错。
可能也是因为这个,才被觉得不太友善吧。
可以在 这里 做格式的规范和验证。

稍微解释一下键值类型,也就是基本的数据类型。

  1. 新的Json对象
    这个是实现多层的一个重要机制,对象一层层对应下去,实现多层。
    可见,"animal"对应的键值就是一个对象,即{“human”:{“age”:18}},
    而"human"对应的键值也是一个对象,即{“age”:18},
    最后"age"对应的键值是数值18.

{
“animal”:
{
“human”:
{
“age”: 18
}
}
}

  1. 数组
    数组的使用是比较容易混的,尤其和对象混在一起,在 JSON 中,数组值的类型必须属于字符串、数字、对象、数组、布尔或 null。
    e.g.

举例:Value为数组
{
“employees”: [
{ “firstName”:“Bill” , “lastName”:“Gates” },
{ “firstName”:“George” , “lastName”:“Bush” },
{ “firstName”:“Thomas” , “lastName”:“Carter” }
]
}
举例:value值为集合
{
“employees”: {
“employees1”:{ “firstName”:“Bill” , “lastName”:“Gates” },
“employees2”:{ “firstName”:“George” , “lastName”:“Bush” },
“employees3”:{ “firstName”:“Thomas” , “lastName”:“Carter” }
}
}

注意,不要写成

{
“employees”: {
{ “firstName”:“Bill” , “lastName”:“Gates” },
{ “firstName”:“George” , “lastName”:“Bush” },
{ “firstName”:“Thomas” , “lastName”:“Carter” }
}
}

这种,{ “firstName”:“Bill” , “lastName”:“Gates” } 、{ “firstName”:“George” , “lastName”:“Bush” } 和{ “firstName”:“Thomas” , “lastName”:“Carter” } 都是双引号包起来的,可见都是对象。
但是3个对象构成的集合,只能是数组,而不是一个新的对象。因此不能再用{}包括,要用[]。
数组可以是对象的集合。
但是对象不能是对象的集合,而是键值对的集合。

  1. 数字、字符串、布尔值、null
    这几个没啥说的,该咋用咋用把。
    数字包含 整数浮点数,可以直接写。
    字符串用双引号包裹。
    布尔值包括true 和 false。

3. cJson使用

cJson是一个使用纯c编写的json解析库,因为是纯C的,所以移植性非常好。同时文件结构十分简单,一个.c和一个.h即可,总共不到1k行,使用时包含头文件,就可以直接使用库接口。
cJson官方下载地址

3.1 核心结构体

/* The cJSON structure: */
typedef struct cJSON {
	struct cJSON *next,*prev;	/* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
	struct cJSON *child;		/* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
<span class="token keyword">int</span> type<span class="token punctuation">;</span>					<span class="token comment">/* The type of the item, as above. */</span>

<span class="token keyword">char</span> <span class="token operator">*</span>valuestring<span class="token punctuation">;</span>			<span class="token comment">/* The item's string, if type==cJSON_String */</span>
<span class="token keyword">int</span> valueint<span class="token punctuation">;</span>				<span class="token comment">/* The item's number, if type==cJSON_Number */</span>
<span class="token keyword">double</span> valuedouble<span class="token punctuation">;</span>			<span class="token comment">/* The item's number, if type==cJSON_Number */</span>

<span class="token keyword">char</span> <span class="token operator">*</span>string<span class="token punctuation">;</span>				<span class="token comment">/* The item's name string, if this item is the child of, or is in the list of subitems of an object. */</span>

} cJSON;

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

下面单独介绍下每一项

  • next 指向同级的下一项
  • prev指向同级的上一项
  • child指向下一级,当值为 数组 或者一个新的对象时,此指针不为空
  • type,值的类型,可以在cJSON.h中找到定义:

    #define cJSON_False 0
    #define cJSON_True 1
    #define cJSON_NULL 2
    #define cJSON_Number 3
    #define cJSON_String 4
    #define cJSON_Array 5
    #define cJSON_Object 6

  • valuestring:如果value是string,可以从这里读取
  • valueint:如果value是整数,可以从这里读取
  • valuedouble:如果value是浮点数,可以从这里读取
  • string:key-value键对值中,key的名字

cJson的解析,并不是直接把整个json文件解析到一个结构体里,因为不同文件的结构不够,cJSON只是把最基本的单元–键值对进行解析,整个文件的实现需要人为的根据json的结构去遍历,因此十分通用。

3.2 重要接口

cJson使用起来比较简单,常用也就几个接口。基本上找个demo看一遍就清楚了。
举个简单的例子:

{
        "a1": 1, 
        "b1": [
                { "b1_1": 2.1}, 
                { "b1_2": 2.2}
        ], 
        "c1": {
                "c1_1": 3.1, 
                "c1_2": 3.2
        }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  1. cJSON_Parse
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */
extern cJSON *cJSON_Parse(const char *value);

 
 
  • 1
  • 2

cJSON_Parse可以解析整段json数据,注意这里入参是字符串,而不是文件路径,如果是读取配置文件,需要自己读取到字符串里,再传给cJSON_Parse。
cJSON_Parse会返回第一个键值对的指针。
假设字符串为上例,cJSON_Parse返回指针指向最外层{}内容,prev 是null,next 是null,child 指向"a1":1的键值对,type 为6(cJSON_Object)。很显然,{ “a1”: 1, “b1”: [
{ “b1_1”: 2.1}, { “b1_2”: 2.2} ], “c1”: {“c1_1”: 3.1, “c1_2”: 3.2}}是3个键值对的集合,类型是对象(object).

  1. cJSON_GetObjectItem
    根据key名称来,返回当前入参指针所指键对值的某个键(Key)的value的指针(cJson类型)
/* Get item "string" from object. Case insensitive. */
extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);

 
 
  • 1
  • 2

如上例,如果cJSON的指针p指向"c1"所在键值对,此键值对的value类型为一个对象object,调用cJSON_GetObjectItem(p,“b1_1”),可以返回b1_1所在键值对的指针。

  1. cJSON_GetArraySize / cJSON_GetArrayItem
/* Returns the number of items in an array (or object). */
extern int	  cJSON_GetArraySize(cJSON *array);
/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);

 
 
  • 1
  • 2
  • 3
  • 4

cJSON_GetArraySize 获取当前数组的成员个数。
如上例,如果cJSON的指针p指向"b1"所在键值对,此键值对的value类型为array数组,调用 cJSON_GetArraySize 可返回2。此数组有2个成员。

“b1”: [
{ “b1_1”: 2.1},
{ “b1_2”: 2.2}
],

同样,使用cJSON_GetArrayItem(p,0)返回指针指向{ “b1_1”: 2.1},使用cJSON_GetArrayItem(p,1)返回指针指向{ “b1_2”: 2.2},

3.3 遇到的问题

  1. 移植后第一次编译,报错:

cJSON.h:58:26: error: unknown type name ‘size_t’

解决方案:编译时不应该编译cJSON.h文件

  1. 找不到cJSON.h
    在这里插入图片描述
    把 #include <cJSON.h> 改成了 #include “cJSON.h”

  2. 找不到floor和pow函数:
    在这里插入图片描述
    链接的时候加入 -lm ,添加math库就好。

  3. cJSON_Parse失败,返回null
    json的格式是没有问题的,就是使用cJSON_Parse解析一直返回null。
    查了一下,是因为每一级的最后一个value后面不能加",",虽然有些格式化工具并不会报这个错。
    e.g.

{
	"a":1,
	"b":2
}

 
 
  • 1
  • 2
  • 3
  • 4

可以解析,但是

{
	"a":1,
	"b":2,
}
  • 1
  • 2
  • 3
  • 4
  • 5

就无法解析。

4. 参考文档

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
cJSON是一个用于在C语言操作JSON数据的开发库。您可以通过在Linux系统上编译和安装cJSON库来使用它。以下是使用cJSON库创建和操作JSON数据的示例代码: ```c #include <stdio.h> #include "cJSON.h" int main() { cJSON *root = cJSON_CreateObject(); // 创建根节点 cJSON *item = cJSON_CreateObject(); // 创建semantic节点 cJSON *next = cJSON_CreateObject(); // 创建slots节点 cJSON_AddItemToObject(root, "rc", cJSON_CreateNumber(0)); // 在根节点下添加rc节点 cJSON_AddItemToObject(root, "operation", cJSON_CreateString("CALL")); // 在根节点下添加operation节点 cJSON_AddItemToObject(root, "service", cJSON_CreateString("telephone")); // 在根节点下添加service节点 cJSON_AddItemToObject(root, "text", cJSON_CreateString("打电话给张三")); // 在根节点下添加text节点 cJSON_AddItemToObject(root, "semantic", item); // 在根节点下添加semantic节点 cJSON_AddItemToObject(item, "slots", next); // 在semantic节点下添加slots节点 cJSON_AddItemToObject(next, "name", cJSON_CreateString("张三")); // 在slots节点下添加name节点 printf("%s\n", cJSON_Print(root)); // 打印整个JSON字符串 return 0; } ``` 这段代码将创建一个包含您提供的样例JSON数据的JSON对象,并打印出整个JSON字符串。您可以根据您的需求修改和扩展这段代码来操作JSON数据。 请注意,您需要在编译和链接过程将cJSON库链接到您的项目。您可以通过克隆cJSON源码并将其添加到您的项目来完成这一步骤。有关如何在Linux上使用cJSON库的更多信息,请参考cJSON库的文档。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值