当前位置:   article > 正文

零基础学习cJSON 源码详解与应用 (三)cJSON_Print();打印json

cjson_print

cJSON系列:

继上一章之后,这章讲json的打印。json存在的目的是为了数据交换的方便,而数据交换就比较通过传输来实现,json在互联网上的传输是以字符串的形式进行的,这就需要将cjson中一个个结构体打印成标准的json的字符串形式。

一,cJSON_Print();源码分析

CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);

注意,返回的字符串指针是通过动态内存分配的,使用完成后应该释放内存。

/*
 * 将一个json结构体及其子json输出为字符串
 * note:使用完字符串后要记得释放内存
 */
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item)
{
    return (char*)print(item, true, &global_hooks);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

其实调用的是print()函数,其中的format参数为true,说明默认是格式化输出(效果就是有换行,TAB等特殊符号):

/*
 * 将json转为字符串
 * format:true:格式化打印
 * 返回:字符串
 */
static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks)
{
    //默认print_buff长度,需要根据json结构体修改
    static const size_t default_buffer_size = 256;  
    printbuffer buffer[1];  //用于存储输出字符串
    unsigned char *printed = NULL;
	//内存初始为0
    memset(buffer, 0, sizeof(buffer));

    //创建默认的buff
    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;
    }

    //打印item的内容到buffer
    if (!print_value(item, buffer))
    {
        goto fail;
    }

    //更新buffer的指针偏移
    update_offset(buffer);

    //重新分配print_buffer的缓存,并将指针赋值给printed
    if (hooks->reallocate != NULL)
    {
        //reallocate在原指针上进行再分配
        printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1);
        if (printed == NULL) {
            goto fail;
        }
        buffer->buffer = NULL;
    }
    else /* otherwise copy the JSON over to a new buffer */
    {
        //系统不支持reallocate,给指针printed重新分配内存,复制并释放buffer->buffer
        printed = (unsigned char*) hooks->allocate(buffer->offset + 1);
        if (printed == NULL)
        {
            goto fail;
        }
        memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1));
        printed[buffer->offset] = '\0'; /* just to be sure */

        /* free the buffer */
        hooks->deallocate(buffer->buffer);
    }

    //返回指向打印的字符串的指针
    return printed;

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

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

    return NULL;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74

这里需要认识一下printbuff结构体该结构体用于辅助打印json:

//缓存json的输出内容
typedef struct
{
    unsigned char *buffer;  //存放结果字符串
    size_t length;  //内存总长度
    size_t offset;  //字符串长度
    size_t depth; //嵌套的层数/* current nesting depth (for formatted printing) */
    cJSON_bool noalloc;
    cJSON_bool format; /* is this print a formatted print */
    internal_hooks hooks;
} printbuffer;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

另外代码中出现了两个比较重要的函数:print_value(),update_offset()接下来将详解他们;

二,print_value();

/*
 * 打印item内容到output_buffer
 * 成功:true
 */
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;
    }

    //根据item的类型,打印内容
    switch ((item->type) & 0xFF)
    {
        case cJSON_NULL:
            //确保输出内存足够
            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:
            return print_number(item, output_buffer);
        //若是原生的json字符串数据,则直接取item->valuestring
        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;
        }

        //打印不同item的内容
        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;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80

print_value() 只打印item的键-值内容,因为不同类型的item,打印内容的逻辑不一样,所以在这个函数中,对不同类型的item作了不同的处理。

我们主要分析打印字符串,数组与object类型的item的代码:

2.1 ensure();

首先另一个同样重要的函数,ensure();人如其名,该函数的作用是确保buff的内存够用,并返回buffer的可用内存的指针。每次给printbuffer赋值前,都需要计算所需的内存,并调用ensure()确保内存够用。 分析如下:

/*
 *  检查printbuffer的缓存区与needed的大小,确保所需内存足够
 * 返回当前buffer的可用内存的指针
 */
