背景
师弟在项目中有这样一个需求,需要拿到json格式的响应报文中的某个字段(key)对应的值,这个值会出现在不同响应中的不同嵌套位置中,而且在某个响应中,同名的字段在不同的嵌套层级可能会出现多次,所以对于每个具体响应,需要使用某个规则的字符串来指出嵌套的位置,比如 key1.key2.key3
实现思路
json的结构其实很像一棵树,某个子节点要么键对应的是一个值要么是一个子json,需要很适合使用dfs进行遍历
其中只需要对反序列化后的子节点进行类型断言即可,这里需要特别注意一下,如果子json字段是一个切片,那么类型断言对应的应该是[]interface{}
而不是 []map[string]interface{}
ps:map[string]interface{}
简直就是json的别名
最后以 key1.key2.key2.0.key4 这种结构就可以取到对应的值,其中数字代表在上一个子字段为切片类型中的第几个
代码
实现代码如下
func main() {
req := `{
"ch": "market.btcusdt.trade.detail",
"status": "ok",
"ts": 1669874392880,
"tick": {
"id": 161221899829,
"ts": 1669874391168,
"data": [{
"id": 161221899829685319577085771,
"ts": 1669874391168,
"trade-id": 102749823404,
"amount": 9.34E-4,
"price": 17115.82,
"direction": "sell",
"data":[{
"id": 161221899829685319577085323,
"ts": 2229874391168,
"trade-id": 222749823404,
"amount": 3.34E-4,
"price": 33115.82,
"direction": "sell"
}]
},{
"id": 161221899829685319577085323,
"ts": 2229874391168,
"trade-id": 222749823404,
"amount": 3.34E-4,
"price": 33115.82,
"direction": "sell"
}]
}
}`
js := map[string]interface{}{}
json.Unmarshal([]byte(req), &js)
tar := "tick.data.0.data.0.price"
fmt.Println(getTargetItem(tar, js))
}
func getTargetItem(tarKey string, js map[string]interface{}) string {
index := strings.Split(tarKey, ".")
var ans string
var dfs func(idx int, key string, mp map[string]interface{})
dfs = func(idx int, key string, mp map[string]interface{}) {
for k, v := range mp {
if k == key {
// if price, ok := v.(float64); ok {
// fmt.Println(price)
// return
// }
switch price := v.(type) {
case string:
ans = price
return
case float64:
ans = fmt.Sprintf("%f", price)
return
default:
}
if nextMp, ok := v.(map[string]interface{}); ok {
dfs(idx+1, index[idx+1], nextMp)
} else if dataSlice, ok := v.([]interface{}); ok {
num, err := strconv.Atoi(index[idx+1])
if err != nil {
fmt.Println(err.Error())
}
if nextP, ok := dataSlice[num].(map[string]interface{}); ok {
dfs(idx+2, index[idx+2], nextP)
}
}
}
}
}
dfs(0, index[0], js)
if ans != "" {
return ans
}
return "not found"
}