You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解析递归结构的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 10:17:57