How to omit map from JSON marshalling only when it's empty? [duplicate] - go

This question already has answers here:
How to not marshal an empty struct into JSON with Go?
(4 answers)
Closed 10 months ago.
I need to return an empty json {} when the map is not nil, but it's empty. When the map is nil I need it to be omitted.
How could I go about doing this?
type ChildMap map[string]string
type Parent struct {
ID int64
T ChildMap `json:"t,omitempty"`
}
Here's a playground that explains what I'm trying to do quite well:
https://go.dev/play/p/hahseo9nyh3
In 1st case it needs to be omitted (this works), 2nd case I need it returned as {} (doesn't work), 3rd case needs to be displayed (also works)

I had the same requirement long ago and this was the only solution.
Set your Slice on Bar and even with no elements it will render []
A more interesting solution need a specific Unmarshal code (perhaps is more readable than this)
type Foo struct {
Bar *interface{} `json:"bar,omitempty"`
Baz *interface{} `json:"baz,omitempty"`
}

Related

Is there a graceful approach to deserializing Go interface types? [duplicate]

This question already has answers here:
Can I unmarshal JSON into implementers of an Interface?
(2 answers)
Closed 11 months ago.
The community reviewed whether to reopen this question 11 months ago and left it closed:
Original close reason(s) were not resolved
Given the following:
type Foo struct {
Td ThingDoer
// ... other stuff
}
type ThingDoer interface {
doThing()
}
type doerA struct {
AGuts string
}
func (a doerA) doThing() {}
type doerB struct {
BGuts string
}
func (b doerB) doThing() {}
is there a preferred serialization / deserialization strategy for Foo?
Attaching, eg, a MarshalJSON function onto doerA and doerB satisfies the serialization, but then Foo.UnmarshalJSON is effectively stuck: it can't know in advance whether the supplied JSON is of doerA or doerB type.
Edit: The linked "similar" question addresses the specific non-solution example outlined in this question. This question is asking about the existence of a graceful solution.
Imagine you have an structure Foo with one or more fields using interface types.
Lets say you have an interface Bar with two possible structures: Baz and Bam.
You can define auxiliary type (FooConf), without any interface. Only concrete types.
This structure may have a method Build() Foo that will choose the right type on each case.
To be possible define what is the concrete type you can define a signature. For instance an extra field “type” (baz or bam).
You just need to be sure about each type can marshal/unmarshal with consistency.

How do you access this Go struct? [duplicate]

This question already has an answer here:
Strange type definition syntax in Golang (name, then type, then string literal)
(1 answer)
Closed 1 year ago.
I'm trying to make sense of this Go struct:
type ListClustersOutput struct {
_ struct{} `type:"structure"`
// A list of all of the clusters for your account in the specified Region.
Clusters []*string `locationName:"clusters" type:"list"`
// The nextToken value to include in a future ListClusters request. When the
// results of a ListClusters request exceed maxResults, you can use this value
// to retrieve the next page of results. This value is null when there are no
// more results to return.
NextToken *string `locationName:"nextToken" type:"string"`
}
Looking at the docs: https://golangdocs.com/structs-in-golang#defining-a-struct-in-go
it gives an example:
type Fruit struct {
name string
}
which seems very different.
In the more complex code, I assume this Clusters []*string `locationName:"clusters" type:"list"` is equivalent to name string but struggling to unpack it.
I'm struggling to find much out about type: "list" - most of the examples seem to refer to slices. Why are they using a list?
what is a locationName?
how do you access the first element of the list in that struct?
Note, for this last question, if I use result.Clusters[0] (where result is of this struct type) I get a pointer. E.g.
fmt.Println("Result: ", result.Clusters[0])
Result: 0xc000372260
How do I dereference it?
Looking at this:
How does pointer dereferencing work in Go?
it seems you need an asterisk or an ampersand. Not clear which one you use or whether you tack it on the beginning or the end.
You are struggling with struct tags.
In your code:
Clusters []*string `locationName:"clusters" type:"list"`
The Clusters field has a type ([]*string) and the rest of the declaration are 2 struct tags that you should take the value(s) of tags using struct tag.
Here's how you access it:
fmt.Println("Result: ", *result.Clusters[0])

What is the advantage of using a pointer to a string instead of a string in Go [duplicate]

This question already has answers here:
What does the asterisk do in "Go"?
(6 answers)
Closed 5 years ago.
Reviewing some go code I came across this:
Person struct {
Name *string `json:"name"`
}
and then some where I saw:
Animal struct {
Name string `json:"name"`
}
What is the advantage of the pointer here?
The * declares a pointer type. A pointer to a string is sometimes used when decoding JSON to distinguish the following JSON:
JSON value of the Name field
{ } nil
{name: ""} pointer to ""
Without the pointer, it's not possible to distinguish a missing value from a blank value in the decoded result.
If the application does not need to make this distinction, then use the second form shown in the question. It's more convenient.
* means pointer.
In your case, Name is a field of type pointer to string.
See http://www.golang-book.com/books/intro/8
The * is a pointer.
A pointer type denotes the set of all pointers to variables of a given
type, called the base type of the pointer. The value of an
uninitialized pointer is nil.
This is coming from the Go Spec. I would suggest reading it all.

Golang: I have a map of int to struct. Why can't I directly modify a field in a map value? [duplicate]

This question already has answers here:
Why do I get a "cannot assign" error when setting value to a struct as a value in a map? [duplicate]
(2 answers)
Closed 6 years ago.
Why do we have to first read the struct, modify it, and then write it back to the map? Am I missing some kind of implied hidden cost in modifying fields of structs in other data structures (like a map or a slice)?
Edit:
I realize I can use pointers, but why is this not allowed by Go?
type dummy struct {
a int
}
x := make(map[int]dummy)
x[1] = dummy{a:1}
x[1].a = 2
You are storing a struct by value which means that accession of that struct in the map gives you a copy of the value. This is why when you modify it, the struct in the map remains unmutated until you overwrite it with the new copy.
As RickyA pointed out in the comment, you can store the pointer to the struct instead and this allows direct modification of the struct being referenced by the stored struct pointer.
i.e. map[whatever]*struct instead of map[whatever]struct

Why do I get a "cannot assign" error when setting value to a struct as a value in a map? [duplicate]

This question already has answers here:
Accessing struct fields inside a map value (without copying)
(2 answers)
Closed 7 years ago.
New to Go. Encountered this error and have had no luck finding the cause or the rationale for it:
If I create a struct, I can obviously assign and re-assign the values no problem:
type Person struct {
name string
age int
}
func main() {
x := Person{"Andy Capp", 98}
x.age = 99
fmt.Printf("age: %d\n", x.age)
}
but if the struct is one value in a map:
type Person struct {
name string
age int
}
type People map[string]Person
func main() {
p := make(People)
p["HM"] = Person{"Hank McNamara", 39}
p["HM"].age = p["HM"].age + 1
fmt.Printf("age: %d\n", p["HM"].age)
}
I get cannot assign to p["HM"].age. That's it, no other info. http://play.golang.org/p/VRlSItd4eP
I found a way around this - creating an incrementAge func on Person, which can be called and the result assigned to the map key, eg p["HM"] = p["HM"].incrementAge().
But, my question is, what is the reason for this "cannot assign" error, and why shouldn't I be allowed to assign the struct value directly?
p["HM"] isn't quite a regular addressable value: hashmaps can grow at runtime, and then their values get moved around in memory, and the old locations become outdated. If values in maps were treated as regular addressable values, those internals of the map implementation would get exposed.
So, instead, p["HM"] is a slightly different thing called a "map index expression" in the spec; if you search the spec for the phrase "index expression" you'll see you can do certain things with them, like read them, assign to them, and use them in increment/decrement expressions (for numeric types). But you can't do everything. They could have chosen to implement more special cases than they did, but I'm guessing they didn't just to keep things simple.
Your approach seems good here--you change it to a regular assignment, one of the specifically-allowed operations. Another approach (maybe good for larger structs you want to avoid copying around?) is to make the map value a regular old pointer that you can modify the underlying object through:
package main
import "fmt"
type Person struct {
name string
age int
}
type People map[string]*Person
func main() {
p := make(People)
p["HM"] = &Person{"Hank McNamara", 39}
p["HM"].age += 1
fmt.Printf("age: %d\n", p["HM"].age)
}
The left side of the assignment must b "addressable".
https://golang.org/ref/spec#Assignments
Each left-hand side operand must be addressable, a map index expression, or (for = assignments only) the blank identifier.
and https://golang.org/ref/spec#Address_operators
The operand must be addressable, that is, either a variable, pointer indirection, or slice indexing operation; or a field selector of an addressable struct operand; or an array indexing operation of an addressable array.
as #twotwotwo's comment, p["HM"] is not addressable.
but, there is no such definition show what is "addressable struct operand" in the spec. I think they should add some description for it.

Resources