MarshalJSON on compound struct doesn’t work as expected [duplicate] - json

This question already has answers here:
Idiomatic way to embed struct with custom MarshalJSON() method
(5 answers)
Closed 27 days ago.
I’m working on an API that receives and sends JSON, nothing fancy here.
The first concern I had was that I needed to only work with UTC times and dates across the API, so any date/time received needed to be converted to UTC.
In order to make this work on already existing structs, I’ve implemented the MarshalJSON() method, and it works like a charm. You can see a simplified example below to illustrate with the Contact struct
package main
import (
"encoding/json"
"fmt"
"time"
)
type Contact struct {
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
CreatedAt time.Time `json:"created_at"`
}
func (c *Contact) MarshalJSON() ([]byte, error) {
type ContactAlias Contact
return json.Marshal(&struct {
*ContactAlias
CreatedAt time.Time `json:"created_at" sql:"created_at"`
}{
ContactAlias: (*ContactAlias)(c),
CreatedAt: c.CreatedAt.UTC(),
})
}
func main() {
contact := &Contact{FirstName: "John", LastName: "Doe", CreatedAt: time.Now()}
s, err := json.Marshal(&contact)
if err != nil {
println(err.Error())
}
fmt.Printf("STEP 1)\n\n%s\n\n\n", s)
}
Then, I needed to add another field to my Contact struct that is Intro
package main
import (
"encoding/json"
"fmt"
"time"
)
type Contact struct {
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
CreatedAt time.Time `json:"created_at"`
}
// STEP-1
func (c *Contact) MarshalJSON() ([]byte, error) {
type ContactAlias Contact
return json.Marshal(&struct {
*ContactAlias
CreatedAt time.Time `json:"created_at" sql:"created_at"`
}{
ContactAlias: (*ContactAlias)(c),
CreatedAt: c.CreatedAt.UTC(),
})
}
// STEP-2
type ContactWithIntro struct {
Contact
Intro string `json:"intro"`
}
func main() {
contact := &Contact{FirstName: "John", LastName: "Doe", CreatedAt: time.Now()}
s, err := json.Marshal(&contact)
if err != nil {
println(err.Error())
}
fmt.Printf("STEP 1)\n\n%s\n\n\n", s)
intro := "Hello World!"
contactWithIntro := ContactWithIntro{
Contact: *contact,
Intro: intro,
}
fmt.Printf("STEP 2-a)\n\n%+v\n\n\n", contactWithIntro)
s, err = json.Marshal(&contactWithIntro)
if err != nil {
println(err.Error())
}
fmt.Printf("STEP 2-b)\n\n%s\n", s)
}
As you can see, I can’t get the intro field in the contactWithInfo JSON string .
After a few tests, I figured out that if I remove the MarshalJSON, then I get the info field in the final JSON but then time is not UTC anymore … I don’t understand what’s happening.
Thank you for your support.
Below is the link to test the snippet if you want to test this out:
https://goplay.tools/snippet/oCBqGT3AR96
Edit 24/01/2023 - undesired workaround
A solution to work with UTC time would be to create a custom type associated to a custom MarshalJSON method but this implies to cast the time value everywhere it's assigned to the field struct. I would prefer avoid this solution.
package main
import (
"encoding/json"
"fmt"
"time"
)
type Contact struct {
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
CreatedAt DateTimeUTC `json:"created_at"`
}
type DateTimeUTC time.Time
func (c DateTimeUTC) MarshalJSON() ([]byte, error) {
return json.Marshal(time.Time(c).UTC().Format(time.RFC3339))
}
func main() {
t := DateTimeUTC(time.Now())
contact := &Contact{FirstName: "John", LastName: "Doe", CreatedAt: t}
s, err := json.Marshal(&contact)
if err != nil {
println(err.Error())
}
fmt.Printf("%s\n", s)
}
snippet here: https://goplay.tools/snippet/V6A11T4z_jy

