💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
相对于很多的语言来说, 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 进行性能的提升. ![](https://box.kancloud.cn/032176b8778dbc41f1ce6b79a83d59c1_900x350.jpg)