C语言解析JSON源码

2020-01-09

关键字:cJSON、linux JSON解析



JSON 是一种在互联网领域内很常用的轻量级数据交换协议。

它与 XML 的地位差不多,但就笔者而言,笔者更喜欢 JSON 的风格,因为它更符合我们的思维习惯,同样一份数据,JSON 格式的就是比 XML 要清晰明了一些。

最近笔者需要在 C语言 上解析 JSON 格式,在网上一顿找,找到一份很不错的开源代码。经过一阵研究与修改以后,终于变成了让笔者用的很顺手的 C语言 版 JSON 解析器。

现将这份经笔者小小修改过的代码记录一下,一来想给自己作个备忘,二来希望能帮到有同样需求的同学。

这份源码以及一份简易 demo 被打包至博客园网盘:

https://files.cnblogs.com/files/chorm590/C%E8%AF%AD%E8%A8%80%E8%A7%A3%E6%9E%90JSON%E7%A4%BA%E4%BE%8B.zip

这个压缩内包含有两份源码文件:

1、cJSON 解析器原始版与示例以及 readme 说明

2、经笔者修改过的示例代码

这里仅以笔者修改过的代码来作简要讲解。

经笔者修改过的代码文件共有 3 个代码文件,如下图所示:

其中 demo.c 是演示程序,共编写了 3 种常见的 JSON 数据格式及其解析方式来展示 cJSON 的用法。整个示例程序非常简单,同学们稍加阅读定能领悟其用法。

在这个解析器中,所有的 JSON 节点都被抽象成是一个 cJSON 对象,即 cJSON 结构体:

如上图所示,在这个解析器中,JSON 对象节点与 JSON 数组节点被绑在 next, prev, child 三个指针变量中。但凡涉及到 JSON 对象与 JSON 数组的解析,都可以快速地用这三个指针变量来找到对应的值。

本节点的类型则被记录在 type 变量中。type 的可选值如下图所示:

节点的值则被保存在 valuestring, valueint, valuedouble 中。其中 boolean 值是被当成 valueint 保存的。true 以 1 表示,false 以 0 表示。

而节点自身的名称,则是被保存在 struct cJSON 结构图所示的 string 变量中。

至于最后一个 pure_json 变量,则是笔者自己添加的。添加它的目的是为了保存去除了额外的空格字符与换行字符以后的纯 JSON 文本。是的,这个 cJSON 解析器默认情况下只能解析最标准格式的 JSON 文本,如下图所示:

而不能解析排版过后的 JSON 格式,如下图所示:

笔者前面提到的“稍稍修改了一下的版本”就是指添加了可以解析这种排版过后的 JSON 文本的功能。笔者为了在将“格式化”后的文本用完以后可以方便地 free 掉,就在 cJSON 结构体中添加了这个 pur_string 变量。同时,笔者这个格式化还可以保留字符串值中的空格字符与换行字符。

在使用这个解析器解析 JSON 时,只需要将原始 JSON 字符串传入 cJSON_Parse() 函数中即可自动将整个字符串解析一遍,并创建对应的节点链表。

不过必须要注意,由于解析 JSON 是使用 malloc 来分配内存空间的,因此在使用完以后一定要释放掉这些内存。释放内存的方式也简单,直接将根节点作为参数传入 cJSON_Delete() 函数中即可。

至于这个解析器解析的流程,有兴趣的同学可以自己去跟踪 cJSON.c 的源码实现。它并不复杂,笔者就不再这里赘述了。

如果你时间有限,也可以直接参考或修改笔者的示例代码来直接应用。

最后,贴上笔者的演示代码:

#include <stdio.h>
#include "cJSON.h"

static void parse_normal_json();
static void parse_object_json();
static void parse_array_json();

int main()
{
    printf("hello world\n");

    parse_normal_json();
    parse_object_json();
    parse_array_json();

    return 0;
}

