Score
0
Watch 62 Star 89 Fork 26

风尘叹 / libjsonC

Join us
Explore and code with more than 2 million developers,Free private repositories !:)
Sign up
This repository doesn't specify license. Without author's permission, this code is only for learning and cannot be used for other purposes.
libjson 是一款简单、高性能的 C 语言 json 库,以 LGPL 协议发布。 spread retract

Clone or download
libjson.c 27.31 KB
Copy Edit Web IDE Raw Blame History
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902
#include "libjson.h"
enum {
STS_START = 0,
STS_END,
STS_OBJECT_START,
STS_OBJECT_COLON,
STS_OBJECT_VALUE,
STS_OBJECT_COMMA,
STS_ARRAY_START,
STS_ARRAY_COMMA,
STS_STRING_START,
STS_STRING_ESCAPE,
STS_STRING_HEX1,
STS_STRING_HEX2,
STS_STRING_HEX3,
STS_STRING_HEX4,
STS_NUMBER_START,
STS_NUMBER_POSITIVE,
STS_NUMBER_A,
STS_NUMBER_B,
STS_NUMBER_DECIMAL,
STS_NUMBER_EXPONENT,
STS_NUMBER_EXPONENT_A,
STS_NUMBER_EXPONENT_DIGIT,
STS_NUMBER_EXPONENT_DIGIT_A
};
int json_parse_true( json_task_t *task );
int json_parse_false( json_task_t *task );
int json_parse_null( json_task_t *task );
int json_parse_number( json_task_t *task, json_object_t *parent );
int json_parse_string( json_task_t *task );
int json_parse_value( json_task_t *task, json_object_t *parent );
int json_parse_array( json_task_t *task, json_object_t *parent );
int json_parse_object( json_task_t *task, json_object_t *parent );
int json_parse_true( json_task_t *task ) {
if( task->count + 3 < task->len &&
*(task->str + task->count) == 'r' &&
*(task->str + task->count + 1) == 'u' &&
*(task->str + task->count + 2) == 'e' ) {
task->count += 3;
return 0;
}
task->err_msg = "expect 'true'";
return -1;
}
int json_parse_false( json_task_t *task ) {
if( task->count + 4 < task->len &&
*(task->str + task->count) == 'a' &&
*(task->str + task->count + 1) == 'l' &&
*(task->str + task->count + 2) == 's' &&
*(task->str + task->count + 3) == 'e' ) {
task->count += 4;
return 0;
}
task->err_msg = "expect 'false'";
return -1;
}
int json_parse_null( json_task_t *task ) {
if( task->count + 3 < task->len &&
*(task->str + task->count) == 'u' &&
*(task->str + task->count + 1) == 'l' &&
*(task->str + task->count + 2) == 'l' ) {
task->count += 3;
return 0;
}
task->err_msg = "expect 'null'";
return -1;
}
int json_parse_number( json_task_t *task, json_object_t *parent ) {
char ch;
task->status = STS_NUMBER_START;
while( ch = *(task->str + task->count) ) {
task->count ++;
switch( task->status ) {
case STS_NUMBER_START:
if( ch == '-' ) {
task->status = STS_NUMBER_POSITIVE;
} else {
task->count --;
task->status = STS_NUMBER_POSITIVE;
}
break;
case STS_NUMBER_POSITIVE:
if( ch == '0' ) {
task->status = STS_NUMBER_A;
} else if ( ch >= '1' && ch <= '9' ) {
task->status = STS_NUMBER_B;
} else {
task->err_msg = "expect '0-9'";
return -1;
}
break;
case STS_NUMBER_B:
if ( ch >= '0' && ch <= '9' ) {
// do nothing
} else {
task->count --;
task->status = STS_NUMBER_A;
}
break;
case STS_NUMBER_A:
if( ch == '.' ) {
parent->value_type = JSON_DOUBLE;
task->status = STS_NUMBER_DECIMAL;
} else {
task->count --;
task->status = STS_NUMBER_EXPONENT;
}
break;
case STS_NUMBER_DECIMAL:
if ( ch >= '0' && ch <= '9' ) {
// do nothing
} else {
task->count --;
task->status = STS_NUMBER_EXPONENT;
}
break;
case STS_NUMBER_EXPONENT:
if( ch == 'e' || ch == 'E' ) {
task->status = STS_NUMBER_EXPONENT_A;
} else {
task->count --;
return 0;
}
break;
case STS_NUMBER_EXPONENT_A:
if( ch == '+' || ch == '-' ) {
task->status = STS_NUMBER_EXPONENT_DIGIT;
} else {
task->count --;
task->status = STS_NUMBER_EXPONENT_DIGIT;
}
break;
case STS_NUMBER_EXPONENT_DIGIT:
if ( ch >= '0' && ch <= '9' ) {
task->status = STS_NUMBER_EXPONENT_DIGIT_A;
} else {
task->err_msg = "expect '0-9'";
return -1;
}
break;
case STS_NUMBER_EXPONENT_DIGIT_A:
if ( ch >= '0' && ch <= '9' ) {
task->status = STS_NUMBER_EXPONENT_DIGIT_A;
} else {
task->count --;
return 0;
}
break;
default:
task->err_msg = "unknown status";
return -1;
}
}
task->err_msg = "unexpect EOF";
return -1;
}
int json_parse_string( json_task_t *task ) {
char ch;
task->status = STS_STRING_START;
while( ch = *(task->str + task->count) ) {
task->count ++;
switch( task->status ) {
case STS_STRING_START:
if( ch == '"' ) {
return 0;
} else if( ch == '\\' ) {
task->status = STS_STRING_ESCAPE;
} else {
// do nothing
}
break;
case STS_STRING_ESCAPE:
if( ch == '"' || ch == '\\' || ch == '/' || ch == 'b' ||
ch == 'f' || ch == 'n' || ch == 'r' || ch == 't' ) {
task->status = STS_STRING_START;
} else if( ch == 'u' ) {
task->status = STS_STRING_HEX1;
} else {
task->err_msg = "illegal escape";
return -1;
}
break;
case STS_STRING_HEX1:
case STS_STRING_HEX2:
case STS_STRING_HEX3:
case STS_STRING_HEX4:
if( ( ch >= '0' && ch <= '9' ) ||
( ch >= 'a' && ch <= 'f' ) ||
( ch >= 'A' && ch <= 'F' ) ) {
if( task->status == STS_STRING_HEX4 ) {
task->status = STS_STRING_START;
} else {
task->status ++;
}
} else {
task->err_msg = "expect '0-9', 'a-f', 'A-F'";
return -1;
}
break;
default:
task->err_msg = "unknown status";
return -1;
}
}
task->err_msg = "unexpect EOF";
return -1;
}
int json_parse_value( json_task_t *task, json_object_t *parent ) {
char ch;
while( ch = *(task->str + task->count) ) {
task->count ++;
if( ch == '"' ) {
parent->value_type = JSON_STRING;
return json_parse_string( task );
} else if( ch == '-' || ( ch >= '0' && ch <= '9' ) ) {
task->count --;
parent->value_type = JSON_LONGLONG;
return json_parse_number( task, parent );
} else if( ch == '{' ) {
parent->value_type = JSON_OBJECT;
return json_parse_object( task, parent );
} else if( ch == '[' ) {
parent->value_type = JSON_ARRAY;
return json_parse_array( task, parent );
} else if( ch == 't' ) {
parent->value_type = JSON_TRUE;
return json_parse_true( task );
} else if( ch == 'f' ) {
parent->value_type = JSON_FALSE;
return json_parse_false( task );
} else if( ch == 'n' ) {
parent->value_type = JSON_NULL;
return json_parse_null( task );
} else {
task->err_msg = "illegal value";
return -1;
}
}
task->err_msg = "unexpect EOF";
return -1;
}
int json_parse_array( json_task_t *task, json_object_t *parent ) {
char ch;
json_object_t node, * append;
node.next = parent;
node.key = NULL;
node.key_len = 0;
if( !task->callback ) {
if( !task->root ) {
append = task->root = json_create_array();
} else {
append = parent->value.p = json_create_array();
}
}
task->status = STS_ARRAY_START;
while( ch = *(task->str + task->count) ) {
task->count ++;
if( ch == ' ' || ch == '\n' || ch == '\t' ) {
continue;
}
switch( task->status ) {
case STS_ARRAY_START:
if( ch == ']' ) {
return 0;
} else {
/* 这里需要将计数减一,因为 json_parse_value 需要根据这一个字符判断 value 类型 */
task->count --;
node.value.s = task->str + task->count;
if( json_parse_value( task, &node ) != 0 ) {
if( node.value_type == JSON_OBJECT ||
node.value_type == JSON_ARRAY ) {
json_delete_object(node.value.p);
}
return -1;
}
// WARNING: value_len 可能发生溢出
node.value_len = task->str + task->count - node.value.s;
// 对 value 进行处理
switch(node.value_type) {
case JSON_STRING:
// 去除字符串两端的引号
node.value.s += 1;
node.value_len -= 2;
break;
case JSON_DOUBLE:
// TODO 在解析测试用例时,atof 与 atoll 使解析时间延长了几乎一半
// 移除对数值的默认解析可以显著提升性能
node.value.d = atof(node.value.s);
break;
case JSON_LONGLONG:
node.value.l = atoll(node.value.s);
break;
case JSON_ARRAY:
case JSON_OBJECT:
break;
}
// 需要放在 node.key_len ++ 之前以便正确输出数组下标
if( task->callback ) {
task->callback( task, &node );
}
if( !task->callback ) {
switch( node.value_type ) {
case JSON_ARRAY:
case JSON_OBJECT:
case JSON_STRING:
append = json_append(append, node.key, node.key_len,
node.value_type, node.value.p, node.value_len);
break;
default:
append = json_append(append, node.key, node.key_len,
node.value_type, &node.value, node.value_len);
}
}
node.key_len ++;
task->status = STS_ARRAY_COMMA;
}
break;
case STS_ARRAY_COMMA:
if( ch == ',' ) {
task->status = STS_ARRAY_START;
} else if( ch == ']' ) {
return 0;
} else {
task->err_msg = "expect ',' or ']'";
return -1;
}
break;
default:
task->err_msg = "unknown status";
return -1;
}
}
task->err_msg = "unexpect EOF";
return -1;
}
int json_parse_object( json_task_t *task, json_object_t *parent ) {
char ch;
json_object_t node, * append;
node.next = parent;
node.key = NULL;
node.key_len = 0;
if( !task->callback ) {
if( !task->root ) {
append = task->root = json_create_object();
} else {
append = parent->value.p = json_create_object();
}
}
task->status = STS_OBJECT_START;
while( ch = *(task->str + task->count) ) {
task->count ++;
if( ch == ' ' || ch == '\n' || ch == '\t' ) {
continue;
}
switch( task->status ) {
case STS_OBJECT_START:
if( ch == '"' ) {
node.key = task->str + task->count;
if( json_parse_string( task ) != 0 ) {
return -1;
}
// WARNING: key_len 可能发生溢出
node.key_len = task->str + task->count - node.key - 1;
task->status = STS_OBJECT_COLON;
} else if( ch == '}' ) {
// 空对象 {},忽略
return 0;
} else {
task->err_msg = "expect '\"' or '}'";
return -1;
}
break;
case STS_OBJECT_COLON:
if( ch == ':' ) {
task->status = STS_OBJECT_VALUE;
} else {
task->err_msg = "expect ':'";
return -1;
}
break;
case STS_OBJECT_VALUE:
/* 这里需要将计数减一,因为 json_parse_value 需要根据这一个字符判断 value 类型 */
task->count --;
node.value.s = task->str + task->count;
if( json_parse_value( task, &node ) != 0 ) {
if( node.value_type == JSON_OBJECT ||
node.value_type == JSON_ARRAY ) {
json_delete_object(node.value.p);
}
return -1;
}
// WARNING: value_len 可能发生溢出
node.value_len = task->str + task->count - node.value.s;
task->status = STS_OBJECT_COMMA;
break;
case STS_OBJECT_COMMA:
if( ( ch == ',' || ch == '}' ) ) {
// 对 value 进行处理
switch(node.value_type) {
case JSON_STRING:
// 去除字符串两端的引号
node.value.s += 1;
node.value_len -= 2;
break;
case JSON_DOUBLE:
node.value.d = atof(node.value.s);
break;
case JSON_LONGLONG:
node.value.l = atoll(node.value.s);
break;
case JSON_ARRAY:
case JSON_OBJECT:
break;
}
if( task->callback ) {
task->callback( task, &node );
}
if( !task->callback ) {
switch( node.value_type ) {
case JSON_ARRAY:
case JSON_OBJECT:
case JSON_STRING:
append = json_append(append, node.key, node.key_len,
node.value_type, node.value.p, node.value_len);
break;
default:
append = json_append(append, node.key, node.key_len,
node.value_type, &node.value, node.value_len);
}
}
}
if( ch == ',' ) {
task->status = STS_OBJECT_START;
} else if( ch == '}' ) {
return 0;
} else {
task->err_msg = "expect ',' or '}'";
return -1;
}
break;
default:
task->err_msg = "unknown status";
return -1;
}
}
task->err_msg = "unexpect EOF";
return -1;
}
void json_err_psotion( json_task_t *task, int *line, int *row ) {
int p = 0;
char ch;
(*line) = 1;
(*row) = 0;
while( ch = *(task->str + p) ) {
p ++;
if( ch == '\n' ) {
(*line) ++;
(*row) = 0;
} else {
(*row) ++;
}
if( p == task->count ) break;
}
}
int json_parser( json_task_t *task ) {
char ch;
task->err_msg = "OK";
task->count = 0;
task->status = STS_START;
task->root = NULL;
if( task->str == NULL || task->len <= 0 ) {
task->err_msg = "the input string is not specified";
return -1;
}
while( task->count < task->len ) {
ch = *(task->str + task->count);
task->count ++;
if( ch == ' ' || ch == '\n' || ch == '\t' ) {
continue;
}
switch( task->status ) {
case STS_START:
if( ch == '{' ) {
if( json_parse_object( task, task->root ) != 0 ) {
return -1;
}
task->status = STS_END;
} else if( ch == '[' ) {
if( json_parse_array( task, task->root ) != 0 ) {
return -1;
}
task->status = STS_END;
} else {
task->err_msg = "expect '{' or '['";
return -1;
}
break;
case STS_END:
if( task->count >= task->len ) {
return 0;
} else {
task->err_msg = "expect EOF";
return -1;
}
break;
default:
task->err_msg = "unknown status";
return -1;
}
}
if( task->status == STS_END ) {
return 0;
} else {
return -1;
}
}
json_object_t * json_create_object() {
json_object_t * obj = (json_object_t *) malloc(sizeof(json_object_t));
if( obj ) {
memset(obj, 0, sizeof(json_object_t));
obj->object_type = JSON_OBJECT;
}
return obj;
}
json_object_t * json_create_array() {
json_object_t * obj = (json_object_t *) malloc(sizeof(json_object_t));
if( obj ) {
memset(obj, 0, sizeof(json_object_t));
obj->object_type = JSON_ARRAY;
}
return obj;
}
// 如果添加失败 返回 NULL
// 如果添加成功,返回所添加的 json_object_t 结构指针
json_object_t * json_append(json_object_t * obj, const char * key, size_t key_len, json_type_t value_type, void * value, size_t value_len) {
assert(obj);
assert( (obj->object_type == JSON_OBJECT && key) || (obj->object_type != JSON_OBJECT) );
while( obj->value_type != JSON_NONE ) {
if( !obj->next ) {
if( obj->object_type == JSON_OBJECT ) {
obj->next = json_create_object();
} else if ( obj->object_type == JSON_ARRAY ) {
obj->next = json_create_array();
} else {
return NULL;
}
}
if( obj->next ) {
obj = obj->next;
} else {
return NULL;
}
}
if( obj->object_type == JSON_OBJECT ) {
#ifdef __x86_64__
assert( !(key_len >> 18) );
#elif __i386__
assert( !(key_len >> 8) );
#endif
obj->key = (char *)key;
obj->key_len = key_len;
}
obj->value_type = value_type;
switch(value_type) {
case JSON_OBJECT:
case JSON_ARRAY:
obj->value.p = (json_object_t *)value;
break;
case JSON_STRING:
#ifdef __x86_64__
assert( !(value_len >> 38) );
#elif __i386__
assert( !(value_len >> 16) );
#endif
obj->value.s = value;
obj->value_len = value_len;
break;
case JSON_DOUBLE:
obj->value.d = *((double *)value);
break;
case JSON_INTEGER:
obj->value.i = *((int *)value);
break;
case JSON_FLOAT:
obj->value.f = *((float *)value);
break;
case JSON_LONGLONG:
obj->value.l = *((long long *)value);
break;
default:
obj->value.l = 1;
break;
}
return obj;
}
void json_print_longlong(long long l, char **buf, size_t *buf_len) {
size_t i = snprintf(*buf, *buf_len, "%lld", l);
if( i > *buf_len ) {
*buf += *buf_len;
*buf_len = 0;
} else {
*buf += i;
*buf_len -= i;
}
}
void json_print_float(float f, char **buf, size_t *buf_len) {
size_t i = snprintf(*buf, *buf_len, "%f", f);
if( i > *buf_len ) {
*buf += *buf_len;
*buf_len = 0;
} else {
*buf += i;
*buf_len -= i;
}
}
void json_print_integer(int i, char **buf, size_t *buf_len) {
size_t si = snprintf(*buf, *buf_len, "%d", i);
if( si > *buf_len ) {
*buf += *buf_len;
*buf_len = 0;
} else {
*buf += si;
*buf_len -= si;
}
}
void json_print_double(double d, char **buf, size_t *buf_len) {
size_t i = snprintf(*buf, *buf_len, "%lf", d);
if( i > *buf_len ) {
*buf += *buf_len;
*buf_len = 0;
} else {
*buf += i;
*buf_len -= i;
}
}
void json_print_char(char c, char **buf, size_t *buf_len) {
size_t i = snprintf(*buf, *buf_len, "%c", c);
if( i > *buf_len ) {
*buf += *buf_len;
*buf_len = 0;
} else {
*buf += i;
*buf_len -= i;
}
}
void json_print_string(const char * str, size_t len, char **buf, size_t *buf_len) {
json_print_char('\"', buf, buf_len);
size_t i;
for(i = 0 ; i < len ; i++) {
switch(str[i]) {
case '\\':
if( i < len - 1) {
json_print_char('\\', buf, buf_len);
json_print_char(str[++i], buf, buf_len);
} else {
json_print_char('\\', buf, buf_len);
json_print_char('\\', buf, buf_len);
}
break;
case '\"':
json_print_char('\\', buf, buf_len);
json_print_char('\"', buf, buf_len);
break;
case '\b':
json_print_char('\\', buf, buf_len);
json_print_char('b', buf, buf_len);
break;
case '\f':
json_print_char('\\', buf, buf_len);
json_print_char('f', buf, buf_len);
break;
case '\n':
json_print_char('\\', buf, buf_len);
json_print_char('n', buf, buf_len);
break;
case '\r':
json_print_char('\\', buf, buf_len);
json_print_char('r', buf, buf_len);
break;
case '\t':
json_print_char('\\', buf, buf_len);
json_print_char('t', buf, buf_len);
break;
default:
json_print_char(str[i], buf, buf_len);
}
}
json_print_char('\"', buf, buf_len);
}
void json_print_true(char **buf, size_t *buf_len) {
size_t i = snprintf(*buf, *buf_len, "true");
if( i > *buf_len ) {
*buf += *buf_len;
*buf_len = 0;
} else {
*buf += i;
*buf_len -= i;
}
}
void json_print_false(char **buf, size_t *buf_len) {
size_t i = snprintf(*buf, *buf_len, "false");
if( i > *buf_len ) {
*buf += *buf_len;
*buf_len = 0;
} else {
*buf += i;
*buf_len -= i;
}
}
void json_print_null(char **buf, size_t *buf_len) {
size_t i = snprintf(*buf, *buf_len, "null");
if( i > *buf_len ) {
*buf += *buf_len;
*buf_len = 0;
} else {
*buf += i;
*buf_len -= i;
}
}
void json_print_object(json_object_t * obj, char **buf, size_t *buf_len) {
json_print_char('{', buf, buf_len);
if( obj->key_len ) {
while(obj) {
json_print_string(obj->key, obj->key_len, buf, buf_len);
json_print_char(':', buf, buf_len);
json_print_value(obj, buf, buf_len);
obj = obj->next;
if(obj) {
json_print_char(',', buf, buf_len);
}
}
}
json_print_char('}', buf, buf_len);
}
void json_print_array(json_object_t * obj, char **buf, size_t *buf_len) {
json_print_char('[', buf, buf_len);
while(obj) {
json_print_value(obj, buf, buf_len);
obj = obj->next;
if(obj) {
json_print_char(',', buf, buf_len);
}
}
json_print_char(']', buf, buf_len);
}
void json_print_value(json_object_t * obj, char **buf, size_t *buf_len) {
switch(obj->value_type) {
case JSON_OBJECT:
json_print_object(obj->value.p, buf, buf_len);
break;
case JSON_ARRAY:
json_print_array(obj->value.p, buf, buf_len);
break;
case JSON_STRING:
json_print_string(obj->value.s, obj->value_len, buf, buf_len);
break;
case JSON_DOUBLE:
json_print_double(obj->value.d, buf, buf_len);
break;
case JSON_INTEGER:
json_print_integer(obj->value.i, buf, buf_len);
break;
case JSON_FLOAT:
json_print_float(obj->value.f, buf, buf_len);
break;
case JSON_LONGLONG:
json_print_longlong(obj->value.l, buf, buf_len);
break;
case JSON_TRUE:
json_print_true(buf, buf_len);
break;
case JSON_FALSE:
json_print_false(buf, buf_len);
break;
case JSON_NULL:
json_print_null(buf, buf_len);
break;
default:
break;
}
}
void json_print(json_object_t * obj, char **buf, size_t *buf_len) {
switch(obj->object_type) {
case JSON_OBJECT:
json_print_object(obj, buf, buf_len);
break;
case JSON_ARRAY:
json_print_array(obj, buf, buf_len);
break;
default:
// JSON 对象必须以对象或者数组为顶层元素
break;
}
}
void json_delete_object(json_object_t * obj) {
while(obj) {
if( obj->value_type == JSON_ARRAY || obj->value_type == JSON_OBJECT ) {
json_delete_object(obj->value.p);
}
json_object_t * next = obj->next;
free(obj);
obj = next;
}
}
json_object_t * json_duplicate_object(json_object_t *obj) {
if(obj == NULL) {
return NULL;
} else {
json_object_t * new_obj = json_create_object();
*new_obj = *obj;
new_obj->next = json_duplicate_object(obj->next);
if( obj->value_type == JSON_ARRAY || obj->value_type == JSON_OBJECT ) {
new_obj->value.p = json_duplicate_object(obj->value.p);
}
return new_obj;
}
}

Comment ( 0 )

Sign in for post a comment

C
1
https://gitee.com/fcten/libjson.git
git@gitee.com:fcten/libjson.git
fcten
libjson
libjson
master

Help Search