我需要IP定位功能,很多IP定位API使用HTTP或HTTPS协议,返回的数据是json格式,例如百度IP定位API:
请求格式:
http://api.map.baidu.com/location/ip?ip=xx.xx.xx.xx&ak=xxxxxxxxxx&coor=bd09ll
https://api.map.baidu.com/location/ip?ip=xx.xx.xx.xx&ak=xxxxxxxxxx&coor=bd09ll
参数ip
就是要查询的IP,参数ak
是向百度申请的开发者密钥。
返回格式:
{
"address": "CN|北京|北京|None|CHINANET|1|None", //详细地址信息
"content": { //结构信息
"address": "北京市", //简要地址信息
"address_detail": { //结构化地址信息
"city": "北京市", //城市
"city_code": 131, //百度城市代码
"district": "", //区县
"province": "北京市", //省份
"street": "", //街道
"street_number": "" //门牌号
},
"point": { //当前城市中心点
"x": "116.39564504", //当前城市中心点经度
"y": "39.92998578" //当前城市中心点纬度
}
},
"status": 0 //结果状态返回码
}
因此我还需要json解析器。根据对json的理解,写下这个json解析器,命名为kajson,读作"咔json"。kajson并没有完全符合标准json格式,标准json见json官网。
-
内存管理模块
json是可递归的树状结构,节点多而杂,如果每个节点都用 malloc()这样的函数分配内存,不但造成大量内存碎片,而且频繁调用malloc()也会影响效率。内存管理模块的策略是,一次分配一块较大内存,比如4k,然后从上面切下一小片使用,比如40字节,切完后再次分配一大块,代码如下:
kajsonMem.h
//==============================================================================
// Copyright (C) 2019 王小康. All rights reserved.
//
// 作者: 王小康
// 描述: kajson内存管理模块
// 日期: 2019-05-07
//
//==============================================================================
#ifndef _KAJSON_MEM_H_
#define _KAJSON_MEM_H_
#define KAJSON_MEM_BLOCK_SIZE (1024*4)
#define KAJSON_MEM_BLOCK_NUMB (256)
typedef struct {
unsigned char *buffer;
unsigned int offset;
} KAJSON_MEM_BLOCK;
typedef struct {
unsigned int blockNum;
KAJSON_MEM_BLOCK blocks[KAJSON_MEM_BLOCK_NUMB];
} KAJSON_MEM_CTX;
void kajsonMem_init (KAJSON_MEM_CTX *ctx); //初始化,并预先分配一个内存块。
void kajsonMem_uninit(KAJSON_MEM_CTX *ctx); //去初始化,释放所有内存块。
void kajsonMem_clean (KAJSON_MEM_CTX *ctx); //清零,不释放内存块,仅仅将所有已分配内存块设置成未使用。
void* kajsonMem_alloc (KAJSON_MEM_CTX *ctx, unsigned int allocSize); //从内存块上切下一小块内存,4字节对齐。如果内存块不够就重新分配内存块。达到极限返回NULL。
#endif
kajsonMem.c
//==============================================================================
// Copyright (C) 2019 王小康. All rights reserved.
//
// 作者: 王小康
// 描述: kajson内存管理模块
// 日期: 2019-05-07
//
//==============================================================================
#include <stdlib.h>
#include <string.h>
#include "kajsonMem.h"
void kajsonMem_init(KAJSON_MEM_CTX *ctx){
memset(ctx, 0, sizeof(KAJSON_MEM_CTX));
ctx->blocks[0].buffer = malloc(KAJSON_MEM_BLOCK_SIZE);
if(ctx->blocks[0].buffer) ctx->blockNum = 1;
}
void kajsonMem_uninit(KAJSON_MEM_CTX *ctx){
unsigned int i;
for(i=0; i<ctx->blockNum; i++){
if(ctx->blocks[i].buffer){
free(ctx->blocks[i].buffer);
}
ctx->blocks[i].buffer = NULL;
ctx->blocks[i].offset = 0;
}
ctx->blockNum = 0;
}
void kajsonMem_clean(KAJSON_MEM_CTX *ctx){
unsigned int i;
for(i=0; i<ctx->blockNum; i++){
ctx->blocks[i].offset = 0;
}
}
void* kajsonMem_alloc(KAJSON_MEM_CTX *ctx, unsigned int allocSize){
allocSize = (allocSize + 3) & (~3);
if(allocSize <= KAJSON_MEM_BLOCK_SIZE){
unsigned int i;
KAJSON_MEM_BLOCK *block;
for(i=0; i<ctx->blockNum; i++){
block = &(ctx->blocks[i]);
if(allocSize <= (KAJSON_MEM_BLOCK_SIZE - block->offset)){
void *p = block->buffer + block->offset;
block->offset += allocSize;
return p;
}
}
if(ctx->blockNum >= KAJSON_MEM_BLOCK_NUMB) return NULL;
block = &(ctx->blocks[ctx->blockNum]);
block->buffer = malloc(KAJSON_MEM_BLOCK_SIZE);
if(block->buffer){
block->offset = allocSize;
ctx->blockNum += 1;
return block->buffer;
}
else{
return NULL;
}
}
else{
return NULL;
}
}
-
kajson解析器
kajson.h
//==============================================================================
// Copyright (C) 2019 王小康. All rights reserved.
//
// 作者: 王小康
// 描述: kajson解析器
// 日期: 2019-05-07
//
//==============================================================================
#ifndef _KAJSON_H_
#define _KAJSON_H_
#include "kajsonMem.h"
#define KAJSON_TYPE_ROOT 1
#define KAJSON_TYPE_STRING 2
#define KAJSON_TYPE_INTEGER 3
#define KAJSON_TYPE_FLOAT 4
#define KAJSON_TYPE_BOOLE 5
#define KAJSON_TYPE_OBJECT 6
#define KAJSON_TYPE_ARRAY 7
typedef struct objectNode {
char *key; //键名字符串
void *value; //值,字符串、整数、浮点数、布尔、对象、数组、null 中任意类型。
struct objectNode *next; //下一个节点
} KAJSON_OBJECT_NODE; //对象中成员列表节点
typedef struct arrayNode {
void *value; //值,字符串、整数、浮点数、布尔、对象、数组、null 中任意类型。
struct arrayNode *next; //下一个节点
} KAJSON_ARRAY_NODE; //数组中成员列表节点
typedef struct {
unsigned short type; //类型
unsigned short strLeng; //字符串长度
char *value; //字符串地址
} KAJSON_STRING; //字符串类型
typedef struct {
unsigned short type; //类型
unsigned short xxxx; //
int value; //整数值
} KAJSON_INTEGER; //整数类型
typedef struct {
unsigned short type; //类型
unsigned short xxxx; //
float value; //浮点值
} KAJSON_FLOAT; //浮点类型
typedef struct {
unsigned short type; //类型
unsigned short value; //布尔值
} KAJSON_BOOLE; //布尔类型
typedef struct {
unsigned short type; //类型
unsigned short number; //成员个数
KAJSON_OBJECT_NODE *head; //对象节点链表头。
KAJSON_OBJECT_NODE *tail; //对象节点链表尾。
} KAJSON_OBJECT; //对象类型
typedef struct {
unsigned short type; //类型
unsigned short number; //成员个数
KAJSON_ARRAY_NODE *head; //数组节点链表头。
KAJSON_ARRAY_NODE *tail; //数组节点链表尾。
} KAJSON_ARRAY; //数组类型
typedef struct {
unsigned short type; //类型
unsigned short xxxx; //
void *value; //根节点,字符串、整数、浮点数、布尔、对象、数组、null 中任意类型。
KAJSON_MEM_CTX memCtx; //对象中所有子孙后代成员的物理位置
} KAJSON_CTX; //kajson结构、kajson环境、json树根节点。
void kajson_init (KAJSON_CTX *jsonCtx); //json结构初始化,会预先分配一个内存块。
void kajson_uninit (KAJSON_CTX *jsonCtx); //json结构去初始化,会释放所有内存块。
void kajson_clean (KAJSON_CTX *jsonCtx); //清空json树结构,并标记所有内存块为未分配。
char* kajson_stringToJson (KAJSON_CTX *jsonCtx, char *src); //将字符串转换成json结构,成功返回src字符串中json的结束地址,失败返回NULL。失败原因可能是格式不对或内存不够。
int kajson_getOutSize (void *json); //使用 kajson_jsonToString() 之前可以用以计算输出buffer大小。
int kajson_jsonToString (void *json, char *out); //将json结构转换成字符串,返回转换后的字节数。可以先通过 kajson_getOutSize() 计算输出buffer大小。
void* kajson_search (void *json, char *path); //查询,可以从json树的任意位置开始查询,可以查询json树中除root以外的任意类型。
#if 0
//假设有如下json对象:
{
"name": "张三",
"age": 25,
"city": "武汉市",
"friends": [
{"name": "李四", "age": 26, "city": "北京市"},
{"name": "王五", "age": 25, "city": "杭州市"},
{"name": "赵六", "age": 24, "city": "上海市"}
]
}
//查询方法:
void *value = kajson_search(&json, ".name"); // 查询张三的名字
void *value = kajson_search(&json, ".friends[0].name"); // 查询张三的第1个朋友的名字
void *value = kajson_search(&json, ".friends[2].age"); // 查询张三的第3个朋友的年龄
//还支持这样查询,暂且叫做"分步查询":
void *value = kajson_search(kajson_search(&json, ".friends"), "[0].name"); // 查询张三的第1个朋友的名字
void *value = kajson_search(kajson_search(&json, ".friends[0]"), ".name"); // 查询张三的第1个朋友的名字
void *value = kajson_search(kajson_search(kajson_search(&json, ".friends"), "[0]"), ".name"); // 查询张三的第1个朋友的名字
//为什么要支持"分步查询"呢?假如同时查询张三的第3个朋友的名字、年龄和所在地,"分步查询"速度更快。
//非"分步查询":
void *name = kajson_search(&json, ".friends[2].name"); // 查询张三的第3个朋友的名字
void *age = kajson_search(&json, ".friends[2].age"); // 查询张三的第3个朋友的年龄
void *city = kajson_search(&json, ".friends[2].city"); // 查询张三的第3个朋友的所在地
//"分步查询":
void *frie = kajson_search(&json, ".friends[2]"); // 查询张三的第3个朋友
void *name = kajson_search(frie, ".name"); // 查询张三的第3个朋友的名字
void *age = kajson_search(frie, ".age"); // 查询张三的第3个朋友的年龄
void *city = kajson_search(frie, ".city"); // 查询张三的第3个朋友的所在地
#endif
#endif
kajson.c
//==============================================================================
// Copyright (C) 2019 王小康. All rights reserved.
//
// 作者: 王小康
// 描述: kajson解析器
// 日期: 2019-05-07
//
//==============================================================================
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "kajsonMem.h"
#include "kajson.h"
////////////////////////////////////////////////////////////////////////////////
///////////////////////////// 字符串转json结构 /////////////////////////////////
static KAJSON_STRING* newString(KAJSON_MEM_CTX *memCtx, char *value){
KAJSON_STRING *string = kajsonMem_alloc(memCtx, sizeof(KAJSON_STRING));
if(string == NULL) return NULL;
string->type = KAJSON_TYPE_STRING;
string->strLeng = ((value)&&(value[0])) ? strlen(value) : 0;
string->value = value;
return string;
}
static KAJSON_INTEGER* newInteger(KAJSON_MEM_CTX *memCtx, int value){
KAJSON_INTEGER *integer = kajsonMem_alloc(memCtx, sizeof(KAJSON_INTEGER));
if(integer == NULL) return NULL;
integer->type = KAJSON_TYPE_INTEGER;
integer->value = value;
return integer;
}
static KAJSON_FLOAT* newFloat(KAJSON_MEM_CTX *memCtx, float value){
KAJSON_FLOAT *floa = kajsonMem_alloc(memCtx, sizeof(KAJSON_FLOAT));
if(floa == NULL) return NULL;
floa->type = KAJSON_TYPE_FLOAT;
floa->value = value;
return floa;
}
static KAJSON_BOOLE* newBoole(KAJSON_MEM_CTX *memCtx, int value){
KAJSON_BOOLE *boole = kajsonMem_alloc(memCtx, sizeof(KAJSON_BOOLE));
if(boole == NULL) return NULL;
boole->type = KAJSON_TYPE_BOOLE;
boole->value = value ? 1 : 0;
return boole;
}
static KAJSON_OBJECT* newObject(KAJSON_MEM_CTX *memCtx, KAJSON_OBJECT_NODE *node){
KAJSON_OBJECT *object = kajsonMem_alloc(memCtx, sizeof(KAJSON_OBJECT));
if(object == NULL) return NULL;
object->type = KAJSON_TYPE_OBJECT;
object->head = node;
object->tail = node;
if(node) object->number = 1;
else object->number = 0;
return object;
}
static KAJSON_ARRAY* newArray(KAJSON_MEM_CTX *memCtx, KAJSON_ARRAY_NODE *node){
KAJSON_ARRAY *array = kajsonMem_alloc(memCtx, sizeof(KAJSON_ARRAY));
if(array == NULL) return NULL;
array->type = KAJSON_TYPE_ARRAY;
array->head = node;
array->tail = node;
if(node) array->number = 1;
else array->number = 0;
return array;
}
static KAJSON_OBJECT_NODE* newObjectNode(KAJSON_MEM_CTX *memCtx, char *key, void *value){
KAJSON_OBJECT_NODE *node = kajsonMem_alloc(memCtx, sizeof(KAJSON_OBJECT_NODE));
if(node == NULL) return NULL;
node->key = key;
node->value = value;
node->next = NULL;
return node;
}
static KAJSON_ARRAY_NODE* newArrayNode(KAJSON_MEM_CTX *memCtx, void *value){
KAJSON_ARRAY_NODE *node = kajsonMem_alloc(memCtx, sizeof(KAJSON_ARRAY_NODE));
if(node == NULL) return NULL;
node->value = value;
node->next = NULL;
return node;
}
static void addObjectNode(KAJSON_OBJECT *object, KAJSON_OBJECT_NODE *node){
object->number += 1;
if(object->head) object->tail->next = node;
else object->head = node;
object->tail = node;
}
static void addArrayNode(KAJSON_ARRAY *array, KAJSON_ARRAY_NODE *node){
array->number += 1;
if(array->head) array->tail->next = node;
else array->head = node;
array->tail = node;
}
////////////////////////////////////////////////////////////////////////////////
//判断是否遇到终止符
#define isTerminatorSymbol(c) (((c)==',')||(((c)>0)&&((c)<=' '))||((c)=='}')||((c)==']'))
// 忽略连续的空白符,这里将(0 ~ ' ']之间的所有字符都当做空白符。
static char* skipOverBlank(char *src){
while(*src){
if((*src > 0) && (*src <= ' ')) src++;
else if((*src > ' ') && (*src <= '~')) return src;
else return NULL;
}
return NULL;
}
// 生成字符串结构,不支持转义字符,字符串中允许出现除 '"' 以外的所有字符。
static char* makeString(KAJSON_STRING **string, KAJSON_MEM_CTX *memCtx, char *src){
char *start = src;
int length = 0;
while(*src){
if(*src != '\"'){
length++;
src++;
}
else{
char* strValue = kajsonMem_alloc(memCtx, length+1);
if(strValue == NULL) return NULL;
memcpy(strValue, start, length);
strValue[length] = 0;
KAJSON_STRING *str = newString(memCtx, strValue);
if(str == NULL) return NULL;
*string = str;
return src+1;
}
}
return NULL;
}
// 生成整数结构或浮点数结构
static char* makeIntegerOrFloat(void **number, KAJSON_MEM_CTX *memCtx, char *src){
int flag = 0; //(0=正数,1=负数。) | (0=整数,2=浮点数)
if(*src == '-'){
flag = 1;
src = skipOverBlank(src+1);
}
else if(*src == '+'){
src = skipOverBlank(src+1);
}
if(src == NULL) return NULL;
char *start = src;
int length = 0;
while(*src){
if((*src >= '0') && (*src <= '9')){
length++;
src++;
}
else if(*src == '.'){
if(flag&2){
return NULL;
}
else{
flag |= 2;
length++;
src++;
}
}
else if(isTerminatorSymbol(*src)){
char *numberBuffer = alloca(length+1);
memcpy(numberBuffer, start, length);
numberBuffer[length] = 0;
if(flag&2){
float value = (flag&1) ? -atof(numberBuffer) : atof(numberBuffer);
KAJSON_FLOAT *floa = newFloat(memCtx, value);
if(floa == NULL) return NULL;
*number = floa;
}
else{
int value = (flag&1) ? -atoi(numberBuffer) : atoi(numberBuffer);
KAJSON_INTEGER *integer = newInteger(memCtx, value);
if(integer == NULL) return NULL;
*number = integer;
}
return src;
}
else{
return NULL;
}
}
return NULL;
}
// 生成键值对的键,这里的键中字符的范围是 52个英文字符 + 10个数字字符 + 下划线。
static char* makeKey(char **key, KAJSON_MEM_CTX *memCtx, char *src){
char *start = src;
int length = 0;
while(*src){
if(((*src>='a')&&(*src<='z')) || ((*src>='A')&&(*src<='Z')) || ((*src>='0')&&(*src<='9')) || (*src=='_')){
length++;
src++;
}
else if(*src == '\"'){
char *keyBuffer = kajsonMem_alloc(memCtx, length+1);
if(keyBuffer == NULL) return NULL;
memcpy(keyBuffer, start, length); //start没有以0结尾,不能用strcpy()
keyBuffer[length] = 0;
*key = keyBuffer;
return src+1;
}
else{
return NULL;
}
}
return NULL;
}
static char* makeValue(void **value, KAJSON_MEM_CTX *memCtx, char *src);
// 生成对象的一个成员节点
static char* makeObjectNode(KAJSON_OBJECT_NODE **node, KAJSON_MEM_CTX *memCtx, char *src){
// 提取键
char *key = NULL;
src = makeKey(&key, memCtx, src);
if(src == NULL) return NULL;
// 寻找键值分割符':'
src = skipOverBlank(src);
if(src == NULL) return NULL;
if(*src != ':') return NULL;
src = skipOverBlank(src+1);
if(src == NULL) return NULL;
// 提取值
void *value = NULL;
src = makeValue(&value, memCtx, src);
if(src == NULL) return NULL;
// 生成对象节点并返回
KAJSON_OBJECT_NODE *objectNode = newObjectNode(memCtx, key, value);
if(objectNode == NULL) return NULL;
*node = objectNode;
return src;
}
// 生成数组的一个成员节点
static char* makeArrayNode(KAJSON_ARRAY_NODE **node, KAJSON_MEM_CTX *memCtx, char *src){
// 提取值
void *value = NULL;
src = makeValue(&value, memCtx, src);
if(src == NULL) return NULL;
// 生成数组节点并返回
KAJSON_ARRAY_NODE *arrayNode = newArrayNode(memCtx, value);
if(arrayNode == NULL) return NULL;
*node = arrayNode;
return src;
}
// 生成对象
static char* makeObject(KAJSON_OBJECT **object, KAJSON_MEM_CTX *memCtx, char *src){
KAJSON_OBJECT *obj = newObject(memCtx, NULL);
if(obj == NULL) return NULL;
int status = 0;
KAJSON_OBJECT_NODE *node;
for(;;){
src = skipOverBlank(src);
if(src == NULL) return NULL;
if(status == 0){
if(*src == '\"'){
src = makeObjectNode(&node, memCtx, src+1);
if(src == NULL) return NULL;
addObjectNode(obj, node);
status = 1;
}
else if(*src == '}'){
*object = obj;
return src+1;
}
else{
return NULL;
}
}
else if(status == 1){
if(*src == ','){
src += 1;
status = 2;
}
else if(*src == '}'){
*object = obj;
return src+1;
}
else{
return NULL;
}
}
else{
if(*src == '\"'){
src = makeObjectNode(&node, memCtx, src+1);
if(src == NULL) return NULL;
addObjectNode(obj, node);
status = 1;
}
else{
return NULL;
}
}
}
}
// 生成数组
static char* makeArray(KAJSON_ARRAY **array, KAJSON_MEM_CTX *memCtx, char *src){
KAJSON_ARRAY *arr = newArray(memCtx, NULL);
if(arr == NULL) return NULL;
int status = 0;
KAJSON_ARRAY_NODE *node;
for(;;){
src = skipOverBlank(src);
if(src == NULL) return NULL;
if(status == 0){
if(*src == ']'){
*array = arr;
return src+1;
}
else{
src = makeArrayNode(&node, memCtx, src);
if(src == NULL) return NULL;
addArrayNode(arr, node);
status = 1;
}
}
else if(status == 1){
if(*src == ','){
src += 1;
status = 2;
}
else if(*src == ']'){
*array = arr;
return src+1;
}
else{
return NULL;
}
}
else{
if(*src != ']'){
src = makeArrayNode(&node, memCtx, src);
if(src == NULL) return NULL;
addArrayNode(arr, node);
status = 1;
}
else{
return NULL;
}
}
}
}
// 生成键值对的值。
static char* makeValue(void **value, KAJSON_MEM_CTX *memCtx, char *src){
if(*src == '\"'){
KAJSON_STRING *str = NULL;
src = makeString(&str, memCtx, src+1);
if(src == NULL) return NULL;
*value = str;
return src;
}
else if(*src == '{'){
KAJSON_OBJECT *obj = NULL;
src = makeObject(&obj, memCtx, src+1);
if(src == NULL) return NULL;
*value = obj;
return src;
}
else if(*src == '['){
KAJSON_ARRAY *arr = NULL;
src = makeArray(&arr, memCtx, src+1);
if(src == NULL) return NULL;
*value = arr;
return src;
}
else if((src[0]=='t')&&(src[1]=='r')&&(src[2]=='u')&&(src[3]=='e')&&(isTerminatorSymbol(src[4]))){
KAJSON_BOOLE *boo = newBoole(memCtx, 1);
if(boo == NULL) return NULL;
*value = boo;
return src+4;
}
else if((src[0]=='f')&&(src[1]=='a')&&(src[2]=='l')&&(src[3]=='s')&&(src[4]=='e')&&(isTerminatorSymbol(src[5]))){
KAJSON_BOOLE *boo = newBoole(memCtx, 0);
if(boo == NULL) return NULL;
*value = boo;
return src+5;
}
else if((src[0]=='n')&&(src[1]=='u')&&(src[2]=='l')&&(src[3]=='l')&&(isTerminatorSymbol(src[4]))){
*value = NULL;
return src+4;
}
else{
void *num = NULL;
src = makeIntegerOrFloat(&num, memCtx, src);
if(src == NULL) return NULL;
*value = num;
return src;
}
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////// 计算json结构转字符串后的大小 ////////////////////////
static int getValueSize(void *json);
static int getObjectSize(KAJSON_OBJECT_NODE *node){
int size = 0;
while(node){
size += (((node->key) && (node->key[0])) ? strlen(node->key) : 0) + 3; //"": = 3.
size += getValueSize(node->value);
node = node->next;
if(node) size += 1; //, = 1.
}
return size;
}
static int getArraySize(KAJSON_ARRAY_NODE *node){
int size = 0;
while(node){
size += getValueSize(node->value);
node = node->next;
if(node) size += 1; //, = 1.
}
return size;
}
static int getValueSize(void *json){
if(json){
int type = *(unsigned short*)json;
switch(type){
case KAJSON_TYPE_ROOT:
return getValueSize(((KAJSON_CTX*)json)->value);
case KAJSON_TYPE_STRING:
return ((KAJSON_STRING*)json)->strLeng + 2; //"" = 2
case KAJSON_TYPE_INTEGER:
return snprintf(NULL, 0, "%d", ((KAJSON_INTEGER*)json)->value);
case KAJSON_TYPE_FLOAT:
return snprintf(NULL, 0, "%f", ((KAJSON_FLOAT*)json)->value);
case KAJSON_TYPE_BOOLE:
return ((((KAJSON_BOOLE*)json)->value) ? 4 : 5); //true = 4, false = 5.
case KAJSON_TYPE_OBJECT:
return getObjectSize(((KAJSON_OBJECT*)json)->head) + 2; //{} = 2
case KAJSON_TYPE_ARRAY:
return getArraySize(((KAJSON_ARRAY*)json)->head) + 2;//[] = 2
default:
return 0;
}
}
else{
return 4; //null = 4.
}
}
////////////////////////////////////////////////////////////////////////////////
//////////////////////////// json结构转字符串 //////////////////////////////////
static int valueToString(char *out, void *value);
static int objectToString(char *out, KAJSON_OBJECT_NODE *node){
int offset = 0;
while(node){
offset += sprintf(out+offset, "\"%s\":", ((node->key) && (node->key[0])) ? node->key : "");
offset += valueToString(out+offset, node->value);
node = node->next;
if(node){
out[offset] = ',';
offset += 1;
}
}
return offset;
}
static int arrayToString(char *out, KAJSON_ARRAY_NODE *node){
int offset = 0;
while(node){
offset += valueToString(out+offset, node->value);
node = node->next;
if(node){
out[offset] = ',';
offset += 1;
}
}
return offset;
}
static int valueToString(char *out, void *value){
if(value){
int n, type = *(unsigned short*)value;
switch(type){
case KAJSON_TYPE_ROOT:
return valueToString(out, ((KAJSON_CTX*)value)->value);
case KAJSON_TYPE_STRING:
return sprintf(out, "\"%s\"", ((KAJSON_STRING*)value)->value ? ((KAJSON_STRING*)value)->value : "");
case KAJSON_TYPE_INTEGER:
return sprintf(out, "%d", ((KAJSON_INTEGER*)value)->value);
case KAJSON_TYPE_FLOAT:
return sprintf(out, "%f", ((KAJSON_FLOAT*)value)->value);
case KAJSON_TYPE_BOOLE:
return sprintf(out, "%s", ((KAJSON_BOOLE*)value)->value ? "true" : "false");
case KAJSON_TYPE_OBJECT:
out[0] = '{';
n = objectToString(out+1, ((KAJSON_OBJECT*)value)->head);
out[1+n] = '}';
out[2+n] = 0;
return n+2;
case KAJSON_TYPE_ARRAY:
out[0] = '[';
n = arrayToString(out+1, ((KAJSON_ARRAY*)value)->head);
out[1+n] = ']';
out[2+n] = 0;
return n+2;
default:
return 0;
}
}
else{
return sprintf(out, "null");
}
}
////////////////////////////////////////////////////////////////////////////////
//////////////////////////////// 查询接口 /////////////////////////////////////
//数组的索引的范围[0 - 9999]
static char* getIndexFromPath(int *index, char *path){
char buffer[8];
int length = 0;
for(;;){
if((*path >= '0') && (*path <= '9')){
if(length >= 4) return NULL;
buffer[length] = *path;
length++;
path++;
}
else if(*path == ']'){
buffer[length] = 0;
*index = atoi(buffer);
return path+1;
}
else{
return NULL;
}
}
}
//对象的键的长度上限127个字节
static char* getKeyFromPath(char *key, char *path){
int length = 0;
for(;;){
if(
((*path >= 'a') && (*path <= 'z')) ||
((*path >= 'A') && (*path <= 'Z')) ||
((*path >= '0') && (*path <= '9')) ||
(*path == '_')
){
if(length >= 127) return NULL;
*key = *path;
length++;
path++;
key++;
}
else if((*path=='.')||(*path=='[')||(*path==0)){
*key = 0;
return path;
}
else{
return NULL;
}
}
}
static void* getValueByIndex(KAJSON_ARRAY_NODE *node, int index){
while(node){
if(index <= 0) return node->value;
node = node->next;
index--;
}
return NULL;
}
static void* getValueByKey(KAJSON_OBJECT_NODE *node, char *key){
while(node){
if((node->key) && (strcmp(key, node->key) == 0)){
return node->value;
}
node = node->next;
}
return NULL;
}
static void* searchValue(void *json, char *path, char *tmpKeyBuffer){
if(json == NULL) return NULL;
if(*path == '.'){
if(*(unsigned short*)json != KAJSON_TYPE_OBJECT) return NULL;
path = getKeyFromPath(tmpKeyBuffer, path+1);
if(path == NULL) return NULL;
json = getValueByKey(((KAJSON_OBJECT*)json)->head, tmpKeyBuffer);
if(json == NULL) return NULL;
return searchValue(json, path, tmpKeyBuffer);
}
else if(*path == '['){
if(*(unsigned short*)json != KAJSON_TYPE_ARRAY) return NULL;
int index;
path = getIndexFromPath(&index, path+1);
if(path == NULL) return NULL;
json = getValueByIndex(((KAJSON_ARRAY*)json)->head, index);
if(json == NULL) return NULL;
return searchValue(json, path, tmpKeyBuffer);
}
else if(*path == 0){
return json;
}
else{
return NULL;
}
}
////////////////////////////////////////////////////////////////////////////////
///////////////////////////////// 对外接口 /////////////////////////////////////
void kajson_init(KAJSON_CTX *jsonCtx){
jsonCtx->type = KAJSON_TYPE_ROOT;
jsonCtx->value = NULL;
kajsonMem_init(&(jsonCtx->memCtx));
}
void kajson_uninit(KAJSON_CTX *jsonCtx){
jsonCtx->value = NULL;
kajsonMem_uninit(&(jsonCtx->memCtx));
}
void kajson_clean(KAJSON_CTX *jsonCtx){
jsonCtx->value = NULL;
kajsonMem_clean(&(jsonCtx->memCtx));
}
char* kajson_stringToJson(KAJSON_CTX *jsonCtx, char *src){
src = skipOverBlank(src);
if(src == NULL) return NULL;
src = makeValue(&(jsonCtx->value), &(jsonCtx->memCtx), src);
if(src == NULL) return NULL;
return src;
}
int kajson_getOutSize(void *json){
return 1 + getValueSize(json);
}
int kajson_jsonToString(void *json, char *out){
return 1 + valueToString(out, json);
}
void* kajson_search(void *json, char *path){
if((json == NULL) || (path == NULL)) return NULL;
char tmpKeyBuffer[128];
if(*(unsigned short*)json == KAJSON_TYPE_ROOT){
return searchValue(((KAJSON_CTX*)json)->value, path, tmpKeyBuffer);
}
else{
return searchValue(json, path, tmpKeyBuffer);
}
}
这个json解析器并没有完全符合json格式,例如字符串目前不支持转义字符。
- 测试程序
//==============================================================================
//
// 作者: 王小康
// 描述: kajson解析器测试程序
// 日期: 2019-05-07
//
//==============================================================================
#include <stdio.h>
#include <stdlib.h>
#include "kajson.h"
int main(int argc, char* argv[]){
char* testStr[] = {
" {\"name\":\"张三\",\"friend\":[{\"name\":\"李四\",\"age\":30},{\"name\":\"王五\",\"age\":33}],\"array\":[[1,2,3],[4,5,6],[7,8,9]]} ", //查询测试
" {\"user\" : \"root\" , \"passwd\" : \"admin\"} ", //测试 root={} 情况
" [\"root\" , \"admin\"] ", //测试 root=[] 情况
" \"hello world ...\" ", //测试 root=字符串 情况
" 123456789 ", //测试 root=整数值 情况
" -12345.678901 ", //测试 root=浮点数 情况
" true ", //测试 root=布尔值 情况
" false ", //测试 root=布尔值 情况
" null ", //测试 root=null 情况
" {\"\":\"root\", \"\":\"admin\", \"name\":\"\"} ", //无名键和空字符串测试
" {\"name\": \"不告诉你\", \"city\": \"武汉市\"} ", //字符串包含中文测试。仅限UTF-8编码。
" [-3402823669209384634633746074317791234.123456789, 0.00000000000000000000000000000000000000000012345678999999] ", //极大极小浮点数测试
" [{\"user\":\"root\"}, \"hello world ...\", 123456, [1,2,3,+ 4,-5.1,6,[[],[]]], true, false, null, +3.1415926] ", //复杂数组测试
" {\"object\":{\"x1\":{\"x2\":[{},{}]}}, \"array\":[{},[[]], [+1.2,3,-1.5], true, null, - 3.14], \"other\":null} ", //复杂对象测试
" \r\n {\r\n \"user\" \n\t\r : \n\t \"root\"\n\t ,\"passwd\":\"admin\", \"number\"\n:-\n\n\n\t3.14\n\n\n} \n\t\r ", //复杂空白符测试
" {} "
};
int testStrNum = (sizeof(testStr)/sizeof(char*));
KAJSON_CTX json;
kajson_init(&json);
// 转换测试
int i, n;
char *end;
for(i=0; i<testStrNum; i++){
end = kajson_stringToJson(&json, testStr[i]);
if(end){
char *buffer = malloc(kajson_getOutSize(&json));
n = kajson_jsonToString(&json, buffer);
printf("test[%2d], outSize() = %3d, string = %s\n", i, n, buffer);
free(buffer);
}
kajson_clean(&json);
}
putchar('\n');
//查询测试
char outBuffer[1024];
void *jsonValue;
kajson_clean(&json);
kajson_stringToJson(&json, testStr[0]);
kajson_jsonToString(&json, outBuffer);
printf("root = %s\n", outBuffer);
jsonValue = kajson_search(&json, ".name");
if(jsonValue){
kajson_jsonToString(jsonValue, outBuffer);
printf("search(\".name\") = %s\n", outBuffer);
}
jsonValue = kajson_search(&json, ".friend");
if(jsonValue){
kajson_jsonToString(jsonValue, outBuffer);
printf("search(\".friend\") = %s\n", outBuffer);
}
jsonValue = kajson_search(&json, ".friend[0]");
if(jsonValue){
kajson_jsonToString(jsonValue, outBuffer);
printf("search(\".friend[0]\") = %s\n", outBuffer);
}
jsonValue = kajson_search(&json, ".friend[1].age");
if(jsonValue){
kajson_jsonToString(jsonValue, outBuffer);
printf("search(\".friend[1].age\") = %s\n", outBuffer);
}
jsonValue = kajson_search(&json, ".array");
if(jsonValue){
kajson_jsonToString(jsonValue, outBuffer);
printf("search(\".array\") = %s\n", outBuffer);
}
jsonValue = kajson_search(&json, ".array[1][1]");
if(jsonValue){
kajson_jsonToString(jsonValue, outBuffer);
printf("search(\".array[1][1]\") = %s\n", outBuffer);
}
jsonValue = kajson_search(kajson_search(kajson_search(&json, ".friend"), "[1]"), ".age");
if(jsonValue){
kajson_jsonToString(jsonValue, outBuffer);
printf("search(search(search(json, \".friend\"), \"[1]\"), \".age\") = %s\n", outBuffer);
}
jsonValue = kajson_search(kajson_search(kajson_search(&json, ".array"), "[2]"), "[2]");
if(jsonValue){
kajson_jsonToString(jsonValue, outBuffer);
printf("search(search(search(json, \".array\"), \"[2]\"), \"[2]\") = %s\n", outBuffer);
}
kajson_uninit(&json);
return 0;
}
-
运行效果
注意事项:因为有中文字符,为了避免乱码,代码中的字符集应该和终端字符集相同,这里我都用的utf8,如图:
网友评论