Go & JSON
json.Marshal
— Go to JSON
type model struct {
State bool
Pictures []string
FooBar string `db:"foo_bar" json:"fooBar,omitempty"`
FooBar2 string `db:"foo_bar_2" json:"fooBar2"`
// The JSON "omitempty" affects only Marshalling (struct to JSON);
// does NOT AFFECT struct on Unmarshal (JSON to struct)
}
m := model{
State: true,
Pictures: []string{
"one.jpg",
"two.jpg",
"three.jpg",
},
FooBar: "",
}
b, err := json.Marshal(m)
if err != nil {
fmt.Println("FAIL @ json.Marshal: ", err)
}
os.Stdout.Write(b)
☩ go run .
{"State":true,"Pictures":["one.jpg","two.jpg","three.jpg"],"fooBar2":""}}
- Note that
omitempty
ensures JSON key is omitted, whether or not its struct field (key) is present, as long as the value thereof is it's type's zero-value.
json.Unmarshal
— JSON to Go
var data img
rcvd := `{
"Width": 800,
"Height":600,
"Title":"Foo Bar",
"Thumbnail":{
"Url":"http://www.example.com/image/481989943",
"Height":125,
"Width":100
},
"Animated":false,
"IDs":[116,943,234,38793]
}`
type thumbnail struct {
URL string
Height, Width int
}
type img struct {
Width, Height int
Title string
Thumbnail thumbnail
Animated bool
IDs []int
}
err := json.Unmarshal([]byte(rcvd), &data)
if err != nil {
log.Fatalln("FAIL @ json.Unmarshal", err)
}
fmt.Printf("%+v\n", data)
☩ go run .
{Width:800 Height:600 Title:Foo Bar Thumbnail:{URL:http://www.example.com/image/481989943 Height:125 Width:100} Animated:false IDs:[116 943 234 38793]}
JSON null
maps to the Golang type's zero value; ""
, 0
, {}
, []
rcvd = `null`
☩ go run .
{Width:0 Height:0 Title: Thumbnail:{URL: Height:0 Width:0} Animated:false IDs:[]}
JSON | Golang (struct ) |
---|---|
{"Thumbnail":{}} |
{Thumbnail:{URL: Height:0 Width:0}} |
{"Thumbnail":null} |
{Thumbnail:{URL: Height:0 Width:0}} |
{"IDs":null} |
{IDs:[]} |
{"Width":null} |
{Width:0} |
{"Title":null} |
{Title:} |
JSON keys ommitted map same as null
rcvd = `{
"Width": 800,
"Title":"Foo Bar"
}`
☩ go run .
{Width:800 Height:0 Title:Foo Bar Thumbnail:{URL: Height:0 Width:0} Animated:false IDs:[]}
Note null
is a javascript keyword.
JSON Tags
Allow for namespace maps, per domain (per package); json.
, db.
, .... Note that conditionals, e.g., omitempty
, apply on mapping from struct to … JSON, DBMS query, or whatever, only; they do not apply to json.Unmarshal
ling.
type img struct {
Width, Height int
Title string
Thumbnail thumbnail
Animated bool
IDs []int
FooBar string `db:"foo_bar" json:"fooBar,omitempty"`
//... JSON key is omitted on Marshalling (struct to JSON).
//... Does NOT AFFECT struct on Unmarshal (JSON to struct)!
}
rcvd = `{
"Width":800
}`
☩ go run .
{Width:800 Height:0 Title: Thumbnail:{URL: Height:0 Width:0} Animated:false IDs:[] FooBar:}
Pointer Types @ Unmarshal (JSON to Go)
With one simple test for nil
, pointer field types allow us to set and abide validation criteria, including those against type's zero-value,
yet conditionally bypass validation on absesnt key and key having null
(javascript keyword) value.
This trick works for all Golang value types (those pointed to); int
, string
, []byte
, struct{}
, … their zero-values are not nil
.
// User ...
type User struct {
Uname *string //`json:"uname,omitempty"`
}
func main() {
var u1, u2, u3 User
json.Unmarshal([]byte(`{"uname":""}`), &u1) // the type's zero-value
json.Unmarshal([]byte(`{}`), &u2) // absent key
json.Unmarshal([]byte(`{"uname":null}`), &u3) // null (javascript keyword)
fmt.Println("uname set:", u1.Uname != nil, *u1.Uname)
fmt.Println("uname set:", u2.Uname != nil, u2.Uname) // Can't dereference nil pointer
fmt.Println("uname set:", u3.Uname != nil, u3.Uname) // Can't dereference nil pointer
}
☩ go run .
uname set: true
uname set: false <nil>
uname set: false <nil>
json-validation/pattern-3.go
// UserP3 is case/pattern handles cases where want keys to bypass validation if not sent or null.
type UserP3 struct {
Name *string `json:"name" validate:"required,min=1"`
Age *uint `json:"age" validate:"omitempty,gte=1"`
Addr *string `json:"addr" validate:"min=1"`
FavColor string `json:"favColor"`
} // Use this struct only @ Unmarshal (JSON to Go)
// Pointers with `"omitempty"` bypass validation (validate) on null||not-sent,
// yet are caught (invalidate) if condition not met when sent and not null.
.Decode
, vs. .Unmarshal
if err := json.NewDecoder(jStream).Decode(&toThisStruct); err != nil {
fmt.Printf("FAIL @ Unmarshalling (JSON to struct) \n%s\n", err)
}
- Use
json.Decoder
if the JSON data is coming from anio.Reader
stream, such as an HTTP request. - Use
json.Unmarshal
if the JSON data is already in memory, as[]byte
.
Pairing types across app boundaries
PostgreSQL | Golang | Javascript |
---|---|---|
BIGINT |
int |
integer |
UUID |
string |
string |
NUMERIC(5,2) |
??? | ??? |
TIMESTAMPTZ |
time.Time |
string |