C 实现解析Json字符串
2016-07-15 17:32
597 查看
除了C的标准库之外,依赖两个数据结构的实现,C 实现ArrayMap字典映射 和 C 实现泛型ArrayList数组。实现了标准的json结构解析,并提供一套访问json数据对象属性的接口。
首先,看数据结构的定义。
Json 就是一个Key-Value结构,value可能有5中类型,array和object类型单独拿出来定义了一下。每一个value都是JsonValue类型的,用联合体来存储类型的值。看着这里并没有给int类型,所有的int类型值是通过float强转得到的。
对于,array和object类型,给定一组接口访问其内部的数据结构。
整个Json的解析通过一个接口完成。
Parse是解析一个Json字符串,ParseWithFile是解析一个Json文件。返回一个JsonValue对象,具体这个是什么类型,需具体判断,然后一步步通过接口就可以访问每个数据成员。
array和object的访问接口如下:
解析Json的过程实现如下:
其核心的思想很简单,就是一个一个字符读入,根据不同的字符来判断需要解析的数据类型。中间,需要跳过无效的字符比如空格什么的。因为array和object可以嵌套。所以在ParseArray和ParseObject的时候,调用ParseValue解析,其中会递归的调用ParseArray和ParseObject。
当解析完成的时候,所有的Json都转化为对象存储起来。Json字符串也可以删除了,以后所有的访问都是在访问结构化的Json对象。Release方法可以释放JsonValue全部申请的内存空间,如果传入根JsonValue就可以释放全部的空间。
首先,看数据结构的定义。
typedef enum { json_object, json_array, json_string, json_float, json_null, } JsonType; typedef struct { ArrayStrMap(JsonValue*) arrayStrMap[1]; } JsonObject; typedef struct { ArrayList(JsonValue*) arrayList[1]; } JsonArray; typedef struct { JsonType type; union { /** json_string */ char* stringValue; /** json_object */ JsonObject* object; /** json_array */ JsonArray* array; /** json_float */ float floatValue; }; } JsonValue;
Json 就是一个Key-Value结构,value可能有5中类型,array和object类型单独拿出来定义了一下。每一个value都是JsonValue类型的,用联合体来存储类型的值。看着这里并没有给int类型,所有的int类型值是通过float强转得到的。
对于,array和object类型,给定一组接口访问其内部的数据结构。
typedef struct { bool (*GetBool) (JsonObject* object, const char* key, bool defaultValue); int (*GetInt) (JsonObject* object, const char* key, int defaultValue); float (*GetFloat) (JsonObject* object, const char* key, float defaultValue); JsonType (*GetType) (JsonObject* object, const char* key); /** * When JsonValue released string value will free */ char* (*GetString) (JsonObject* object, const char* key, char* defaultValue); /** Not found return NULL */ JsonObject* (*GetObject) (JsonObject* object, const char* key); /** Not found return NULL */ JsonArray* (*GetArray) (JsonObject* object, const char* key); const char* (*GetKey) (JsonObject* object, int index); /** Get JsonObject in index of JsonObject map */ JsonObject* (*GetObjectByIndex)(JsonObject* object, int index); /** Get JsonArray in index of JsonObject map */ JsonArray* (*GetArrayByIndex) (JsonObject* object, int index); } _AJsonObject_; extern _AJsonObject_ AJsonObject[1]; typedef struct { bool (*GetBool) (JsonArray* array, int index); int (*GetInt) (JsonArray* array, int index); float (*GetFloat) (JsonArray* array, int index); JsonType (*GetType) (JsonArray* array, int index); /** When JsonValue released string value will free */ char* (*GetString) (JsonArray* array, int index); JsonObject* (*GetObject) (JsonArray* array, int index); JsonArray* (*GetArray) (JsonArray* array, int index); } _AJsonArray_; extern _AJsonArray_ AJsonArray[1];
整个Json的解析通过一个接口完成。
typedef struct { /** * Parse with Json string, return root JsonValue */ JsonValue* (*Parse) (const char* jsonString); /** * Parse with Json file, return root JsonValue */ JsonValue* (*ParseWithFile) (const char* jsonPath); /** * Release JsonValue member memory space and free itself * if release root JsonValue will free all memory space */ void (*Release) (JsonValue* jsonValue); } _AJson_;
Parse是解析一个Json字符串,ParseWithFile是解析一个Json文件。返回一个JsonValue对象,具体这个是什么类型,需具体判断,然后一步步通过接口就可以访问每个数据成员。
array和object的访问接口如下:
static bool ObjectGetBool(JsonObject* object, const char* key, bool defaultValue) { JsonValue* jsonValue = *(JsonValue**) AArrayStrMap->Get(object->arrayStrMap, key, (void*[]) {NULL}); return jsonValue ? strcmp(jsonValue->stringValue, "true") == 0 : defaultValue; } static int ObjectGetInt(JsonObject* object, const char* key, int defaultValue) { JsonValue* jsonValue = *(JsonValue**) AArrayStrMap->Get(object->arrayStrMap, key, (void*[]) {NULL}); if (jsonValue) { return jsonValue->floatValue; } return defaultValue; } static float ObjectGetFloat(JsonObject* object, const char* key, float defaultValue) { JsonValue* jsonValue = *(JsonValue**) AArrayStrMap->Get(object->arrayStrMap, key, (void*[]) {NULL}); if (jsonValue) { return jsonValue->floatValue; } return defaultValue; } static char* ObjectGetString(JsonObject* object, const char* key, char* defaultValue) { JsonValue* jsonValue = *(JsonValue**) AArrayStrMap->Get(object->arrayStrMap, key, (void*[]) {NULL}); return jsonValue ? jsonValue->stringValue : defaultValue; } static JsonObject* ObjectGetObject(JsonObject* object, const char* key) { JsonValue* jsonValue = *(JsonValue**) AArrayStrMap->Get(object->arrayStrMap, key, (void*[]) {NULL}); return jsonValue ? jsonValue->object : NULL; } static JsonArray* ObjectGetArray(JsonObject* object, const char* key) { JsonValue* jsonValue = *(JsonValue**) AArrayStrMap->Get(object->arrayStrMap, key, (void*[]) {NULL}); return jsonValue ? jsonValue->array : NULL; } static JsonType ObjectGetType(JsonObject* object, const char* key) { JsonValue* jsonValue = *(JsonValue**) AArrayStrMap->Get(object->arrayStrMap, key, (void*[]) {NULL}); if (!jsonValue) { return json_null; } return jsonValue->type; } static const char* ObjectGetKey(JsonObject* object, int index) { return AArrayStrMap->GetKey(object->arrayStrMap, index); } static JsonObject* ObjectGetObjectByIndex(JsonObject* object, int index) { return (*(JsonValue**) AArrayStrMap->GetAt(object->arrayStrMap, index))->object; } static JsonArray* ObjectGetArrayByIndex(JsonObject* object, int index) { return (*(JsonValue**) AArrayStrMap->GetAt(object->arrayStrMap, index))->array; } _AJsonObject_ AJsonObject[1] = { ObjectGetBool, ObjectGetInt, ObjectGetFloat, ObjectGetType, ObjectGetString, ObjectGetObject, ObjectGetArray, ObjectGetKey, ObjectGetObjectByIndex, ObjectGetArrayByIndex, };
解析Json的过程实现如下:
// predefine static JsonValue* ParseValue(const char** jsonPtr); /** * If json_array free each items and recursive * If json_object free each K-V and recursive */ static void Release(JsonValue* value) { // JsonValue hold the whole memory // so free JsonValue will be release JsonValue's memory switch (value->type) { case json_array: { ArrayList* list = value->array->arrayList; for (int i = 0; i < list->size; i++) { Release(AArrayList_Get(list, i, JsonValue*)); } AArrayList->Release(list); } break; case json_object: { ArrayStrMap* map = value->object->arrayStrMap; for (int i = 0; i < map->arrayList->size; i++) { Release(*(JsonValue**) AArrayStrMap->GetAt(map, i)); } AArrayStrMap->Release(map); } break; default: break; } free(value); } static JsonValue* CreateJsonValue(void* data, size_t valueSize, JsonType type) { JsonValue* value = (JsonValue*) malloc(sizeof(JsonValue) + valueSize); switch (type) { case json_float: break; case json_string: { void* v = (char*) value + sizeof(JsonValue); memcpy(v, data, valueSize); value->stringValue = v; } break; case json_array: value->array = (JsonArray*) ((char*) value + sizeof(JsonValue)); AArrayList->Init(sizeof(JsonValue*), value->array->arrayList); value->array->arrayList->increment = 20; break; case json_object: value->object = (JsonObject*) ((char*) value + sizeof(JsonValue)); AArrayStrMap->Init(sizeof(JsonValue*), value->object->arrayStrMap); value->object->arrayStrMap->arrayList->increment = 20; break; default: ALog_A(0, "CreateJsonValue unknown JsonType = %d", type); } value->type = type; return value; }
// skip whitespace and CR/LF static void Skip(const char** jsonPtr) { const char* json = *jsonPtr; while (json && isspace(*json)) { json++; } ALog_A(json, "Json Parse failed on NULL, json is incomplete"); *jsonPtr = json; } static void* ParseNumber(const char** jsonPtr) { char* json = (char*) *jsonPtr; char c; do { json++; c = *json; } while (isdigit(c) || c == '-' || c == '.' || c == 'e' || c == 'E'); *json = '\0'; ALog_D("Json number = %s", *jsonPtr); JsonValue* value = CreateJsonValue(NULL, 0, json_float); value->floatValue = atof(*jsonPtr); *json = c; *jsonPtr = json; return value; } static JsonValue* ParseString(const char** jsonPtr) { // Skip '\"' (*jsonPtr)++; const char* json = *jsonPtr; char c = *json; int count = 0; while (c != '\"') { // skip escaped quotes if (c == '\\') { do { json += 2; count += 2; c = *json; } while (c == '\\'); } else { json++; count++; c = *json; } } JsonValue* value = CreateJsonValue((void*)(*jsonPtr), (count + 1) * sizeof(char), json_string); value->stringValue[count] = '\0'; *jsonPtr = json + 1; ALog_D("Json string = %s", value->stringValue); return value; } static char* ParseKey(char** jsonPtr, int* outKeyLen) { // skip '\"' (*jsonPtr)++; char* json = *jsonPtr; char c = *json; int count = 0; while (c != '\"') { // skip escaped quotes if (c == '\\') { do { json += 2; count += 2; c = *json; } while (c == '\\'); } else { json++; count++; c = *json; } } ALog_D("Json key = %.*s", count, *jsonPtr); char* key = *jsonPtr; *outKeyLen = count; *jsonPtr = json + 1; return key; } static JsonValue* ParseArray(const char** jsonPtr) { JsonValue* jsonValue = CreateJsonValue(NULL, sizeof(JsonArray), json_array); ArrayList* list = jsonValue->array->arrayList; ALog_D("Json Array: ["); // skip '[' (*jsonPtr)++; Skip(jsonPtr); // empty array if (**jsonPtr == ']') { goto label_json_array_end; } do { JsonValue* value = ParseValue(jsonPtr); // add Array element AArrayList->Add(list, &value); Skip(jsonPtr); if (**jsonPtr == ',') { (*jsonPtr)++; Skip(jsonPtr); if (**jsonPtr == ']') { goto label_json_array_end; } } else { break; } } while (true); Skip(jsonPtr); ALog_A(**jsonPtr == ']', "Json Array not has ']', error char = %c ", **jsonPtr); label_json_array_end: // skip ']' (*jsonPtr)++; ALog_D("] JsonArray element count = %d", list->size); return jsonValue; } static JsonValue* ParseObject(const char** jsonPtr) { JsonValue* jsonValue = CreateJsonValue(NULL, sizeof(JsonObject), json_object); ArrayStrMap* map = jsonValue->object->arrayStrMap; ALog_D("Json Object: {"); // skip '{' (*jsonPtr)++; Skip(jsonPtr); // empty object if (**jsonPtr == '}') { goto label_json_object_end; } do { Skip(jsonPtr); if (**jsonPtr == '}') { goto label_json_object_end; } ALog_A(**jsonPtr == '\"', "Json object Parse failed, char = %c, should be '\"' ", **jsonPtr); int keyLen; // get object key char* key = ParseKey((char**)jsonPtr, &keyLen); Skip(jsonPtr); ALog_A((**jsonPtr) == ':', "Json object Parse failed, char = %c, should be ':' ", **jsonPtr); // Skip ':' (*jsonPtr)++; JsonValue* value = ParseValue(jsonPtr); char c = key[keyLen]; // make string end in json string key[keyLen] = '\0'; // set object element AArrayStrMap->Put(map, key, &value); key[keyLen] = c; Skip(jsonPtr); if (**jsonPtr == ',') { (*jsonPtr)++; } else { break; } } while (true); Skip(jsonPtr); ALog_A(**jsonPtr == '}', "Json Object not has '}', error char = %c ", **jsonPtr); label_json_object_end: // skip '}' (*jsonPtr)++; ALog_D("} JsonObject elements count = %d", map->arrayList->size); return jsonValue; } /** * ParseValue changed the *jsonPtr, so if *jsonPtr is direct malloc will cause error */ static JsonValue* ParseValue(const char** jsonPtr) { Skip(jsonPtr); char c = **jsonPtr; switch (c) { case '[': return ParseArray(jsonPtr); case '{': return ParseObject(jsonPtr); case '\"': return ParseString(jsonPtr); default: if (isdigit(c) || c == '-') { return ParseNumber(jsonPtr); } else if (strncmp(*jsonPtr, "null", 4) == 0) { ALog_D("Json null"); (*jsonPtr) += 4; // copy with '\0' return CreateJsonValue("null", 5, json_string); } else if (strncmp(*jsonPtr, "false", 5) == 0) { ALog_D("Json false"); (*jsonPtr) += 5; // copy with '\0' return CreateJsonValue("false", 6, json_string); } else if (strncmp(*jsonPtr, "true", 4) == 0) { ALog_D("Json true"); (*jsonPtr) += 4; // copy with '\0' return CreateJsonValue("true", 5, json_string); } else { ALog_A(0, "Can't parse value type, error char = %c", c); } break; } return NULL; } static JsonValue* Parse(const char* jsonString) { return ParseValue(&jsonString); } static JsonValue* ParseWithFile(const char* jsonPath) { char* jsonString = AFileTool->ReadString(jsonPath); JsonValue* value = Parse(jsonString); free(jsonString); return value; } _AJson_ AJson[1] = { Parse, ParseWithFile, Release, };
其核心的思想很简单,就是一个一个字符读入,根据不同的字符来判断需要解析的数据类型。中间,需要跳过无效的字符比如空格什么的。因为array和object可以嵌套。所以在ParseArray和ParseObject的时候,调用ParseValue解析,其中会递归的调用ParseArray和ParseObject。
当解析完成的时候,所有的Json都转化为对象存储起来。Json字符串也可以删除了,以后所有的访问都是在访问结构化的Json对象。Release方法可以释放JsonValue全部申请的内存空间,如果传入根JsonValue就可以释放全部的空间。
相关文章推荐
- javascript语句——条件语句、循环语句和跳转语句
- 总结一下,最近工作中的问题
- javascript坐标:event.x、event.clientX、event.offsetX、event.screenX 用法
- 如何更方便地调试javascript代码
- 下拉分页异步加载infinitescroll的使用
- JS 根据子网掩码,网关计算出所有的IP范围
- 常用javaScript收集
- 事件——《JS高级程序设计》
- 很棒的js选项卡切换效果
- Jsoup介绍及解析常用方法
- JavaScript强化教程—— RegExp 对象
- js对json对象的增加
- javascript中的event事件用法详解
- JavaScript强化教程—— RegExp 对象
- 使用select2实现查询结果分页显示
- 史上最详细的JavaScript事件使用指南
- JavaScript实现拖拽预览,AJAX小文件上传
- ---Jsp(二)隐含对象和四个域对象
- odoo 新手之路-html js 上传文件 到odoo
- 身份证验证大全-javascript