Golang 流式解析 Json

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

json-iterator 庫:https://github.com/json-iterator/go

動機

現有的golang解析json的庫都是push模式的,缺少一種基於pull api的庫。另外就是看一下golang解析json的速度到底如何,還有多少的提高空間。

API 風格

api 風格上是以 StAX 為基礎,但是針對 JSON 做了特別的最佳化。比 StAX 和 SAX 都更簡單可控。當然如果需要最簡單,還是 DOM 類的 api 最簡單。使用流式pull的api為的就是最大化控制解析過程。

解析 Array

iter := ParseString(`[1,2,3]`)for iter.ReadArray() {  iter.ReadUint64()}

可以看到,pull api 的風格非常不同。整個解析流程是調用者驅動的

解析 Object

type TestObj struct {    Field1 string    Field2 uint64}
iter := ParseString(`{"field1": "1", "field2": 2}`)obj := TestObj{}for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {    switch field {    case "field1":        obj.Field1 = iter.ReadString()    case "field2":        obj.Field2 = iter.ReadUint64()    default:        iter.ReportError("bind object", "unexpected field")    }}

解析過程不依賴反射,解析出來的值幹什麼事情完全由你來操縱。你可以直接做一些累加操作,而不把值先綁定到對象上。

SKIP

iter := ParseString(`[ {"a" : [{"b": "c"}], "d": 102 }, "b"]`)iter.ReadArray()iter.Skip()iter.ReadArray()if iter.ReadString() != "b" {    t.FailNow()}

對於不關心的欄位,可以選擇跳過。

效能最佳化

這個項目的另外一個目的是看一下golang原生的json api是快還是慢,有沒有提高空間。

基於流解析,無需一次讀到記憶體裡

// "encoding/json"func Benchmark_stardard_lib(b *testing.B) {    b.ReportAllocs()    for n := 0; n < b.N; n++ {        file, _ := os.Open("/tmp/large-file.json")        result := []struct{}{}        decoder := json.NewDecoder(file)        decoder.Decode(&result)        file.Close()    }}

5 215547514 ns/op 71467118 B/op 272476 allocs/op

// "github.com/json-iterator/go"func Benchmark_jsoniter(b *testing.B) {    b.ReportAllocs()    for n := 0; n < b.N; n++ {        file, _ := os.Open("/tmp/large-file.json")        iter := jsoniter.Parse(file, 1024)        for iter.ReadArray() {            iter.Skip()        }        file.Close()    }}

10 110209750 ns/op 4248 B/op 5 allocs/op

可以看到 json iterator 的實現對於記憶體佔用非常節省。比標準庫的實現快一倍。GC壓力就小更多了。

直接解析出 int

對於 int 的解析無需兩遍,一次性讀取。把 ParseInt 的實現合并到 json 解析的代碼裡。

func Benchmark_jsoniter_array(b *testing.B) {    for n := 0; n < b.N; n++ {        iter := ParseString(`[1,2,3]`)        for iter.ReadArray() {            iter.ReadUint64()        }    }}

10000000 189 ns/op

func Benchmark_json_array(b *testing.B) {    for n := 0; n < b.N; n++ {        result := []interface{}{}        json.Unmarshal([]byte(`[1,2,3]`), &result)    }}

1000000 1327 ns/op

這個情境是 7x 的速度

無反射的,有schema的解析

按照schema解析,減少if-else判斷。直接賦值,無需經過反射

type Level1 struct {    Hello []Level2}type Level2 struct {    World string}func Benchmark_jsoniter_nested(b *testing.B) {    for n := 0; n < b.N; n++ {        iter := ParseString(`{"hello": [{"world": "value1"}, {"world": "value2"}]}`)        l1 := Level1{}        for l1Field := iter.ReadObject(); l1Field != ""; l1Field = iter.ReadObject() {            switch l1Field {            case "hello":                l1.Hello = readLevel1Hello(iter)            default:                iter.Skip()            }        }    }}func readLevel1Hello(iter *Iterator) []Level2 {    l2Array := make([]Level2, 0, 2)    for iter.ReadArray() {        l2 := Level2{}        for l2Field := iter.ReadObject(); l2Field != ""; l2Field = iter.ReadObject() {            switch l2Field {            case "world":                l2.World = iter.ReadString()            default:                iter.Skip()            }        }        l2Array = append(l2Array, l2)    }    return l2Array}

2000000 640 ns/op

func Benchmark_json_nested(b *testing.B) {    for n := 0; n < b.N; n++ {        l1 := Level1{}        json.Unmarshal([]byte(`{"hello": [{"world": "value1"}, {"world": "value2"}]}`), &l1)    }}

1000000 1816 ns/op

總結

golang 內建的 json 庫其實效能很不錯了。根據benchmark(https://github.com/json-itera...)其實速度比其他的基於流的解析庫還要快(https://github.com/ugorji/go/...)。而這個庫 https://github.com/pquerna/ff... 雖然號稱更快,但是不支援流式解析(要求所有的[]byte都提前讀入到記憶體裡)。大部分情況下,就用golang內建的就足夠好了,別瞎整一些其他的json解析庫。

如果需要pull api,或者需要額外的2x~6x效能,可以考慮:https://github.com/json-iterator/go

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.