cJSON Note(4):转换字符串

引言(Introduction)

写在文章开头的一句话,怕什么真理无穷,进一步有一步的惊喜。

在第三篇的字符串解析中,介绍了cJSON的源码是如何实现解析字符串对象为一个json结构的。本文将介绍cJSON是如何实现将json结构转化为字符串的,因为该部分源码比较长,所以可能有些地方有些错误,还望纠正。

1. 实例(Example)

在分析源码之前,一样先给一个例子:

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

// {
//     "name": "Zhang San",    // string
//     "sex": 1,               // boolen
//     "height": 1.8
//     "family": [
//         {
//             "name": "Zhang Si",
//             "relationship": "Father"
//         },
//         {
//             "name": "Li Si",
//             "relationship": "Mother"
//         }
//     ],
//     "birthday": {
//         "year": 2000,
//         "month": 1,
//         "day":1
//     },
// }

int CJSON_CDECL main(void){
    cJSON *root = NULL;
    cJSON *family = NULL;
    cJSON *father = NULL;
    cJSON *mother = NULL;
    cJSON *birthday = NULL;
    char *output = NULL;
    char outputBuffer[1024];

    root = cJSON_CreateObject();
    cJSON_AddItemToObject(root, "name", cJSON_CreateString("Zhang San"));
    cJSON_AddTrueToObject(root, "sex");
    cJSON_AddNumberToObject(root, "height", 1.8);
    family = cJSON_AddArrayToObject(root, "family");

    cJSON_AddItemToArray(family, father = cJSON_CreateObject());
    cJSON_AddItemToObject(father, "name", cJSON_CreateString("Zhang Si"));
    cJSON_AddItemToObject(father, "relationship", cJSON_CreateString("Father"));

    cJSON_AddItemToArray(family, mother = cJSON_CreateObject());
    cJSON_AddItemToObject(mother, "name", cJSON_CreateString("Li Si"));
    cJSON_AddItemToObject(mother, "relationship", cJSON_CreateString("Mother"));

    cJSON_AddItemToObject(root, "birthday", birthday = cJSON_CreateObject());
    cJSON_AddNumberToObject(birthday, "year", 2000);
    cJSON_AddNumberToObject(birthday, "month", 1);
    cJSON_AddNumberToObject(birthday, "day", 1);

    output = cJSON_Print(root);
    printf("cJSON_Print(): \n%s\n", output);

    output = cJSON_PrintUnformatted(root);
    printf("cJSON_PrintUnformatted(): \n%s\n", output);

    output = cJSON_PrintBuffered(root, (int)sizeof(root) + 5, 1);
    printf("cJSON_PrintBuffered(): \n%s\n", output);

    if(cJSON_PrintPreallocated(root, outputBuffer, 1000, 1))
        printf("cJSON_PrintPreallocated(): \n%s\n", outputBuffer);

    free(output);
    cJSON_Delete(root);

    return 0;
}

cJSON中提供了四个接口:

  • cJSON_Print:将json结构转换为字符数组,字符数组中含有制表符与换行符,并返回该字符数组的指针;
  • cJSON_PrintUnformated:将json结构转换为字符数组,字符数组中不含有制表符与换行符,并返回该字符数组的指针;
  • cJSON_PrintBuffered:指定输出字符串长度的版本,可以选择是否按格式输出;
  • cJSON_PrintPreallocated:指定输出字符串指针与输出字符串长度,可以选择是否按格式输出。

这些函数在底层实现上是相近的,因为最终都会调用到"print_value",这将会在下面的源码分析上进行介绍。

在这里插入图片描述

2. 源码(Source code)

下面直接给出"cJSON_Print"以及"cJSON_PrintUnformated"的源码以及其注释。

  • cJSON_Print:

    // cJSON.h
    // 将cJSON对象转化为字符串,这是有格式的版本
    CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
    
    // cJSON.c
    CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item)
    {
        return (char*)print(item, true, &global_hooks);
    }
    
  • cJSON_PrintUnformated:

    // cJSON.h
    // 将cJSON对象转化为字符串,这是上面无格式的版本
    CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
    
    // cJSON.c
    CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item)
    {
        return (char*)print(item, false, &global_hooks);
    }
    

