John Gilmore
John Gilmore

Reputation: 2815

Implementing json marshaller over embedded stuct in Go

I have a struct that I would like to efficiently JSON encode:

type MyStruct struct {
    *Meta
    Contents []interface{}
}

type Meta struct {
    Id int
}

The struct contains meta data of a known form and Contents of an unknown form, The contents list is populated during runtime, so I don't really have control over them. To improve Go's marshalling speed, I would like to implement the Marshaller interface over the Meta struct. The Marshaller interface looks like this:

type Marshaler interface {
        MarshalJSON() ([]byte, error)
}

Please keep in mind that the Meta struct is not as simple as shown here. I've tried implementing the Marshaler interface over the Meta struct, but it seems that when I then JSON marshal MyStruct, the result is only the result returned by the Meta marshalling interface.

So my question is: How can I JSON marshal a struct, that contains en embedded struct with its own JSON marshaller and another struct without one?

Upvotes: 5

Views: 4380

Answers (1)

ANisus
ANisus

Reputation: 77925

Since the MarshalJSON method of the anonymous field *Meta will be promoted to MyStruct, the encoding/json package will use that method when MyStruct will be marshalled.

What you can do is, instead of letting Meta implement the Marshaller interface, you can implement the interface on MyStruct like this:

package main

import (
    "fmt"
    "encoding/json"
    "strconv"
)

type MyStruct struct {
    *Meta
    Contents []interface{}
}

type Meta struct {
    Id int
}

func (m *MyStruct) MarshalJSON() ([]byte, error) {
    // Here you do the marshalling of Meta
    meta := `"Id":` + strconv.Itoa(m.Meta.Id)

    // Manually calling Marshal for Contents
    cont, err := json.Marshal(m.Contents)
    if err != nil {
        return nil, err
    }

    // Stitching it all together
    return []byte(`{` + meta + `,"Contents":` + string(cont) + `}`), nil
}


func main() {
    str := &MyStruct{&Meta{Id:42}, []interface{}{"MyForm", 12}}

    o, err := json.Marshal(str)
    if err != nil {
        panic(err)
    }
    fmt.Println(string(o))
}

{"Id":42,"Contents":["MyForm",12]}

Playground

Upvotes: 4

Related Questions