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"
}
source to share
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.
source to share
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))
}
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.
source to share