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 · 4315 次点击
    这是一个创建于 1386 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近尝试用 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   ·   我们的愿景   ·   实用小工具   ·   2412 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 15:58 · PVG 23:58 · LAX 08:58 · JFK 11:58
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.