Because you've embedded the Contact directly the compiler seems to be casting the ContactWithIntro up to Contact, which is why it's ignoring the Intro field.
If you change the declaration:
type ContactWithIntro struct {
Contact Contact
Intro string `json:"intro"`
}
It will then marshal correctly.
Also, at the beginning, you don't need to store the pointer to Contact and then pass a pointer to the pointer to json.Marshal:
contact := Contact{FirstName: "John", LastName: "Doe", CreatedAt: time.Now()} // No & here
...
contactWithIntro := ContactWithIntro{
Contact: contact, // No * here
Intro: intro,
}
This form is actually a bit cleaner (IMHO)
I've amended your playground: https://goplay.tools/snippet/G4CCz99Cjqx

Related

Unmarshalling nested JSON objects with dates in Golang

I'm a noob with Golang. I managed to get some things done with lots of effort.
I'm dealing with JSON files containing dates in a nested way.
I came across some workaround to unmarshal dates from JSON data into time.Time but I'm having a hard time dealing with nested ones.
The following code (obtained here in StackOverflow) is easy to understand since creates a user-defined function to parse the time objects first to a string and then to time.Time with time.Parse.
package main
import (
"encoding/json"
"fmt"
"log"
"time"
)
const dateFormat = "2006-01-02"
const data = `{
"name": "Gopher",
"join_date": "2007-09-20"
}`
type User struct {
Name string `json:"name"`
JoinDate time.Time `json:"join_date"`
}
func (u *User) UnmarshalJSON(p []byte) error {
var aux struct {
Name string `json:"name"`
JoinDate string `json:"join_date"`
}
err := json.Unmarshal(p, &aux)
if err != nil {
return err
}
t, err := time.Parse(dateFormat, aux.JoinDate)
if err != nil {
return err
}
u.Name = aux.Name
u.JoinDate = t
return nil
}
func main() {
var u User
err := json.Unmarshal([]byte(data), &u)
if err != nil {
log.Fatal(err)
}
fmt.Println(u.JoinDate.Format(time.RFC3339))
}
So far, so good.
Now I would like to extend it in order to handle the nested date fields in the JSON, like the example below:
[{
"name": "Gopher",
"join_date": "2007-09-20",
"cashflow": [
{"date": "2021-02-25",
"amount": 100},
{"date": "2021-03-25",
"amount": 105}
]
}]
The struct that I would like to get is:
type Record []struct {
Name string `json:"name"`
JoinDate time.Time `json:"join_date"`
Cashflow []struct {
Date time.Time `json:"date"`
Amount int `json:"amount"`
} `json:"cashflow"`
}
Thanks for the help.
To solve this using the patterns you've already got, you can write a separate unmarshalling function for the inner struct. You can do that by hoisting the inner struct to its own named struct, and then writing the function.
type CashflowRec struct {
Date time.Time `json:"date"`
Amount int `json:"amount"`
}
type Record struct {
Name string `json:"name"`
JoinDate time.Time `json:"join_date"`
Cashflow []CashflowRec `json:"cashflow"`
}
You've already shown how to write the unmarshalling function for CashflowRec, it looks almost the same as your User function. The unmarshalling function for Record will make use of that when it calls
func (u *Record) UnmarshalJSON(p []byte) error {
var aux struct {
Name string `json:"name"`
JoinDate string `json:"join_date"`
Cashflow []CashflowRec `json:"cashflow"`
}
err := json.Unmarshal(p, &aux)
Working example: https://go.dev/play/p/1X7BJ4NETM0
aside 1 Something amusing I learned while looking at this: because you've provided your own unmarshalling function, you don't actually need the json tags in your original structs. Those are hints for the unmarshaller that the json package provides. You should probably still leave them in, in case you have to marshal the struct later. Here's it working without those tags: https://go.dev/play/p/G2VWopO_A3t
aside 2 You might find it simpler not to use time.Time, but instead create a new type of your own, and then give that type its own unmarshaller. This gives you the interesting choice for writing only that one unmarshaller, but whether or not this is a win depends on what else you do with the struct later on. Working example that still uses your nested anonymous structs: https://go.dev/play/p/bJUcaw3_r41
type dateType time.Time
type Record struct {
Name string `json:"name"`
JoinDate dateType `json:"join_date"`
Cashflow []struct {
Date dateType `json:"date"`
Amount int `json:"amount"`
} `json:"cashflow"`
}
func (c *dateType) UnmarshalJSON(p []byte) error {
var s string
if err := json.Unmarshal(p, &s); err != nil {
return err
}
t, err := time.Parse(dateFormat, s)
if err != nil {
return err
}
*c = dateType(t)
return nil
}

Changing JSON tags in struct with custom MarshalJSON

We get some JSON input, unmarshal, perform some work, then marshal and ship off somewhere else. The JSON we get may have a field named "user". When we marshal back to JSON we need to have that field "user" changed to "username". We can do this by creating a new struct with all the same fields, but different JSON tags, but that seemed a bit cumbersome. I thought a custom marshaller would work here, but I'm a bit stuck. Consider the following code.
package main
import (
"encoding/json"
"fmt"
)
type StructA struct {
Username string `json:"user"`
Process string `json:"process"`
}
func main() {
var test1 StructA
err := json.Unmarshal([]byte(`{"user": "user123", "process": "something"}`), &test1)
if err != nil {
fmt.Println(err)
}
// do some work with test1
jsonByte, err := json.Marshal(&test1)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(jsonByte))
}
func (u *StructA) MarshalJSON() ([]byte, error) {
type Alias StructA
return json.Marshal(&struct {
Username string `json:"username"`
*Alias
}{
Username: u.Username,
Alias: (*Alias)(u),
})
}
https://play.golang.org/p/_w0rlQrcgrW
Ideally this would allow me to change the JSON tag on that field from "user" to "username". However, I get both "username" and "user".
{"username":"user123","user":"user123","process":"something"}
I certainly could create an entirely new struct that mirrors StructA with the tags I want, but I don't have to have to copy every single field and worry about keeping those two structs in sync. It's not the end of the world, but it doesn't seem like a good approach.
To be clear, the end result I'm looking for is the following:
{"username":"user123","process":"something"}
I'm sure I'm missing something trivial here, but it's been a long week and any assistance would be appreciated. Thanks!
One option could be to have one struct with the non-changing values and than 2 alternative structs which both include that struct and have only the changing values. You then use one for unmarshaling and the second one for marshaling.
type StructA struct {
Process string `json:"process"`
...
}
type WithUser struct {
StructA
Username `json:"user"`
}
type WithUsername struct {
StructA
Username `json:"username"`
}
This would require multiple structs but no duplication in each one and can be quite flexible in what you include, instead of hard coding what you want to change into a custom marshal function.
use reflect to create struct and change it's tag
package main
import (
"encoding/json"
"fmt"
"reflect"
)
type StructA struct {
Username string `json:"user"`
Process string `json:"process"`
}
func main() {
var test1 StructA
err := json.Unmarshal([]byte(`{"user": "user123", "process": "something"}`), &test1)
if err != nil {
fmt.Println(err)
}
// do some work with test1
jsonByte, err := json.Marshal(&test1)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(jsonByte))
}
func (u *StructA) MarshalJSON() ([]byte, error) {
// get old struct fields
uType := reflect.TypeOf(u).Elem()
userNameField, _ := uType.FieldByName("Username")
// set username field tag
userNameField.Tag = `json:"username"`
processField, _ := uType.FieldByName("Process")
newType := reflect.StructOf([]reflect.StructField{userNameField, processField})
// set new value field
oldValue := reflect.ValueOf(u).Elem()
newtValue := reflect.New(newType).Elem()
for i := 0; i < oldValue.NumField(); i++ {
newtValue.Field(i).Set(oldValue.Field(i))
}
return json.Marshal(newtValue.Interface())
}

