ajoseps
ajoseps

Reputation: 2101

How do I Marshal a struct with an embdedded struct field to be a flat JSON object in Go?

package main

import (
    "encoding/json"
    "fmt"
)

type City struct {
    City string `json:"City"`
    Size int        `json:"Size"`
}

type Location struct {
    City City
    State string `json:"State"`
}

func main() {
    city := City{City: "San Francisco", Size: 8700000}
    loc := Location{}
    loc.State = "California"
    loc.City = city
    js, _ := json.Marshal(loc)
    fmt.Printf("%s", js)
}

Outputs the following:

{"City":{"City":"San Francisco","Size":8700000},"State":"California"}

The intended output I want is:

{"City":"San Francisco","Size":8700000,"State":"California"}

I've read this blog post for custom JSON Marshalling, but I can't seem to get it to work for a struct with another embedded struct.

I tried flattening the struct by defining a custom MarshalJSON function but I still get the same nested output:

func (l *Location) MarshalJSON() ([]byte, error) {
    return json.Marshal(&struct {
            City string `json:"City"`
            Size int    `json:"Size"`
            State string `json:"State"`
    }{
        City: l.City.City,
        Size: l.City.Size,
        State:   l.State,
    })
}

Upvotes: 3

Views: 1192

Answers (1)

Thundercat
Thundercat

Reputation: 120931

Use an anonymous field to flatten the JSON output:

type City struct {
    City string `json:"City"`
    Size int        `json:"Size"`
}

type Location struct {
    City     // <-- anonymous field has type, but no field name
    State string `json:"State"`
}

The MarshalJSON method is ignored in the question because code encodes a Location value, but the MarshalJSON method is declared with a pointer receiver. Fix by encoding a *Location.

js, _ := json.Marshal(&loc)  // <-- note &

Upvotes: 4

Related Questions