Derek
Derek

Reputation: 12378

Separate struct from its json logic in golang?

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

Upvotes: 2

Views: 479

Answers (1)

icza
icza

Reputation: 417412

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).

Upvotes: 2

Related Questions