Go语言如何反序列化含混合类型的嵌套JSON数组?
解决Go中嵌套混合数组JSON的反序列化问题
你的问题很典型——这个JSON是混合类型的嵌套表达式结构,既有三元组数组(比如["name", "contains", "test"]),又有逻辑运算符字符串("and"),还有更深层的嵌套表达式,直接用[]map[string]interface{}肯定不行,因为Go会尝试把每个元素解析成键值对map,但实际元素是数组或字符串,自然会报错。
下面给你两种可行的解决方案:
方案一:用interface{}接收并递归遍历处理
如果只是需要读取或遍历这个结构的数据,最快速的方式是用interface{}来接收整个JSON,然后通过类型断言递归解析每个元素:
package main import ( "encoding/json" "fmt" ) func parseFilterNode(node interface{}) { switch val := node.(type) { case []interface{}: // 处理数组节点:可能是三元组条件,也可能是逻辑表达式组 if len(val) == 3 { // 匹配三元组条件 [字段, 操作符, 值] field, _ := val[0].(string) operator, _ := val[1].(string) value := val[2] fmt.Printf("条件:%s %s %v\n", field, operator, value) } else { // 匹配逻辑表达式组,格式是 [表达式, 操作符, 表达式, ...] for i := 0; i < len(val); i++ { if i%2 == 0 { // 递归处理子表达式 parseFilterNode(val[i]) } else { // 处理逻辑运算符 op, _ := val[i].(string) fmt.Printf("逻辑运算符:%s\n", op) } } } case string: // 这里一般是逻辑运算符,不过也可以根据实际情况扩展 fmt.Printf("运算符:%s\n", val) default: fmt.Printf("未知类型节点:%T %v\n", val, val) } } func main() { // 模拟你的请求参数 f := `[ [ "name", "contains", "test" ], "and", [ "group", "=", "123" ], "and", [ [ "date", ">=", "2020-06-30T23:00:00Z" ], "and", [ "date", "<", "2020-07-31T23:00:00Z" ] ] ]` var dat interface{} if err := json.Unmarshal([]byte(f), &dat); err != nil { panic(err) } // 递归解析整个过滤表达式 parseFilterNode(dat) }
这种方式不需要定义复杂的结构体,适合快速处理数据的场景。
方案二:自定义类型实现json.Unmarshaler接口
如果需要把这个JSON转换成强类型的Go对象(方便后续业务逻辑处理,比如生成SQL语句),可以定义对应的表达式类型,并手动实现反序列化逻辑:
package main import ( "encoding/json" "fmt" ) // FilterExpr 统一的过滤表达式接口 type FilterExpr interface{} // Condition 三元组条件:[字段, 操作符, 值] type Condition struct { Field string Operator string Value interface{} } // LogicalExpr 逻辑表达式:由运算符连接多个子表达式 type LogicalExpr struct { Operator string Exprs []FilterExpr } // UnmarshalJSON 自定义反序列化逻辑 func (e *FilterExpr) UnmarshalJSON(data []byte) error { // 先尝试解析成数组 var arr []interface{} if err := json.Unmarshal(data, &arr); err != nil { return err } // 判断是三元组条件还是逻辑表达式 if len(arr) == 3 { // 解析为Condition field, ok1 := arr[0].(string) op, ok2 := arr[1].(string) if !ok1 || !ok2 { return fmt.Errorf("invalid condition format") } *e = Condition{ Field: field, Operator: op, Value: arr[2], } return nil } // 解析为LogicalExpr exprs := make([]FilterExpr, 0) var operator string for i := 0; i < len(arr); i++ { if i%2 == 0 { // 递归解析子表达式 subData, err := json.Marshal(arr[i]) if err != nil { return err } var subExpr FilterExpr if err := json.Unmarshal(subData, &subExpr); err != nil { return err } exprs = append(exprs, subExpr) } else { // 记录逻辑运算符(这里假设所有运算符一致,比如都是and,你可以根据需求扩展) op, ok := arr[i].(string) if !ok { return fmt.Errorf("invalid operator at index %d", i) } operator = op } } *e = LogicalExpr{ Operator: operator, Exprs: exprs, } return nil } func main() { f := `[ [ "name", "contains", "test" ], "and", [ "group", "=", "123" ], "and", [ [ "date", ">=", "2020-06-30T23:00:00Z" ], "and", [ "date", "<", "2020-07-31T23:00:00Z" ] ] ]` var expr FilterExpr if err := json.Unmarshal([]byte(f), &expr); err != nil { panic(err) } // 这里可以强类型断言处理不同的表达式 logicalExpr, ok := expr.(LogicalExpr) if ok { fmt.Printf("逻辑运算符:%s\n", logicalExpr.Operator) for _, subExpr := range logicalExpr.Exprs { if cond, ok := subExpr.(Condition); ok { fmt.Printf("条件:%s %s %v\n", cond.Field, cond.Operator, cond.Value) } else if subLogical, ok := subExpr.(LogicalExpr); ok { fmt.Printf("嵌套逻辑运算符:%s\n", subLogical.Operator) for _, c := range subLogical.Exprs { cond := c.(Condition) fmt.Printf("嵌套条件:%s %s %v\n", cond.Field, cond.Operator, cond.Value) } } } } }
这种方式可以把松散的JSON结构转换成清晰的Go对象,适合需要基于这个结构做复杂业务处理的场景。
为什么你的原始代码会报错?
你用了[]map[string]interface{}来接收,但这个JSON的元素不全是键值对的map——比如第一个元素是数组["name","contains","test"],第三个是字符串"and",Go在反序列化时会尝试把这些元素转换成map[string]interface{},显然无法完成,所以抛出了json: cannot unmarshal array into Go value of type map[string]interface {}的错误。
内容的提问来源于stack exchange,提问作者Shadoweb
相关产品推荐
相关产品推荐

