Golang Go语言中 JSON 序列化要单独定义一个 struct 吗?
最近业务上遇到一些场景产生的疑问:
假如说我有一套领域模型,定义在 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
字段写注解,序列化为 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: &User{<br> Username: "admin",<br> Password: "123456",<br> },<br> PasswordHash: "123456",<br> })<br>}<br><br>
#3
未导出字段可以用第三方包,例如 https://github.com/json-iterator/go/blob/master/extra/privat_fields.go#L10
意思是不要把结构体严格等同于其他语言中的类,在需要时创建匿名结构体来满足需要就可以了吗?
#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 主要取决于你的具体需求和场景。
-
使用 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)
-
不使用 struct 序列化: 对于简单或临时的数据,可以使用 map[string]interface{} 或其他基本类型直接序列化。这种方式灵活但缺乏结构化的清晰性,适合快速原型开发或字段较少的情况。
data := map[string]interface{}{ "name": "Bob", "age": 25, } jsonData, _ := json.Marshal(data)
总之,如果数据结构复杂且需要清晰表达,建议定义 struct。如果数据结构简单或临时,可以考虑使用 map。选择哪种方式取决于你的具体需求和数据结构的复杂性。