3

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 Problem items in type Problems []Problem are known ahead of time, as can be seen in func ONE() below and in Playground demo here.

However, if some K:V pairs contain empty values I'd like to avoid getting {{a},{},{c}} instead of the desired {{a},{c}}, as in func TWO() 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 (
    "encoding/json"
    "fmt"
)

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

 type Problems []Problem

 func main() {
     ONE()
     TWO()
}

 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)
     fmt.Println(string(b))

       // 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)
     fmt.Println(string(b))

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

3 Answers 3

1

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

[{"s":"s0","t":"t0"},{"s":"s1","t":"t1"},{"s":"s2","t":"t2"}]
[{"s":"s0","t":"t0"},{"s":"s2","t":"t2"}]
Sign up to request clarification or add additional context in comments.

1 Comment

Yes, I was hoping there's a json:",omitempty" type of magical potion to omit the empty struct altogether, and thus avoiding the creation of the semi-large JSON object once and then processing again to remove empty structs. :) But thanks for your answer.
0

You can't achieve this easily. Empty structure is also a structure and it will be serialized as {}. Even nil value will be serialized as null.

The following code: package main

import (
    "encoding/json"
    "fmt"
)

func main() {
     xs := []interface{}{struct{}{},nil}
     b, _ := json.Marshal(xs)
     fmt.Println(string(b))
}

will produce:

[{},null]

Playground

The solution would be to implement json.Marshaller interface for Problems type to skip empty structures.

Comments

0
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, prob2}

    for i,v := range probs {
        if v == reflect.Zero(reflect.TypeOf(v)).Interface() {
            probs = append(probs[:i], probs[i+1:]...)
        }
    }

    // fmt.Println(probs)
    // GOT:    [{s0 t0} { } {s2 t2}]
    // WANTED: [{s0 t0} {s2 t2}]
    b, _ := json.Marshal(probs)
    fmt.Println(string(b))
    // GOT:    [{"s":"s0","t":"t0"},{},{"s":"s2","t":"t2"}]
    // WANTED: [{"s":"s0","t":"t0"},{"s":"s2","t":"t2"}]
}

Alternatively, you could use reflection

Here's the link to the playground (https://play.golang.org/p/D0pW4xE4uf). I'm not sure if this is the best answer but it's an alternative approach

3 Comments

Thanks, I don't yet know the eventual length of the probs JSON object and if reflection might cause any performance issues. Will check.
Personally I think the accepted answer is better for your situation, however the reflection approach works well when you have a slice of multiple types - e.g interface types. Here's a contrived example where this approach works with a slice of various different types and it removes all zero valued elements play.golang.org/p/WFkSFYcWos
There's an another part of this app where this approach could be useful, thanks.

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.