如何解析递归结构的JSON并映射到对应结构体?
递归类型过滤器的JSON映射解决方案
问题描述
我有如下格式的JSON:
{ "Filter": { "Filters": [ { "Filter": { "someKey": "someValue1", }, "FilterType": "trait" }, { "Filter": { "someKey": "someValue2", }, "FilterType": "trait" } ] }, "FilterType": "and" // 也可以是or }每个过滤器的
FilterType对应代码里的一个特定结构体,而且这个JSON是递归结构:and/or类型的过滤器包含多个子过滤器,子过滤器也可以是and、or、trait或其他类型。我需要根据类型把它递归映射到正确的结构体,之前用mapstructure实现,但代码太繁琐,实际还有更多过滤器类型,求靠谱解决方案。
方法1:自定义JSON反序列化(更推荐)
直接用Go的json.Unmarshaler接口自己实现解析逻辑,比mapstructure灵活,代码也更清晰,还能完美处理递归。
步骤1:定义结构体和接口
先把所有过滤器类型对应的结构体和统一接口定义好:
package main import ( "encoding/json" "fmt" "log" ) // Filter 所有过滤器的统一接口 type Filter interface { FilterType() string } // AndFilter 对应and类型的过滤器 type AndFilter struct { Filters []Filter `json:"Filters"` } func (f *AndFilter) FilterType() string { return "and" } // OrFilter 对应or类型的过滤器 type OrFilter struct { Filters []Filter `json:"Filters"` } func (f *OrFilter) FilterType() string { return "or" } // TraitFilter 对应trait类型的过滤器 type TraitFilter struct { SomeKey string `json:"someKey"` } func (f *TraitFilter) FilterType() string { return "trait" } // FilterWrapper 临时包装结构体,用来先解析FilterType type FilterWrapper struct { FilterType string `json:"FilterType"` Filter json.RawMessage `json:"Filter"` }
步骤2:实现递归解析逻辑
写一个解析函数,根据FilterType实例化对应的结构体,同时递归解析子过滤器:
// ParseFilter 把JSON字节解析成具体的Filter实例 func ParseFilter(data []byte) (Filter, error) { var wrapper FilterWrapper if err := json.Unmarshal(data, &wrapper); err != nil { return nil, err } var filter Filter switch wrapper.FilterType { case "and": var andFilter AndFilter // 先解析外层的Filters字段(此时子过滤器是RawMessage) if err := json.Unmarshal(wrapper.Filter, &andFilter); err != nil { return nil, err } // 递归解析每个子过滤器 for i, subRaw := range andFilter.Filters { subFilter, err := ParseFilter(subRaw.(json.RawMessage)) if err != nil { return nil, err } andFilter.Filters[i] = subFilter } filter = &andFilter case "or": var orFilter OrFilter if err := json.Unmarshal(wrapper.Filter, &orFilter); err != nil { return nil, err } // 递归解析子过滤器 for i, subRaw := range orFilter.Filters { subFilter, err := ParseFilter(subRaw.(json.RawMessage)) if err != nil { return nil, err } orFilter.Filters[i] = subFilter } filter = &orFilter case "trait": var traitFilter TraitFilter if err := json.Unmarshal(wrapper.Filter, &traitFilter); err != nil { return nil, err } filter = &traitFilter default: return nil, fmt.Errorf("不支持的过滤器类型: %s", wrapper.FilterType) } return filter, nil }
步骤3:使用示例
func main() { // 带递归结构的测试JSON jsonData := []byte(`{ "Filter": { "Filters": [ { "Filter": { "someKey": "someValue1" }, "FilterType": "trait" }, { "Filter": { "Filters": [ { "Filter": {"someKey": "someValue3"}, "FilterType": "trait" } ] }, "FilterType": "or" } ] }, "FilterType": "and" }`) filter, err := ParseFilter(jsonData) if err != nil { log.Fatal(err) } // 类型断言处理具体过滤器 if andFilter, ok := filter.(*AndFilter); ok { fmt.Printf("这是一个AND过滤器,包含%d个子过滤器\n", len(andFilter.Filters)) // 可以继续遍历子过滤器处理 } }
方法2:优化mapstructure的使用
如果一定要用mapstructure,可以通过注册自定义钩子函数来简化递归解析,避免重复写繁琐的代码:
步骤1:定义结构体和钩子函数
package main import ( "encoding/json" "fmt" "log" "reflect" "github.com/mitchellh/mapstructure" ) type Filter interface { FilterType() string } type AndFilter struct { Filters []Filter `mapstructure:"Filters"` } func (f *AndFilter) FilterType() string { return "and" } type OrFilter struct { Filters []Filter `mapstructure:"Filters"` } func (f *OrFilter) FilterType() string { return "or" } type TraitFilter struct { SomeKey string `mapstructure:"someKey"` } func (f *TraitFilter) FilterType() string { return "trait" } // filterHook mapstructure的自定义解码钩子,用来解析Filter接口 func filterHook(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { // 只处理Filter类型的字段 if t != reflect.TypeOf((*Filter)(nil)).Elem() { return data, nil } // 把数据转成map,获取FilterType tempMap, ok := data.(map[string]interface{}) if !ok { return nil, fmt.Errorf("无效的过滤器数据") } filterType, ok := tempMap["FilterType"].(string) if !ok { return nil, fmt.Errorf("缺少FilterType字段") } // 根据类型实例化对应的结构体 var filter Filter switch filterType { case "and": filter = &AndFilter{} case "or": filter = &OrFilter{} case "trait": filter = &TraitFilter{} default: return nil, fmt.Errorf("不支持的过滤器类型: %s", filterType) } // 用mapstructure解析具体字段 err := mapstructure.Decode(data, filter) return filter, err }
步骤2:使用示例
func main() { jsonData := []byte(`...`) // 同之前的测试JSON var raw map[string]interface{} if err := json.Unmarshal(jsonData, &raw); err != nil { log.Fatal(err) } var filter Filter // 配置解码器,注册钩子 decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ DecodeHook: filterHook, Result: &filter, }) if err != nil { log.Fatal(err) } if err := decoder.Decode(raw); err != nil { log.Fatal(err) } // 后续处理同方法1 }
内容的提问来源于stack exchange,提问作者Martin
相关产品推荐
相关产品推荐

