3

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).

1 Answer 1

2

With Wrappers (struct or map)

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.

With Custom Marshaling

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, @icza. Custom marshaling seems very close to what I want, with one exception.The object that is an argument to FinctionX1 might not be an item but another FunctionX1 or FunctionX2 in which case the sub-item should not be called item. I tried to apply the same custom marshaler to item too, but I am getting one extra level there: play.golang.org/p/Op2tg1LOtZ. Ideas how I can get rid of it?
Actually, I think I figured it out play.golang.org/p/6lVxnjnyit

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.