static void parse_normal_json()
{
    printf("\n\n\n>>> parse_normal_json()\n\n");
    const char *json = "\n\
    {\n        \"key0\":\"Js   on is a fun\nny data for      mat!\",\n\
        \"key1\":\"value1\",\n\
        \"key2\":26,\n\
        \"key3\":false\n\
    }\n    ";

    printf("json:\n%s\n\n", json);

    cJSON *root = cJSON_Parse(json);
    if(root == 0)
    {
        printf("error\n");
        return;
    }

    cJSON *key0_node = cJSON_GetObjectItem(root, "key0");
    if(key0_node == 0)
        return;
    printf("key0 name:\n\t%s\nkey0 value:\n\t%s\n", key0_node->string, key0_node->valuestring);

    cJSON *key1_node = cJSON_GetObjectItem(root, "key1");
    if(key1_node == 0)
        return;
    printf("key1 value:\n\t%s\n", key1_node->valuestring);

    cJSON *key2_node = cJSON_GetObjectItem(root, "key2");
    if(key2_node == 0)
        return;
    printf("key2 value:\n\t%d\n", key2_node->valueint);

    cJSON *key3_node = cJSON_GetObjectItem(root, "key3");
    if(key3_node == 0)
        return;
    printf("key3 value:\n\t%d\n", key1_node->valueint);

    cJSON_Delete(root);
}

static void parse_object_json()
{
    printf("\n\n\n>>> parse_object_json()\n\n");
    const char *json = "\n\
    {\n        \"obj\":{\n\
            \"key\":71,\n\
            \"name\":22\n\
        }\n    }\n    ";

    printf("json:\n%s\n\n", json);

    cJSON *root = cJSON_Parse(json);
    if(root == 0)
    {
        printf("error\n");
        return;
    }

    cJSON *obj_node = cJSON_GetObjectItem(root, "obj");
    if(obj_node == 0)
        return;

    cJSON *key_node = cJSON_GetObjectItem(obj_node, "key");
    if(key_node == 0)
        return;
    printf("key value:\n\t%d\n", key_node->valueint);

    cJSON *child = obj_node->child;
    if(child == 0)
        return;

    printf("key name:\n\t%s\nkey value:\n\t%d\n", child->string, child->valueint);
    cJSON *name_node = child->next;
    if(name_node == 0)
        return;
    printf("name name:\n\t%s\nname value:\n\t%d\n", name_node->string, name_node->valueint);

    cJSON_Delete(root);
}

static void parse_array_json()
{
     printf("\n\n\n>>> parse_array_json()\n\n");
    const char *json = "\n\
    {\n        \"arrs\":[15,3,99,108,22]\n\
    }\n    ";

    printf("json:\n%s\n\n", json);

    cJSON *root = cJSON_Parse(json);
    if(root == 0)
    {
        printf("error\n");
        return;
    }

    cJSON *arrs_node = cJSON_GetObjectItem(root, "arrs");
    if(arrs_node == 0)
        return;

    if(arrs_node->type == cJSON_Array)
    {
        cJSON *node = arrs_node->child;
        int i = 0;
        while(node != 0)
        {
            printf("item%d:\n\t%d\n", i++, node->valueint);
            node = node->next;
        }
    }
    else
    {
        printf("arrs_node is not a json_array.\n");
    }

    cJSON_Delete(root);
}

cJSON的演示代码



C语言解析JSON源码

原文地址:https://www.cnblogs.com/chorm590/p/12152467.html

时间: 2024-11-06 17:27:32

C语言解析JSON源码的相关文章

GlusterFS源码解析 —— GlusterFS 源码安装

安装环境: CentOS6.2 glusterfs-3.4.3 GlusterFS 挂载需要 fuse 支持,如果你的内核版本低于 2.6.16 则需要下载fuse的源码包自行编译安装,也可下载 fuse 的rpm包.安装fuse的方法我就不说了,不会源码安装的直接去rpmfind.net上下载rpm即可.高于此版本的内核中已经有了fuse.ko的模块,需要的时候可以执行以下命令进行加载: modprobe -b fuse 1.下载GlusterFS的源码包,目前已经有更新版本 : wget h

