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

时间:2015-05-16 08:05:48

标签: json struct go marshalling

我正在寻求一种干净的方法,将方法的返回值嵌入到类型/值的JSON编组中。 如果我不需要编写自定义JSON marshaller,那就太棒了。

例如,如果用户类型包含FirstNameLastName字段以及FullName()方法,那么如何轻松地将full_name字段嵌入到JSON输出中?

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

预期的JSON:

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

3 个答案:

答案 0 :(得分:5)

如果不提供某些编组程序,则无法轻松处理。我知道您不想编写MarshalJSON并手动执行所有操作,但您可以尝试在自定义编组器中扩展您的结构,而不是依赖于默认编组器。概念证明:

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]

您需要将User强制转换为rawUser以删除所有方法 - 否则您将拥有MarshalJSON的无限循环。另外,我选择MarshalJSON来操作副本而不是指针,以确保json.Marshal(user)产生与json.Marshal(&user)相同的结果。

这不是一个单行,但隐藏了标准界面背后的复杂性,因此您不需要记住那种将您的结构转换为JSON的特殊非标准方式。

答案 1 :(得分:3)

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

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

}

Playground link

如果我们可以让用户实现json.Marshaller,并让User.MarshalJSON()在底层创建一个UserForJSON对象,那会很好,但这会导致无限递归。

答案 2 :(得分:0)

我不确定它是否是最好的"这样做的方法很简单。

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