Go的标准包encoding/json提供了解析JSON的方法,我已在另一篇博文Go操作JSON介绍了高级用法,这里介绍基于Token的低级解析方法。json.Decoder提供了Token方法,用来返回JSON串中的每一个Token。Token包括:json.Delim,基本类型(bool,float64,Number,string)和nil。其中json.Delim包括[ ] { }
。每一次调用Token()方法都会返回以上Token中的一个。下例基于Token解析JSON并将解析结果格式化打印:
package main
import (
"bytes"
"container/list"
"encoding/json"
"fmt"
"io"
"os"
)
func main() {
str := `{"took": 596,
"timed_out": false,
"_shards": {
"total": 11,
"successful": 11,
"failed": 0
}}`
// 构建Decoder
reader := bytes.NewReader([]byte(str))
dec := json.NewDecoder(reader)
const (
obj = iota
arr
)
curType := -1
const (
mapKey = iota
mapValue
arrEle
)
curEle := -1
indent := ""
const sep = " "
const newline = "\n"
stack := list.New()
setEleType := func() {
switch curType {
case obj:
curEle = mapKey
case arr:
curEle = arrEle
}
}
for {
tok, err := dec.Token() // 返回待处理的下一个Token
if err == io.EOF {
break
} else if err != nil {
os.Exit(1)
}
switch tok := tok.(type) { // 根据Token的类型,做相应处理
case json.Delim:
switch tok {
case '{':
stack.PushBack(obj)
curType = obj
setEleType()
fmt.Printf("%s%s{%s", newline, indent, newline)
indent += sep
case '}':
stack.Remove(stack.Back())
if stack.Len() > 0 {
curType = stack.Back().Value.(int)
setEleType()
}
indent = indent[:len(indent)-len(sep)]
fmt.Printf("%s%s}%s", newline, indent, newline)
case '[':
stack.PushBack(arr)
curType = arr
setEleType()
fmt.Printf("%s%s[%s", newline, indent, newline)
indent += sep
case ']':
stack.Remove(stack.Back())
if stack.Len() > 0 {
curType = stack.Back().Value.(int)
setEleType()
}
indent = indent[:len(indent)-len(sep)]
fmt.Printf("%s%s]%s", newline, indent, newline)
}
default:
switch curType {
case obj:
switch curEle {
case mapKey:
fmt.Printf("%s", indent)
fmt.Print(tok)
fmt.Printf(": ")
curEle = mapValue
case mapValue:
fmt.Print(tok)
fmt.Printf(",%s", newline)
curEle = mapKey
}
case arr:
switch curEle {
case arrEle:
fmt.Printf("%s", indent)
fmt.Print(tok)
fmt.Printf(",%s", newline)
}
}
}
}
}