Separate struct from its json logic in golang?

huangapple go评论90阅读模式
英文:

Separate struct from its json logic in golang?

问题

在Go语言中,有一种惯用的方法可以将结构体与其JSON编组逻辑分离吗?

通常情况下:

package models

type Foo struct {
  Name  `json:"full_name"`
}

但是我想要关注点分离。我不希望在models包中的结构体中指定json逻辑,也许可以将json逻辑放在另一个serializers包中。你会如何在Go语言中实现这一点?也许可以类似于rails项目中处理active_model_serializers代码的方式。

英文:

Is there an idiomatic way in go to separate a struct from its json marshal logic?

Normally:

package models

type Foo struct {
  Name  `json:"full_name"`
}

But I want a separation of concerns. I don't want the json specifying logic in the models package with the struct, maybe put the json logic in another serializers package. How would you do that in idiomatic go? Maybe similar to how rails projects handle active_model_serializers code

答案1

得分: 2

json标签是属于编组逻辑还是json模型的问题存在争议。我认为,指定json标签只是描述json模型,因此最好将其放在Go模型旁边。

编组/解组逻辑是在encoding/json包中实现的。如果需要自定义逻辑,可以通过实现json.Marshalerjson.Unmarshaler接口来指定/实现。这意味着需要为类型定义方法。在Go中,只能为同一包中的类型指定方法,因此如果将模型与自定义解析逻辑分开,解析包将无法为模型类型定义方法。规范:方法声明:

> T所表示的类型称为接收器的基本类型;它不能是指针或接口类型,并且必须在同一包中声明。

也就是说,您需要在不同的类型上定义自定义解析逻辑,并且需要进一步的逻辑来映射/复制到模型类型作为解析逻辑的一部分。通过将模型与解析逻辑分开,您将失去更多而不是获得更多。

进一步说,您编组到的结构类型可能包含未导出的字段,如果解析逻辑在同一包中,可以正确初始化这些字段。通过分离模型和逻辑,逻辑将无法初始化未导出的字段。一种方法是提供导出的方法或函数,但这样会将它们导出给其他所有人,而不仅仅是解析逻辑。

我认为Go的方式和最简单的方式是将模型和解析逻辑放在同一个包中。这仍然给您提供了一种“小型”分离的可能性:您可以将类型定义放在一个文件中(例如model.go),并且可以将自定义解析逻辑(实现json.Marshalerjson.Unmarshaler的方法)放在另一个文件中(例如parse.go),当然还是在同一个包中,但是将类型及其所有方法放在一个地方(一个文件中)可能更好。

英文:

It is arguable whether json tags belong to marshaling logic or to json model. I would say specifying json tags just describe the json model and as such it may be better residing next to your Go model.

The marshaling / unmarshaling logic is implemented in the encoding/json package itself. If you need custom logic, you can specify / implement that by implementing the json.Marshaler and json.Unmarshaler interfaces. This means defining methods to your type. In Go you can only specify methods to types being in the same package, so if you would separate your model from your custom parsing logic, the parsing package could not define methods to model types. Spec: Method declarations:

> The type denoted by T is called the receiver base type; it must not be a pointer or interface type and it must be declared in the same package as the method.

That being said you would need to define your custom parsing logic on a different type, and you would need further logic to map / copy into the model type as part of the parsing logic. You would lose more than you would gain by separating the model from the parsing logic.

Going further, the struct type you marshal into may contain unexported fields which - if the parsing logic is in the same package - can be initialized properly. By separating model and logic, the logic would have troubles initializing unexported fields. One way would be to provide exported methods or functions, but then those are exported to everyone else, not just for the parsing logic.

I'd say the Go way and the easiest way is to put model and parsing logic into the same package. It still gives you a "small" separation possibility: you may put the type definition in one file (e.g. model.go), and you may put custom parsing logic (methods implementing json.Marshaler and json.Unmarshaler) in another file (e.g. parse.go) but in the same package of course, but it may be better to see a type and all its methods in one place (in one file).

huangapple
  • 本文由 发表于 2016年1月15日 15:12:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/34805964.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定