英文:
How to embed a method result into JSON output when marshalling a type?
问题
我正在寻找一种简洁的方法,将方法的返回值嵌入到类型/值的JSON编组中。如果不需要编写自定义的JSON编组器,那就太好了。
例如,如果User类型有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"
}
英文:
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?
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"
}
答案1
得分: 6
这个问题不能简单地处理,除非提供一些编组器。我理解你不想编写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)
// 使用扩展的 rawUser 进行编组
return json.Marshal(struct {
rawUser
FullName string `json:"full_name"`
}{rawUser(u), u.FullName()})
}
你需要将 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:
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 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
的匿名字段,这两个字段会合并:
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
对象,那将导致无限递归,所以这样做是不可行的。
英文:
You can create a new type and encode that to JSON. If you include an anonymous field of type *User
, the two get merged:
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 json.Marshaller
instead, and let User.MarshalJSON()
create a UserForJSON
object under the hood, but that leads to infinite recursion.
答案3
得分: 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
}
英文:
Im not sure if its the "nicest" way to do it but it`s very simple one.
func (u User) FullNameMarshal() []byte {
u.FullName = u.FirstName + " " + u.LastName
uj, err := json.Marshal(&u)
if err != nil {
fmt.Println(err)
}
return uj
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论