How to insert method result into JSON output when sorting type?

I'm looking for a clean approach for embedding the return value of a method in a JSON type / value collation. It would be great if I didn't have to write a custom JSON marshaller.

For example, if the user type has fields FirstName

and LastName

and a method FullName()

, how can I easily insert the field full_name

into the JSON output?

 type User struct {
     FirstName string `json:"first_name"`
     LastName  string `json:"last_name"`
 }

 func (u User) FullName() string {
     return fmt.Sprintf("%s %s", u.FirstName, u.LastName)
 }

      

Expected JSON:

 {
     "first_name": "John",
     "last_name":  "Smith",
     "full_name":  "John Smith"
 }

      

+3


source to share


3 answers


This cannot be handled with ease without the provision of some kind of marshaller. I understand that you don't want to write MarshalJSON

and do everything by hand, but you can try to extend your framework in a custom marshaler rather than relying on the default. Proof of Concept:

type User struct {
    FirstName string `json:"first_name"`
    LastName  string `json:"last_name"`
}

func (u *User) FullName() string {
    return fmt.Sprintf("%s %s", u.FirstName, u.LastName)
}

func (u User) MarshalJSON() ([]byte, error) {
    type rawUser User // raw struct, without methods (and marshaller)       
    // Marshal rawUser with an extension
    return json.Marshal(struct {
        rawUser
        FullName string `json:"full_name"`
    }{rawUser(u), u.FullName()})
}

      

[ play ]



You need to drop User

before rawUser

in order to remove all methods - otherwise you will end up with an infinite loop MarshalJSON

. Also I chose MarshalJSON

to work with a copy rather than a pointer to make sure it json.Marshal(user)

gives the same result as json.Marshal(&user)

.

It's not one liner, but it hides the complexity of the standard interface, so you don't have to remember about a special, non-standard way to convert your structure to JSON.

+5


source


You can create a new type and encode it to JSON. If you include an anonymous type field *User

, they are concatenated:

type UserForJSON struct {
    *User
    FullName string `json:"full_name"`
}

func NewUserForJSON(u *User) *UserForJSON {
    return &UserForJSON{u, u.FullName()}
}

func main() {
    u := &User{"John", "Smith"}
    j, _ := json.Marshal(NewUserForJSON(u))
    fmt.Print(string(j))

}

      



link for the playground .

It would be nice if we could let User implement it json.Marshaller

instead and let it User.MarshalJSON()

create an object UserForJSON

under the hood, but that leads to infinite recursion.

+3


source


I'm not sure if this is the "nicest" way to do it, but it's very simple.

func (u User) FullNameMarshal() []byte {
    u.FullName = u.FirstName + " " + u.LastName
    uj, err := json.Marshal(&u)
    if err != nil {
        fmt.Println(err)
    }
    return uj
}

      

GO PLAYGROUND

0


source







All Articles