背景
golang处理json,标注使用encoding/json库去Unmarshal,然后用map[string]interface{}等解决,其中需要各种数据类型强转,没有那么方便。gabs作用就是可以直接用path寻找对应结构,也可以通过path构造新的json。
介绍
Official description:Gabs is a small utility for dealing with dynamic or unknown JSON structures in Go. It's pretty much just a helpful wrapper for navigating hierarchies of map[string]interface{}
objects provided by the encoding/json
package. It does nothing spectacular apart from being fabulous.
Get gabs
go get -u github.com/Jeffail/gabs
Parse Json
Common Parse
// +build ignore
package main
import "github.com/Jeffail/gabs"
/*
@func: gabs的库测试使用
@author: Andy_文铎
@time: 2021/11/18
*/
func main() {
s := `{
"outter":{
"inner":{
"value1":10,
"value2":22
},
"alsoInner":{
"value1":20,
"array1":[
30, 40
]
}
}
}`
parseJson(s)
}
func parseJson(s string) {
jsonParsed, err := gabs.ParseJSON([]byte(s))
if err != nil {
panic(err)
}
var value float64
var ok bool
value, ok = jsonParsed.Path("outter.inner.value1").Data().(float64)
// value == 10.0, ok == true
value, ok = jsonParsed.Search("outter", "inner", "value1").Data().(float64)
// value == 10.0, ok == true
value, ok = jsonParsed.Search("outter", "alsoInner", "array1", "1").Data().(float64)
// value == 40.0, ok == true
gObj, err := jsonParsed.JSONPointer("/outter/alsoInner/array1/1")
if err != nil {
panic(err)
}
value, ok = gObj.Data().(float64)
// value == 40.0, ok == true
value, ok = jsonParsed.Path("does.not.exist").Data().(float64)
// value == 0.0, ok == false
exists := jsonParsed.Exists("outter", "inner", "value1")
// exists == true
exists = jsonParsed.ExistsP("does.not.exist")
// exists == false
}
通常先用
jsonParsed, err :=gabs.ParseJSON([]byte(s))
获取一个*gabs.Container,接着用
data := jsonParsed.Path(path).Data()
获取对应path下边的数据data,最后可以根据需要对data进行数据强转
Parse Object
对于这个官方有很多种方式, 第一种:
// 把array的索引镶嵌在path里
jsonParsed, err := gabs.ParseJSON([]byte(`{"array":[{"value":1},{"value":2},{"value":3}]}`))
if err != nil {
panic(err)
}
fmt.Println(jsonParsed.Path("array.1.value").String())
第二种:
// 使用Children()
jsonParsed, err := gabs.ParseJSON([]byte(`{"array":["first","second","third"]}`))
if err != nil {
panic(err)
}
for _, child := range jsonParsed.S("array").Children() {
fmt.Println(child.Data().(string))
}
这种测试使用了一下,当array的元素比较多的时候会有问题,具体原因还不清楚,所以array中object比较大的时候不建议使用
第三种:
// 使用ArrayElementP, 获取某path下的array某个索引的内容
// 获取array的数量
count, err := jsonParsed.ArrayCountP("detail.pois")
fmt.Println("count->", count)
con, err := jsonParsed.ArrayElementP(0, "detail.pois")
fmt.Println("con->", con)
这种不会出现第二种的问题,个人通常使用这种方式获取array的数据
New Json
新建*gabs.Container, 并写入
// 把数据data写入对应path
jsonObj := gabs.New()
jsonObj.SetP(data, "data.board.icon")
个人一般会封装一个函数,用来从一个json获取数据并赋值给新的json
// 从旧的Container到新的Container
func pathToNewPath(jsonParsed *gabs.Container, path string, jsonObj *gabs.Container, newPath string) {
if jsonParsed.ExistsP(path) {
jsonObj.SetP(jsonParsed.Path(path).Data(), newPath)
}
}
实例
// 解析数据
func parseMSData(content []byte) (string, error) {
result := ""
jsonParsed, err := gabs.ParseJSON(content)
if err != nil {
return result, err
}
jsonObj := gabs.New()
con, err := jsonParsed.ArrayElementP(0, "xxx")
if err != nil {
return result, err
}
fmt.Println("conn data->", con.Data())
poiPath, err := con.S("xxx").Children()
if err != nil {
return result, err
}
currentCity := ""
for _, child := range poiPath {
ctype := child.Path("ctype").Data().(float64)
if ctype == 2 {
currentCity = child.Path("cname").Data().(string)
}
}
// sUid
pathToNewPath(con, "uid", jsonObj, "data.tPOI.sUid")
// sName
pathToNewPath(con, "name", jsonObj, "data.tPOI.sName")
// sAddr
pathToNewPath(con, "addr", jsonObj, "data.tPOI.sAddr")
// sPhone
pathToNewPath(con, "phone", jsonObj, "data.tPOI.sPhone")
// sShortAddr
pathToNewPath(con, "short_addr", jsonObj, "data.tPOI.sShortAddr")
// nCO
pathToNewPath(con, "CO", jsonObj, "data.tPOI.nCO")
// Cotype
CO := con.Path("CO").Data().(string)
jsonObj.SetP(fileconfig.GetConfig().CoType[CO], "data.tPOI.co_type")
// richInfo
pathToNewPath(con, "richInfo", jsonObj, "data.richInfo")
// TT标签
tags := getTTTags(con.Path("TT"), fileconfig.GetConfig().CoType[CO], CO)
jsonObj.SetP(tags, "data.review_tags")
// board
bangdanIcon := "xxx"
bgColor := "xxx"
fontColor := "xxx"
jsonObj.SetP(bangdanIcon, "data.board.icon")
jsonObj.SetP(bgColor, "data.board.bgColor")
jsonObj.SetP(fontColor, "data.board.fontColor")
boardContent, err := con.ArrayElementP(0, "richInfo.deep.bangdan_new")
pathToNewPath(boardContent, "ranklist_name", jsonObj, "data.board.name")
pathToNewPath(boardContent, "poi_order", jsonObj, "data.board.rank")
pathToNewPath(boardContent, "ranklist_id", jsonObj, "data.board.boardId")
//boardContent := getGabsChild(con, "richInfo.deep.bangdan_new", 0)
boardUrl := ""
// do something to splice boardUrl
if ranklist_id != "" && ranklist_name != "" {
jsonObj.SetP(boardUrl, "data.board.boardUrl")
}
// indoor
// floor_name
pathToNewPath(con, "floor_name", jsonObj, "data.indoor.floor_name")
// is_inside
pathToNewPath(con, "is_inside", jsonObj, "data.indoor.is_inside")
// inside_class
pathToNewPath(con, "inside_class", jsonObj, "data.indoor.inside_class")
// build_id
pathToNewPath(con, "build_id", jsonObj, "data.indoor.build_id")
// shinei_info.Bld_type
// poiNote
// PL
// strNPLDescription
// baseMapIconInfo
// brand_id
pathToNewPath(con, "brand_id", jsonObj, "data.brand_id")
// brand_name
pathToNewPath(con, "brand_name", jsonObj, "data.brand_name")
// 加上err_msg和err_code
jsonObj.SetP("ok", "err_msg")
jsonObj.SetP(0, "err_code")
return jsonObj.String(), nil
}
// 从旧的Container到新的Container
func pathToNewPath(jsonParsed *gabs.Container, path string, jsonObj *gabs.Container, newPath string) {
if jsonParsed.ExistsP(path) {
jsonObj.SetP(jsonParsed.Path(path).Data(), newPath)
}
}
结语
在此mark一下gabs库的使用,亲测通过此库可以轻松解析json的path下的内容,甚至可以实现通过配置完成静态数据的旧结构到新结构的转换,后续有新的使用感悟再补充.