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

发布于 1周前 作者 h691938207 来自 Go语言

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

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

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

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

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


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

更多关于Golang Go语言中 JSON 序列化要单独定义一个 struct 吗?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html

14 回复

字段写注解,序列化为 json/yaml 时重新命名

更多关于Golang Go语言中 JSON 序列化要单独定义一个 struct 吗?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html





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

我的领域模型中属性默认都是非导出的(即小写字母开头),这种情况下没法直接添加 json tag 来做序列化,所以我很纠结,是为了序列化将属性改为 public ,还是应该定义一个新的 struct 用来做序列化。

对于结构体的玩法,其实就是 Golang 中的组合与嵌套,和面向对象截然不同。

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

- 比如最常见的,需要在序列化时忽略一个字段
golang<br>type User struct {<br> Username string<br> Password string<br>}<br><br>func Marshal() {<br> json.Marshal(struct {<br> *User<br> Password string `json:"-"`<br> }{})<br>}<br><br>

- 添加额外的字段
golang<br>type User struct {<br> Username string<br> Password string<br>}<br><br>func Marshal() {<br> json.Marshal(struct {<br> *User<br> Token bool<br> }{})<br>}<br><br>

- 字段改名
golang<br>type User struct {<br> Username string<br> Password string<br>}<br><br>func Marshal() {<br> json.Marshal(struct {<br> *User<br> Password string `json:"-"`<br> PasswordHash string<br> }{<br> User: &amp;User{<br> Username: "admin",<br> Password: "123456",<br> },<br> PasswordHash: "123456",<br> })<br>}<br><br>

意思是不要把结构体严格等同于其他语言中的类,在需要时创建匿名结构体来满足需要就可以了吗?

#6 呃呃呃,也不是这个意思。

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

代码组织良好的情况下,Golang 的组合能让代码看起来更舒适。也会带来更多的问题,例如,如果你需要写 API 文档,用 swagger 的话,你会发现匿名结构体就是灾难。所以我选择不用 swagger 甚至不写 API

和 OP 组织代码的方式相同,但是 struct 里的字段都是大写,每个模型都有 DTO 、DO 和 VO ,

感谢,我再尝试一下哪种方案更好用吧。

全大写的话意味着破坏了封装性,不过如 Python 之类的语言同样也淡化了这方面的限制,我不太清楚这样做是否会带来什么问题。

我现在用的方案是 request 和 entity 区分开,复杂的 response 自定义 Marshal 方法

不过现在从 gorm 转到 ent ,或许可以考虑自定义 Annotation 和 Template 的方案

首先这个要看你的实践原则是如何的。如果是 API (Design) First 方式,request/response 是一定需要单的结构体进行,甚至还需要 json schema 验证避免带来的破坏性更新。

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

我个人建议团队的实践是根据不同的作用域使用单独的模型,也就是 Java 中常说的 DTO 之类的概念。这样可以有效的把风险隔绝在自己的作用域中,有效防止一次修改到处救火的问题。





谢谢各位回复,最后在翻阅标准库的注释时找到了比较官方的解决方案:实现 Marshaler 和 Unmarshaler 接口即可,也就是说为每个 struct 定义 MarshalJSON 和 UnmarshalJSON 方法,之后即可使用标准库 json 进行自定义的序列化和反序列化,也解决了私有属性的序列化问题。

在 Go 语言(Golang)中处理 JSON 序列化时,是否需要单独定义一个 struct 主要取决于你的具体需求和场景。

  1. 使用 struct 序列化: 定义一个 struct 是最常见和推荐的方式。struct 提供了清晰的数据结构,并且 Go 的标准库 encoding/json 能够轻松地将 struct 序列化为 JSON 字符串,或从 JSON 字符串反序列化为 struct。这种方式非常适合需要结构化数据且字段较多或较复杂的情况。

    type Person struct {
        Name string `json:"name"`
        Age  int    `json:"age"`
    }
    
    p := Person{Name: "Alice", Age: 30}
    jsonData, _ := json.Marshal(p)
    
  2. 不使用 struct 序列化: 对于简单或临时的数据,可以使用 map[string]interface{} 或其他基本类型直接序列化。这种方式灵活但缺乏结构化的清晰性,适合快速原型开发或字段较少的情况。

    data := map[string]interface{}{
        "name": "Bob",
        "age":  25,
    }
    jsonData, _ := json.Marshal(data)
    

总之,如果数据结构复杂且需要清晰表达,建议定义 struct。如果数据结构简单或临时,可以考虑使用 map。选择哪种方式取决于你的具体需求和数据结构的复杂性。

回到顶部