编组类型时如何将方法结果嵌入 JSON 输出?
How to embed a method result into JSON output when marshalling a type?
我正在寻找一种干净的方法来将方法的 return 值嵌入到 type/value 的 JSON 编组中。
如果我不需要编写自定义 JSON 编组器,那就太好了。
例如,如果用户类型有 FirstName
和 LastName
字段以及 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"
}
我不确定它是否是 "nicest" 的方法,但它非常简单。
func (u User) FullNameMarshal() []byte {
u.FullName = u.FirstName + " " + u.LastName
uj, err := json.Marshal(&u)
if err != nil {
fmt.Println(err)
}
return uj
}
您可以创建一个新类型并将其编码为 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))
}
如果我们能让 User 实现 json.Marshaller
并让 User.MarshalJSON()
在幕后创建一个 UserForJSON
对象就好了,但这会导致无限递归。
如果不提供 some 编组器,则无法轻松处理。我知道您不想编写 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 的特殊非标准方法。
我正在寻找一种干净的方法来将方法的 return 值嵌入到 type/value 的 JSON 编组中。 如果我不需要编写自定义 JSON 编组器,那就太好了。
例如,如果用户类型有 FirstName
和 LastName
字段以及 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"
}
我不确定它是否是 "nicest" 的方法,但它非常简单。
func (u User) FullNameMarshal() []byte {
u.FullName = u.FirstName + " " + u.LastName
uj, err := json.Marshal(&u)
if err != nil {
fmt.Println(err)
}
return uj
}
您可以创建一个新类型并将其编码为 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))
}
如果我们能让 User 实现 json.Marshaller
并让 User.MarshalJSON()
在幕后创建一个 UserForJSON
对象就好了,但这会导致无限递归。
如果不提供 some 编组器,则无法轻松处理。我知道您不想编写 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 的特殊非标准方法。