Idiomatic way to embed struct with custom MarshalJSON() method

Given the following structs:
type Person struct {
Name string `json:"name"`
}
type Employee struct {
*Person
JobRole string `json:"jobRole"`
}
I can easily marshal an Employee to JSON as expected:
p := Person{"Bob"}
e := Employee{&p, "Sales"}
output, _ := json.Marshal(e)
fmt.Printf("%s\n", string(output))
Output:
{"name":"Bob","jobRole":"Sales"}
But when the embedded struct has a custom MarshalJSON() method...
func (p *Person) MarshalJSON() ([]byte,error) {
return json.Marshal(struct{
Name string `json:"name"`
}{
Name: strings.ToUpper(p.Name),
})
}
it breaks entirely:
p := Person{"Bob"}
e := Employee{&p, "Sales"}
output, _ := json.Marshal(e)
fmt.Printf("%s\n", string(output))
Now results in:
{"name":"BOB"}
(Note the conspicuous lack of jobRole field)
This is easily anticipated... the embedded Person struct implements the MarshalJSON() function, which is being called.
The trouble is, it's not what I want. What I want would be:
{"name":"BOB","jobRole":"Sales"}
That is, encode Employee's fields normally, and defer to Person's MarshalJSON() method to marshal its fields, and hand back some tidy JSON.
Now I could add a MarshalJSON() method to Employee as well, but this requires that I know that the embedded type implements MarshalJSON() as well, and either (a) duplicate its logic, or (b) call Person's MarshalJSON() and somehow manipulate its output to fit where I want it. Either approach seems sloppy, and not very future proof (what if an embedded type I don't control some day adds a custom MarshalJSON() method?)
Are there any alternatives here that I haven't considered?
Don't put MarshalJSON on Person since that's being promoted to the outer type. Instead make a type Name string and have Name implement MarshalJSON. Then change Person to
type Person struct {
Name Name `json:"name"`
}
Example: https://play.golang.org/p/u96T4C6PaY
Update
To solve this more generically you're going to have to implement MarshalJSON on the outer type. Methods on the inner type are promoted to the outer type so you're not going to get around that. You could have the outer type call the inner type's MarshalJSON then unmarshal that into a generic structure like map[string]interface{} and add your own fields. This example does that but it has a side effect of changing the order of the final output fields
https://play.golang.org/p/ut3e21oRdj
Nearly 4 years later, I've come up with an answer that is fundamentally similar to #jcbwlkr's, but does not require the intermediate unmarshal/re-marshal step, by using a little bit of byte-slice manipulation to join two JSON segments.
func (e *Employee) MarshalJSON() ([]byte, error) {
pJSON, err := e.Person.MarshalJSON()
if err != nil {
return nil, err
}
eJSON, err := json.Marshal(map[string]interface{}{
"jobRole": e.JobRole,
})
if err != nil {
return nil, err
}
eJSON[0] = ','
return append(pJSON[:len(pJSON)-1], eJSON...), nil
}
Additional details and discussion of this approach here.
While this produces a different output than what the OP wants, I think it is still useful as a technique to prevent MarshalJSON of embedded structs from breaking the marshaling of structs that contain them.
There is a proposal for encoding/json to recognize an inline option in struct tags. If that ever gets implemented, then I think avoiding embedding structs for cases like in OP's example might be the best bet.
Currently, a reasonable workaround was described in a comment on the Go issue tracker and is the basis for this answer. It consists of defining a new type that will have the same memory layout as the original struct being embedded, but none of the methods:
https://play.golang.org/p/BCwcyIqv0F7
package main
import (
"encoding/json"
"fmt"
"strings"
)
type Person struct {
Name string `json:"name"`
}
func (p *Person) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Name string `json:"name"`
}{
Name: strings.ToUpper(p.Name),
})
}
// person has all the fields of Person, but none of the methods.
// It exists to be embedded in other structs.
type person Person
type EmployeeBroken struct {
*Person
JobRole string `json:"jobRole"`
}
type EmployeeGood struct {
*person
JobRole string `json:"jobRole"`
}
func main() {
{
p := Person{"Bob"}
e := EmployeeBroken{&p, "Sales"}
output, _ := json.Marshal(e)
fmt.Printf("%s\n", string(output))
}
{
p := Person{"Bob"}
e := EmployeeGood{(*person)(&p), "Sales"}
output, _ := json.Marshal(e)
fmt.Printf("%s\n", string(output))
}
}
Outputs:
{"name":"BOB"}
{"name":"Bob","jobRole":"Sales"}
The OP wants {"name":"BOB","jobRole":"Sales"}. To achieve that, one would need to "inline" the object returned by Person.MarshalJSON into the object produced by Employee.MashalJSON, excluding the fields defined in Person.
I've used this approach on parent structs to keep the embedded struct from overriding marshaling:
func (e Employee) MarshalJSON() ([]byte, error) {
v := reflect.ValueOf(e)
result := make(map[string]interface{})
for i := 0; i < v.NumField(); i++ {
fieldName := v.Type().Field(i).Name
result[fieldName] = v.Field(i).Interface()
}
return json.Marshal(result)
}
It's handy but nests the embedded structs in the output::
{"JobRole":"Sales","Person":{"name":"Bob"}}
For a tiny struct like the one in the question, #Flimzy's answer is good but can be done more succinctly:
func (e Employee) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]interface{}{
"jobRole": e.JobRole,
"name": e.Name,
})
}
A more generic way to support massive fields in both inner and outer fields.
The side effect is you need to write this for every outer structs.
Example: https://play.golang.org/p/iexkUYFJV9K
package main
import (
"encoding/json"
"fmt"
"log"
"strings"
)
func Struct2Json2Map(obj interface{}) (map[string]interface{}, error) {
data, err := json.Marshal(obj)
if err != nil {
return nil, err
}
var kvs map[string]interface{}
err = json.Unmarshal(data, &kvs)
if err != nil {
return nil, err
}
return kvs, nil
}
type Person struct {
Name string `json:"-"`
}
func (p Person) MarshalJSONHelper() (map[string]interface{}, error) {
return Struct2Json2Map(struct {
Name string `json:"name"`
}{
Name: strings.ToUpper(p.Name),
})
}
type Employee struct {
Person
JobRole string `json:"jobRole"`
}
func (e Employee) MarshalJSON() ([]byte, error) {
personKvs, err := e.Person.MarshalJSONHelper()
if err != nil {
return nil, err
}
type AliasEmployee Employee
kvs, err := Struct2Json2Map(struct {
AliasEmployee
} {
AliasEmployee(e),
})
for k,v := range personKvs {
kvs[k] = v
}
return json.Marshal(kvs)
}
func main() {
bob := Employee{
Person: Person{
Name: "Bob",
},
JobRole: "Sales",
}
output, err := json.Marshal(bob)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(output))
}