static unsigned char* ensure(printbuffer * const p, size_t needed)
{
    unsigned char *newbuffer = NULL;
    size_t newsize = 0;

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

    //offset此时应该是0,确保offset的正确性
    if ((p->length > 0) && (p->offset >= p->length))
    {
        /* make sure that offset is valid */
        return NULL;
    }
    //限制缓存大小
    if (needed > INT_MAX)
    {
        /* sizes bigger than INT_MAX are currently not supported */
        return NULL;
    }

    //所需内存是offset+1这个1是放字符串结束符
    needed += p->offset + 1;    

    //所需要的内存足够用,返回可用内存的起点
    if (needed <= p->length)
    {
        return p->buffer + p->offset;
    }

    //否则内存不够,需要重新分配,检查分配函数是否存在
    if (p->noalloc) {
        return NULL;
    }

    /* calculate new buffer size */
    //计算所需要的内存,限制规模
    if (needed > (INT_MAX / 2))
    {
        /* overflow of int, use INT_MAX if possible */
        if (needed <= INT_MAX)
        {
            newsize = INT_MAX;
        }
        else
        {
            return NULL;
        }
    }
    else
    {
        newsize = needed * 2;
    }

    //reallocate在原指针上重新分配内存
    if (p->hooks.reallocate != NULL)
    {
        /* reallocate with realloc if available */
        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
    {
        /* otherwise reallocate manually */
        //系统不支持reallocate,使用allocate创建新的指针,再分配内存
        newbuffer = (unsigned char*)p->hooks.allocate(newsize);
        if (!newbuffer)
        {
            p->hooks.deallocate(p->buffer);
            p->length = 0;
            p->buffer = NULL;

            return NULL;
        }
        //给new_buff复制旧buff的内容
        if (newbuffer)
        {
            memcpy(newbuffer, p->buffer, p->offset + 1);
        }
        //再释放旧buff的内存
        p->hooks.deallocate(p->buffer);
    }

    //给p装上新的buff
    p->length = newsize;
    p->buffer = newbuffer;

    return newbuffer + p->offset;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103

2.2 print_string();

打印字符串类型的item,如:

“name”: “fool”

//将item中的数据复制到printbuffer
static cJSON_bool print_string(const cJSON * const item, printbuffer * const p)
{
    return print_string_ptr((unsigned char*)item->valuestring, p);
}
  • 1
  • 2
  • 3
  • 4
  • 5

函数的作用是将string类型的item里的字符串值打印到printbuff,实际实现是以下函数print_string_ptr(),它的作用是打印字符串指针

/*
 * 打印字符串
 * 例如input的内容可能是 "string",其中的双引号需要特殊处理
 */
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;
    unsigned char *output_pointer = NULL;
    size_t output_length = 0;
    /* numbers of additional characters needed for escaping */
    //需要补充字节数
    size_t escape_characters = 0;

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

    /* empty string */
    if (input == NULL)
    {
        //输出为空串
        output = ensure(output_buffer, sizeof("\"\""));
        if (output == NULL)
        {
            return false;
        }
        strcpy((char*)output, "\"\"");

        return true;
    }

    
    /* set "flag" to 1 if something needs to be escaped */
    //检查输入字符串中的特殊字符,如果是换行符'\n',则输出需要两个字符:'\'和'n'
    for (input_pointer = input; *input_pointer; input_pointer++)
    {
        switch (*input_pointer)
        {
            case '\"':
            case '\\':
            case '\b':  //退格(BS) ,将当前位置移到前一列
            case '\f':  //换页(FF),将当前位置移到下页开头
            case '\n': //换行(LF) ,将当前位置移到下一行开头
            case '\r':  //回车(CR) ,将当前位置移到本行开头
            case '\t':  //水平制表(HT)
                /* one character escape sequence */
                //需要额外一个字符
                escape_characters++;
                break;
            default:
                if (*input_pointer < 32)
                {
                    /* UTF-16 escape sequence uXXXX */
                    //需要额外5个字符
                    escape_characters += 5;
                }
                break;
        }
    }

    //输出的长度是输入长度+额外的字节
    output_length = (size_t)(input_pointer - input) + escape_characters;

    //确保输出buff足够大
    output = ensure(output_buffer, output_length + sizeof("\"\""));
    if (output == NULL)
    {
        return false;
    }

    //无额外字节(无转义字符)时直接复制字符串 记得带上双引号和结束符
    /* no characters have to be escaped */
    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;
    /* copy the string */
    for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++)
    {
        if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\'))
        {
            /* normal character, copy */
            //普通的字符直接复制
            *output_pointer = *input_pointer;
        }
        else    //特殊字符
        {
            /* character needs to be escaped */
            //需要补充的字符:主要是转义字符
            *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:
                    /* escape and print as unicode codepoint */
                    sprintf((char*)output_pointer, "u%04x", *input_pointer);
                    output_pointer += 4;
                    break;
            }
        }
    }
    output[output_length + 1] = '\"';
    output[output_length + 2] = '\0';

    return true;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137

需要关注的是特殊字符的处理,例如换行符LF的ascii码为0x0A,在内存中占一个字节,转换为字符串时为\n占有两个字符。
这也是该函数与字符串复制函数strcpy不同的地方。

2.3,print_number();

打印数字字符串

/*
 * 将数字转成字符串 赋值到printbuffer
 */
