Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I use MarshalJSON to add arbitrary fields to a json encoding in golang?

Tags:

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.

like image 453
Christopher Avatar asked Apr 13 '14 17:04

Christopher


People also ask

How do you Unmarshal a JSON?

To parse JSON, we use the Unmarshal() function in package encoding/json to unpack or decode the data from JSON to a struct. Unmarshal parses the JSON-encoded data and stores the result in the value pointed to by v. Note: If v is nil or not a pointer, Unmarshal returns an InvalidUnmarshalError.

How does JSON Unmarshal work Golang?

To unmarshal a JSON array into a slice, Unmarshal resets the slice length to zero and then appends each element to the slice. As a special case, to unmarshal an empty JSON array into a slice, Unmarshal replaces the slice with a new empty slice.

What does Omitempty do in Golang?

Omitempty tag `json:,omitempty` is a json tag for a field in a struct. When unmarshalling json data into a struct, if that particular field is empty, the field would be ignored. Without the omitempty tag, a default value will be used.


2 Answers

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

like image 66
Evan Avatar answered Oct 05 '22 06:10

Evan


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",     }) } 
like image 40
Christopher Avatar answered Oct 05 '22 05:10

Christopher