本文例子及相关解释来自文章cJSON的使用方法,修复原文中的几个明显错误和内存泄露,因在项目中需要使用到cJSON,因此本文主要学习cJSON的相关使用,源码阅读与解析在后续文章中更新。

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。易于人阅读和编写,同时也易于机器解析和生成。它基于JavaScript(Standard ECMA-262 3rd Edition - December 1999)的一个子集。 JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, C#, Java, JavaScript, Perl, Python等)。这些特性使JSON成为理想的数据交换语言。

cJSON从名字可知,整个项目都是以极标准的C来写的,意思说,可以跨各种平台使用了。cJSON是一个超轻巧,携带方便,单文件,简单的可以作为ANSI-C标准的JSON解析器。
cJSON github项目地址

cJSON,目前来说,就只有两个文件,一个cJSON.c 一个cJSON.h文件。使用的时候,自己创建好一个main.c文件后,将头文件include进去。
如果是在linux pc上,请使用以下命令进行编译:
gcc *.c -lm
-lm是链接的libc标准数学库。
使用例子:

封装json
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include"cJSON.h"
 
int main()
{
    cJSON * usr;
    cJSON *arry;
 
    usr=cJSON_CreateObject();   //创建根数据对象       
    cJSON_AddStringToObject(usr,"name","52coder");  //加入键值,加字符串
    cJSON_AddStringToObject(usr,"sex","man");
    cJSON_AddNumberToObject(usr,"age",25);  //加整数
    
    char *out = cJSON_Print(usr);   //将json形式转换成正常字符串形式
    printf("%s\n",out);
    
    // 释放内存  
    cJSON_Delete(usr);  
    free(out); 
 
        
}

程序运行结果:

{
    "name": "52coder",
    "sex":  "man",
    "age":  25
}

相关解释:
cJSON_CreateObject函数可创建一个根数据项,之后便可向该根数据项中添加string或int等内容,返回的是一个 cJSON的指针,注意,在这个指针用完了以后,需要手工调用 cJSON_Delete(root); 进行内存回收。

cJSON_AddNumberToObject向节点中添加子节点,例如此处添加name节点,节点值为字符串"52coder"。

需要注意的是 json 格式的数据,虽然也是一个字符串的样子,但这个时候还是无法当成普通的字符串进行使用,需要调用 cJSON_PrintUnformatted(root) 或者 cJSON_Print(root);来将json对象转换成普通的字符串,并且都是以该json对象的根为基点。两个API的区别即是:一个是没有格式的:也就是转换出的字符串中间不会有"\n" "\t"之类的东西存在,而cJSON_Print(root);打印出来是人看起来很舒服的格式。

因为函数内部封装有malloc函数,所以使用free函数释放被out占用的内存空间。

使用数组
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include"cJSON.h"
 
int main()
{
    cJSON *root, *js_body;
    root = cJSON_CreateArray();
    cJSON_AddItemToArray(root, cJSON_CreateString("Hello world"));
    cJSON_AddItemToArray(root, cJSON_CreateNumber(10)); 
    
    //char *s = cJSON_Print(root);
    char *s = cJSON_PrintUnformatted(root);
    
    if(s)
    {
        printf("%s\n",s);
        free(s);
    }
    
    if(root)
        cJSON_Delete(root);
 
    return 0;
}

程序运行结果:

["Hello world",10]
对象数组组合
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include"cJSON.h"
 
int main()
{
    cJSON *root, *js_body, *js_list;
    root = cJSON_CreateObject();
    cJSON_AddItemToObject(root,"body", js_body = cJSON_CreateArray());
    cJSON_AddItemToArray(js_body, js_list = cJSON_CreateObject());
    cJSON_AddStringToObject(js_list,"name","52coder");
    cJSON_AddNumberToObject(js_list,"status",100);
 
    //char *s = cJSON_Print(root);
    char *s = cJSON_PrintUnformatted(root);
    if(s)
    {
        printf("%s\n",s);
        free(s);
    }
    
    if(root)
        cJSON_Delete(root);
 
    return 0;

}
{"body":[{"name":"52coder","status":100}]}

解析json

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include"cJSON.h"
 
int main()
{
    cJSON *json,*json_name,*json_passwd,*json_num;
    char* out="{\"name\":\"52coder\",\"passwd\":\"admin\",\"num\":1}";
 
    json = cJSON_Parse(out); //解析成json形式
    json_name = cJSON_GetObjectItem( json , "name" );  //获取键值内容
    json_passwd = cJSON_GetObjectItem( json , "passwd" );
    json_num = cJSON_GetObjectItem( json , "num" );
 
    printf("name:%s,passwd:%s,num:%d\n",json_name->valuestring,json_passwd->valuestring,json_num->valueint);
 
    cJSON_Delete(json);  //释放内存 

    return 0;

}

