Golang如何基于任意层级嵌套结构体递归构建嵌套JSON对象
代码问题定位
你当前实现无法生成正确嵌套结构的核心原因有4个:
- 递归函数返回值为空:
buildRecursiveFunction最后返回的是初始化的空tempmap,没有存入遍历生成的字段数组,递归拿到的子结构始终是空值。 - 嵌套字段类型赋值错误:遇到指针类型的结构体字段时,你给字段
Type赋值为"ptr",但目标输出中嵌套结构体的类型固定为"struct",指针只是类型的引用方式,不是最终输出的类型标识。 - 根节点结构缺失:现有逻辑直接遍历字段,没有生成最外层包含结构体名、Fields数组的顶层结构,和目标输出格式不匹配。
- 子结构赋值逻辑错误:遇到指针指向的结构体时,你直接把递归返回的整个map塞给当前字段的
Fields,但递归返回的map是子结构体的完整描述(含子结构体名、子结构体Fields),实际只需要取子结构的Fields数组赋值即可。
修正后完整实现
package main import ( "encoding/json" "fmt" "reflect" ) // 示例结构体定义 type Data struct { ID string Name string Types *Details } type Details struct { Customer int32 Countries int32 } func check(e error) { if e != nil { panic(e) } } // 递归构建结构体的schema描述,入参必须是struct类型 func buildStructSchema(t reflect.Type) map[string]interface{} { schema := make(map[string]interface{}) schema["Name"] = t.Name() var fields []interface{} for i := 0; i < t.NumField(); i++ { sf := t.Field(i) // 跳过protobuf生成的内置保留字段 if sf.Name == "state" || sf.Name == "unknownFields" || sf.Name == "sizeCache" { continue } fieldObj := make(map[string]interface{}) // 默认取结构体字段名作为输出Name,如需取类型名可在此处修改逻辑 fieldObj["Name"] = sf.Name fieldType := sf.Type // 循环解引用所有指针层级 for fieldType.Kind() == reflect.Ptr { fieldType = fieldType.Elem() } // 嵌套结构体递归处理 if fieldType.Kind() == reflect.Struct { fieldObj["Type"] = "struct" subSchema := buildStructSchema(fieldType) fieldObj["Fields"] = subSchema["Fields"] } else { // 基础类型直接赋值类型字符串 fieldObj["Type"] = fieldType.Kind().String() } fields = append(fields, fieldObj) } schema["Fields"] = fields return schema } // 入口方法,兼容传入结构体指针/结构体值 func GetStructSchema(v interface{}) (map[string]interface{}, error) { t := reflect.TypeOf(v) // 解引用顶层指针 for t.Kind() == reflect.Ptr { t = t.Elem() } if t.Kind() != reflect.Struct { return nil, fmt.Errorf("input must be struct or struct pointer") } return buildStructSchema(t), nil } func main() { schema, err := GetStructSchema(&Data{}) check(err) // 带缩进序列化,和示例输出格式一致 jsonData, err := json.MarshalIndent(schema, "", " ") check(err) fmt.Println(string(jsonData)) }
补充说明
- 上述代码运行后输出的
Types字段Name为字段名Types,和你给出的示例中写的Details不一致,这是因为你定义的结构体中该字段的名称就是Types,如果需要输出类型名而非字段名,把fieldObj["Name"] = sf.Name替换为指针/非指针场景下取类型名的逻辑即可。 - 现有逻辑已经支持任意深度的结构体嵌套,包括多级指针、多级嵌套结构体的场景。如果后续需要支持切片、map、枚举等类型,只需要在类型判断分支追加对应处理逻辑,递归框架不需要修改。
- 你给出的目标JSON示例末尾缺少一个闭合大括号,序列化输出时会自动生成标准合法的JSON格式。
内容的提问来源于stack exchange,提问作者sachin srinivasan
相关产品推荐
相关产品推荐