JSON omitempty With time.Time Field

Trying to json Marshal a struct that contains 2 time fields. But I only want the field to come through if it has a time value. So I'm using json:",omitempty" but it's not working.
What can I set the Date value to so json.Marshal will treat it like an empty (zero) value and not include it in the json string?
Playground: http://play.golang.org/p/QJwh7yBJlo
Actual Outcome:
{"Timestamp":"2015-09-18T00:00:00Z","Date":"0001-01-01T00:00:00Z"}
Desired Outcome:
{"Timestamp":"2015-09-18T00:00:00Z"}
Code:
package main
import (
"encoding/json"
"fmt"
"time"
)
type MyStruct struct {
Timestamp time.Time `json:",omitempty"`
Date time.Time `json:",omitempty"`
Field string `json:",omitempty"`
}
func main() {
ms := MyStruct{
Timestamp: time.Date(2015, 9, 18, 0, 0, 0, 0, time.UTC),
Field: "",
}
bb, err := json.Marshal(ms)
if err != nil {
panic(err)
}
fmt.Println(string(bb))
}
The omitempty tag option does not work with time.Time as it is a struct. There is a "zero" value for structs, but that is a struct value where all fields have their zero values. This is a "valid" value, so it is not treated as "empty".
But by simply changing it to a pointer: *time.Time, it will work (nil pointers are treated as "empty" for json marshaling/unmarshaling). So no need to write custom Marshaler in this case:
type MyStruct struct {
Timestamp *time.Time `json:",omitempty"`
Date *time.Time `json:",omitempty"`
Field string `json:",omitempty"`
}
Using it:
ts := time.Date(2015, 9, 18, 0, 0, 0, 0, time.UTC)
ms := MyStruct{
Timestamp: &ts,
Field: "",
}
Output (as desired):
{"Timestamp":"2015-09-18T00:00:00Z"}
Try it on the Go Playground.
If you can't or don't want to change it to a pointer, you can still achieve what you want by implementing a custom Marshaler and Unmarshaler. If you do so, you can use the Time.IsZero() method to decide if a time.Time value is the zero value.
You may define you self Time type for custom marshal format, and use it everywhere instead time.Time
https://play.golang.org/p/C8nIR1uZAok
package main
import (
"bytes"
"encoding/json"
"fmt"
"time"
)
type MyTime struct {
*time.Time
}
func (t MyTime) MarshalJSON() ([]byte, error) {
return []byte(t.Format("\"" + time.RFC3339 + "\"")), nil
}
// UnmarshalJSON implements the json.Unmarshaler interface.
// The time is expected to be a quoted string in RFC 3339 format.
func (t *MyTime) UnmarshalJSON(data []byte) (err error) {
// by convention, unmarshalers implement UnmarshalJSON([]byte("null")) as a no-op.
if bytes.Equal(data, []byte("null")) {
return nil
}
// Fractional seconds are handled implicitly by Parse.
tt, err := time.Parse("\""+time.RFC3339+"\"", string(data))
*t = MyTime{&tt}
return
}
func main() {
t := time.Now()
d, err := json.Marshal(MyTime{&t})
fmt.Println(string(d), err)
var mt MyTime
json.Unmarshal(d, &mt)
fmt.Println(mt)
}
As a follow up to icza's answer here is a custom marshaller that omits an empty date field but keeps the rest of the fields unchanged.
func (ms *MyStruct) MarshalJSON() ([]byte, error) {
type Alias MyStruct
if ms.Timestamp.IsZero() {
return json.Marshal(&struct {
Timestamp int64 `json:",omitempty"`
*Alias
}{
Timestamp: 0,
Alias: (*Alias)(ms),
})
} else {
return json.Marshal(&struct {
*Alias
}{
Alias: (*Alias)(ms),
})
}
}
This borrows from http://choly.ca/post/go-json-marshalling/
The OPs case has two time fields which would make it much more complicated. (you'd have to check for neither, either and both being empty!)
There may be better ways to achieve this, so comments are welcome.

Converting Go struct to JSON

I am trying to convert a Go struct to JSON using the json package but all I get is {}. I am certain it is something totally obvious but I don't see it.
package main
import (
"fmt"
"encoding/json"
)
type User struct {
name string
}
func main() {
user := &User{name:"Frank"}
b, err := json.Marshal(user)
if err != nil {
fmt.Printf("Error: %s", err)
return;
}
fmt.Println(string(b))
}
Then when I try to run it I get this:
$ 6g test.go && 6l -o test test.6 && ./test
{}
You need to export the User.name field so that the json package can see it. Rename the name field to Name.
package main
import (
"fmt"
"encoding/json"
)
type User struct {
Name string
}
func main() {
user := &User{Name: "Frank"}
b, err := json.Marshal(user)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(b))
}
Output:
{"Name":"Frank"}
Related issue:
I was having trouble converting struct to JSON, sending it as response from Golang, then, later catch the same in JavaScript via Ajax.
Wasted a lot of time, so posting solution here.
In Go:
// web server
type Foo struct {
Number int `json:"number"`
Title string `json:"title"`
}
foo_marshalled, err := json.Marshal(Foo{Number: 1, Title: "test"})
fmt.Fprint(w, string(foo_marshalled)) // write response to ResponseWriter (w)
In JavaScript:
// web call & receive in "data", thru Ajax/ other
var Foo = JSON.parse(data);
console.log("number: " + Foo.number);
console.log("title: " + Foo.title);
This is an interesting question, it is very easy using the new go versions. You should do this:
package main
import (
"fmt"
"encoding/json"
)
type User struct {
Name string `json:"name"`
}
func main() {
user := &User{name:"Frank"}
b, err := json.Marshal(user)
if err != nil {
fmt.Printf("Error: %s", err)
return;
}
fmt.Println(string(b))
}
Change this name to Name.
You can define your own custom MarshalJSON and UnmarshalJSON methods and intentionally control what should be included, ex:
package main
import (
"fmt"
"encoding/json"
)
type User struct {
name string
}
func (u *User) MarshalJSON() ([]byte, error) {
return json.Marshal(&struct {
Name string `json:"name"`
}{
Name: "customized" + u.name,
})
}
func main() {
user := &User{name: "Frank"}
b, err := json.Marshal(user)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(b))
}
Struct values encode as JSON objects. Each exported struct field becomes
a member of the object unless:
the field's tag is "-", or
the field is empty and its tag specifies the "omitempty" option.
The empty values are false, 0, any nil pointer or interface value, and any array, slice, map, or string of length zero. The object's default key string is the struct field name but can be specified in the struct field's tag value. The "json" key in the struct field's tag value is the key name, followed by an optional comma and options.