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

This is one method of handling it:

type Object struct {
    A string
    B int
    Extra map[string]interface{} `json:"-"`
}

func (o Object) MarshalJSON() ([]byte, error) {
    type Object_ Object
    b, err := json.Marshal(Object_(o))
    if err != nil {
        return nil, err
    }
    if o.Extra == nil || len(o.Extra) == 0 {
        return b, nil
    }
    m, err := json.Marshal(o.Extra)
    if err != nil {
        return nil, err
    }
    if len(b) == 2 {
        return m, nil
    } else {
        b[len(b)-1] = ','
        return append(b, m[1:]...), nil
    }
}

You can add whatever additional fields to the Extra map and they will appear without nesting in the output.

Go Playground


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",
    })
}

Marshalling a map is another way around the problem.

tmap := make(map[string]interface{})

tmap["struct"] = struct {
    StructValue string `json:"struct_value"`
}{
    "Value 02",
}

tmap["string"] = "Value 01"

out, err := json.MarshalIndent(tmap, "", "  ")
if err != nil {
    log.Fatalln(err)
}
fmt.Println(string(out))

This will output:

{
  "string": "Value 01",
  "struct": {
    "struct_value": "Value 02"
  }
}

Where you have many arbitrary key names this could be a good solution.

Playground: https://play.golang.org/p/Umy9rtx2Ms


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

Tags:

Json

Go