英文:
Can I use MarshalJSON to add arbitrary fields to a json encoding in golang?
问题
假设我已经写了以下代码片段。点击这里查看完整代码。
type Book struct {
Title string
Author string
}
func main() {
ms := Book{"Catch-22", "Joseph Heller"}
out, err := json.MarshalIndent(ms, "", " ")
if err != nil {
log.Fatalln(err)
}
fmt.Println(string(out))
}
这段代码的输出结果如下,正如我所期望的那样:
{
"Title": "Catch-22",
"Author": "Joseph Heller"
}
假设我想在JSON输出中添加一个字段,但不在Book
结构体中包含它,比如一个genre(类型):
{
"Title": "Catch-22",
"Author": "Joseph Heller",
"Genre": "Satire"
}
我可以使用MarshalJSON()
在Marshal()
过程中添加一个任意的字段吗?类似这样:
func (b *Book) MarshalJSON() ([]byte, error) {
// 一些代码
}
其他的答案让我觉得这是可能的,但我还在努力弄清楚具体的实现方法。
英文:
Suppose I've written the following code snippet. Full code on the playground here for those inclined.
type Book struct {
Title string
Author string
}
func main() {
ms := Book{"Catch-22", "Joseph Heller"}
out, err := json.MarshalIndent(ms, "", " ")
if err != nil {
log.Fatalln(err)
}
fmt.Println(string(out))
}
This code outputs the following, exactly as I'd expect:
{
"Title": "Catch-22",
"Author": "Joseph Heller"
}
Suppose for a moment I wanted to add a field to the JSON output without including it in the Book
struct. Perhaps a genre:
{
"Title": "Catch-22",
"Author": "Joseph Heller",
"Genre": "Satire"
}
Can I use MarshalJSON()
to add an arbitrary field to the JSON payload on Marshal()
? Something like:
func (b *Book) MarshalJSON() ([]byte, error) {
// some code
}
Other answers make me think this should be possible, but I'm struggling to figure out the implementation.
答案1
得分: 42
以下是翻译好的内容:
这是比我之前的回答更好的答案。
type FakeBook Book
func (b Book) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
FakeBook
Genre string
}{
FakeBook: FakeBook(b),
Genre: "Satire",
})
}
由于匿名结构字段是“合并”的(还有一些其他考虑因素),我们可以利用这一点避免重新映射各个字段。请注意使用FakeBook
类型以避免否则会发生的无限递归。
Playground: http://play.golang.org/p/21YXhB6OyC
英文:
Here's a better answer than my previous one.
type FakeBook Book
func (b Book) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
FakeBook
Genre string
}{
FakeBook: FakeBook(b),
Genre: "Satire",
})
}
Since anonymous struct fields are "merged" (with a few additional considerations) we can use that to avoid remapping the individual fields. Note the use of the FakeBook
type to avoid the infinite recursion which would otherwise occur.
Playground: http://play.golang.org/p/21YXhB6OyC
答案2
得分: 4
这个问题的一个可能答案是使用结构体字面量(代码在这里),尽管我希望有一个更通用的解决方案,不需要重新映射结构体的所有字段:
func (b *Book) MarshalJSON() ([]byte, error) {
return json.Marshal(struct{
Title string
Author string
Genre string
} {
Title: b.Title,
Author: b.Author,
Genre: "Satire",
})
}
英文:
One possible answer to this question is a struct literal (code here), although I'm hoping for something a bit more general, which doesn't require remapping all of the struct's fields:
func (b *Book) MarshalJSON() ([]byte, error) {
return json.Marshal(struct{
Title string
Author string
Genre string
} {
Title: b.Title,
Author: b.Author,
Genre: "Satire",
})
}
答案3
得分: 4
编组(Marshalling)一个map
是解决这个问题的另一种方法。
tmap := make(map[string]interface{})
tmap["struct"] = struct {
StructValue string `json:"struct_value"`
}{
"Value 02",
}
tmap["string"] = "Value 01"
out, err := json.MarshalIndent(tmap, "", " ")
if err != nil {
log.Fatalln(err)
}
fmt.Println(string(out))
这将输出:
{
"string": "Value 01",
"struct": {
"struct_value": "Value 02"
}
}
当你有许多任意的键名时,这可能是一个很好的解决方案。
Playground: https://play.golang.org/p/Umy9rtx2Ms
英文:
Marshalling a map
is another way around the problem.
<!-- language: lang-golang -->
tmap := make(map[string]interface{})
tmap["struct"] = struct {
StructValue string `json:"struct_value"`
}{
"Value 02",
}
tmap["string"] = "Value 01"
out, err := json.MarshalIndent(tmap, "", " ")
if err != nil {
log.Fatalln(err)
}
fmt.Println(string(out))
This will output:
<!-- language: lang-json -->
{
"string": "Value 01",
"struct": {
"struct_value": "Value 02"
}
}
Where you have many arbitrary key names this could be a good solution.
Playground: https://play.golang.org/p/Umy9rtx2Ms
答案4
得分: 3
这是一种处理方法:
type Object struct {
A string
B int
Extra map[string]interface{} `json:"-"`
}
func (o Object) MarshalJSON() ([]byte, error) {
type Object_ Object
b, err := json.Marshal(Object_(o))
if err != nil {
return nil, err
}
if o.Extra == nil || len(o.Extra) == 0 {
return b, nil
}
m, err := json.Marshal(o.Extra)
if err != nil {
return nil, err
}
if len(b) == 2 {
return m, nil
} else {
b[len(b)-1] = ','
return append(b, m[1:]...), nil
}
}
你可以向 Extra
map 添加任何额外的字段,它们将以不嵌套的方式出现在输出中。
英文:
This is one method of handling it:
type Object struct {
A string
B int
Extra map[string]interface{} `json:"-"`
}
func (o Object) MarshalJSON() ([]byte, error) {
type Object_ Object
b, err := json.Marshal(Object_(o))
if err != nil {
return nil, err
}
if o.Extra == nil || len(o.Extra) == 0 {
return b, nil
}
m, err := json.Marshal(o.Extra)
if err != nil {
return nil, err
}
if len(b) == 2 {
return m, nil
} else {
b[len(b)-1] = ','
return append(b, m[1:]...), nil
}
}
You can add whatever additional fields to the Extra
map and they will appear without nesting in the output.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论