以前咱們介紹過gojsonq
,能夠方便地從一個 JSON 串中讀取值。同時它也支持各類查詢、彙總統計等功能。今天咱們再介紹一個相似的庫gjson
。在上一篇文章Go 每日一庫之 buntdb中咱們介紹過 JSON 索引,內部實現其實就是使用gjson
這個庫。gjson
其實是get + json
的縮寫,用於讀取 JSON 串,一樣的還有一個sjson
(set + json
)庫用來設置 JSON 串。git
先安裝:github
$ go get github.com/tidwall/gjson
後使用:golang
package main import ( "fmt" "github.com/tidwall/gjson" ) func main() { json := `{"name":{"first":"li","last":"dj"},"age":18}` lastName := gjson.Get(json, "name.last") fmt.Println("last name:", lastName.String()) age := gjson.Get(json, "age") fmt.Println("age:", age.Int()) }
使用很簡單,只須要傳入 JSON 串和要讀取的鍵路徑便可。注意一點細節,由於gjson.Get()
函數實際上返回的是gjson.Result
類型,咱們要調用其相應的方法進行轉換對應的類型。如上面的String()
和Int()
方法。json
若是是直接打印輸出,其實能夠省略String()
,fmt
包的大部分函數均可以對實現fmt.Stringer
接口的類型調用String()
方法。數組
鍵路徑其實是以.
分隔的一系列鍵。gjson
支持在鍵中包含通配符*
和?
,*
匹配任意多個字符,?
匹配單個字符,例如ca*
能夠匹配cat/cate/cake
等以ca
開頭的鍵,ca?
只能匹配cat/cap
等以ca
開頭且後面只有一個字符的鍵。微信
數組使用鍵名 + .
+ 索引(索引從 0 開始)的方式讀取元素,若是鍵pets
對應的值是一個數組,那麼pets.0
讀取數組的第一個元素,pets.1
讀取第二個元素。函數
數組長度使用鍵名 + .
+ #
獲取,例如pets.#
返回數組pets
的長度。性能
若是鍵名中出現.
,那麼須要使用\
進行轉義。學習
package main const json = ` { "name":{"first":"Tom", "last": "Anderson"}, "age": 37, "children": ["Sara", "Alex", "Jack"], "fav.movie": "Dear Hunter", "friends": [ {"first": "Dale", "last":"Murphy", "age": 44, "nets": ["ig", "fb", "tw"]}, {"first": "Roger", "last": "Craig", "age": 68, "nets": ["fb", "tw"]}, {"first": "Jane", "last": "Murphy", "age": 47, "nets": ["ig", "tw"]} ] } ` func main() { fmt.Println("last name:", gjson.Get(json, "name.last")) fmt.Println("age:", gjson.Get(json, "age")) fmt.Println("children:", gjson.Get(json, "children")) fmt.Println("children count:", gjson.Get(json, "children.#")) fmt.Println("second child:", gjson.Get(json, "children.1")) fmt.Println("third child*:", gjson.Get(json, "child*.2")) fmt.Println("first c?ild:", gjson.Get(json, "c?ildren.0")) fmt.Println("fav.moive", gjson.Get(json, `fav.\moive`)) fmt.Println("first name of friends:", gjson.Get(json, "friends.#.first")) fmt.Println("last name of second friend:", gjson.Get(json, "friends.1.last")) }
前 3 個比較簡單,就不贅述了。看後面幾個:this
children.#
:返回數組children
的長度;children.1
:讀取數組children
的第 2 個元素(注意索引從 0 開始);child*.2
:首先child*
匹配children
,.2
讀取第 3 個元素;c?ildren.0
:c?ildren
匹配到children
,.0
讀取第一個元素;fav.\moive
:由於鍵名中含有.
,故須要\
轉義;friends.#.first
:若是數組後#
後還有內容,則之後面的路徑讀取數組中的每一個元素,返回一個新的數組。因此該查詢返回的數組全部friends
的first
字段組成;friends.1.last
:讀取friends
第 2 個元素的last
字段。運行結果:
last name: Anderson age: 37 children: ["Sara", "Alex", "Jack"] children count: 3 second child: Alex third child*: Jack first c?ild: Sara fave.moive first name of friends: ["Dale","Roger","Jane"] last name of second friend: Craig
對於數組,gjson
還支持按條件查詢元素,#(條件)
返回第一個知足條件的元素,#(條件)#
返回全部知足條件的元素。括號內的條件能夠有==
、!=
、<
、<=
、>
、>=
,還有簡單的模式匹配%
(符合某個模式),!%
(不符合某個模式):
fmt.Println(gjson.Get(json, `friends.#(last="Murphy").first`)) fmt.Println(gjson.Get(json, `friends.#(last="Murphy")#.first`)) fmt.Println(gjson.Get(json, "friends.#(age>45)#.last")) fmt.Println(gjson.Get(json, `friends.#(first%"D*").last`)) fmt.Println(gjson.Get(json, `friends.#(first!%"D*").last`)) fmt.Println(gjson.Get(json, `friends.#(nets.#(=="fb"))#.first`))
仍是使用上面的 JSON 串。
friends.#(last="Murphy").first
:friends.#(last="Murphy")
返回數組friends
中第一個last
爲Murphy
的元素,.first
表示取出該元素的first
字段返回;friends.#(last="Murphy")#.first
:friends.#(last="Murphy")#
返回數組friends
中全部的last
爲Murphy
的元素,而後讀取它們的first
字段放在一個數組中返回。注意與上面一個的區別;friends.#(age>45)#.last
:friends.#(age>45)#
返回數組friends
中全部年齡大於 45 的元素,而後讀取它們的last
字段返回;friends.#(first%"D*").last
:friends.#(first%"D*")
返回數組friends
中第一個first
字段知足模式D*
的元素,取出其last
字段返回;friends.#(first!%"D*").last
:`friends.#(first!%"D*")
返回數組friends
中第一個first
字段不知足模式D*
的元素,讀取其last
字段返回;friends.#(nets.#(=="fb"))#.first
:這是個嵌套條件,friends.#(nets.#(=="fb"))#
返回數組friends
的元素的nets
字段中有fb
的全部元素,而後取出first
字段返回。運行結果:
Dale ["Dale","Jane"] ["Craig","Murphy"] Murphy Craig ["Dale","Roger"]
修飾符是gjson
提供的很是強大的功能,和鍵路徑搭配使用。gjson
提供了一些內置的修飾符:
@reverse
:翻轉一個數組;@ugly
:移除 JSON 中的全部空白符;@pretty
:使 JSON 更易用閱讀;@this
:返回當前的元素,能夠用來返回根元素;@valid
:校驗 JSON 的合法性;@flatten
:數組平坦化,即將["a", ["b", "c"]]
轉爲["a","b","c"]
;@join
:將多個對象合併到一個對象中。修飾符的語法和管道相似,以|
分隔鍵路徑和分隔符。
const json = `{ "name":{"first":"Tom", "last": "Anderson"}, "age": 37, "children": ["Sara", "Alex", "Jack"], "fav.movie": "Dear Hunter", "friends": [ {"first": "Dale", "last":"Murphy", "age": 44, "nets": ["ig", "fb", "tw"]}, {"first": "Roger", "last": "Craig", "age": 68, "nets": ["fb", "tw"]}, {"first": "Jane", "last": "Murphy", "age": 47, "nets": ["ig", "tw"]} ] }` func main() { fmt.Println(gjson.Get(json, "children|@reverse")) fmt.Println(gjson.Get(json, "children|@reverse|0")) fmt.Println(gjson.Get(json, "friends|@ugly")) fmt.Println(gjson.Get(json, "friends|@pretty")) fmt.Println(gjson.Get(json, "@this")) nestedJSON := `{"nested": ["one", "two", ["three", "four"]]}` fmt.Println(gjson.Get(nestedJSON, "nested|@flatten")) userJSON := `{"info":[{"name":"dj", "age":18},{"phone":"123456789","email":"dj@example.com"}]}` fmt.Println(gjson.Get(userJSON, "info|@join")) }
children|@reverse
先讀取數組children
,而後使用修飾符@reverse
翻轉以後返回,輸出:
["Jack","Alex","Sara"]
children|@reverse|0
在上面翻轉的基礎上讀取第一個元素,即原數組的最後一個元素,輸出:
Jack
friends|@ugly
移除friends
數組中的全部空白字符,返回一行長長的字符串:
[{"first":"Dale","last":"Murphy","age":44,"nets":["ig","fb","tw"]},{"first":"Roger","last":"Craig","age":68,"nets":["fb","tw"]},{"first":"Jane","last":"Murphy","age":47,"nets":["ig","tw"]}]
friends|@pretty
格式化friends
數組,使之更易讀:
[ { "first": "Dale", "last": "Murphy", "age": 44, "nets": ["ig", "fb", "tw"] }, { "first": "Roger", "last": "Craig", "age": 68, "nets": ["fb", "tw"] }, { "first": "Jane", "last": "Murphy", "age": 47, "nets": ["ig", "tw"] } ]
@this
返回原始的 JSON 串。
@flatten
將數組nested
的內層數組平坦到外層後返回,即將全部內層數組的元素依次添加到外層數組後面並移除內層數組,輸出:
["one","two","three", "four"]
@join
將一個數組中的各個對象合併到一箇中,例子中將數組中存放的部分我的信息合併成一個對象返回:
{"name":"dj","age":18,"phone":"123456789","email":"dj@example.com"}
修飾符還能夠有參數,經過在修飾符後加:
後跟參數。若是咱們在格式化 JSON 串時,想要對鍵進行排序,那麼可使用@pretty
修飾符的sortKeys
參數。咱們仍是拿上面的 JSON 數據舉例:
fmt.Println(gjson.Get(json, `friends|@pretty:{"sortKeys":true}`))
最終按鍵名順序輸出 JSON 串:
[ { "age": 44, "first": "Dale", "last": "Murphy", "nets": ["ig", "fb", "tw"] }, { "age": 68, "first": "Roger", "last": "Craig", "nets": ["fb", "tw"] }, { "age": 47, "first": "Jane", "last": "Murphy", "nets": ["ig", "tw"] } ]
固然還能夠指定每行縮進indent
(默認兩個空格),每行開頭字符串prefix
(默認爲空串)和一行最多顯示字符數width
(默認 80 字符)。下面在每行前增長兩個空格:
fmt.Println(gjson.Get(json, `friends|@pretty:{"sortKeys":true,"prefix":" "}`))
如此強大的功固然要支持自定義!gjson
使用AddModifier()
添加一個修飾符,傳入一個名字和類型爲func(json arg string) string
的處理函數。處理函數接受待處理的 JSON 值和修飾符參數,返回處理後的結果。下面編寫一個轉換大小寫的修飾符:
func main() { gjson.AddModifier("case", func(json, arg string) string { if arg == "upper" { return strings.ToUpper(json) } if arg == "lower" { return strings.ToLower(json) } return json }) const json = `{"children": ["Sara", "Alex", "Jack"]}` fmt.Println(gjson.Get(json, "children|@case:upper")) fmt.Println(gjson.Get(json, "children|@case:lower")) }
輸出:
["SARA", "ALEX", "JACK"] ["sara", "alex", "jack"]
gjson
提供..
語法能夠將多行數據當作一個數組,每行數據是一個元素:
const json = ` {"name": "Gilbert", "age": 61} {"name": "Alexa", "age": 34} {"name": "May", "age": 57} {"name": "Deloise", "age": 44}` func main() { fmt.Println(gjson.Get(json, "..#")) fmt.Println(gjson.Get(json, "..1")) fmt.Println(gjson.Get(json, "..#.name")) fmt.Println(gjson.Get(json, `..#(name="May").age`)) }
..#
:返回有多少行 JSON 數據;..1
:返回第一行,即{"name": "Gilbert", "age": 61}
;..#.name
:#
後再接路徑,表示對數組中每一個元素讀取後面的路徑,將讀取到的值組成一個新數組返回;..#.name
表示讀取每一行中的name
字段,最終返回["Gilbert","Alexa","May","Deloise"]
;..#(name="May").age
:括號中的內容(name="May")
表示條件,因此該條含義爲取name
爲"May"
的行中的age
字段。gjson
還提供了遍歷 JSON 行的方法:gjson.ForEachLine()
,參數爲 JSON 串和類型爲func(line gjson.Result) bool
的回調函數。回調返回false
時遍歷中止。下面代碼讀取輸出每一行的name
字段:
gjson.ForEachLine(json, func(line gjson.Result) bool { fmt.Println("name:", gjson.Get(line.String(), "name")) return true })
上面咱們介紹了遍歷 JSON 行的方式,實際上gjson
還提供了通用的遍歷數組和對象的方式。gjson.Get()
方法返回一個gjson.Result
類型的對象,json.Result
提供了ForEach()
方法用於遍歷。該方法接受一個類型爲func (key, value gjson.Result) bool
的回調函數。遍歷對象時key
和value
分別爲對象的鍵和值;遍歷數組時,value
爲數組元素,key
爲空(不是索引)。回調返回false
時,遍歷中止。
const json = ` { "name":"dj", "age":18, "pets": ["cat", "dog"], "contact": { "phone": "123456789", "email": "dj@example.com" } }` func main() { pets := gjson.Get(json, "pets") pets.ForEach(func(_, pet gjson.Result) bool { fmt.Println(pet) return true }) contact := gjson.Get(json, "contact") contact.ForEach(func(key, value gjson.Result) bool { fmt.Println(key, value) return true }) }
調用gjson.Get()
時,gjson
假設咱們傳入的 JSON 串是合法的。若是 JSON 非法也不會panic
,這時會返回不肯定的結果:
func main() { const json = `{"name":dj,age:18}` fmt.Println(gjson.Get(json, "name")) }
上面 JSON 串是非法的,dj
和age
都沒有加上雙引號(實際上習慣了 Go 語言map
的寫法,很容易把 JSON 寫成這樣😭)。上面代碼輸出18,顯然是錯誤的。咱們可使用gjson.Valid()
檢測 JSON 串是否合法:
if !gjson.Valid(json) { fmt.Println("error") } else { fmt.Println("ok") }
調用gjson.Get()
一次只能讀取一個值,屢次調用又比較麻煩,gjson
提供了GetMany()
能夠一次讀取多個值,返回一個數組[]gjson.Result
。
const json = ` { "name":"dj", "age":18, "pets": ["cat", "dog"], "contact": { "phone": "123456789", "email": "dj@example.com" } }` func main() { results := gjson.GetMany(json, "name", "age", "pets.#", "contact.phone") for _, result := range results { fmt.Println(result) } }
上面代碼返回字段name
、age
、數組pets
的長度和contact.phone
字段。
gjson
使用比較方便,功能強大,性能可觀,值得一學。
你們若是發現好玩、好用的 Go 語言庫,歡迎到 Go 每日一庫 GitHub 上提交 issue😄
歡迎關注個人微信公衆號【GoUpUp】,共同窗習,一塊兒進步~