上面两个接口都调用了"print"函数,该函数是在cJSON.c文件中定义的静态函数,只能用于该文件。

// cJSON.c
static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks)
{
    static const size_t default_buffer_size = 256;
    // printbuffer为存储json转换过程中信息的结构体,与parsebuffer相似
    printbuffer buffer[1];
    // printed为最终输出的字符数组
    unsigned char *printed = NULL;

    memset(buffer, 0, sizeof(buffer));

    buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size);
    buffer->length = default_buffer_size;
    buffer->format = format;
    buffer->hooks = *hooks;
    if (buffer->buffer == NULL)
    {
        goto fail;
    }

    // 把当前cJSON内部的信息存入到buffer中
    if (!print_value(item, buffer))
    {
        goto fail;
    }
    update_offset(buffer);

    if (hooks->reallocate != NULL)
    {
        printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1);
        if (printed == NULL) {
            goto fail;
        }
        buffer->buffer = NULL;
    }
    else
    {
        printed = (unsigned char*) hooks->allocate(buffer->offset + 1);
        if (printed == NULL)
        {
            goto fail;
        }
        // 将buffer中的信息复制到printed中
        // #define cjson_min(a, b) (((a) < (b)) ? (a) : (b))
        memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1));
        printed[buffer->offset] = '\0';

        hooks->deallocate(buffer->buffer);
    }

    return printed;

fail:
    if (buffer->buffer != NULL)
    {
        hooks->deallocate(buffer->buffer);
    }

    if (printed != NULL)
    {
        hooks->deallocate(printed);
    }

    return NULL;
}

上示源码中,使用到了"printbuffer"结构体存储字符串信息,其内容为:

// cJSON.c
// 用于将cJSON转化为字符串的结构体
typedef struct
{
    unsigned char *buffer;  // 用于存放字符串
    size_t length;          // buffer的长度
    size_t offset;          // 输入指针在buffer中距离开端的偏移量
    size_t depth; 		   // 表示json结构体中嵌套的深度
    cJSON_bool noalloc;		// 表示是否需要分配重新分配内存
    cJSON_bool format; 		// 表示是否按照格式输出字符串
    internal_hooks hooks; 	// 内存分配函数
} printbuffer;

另外两个接口分别为:

  • cJSON_PrintBuffered:

    // cJSON.h
    // 这是使用指定buffer大小策略的Print版本,可以根据fmt选择是否具有格式。
    CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
    
    // cJSON.c
    CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)
    {
        printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
    
        if (prebuffer < 0)
        {
            return NULL;
        }
    
        // 预先按照指定大小分配内存
        p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer);
        if (!p.buffer)
        {
            return NULL;
        }
    
        p.length = (size_t)prebuffer;
        p.offset = 0;
        p.noalloc = false;			// 指明会对buffer进行扩容
        p.format = fmt;
        p.hooks = global_hooks;
    
        if (!print_value(item, &p))
        {
            global_hooks.deallocate(p.buffer);
            return NULL;
        }
    
        return (char*)p.buffer;
    }
    
  • cJSON_PrintPreallocated:

    // cJSON.h
    // 使用指定buffer的版本,解析的字符串会写入到指定的buffer中
    // NOTE: cJSON在预用内存的估计上并不是百分百准确的,所以可以多分配5个字节的内存
    CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
    
    // cJSON.c
    CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format)
    {
        printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
    
        if ((length < 0) || (buffer == NULL))
        {
            return false;
        }
    	
        // 将输出的数组指向指定的buffer
        p.buffer = (unsigned char*)buffer;
        p.length = (size_t)length;
        p.offset = 0;
        p.noalloc = true;		// 不支持对buffer扩容
        p.format = format;
        p.hooks = global_hooks;
    
        return print_value(item, &p);
    }
    

