相對于很多的語言來說, Go 的 JSON 解析可謂簡單至極.
問題
通常情況下, 我們在 Go 中經常這樣進行 JSON 的解碼:
~~~
package main
import "encoding/json"
// jsonText comes from http://json.org/example.html
var jsonText = []byte(`
{
"glossary":{
"title":"example glossary",
"GlossDiv":{
"title":"S",
"GlossList":{
"GlossEntry":{
"ID":"SGML",
"SortAs":"SGML",
"GlossTerm":"Standard Generalized Markup Language",
"Acronym":"SGML",
"Abbrev":"ISO 8879:1986",
"GlossDef":{
"para":"A meta-markup language, used to create markup languages such as DocBook.",
"GlossSeeAlso":[
"GML",
"XML"
]
},
"GlossSee":"markup"
}
}
}
}
}`)
type glossary struct {
Glossary struct {
Title string `json:"title"`
GlossDiv struct {
Title string `json:"title"`
GlossList struct {
GlossEntry struct {
ID string `json:"ID"`
SortAs string `json:"SortAs"`
GlossTerm string `json:"GlossTerm"`
Acronym string `json:"Acronym"`
Abbrev string `json:"Abbrev"`
GlossDef struct {
Para string `json:"para"`
GlossSeeAlso []string `json:"GlossSeeAlso"`
} `json:"GlossDef"`
GlossSee string `json:"GlossSee"`
} `json:"GlossEntry"`
} `json:"GlossList"`
} `json:"GlossDiv"`
} `json:"glossary"`
}
func main() {
var g glossary
json.Unmarshal(jsonText, &g)
}
~~~
這樣的解碼對于我們日常使用好像也沒什么問題, 起碼能用 ? 對于一段 JSON, 我們解碼的時候未必需要立即解碼所有的部分, 什么意思呢 ?
拿上面的例子代碼來說, 我們解碼 jsonText , 可能僅需要馬上使用 Title 和 GlossDiv.Title . 那么對于這種情況我們怎么做合適呢 ?
~~~
package main
import "encoding/json"
// jsonText comes from http://json.org/example.html
var jsonText = []byte(`
{
... // 此處省略, 同上
}`)
type glossarySectional struct {
Glossary struct {
Title string `json:"title"`
GlossDiv struct {
Title string `json:"title"`
GlossList json.RawMessage `json:"GlossList"` // diff: delay JSON decoding
} `json:"GlossDiv"`
} `json:"glossary"`
}
func main() {
var g glossarySectional
json.Unmarshal(jsonText, &g)
}
~~~
沒錯, 魔法就在 GlossList json.RawMessage . 我們看相關文檔怎么說:
RawMessage is a raw encoded JSON value. It implements Marshaler and Unmarshaler and can be used to delay JSON decoding or precompute a JSON encoding.
一目了然, RawMessage 起到了延遲解碼一個 JSON 值的作用. 那么你可能會說, 這有啥用呢 ?
這對于普通的解碼可能問題不大, 但是對于一些像消息傳遞(Kafka 這種), 細微的延遲可能會造成很大的影響. 我們可以通過簡單的 benchmark 測試一下這細微的差別:
~~~
// 其他代碼略 ... 完整代碼參見: http://bit.ly/2skxY9L .
func benchmarkJSONUnmarshal(f func(), b *testing.B) {
for n := 0; n < b.N; n++ {
f()
}
}
func BenchmarkJSONUnmarshal_0(b *testing.B) {
benchmarkJSONUnmarshal(func() {
var g glossary
json.Unmarshal(jsonText, &g)
}, b)
}
func BenchmarkJSONUnmarshal_1(b *testing.B) {
benchmarkJSONUnmarshal(func() {
var g glossarySectional
json.Unmarshal(jsonText, &g)
}, b)
}
~~~
我們通過運行 go test -run=NONE -bench=. ./... 可以得出(不同環境有略微差別):
~~~
BenchmarkJSONUnmarshal_0-8 200000 10565 ns/op
BenchmarkJSONUnmarshal_1-8 200000 7699 ns/op
~~~
差別幅度:
~~~
benchmark old ns/op new ns/op delta
BenchmarkJSONUnmarshal-8 10298 7591 -26.29%
~~~
可以看得出這個差別還是很大的, 特別是當 JSON 本身體量很大的時候.
結論
對于一些關乎性能的 JSON 解析的處理, 我們可以通過 json.RawMessage 進行性能的提升.

- Go語言基礎篇
- Go語言簡介
- Go語言教程
- Go語言環境安裝
- Go語言結構
- Go語言基礎語法
- Go語言數據類型
- Go語言變量
- Go語言提高篇
- Go語言實現貪吃蛇
- Go 諺語
- 解決連通性問題的四種算法
- golang 幾種字符串的連接方式
- Go JSON 技巧
- Go += 包版本
- Golang 編譯成 DLL 文件
- Go指南:牛頓法開方
- Go語言異步服務器框架原理和實現
- Golang適合高并發場景的原因分析
- 如何設計并實現一個線程安全的 Map ?(上篇)
- go語言執行cmd命令關機、重啟等
- IT雜項
- IT 工程師的自我管理
- IT界不為人知的14個狗血故事
- Go語言版本說明
- Go 1.10中值得關注的幾個變化
- Golang面試題解析
- Golang面試題
- Golang語言web開發
- golang 模板(template)的常用基本語法
- go語言快速入門:template模板
- Go Template學習筆記
- LollipopGo框架
- 框架簡介
- Golang語言版本設計模式
- 設計模式-單例模式
- Golang語言資源下載
- 公眾賬號
- leaf
- 合作講師
- 公開課目錄