最近尝试用 gin 写一个个人项目,有个问题就是,因为 golang 里用大小写控制访问权限,所以我定义的 struct 的属性名都是大写的,这样以 json 格式返回后 key 也都是首字母大写的 PascalCase 风格,但我需要 cameCase 风格,有什么办法统一处理吗?
我目前知道可以在定义 struct 的时候加json
的 tag,但是一个有点麻烦,另一个就是对于一些第三方依赖包里定义的 struct 就没有办法了,有什么优雅的办法吗?
![]() | 1 Hanggi 2020-06-12 10:36:48 +08:00 请认真阅读文档: https://github.com/gin-gonic/gin#model-binding-and-validation 如果遇到路由冲突: https://hanggi.me/post/golang/wildcard-conflict/ 当然好好设计针对 httprouter 的路由规则才是正解。 |
![]() | 2 beiping96 2020-06-12 10:37:25 +08:00 `encoding/json`最优雅 |
3 dilu 2020-06-12 11:05:34 +08:00 u1s1,个人项目用 php 一把梭不香嘛 或者你擅长的语言也可以噻 |
![]() | 6 Fitz 2020-06-12 11:16:11 +08:00 加个 tag 有什么麻烦的, 第三方的你只能自己定义一个一样的结构体,把数据拷过来了 |
7 eslizn 2020-06-12 11:16:35 +08:00 给第三方 struct 定义别名,并给这个别名实现 Marshaler 接口 |
![]() | 8 lasuar 2020-06-12 11:21:13 +08:00 默认就是和属性名保持一致,没毛病,要驼峰加 tag,这个做法没有问题,你的需求是个性化的。 |
![]() | 9 lasuar 2020-06-12 11:21:59 +08:00 要么自己实现对应接口,那个成本更高 |
![]() | 10 Hanggi 2020-06-12 11:26:47 +08:00 via iPhone @lancelock 原理是一样的,静态语言就是要把 json 绑定到结构体里。不要想着动态解析它,用不来可以用 nodejs,现在 typescript 开发也都要绑定结构体,一样的。 |
![]() | 11 lancelock OP 按我的想法,这个可以用一个后置的中间件,handlle 结束后做统一处理,可是没找到现成的,我试试自己写一个吧 |
![]() | 12 qloog 2020-06-12 11:28:28 +08:00 一般的做法是在 struct 的 tag 进行处理,添加 json,里面随便定义,下划线或者驼峰方式都可以。 |
![]() | 13 AlphaTr 2020-06-12 11:30:12 +08:00 via iPhone 对 第三方 struct 应该可以用 reflect 处理 |
![]() | 15 whoami9894 2020-06-12 11:43:20 +08:00 笑死了 #1 #10,一本正经的答非所问 可以找一个自动生成 tag 的结构体生成器,第三方包的话就把字段反射解析出来 |
![]() | 16 dai875939260 2020-06-12 11:47:17 +08:00 https://github.com/fatih/gomodifytags 修改 tag,第三方的包的可以自己再建一个结构体,然后用 mapstructure 转换? |
![]() | 17 wangyzj 2020-06-12 11:47:18 +08:00 go 这个东西不就是这样 你都习惯别的了还纠结啥 tag |
18 scnace 2020-06-12 11:58:09 +08:00 via Android 自己实现一个自用 Marshaller ? |
![]() | 19 Kisesy 2020-06-12 12:17:48 +08:00 只需要实现 Marshaler 接口并包裹一下(这篇文章中用正则表达式来处理比较低效,需要优化) https://www.cnblogs.com/chenqionghe/p/13067596.html |
20 mengzhuo 2020-06-12 12:39:55 +08:00 https://json.to-go.online |
![]() | 21 Hanggi 2020-06-12 12:50:49 +08:00 @lancelock 我说得很清楚了,Go 是静态语言,不要想着什么 marshal 之类的优雅解决。 你要做的就是定义一个 response 结构体,把你要返回的数据 mapping 上去。 你可以用一些工具简化这个操作,但是结果都是一样的。 如果你觉得麻烦,不想这样,可以考虑使用 Nodejs 这种动态语言。 |
![]() | 23 blessyou 2020-06-12 13:29:57 +08:00 那看起来还是转换风格比较 easy 。 |
24 sunxiansong 2020-06-12 13:58:26 +08:00 VSCode go 插件有个命令:Go: Add Tags To Struct Fields 不过缺点是支持 snake_case,不知道在哪里改 好在我找到 VSCode 有个插件 change-case ,可以方便的在不认风格间切换 |
![]() | 25 yxlimo 2020-06-12 14:03:50 +08:00 via iPhone 我觉得问题的点在于,你为什么要依赖第三方包的 struct 生成 response 。这样子是不可控的 |
![]() | 26 Hanggi 2020-06-12 14:15:34 +08:00 @lancelock 怪心累的,听不懂人话呢,只说最后一次。 看你问这个问题就知道你没怎么用过 go 语言。你以为你是这世界上第一个有这种需求的人吗? go 语言发展了 10 年为什么找不到你要的所谓的第三方依赖包,因为 go 语言不提倡这么做。 像 #13 楼说的 reflect 其实就是一种动态判断类型的方法,但是,不要这么做,原因自己查。 不要什么后置中间件统一处理,如果你非要这么做可能 go 语言不适合你。 go 语言 json struct 可以嵌套你知道吧?你要做的就是定义好结构体,加好 tag,组合使用他们。 如果这样还看不懂,你也不用回了。 |
![]() | 27 labulaka521 2020-06-12 14:34:32 +08:00 via iPhone 定义 tag ? |
28 iceiceice 2020-06-12 15:21:40 +08:00 package jsonconv import ( "bytes" "encoding/json" "log" "regexp" "strconv" "strings" "unicode" ) /*************************************** 下划线 json ***************************************/ type JsonSnakeCase struct { Value interface{} } func (c JsonSnakeCase) MarshalJSON() ([]byte, error) { // Regexp definitions var keyMatchRegex = regexp.MustCompile(`\"(\w+)\":`) var wordBarrierRegex = regexp.MustCompile(`(\w)([A-Z])`) marshalled, err := json.Marshal(c.Value) converted := keyMatchRegex.ReplaceAllFunc( marshalled, func(match []byte) []byte { return bytes.ToLower(wordBarrierRegex.ReplaceAll( match, []byte(`${1}_${2}`), )) }, ) return converted, err } /*************************************** 驼峰 json ***************************************/ type JsonCamelCase struct { Value interface{} } func (c JsonCamelCase) MarshalJSON() ([]byte, error) { var keyMatchRegex = regexp.MustCompile(`\"(\w+)\":`) marshalled, err := json.Marshal(c.Value) converted := keyMatchRegex.ReplaceAllFunc( marshalled, func(match []byte) []byte { matchStr := string(match) key := matchStr[1 : len(matchStr)-2] resKey := Lcfirst(Case2Camel(key)) return []byte(`"` + resKey + `":`) }, ) return converted, err } /*************************************** 其他方法 ***************************************/ // 驼峰式写法转为下划线写法 func Camel2Case(name string) string { buffer := NewBuffer() for i, r := range name { if unicode.IsUpper(r) { if i != 0 { buffer.Append('_') } buffer.Append(unicode.ToLower(r)) } else { buffer.Append(r) } } return buffer.String() } // 下划线写法转为驼峰写法 func Case2Camel(name string) string { name = strings.Replace(name, "_", " ", -1) name = strings.Title(name) return strings.Replace(name, " ", "", -1) } // 首字母大写 func Ucfirst(str string) string { for i, v := range str { return string(unicode.ToUpper(v)) + str[i+1:] } return "" } // 首字母小写 func Lcfirst(str string) string { for i, v := range str { return string(unicode.ToLower(v)) + str[i+1:] } return "" } // 内嵌 bytes.Buffer,支持连写 type Buffer struct { *bytes.Buffer } func NewBuffer() *Buffer { return &Buffer{Buffer: new(bytes.Buffer)} } func (b *Buffer) Append(i interface{}) *Buffer { switch val := i.(type) { case int: b.append(strconv.Itoa(val)) case int64: b.append(strconv.FormatInt(val, 10)) case uint: b.append(strconv.FormatUint(uint64(val), 10)) case uint64: b.append(strconv.FormatUint(val, 10)) case string: b.append(val) case []byte: b.Write(val) case rune: b.WriteRune(val) } return b } func (b *Buffer) append(s string) *Buffer { defer func() { if err := recover(); err != nil { log.Println("*****内存不够了!******") } }() b.WriteString(s) return b } func TestJsonCamelCase_MarshalJSON(t *testing.T) { type Person struct { HelloWold string LightWeightBaby string } var a = Person{HelloWold: "xxx", LightWeightBaby: "muscle"} res, _ := json.Marshal(JsonCamelCase{a}) fmt.Printf("%s", res) } |
29 BlackBerry999 2020-06-12 16:35:55 +08:00 @iceiceice 秀! |