可以发现,上面四个接口最终都调用了"print_value"函数,该函数的源码如下:

// 将cJSON对象转化为字符串的实际操作函数
static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer)
{
    unsigned char *output = NULL;

    if ((item == NULL) || (output_buffer == NULL))
    {
        return false;
    }

    switch ((item->type) & 0xFF)
    {
        case cJSON_NULL:
            // ensure函数为output_buffer分配多余的内存
            // 为"null"字符串在最后添加5字节的空间,预留一个空字节的空间
            output = ensure(output_buffer, 5);
            if (output == NULL)
            {
                return false;
            }
            strcpy((char*)output, "null");
            return true;

        case cJSON_False:
            output = ensure(output_buffer, 6);
            if (output == NULL)
            {
                return false;
            }
            strcpy((char*)output, "false");
            return true;

        case cJSON_True:
            output = ensure(output_buffer, 5);
            if (output == NULL)
            {
                return false;
            }
            strcpy((char*)output, "true");
            return true;

        case cJSON_Number:
            // 将数值型对象转化为字符串型,因为存在double型的值,所以需要将其小数点位置也找出来
            return print_number(item, output_buffer);

        case cJSON_Raw:
        {
            size_t raw_length = 0;
            if (item->valuestring == NULL)
            {
                return false;
            }

            raw_length = strlen(item->valuestring) + sizeof("");
            output = ensure(output_buffer, raw_length);
            if (output == NULL)
            {
                return false;
            }
            memcpy(output, item->valuestring, raw_length);
            return true;
        }

        case cJSON_String:
            return print_string(item, output_buffer);

        case cJSON_Array:
            return print_array(item, output_buffer);

        case cJSON_Object:
            return print_object(item, output_buffer);

        default:
            return false;
    }
}

这里面涉及到了扩容函数"ensure",打印数字"print_number",打印字符串"print_string",打印数组"print_array"以及打印对象"print_object"的函数。

扩容函数"ensure"主要是为了确保buffer中还存在足够的空余空间,其函数实现如下:

// 检查p中的buffer是否还具有needed大小的空间,如果空余空间不足,则需要分配多余的内存(noalloc为false)
static unsigned char* ensure(printbuffer * const p, size_t needed)
{
    // newbuffer为新的buffer空间
    unsigned char *newbuffer = NULL;
    size_t newsize = 0;

    if ((p == NULL) || (p->buffer == NULL))
    {
        return NULL;
    }

    if ((p->length > 0) && (p->offset >= p->length))
    {
        return NULL;
    }

    if (needed > INT_MAX)
    {
        return NULL;
    }

    // 在当前偏移量的基础上判断是否需要分配更多的内存
    needed += p->offset + 1;
    if (needed <= p->length)
    {
        // 不需要分配更多的内存
        return p->buffer + p->offset;
    }

    if (p->noalloc) {
        // 判断是否能够分配内存给buffer
        return NULL;
    }
    // 后续代码实现分配两倍needed或者INT_MAX大小的内存
    if (needed > (INT_MAX / 2))
    {
        // INT_MAX/2 < needed <= INT_MAX/2时分配INT_MAX大小内存
        if (needed <= INT_MAX)
        {
            newsize = INT_MAX;
        }
        else
        {
            return NULL;
        }
    }
    else
    {
        // needed <= INT_MAX/2时分配needed大小内存
        newsize = needed * 2;
    }

    if (p->hooks.reallocate != NULL)
    {
        // 如果存在内存重分配函数
        newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize);
        if (newbuffer == NULL)
        {
            p->hooks.deallocate(p->buffer);
            p->length = 0;
            p->buffer = NULL;
            return NULL;
        }
    }
    else
    {
        // 如果不存在内存重分配函数
        newbuffer = (unsigned char*)p->hooks.allocate(newsize);
        if (!newbuffer)
        {
            p->hooks.deallocate(p->buffer);
            p->length = 0;
            p->buffer = NULL;

            return NULL;
        }
        
        memcpy(newbuffer, p->buffer, p->offset + 1);
        p->hooks.deallocate(p->buffer);
    }
    p->length = newsize;
    p->buffer = newbuffer;

    return newbuffer + p->offset;
}

