按照通常学习的思路,将对Json的学习分为4步,如下:
一、了解Json格式;
二、解析Json格式;
三、创建Json格式;
四、使用标准接口;
JSON 是基于纯文本的数据格式,我们可以用 JSON 传输一个简单的 String,Number,Boolean,也可以传输一个数组,或者一个复杂的 Object 对象。Json可以理解为一种规范化的键值对(key:value)描述方法,同 XML 相比,JSON 提供了更好的简单性和灵活性。
对于JSON的定义,在网上能找到许多,在其官方网站http://www.json.org.cn/ 有详细的介绍。
光看看Json的定义,还是远远不够的,下面就来动一下手,用C语言写个程序来解读一下Json字符串。
我是在Linux下编程的,但是对于C与Json,都是与平台无关的,所以windows平台上编程也是一样适用的。
Json使用的通常情况,分为两种,一个是做为接口,在两个进程之间传递信息;一个是做为存储信息,在程序启动之初读取后做初始化使用。
为了便于测试,我这里是手工写好一个符合Json格式的文本文件,然后进行读取解析。
这里主要是实现对key的解析,以及对value的解析。value又分为几种情况,我只考虑3种情况,string,int,float,这样,对于简单的应用,也是足够使用了的。
解析第一个Json字符串,如下:
{"firstName":"Brett"}
或者如下,一个数字的例子:
{"age":8}
要读取的内容如下:
{"firstName":" Brett ","lastName":"Jason","email":"[email protected]","age":8,"height":1.67}
要读取的内容是一个对象,我们需要定义一个与之匹配的结构体,如下:
typedef struct
{
charfirstName[32];
charlastName[32];
charemail[64];
intage;
floatheight;
}people;
需要注意,结构体中的成员在Json串中出现的顺序,不一定与结构体中的成员定义顺序一致(例如,age可能出现在第一个位置)。说到这里,其实就是提出了一个问题,在我们写的程序中,需要考虑哪些异常情况?例如,结构体中成员出现的顺序乱,或者某个成员不存在,或者多了个“{”或“,”等,哪些异常情况需要支持,哪些不需要支持,只需要报错就好了。
异常的情况比较多,不容易一次考虑全,可以参考下别人是怎么做的。建议使用网上的Json在线解析工具做一些异常测试,再来决定如何处理。网上的在线解析工具,可以参考这个:
http://json.cn/
我是用了一个比较复杂的数组,成员为结构体的数组,如下:
{
"people":[
{"firstName":"z","lastName":"Jason","email":"[email protected]","height":1.67},
{"lastName":"jadena","email":"[email protected]","age":8,"height":1.17},
{"email":"[email protected]","firstName":"z","lastName":"Juliet","age":36,"height":1.55}
]
}
注意看下,这个数组中,其成员结构体应该还是同上面定义的结构体people一致,但是,其中某些项是缺少的,某些成员的出现顺序是错乱的,这也是对我们程序的一种测试。
对于结构体为成员的数组,需要考虑的东西就比较多了,我大体列了几项:
1, 如何实现某个key的重复出现的解析?
首先看看“lastName”,它是在每个对象中出现一次的,也就是说,第一个“lastName”赋值给第一个对象,第二个“lastName”赋值给第二个对象,如此类推。
2, 对于缺少的成员,如何处理?
例如“firstName”,在第二个对象中就缺少了,此时,绝不能搜索第三个对象中的“firstName”来填充第二个对象。这里就出来一个问题,怎么判断第二个对象的结束?
每个对象都是有边界的,由“{”开始,到“}”结束。对于第二个对象,若搜索到结束处,还没有“firstName”,那么,就认为是读取不到该值了。此时,并不是认为Json串有格式问题,而是给相应值赋上默认值,继续往后解析。
3, 如何界定数组的结束?
每个数组都是有边界的,由“[”开始,到“]”结束。
4, 对于更复杂的情况,例如,数组的成员是结构体,在结构体中又有数组,该如何解析呢?
两种方法:
1)先定义一个解析的规则,或者说模板,然后照着模板来解析。对于解析规则,要考虑到兼容性:
若添加了新项,旧的解析规则应该能工作,只是不解析新添加成员;
对于旧json串,新的解析规则应该能工作,只是新添加成员不能赋值;
2)使用一种标准的解析方法,能够实现互相嵌套解析。
目前,我使用了第一种方案,因为这样对于编程来说要简单一些。如果想做一个通用接口,还是应该选择第二种方案来做。
整体判断是否符合json串的规则,我整理了几项:
1,“{”与“}”的个数,应该是匹配的(需要去掉字符串值中的{});
2,“[”与“]”应该匹配(需要去掉字符串值中的[]);
3,“"”是成对的(需要去掉字符串内容中的");
4,每个键值对中有一个“:”;
5,两个同级相邻的键值对(或者数组元素、对象)之间是“,”;
6,判断过程中要考虑字符串的规则,尤其是特殊字符;
7,判断过程中要考虑数字的规则;
在代码实现中,我使用了前3项来进行有效性判断,并不完整,但是也能做一个初步的判断了。
关于效率问题,其实是与实现方案密切相关的,我在编程过程中考虑过3种方案:
1, 在整个Json串中进行逐个key的搜索;
----效率不高,实现逻辑比较简单。这是我第一次选择的方案,但是存在问题:若数组内某个对象缺少某个成员的赋值,则该key值不存在,会导致搜索下一个对象的,从而导致赋值错误。
2, 从json串的头开始解析,循序往后;
----效率高,但是这个解析方法的实现,比较复杂。里面还涉及递归调用的问题,我还没有考虑明白,暂时不采用。
3, 对每个结构体内容的逐个搜索;
对于一个结构体,知道开始与结束,就有了边界,可以解决第1个方案中的成员缺少问题。这也是我目前采用的方案,效率介于以上两个方案之间。
本文所附带例程,只是一个学习之作,对于初学Json的同学,可以有些借鉴参考的作用。但是,对于异常处理、字符串中的特殊字符的处理等,都不完善,解析方法也不是做成一个通用的接口,只能根据具体的协议类型,来定制一个解析函数,在使用过程中受限较多,所以,并不能真正用于软件工程开发中。
真正用于软件工程开发中的Json接口,C语言版本的大家可以参考CJSON,下载地址为http://sourceforge.net/projects/cjson/。至于其他语言的,可以到 http://www.json.org.cn/ 中去寻找相应的实现。
#include <stdio.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #define PRINT(msg...) do {printf("lintax: "); printf(msg);}while(0) typedef struct { int id; char firstName[32]; char lastName[32]; char email[64]; int age; float height; }people; //在缓冲buf 中寻找字符串str //this func could find something in bin (not only in str) int find_str_in_buf(char * buf,int len,char * str,int len_str) { int pos_buf=0; char * buf_tmp=buf; int i,num; if((buf==NULL)||(str==NULL)) { return -1; } while(pos_buf+len_str<=len) { num=0; for(i=0;i<len_str;i++) { if(buf_tmp[i]==str[i]) { num++; } else { break; } } if(num==len_str) { return pos_buf; } pos_buf++; buf_tmp++; } return -1; } //parse json string //judge whether the string meet the Json standards //Json conditions: //{}匹配 //[]匹配 //"是成对的,去掉字符串内容中的" ////每个键值对中有一个: ////两个键值对(数组元素,对象)之间是, ////字符串的规则 ////数字的规则 int judge_json_format(char * json_str, int str_len) { int pos_str=0; int num_brace_left=0,num_brace_right=0; int num_bracket_left=0,num_bracket_right=0; int num_quote=0; int is_quote_left=0; int num_solidus=0; if ( (json_str==NULL)||(str_len<=0) ) { return -1; } //跳过开头的空格与回车,寻找第一个{ char char_tmp='\0'; do { char_tmp=*(json_str+pos_str); if(( char_tmp==' ') ||( char_tmp=='\t') ||( char_tmp=='\n') ||( char_tmp=='\r') ) { pos_str++; } else if( char_tmp=='{') { pos_str++; break; } else { PRINT("find the first { failed\n"); return -1; } } while(char_tmp!='{'); num_brace_left++; PRINT("find the first { ,pos_str=%d,num_brace_left=%d\n",pos_str,num_brace_left); //count the "[]{} do { char_tmp=*(json_str+pos_str); switch(char_tmp) { case '\\': num_solidus++; pos_str++;//跳过转义字符 PRINT("pos_str=%d ",pos_str); PRINT("num_solidus=%d\n",num_solidus); break; case '{': if(is_quote_left==0) { num_brace_left++; PRINT("pos_str=%d ",pos_str); PRINT("num_brace_left=%d,num_brace_right=%d\n",num_brace_left,num_brace_right); } break; case '}': if(is_quote_left==0) { num_brace_right++; PRINT("pos_str=%d ",pos_str); PRINT("num_brace_left=%d,num_brace_right=%d\n",num_brace_left,num_brace_right); } break; case '[': if(is_quote_left==0) { num_bracket_left++; PRINT("pos_str=%d ",pos_str); PRINT("num_bracket_left=%d,num_bracket_right=%d\n",num_bracket_left,num_bracket_right); } break; case ']': if(is_quote_left==0) { num_bracket_right++; PRINT("pos_str=%d ",pos_str); PRINT("num_bracket_left=%d,num_bracket_right=%d\n",num_bracket_left,num_bracket_right); } break; case '\"': num_quote++; is_quote_left=1-is_quote_left; //PRINT("pos_str=%d ",pos_str); //PRINT("num_quote=%d\n",num_quote); break; default: break; } pos_str++; } while((char_tmp!='\0')||(pos_str<str_len)); PRINT("num_brace_left=%d,num_brace_right=%d\n",num_brace_left,num_brace_right); PRINT("num_bracket_left=%d,num_bracket_right=%d\n",num_bracket_left,num_bracket_right); PRINT("num_quote=%d\n",num_quote); if(num_brace_left!=num_brace_right) { PRINT("failed : num_brace_left=%d,num_brace_right=%d\n",num_brace_left,num_brace_right); return -1; } if(num_bracket_left!=num_bracket_right) { PRINT("failed : num_bracket_left=%d,num_bracket_right=%d\n",num_bracket_left,num_bracket_right); return -1; } if((num_quote%2)!=0) { PRINT("failed : num_quote=%d\n",num_quote); return -1; } return 0; } //读取 key": 都是一样的,提取为一个函数 int find_value_from_json_str(char * json_str, int str_len, char * str_key, int len_key) { int pos_str=0; if ( (json_str==NULL)||(str_key==NULL) ) { return -1; } //1,find key int pos=find_str_in_buf(json_str+pos_str, str_len, str_key, len_key); //PRINT("%s: pos_str=%d, pos=%d\n",__func__,pos_str, pos); //PRINT("str is :%s\n",json_str+pos_str); if(pos>=0) { pos_str+=pos; } else { PRINT("find key failed\n"); return -1; } //2,find the key end " //跳过key的长度 pos_str+=len_key; pos=find_str_in_buf(json_str+pos_str, str_len-pos_str, "\"", 1); //PRINT("%s: find the key end \" pos_str=%d, pos=%d\n",__func__,pos_str, pos); //PRINT("str is :%s\n",json_str+pos_str); if(pos>=0) { pos_str+=pos; } else { PRINT("find the key end \" failed\n"); return -1; } //3,find : pos_str+=pos+1; pos=find_str_in_buf(json_str+pos_str, str_len-pos_str, ":", 1); //PRINT("%s: find : pos_str=%d, pos=%d\n",__func__,pos_str, pos); //PRINT("str is :%s\n",json_str+pos_str+1); if(pos>=0) { pos_str+=pos; } else { PRINT("find : failed\n"); return -1; } //PRINT("after find , to return: %d\n", pos_str+1); return (pos_str+1); } int get_int_from_json_str(char * json_str, int str_len, char * str_key, int len_key, int *value) { int pos_str=0; if ( (json_str==NULL)||(str_key==NULL)||(value==NULL)) { return -1; } //1,find value pos pos_str=find_value_from_json_str(json_str, str_len, str_key, len_key); if(pos_str<0) { PRINT("find value failed\n"); return -1; } //2,change the string to int int value_int=atoi(json_str+pos_str); *value=value_int; //PRINT("after memcpy str_value, to return: %d\n", pos_str); return (pos_str); } int get_float_from_json_str(char * json_str, int str_len, char * str_key, int len_key, float *value) { int pos_str=0; if ( (json_str==NULL)||(str_key==NULL)||(value==NULL)) { return -1; } //1,find key value pos_str=find_value_from_json_str(json_str, str_len, str_key, len_key); if(pos_str<0) { PRINT("find value failed\n"); return -1; } //2,change the string to float float value_float=atof(json_str+pos_str); *value=value_float; //PRINT("after memcpy str_value, to return: %d\n", pos_str); return (pos_str); } int get_str_from_json_str(char * json_str, int str_len, char * str_key, int len_key, char * str_value, int * plen_value) { int pos_str=0; int pos_start=0; int pos=0; int size_value=*plen_value; if ( (json_str==NULL)||(str_key==NULL)||(str_value==NULL)) { str_value=NULL; return -1; } //1,find key value pos_str=find_value_from_json_str(json_str, str_len, str_key, len_key); if(pos_str<0) { PRINT("find value failed\n"); str_value=NULL; return -1; } //2,find the value start " pos=find_str_in_buf(json_str+pos_str, str_len-pos_str, "\"", 1); //PRINT("%s: find the value start \" pos_str=%d, pos=%d\n",__func__,pos_str, pos); //PRINT("str is :%s\n",json_str+pos_str); if(pos>=0) { pos_str+=pos; pos_start=pos_str+1; } else { PRINT("find the value start \" failed\n"); str_value=NULL; return -1; } //3,find the value end " pos_str+=pos+1; pos=find_str_in_buf(json_str+pos_str, str_len-pos_str, "\"", 1); //PRINT("%s: find the value end \" pos_str=%d, pos=%d\n",__func__,pos_str, pos); //PRINT("str is :%s\n",json_str+pos_str); if(pos>=0) { pos_str+=pos; } else { PRINT("find the value end \" failed\n"); str_value=NULL; return -1; } if(pos_str-pos_start>=size_value) { *plen_value=size_value-1; } else { *plen_value=pos_str-pos_start; } //PRINT("to memcpy str_value: pos_str-pos_start=%d, *plen_value=%d\n",pos_str-pos_start,*plen_value); memcpy(str_value, json_str+pos_start, *plen_value); str_value[*plen_value]=0; //PRINT("after memcpy str_value, to return: %d\n", pos_str+1); return (pos_str+1); } int main(int argc, char **argv) { int ret=-1, i; char str1[4096], *str; int len_find=0; int pos; int len_value; FILE *fp; int flen1 = 0; char file_path[100]; people workers[10]; //parse json string sprintf(file_path,"json_str3.txt");// json_str1.txt, json_str2.txt, json_str3.txt, json_str4.txt fp = fopen(file_path, "r"); if (NULL == fp) { printf("open file %s fail : %m.\n", file_path); return ret; } fseek(fp,0,SEEK_END); /* 定位到文件末尾 */ flen1=ftell(fp); /* 得到文件大小 */ fseek(fp,0,SEEK_SET); /* 定位到文件开头 */ ret=fread(str1,flen1,1,fp); /* 一次性读取全部文件内容 */ /**/ fclose(fp); if(ret<=0) { PRINT("read file %s failed\n",file_path); return -1; } printf("after read file %s .\n", file_path); ret=judge_json_format(str1, flen1);//whether meet the Json standards if(ret<0) { PRINT("the json str is invalid\n"); return -1; } else { PRINT("the json str is valid\n\n"); } pos=0; str=str1; len_find=flen1; pos=find_value_from_json_str(str, len_find, "people", strlen("people")); if(pos<0) { PRINT("find people failed \n"); return -1; } else { PRINT("find people successful .\n" ); } str=str+pos; len_find-=pos; pos=find_str_in_buf(str, len_find, "[", strlen("[")); // if(pos<0) { PRINT("find [ failed \n"); return -1; } else { PRINT("find [ successful .\n" ); } str=str+pos; len_find-=(pos+1); for(i=0;i<3;i++) { //需要定义一个边界,避免搜索到下一个结构体中的内容(当前结构体中缺少某成员的赋值)。每个结构体都是{}为边界。 int pos_struct_start=0; int pos_struct_end=0; int len_find_struct=0; pos=find_str_in_buf(str, len_find, "{", strlen("{")); // if(pos<0) { PRINT("find { failed \n"); return -1; } else { PRINT("find { successful .\n" ); } str=str+pos+1; len_find-=(pos+1); pos_struct_start=0;//the pos in str now. pos=find_str_in_buf(str, len_find, "}", strlen("}")); // if(pos<0) { PRINT("find } failed \n"); return -1; } else { PRINT("find } successful .\n" ); } //str=str+pos;//just find the pos,not change str pos pos_struct_end=pos-1; len_find-=(pos+1); len_find_struct = pos_struct_end - pos_struct_start + 1; workers[i].id=i; len_value=sizeof(workers[i].firstName); pos=get_str_from_json_str(str, len_find_struct, "firstName", strlen("firstName"), workers[i].firstName, &len_value); if(pos<0) { PRINT("read firstName failed , i=%d\n",i); } else { PRINT("id=%d, firstName is %s .\n", i,workers[i].firstName); } len_value=sizeof(workers[i].lastName); pos=get_str_from_json_str(str, len_find_struct, "lastName", strlen("lastName"), workers[i].lastName, &len_value);//str1+strlen("firstName") if(pos<0) { PRINT("read lastName failed , i=%d\n",i); } else { PRINT("id=%d, lastName is %s .\n", i,workers[i].lastName); } len_value=sizeof(workers[i].email); pos=get_str_from_json_str(str, len_find_struct, "email", strlen("email"), workers[i].email, &len_value);//str1+strlen("lastName") if(pos<0) { PRINT("read email failed , i=%d\n",i); } else { PRINT("id=%d, email is %s .\n", i,workers[i].email); } pos=get_int_from_json_str(str, len_find_struct, "age", strlen("age"), &(workers[i].age)); if(pos<0) { PRINT("read age failed , i=%d\n",i); } else { PRINT("id=%d, age is %d .\n", i,workers[i].age); } pos=get_float_from_json_str(str, len_find_struct, "height", strlen("height"), &(workers[i].height)); if(pos<0) { PRINT("read height failed , i=%d\n",i); } else { PRINT("id=%d, height is %.02f .\n", i,workers[i].height); } PRINT("id=%d, len_find_struct=%d .\n", i,len_find_struct); str=str+len_find_struct+1;// } return 0; }