运行结果:

name:52coder,passwd:admin,num:1

官方例子

/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
/*
  Copyright (c) 2009-2017 Dave Gamble and cJSON contributors

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.
*/

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

/* Used by some code below as an example datatype. */
struct record
{
    const char *precision;
    double lat;
    double lon;
    const char *address;
    const char *city;
    const char *state;
    const char *zip;
    const char *country;
};


/* Create a bunch of objects as demonstration. */
static int print_preallocated(cJSON *root)
{
    /* declarations */
    char *out = NULL;
    char *buf = NULL;
    char *buf_fail = NULL;
    size_t len = 0;
    size_t len_fail = 0;

    /* formatted print */
    out = cJSON_Print(root);

    /* create buffer to succeed */
    /* the extra 5 bytes are because of inaccuracies when reserving memory */
    len = strlen(out) + 5;
    buf = (char*)malloc(len);
    if (buf == NULL)
    {
        printf("Failed to allocate memory.\n");
        exit(1);
    }

    /* create buffer to fail */
    len_fail = strlen(out);
    buf_fail = (char*)malloc(len_fail);
    if (buf_fail == NULL)
    {
        printf("Failed to allocate memory.\n");
        exit(1);
    }

    /* Print to buffer */
    if (!cJSON_PrintPreallocated(root, buf, (int)len, 1)) {
        printf("cJSON_PrintPreallocated failed!\n");
        if (strcmp(out, buf) != 0) {
            printf("cJSON_PrintPreallocated not the same as cJSON_Print!\n");
            printf("cJSON_Print result:\n%s\n", out);
            printf("cJSON_PrintPreallocated result:\n%s\n", buf);
        }
        free(out);
        free(buf_fail);
        free(buf);
        return -1;
    }

    /* success */
    printf("%s\n", buf);

    /* force it to fail */
    if (cJSON_PrintPreallocated(root, buf_fail, (int)len_fail, 1)) {
        printf("cJSON_PrintPreallocated failed to show error with insufficient memory!\n");
        printf("cJSON_Print result:\n%s\n", out);
        printf("cJSON_PrintPreallocated result:\n%s\n", buf_fail);
        free(out);
        free(buf_fail);
        free(buf);
        return -1;
    }

    free(out);
    free(buf_fail);
    free(buf);
    return 0;
}