[Go语言]从Docker源码学习Go——init()方法和identifier首字母大小写区分

init()方法 如果想在一个go文件里,进行一些初始化的工作,可以把代码放到init()方法中. init()方法先被执行. func init() { // initialization of package } 注意:它是没有输入参数和返回参数的. identifier首字母大小写区分 当identifier(比如constant,variable,type,function,struct field,...)的命名首字母是大写的话,那表示这个identifier在包外是可以见的: 类似p

[Go语言]从Docker源码学习Go——main函数

Go程序从main包下的main函数开始执行,当main执行结束后,程序退出. Docker的main函数在 docker/docker/docker.go package main //Import needed packages; import ( ... "github.com/docker/docker/api" ... ) //define const; const ( defaultCaFile = "ca.pem" defaultKeyFile = &

[Go语言]从Docker源码学习Go——指针和Structs

这两天在看reflect这个包在Docker中的使用时,遇到了各种问题,最后虽然知道怎么用了. 但是对于这块的原理还不是太懂,于是把"THE WAY TO GO"中关键的几章看了下. 继续坚持往下写,争取能说明白. 源码 还是先看Docker中源码, docker/api/client/cli.go type DockerCli struct { proto string addr string configFile *registry.ConfigFile in io.ReadClo

[Go语言]从Docker源码学习Go——结构和函数的定义

Docker在最近很火,而作为Docker的开发语言-Go也再次被大家提到. 已经使用Docker一段时间了,但是对于源码,尤其是其开发语言Go却一直是一知半解. 最近准备利用空余时间从Docker源代码入手来学习一下Go,同时对Docker的实现也希望可以提高一个层次. 有兴趣的可以一起讨论,学习. 准备工作: 1. Docker源代码https://github.com/docker/docker (版本1.1.2) 2. 安装开发工具LiteIDE, 这个是官方的Go语言的IDE 3. G

c 语言简单计算器源码

//  main.c //  计算器 //  Created by qianfeng on 14-7-15. //  Copyright (c) 2014年 ___FGY___. All rights reserved. //iPhone自带计算器不够好,由于你技术出众,你被安排去开发一款iOS新式计算器. /*项目经理认为计算器第一版要支持表达式求值,所以要求如下: 输入任意表达式 求出他的值(支持负数,不支持小数) 这里支持6种表达式 () * / + - ()优先级最高, * /优先级其次

Android中图片加载框架Glide解析2----从源码的角度理解Glide的执行流程

转载地址:http://blog.csdn.net/guolin_blog/article/details/53939176 在本系列的上一篇文章中,我们学习了Glide的基本用法,体验了这个图片加载框架的强大功能,以及它非常简便的API.还没有看过上一篇文章的朋友,建议先去阅读 Android图片加载框架最全解析(一),Glide的基本用法 . 在多数情况下,我们想要在界面上加载并展示一张图片只需要一行代码就能实现,如下所示: Glide.with(this).load(url).into(i

Python解析器源码加密系列之(二):一次使用标准c的FILE*访问内存块的尝试

摘要:由于近期打算修改Python解释器以实现pyc文件的加密/解密,出于保密的要求,解密之后的数据只能放在内存中,不能写入到文件中.但是后续的解析pyc文件的代码又只能接受FILE*作为入参,所以就提出了一种把通过FILE*来访问内存的需求,下文是针对这个需求的几个方面的尝试及其结论. 以下尝试的前提是:Win7 + VS2010. 在vc中,FILE其实就是_iobuf,定义如下: struct _iobuf { char *_ptr; //文件输入的下一个位置 int _cnt; //当前

[Go语言]从Docker源码学习Go——if语句和map结构

if语句 继续看docker.go文件的main函数 if reexec.Init() { return } go语言的if不需要像其它语言那样必须加括号,而且,可以在判断以前,增加赋值语句 语法 IfStmt = "if" [ SimpleStmt ";" ] Expression Block [ "else" ( IfStmt | Block ) ] . 例子 if x := f(); x < y { return x } else i