Go 语言的 JSON 序列化要单独定义一个 struct 吗? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
lanlanye
V2EX    Go 编程语言

Go 语言的 JSON 序列化要单独定义一个 struct 吗?

  •  
  •   lanlanye
    laipz8200 2022-05-04 18:07:11 +08:00 3117 次点击
    这是一个创建于 1309 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近业务上遇到一些场景产生的疑问:

    假如说我有一套领域模型,定义在 domain/model ,一个模型就是一个 struct ,上面绑定了用于业务的函数(方法)

    但这些模型持久化的时候在数据库里可能是另一种格式,这就可能需要另一套模型(数据库模型),可能在 db/model

    然后开始写接口,然而 request 和 response 又是另一套格式,那这里是不是又需要单独定义一组 struct 用来序列化我的 domain/model ?** 假如 response 结构和原本的 model 只有很小的区别,也需要定义一个新的 struct 用来做序列化吗? **

    类似这样的代码一般是怎么组织的?

    ch2
        1
    ch2  
       2022-05-04 18:12:58 +08:00
    字段写注解,序列化为 json/yaml 时重新命名
    honkew
        2
    honkew  
       2022-05-04 18:14:10 +08:00
    注解
    lanlanye
        3
    lanlanye  
    OP
       2022-05-04 18:17:30 +08:00
    @ch2
    @honkew

    写完发现有个地方没讲清楚,想补充但是超过可编辑时间了,情况是这样的:

    我的领域模型中属性默认都是非导出的(即小写字母开头),这种情况下没法直接添加 json tag 来做序列化,所以我很纠结,是为了序列化将属性改为 public ,还是应该定义一个新的 struct 用来做序列化。
    Aoang
        4
    Aoang  
       2022-05-04 18:21:20 +08:00   1
    对于结构体的玩法,其实就是 Golang 中的组合与嵌套,和面向对象截然不同。

    假定你已经知道并使用过 `omitempty` 等等内容

    - 比如最常见的,需要在序列化时忽略一个字段
    ```golang
    type User struct {
    Username string
    Password string
    }

    func Marshal() {
    json.Marshal(struct {
    *User
    Password string `json:"-"`
    }{})
    }

    ```

    - 添加额外的字段
    ```golang
    type User struct {
    Username string
    Password string
    }

    func Marshal() {
    json.Marshal(struct {
    *User
    Token bool
    }{})
    }

    ```

    - 字段改名
    ```golang
    type User struct {
    Username string
    Password string
    }

    func Marshal() {
    json.Marshal(struct {
    *User
    Password string `json:"-"`
    PasswordHash string
    }{
    User: &User{
    Username: "admin",
    Password: "123456",
    },
    PasswordHash: "123456",
    })
    }

    ```
    Aoang
        5
    Aoang  
       2022-05-04 18:23:56 +08:00
    lanlanye
        6
    lanlanye  
    OP
       2022-05-04 18:24:47 +08:00
    @Aoang 意思是不要把结构体严格等同于其他语言中的类,在需要时创建匿名结构体来满足需要就可以了吗?
    Aoang
        7
    Aoang  
       2022-05-04 18:31:48 +08:00
    @lanlanye #6 呃呃呃,也不是这个意思。

    Golang 写业务算是很麻烦的... 意思其实是,有很多办法可以解决这个问题,但是并不是说那就是最佳实践。
    至于类的问题,Golang 的结构体真和类差距很大。个人也不喜欢类,或者说不喜欢 Java 中的类。

    代码组织良好的情况下,Golang 的组合能让代码看起来更舒适。也会带来更多的问题,例如,如果你需要写 API 文档,用 swagger 的话,你会发现匿名结构体就是灾难。所以我选择不用 swagger 甚至不写 API
    lessMonologue
        8
    lessMonologue  
       2022-05-04 19:37:08 +08:00
    和 OP 组织代码的方式相同,但是 struct 里的字段都是大写,每个模型都有 DTO 、DO 和 VO ,
    lanlanye
        9
    lanlanye  
    OP
       2022-05-04 20:00:05 +08:00
    @Aoang 感谢,我再尝试一下哪种方案更好用吧。

    @lessMonologue 全大写的话意味着破坏了封装性,不过如 Python 之类的语言同样也淡化了这方面的限制,我不太清楚这样做是否会带来什么问题。
    TinyKube
        10
    TinyKube  
       2022-05-04 20:29:26 +08:00 via iPhone
    我现在用的方案是 request 和 entity 区分开,复杂的 response 自定义 Marshal 方法
    TinyKube
        11
    TinyKube  
       2022-05-04 20:32:49 +08:00 via iPhone
    @TinyKube 不过现在从 gorm 转到 ent ,或许可以考虑自定义 Annotation 和 Template 的方案
    janxin
        12
    janxin  
       2022-05-04 20:42:44 +08:00
    首先这个要看你的实践原则是如何的。如果是 API (Design) First 方式,request/response 是一定需要单的结构体进行,甚至还需要 json schema 验证避免带来的破坏性更新。

    如果没有这个前提,API 字段随时可能面对各种变化,那么这个前提下使用相同结构体自然没有什么问题。但是这随时可能带来潜在的破坏性更新。不过这种方式缺点也很明显,会给其他人明显的前置条件阻碍,可能产生臃肿的 API 字段(无用的结果输出),缺少文档之类的。

    我个人建议团队的实践是根据不同的作用域使用单独的模型,也就是 Java 中常说的 DTO 之类的概念。这样可以有效的把风险隔绝在自己的作用域中,有效防止一次修改到处救火的问题。
    lanlanye
        13
    lanlanye  
    OP
       2022-05-04 20:45:30 +08:00   1
    @Aoang
    @lessMonologue
    @TinyKube

    谢谢各位回复,最后在翻阅标准库的注释时找到了比较官方的解决方案:实现 Marshaler 和 Unmarshaler 接口即可,也就是说为每个 struct 定义 MarshalJSON 和 UnmarshalJSON 方法,之后即可使用标准库 json 进行自定义的序列化和反序列化,也解决了私有属性的序列化问题。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3187 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 11:24 PVG 19:24 LAX 03:24 JFK 06:24
    Do have faith in what you're doing.
    ubao msn 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