如何在类型编组时将方法结果嵌入JSON输出中?

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

How to embed a method result into JSON output when marshalling a type?

问题

我正在寻找一种简洁的方法,将方法的返回值嵌入到类型/值的JSON编组中。如果不需要编写自定义的JSON编组器,那就太好了。

例如,如果User类型有FirstNameLastName字段以及一个FullName()方法,我如何轻松地将full_name字段嵌入到JSON输出中?

  1. type User struct {
  2. FirstName string `json:"first_name"`
  3. LastName string `json:"last_name"`
  4. }
  5. func (u User) FullName() string {
  6. return fmt.Sprintf("%s %s", u.FirstName, u.LastName)
  7. }

期望的JSON输出:

  1. {
  2. "first_name": "John",
  3. "last_name": "Smith",
  4. "full_name": "John Smith"
  5. }
英文:

I'm seeking a clean approach to embed the return value of a method into the JSON marshalling of a type/value.
It would be great if I don't need to write custom JSON marshaller.

For example, if the User type has FirstName and LastName fields and a FullName() method, how can I easily embed a full_name field into JSON output?

  1. type User struct {
  2. FirstName string `json:"first_name"`
  3. LastName string `json:"last_name"`
  4. }
  5. func (u User) FullName() string {
  6. return fmt.Sprintf("%s %s", u.FirstName, u.LastName)
  7. }

Expected JSON:

  1. {
  2. "first_name": "John",
  3. "last_name": "Smith",
  4. "full_name": "John Smith"
  5. }

答案1

得分: 6

这个问题不能简单地处理,除非提供一些编组器。我理解你不想编写MarshalJSON并手动完成所有操作,但你可以尝试在自定义编组器中扩展你的结构,然后依赖默认的编组器。概念验证:

  1. type User struct {
  2. FirstName string `json:"first_name"`
  3. LastName string `json:"last_name"`
  4. }
  5. func (u *User) FullName() string {
  6. return fmt.Sprintf("%s %s", u.FirstName, u.LastName)
  7. }
  8. func (u User) MarshalJSON() ([]byte, error) {
  9. type rawUser User // raw struct, without methods (and marshaller)
  10. // 使用扩展的 rawUser 进行编组
  11. return json.Marshal(struct {
  12. rawUser
  13. FullName string `json:"full_name"`
  14. }{rawUser(u), u.FullName()})
  15. }

你需要将 User 转换为 rawUser 来去除所有方法,否则会导致MarshalJSON的无限循环。此外,我选择在MarshalJSON中操作副本而不是指针,以确保json.Marshal(user)json.Marshal(&user)产生相同的结果。

虽然这不是一行代码,但它隐藏了复杂性,使用了标准接口,因此你不需要记住有一种特殊的非标准方法将你的结构转换为JSON。

英文:

This cannot be easily handled without providing some marshaller. I understand you don't want to write a MarshalJSON and do everything manually, but you can try to extend your structure in the custom marshaller and than rely on the default one. Proof of concept:

  1. type User struct {
  2. FirstName string `json:"first_name"`
  3. LastName string `json:"last_name"`
  4. }
  5. func (u *User) FullName() string {
  6. return fmt.Sprintf("%s %s", u.FirstName, u.LastName)
  7. }
  8. func (u User) MarshalJSON() ([]byte, error) {
  9. type rawUser User // raw struct, without methods (and marshaller)
  10. // Marshal rawUser with an extension
  11. return json.Marshal(struct {
  12. rawUser
  13. FullName string `json:"full_name"`
  14. }{rawUser(u), u.FullName()})
  15. }

[play]

You need to cast User to rawUser to strip all methods – otherwise you would have infinite loop of MarshalJSON. Also I've chosen MarshalJSON to operate on copy rather than pointer to make sure json.Marshal(user) will yield the same result as json.Marshal(&user).

This is not a one liner, but hides the complexity behind a standard interface, so you don't need to remember there's a special, non-standard way of converting your structure to JSON.

答案2

得分: 3

你可以创建一个新类型并将其编码为JSON。如果你包含一个类型为*User的匿名字段,这两个字段会合并:

  1. type UserForJSON struct {
  2. *User
  3. FullName string `json:"full_name"`
  4. }
  5. func NewUserForJSON(u *User) *UserForJSON {
  6. return &UserForJSON{u, u.FullName()}
  7. }
  8. func main() {
  9. u := &User{"John", "Smith"}
  10. j, _ := json.Marshal(NewUserForJSON(u))
  11. fmt.Print(string(j))
  12. }

Playground链接

如果我们让User实现json.Marshaller,并让User.MarshalJSON()在内部创建一个UserForJSON对象,那将导致无限递归,所以这样做是不可行的。

英文:

You can create a new type and encode that to JSON. If you include an anonymous field of type *User, the two get merged:

  1. type UserForJSON struct {
  2. *User
  3. FullName string `json:"full_name"`
  4. }
  5. func NewUserForJSON(u *User) *UserForJSON {
  6. return &UserForJSON{u, u.FullName()}
  7. }
  8. func main() {
  9. u := &User{"John", "Smith"}
  10. j, _ := json.Marshal(NewUserForJSON(u))
  11. fmt.Print(string(j))
  12. }

Playground link.

It would be nice if we could let User implement json.Marshaller instead, and let User.MarshalJSON() create a UserForJSON object under the hood, but that leads to infinite recursion.

答案3

得分: 0

我不确定这是否是最好的方法,但它非常简单。

  1. func (u User) FullNameMarshal() []byte {
  2. u.FullName = u.FirstName + " " + u.LastName
  3. uj, err := json.Marshal(&u)
  4. if err != nil {
  5. fmt.Println(err)
  6. }
  7. return uj
  8. }

GO PLAYGROUND

英文:

Im not sure if its the "nicest" way to do it but it`s very simple one.

  1. func (u User) FullNameMarshal() []byte {
  2. u.FullName = u.FirstName + " " + u.LastName
  3. uj, err := json.Marshal(&u)
  4. if err != nil {
  5. fmt.Println(err)
  6. }
  7. return uj
  8. }

GO PLAYGROUND

huangapple
  • 本文由 发表于 2015年5月16日 16:05:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/30273286.html
匿名

发表评论

匿名网友

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

确定