打印各种类型的函数的源码依次为:

  • print_number

    // 将数字转化为字符串
    static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer)
    {
        unsigned char *output_pointer = NULL;			   // 指向output_buffer中的转化结果
        double d = item->valuedouble;					  // 需要转换的数字
        int length = 0;
        size_t i = 0; 								     // 迭代下标
        unsigned char number_buffer[26] = {0}; 			   // 临时存储数字字符的数组
        unsigned char decimal_point = get_decimal_point();  // 获取小数点的字符表达
        double test = 0.0;								 // number_buffer中的数字
    
        if (output_buffer == NULL)
        {
            return false;
        }
    
        // 检查需要打印的数字是否是nan或者inf
        if (isnan(d) || isinf(d))
        {
            length = sprintf((char*)number_buffer, "null");
        }
        else
        {
            // 检查d的小数点后15位,避免不必要的空间
            length = sprintf((char*)number_buffer, "%1.15g", d);
    
            // 检查number_buffer中的数字是否能够表示d
            if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d))
            {
                // 如果不能,则使用小数点后17位表示d
                length = sprintf((char*)number_buffer, "%1.17g", d);
            }
        }
    
    	// 转换错误或者发生溢出
        if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1)))
        {
            return false;
        }
    
    	// 为output_buffer分配内存
        output_pointer = ensure(output_buffer, (size_t)length + sizeof(""));
        if (output_pointer == NULL)
        {
            return false;
        }
    
        for (i = 0; i < ((size_t)length); i++)
        {
            // 利用output_pointer将number_buffer中的字符一一复制到output_buffer中
            if (number_buffer[i] == decimal_point)
            {
                output_pointer[i] = '.';
                continue;
            }
    
            output_pointer[i] = number_buffer[i];
        }
        // 添加结束字符
        output_pointer[i] = '\0';
    
        output_buffer->offset += (size_t)length;
    
        return true;
    }
    
  • print_string
    print_string中调用了"print_string_ptr"函数,因为在"print_string_ptr"中能够实现键名的打印与键值字符串的打印功能。

    // 调用print_string_ptr打印键名
    static cJSON_bool print_string(const cJSON * const item, printbuffer * const p)
    {
        return print_string_ptr((unsigned char*)item->valuestring, p);
    }
    

    print_string_ptr的源码如下:

    // 打印字符串,需要处理转义字符
    static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer)
    {
        const unsigned char *input_pointer = NULL;		 //	打印字符的遍历指针
        unsigned char *output = NULL; 					// 指向output_buffer中buffer的末尾地址
        unsigned char *output_pointer = NULL;			 // 输出字符的遍历指针
        size_t output_length = 0;						// 需要申请的多余空间
        size_t escape_characters = 0;					// 需要跳过的字节数,在处理取消转义字符时用到
    
        if (output_buffer == NULL)
        {
            return false;
        }
    
        if (input == NULL)
        {
            // 如果字符串为NULL,那么输出为"\"\""
            output = ensure(output_buffer, sizeof("\"\""));
            if (output == NULL)
            {
                return false;
            }
            strcpy((char*)output, "\"\"");
    
            return true;
        }
    
        for (input_pointer = input; *input_pointer; input_pointer++)
        {
            // 利用input_pointer遍历一遍input字符串
            switch (*input_pointer)
            {
                case '\"':
                case '\\':
                case '\b':
                case '\f':
                case '\n':
                case '\r':
                case '\t':
                    // 一个字符的转义序列,扩充一个字节
                    escape_characters++;
                    break;
                default:
                    if (*input_pointer < 32)
                    {
                        // 特殊字符的转义需要需要扩充五个字节
                        escape_characters += 5;
                    }
                    break;
            }
        }
        // 计算需要申请的多余空间
        output_length = (size_t)(input_pointer - input) + escape_characters;
    
        // 扩容
        output = ensure(output_buffer, output_length + sizeof("\"\""));
        if (output == NULL)
        {
            return false;
        }
    
        if (escape_characters == 0)
        {
            // 没有转义字符存在的情况
            output[0] = '\"';
            memcpy(output + 1, input, output_length);
            output[output_length + 1] = '\"';
            output[output_length + 2] = '\0';
    
            return true;
        }
    
        output[0] = '\"';
        output_pointer = output + 1;
        // 复制字符串到output
        for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++)
        {
            if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\'))
            {
                // 普通字符
                *output_pointer = *input_pointer;
            }
            else
            {
                // 需要转义的字符,先在前面加上'\\'
                *output_pointer++ = '\\';
                switch (*input_pointer)
                {
                    case '\\':
                        *output_pointer = '\\';
                        break;
                    case '\"':
                        *output_pointer = '\"';
                        break;
                    case '\b':
                        *output_pointer = 'b';
                        break;
                    case '\f':
                        *output_pointer = 'f';
                        break;
                    case '\n':
                        *output_pointer = 'n';
                        break;
                    case '\r':
                        *output_pointer = 'r';
                        break;
                    case '\t':
                        *output_pointer = 't';
                        break;
                    default:
                        // 特殊字符需要编码为unicode的格式
                        sprintf((char*)output_pointer, "u%04x", *input_pointer);
                        output_pointer += 4;
                        break;
                }
            }
        }
        output[output_length + 1] = '\"';
        output[output_length + 2] = '\0';
    
        // 字符串的输出都是 \" + string + \" + \0 的格式
        return true;
    }
    
  • print_array

    // 将json数组转化为字符串
    static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer)
    {
        unsigned char *output_pointer = NULL;
        size_t length = 0;
        cJSON *current_element = item->child;	// 通过链表遍历json数组
    
        if (output_buffer == NULL)
        {
            return false;
        }
    
    	// 为"["字符确保一个字节的空间
        output_pointer = ensure(output_buffer, 1);
        if (output_pointer == NULL)
        {
            return false;
        }
    
        *output_pointer = '[';
        output_buffer->offset++;
        output_buffer->depth++;
    
        while (current_element != NULL)
        {
            // 遍历array中的数据
            if (!print_value(current_element, output_buffer))
            {
                return false;
            }
            update_offset(output_buffer);
            if (current_element->next)
            {
                // 判断数组的相邻元素之间是否需要添加空格
                length = (size_t) (output_buffer->format ? 2 : 1);
                output_pointer = ensure(output_buffer, length + 1);
                if (output_pointer == NULL)
                {
                    return false;
                }
                *output_pointer++ = ',';
                if(output_buffer->format)
                {
                    *output_pointer++ = ' ';
                }
                *output_pointer = '\0';
                output_buffer->offset += length;
            }
            current_element = current_element->next;
        }
    
        output_pointer = ensure(output_buffer, 2);
        if (output_pointer == NULL)
        {
            return false;
        }
        *output_pointer++ = ']';
        *output_pointer = '\0';
        output_buffer->depth--;
        // 最终的结果是 [ + array + ] + \0
        return true;
    }
    
  • print_object

    // 将一个object对象转换为字符串
    static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer)
    {
        unsigned char *output_pointer = NULL;
        size_t length = 0;
        cJSON *current_item = item->child;
    
        if (output_buffer == NULL)
        {
            return false;
        }
    
    	// 为"{"与"\n"分配空间
        length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */
        output_pointer = ensure(output_buffer, length + 1);
        if (output_pointer == NULL)
        {
            return false;
        }
    
        *output_pointer++ = '{';
        output_buffer->depth++;
        if (output_buffer->format)
        {
            *output_pointer++ = '\n';
        }
        output_buffer->offset += length;
    
        while (current_item)
        {
            if (output_buffer->format)
            {
                size_t i;
                output_pointer = ensure(output_buffer, output_buffer->depth);
                if (output_pointer == NULL)
                {
                    return false;
                }
                for (i = 0; i < output_buffer->depth; i++)
                {
                    // 每增加一层深度,添加一个制表符'\t'
                    *output_pointer++ = '\t';
                }
                output_buffer->offset += output_buffer->depth;
            }
    
            // 转化键名
            if (!print_string_ptr((unsigned char*)current_item->string, output_buffer))
            {
                // 打印键值字符串
                return false;
            }
            update_offset(output_buffer);
    
            // 为 ":" 申请缓冲区大小
            length = (size_t) (output_buffer->format ? 2 : 1);
            output_pointer = ensure(output_buffer, length);
            if (output_pointer == NULL)
            {
                return false;
            }
            *output_pointer++ = ':';
            if (output_buffer->format)
            {
                *output_pointer++ = '\t';
            }
            output_buffer->offset += length;
    
            // 打印键值
            if (!print_value(current_item, output_buffer))
            {
                return false;
            }
            update_offset(output_buffer);
    
            // 确保一个","或者"\n",与下一个item的大小的空间
            length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0));
            output_pointer = ensure(output_buffer, length + 1);
            if (output_pointer == NULL)
            {
                return false;
            }
            if (current_item->next)
            {
                *output_pointer++ = ',';
            }
    
            if (output_buffer->format)
            {
                *output_pointer++ = '\n';
            }
            *output_pointer = '\0';
            output_buffer->offset += length;
    
            current_item = current_item->next;
        }
    
        output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2);
        if (output_pointer == NULL)
        {
            return false;
        }
        if (output_buffer->format)
        {
            size_t i;
            for (i = 0; i < (output_buffer->depth - 1); i++)
            {
                *output_pointer++ = '\t';
            }
        }
        *output_pointer++ = '}';
        *output_pointer = '\0';
        output_buffer->depth--;
        // 输出对象的格式为 { + cJSON object + } + \0
        return true;
    }
    

