2023-06-17 14:54:30 +08:00
|
|
|
#include "_json.h"
|
2023-06-18 00:03:16 +08:00
|
|
|
#include "../pikascript-lib/pika_cjson/cJSON.h"
|
2023-06-17 14:54:30 +08:00
|
|
|
#include "jsmn.h"
|
|
|
|
|
|
|
|
#if !PIKASCRIPT_VERSION_REQUIRE_MINIMUN(1, 13, 0)
|
|
|
|
#error "pikapython version must be greater than 1.13.0"
|
|
|
|
#endif
|
|
|
|
|
2023-06-18 00:03:16 +08:00
|
|
|
cJSON* _cjson_decode(Arg* d);
|
|
|
|
|
2023-06-17 14:54:30 +08:00
|
|
|
int parse_json(const char* json_string,
|
|
|
|
jsmn_parser* parser,
|
|
|
|
jsmntok_t** tokens) {
|
|
|
|
jsmn_init(parser);
|
|
|
|
|
|
|
|
int token_count =
|
|
|
|
jsmn_parse(parser, json_string, strlen(json_string), NULL, 0);
|
|
|
|
|
|
|
|
if (token_count < 0) {
|
|
|
|
// JSMN_ERROR_INVAL or JSMN_ERROR_PART
|
|
|
|
pika_platform_printf("Failed to parse JSON: %d\n", token_count);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
*tokens = pika_platform_malloc(sizeof(jsmntok_t) * token_count);
|
|
|
|
if (*tokens == NULL) {
|
|
|
|
pika_platform_printf("Failed to allocate memory: %d\n", token_count);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
jsmn_init(parser);
|
|
|
|
int result = jsmn_parse(parser, json_string, strlen(json_string), *tokens,
|
|
|
|
token_count);
|
|
|
|
if (result < 0) {
|
|
|
|
pika_platform_printf("Failed to parse JSON: %d\n", result);
|
|
|
|
pika_platform_free(*tokens);
|
|
|
|
*tokens = NULL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2023-06-18 00:03:16 +08:00
|
|
|
Arg* json_to_arg_recursive(jsmntok_t* t,
|
|
|
|
int* index,
|
|
|
|
char* json_str,
|
|
|
|
int token_count) {
|
2023-06-17 14:54:30 +08:00
|
|
|
Arg* val;
|
2023-06-18 00:03:16 +08:00
|
|
|
pika_assert(*index >= 0);
|
|
|
|
pika_assert(*index < token_count);
|
2023-06-17 14:54:30 +08:00
|
|
|
jsmntype_t type = t[*index].type;
|
|
|
|
switch (type) {
|
|
|
|
case JSMN_PRIMITIVE: {
|
|
|
|
char buff[PIKA_NAME_BUFF_SIZE] = {0};
|
2023-06-18 00:03:16 +08:00
|
|
|
pika_assert(t[*index].end - t[*index].start < PIKA_NAME_BUFF_SIZE);
|
2023-06-17 14:54:30 +08:00
|
|
|
pika_platform_memcpy(buff, json_str + t[*index].start,
|
|
|
|
t[*index].end - t[*index].start);
|
|
|
|
if (strEqu(buff, "true")) {
|
|
|
|
val = arg_newBool(1);
|
|
|
|
} else if (strEqu(buff, "false")) {
|
|
|
|
val = arg_newBool(0);
|
|
|
|
} else if (strEqu(buff, "null")) {
|
|
|
|
val = arg_newNull();
|
|
|
|
} else {
|
|
|
|
/* float */
|
|
|
|
if (strIsContain(buff, '.') ||
|
|
|
|
(strIsContain(buff, 'e') || strIsContain(buff, 'E'))) {
|
|
|
|
val = arg_newFloat(strtod(buff, NULL));
|
|
|
|
} else {
|
|
|
|
/* int */
|
|
|
|
val = arg_newInt(fast_atoi(buff));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2023-06-21 21:54:30 +08:00
|
|
|
case JSMN_STRING: {
|
2023-06-17 14:54:30 +08:00
|
|
|
val = arg_newStrN(json_str + t[*index].start,
|
|
|
|
t[*index].end - t[*index].start);
|
2023-06-21 21:54:30 +08:00
|
|
|
char* raw = arg_getStr(val);
|
|
|
|
if (strIsContain(arg_getStr(val), '\\')) {
|
|
|
|
Args buffs = {0};
|
|
|
|
size_t i = 0;
|
|
|
|
char* transfered_str = strsTransfer(&buffs, raw, &i);
|
|
|
|
Arg* val_transfered = arg_newStr(transfered_str);
|
|
|
|
arg_deinit(val);
|
|
|
|
val = val_transfered;
|
|
|
|
strsDeinit(&buffs);
|
|
|
|
}
|
2023-06-17 14:54:30 +08:00
|
|
|
break;
|
2023-06-21 21:54:30 +08:00
|
|
|
}
|
2023-06-17 14:54:30 +08:00
|
|
|
case JSMN_OBJECT: {
|
|
|
|
PikaObj* ret = obj_newDict(NULL);
|
|
|
|
int num_keys = t[*index].size;
|
|
|
|
for (int i = 0; i < num_keys; i++) {
|
2023-06-18 00:14:03 +08:00
|
|
|
(*index)++;
|
2023-06-18 00:03:16 +08:00
|
|
|
jsmntok_t* key_tok = &t[*index];
|
|
|
|
char* value = json_str + key_tok->start;
|
|
|
|
int size = key_tok->end - key_tok->start;
|
|
|
|
pika_assert(key_tok->type == JSMN_STRING);
|
2023-06-17 14:54:30 +08:00
|
|
|
char key[PIKA_NAME_BUFF_SIZE] = {0};
|
2023-06-18 00:03:16 +08:00
|
|
|
pika_assert(size < PIKA_NAME_BUFF_SIZE);
|
|
|
|
pika_platform_memcpy(key, value, size);
|
2023-06-17 14:54:30 +08:00
|
|
|
(*index)++;
|
2023-06-18 00:03:16 +08:00
|
|
|
Arg* val_nested =
|
|
|
|
json_to_arg_recursive(t, index, json_str, token_count);
|
|
|
|
objDict_set(ret, key, val_nested);
|
2023-06-17 14:54:30 +08:00
|
|
|
arg_deinit(val_nested);
|
|
|
|
}
|
|
|
|
val = arg_newObj(ret);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case JSMN_ARRAY: {
|
|
|
|
PikaObj* ret = obj_newList(NULL);
|
2023-06-18 00:03:16 +08:00
|
|
|
jsmntok_t* key_tok = &t[*index];
|
|
|
|
int num_elements = key_tok->size;
|
2023-06-17 14:54:30 +08:00
|
|
|
for (int i = 0; i < num_elements; i++) {
|
2023-06-18 00:14:03 +08:00
|
|
|
(*index)++;
|
2023-06-18 00:03:16 +08:00
|
|
|
Arg* val_nested =
|
|
|
|
json_to_arg_recursive(t, index, json_str, token_count);
|
|
|
|
objList_append(ret, val_nested);
|
2023-06-17 14:54:30 +08:00
|
|
|
arg_deinit(val_nested);
|
|
|
|
}
|
|
|
|
val = arg_newObj(ret);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default: /* Should not reach here */
|
|
|
|
val = NULL;
|
|
|
|
}
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
Arg* _json_loads(PikaObj* self, char* json_str) {
|
|
|
|
jsmn_parser parser;
|
|
|
|
jsmntok_t* tokens = NULL;
|
|
|
|
Arg* ret = NULL;
|
|
|
|
int token_count = parse_json(json_str, &parser, &tokens);
|
|
|
|
if (token_count < 0) {
|
|
|
|
ret = NULL;
|
|
|
|
goto __exit;
|
|
|
|
}
|
2023-06-18 00:03:16 +08:00
|
|
|
ret = json_to_arg_recursive(tokens, &(int){0}, json_str, token_count);
|
2023-06-17 14:54:30 +08:00
|
|
|
__exit:
|
|
|
|
if (NULL != tokens) {
|
|
|
|
free(tokens);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
2023-06-18 00:03:16 +08:00
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
cJSON* jsonArray;
|
|
|
|
} JsonListContext;
|
|
|
|
|
|
|
|
int32_t jsonListEachHandle(PikaObj* self,
|
|
|
|
int itemIndex,
|
|
|
|
Arg* itemEach,
|
|
|
|
void* context) {
|
|
|
|
JsonListContext* ctx = (JsonListContext*)context;
|
|
|
|
cJSON_AddItemToArray(ctx->jsonArray, _cjson_decode(itemEach));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
cJSON* jsonObject;
|
|
|
|
} JsonDictContext;
|
|
|
|
|
|
|
|
int32_t jsonDictEachHandle(PikaObj* self,
|
|
|
|
Arg* keyEach,
|
|
|
|
Arg* valEach,
|
|
|
|
void* context) {
|
|
|
|
JsonDictContext* ctx = (JsonDictContext*)context;
|
|
|
|
cJSON_AddItemToObject(ctx->jsonObject, arg_getStr(keyEach),
|
|
|
|
_cjson_decode(valEach));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
cJSON* _cjson_decode(Arg* d) {
|
|
|
|
ArgType type = arg_getType(d);
|
2023-06-21 21:54:30 +08:00
|
|
|
switch (type) {
|
|
|
|
case ARG_TYPE_NONE:
|
|
|
|
return cJSON_CreateNull();
|
|
|
|
|
|
|
|
case ARG_TYPE_INT:
|
|
|
|
return cJSON_CreateNumber(arg_getInt(d));
|
|
|
|
|
|
|
|
case ARG_TYPE_FLOAT:
|
|
|
|
return cJSON_CreateNumber(arg_getFloat(d));
|
|
|
|
|
|
|
|
case ARG_TYPE_BOOL:
|
|
|
|
return arg_getBool(d) ? cJSON_CreateTrue() : cJSON_CreateFalse();
|
|
|
|
|
|
|
|
case ARG_TYPE_STRING:
|
|
|
|
return cJSON_CreateString(arg_getStr(d));
|
|
|
|
|
|
|
|
default:
|
|
|
|
if (arg_isList(d)) {
|
|
|
|
JsonListContext context;
|
|
|
|
context.jsonArray = cJSON_CreateArray();
|
|
|
|
objList_forEach(arg_getObj(d), jsonListEachHandle, &context);
|
|
|
|
return context.jsonArray;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (arg_isDict(d)) {
|
|
|
|
JsonDictContext context;
|
|
|
|
context.jsonObject = cJSON_CreateObject();
|
|
|
|
objDict_forEach(arg_getObj(d), jsonDictEachHandle, &context);
|
|
|
|
return context.jsonObject;
|
|
|
|
}
|
|
|
|
return cJSON_CreateNull();
|
2023-06-18 00:03:16 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
char* _json_dumps(PikaObj* self, Arg* d) {
|
|
|
|
cJSON* json = _cjson_decode(d);
|
|
|
|
char* ret = cJSON_Print(json);
|
|
|
|
char* cached = obj_cacheStr(self, ret);
|
|
|
|
cJSON_Delete(json);
|
|
|
|
pika_platform_free(ret);
|
|
|
|
return cached;
|
|
|
|
}
|