Go语言中JSON蛇形键转驼峰键的高效实现方法问询
高效解决Go结构体JSON蛇形键转驼峰键的方案
你现在手动维护两套结构体的做法确实太繁琐了,尤其是结构体字段多或者需要扩展的时候,很容易出错。下面给你几个更优雅的解决方案:
方案一:利用反射自动转换字段与键名
我们可以写一个通用的工具函数,通过反射遍历结构体的字段,读取原有蛇形JSON标签的值,将其转换为驼峰格式后,构建一个新的map[string]interface{},最后序列化这个map得到驼峰键的JSON。
示例代码:
import ( "encoding/json" "reflect" "strings" "github.com/iancoleman/strcase" // 用这个库快速实现蛇形转驼峰(不想依赖的话可以自己实现字符串转换) ) // SnakeToCamelJSON 将带蛇形JSON标签的结构体序列化为驼峰键的JSON func SnakeToCamelJSON(v interface{}) ([]byte, error) { val := reflect.ValueOf(v) if val.Kind() == reflect.Ptr { val = val.Elem() } if val.Kind() != reflect.Struct { return json.Marshal(v) // 非结构体直接返回原序列化结果 } camelMap := make(map[string]interface{}) typ := val.Type() for i := 0; i < typ.NumField(); i++ { field := typ.Field(i) jsonTag := field.Tag.Get("json") if jsonTag == "" || jsonTag == "-" { continue // 忽略无JSON标签或标记为忽略的字段 } // 剥离JSON标签里的额外选项(比如omitempty) snakeKey := strings.Split(jsonTag, ",")[0] camelKey := strcase.ToLowerCamel(snakeKey) // 转成小驼峰格式 fieldVal := val.Field(i).Interface() // 递归处理嵌套的结构体 if reflect.ValueOf(fieldVal).Kind() == reflect.Struct { nestedJSON, err := SnakeToCamelJSON(fieldVal) if err != nil { return nil, err } var nestedMap map[string]interface{} if err := json.Unmarshal(nestedJSON, &nestedMap); err != nil { return nil, err } camelMap[camelKey] = nestedMap } else { camelMap[camelKey] = fieldVal } } return json.Marshal(camelMap) }
使用时直接传入你的原有结构体就行:
var userWithAddress UserWithAddress // 假设已经完成了userWithAddress的数据填充 camelJSON, err := SnakeToCamelJSON(userWithAddress) if err != nil { // 处理错误逻辑 } // camelJSON就是你想要的驼峰键JSON数据
如果不想依赖第三方库,自己实现蛇形转驼峰的函数也很简单:
func snakeToCamel(s string) string { parts := strings.Split(s, "_") for i := 1; i < len(parts); i++ { parts[i] = strings.Title(parts[i]) } return strings.Join(parts, "") }
方案二:自定义结构体的MarshalJSON方法
如果你的结构体相对固定,也可以给结构体实现json.Marshaler接口,手动控制序列化时的键名转换。这种方式性能比反射更好,但需要为每个结构体编写对应的方法。
示例代码:
func (u BaseUser) MarshalJSON() ([]byte, error) { return json.Marshal(map[string]interface{}{ "userId": u.ID, "userName": u.Name, "userEmail": u.Email, }) } func (u UserWithAddress) MarshalJSON() ([]byte, error) { baseJSON, err := u.BaseUser.MarshalJSON() if err != nil { return nil, err } var baseMap map[string]interface{} if err := json.Unmarshal(baseJSON, &baseMap); err != nil { return nil, err } // 添加子结构体的字段 baseMap["userPostal"] = u.Postal baseMap["userCity"] = u.City baseMap["userStreet"] = u.Street return json.Marshal(baseMap) }
使用时直接序列化原有结构体即可:
jsonData, err := json.Marshal(userWithAddress) // jsonData就是驼峰键的JSON结果
这种方式的缺点是结构体扩展时需要同步修改MarshalJSON方法,但比维护两套结构体要轻松不少。
方案三:使用支持自定义命名策略的JSON库(Go 1.18+)
如果你用的是Go 1.18及以上版本,可以换用支持自定义命名策略的第三方JSON库,比如goccy/go-json,它能直接在编解码时自动转换蛇形和驼峰键,完全不用修改原有结构体。
示例代码:
import ( "os" "github.com/goccy/go-json" ) // 你的原有结构体保持不变 type BaseUser struct { ID int64 `json:"user_id"` Name string `json:"user_name"` Email string `json:"user_email"` } type UserWithAddress struct { BaseUser Postal string `json:"user_postal"` City string `json:"user_city"` Street string `json:"user_street"` } func main() { user := UserWithAddress{ BaseUser: BaseUser{ID: 123, Name: "test", Email: "test@mail.com"}, Postal: "12312", City: "city", Street: "street", } // 创建编码器并设置命名策略为小驼峰 encoder := json.NewEncoder(os.Stdout) encoder.SetNamingStrategy(json.LowerCamelCase) _ = encoder.Encode(user) // 输出结果就是驼峰键的JSON }
这种方式最简洁,零额外代码维护,只需要替换标准库的JSON编解码工具即可。
内容的提问来源于stack exchange,提问作者Tarasovych
相关产品推荐
相关产品推荐

