Lets say I have:
type IObject interface {
}
type Item struct {
Description string
Data []byte
}
type FunctionX1 struct {
Object IInclusionObject
}
type FunctionX2 struct {
Object1 IInclusionObject
Object2 IInclusionObject
}
I would like to be able to serialization/deserialization a model, where Item
, FunctionX1
FunctionX2
they all implement IObject
and they could be pointing to each other arbitrarily deep.
Note that I do not want: FunctionX1{Item{"foo", []byte("bar")}}
to be serialized as:
"object": {
"Description": "foo"
"Data": ...
}
but rather:
"FunctionX1": {
"item": {
"Description": "foo"
"Data": ...
}
}
Do I need to do my own JSON marshaller - it does not seem that I can use the existing one.
Related questions in case I need my own. Is there JSON prettifier where I can stream valid but randomly formatted JSON and it to output pretty version (note the JSON could be significantly large - I do not want to generate, parse generate formatted).
You may use struct tags to map fields of structs to different names in/from JSON. So with this you can change "Object"
to "item"
:
type FunctionX1 struct {
Object IInclusionObject `json:"item"`
}
But you still need a wrapper struct or map if you want "FunctionX1"
to appear in the JSON text. For example:
f := FunctionX1{Item{"foo", []byte("bar")}}
if data, err := json.Marshal(map[string]interface{}{"FunctionX1": f}); err != nil {
panic(err)
} else {
fmt.Println(string(data))
}
Output:
{"FunctionX1":{"item":{"Description":"foo","Data":"YmFy"}}}
Or with a wrapper struct:
type Wrapper struct {
FunctionX1 FunctionX1
}
f := FunctionX1{Item{"foo", []byte("bar")}}
if data, err := json.Marshal(Wrapper{f}); err != nil {
panic(err)
} else {
fmt.Println(string(data))
}
Output is the same:
{"FunctionX1":{"item":{"Description":"foo","Data":"YmFy"}}}
If you want pretty formatted JSON, you may use json.MarshalIndent()
to do the marshaling:
if data, err := json.MarshalIndent(Wrapper{f}, "", " "); err != nil {
panic(err)
} else {
fmt.Println(string(data))
}
Output:
{
"FunctionX1": {
"item": {
"Description": "foo",
"Data": "YmFy"
}
}
}
Try all the examples on the Go Playground.
If you don't want to utilize a wrapper struct or map, you need to use custom marshalling, but it's quite simple:
type FunctionX1 struct {
Object IInclusionObject `json:"item"`
}
func (f FunctionX1) MarshalJSON() ([]byte, error) {
type FunctionX1_ FunctionX1
return json.Marshal(map[string]interface{}{"FunctionX1": FunctionX1_(f)})
}
Effectively we moved the wrapping into the MarshalJSON()
method, so others marshaling values of FunctionX1
don't have to.
Testing it:
f := FunctionX1{Item{"foo", []byte("bar")}}
if data, err := json.Marshal(f); err != nil {
panic(err)
} else {
fmt.Println(string(data))
}
if data, err := json.MarshalIndent(f, "", " "); err != nil {
panic(err)
} else {
fmt.Println(string(data))
}
Note that the new FunctionX1_
type inside MarshalJSON()
is to avoid infinite "recursion".
Output:
{"FunctionX1":{"item":{"Description":"foo","Data":"YmFy"}}}
{
"FunctionX1": {
"item": {
"Description": "foo",
"Data": "YmFy"
}
}
}
Try this one on the Go Playground.