static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer)
{
    unsigned char *output_pointer = NULL;
    double d = item->valuedouble;   //获取item的数值
    int length = 0;     //数字的字符串长度
    size_t i = 0;
    unsigned char number_buffer[26]; /* temporary buffer to print the number into */
    unsigned char decimal_point = get_decimal_point();  //十进制小数点字符
    double test;

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

    /* This checks for NaN and Infinity */
    //检查json的数值是否无穷大
    if ((d * 0) != 0)
    {
        //在这里是将null赋值给number_buffer,表示该数值不可信
        length = sprintf((char*)number_buffer, "null");
    }
    else
    {
        //先尝试小数点后15位的精度打印
        /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */
        length = sprintf((char*)number_buffer, "%1.15g", d);

        /* Check whether the original double can be recovered */
        //检查是否能恢复原来的数据
        if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || ((double)test != d))
        {
            /* If not, print with 17 decimal places of precision */
            //如果无法还原,说明转换精度不够,提高到小数点后17位
            length = sprintf((char*)number_buffer, "%1.17g", d);
        }
    }

    /* sprintf failed or buffer overrun occurred */
    //检查number_buffer是否正常赋值或者溢出
    if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1)))
    {
        return false;
    }

    /* reserve appropriate space in the output */
    //确保内存足够
    output_pointer = ensure(output_buffer, (size_t)length + sizeof(""));
    if (output_pointer == NULL)
    {
        return false;
    }

    /* copy the printed number to the output and replace locale
     * dependent decimal point with '.' */
    //将number_buffer复制到output_pointer中,并添加小数点
    for (i = 0; i < ((size_t)length); i++)
    {
        if (number_buffer[i] == decimal_point)
        {
            output_pointer[i] = '.';
            continue;
        }

        output_pointer[i] = number_buffer[i];
    }

    //加入字符串结束符,更新字符串长度offset
    output_pointer[i] = '\0';

    output_buffer->offset += (size_t)length;

    return true;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77

sprintf();是c语言自带的格式化输出函数。

2.4,print_array();

打印数组

/* Render an array to text */
/*
 * 打印数组全部内容
 */
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;   //获取数组的第一个成员

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

    /* Compose the output array. */

    //因为是数组所以给字符串先赋值一个[
    output_pointer = ensure(output_buffer, 1);
    if (output_pointer == NULL)
    {
        return false;
    }

    *output_pointer = '[';
    output_buffer->offset++;

    //嵌套层数计+1(因为进入了child)
    output_buffer->depth++;

    //从头打印数组里的子元素
    while (current_element != NULL)
    {
        //打印一个子元素
        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++ = ' ';
            }
            //'\0'后续会被覆盖掉,因为offset在它之前
            *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--;

    return true;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78

2.5 print_object();

打印json对象

/*
 * 打印json数据到output_buffer
 */
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;  //object对象下的第一个item

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

    /* Compose the output: */
    //如果是格式化输出,则需要换行符
    length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */

    //确保内存
    output_pointer = ensure(output_buffer, length + 1);
    if (output_pointer == NULL)
    {
        return false;
    }

    //json必须有大括号
    *output_pointer++ = '{';

    //嵌套层数+1
    output_buffer->depth++;
    if (output_buffer->format)
    {
        //格式化输出带换行符
        *output_pointer++ = '\n';
    }
    //更新offset
    output_buffer->offset += length;

    //打印该json所有的item
    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++)
            {
                *output_pointer++ = '\t';
            }
            output_buffer->offset += output_buffer->depth;
        }

        /* print key */
        //打印item的键
        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++ = ':';
        //oh,格式化输出,冒号后还要一个制表符
        if (output_buffer->format)
        {
            *output_pointer++ = '\t';
        }
        output_buffer->offset += length;

        /* print value */
        //打印item的内容
        if (!print_value(current_item, output_buffer))
        {
            return false;
        }
        update_offset(output_buffer);

        /* print comma if not last */
        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;
        }
        //如果还有item,则用逗号隔开,最后一个item结尾不用逗号
        if (current_item->next)
        {
            *output_pointer++ = ',';
        }

        if (output_buffer->format)
        {
            //格式化输出需要换行
            *output_pointer++ = '\n';
        }
        //给output_pointer一个字符串结束符,outputbuff不会受到影响
        *output_pointer = '\0';
        output_buffer->offset += length;

        //打印下一个item
        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--;

    return true;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137

三 ,update_offset();

如果你足够细心就会发现,每次调用print_value()后都要更新offset,这是由于print_value()会将item打印到printbuffer里,但不会更新offset,而是在printbuffer后加了字符串结束符\0。update_offset()就是以此来更新offset。

/*
 * 计算printbuffer里的字符串长度,并由此更新printbuffer的offset,调用print_value()都要更新
 */
static void update_offset(printbuffer * const buffer)
{
    const unsigned char *buffer_pointer = NULL;
    if ((buffer == NULL) || (buffer->buffer == NULL))
    {
        return;
    }
    //获取当前offset指向的地址
    buffer_pointer = buffer->buffer + buffer->offset;

    //计算新的字符串长度并加到offset
    buffer->offset += strlen((const char*)buffer_pointer);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

四,其他打印函数

非格式化打印,效果如下:

1, CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);

在这里插入代码片
  • 1

prebuffer:指定打印输出buffer的大小;
fmt:格式化输出;

2, CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)

由外部代码申请的内存来存放打印的数据。
buf:存放打印结构的内存;
len:内存大小;
fmt:格式化输出;

CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cJSON_bool fmt)

小结

通过本章打印字符串的学习,对cjson结构体与json字符串之间转换加深理解,对于接下来的逆过程:json字符串转cjson结构体的理解会有更大的帮助。
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/460616
推荐阅读
相关标签
  

闽ICP备14008679号