官方的json庫, 只支援整體的序列化與還原序列化. 像其它語言的庫, json庫都支援單個getValue/setValue這類操作.
找了下golang相關的開源json庫, GJSON star數不錯,支援的查詢功能很豐富. 但是沒有寫入介面. 找了一圈也沒有
找到有寫入功能的json庫. 能想到的只有定義類型序列化或map序列化了. 以下是本文, 翻譯自項目readme:
開始
開始使用GJSON之前, 先安裝Go, 然後運行go get
:
$ go get -u github.com/tidwall/gjson
擷取值
Get查詢指定路徑, 通過.
來區分. 比如"name.last"或者"age". 如果找到了匹配路徑, 將返回結果.
package mainimport "github.com/tidwall/gjson"const json = `{"name":{"first":"Janet","last":"Prichard"},"age":47}`func main() { value := gjson.Get(json, "name.last") println(value.String())}
輸出結果:
Prichard
同時有 GetMany 方法批量擷取值, 也有 GetBytes 方法擷取位元組切片.
路徑解析
路徑是一系列被.
分隔的key拼接而成.
路徑可能包含萬用字元'*'和'?'.
通過下標訪問數組值.
通過'#'來擷取值在元素中的排位或訪問子路徑.
.
和萬用字元可以通過'\'來轉義.
{ "name": {"first": "Tom", "last": "Anderson"}, "age":37, "children": ["Sara","Alex","Jack"], "fav.movie": "Deer Hunter", "friends": [ {"first": "Dale", "last": "Murphy", "age": 44}, {"first": "Roger", "last": "Craig", "age": 68}, {"first": "Jane", "last": "Murphy", "age": 47} ]}
"name.last" >> "Anderson""age" >> 37"children" >> ["Sara","Alex","Jack"]"children.#" >> 3"children.1" >> "Alex""child*.2" >> "Jack""c?ildren.0" >> "Sara""fav\.movie" >> "Deer Hunter""friends.#.first" >> ["Dale","Roger","Jane"]"friends.1.last" >> "Craig"
你同樣能通過#[...]
來查詢數組中的第一個匹配的項, 或通過'#[...]#'查詢所有匹配的項.
查詢支援==
, !=
, <
, <=
, >
, >=
比較子和'%'模糊比對.
friends.#[last=="Murphy"].first >> "Dale"friends.#[last=="Murphy"]#.first >> ["Dale","Jane"]friends.#[age>45]#.last >> ["Craig","Murphy"]friends.#[first%"D*"].last >> "Murphy"
JSON 行
同樣支援JSON Lines, 使用 ..
首碼, 把多行文檔視作數組.
比如:
{"name": "Gilbert", "age": 61}{"name": "Alexa", "age": 34}{"name": "May", "age": 57}{"name": "Deloise", "age": 44}
..# >> 4..1 >> {"name": "Alexa", "age": 34}..3 >> {"name": "Deloise", "age": 44}..#.name >> ["Gilbert","Alexa","May","Deloise"]..#[name="May"].age >> 57
ForEachLines
方法可以迭代json.
gjson.ForEachLine(json, func(line gjson.Result) bool{ println(line.String()) return true})
Result Type
GJSON支援json類型包括 string
, number
, bool
, and null
. 數組和對象被擋住基礎類型返回.
Result
持有如下其中一種類型:
bool, for JSON booleansfloat64, for JSON numbersstring, for JSON string literalsnil, for JSON null
直接存取value:
result.Type // can be String, Number, True, False, Null, or JSONresult.Str // holds the stringresult.Num // holds the float64 numberresult.Raw // holds the raw jsonresult.Index // index of raw value in original json, zero means index unknown
有各種各樣的方便的函數可以擷取結果:
result.Exists() boolresult.Value() interface{}result.Int() int64result.Uint() uint64result.Float() float64result.String() stringresult.Bool() boolresult.Time() time.Timeresult.Array() []gjson.Resultresult.Map() map[string]gjson.Resultresult.Get(path string) Resultresult.ForEach(iterator func(key, value Result) bool)result.Less(token Result, caseSensitive bool) bool
result.Value()
方法返回 interface{}
Go基本類型之一.
result.Array()
方法返回一組值.
如果結果是不存在的值, 將會返回空數組.
如果結果不是JSON數組, 將會返回只包含一個值的數組.
boolean >> boolnumber >> float64string >> stringnull >> nilarray >> []interface{}object >> map[string]interface{}
64-bit integers
result.Int()
和 result.Uint()
返回的是64位大數字.
result.Int() int64 // -9223372036854775808 to 9223372036854775807result.Uint() int64 // 0 to 18446744073709551615
讀取嵌套數組
假如你想從下列json擷取所有的lastName:
{ "programmers": [ { "firstName": "Janet", "lastName": "McLaughlin", }, { "firstName": "Elliotte", "lastName": "Hunter", }, { "firstName": "Jason", "lastName": "Harold", } ]}
你可以使用如下路徑programmers.#.lastName
:
result := gjson.Get(json, "programmers.#.lastName")for _, name := range result.Array() { println(name.String())}
你同樣能擷取數組裡的對象:
name := gjson.Get(json, `programmers.#[lastName="Hunter"].firstName`)println(name.String()) // prints "Elliotte"
對象或數組迭代
ForEach
方法允許你快速的迭代對象或數組.
key和value被傳遞給對象的迭代器函數.
只有value被傳遞給數組. 迭代器返回false
將會終止迭代.
簡易的Parse和Get
Parse(json)
方法可以簡單的分析json, result.Get(path)
查詢結果.
比如, 下面的幾種情況都將返回相同的結果:
gjson.Parse(json).Get("name").Get("last")gjson.Get(json, "name").Get("last")gjson.Get(json, "name.last")
檢查value是否存在
有時你想要知道值是否存在.
value := gjson.Get(json, "name.last")if !value.Exists() { println("no last name")} else { println(value.String())}// Or as one stepif gjson.Get(json, "name.last").Exists() { println("has a last name")}
驗證JSON
Get*
和 Parse*
方法預期json格式是正常的, 如果不正常, 將會返回不可預料的結果.
如果你讀取的json來源不可預料, 那麼你可以通過GJSON這麼事先驗證.
if !gjson.Valid(json) { return errors.New("invalid json")}value := gjson.Get(json, "name.last")
還原序列化到map
還原序列化到map[string]interface{}
:
m, ok := gjson.Parse(json).Value().(map[string]interface{})if !ok { // not a map}## 處理Bytes如果你的JSON包含位元組數組切片, 與其調用`Get(string(data), path)`, 不如調用[GetBytes](https://godoc.org/github.com/tidwall/gjson#GetBytes)方法更優.```govar json []byte = ...result := gjson.GetBytes(json, path)
如果你在使用gjson.GetBytes(json, path)
方法, 並且你想避免從result.Raw
轉換到 []byte
, 你可以使用這種模式:
var json []byte = ...result := gjson.GetBytes(json, path)var raw []byteif result.Index > 0 { raw = json[result.Index:result.Index+len(result.Raw)]} else { raw = []byte(result.Raw)}
這是最好的模式, 不會為子切片重新分配記憶體. 這個模式使用了result.Index
欄位, 它直接指向了raw data所處原來json中的位置.
如果result.Raw
是轉換成[]byte
的, result.Index
將會為0.
一次擷取多個值
GetMany
方法可以用於同時擷取多個值.
results := gjson.GetMany(json, "name.first", "name.last", "age")
傳回值是[]Result
類型, 總是返回正傳入路徑個數的數量.
效能
測試了GJSON以及 encoding/json,
ffjson,
EasyJSON,
jsonparser,
and json-iterator
BenchmarkGJSONGet-8 3000000 372 ns/op 0 B/op 0 allocs/opBenchmarkGJSONUnmarshalMap-8 900000 4154 ns/op 1920 B/op 26 allocs/opBenchmarkJSONUnmarshalMap-8 600000 9019 ns/op 3048 B/op 69 allocs/opBenchmarkJSONDecoder-8 300000 14120 ns/op 4224 B/op 184 allocs/opBenchmarkFFJSONLexer-8 1500000 3111 ns/op 896 B/op 8 allocs/opBenchmarkEasyJSONLexer-8 3000000 887 ns/op 613 B/op 6 allocs/opBenchmarkJSONParserGet-8 3000000 499 ns/op 21 B/op 0 allocs/opBenchmarkJSONIterator-8 3000000 812 ns/op 544 B/op 9 allocs/op
使用的JSON:
{ "widget": { "debug": "on", "window": { "title": "Sample Konfabulator Widget", "name": "main_window", "width": 500, "height": 500 }, "image": { "src": "Images/Sun.png", "hOffset": 250, "vOffset": 250, "alignment": "center" }, "text": { "data": "Click Here", "size": 36, "style": "bold", "vOffset": 100, "alignment": "center", "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" } }}
每個操作都是在以下的搜尋路徑中進行旋轉的:
widget.window.namewidget.image.hOffsetwidget.text.onMouseUp
這些基準是在MacBook Pro 15" 2.8 GHz英特爾酷睿i7上啟動並執行, 使用Go 1.8, 可以找到. here.