有个 go 的问题,求解答 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
lancelock
V2EX    Go 编程语言

有个 go 的问题,求解答

  •  
  •   lancelock 2020-06-12 10:28:33 +08:00 5074 次点击
    这是一个创建于 1946 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近尝试用 gin 写一个个人项目,有个问题就是,因为 golang 里用大小写控制访问权限,所以我定义的 struct 的属性名都是大写的,这样以 json 格式返回后 key 也都是首字母大写的 PascalCase 风格,但我需要 cameCase 风格,有什么办法统一处理吗?

    我目前知道可以在定义 struct 的时候加json的 tag,但是一个有点麻烦,另一个就是对于一些第三方依赖包里定义的 struct 就没有办法了,有什么优雅的办法吗?

    31 条回复    2020-12-18 17:57:31 +08:00
    Hanggi
        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 的路由规则才是正解。
    beiping96
        2
    beiping96  
       2020-06-12 10:37:25 +08:00
    `encoding/json`最优雅
    dilu
        3
    dilu  
       2020-06-12 11:05:34 +08:00
    u1s1,个人项目用 php 一把梭不香嘛 或者你擅长的语言也可以噻
    lancelock
        4
    lancelock  
    OP
       2020-06-12 11:09:31 +08:00
    @Hanggi 说的不是参数绑定,是返回数据啊,而且这里说的而不也是加 tag 吗
    lancelock
        5
    lancelock  
    OP
       2020-06-12 11:11:54 +08:00
    @dilu 不会 php
    Fitz
        6
    Fitz  
       2020-06-12 11:16:11 +08:00
    加个 tag 有什么麻烦的, 第三方的你只能自己定义一个一样的结构体,把数据拷过来了
    eslizn
        7
    eslizn  
       2020-06-12 11:16:35 +08:00
    给第三方 struct 定义别名,并给这个别名实现 Marshaler 接口
    lasuar
        8
    lasuar  
       2020-06-12 11:21:13 +08:00
    默认就是和属性名保持一致,没毛病,要驼峰加 tag,这个做法没有问题,你的需求是个性化的。
    lasuar
        9
    lasuar  
       2020-06-12 11:21:59 +08:00
    要么自己实现对应接口,那个成本更高
    Hanggi
        10
    Hanggi  
       2020-06-12 11:26:47 +08:00 via iPhone
    @lancelock 原理是一样的,静态语言就是要把 json 绑定到结构体里。不要想着动态解析它,用不来可以用 nodejs,现在 typescript 开发也都要绑定结构体,一样的。
    lancelock
        11
    lancelock  
    OP
       2020-06-12 11:27:30 +08:00
    按我的想法,这个可以用一个后置的中间件,handlle 结束后做统一处理,可是没找到现成的,我试试自己写一个吧
    qloog
        12
    qloog  
       2020-06-12 11:28:28 +08:00
    一般的做法是在 struct 的 tag 进行处理,添加 json,里面随便定义,下划线或者驼峰方式都可以。
    AlphaTr
        13
    AlphaTr  
       2020-06-12 11:30:12 +08:00 via iPhone
    对 第三方 struct 应该可以用 reflect 处理
    lancelock
        14
    lancelock  
    OP
       2020-06-12 11:31:25 +08:00
    @Hanggi 我啥时候要动态解析了,我又什么时候没绑定结构体了?你能不能认真审题
    whoami9894
        15
    whoami9894  
       2020-06-12 11:43:20 +08:00
    笑死了 #1 #10,一本正经的答非所问

    可以找一个自动生成 tag 的结构体生成器,第三方包的话就把字段反射解析出来
    dai875939260
        16
    dai875939260  
       2020-06-12 11:47:17 +08:00
    https://github.com/fatih/gomodifytags 修改 tag,第三方的包的可以自己再建一个结构体,然后用 mapstructure 转换?
    wangyzj
        17
    wangyzj  
       2020-06-12 11:47:18 +08:00
    go 这个东西不就是这样
    你都习惯别的了还纠结啥 tag
    scnace
        18
    scnace  
       2020-06-12 11:58:09 +08:00 via Android
    自己实现一个自用 Marshaller ?
    Kisesy
        19
    Kisesy  
       2020-06-12 12:17:48 +08:00
    只需要实现 Marshaler 接口并包裹一下(这篇文章中用正则表达式来处理比较低效,需要优化)
    https://www.cnblogs.com/chenqionghe/p/13067596.html
    mengzhuo
        20
    mengzhuo  
       2020-06-12 12:39:55 +08:00
    https://json.to-go.online
    Hanggi
        21
    Hanggi  
       2020-06-12 12:50:49 +08:00
    @lancelock 我说得很清楚了,Go 是静态语言,不要想着什么 marshal 之类的优雅解决。
    你要做的就是定义一个 response 结构体,把你要返回的数据 mapping 上去。
    你可以用一些工具简化这个操作,但是结果都是一样的。

    如果你觉得麻烦,不想这样,可以考虑使用 Nodejs 这种动态语言。
    lancelock
        22
    lancelock  
    OP
       2020-06-12 13:28:20 +08:00
    @Hanggi 我返回的数据就是个结构体你听不听得懂人话?求你别回我了
    blessyou
        23
    blessyou  
       2020-06-12 13:29:57 +08:00
    那看起来还是转换风格比较 easy 。
    sunxiansong
        24
    sunxiansong  
       2020-06-12 13:58:26 +08:00
    VSCode go 插件有个命令:Go: Add Tags To Struct Fields

    不过缺点是支持 snake_case,不知道在哪里改

    好在我找到 VSCode 有个插件 change-case ,可以方便的在不认风格间切换
    yxlimo
        25
    yxlimo  
       2020-06-12 14:03:50 +08:00 via iPhone
    我觉得问题的点在于,你为什么要依赖第三方包的 struct 生成 response 。这样子是不可控的
    Hanggi
        26
    Hanggi  
       2020-06-12 14:15:34 +08:00
    @lancelock 怪心累的,听不懂人话呢,只说最后一次。

    看你问这个问题就知道你没怎么用过 go 语言。你以为你是这世界上第一个有这种需求的人吗?
    go 语言发展了 10 年为什么找不到你要的所谓的第三方依赖包,因为 go 语言不提倡这么做。

    像 #13 楼说的 reflect 其实就是一种动态判断类型的方法,但是,不要这么做,原因自己查。

    不要什么后置中间件统一处理,如果你非要这么做可能 go 语言不适合你。


    go 语言 json struct 可以嵌套你知道吧?你要做的就是定义好结构体,加好 tag,组合使用他们。
    如果这样还看不懂,你也不用回了。
    labulaka521
        27
    labulaka521  
       2020-06-12 14:34:32 +08:00 via iPhone
    定义 tag ?
    iceiceice
        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)
    }
    BlackBerry999
        29
    BlackBerry999  
       2020-06-12 16:35:55 +08:00
    @iceiceice 秀!
    lancelock
        30
    lancelock  
    OP
       2020-06-12 17:38:45 +08:00
    @iceiceice 可以,给力
    zoharSoul
        31
    zoharSoul  
       2020-12-18 17:57:31 +08:00
    @Hanggi 你这啥理解能力?
    别人已经说得很清楚了, 它定义了 struct.
    而且这和静态 /动态语言有啥关系.
    java 是不是静态? 一样可以指定序列号模式.

    一知半解在这指点江山
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2301 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 15:53 PVG 23:53 LAX 08:53 JFK 11:53
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86