总结(Conclusion)

cJSON转化json结构为字符串时,需要分别针对不同类型的键值进行分支处理,每种情况需要的字节数不同。同时处理的逻辑也不尽相同,如处理数字时需要判断该数字大小,处理object对象时需要判断深度添加制表符等。从源码中学习,不断成长。

参考资料(Reference)

cjson-sourceforge

cjson-github

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
CJSON中,将JSON转换字符串有两种方法。第一种方法是构建JSON结构,然后使用cJSON_Print函数将其转换字符串。例如,你可以使用以下代码将JSON结构转换字符串: ```c cJSON* json = cJSON_CreateObject(); cJSON_AddStringToObject(json, "name", "json_test"); cJSON_AddNumberToObject(json, "num", 520); cJSON* arry = cJSON_CreateArray(); cJSON_AddItemToArray(arry, cJSON_CreateNumber(1)); cJSON_AddItemToArray(arry, cJSON_CreateNumber(2)); cJSON_AddItemToArray(arry, cJSON_CreateNumber(3)); cJSON_AddItemToObject(json, "arry", arry); char* json_str = cJSON_Print(json); printf("JSON字符串: %s\n", json_str); cJSON_Delete(json); free(json_str); ``` 第二种方法是直接将JSON结构转换字符串,而不需要构建一个新的JSON结构。你可以使用cJSON_PrintUnformatted函数将JSON结构转换字符串。例如,你可以使用以下代码将JSON结构转换字符串: ```c cJSON* json = cJSON_Parse(json_str); char* json_str = cJSON_PrintUnformatted(json); printf("JSON字符串: %s\n", json_str); cJSON_Delete(json); free(json_str); ``` 无论你选择哪种方法,记得在使用完字符串后,使用free函数释放内存。 #### 引用[.reference_title] - *1* *2* *3* [cjson:json字符串的理解](https://blog.csdn.net/Mr_zhang1911116/article/details/122653977)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值