parens parens - 1 year ago 132
JSON Question

Composing arbitrary JSON objects in embedded structs in Go

I'm trying to generate JSON objects in the form of

{{"s":"v1", "t":"v2"}, {"s":"v3", "t":"v4"}, etc}
via embedded structs in Go.

It all works out fine when all
items in
type Problems []Problem
are known ahead of time, as can be seen in func
below and in Playground demo here.

However, if some K:V pairs contain empty values I'd like to avoid getting
instead of the desired
, as in func
below and in demo.

Or alternatively, how can I compose
probs := Problems{prob0, prob1, prob2, etc}
below arbitrarily without knowing ahead of time whether a prob item will be added or omitted?

package main

import (

type Problem struct {
S string `json:"s,omitempty"`
T string `json:"t,omitempty"`

type Problems []Problem

func main() {

func ONE() {
prob0 := Problem{S: "s0", T: "t0"}
prob1 := Problem{S: "s1", T: "t1"}
prob2 := Problem{S: "s2", T: "t2"}

probs := Problems{prob0, prob1, probe}

// fmt.Println(probs) // CORRECT: [{s0 t0} {s1 t1} {s2 t2}]

b, _ := json.Marshal(probs)

// CORRECT: [{"s":"s0","t":"t0"},{"s":"s1","t":"t1"},{"s":"s2","t":"t2"}]``

func TWO() {
prob0 := Problem{S: "s0", T: "t0"}
prob1 := Problem{S: "", T: ""} // empty values omited BUT NOT {}
prob2 := Problem{S: "s2", T: "t2"}

probs := Problems{prob0, prob1, probe}

// fmt.Println(probs)
// GOT: [{s0 t0} { } {s2 t2}]
// WANTED: [{s0 t0} {s2 t2}]

b, _ := json.Marshal(probs)

// GOT: [{"s":"s0","t":"t0"},{},{"s":"s2","t":"t2"}]
// WANTED: [{"s":"s0","t":"t0"},{"s":"s2","t":"t2"}]

Answer Source

Once you add an element to the array/slice which you marshal, there's nothing you can do about it. If an element is in the array/slice, it will be marshalled (will be included in the JSON output). How could the json.Marshal() function guess which elements you don't want to marshal? It can't.

You have to exclude elements which you don't what to appear in the output. In your case you want to exclude empty Problem structs.

Best/easiest is to create a helper function which creates the []Problem slice for you, empty structs excluded:

func createProbs(ps ...Problem) []Problem {
    // Remove empty Problem structs:
    empty := Problem{}
    for i := len(ps) - 1; i >= 0; i-- {
        if ps[i] == empty {
            ps = append(ps[:i], ps[i+1:]...)
    return ps

Using this creating a slice is like this:

probs := createProbs(prob0, prob1, prob2)

Try your modified application on the Go Playground.

Output of the modified code (notice the empty struct is missing):