How can I use the same MarshalJSON() method for multiple structs?

2,841 views
Skip to first unread message

Christopher Tiwald

unread,
Apr 22, 2014, 2:55:57 PM4/22/14
to golan...@googlegroups.com
I'm trying to build a complicated, deeply-nested JSON payload. The total number of structs in it numbers in the hundreds, but they can all be sorted into three or four different classifications, each of which need to be marshaled a little bit differently. Specifically, I need to add an arbitrary field to their json encodings, but each classification requires a different field.

I asked a question on StackOverflow last week about adding arbitrary fields, and it got a solid response: http://stackoverflow.com/q/23045884/877115. Playground example here: http://play.golang.org/p/21YXhB6OyC.

The problem is I can't reuse the MarshalJSON() method for anything that isn't a Book:

func (b Book) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
FakeBook
Genre string
}{
FakeBook: FakeBook(b),
Genre:    "Satire",
})
}

I tried pulling the logic in MarshalJSON out to its own function, then letting MarshalJSON() call that function, but it ends up being infinitely recursive (http://play.golang.org/p/ImRmt5V3hd), which makes sense once you poke at the source code of encoding/json/encode.go.

Is there any other way to share the same MarshalJSON() implementation between structs, or perhaps another way to hook into "encoding/json" for this purpose?

Christopher Tiwald

unread,
Apr 22, 2014, 5:23:54 PM4/22/14
to golan...@googlegroups.com
On Tuesday, April 22, 2014 1:55:57 PM UTC-5, Christopher Tiwald wrote:
I'm trying to build a complicated, deeply-nested JSON payload. The total number of structs in it numbers in the hundreds, but they can all be sorted into three or four different classifications, each of which need to be marshaled a little bit differently. Specifically, I need to add an arbitrary field to their json encodings, but each classification requires a different field.

It might help to have a little more context about my use case. I'm actually using the "reflect" package to find the struct's type and shove that into the payload:


Note that the JSON output there is still not quite what I want, because FakeBook is an interface{} in that example, and "encoding/json" treats interfaces differently from embedded structs.

You might reasonably ask, "Why not just use a custom function then, or roll the 'genre' into the original structs or some sort of constructor function?" Totally reasonable questions, and if I end up having to go one of those routes I can. This thing I'm writing, however, is designed to be a library for a bunch of different callers. I'd love for them to not have to think about custom marshaling or default-struct constructors or really anything at all. I'd love for them to just refer to the docs to see which structs they need, create them as they see fit, and issue `json.Marshal()` on the final result, letting the library handle everything else.

Kyle Lemons

unread,
Apr 22, 2014, 8:12:38 PM4/22/14
to Christopher Tiwald, golang-nuts
The simplest thing is probably to walk the struct yourself using reflect, build up a map[string]interface{} and then pass that to json.Marshal.


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Christopher Tiwald

unread,
Apr 23, 2014, 2:41:25 AM4/23/14
to Kyle Lemons, golang-nuts
On Tue, Apr 22, 2014 at 7:12 PM, Kyle Lemons <kev...@google.com> wrote:
The simplest thing is probably to walk the struct yourself using reflect, build up a map[string]interface{} and then pass that to json.Marshal.

That's a very promising avenue. Thanks.

Benny SCETBUN

unread,
Apr 23, 2014, 10:50:46 AM4/23/14
to golan...@googlegroups.com, Kyle Lemons
a easier thing than building map[string]interface{}
I made it for that kind of purpose
Reply all
Reply to author
Forward
0 new messages