在Golang中序列化模型

I'm trying to separate my code into models and serializers with the idea that there be defined serializers that handles all json responsibilities, i.e. separation of concerns. I also want to be able to call a model object obj.Serialize() to get the serializer struct obj that I can then marshal. Therefore, I've come up with the following design. To avoid circular import I had to use interfaces in my serializers which leads to using getters in my models. I've read that getters/setters aren't idiomatic go code and I would prefer not to have "boilerplate" getter code all over my models. Is there a better solution to what I want to accomplish, keeping in mind I want separation of concerns and obj.Serialize()?

src/
    models/
        a.go
    serializers/
        a.go

models/a.go

import "../serializers"

type A struct {
  name string
  age int // do not marshal me
}

func (a *A) Name() string {
  return a.name
}

// Serialize converts A to ASerializer
func (a *A) Serialize() interface{} {
    s := serializers.ASerializer{}
    s.SetAttrs(a)
    return s
}

serializers/a.go

// AInterface used to get Post attributes
type AInterface interface {
  Name() string
}

// ASerializer holds json fields and values
type ASerializer struct {
  Name `json:"full_name"`
}

// SetAttrs sets attributes for PostSerializer
func (s *ASerializer) SetAttrs(a AInterface) {
  s.Name = a.Name()
}

It looks like you are actually trying to translate between your internal structs and json. We can start by taking advantage of the json library.

If you want certain libraries to handle your struct fields in certain ways, there are tags. This example shows how json tags tell json to never marshal the field age into json, and to only add the field jobTitle if it is not empty, and that the field jobTitle is actually called title in json. This renaming feature is very useful when structs in go contain capitalized (exported) fields, but the json api you're connecting to uses lowercase keys.

type A struct {
  Name     string
  Age      int `json:"-"`// do not marshal me
  location      string // unexported (private) fields are not included in the json marshal output
  JobTitle string `json:"title,omitempty"` // in our json, this field is called "title", but we only want to write the key if the field is not empty.
}

If you need to precompute a field, or simply add a field in your json output of a struct that isn't a member of that struct, we can do that with some magic. When json objects are decoded again into golang structs, fields that don't fit (after checking renamed fields and capitalization differences) are simply ignored.

// AntiRecursionMyStruct avoids infinite recursion in MashalJSON. Only intended for the json package to use.
type AntiRecursionMyStruct MyStruct

// MarshalJSON implements the json.Marshaller interface. This lets us marshal this struct into json however we want. In this case, we add a field and then cast it to another type that doesn't implement the json.Marshaller interface, and thereby letting the json library marshal it for us.
func (t MyStruct) MarshalJSON() ([]byte, error) {
    return json.Marshal(struct {
        AntiRecursionMyStruct
        Kind string // the field we want to add, in this case a text representation of the golang type used to generate the struct
    }{
        AntiRecursionMyStruct: AntiRecursionMyStruct(t),
        Kind: fmt.Sprintf("%T", MyStruct{}),
    })
}

Keep in mind that json will only include your exported (capitalized) struct members. I've made this misstake multiple times.

As a general rule, if something seems too complicated, there's probably a better way to do it.