0%

golang json 源码解析

decoder

入口函数:

1
2
3
4
5
6
7
8
9
10
func Unmarshal(data []byte, v interface{}) error {
var d decodeState
err := checkValid(data, &d.scan) //使用一个状态机来判断是否是合法的json字符串
if err != nil {
return err
}

d.init(data)
return d.unmarshal(v)//真正的解析赋值步骤
}

golang 的 json decoder从Unmarshal进入,该函数创建一个decodeState结构体,该结构体用于存储解析json数据时当前的解析状态信息,例如下个需要解析的字符串的截止index,在解析中遇到的错误,以及一个存储了一个json解析的状态机。该函数首先会遍历一遍json字符串,用状态机来判断一个是否是一个合法的json字符串。如果是一个合法的字符串,则再变遍历变解析。所以一次decode操作会遍历两次json字符串。

状态机解析

首先看第一步用状态机判断是否是一个合法的json字符串是如何实现的。

状态机结构体的定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// A scanner is a JSON scanning state machine.
// Callers call scan.reset and then pass bytes in one at a time
// by calling scan.step(&scan, c) for each byte.
// The return value, referred to as an opcode, tells the
// caller about significant parsing events like beginning
// and ending literals, objects, and arrays, so that the
// caller can follow along if it wishes.
// The return value scanEnd indicates that a single top-level
// JSON value has been completed, *before* the byte that
// just got passed in. (The indication must be delayed in order
// to recognize the end of numbers: is 123 a whole value or
// the beginning of 12345e+6?).
type scanner struct {
// The step is a func to be called to execute the next transition.
// Also tried using an integer constant and a single func
// with a switch, but using the func directly was 10% faster
// on a 64-bit Mac Mini, and it's nicer to read.
step func(*scanner, byte) int

// Reached end of top-level value.
endTop bool

// Stack of what we're in the middle of - array values, object keys, object values.
parseState []int

// Error that happened, if any.
err error

// total bytes consumed, updated by decoder.Decode (and deliberately
// not set to zero by scan.reset)
bytes int64
}

scanner的定义很简单,只有5个字段。第一个字段step,它是一个函数类型,注释的解释是它用于根据状体机当前的状态,以及输入的字符来前往下一个状态(即改变状态机结构体内部的数据),并返回一个int类型的code,用来告诉当前是否有一些事件发生,例如是字符串序列的结束字符(第二个引号)、对象类型的结束字符(”}”)、字符串的的结束字符(”]”)、解析出错等等。

第二个字段endTop,用于标记是否当前是否已经结束最上层的对象解析,即整个json字符串对象的解析。第三个字段parseState,一个用数组简单实现的栈类型数据结构,每进入到数组类型的解析或者进入到嵌套对象类型的解析时,都会往这个栈上压入一个int数据(这个int数据是个简单的标志位,会用到数组类型的解析和嵌套对象类型的解析中,在后面的源码解析中可以看到)。在完成数组类型或者嵌套对象类型后,会弹出这个int数据。

第四个字段err,用于记录在当前解析遇到的错误。第五个字段bytes,用于记录已经解析的字符串长度,这个字段状态机本身是不会更新这个字段的,只会由外部来更新(这里目前我也有点不太清楚为什么)