/* Create a bunch of objects as demonstration. */
static void create_objects(void)
{
    /* declare a few. */
    cJSON *root = NULL;
    cJSON *fmt = NULL;
    cJSON *img = NULL;
    cJSON *thm = NULL;
    cJSON *fld = NULL;
    int i = 0;

    /* Our "days of the week" array: */
    const char *strings[7] =
    {
        "Sunday",
        "Monday",
        "Tuesday",
        "Wednesday",
        "Thursday",
        "Friday",
        "Saturday"
    };
    /* Our matrix: */
    int numbers[3][3] =
    {
        {0, -1, 0},
        {1, 0, 0},
        {0 ,0, 1}
    };
    /* Our "gallery" item: */
    int ids[4] = { 116, 943, 234, 38793 };
    /* Our array of "records": */
    struct record fields[2] =
    {
        {
            "zip",
            37.7668,
            -1.223959e+2,
            "",
            "SAN FRANCISCO",
            "CA",
            "94107",
            "US"
        },
        {
            "zip",
            37.371991,
            -1.22026e+2,
            "",
            "SUNNYVALE",
            "CA",
            "94085",
            "US"
        }
    };
    volatile double zero = 0.0;

    /* Here we construct some JSON standards, from the JSON site. */

    /* Our "Video" datatype: */
    root = cJSON_CreateObject();
    cJSON_AddItemToObject(root, "name", cJSON_CreateString("Jack (\"Bee\") Nimble"));
    cJSON_AddItemToObject(root, "format", fmt = cJSON_CreateObject());
    cJSON_AddStringToObject(fmt, "type", "rect");
    cJSON_AddNumberToObject(fmt, "width", 1920);
    cJSON_AddNumberToObject(fmt, "height", 1080);
    cJSON_AddFalseToObject (fmt, "interlace");
    cJSON_AddNumberToObject(fmt, "frame rate", 24);

    /* Print to text */
    if (print_preallocated(root) != 0) {
        cJSON_Delete(root);
        exit(EXIT_FAILURE);
    }
    cJSON_Delete(root);

    /* Our "days of the week" array: */
    root = cJSON_CreateStringArray(strings, 7);

    if (print_preallocated(root) != 0) {
        cJSON_Delete(root);
        exit(EXIT_FAILURE);
    }
    cJSON_Delete(root);

    /* Our matrix: */
    root = cJSON_CreateArray();
    for (i = 0; i < 3; i++)
    {
        cJSON_AddItemToArray(root, cJSON_CreateIntArray(numbers[i], 3));
    }

    /* cJSON_ReplaceItemInArray(root, 1, cJSON_CreateString("Replacement")); */

    if (print_preallocated(root) != 0) {
        cJSON_Delete(root);
        exit(EXIT_FAILURE);
    }
    cJSON_Delete(root);

    /* Our "gallery" item: */
    root = cJSON_CreateObject();
    cJSON_AddItemToObject(root, "Image", img = cJSON_CreateObject());
    cJSON_AddNumberToObject(img, "Width", 800);
    cJSON_AddNumberToObject(img, "Height", 600);
    cJSON_AddStringToObject(img, "Title", "View from 15th Floor");
    cJSON_AddItemToObject(img, "Thumbnail", thm = cJSON_CreateObject());
    cJSON_AddStringToObject(thm, "Url", "http:/*www.example.com/image/481989943");
    cJSON_AddNumberToObject(thm, "Height", 125);
    cJSON_AddStringToObject(thm, "Width", "100");
    cJSON_AddItemToObject(img, "IDs", cJSON_CreateIntArray(ids, 4));

    if (print_preallocated(root) != 0) {
        cJSON_Delete(root);
        exit(EXIT_FAILURE);
    }
    cJSON_Delete(root);

    /* Our array of "records": */
    root = cJSON_CreateArray();
    for (i = 0; i < 2; i++)
    {
        cJSON_AddItemToArray(root, fld = cJSON_CreateObject());
        cJSON_AddStringToObject(fld, "precision", fields[i].precision);
        cJSON_AddNumberToObject(fld, "Latitude", fields[i].lat);
        cJSON_AddNumberToObject(fld, "Longitude", fields[i].lon);
        cJSON_AddStringToObject(fld, "Address", fields[i].address);
        cJSON_AddStringToObject(fld, "City", fields[i].city);
        cJSON_AddStringToObject(fld, "State", fields[i].state);
        cJSON_AddStringToObject(fld, "Zip", fields[i].zip);
        cJSON_AddStringToObject(fld, "Country", fields[i].country);
    }

    /* cJSON_ReplaceItemInObject(cJSON_GetArrayItem(root, 1), "City", cJSON_CreateIntArray(ids, 4)); */

    if (print_preallocated(root) != 0) {
        cJSON_Delete(root);
        exit(EXIT_FAILURE);
    }
    cJSON_Delete(root);

    root = cJSON_CreateObject();
    cJSON_AddNumberToObject(root, "number", 1.0 / zero);

    if (print_preallocated(root) != 0) {
        cJSON_Delete(root);
        exit(EXIT_FAILURE);
    }
    cJSON_Delete(root);
}

int CJSON_CDECL main(void)
{
    /* print the version */
    printf("Version: %s\n", cJSON_Version());

    /* Now some samplecode for building objects concisely: */
    create_objects();

    return 0;
}

程序输出:

Version: 1.7.11
{
    "name": "Jack (\"Bee\") Nimble",
    "format":   {
        "type": "rect",
        "width":    1920,
        "height":   1080,
        "interlace":    false,
        "frame rate":   24
    }
}
["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
[[0, -1, 0], [1, 0, 0], [0, 0, 1]]
{
    "Image":    {
        "Width":    800,
        "Height":   600,
        "Title":    "View from 15th Floor",
        "Thumbnail":    {
            "Url":  "http:/*www.example.com/image/481989943",
            "Height":   125,
            "Width":    "100"
        },
        "IDs":  [116, 943, 234, 38793]
    }
}
[{
        "precision":    "zip",
        "Latitude": 37.7668,
        "Longitude":    -122.3959,
        "Address":  "",
        "City": "SAN FRANCISCO",
        "State":    "CA",
        "Zip":  "94107",
        "Country":  "US"
    }, {
        "precision":    "zip",
        "Latitude": 37.371991,
        "Longitude":    -122.026,
        "Address":  "",
        "City": "SUNNYVALE",
        "State":    "CA",
        "Zip":  "94085",
        "Country":  "US"
    }]
{
    "number":   null
}