Gson全解析之一:JsonReader的beginObject()
Posted 王梵
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Gson全解析之一:JsonReader的beginObject()相关的知识,希望对你有一定的参考价值。
如果按重要性,对Gson中的类排个序,JsonReader绝对排在第一,它是底层真正解析json格式数据的支持类,非常值得一看。
我们来看一个简单的解析。
String json = "\\"name\\":\\"王成wisely\\",\\"age\\":\\"24\\"";
private void readJson2User()
User user = new User();
StringReader reader = new StringReader(json);
JsonReader jsonReader = new JsonReader(reader);
try
jsonReader.beginObject();
while (jsonReader.hasNext())
String name = jsonReader.nextName();
switch (name)
case "name":
user.name = jsonReader.nextString();
break;
case "age":
user.age = jsonReader.nextInt();
break;
jsonReader.endObject();
catch (IOException e)
e.printStackTrace();
一、构造方法
我们来看JsonReader的构造方法,如下:
private int[] stack = new int[32];
private int stackSize = 0;
stack[stackSize++] = JsonScope.EMPTY_DOCUMENT;
public JsonReader(Reader in)
if (in == null)
throw new NullPointerException("in == null");
this.in = in;
JsonReader的构造方法,需要一个Reader的实例,它的作用是用来读取json格式的数据流。上面的代码中,除了构造方法外,还有一个静态代码块,初始化了一个名为stack的数组的第0项,后面会用到。
二、beginObject()
int peeked = PEEKED_NONE;
public void beginObject() throws IOException
int p = peeked;
if (p == PEEKED_NONE)
p = doPeek();
if (p == PEEKED_BEGIN_OBJECT)
push(JsonScope.EMPTY_OBJECT);
peeked = PEEKED_NONE;
else
throw new IllegalStateException("Expected BEGIN_OBJECT but was " + peek() + locationString());
peeked是个很重要的变量,默认值为PEEKED_NONE。在上面的方法中,调用了一个方法doPeek(),这个类可以说是JsonReader类的核心方法之一。
三、doPeek()
int doPeek() throws IOException
int peekStack = stack[stackSize - 1];
if (peekStack == JsonScope.EMPTY_ARRAY)
......
else if (peekStack == JsonScope.NONEMPTY_ARRAY)
......
else if (peekStack == JsonScope.EMPTY_OBJECT || peekStack == JsonScope.NONEMPTY_OBJECT)
......
else if (peekStack == JsonScope.DANGLING_NAME)
......
else if (peekStack == JsonScope.EMPTY_DOCUMENT)
if (lenient)
consumeNonExecutePrefix();
stack[stackSize - 1] = JsonScope.NONEMPTY_DOCUMENT;
else if (peekStack == JsonScope.NONEMPTY_DOCUMENT)
......
else if (peekStack == JsonScope.CLOSED)
throw new IllegalStateException("JsonReader is closed");
int c = nextNonWhitespace(true);
switch (c)
case ']':
if (peekStack == JsonScope.EMPTY_ARRAY)
return peeked = PEEKED_END_ARRAY;
// fall-through to handle ",]"
case ';':
case ',':
// In lenient mode, a 0-length literal in an array means 'null'.
if (peekStack == JsonScope.EMPTY_ARRAY || peekStack == JsonScope.NONEMPTY_ARRAY)
checkLenient();
pos--;
return peeked = PEEKED_NULL;
else
throw syntaxError("Unexpected value");
case '\\'':
checkLenient();
return peeked = PEEKED_SINGLE_QUOTED;
case '"':
return peeked = PEEKED_DOUBLE_QUOTED;
case '[':
return peeked = PEEKED_BEGIN_ARRAY;
case '':
return peeked = PEEKED_BEGIN_OBJECT;
default:
pos--; // Don't consume the first character in a literal value.
int result = peekKeyword();
if (result != PEEKED_NONE)
return result;
result = peekNumber();
if (result != PEEKED_NONE)
return result;
if (!isLiteral(buffer[pos]))
throw syntaxError("Expected value");
checkLenient();
return peeked = PEEKED_UNQUOTED;
第2行,初始化变量peekStack,stackSize = 1,peekStack=stack[0],也就是JsonScope.EMPTY_DOCUMENT,在静态代码块中进行的初始化 。
第15行,重新为stack[0]赋值,stack[0]=JsonScope.NONEMPTY_DOCUMENT。
下面是stack数组的值的变迁
stack数组 | value |
---|---|
stack[0] |
第22行,调用了nextNonWhitespace()方法,如下:
四、nextNonWhitespace()
private final char[] buffer = new char[1024];
private int pos = 0;
private int limit = 0;
private int nextNonWhitespace(boolean throwOnEof) throws IOException
char[] buffer = this.buffer;
int p = pos;
int l = limit;
while (true)
if (p == l)
pos = p;
if (!fillBuffer(1))
break;
p = pos;
l = limit;
int c = buffer[p++];
if (c == '\\n')
lineNumber++;
lineStart = p;
continue;
else if (c == ' ' || c == '\\r' || c == '\\t')
continue;
if (c == '/')
...
else if (c == '#')
...
else
pos = p;
return c;
if (throwOnEof)
throw new EOFException("End of input"
+ " at line " + getLineNumber() + " column " + getColumnNumber());
else
return -1;
第5~7行,buffer是个空数组,p=pos=0,l=limit=0,代码运行到第11行,调用了一个方法fillBuffer()
五、fillBuffer()
private int lineNumber = 0;
private int lineStart = 0;
private boolean fillBuffer(int minimum) throws IOException
char[] buffer = this.buffer;
lineStart -= pos;
if (limit != pos)
limit -= pos;
System.arraycopy(buffer, pos, buffer, 0, limit);
else
limit = 0;
pos = 0;
int total;
while ((total = in.read(buffer, limit, buffer.length - limit)) != -1)
limit += total;
// if this is the first read, consume an optional byte order mark (BOM) if it exists
if (lineNumber == 0 && lineStart == 0 && limit > 0 && buffer[0] == '\\ufeff')
pos++;
lineStart++;
minimum++;
if (limit >= minimum)
return true;
return false;
从方法名就可以看出,这个方法的主要作用是填充空的buffer数组。第5行,lineStart = 0。第6行的if判断,limit=pos=0,程序运行到第10行,limit=0。
第15行,将字符数组读入buffer数组中,total=30。第15行,limit=30。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
“ | n | a | m | e | “ | : | “ | 王 | 成 | w | i | s | e | |
15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
l | y | “ | , | “ | a | g | e | “ | : | “ | 2 | 4 | “ |
第19~23行,如下:
// if this is the first read, consume an optional byte order mark (BOM) if it exists
if (lineNumber == 0 && lineStart == 0 && limit > 0 && buffer[0] == '\\ufeff')
pos++;
lineStart++;
minimum++;
if条件中,前3个条件都满足,第4个条件判断buffer[0]是不是等于“\\ufeff”,其实就是判断字符串是不是UTF-8编码,并且有BOM。关于BOM的内容,在最下面。
不满足第4个条件,程序运行到第25行,limit=30>minimum=1。
fillBuffer()方法运行完,再回到nextNonWhitespace(),第14,15行,p=pos=0,l=limit=30。
第18行,这是一句重要代码。
int c = buffer[p++];
首先,将buffer[0]赋值给c,也就是,int c = buffer[0] = 123,左大括号在ANSCII中的值就是123。
然后,将p的值加1,现在p=1。
程序运行到第32行,pos=p=1,将c作为返回值返回。程序再次回到doPeek()方法中。
在doPeek()方法的第22行,
int c = nextNonWhitespace(true);
得到c=123,也就是左大括号。程序继续运行到第47行,为peeked赋值
peeked = PEEKED_BEGIN_OBJECT
peeked的值 |
---|
PEEKED_BEGIN_OBJECT |
代码再次回到beginObject(),程序运行到第7句,p == PEEKED_BEGIN_OBJECT,进入if语句,运行如下代码
push(JsonScope.EMPTY_OBJECT);
peeked = PEEKED_NONE;
先运行push方法
private void push(int newTop)
if (stackSize == stack.length)
int[] newStack = new int[stackSize * 2];
System.arraycopy(stack, 0, newStack, 0, stackSize);
stack = newStack;
stack[stackSize++] = newTop;
stackSize=1,stack.length=32,程序运行到最后一句,stack[1]=JsonScope.EMPTY_OBJECT,之后stackSize=2。
stack数组 | value |
---|---|
stack[0] | |
stack[1] | JsonScope.EMPTY_OBJECT |
再次回到beginObject()方法,peeked = PEEKED_NONE;
peeked的值 |
---|
PEEKED_NONE |
至此,beginObject()结束。已经读取的数组如下
0 |
---|
pos=1。
附录1:BOM
使用UTF-8编码的字符串,默认在前面添加BOM(byte order mark),比如字符串aaa,如果它的ASCII编码为
61 61 61
而如果使用UTF-8编码,那么就变成了
EF BB BF 61 61 61
在java中,EF BB BF被当成‘\\ufeff’处理,在其它语言或编码中则各有不同,参见这里和这里。
BOM原意是Unicode编码中标记字节顺序的方法,现在大都用来查看是编码形式,比如前面如果是EF BB BF,那么就是UTF-8编码的。
以上是关于Gson全解析之一:JsonReader的beginObject()的主要内容,如果未能解决你的问题,请参考以下文章
Gson使用google的JsonReader读取Json文件并转化成对象