JSON
定义
JSON( JavaScript Object Notation)是一种轻量级的数据交换格式
作用
数据标记,存储,传输
特点
1.读写速度快
2.解析简单
3.轻量级
4.独立于语言,平台
5.具有自我描叙性
JSON解析
语法
JSON建构于两种结构:
“名称/值”对的集合(A collection of name/value pairs)。不同的语言中,它被理解为对象
(object),纪录(record),结构(struct),字典(dictionary),哈希表(hash table),有键列表(keyed list),或者关联数组 (associative array)。
值的有序列表(An ordered list of values)。在大部分语言中,它被理解为数组(array)。
这些都是常见的数据结构。事实上大部分现代计算机语言都以某种形式支持它们。这使得一种数据格式在同样基于这些结构的编程语言之间交换成为可能。
JSON具有以下这些形式:
对象是一个无序的“‘名称/值’对”集合。一个对象以“{”(左括号)开始,“}”(右括号)结束。每个“名称”后跟一个“:”(冒号);“‘名称/值’对”之间使用“,”(逗号)分隔。
数组是值(value)的有序集合。一个数组以“[”(左中括号)开始,“]”(右中括号)结束。值之间使用
“,”(逗号)分隔。
值(value)可以是双引号括起来的字符串(string)、数值(number)、true、false、null、对象
(object)或者数组(array)。这些结构可以嵌套。
字符串(string)是由双引号包围的任意数量Unicode字符的集合,使用反斜线转义。一个字符
(character)即一个单独的字符串(character string)。
字符串(string)与C或者Java的字符串非常相似。
数值(number)也与C或者Java的数值非常相似。除去未曾使用的八进制与十六进制格式。除去一些编码细节。
JSON解析方式
Android Studio自带org.json解析
解析原理:基于文档驱动,需要把全部文件读入到内存中,然后遍历所有数据,根据需要检索想要的数据
具体使用
1//生成JSON
2privatevoidcreateJson(Context context)throwsException{
3Filefile=newFile(getFilesDir(),"orgjson.json");//获取到应用在内部的私有文件夹下对应的orgjson.json文件
4JSONObjectstudent=newJSONObject();//实例化一个JSONObject对象
5student.put("name","OrgJson");//对其添加一个数据
6student.put("sax","男");
7student.put("age",23);
8JSONObject course1=newJSONObject();
9course1.put("name","语文");
10course1.put("score",98.2f);
11JSONObject course2=newJSONObject();
12course2.put("name","数学");
13course2.put("score",93.2f);
14JSONArraycoures=newJSONArray();//实例化一个JSON数组
15coures.put(0,course1);//将course1添加到JSONArray,下标为0
16coures.put(1,course2);
17//然后将JSONArray添加到名为student的JSONObject
18student.put("courses",coures);
19FileOutputStream fos=newFileOutputStream(file);
20fos.write(student.toString().getBytes());
21fos.close();
22Log.i(TAG,"createJson: "+student.toString());
23Toast.makeText(context,"创建成功",Toast.LENGTH_LONG).show();24}
25
%1//解析JSON
%1privatevoidparseJson(Context context)throwsException{
%1File file=newFile(getFilesDir(),"orgjson.json");
%1FileInputStream fis=newFileInputStream(file);
%1InputStreamReader isr=newInputStreamReader(fis);
%1BufferedReader br=newBufferedReader(isr);
%1Stringline;
%1StringBuffersb=newStringBuffer();34
%1while(null!=(line=br.readLine())){
%1sb.append(line);37}
%1fis.close();
%1isr.close();
%1br.close();41
%1Student student=newStudent();
%1//利用JSONObject进行解析
%1JSONObject stuJsonObject=newJSONObject(sb.toString());
%1//为什么不用getString?
%1//optString会在得不到你想要的值时候返回空字符串"",而getString会抛出异常
%1Stringname=stuJsonObject.optString("name","");
%1student.setName(name);
%1student.setSax(stuJsonObject.optString("sax","男"));
%1student.setAge(stuJsonObject.optInt("age",18));51
%1//获取数组数据
%1JSONArraycouresJson=stuJsonObject.optJSONArray("courses");54
%1for(inti=0;i(couresJson.length();i++){
%1JSONObject courseJsonObject=couresJson.getJSONObject(i);
%1Course course=newCourse();
%1course.setName(courseJsonObject.optString("name",""));
%1course.setScore((float)courseJsonObject.optDouble("score",0));
%1student.addCourse(course);61}
62
%1Log.i(TAG,"parseJson: "+student);
%1Toast.makeText(context,"解析成功",Toast.LENGTH_LONG).show();65}
Gson解析
解析原理:基于事件驱动
解析流程:根据所需取的数据 建立1个对应于JSON数据的JavaBean类,即可通过简单操作解析出所需数据
Gson不要求JavaBean类里面的属性一定全部和JSON数据里的所有key相同,可以按需取数据具体实现
1.创建一个与JSON数据对应的JavaBean类(用作存储需要解析的数据)
JSON的大括号对应一个对象对象里面有key,value
JavaBean的类属性名= key
JSON的方括号对应一个数组
JavaBean里面对应的也是数组
对象里 可以有值/对象
若对象里面只有值,没有key,则说明是纯数组,对应JavaBean里的数组类型若对象里面有值和key,则说明是对象数组,对应JavaBean里的内部类
对象嵌套
建立内部类 该内部类对象的名字=父对象的key ,类似对象数组
转化成JavaBean
31
32 |
publicList(List(ArraysBean>>getArrays() { |
33 |
returnarrays; |
34 |
} |
35 |
|
36 |
publicvoidsetArrays(List(List(ArraysBean>>arrays) { |
37 |
this.arrays=arrays; |
38 |
} |
39 |
|
40 |
public static classInnerclassBean{ |
41 |
|
42 |
privateStringname; |
43 |
privateintage; |
44 |
privateStringsax; |
45 |
|
46 |
publicStringgetName() { |
47 |
returnname; |
48 |
} |
49 |
|
50 |
publicvoidsetName(Stringname) { |
51 |
this.name=name; |
52 |
} |
53 |
|
54 |
publicintgetAge() { |
55 |
returnage; |
56 |
} |
57 |
|
58 |
publicvoidsetAge(intage) { |
59 |
this.age=age; |
60 |
} |
61 |
|
62 |
publicStringgetSax() { |
63 |
returnsax; |
64 |
} |
65 |
|
66 |
publicvoidsetSax(Stringsax) { |
67 |
this.sax=sax; |
68 |
} |
69 |
} |
70 |
|
71 |
public static classArraysBean{ |
72 |
|
73 |
privateStringarrInnerClsKey; |
74 |
privateintarrInnerClsKeyNub; |
75 |
|
76 |
publicStringgetArrInnerClsKey() { |
77 |
returnarrInnerClsKey; |
78 |
} |
79 |
|
80 |
publicvoidsetArrInnerClsKey(StringarrInnerClsKey) { |
81 |
this.arrInnerClsKey=arrInnerClsKey; |
82 |
} |
83 |
|
84 |
publicintgetArrInnerClsKeyNub() { |
85 |
returnarrInnerClsKeyNub; |
86 |
} |
87 |
|
88 |
publicvoidsetArrInnerClsKeyNub(intarrInnerClsKeyNub) { |
使用例子
Jackson解析
解析原理:基于事件驱动解析过程:
1.类似GSON,先创建1个对应于JSON数据的JavaBean类,再通过简单操作即可解析
2.与Gson解析不同的是:GSON可按需解析,即创建的JavaBean类不一定完全涵盖所要解析的JSON数据,按需创建属性;但Jackson解析对应的JavaBean必须把Json数据里面的所有key都有所对应,即必须把JSON内的数据所有解析出来,无法按需解析
导入Jackson依赖
使用Jackson解析
%2student.setAge(28);
%2student.addCourse(newCourse("英语",78.3f));
%2student.addCourse(newCourse("语文",88.9f));
%2student.addCourse(newCourse("数学",48.2f));10
%1ObjectMapper objectMapper=newObjectMapper();
%1//jackson序列化
%1File file=newFile(CurPath+"/jacksontest.json");
%1FileOutputStream fileOutputStream=newFileOutputStream(file);
%1objectMapper.writeValue(fileOutputStream,student);16
17
%1//反序列化
%1Student student1=objectMapper.readValue(file,Student.class);
%1System.out.println(student1);21
%1//HashMap(String, Student> studentHashMap = newHashMap(>();
%1//Studentstu1=newStudent("King","男",32);
%1//stu1.addCourse(newCourse("物理",68.9f));
%1//Studentstu2=newStudent("Mark","男",33);
%1//studentHashMap.put("key1",stu1);
%1//studentHashMap.put("key2",stu2);
%1//System.out.println("studentHashMap:\n" +studentHashMap);
%1//JacksonUtil.encode2File(studentHashMap,CurPath+"/jacksontest1.json");
%1//String jsonStr =JacksonUtil.encode(studentHashMap);
%1//System.out.println(jsonStr);32
%1//反序列化TypeReference用法
%1//HashMap(String,Student> studentHashMap1 =objectMapper.readValue(jsonStr,HashMap.class);//错误做法
%1//正确的方式
%1//HashMap(String,Student> studentHashMap1 = objectMapper.readValue(jsonStr,newTypeReference(HashMap(String,Student>>()
{});
37 |
// |
System.out.println("studentHashMap1:\n" +studentHashMap1); |
38 |
||
39 |
||
40 |
//List |
|
41 |
// |
List(Student> studentList = new ArrayList(>(); |
42 |
// |
studentList.add(stu1); |
43 |
// |
studentList.add(stu2); |
44 |
// |
JacksonUtil.encode2File(studentList, CurPath + |
"/jacksontest2.json");
45//StringjsonStr2=JacksonUtil.encode(studentList);46//
%1//List(Student>studentList1=objectMapper.readValue(jsonStr2,objectMapper.getTypeFactory().constructParametricType(ArrayList.class,Student.class));
%1//System.out.println("studentList1:\n"+studentList1);49
50}
Fastjson解析
导入Fastjson依赖
使用Fastjson解析
自定义一个JSON解析库
编写一个JSON解析器实际上就是一个方法,它的输入是一个表示JSON的字符串,输出是结构化的对应到语言本身的数据结构
一般来说,解析过程包括词法分析和语法分析两个阶段。词法分析阶段的目标是按照构词规则将JSON
字符串解析成Token流,比如有如下的JSON字符串:
结果词法分析后,得到一组Token,如下:
{key : value, }
词法分析解析出Token序列后,接下来要进行语法分析。语法分析的目的是根据JSON文法检查上面Token序列所构成的JSON结构是否合法。比如JSON文法要求非空JSON对象以键值对的形式出现,形如object = {string : value}。如果传入了一个格式错误的字符串,比如
那么在语法分析阶段,语法分析器分析完Tokenname后,认为它是一个符合规则的Token,并且认为它是一个键。接下来,语法分析器读取下一个Token,期望这个Token是:。但当它读取了这个
Token,发现这个Token是,,并非其期望的:,于是文法分析器就会报错误。
JSON解析分析小结
1.通过词法分析是将字符串解析成一组Token序列
2.然后通过语法分析检查输入的Token序列所构成的JSON格式是否合法
词法分析
按照“构词规则”将JSON字符串解析成Token流。请注意双引号引起来词–构词规则,所谓构词规则是指词法分析模块在将字符串解析成Token时所参考的规则。在JSON中,构词规则对应于几种数据类型,当词法解析器读入某个词,且这个词类型符合JSON所规定的数据类型时,词法分析器认为这个词符合构词规则,就会生成相应的Token。这里我们可以参考http://www.json.org/ 对JSON的定义,罗列一下JSON所规定的数据类型:
BEGIN_OBJECT({)END_OBJECT(})BEGIN_ARRAY([)END_ARRAY(])
NULL(null)NUMBER(数字)STRING(字符串)
BOOLEAN(true/false)SEP_COLON(:)
SEP_COMMA(,)
当词法分析器读取的词是上面类型中的一种时,即可将其解析成一个Token。我们可以定义一个枚举类来表示上面的数据类型,如下:
在解析过程中,仅有TokenType类型还不行。我们除了要将某个词的类型保存起来,还需要保存这个词的字面量。所以,所以这里还需要定义一个Token类。用于封装词类型和字面量,如下:
定义好了Token类,接下来再来定义一个读取字符串的类
有了TokenType、Token和CharReader这三个辅助类,接下来我们就可以实现词法解析器了
|
|
1 2 3 4 5 |
public classTokenizer{ privateCharReader charReader;privateTokenList tokens; publicTokenList tokenize(CharReader charReader)throwsIOException{ |
6
this.charReader=charReader; |
|
7 |
tokens=newTokenList(); |
8 |
tokenize(); |
9 |
|
10 |
returntokens; |
11 |
} |
12 |
|
13 |
privatevoidtokenize()throwsIOException{ |
14 |
//使用do-while处理空文件 |
15 |
Token token; |
16 |
do{ |
17 |
token=start(); |
18 |
tokens.add(token); |
19 |
}while(token.getTokenType()!=TokenType.END_DOCUMENT); |
20 |
} |
21 |
|
22 |
privateToken start()throwsIOException{ |
23 |
charch; |
24 |
for(;;) { |
25 |
if(!charReader.hasMore()) { |
26 |
return newToken(TokenType.END_DOCUMENT,null); |
27 |
} |
28 |
|
29 |
ch=charReader.next(); |
30 |
if(!isWhiteSpace(ch)) { |
31 |
break; |
32 |
} |
33 |
} |
34 |
|
35 |
switch(ch) { |
36 |
case'{': |
37 |
return newToken(TokenType.BEGIN_OBJECT, |
|
String.valueOf(ch)); |
38 |
case'}': |
39 |
return newToken(TokenType.END_OBJECT,String.valueOf(ch)); |
40 |
case'[': |
41 |
return newToken(TokenType.BEGIN_ARRAY,String.valueOf(ch)); |
42 |
case']': |
43 |
return newToken(TokenType.END_ARRAY,String.valueOf(ch)); |
44 |
case',': |
45 |
return newToken(TokenType.SEP_COMMA,String.valueOf(ch)); |
46 |
case':': |
47 |
return newToken(TokenType.SEP_COLON,String.valueOf(ch)); |
48 |
case'n': |
49 |
returnreadNull(); |
50 |
case't': |
51 |
case'f': |
52 |
returnreadBoolean(); |
53 |
case'"': |
54 |
returnreadString(); |
55 |
case'-': |
56 |
returnreadNumber(); |
57 |
} |
58 |
|
59 |
if(isDigit(ch)) { |
60 |
returnreadNumber(); |
61 |
} |
62 |
上面的代码是词法分析器的实现,部分代码这里没有贴出来,后面具体分析的时候再贴。先来看看词法分析器的核心方法start,这个方法代码量不多,并不复杂。其通过一个死循环不停的读取字符,然后再根据字符的类型,执行不同的解析逻辑。上面说过,JSON的解析过程比较简单。原因在于,在解析时,只需通过每个词第一个字符即可判断出这个词的TokenType。比如:
第一个字符是{、}、[、]、,、:,直接封装成相应的Token返回即可第一个字符是n,期望这个词是null,Token类型是NULL
第一个字符是t或f,期望这个词是true或者false,Token类型是BOOLEAN
第一个字符是”,期望这个词是字符串,Token类型为String第一个字符是0~9或-,期望这个词是数字,类型为NUMBER
正如上面所说,词法分析器只需要根据每个词的第一个字符,即可知道接下来它所期望读取的到的内容是什么样的。如果满足期望了,则返回Token,否则返回错误。下面就来看看词法解析器在碰到第一个字符是n和”时的处理过程。先看碰到字符n的处理过程:
上面的代码很简单,词法分析器在读取字符n后,期望后面的三个字符分别是u,l,l,与n组成词null。如果满足期望,则返回类型为NULL的Token,否则报异常。readNull方法逻辑很简单,不多说了。接下来看看string类型的数据处理过程:
string类型的数据解析起来要稍微复杂一些,主要是需要处理一些特殊类型的字符。JSON所允许的特殊类型的字符如下:
最后一种特殊字符\/代码中未做处理,其他字符均做了判断,判断逻辑在isEscape方法中。在传入JSON字符串中,仅允许字符串包含上面所列的转义字符。如果乱传转义字符,解析时会报错。对于STRING类型的词,解析过程始于字符”,也终于”。所以在解析的过程中,当再次遇到字符”,
readString方法会认为本次的字符串解析过程结束,并返回相应类型的Token。
上面说了null类型和string类型的数据解析过程,过程并不复杂,理解起来应该不难。至于boolean
和number类型的数据解析过程,大家有兴趣的话可以自己看源码,这里就不在说了。
语法分析
当词法分析结束后,且分析过程中没有抛出错误,那么接下来就可以进行语法分析了。语法分析过程以词法分析阶段解析出的Token序列作为输入,输出JSONObject或JSONArray。语法分析器的实现的文法如下:
当词法分析结束后,且分析过程中没有抛出错误,那么接下来就可以进行语法分析了。语法分析过程以
词法分析阶段解析出的Token序列作为输入,输出JSON Object或JSON Array。语法分析器的实现的文法如下:
1 |
object={} |
|
2 |
|{members} |
|
3 |
||
4 |
members=pair |
|
5 |
|pair,members |
|
6 |
||
7 |
pair=string:value |
|
8 |
||
9 |
array=[] |
|
10 |
|[elements] |
|
11 |
||
12 |
elements=value |
|
13 |
|value,elements |
|
14 |
||
15 |
value=string |
|
16 |
|number |
|
17 |
|object |
|
18 |
|array |
|
19 |
|true |
|
20 |
|false |
|
21 |
|null |
|
22 |
||
23 |
string="" |
|
24 |
|" chars " |
|
25 |
||
26 |
chars=char |
|
27 |
|charchars |
|
28 |
||
29 |
char=any-Unicode-character-except-"-or-\-or- |
control-character |
30 |
|\" |
|
31 |
|\\ |
|
32 |
|\/ |
|
33 |
|\b |
|
34 |
|\f |
|
35 |
|\n |
|
36 |
|\r |
|
37 |
|\t |
|
38 |
|\u four-hex-digits |
|
39 |
||
40 |
number=int |
|
41 |
|intfrac |
|
42 |
|intexp |
|
43 |
|intfrac exp |
|
44 |
||
45 |
int=digit |
|
46 |
|digit1-9digits |
|
47 |
| -digit |
|
48 |
| -digit1-9digits |
|
49 |
||
50 |
frac=.digits |
|
51 |
||
52 |
exp=e digits |
|
53 |
||
54 |
digits=digit |
语法分析器的实现需要借助两个辅助类,也就是语法分析器的输出类,分别是JsonObject和
JsonArray。代码如下:
1public classJsonObject{2
3privateMap(String,Object>map=newHashMap(String,Object>();4
%1publicvoidput(Stringkey,Objectvalue){
%1map.put(key,value);7}
8
9publicObjectget(Stringkey){
10returnmap.get(key);11}
12
%1publicList(Map.Entry(String,Object>>getAllKeyValue(){
%1return newArrayList(>(map.entrySet());15}
16
%1publicJsonObject getJsonObject(Stringkey){
%1if(!map.containsKey(key)){
%1throw newIllegalArgumentException("Invalidkey");20}
21
%1Objectobj=map.get(key);
%1if(!(objinstanceofJsonObject)){
%1thrownewJsonTypeException("TypeofvalueisnotJsonObject");25}
26
27return(JsonObject)obj;28}
29
%1publicJsonArray getJsonArray(Stringkey){
%1if(!map.containsKey(key)){
%1throw newIllegalArgumentException("Invalidkey");33}
34
%1Objectobj=map.get(key);
%1if(!(objinstanceofJsonArray)){
%1thrownewJsonTypeException("TypeofvalueisnotJsonArray");38}
39
40return(JsonArray)obj;41}
42
%1@Override
%1publicStringtoString(){
%1returnBeautifyJsonUtils.beautify(this);
46}
47}
48
49public classJsonArrayimplementsIterable{50
51privateList list=newArrayList();52
%1publicvoidadd(Objectobj){
%1list.add(obj);55}
56
%1publicObjectget(intindex){
%1returnlist.get(index);59}
60
%1publicintsize(){
%1returnlist.size();63}
64
%1publicJsonObject getJsonObject(intindex){
%1Objectobj=list.get(index);
%1if(!(objinstanceofJsonObject)){
%1thrownewJsonTypeException("TypeofvalueisnotJsonObject");69}
70
71return(JsonObject)obj;72}
73
%1publicJsonArray getJsonArray(intindex){
%1Objectobj=list.get(index);
%1if(!(objinstanceofJsonArray)){
%1thrownewJsonTypeException("TypeofvalueisnotJsonArray");78}
79
80return(JsonArray)obj;81}
82
%1@Override
%1publicStringtoString(){
%1returnBeautifyJsonUtils.beautify(this);86}
87
%1publicIterator iterator(){
%1returnlist.iterator();90}
91}
语法解析器的核心逻辑封装在了parseJsonObject和parseJsonArray两个方法中,接下来我会详细分析parseJsonObject方法,parseJsonArray方法大家自己分析吧。parseJsonObject方法实现如下:
8
TokenType tokenType=token.getTokenType(); |
|||
9 |
StringtokenValue=token.getValue(); |
||
10 |
switch(tokenType) { |
||
11 |
caseBEGIN_OBJECT: |
||
12 |
checkExpectToken(tokenType,expectToken); |
||
13 |
jsonObject.put(key,parseJsonObject());//递归解析json |
||
|
object |
|
|
14 |
expectToken=SEP_COMMA_TOKEN|END_OBJECT_TOKEN; |
||
15 |
break; |
||
16 |
caseEND_OBJECT: |
||
17 |
checkExpectToken(tokenType,expectToken); |
||
18 |
returnjsonObject; |
||
19 |
caseBEGIN_ARRAY://解析jsonarray |
||
20 |
checkExpectToken(tokenType,expectToken); |
||
21 |
jsonObject.put(key,parseJsonArray()); |
||
22 |
expectToken=SEP_COMMA_TOKEN|END_OBJECT_TOKEN; |
||
23 |
break; |
||
24 |
caseNULL: |
||
25 |
checkExpectToken(tokenType,expectToken); |
||
26 |
jsonObject.put(key,null); |
||
27 |
expectToken=SEP_COMMA_TOKEN|END_OBJECT_TOKEN; |
||
28 |
break; |
||
29 |
caseNUMBER: |
||
30 |
checkExpectToken(tokenType,expectToken); |
||
31 |
if(tokenValue.contains(".")||tokenValue.contains("e")|| |
||
|
tokenValue.contains("E")) { |
|
|
32 |
jsonObject.put(key,Double.valueOf(tokenValue)); |
||
33 |
}else{ |
||
34 |
Longnum=Long.valueOf(tokenValue); |
||
35 |
if(num>Integer.MAX_VALUE||num(Integer.MIN_VALUE) |
{ |
|
36 |
jsonObject.put(key,num); |
||
37 |
}else{ |
||
38 |
jsonObject.put(key,num.intValue()); |
||
39 |
} |
||
40 |
} |
||
41 |
expectToken=SEP_COMMA_TOKEN|END_OBJECT_TOKEN; |
||
42 |
break; |
||
43 |
caseBOOLEAN: |
||
44 |
checkExpectToken(tokenType,expectToken); |
||
45 |
jsonObject.put(key,Boolean.valueOf(token.getValue())); |
||
46 |
expectToken=SEP_COMMA_TOKEN|END_OBJECT_TOKEN; |
||
47 |
break; |
||
48 |
caseSTRING: |
||
49 |
checkExpectToken(tokenType,expectToken); |
||
50 |
Token preToken=tokens.peekPrevious(); |
||
51 |
/* |
||
52 |
*在JSON中,字符串既可以作为键,也可作为值。 |
||
53 |
*作为键时,只期待下一个Token类型为SEP_COLON。 |
||
54 |
*作为值时,期待下一个Token类型为SEP_COMMA或END_OBJECT |
||
55 |
*/ |
||
56 |
if(preToken.getTokenType()==TokenType.SEP_COLON) { |
||
57 |
value=token.getValue(); |
||
58 |
jsonObject.put(key,value); |
||
59 |
expectToken=SEP_COMMA_TOKEN|END_OBJECT_TOKEN; |
||
60 |
}else{ |
||
61 |
key=token.getValue(); |
||
62 |
expectToken=SEP_COLON_TOKEN; |
||
63 |
} |
68 |
|BEGIN_OBJECT_TOKEN|BEGIN_ARRAY_TOKEN; |
69 |
break; |
70 |
caseSEP_COMMA: |
71 |
checkExpectToken(tokenType,expectToken); |
72 |
expectToken=STRING_TOKEN; |
73 |
break; |
74 |
caseEND_DOCUMENT: |
75 |
checkExpectToken(tokenType,expectToken); |
76 |
returnjsonObject; |
77 |
default: |
78 |
throw newJsonParseException("UnexpectedToken."); |
79 |
} |
80 |
} |
81 |
|
82 |
thrownewJsonParseException("Parseerror,invalidToken."); |
83 |
} |
84 |
parseJsonObject方法解析流程大致如下:
1.读取一个Token,检查这个Token是否是其所期望的类型
2.如果是,更新期望的Token类型。否则,抛出异常,并退出
3.重复步骤1和2,直至所有的Token都解析完,或出现异常
上面的步骤并不复杂,但有可能不好理解。这里举个例子说明一下,有如下的Token序列:
{、id、:、1、}
parseJsonObject解析完{Token后,接下来它将期待STRING类型的Token或者END_OBJECT类型的Token出现。于是parseJsonObject读取了一个新的Token,发现这个Token的类型是STRING类型,满足期望。于是parseJsonObject更新期望Token类型为SEL_COLON,即:。如此循环下去,直至Token序列解析结束或者抛出异常退出。
上面的解析流程虽然不是很复杂,但在具体实现的过程中,还是需要注意一些细节问题。比如:
1.在JSON中,字符串既可以作为键,也可以作为值。作为键时,语法分析器期待下一个Token类型为SEP_COLON。而作为值时,则期待下一个Token类型为SEP_COMMA或END_OBJECT。所以这里要判断该字符串是作为键还是作为值,判断方法也比较简单,即判断上一个Token的类型即可。如果上一个Token是SEP_COLON,即:,那么此处的字符串只能作为值了。否则,则只能做为键。
2.对于整数类型的Token进行解析时,简单点处理,可以直接将该整数解析成Long类型。但考虑到空间占用问题,对于[Integer.MIN_VALUE,Integer.MAX_VALUE]范围内的整数来说,解析成Integer更为合适,所以解析的过程中也需要注意一下。
参考
https://segmentfault.com/a/1190000010998941#articleHeader1
Gson原理解析
在这个序列化和反序列化的过程中,充当的了一个解析器的角色
JsonElement
该类是一个抽象类,代表着json串的某一个元素。这个元素可以是一个Json(JsonObject)、可以是一个数组(JsonArray)、可以是一个Java的基本类型(JsonPrimitive)、当然也可以为
null( JsonNull);JsonObject,JsonArray,JsonPrimitive,JsonNull都是JsonElement这个抽象类的子类。JsonElement提供了一系列的方法来判断当前的JsonElement
各个JsonElement的关系可以用如下图表示:
JsonObject对象可以看成name/values的集合,而这写values就是一个个JsonElement,他们的结构可以用如下图表示:
JsonDeserializer的工作原理
TypeAdapter的工作原理
Gson的整体解析原理
Gson的反射解析机制
Gson解析常见的错误
Expected BEGIN_ARRAY but was STRING at line 1 column 27
这种错误一般都是原来是一个字段需要是数组类型,但是事实上给的是””,导致的
-解决办法
1.让返回null即可解决问题
2.用Gson自带的解决方案
11
12 |
GsonError1 gsonError1=newGsonError1(); |
13 |
|
14 |
if(jsonAuthors.isJsonArray()) {//如果数组类型,此种情况是我们需要的 |
15 |
//关于context在文章最后有简单说明 |
16 |
GsonError1.AuthorsBean[]authors= |
|
context.deserialize(jsonAuthors,GsonError1.AuthorsBean[].class); |
17 |
gsonError1.setAuthors(Arrays.asList(authors)); |
18 |
}else{//此种情况为无效情况 |
19 |
gsonError1.setAuthors(null); |
20 |
} |
21 |
gsonError1.setName(name); |
22 |
returngsonError1; |
23 |
} |
24 |
} |
25 |
|
26 |
static classAuthorDeserializerimplementsJsonDeserializer{ |
27 |
|
28 |
@Override |
29 |
publicObjectdeserialize(JsonElement json,Type typeOfT, |
|
JsonDeserializationContext context)throwsJsonParseException{ |
30 |
finalJsonObject jsonObject=json.getAsJsonObject(); |
31 |
|
32 |
finalGsonError1.AuthorsBean author=new |
|
GsonError1.AuthorsBean(); |
33 |
author.setId(jsonObject.get("id").getAsString()); |
34 |
author.setName(jsonObject.get("name").getAsString()); |
35 |
returnauthor; |
36 |
} |
37 |
} |
38 |
|
39 |
public staticvoidmain(String...args) { |
40 |
//TODO: |
41 |
//test1(); |
42 |
//test2(); |
43 |
test3(); |
44 |
} |
45 |
|
46 |
public staticvoidtest1() { |
47 |
//TODO: |
48 |
Stringjson="{\n"+ |
49 |
"\"name\": \"java\",\n"+ |
50 |
"\"authors\": [\n"+ |
51 |
"{\n"+ |
52 |
"\"id\": \"1'\",\n"+ |
53 |
"\"name\": \"Joshua Bloch'\"\n"+ |
54 |
"},\n"+ |
55 |
"{\n"+ |
56 |
"\"id\": \"2'\",\n"+ |
57 |
"\"name\": \"Tom\"\n"+ |
58 |
"}\n"+ |
59 |
"]\n"+ |
60 |
"}"; |
61 |
Gson gson=newGson(); |
62 |
GsonError1 gsonError1=gson.fromJson(json,GsonError1.class); |
63 |
|
64 |
System.out.println(gsonError1); |
65 |