Go JSON 技巧

2018-02-11 14:06:56来源:https://lingchao.xin/post/json-tips-in-go.html作者:A Coder at Work人点击

分享

相对于很多的语言来说, Go 的 JSON 解析可谓简单至极.


问题

通常情况下, 我们在 Go 中经常这样进行 JSON 的解码:


package main
import "encoding/json"
// jsonText comes from /2014th7cj/d/file/p/20180211/example.htmlbrvar 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 {
Titlestring `json:"title"`
GlossDiv struct {
Title string `json:"title"`
GlossList struct {
GlossEntry struct {
IDstring `json:"ID"`
SortAsstring `json:"SortAs"`
GlossTerm string `json:"GlossTerm"`
Acronym string `json:"Acronym"`
Abbrevstring `json:"Abbrev"`
GlossDefstruct {
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 /2014th7cj/d/file/p/20180211/example.htmlbrvar jsonText = []byte(`
{
... // 此处省略, 同上
}`)
type glossarySectional struct {
Glossary struct {
Titlestring `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-8200000 10565 ns/op
BenchmarkJSONUnmarshal_1-82000007699 ns/op

差别幅度:


benchmarkold ns/op new ns/op delta
BenchmarkJSONUnmarshal-8 10298 7591-26.29%

可以看得出这个差别还是很大的, 特别是当 JSON 本身体量很大的时候.


结论


对于一些关乎性能的 JSON 解析的处理, 我们可以通过json.RawMessage
进行性能的提升.


最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台