Go - JSON
Updated at 2015-07-26 00:27
Go has built-in support for JSON.
package main
import (
"encoding/json"
)
func assert(condition bool) {
if !condition {
panic("assert failed")
}
}
func main() {
boolJson, _ := json.Marshal(true)
assert(string(boolJson) == "true")
intJson, _ := json.Marshal(1)
assert(string(intJson) == "1")
floatJson, _ := json.Marshal(2.34)
assert(string(floatJson) == "2.34")
stringJson, _ := json.Marshal("gopher")
assert(string(stringJson) == `"gopher"`)
slice := []string{"apple", "peach", "pear"}
sliceJson, _ := json.Marshal(slice)
assert(string(sliceJson) == `["apple","peach","pear"]`)
fruitMap := map[string]int{"apple": 5, "lettuce": 7}
fruitMapJson, _ := json.Marshal(fruitMap)
assert(string(fruitMapJson) == `{"apple":5,"lettuce":7}`)
}
Custom type values get converted to JSON using their field name as the JSON key name.
package main
import (
"encoding/json"
)
type ShoppingList struct {
Milk int
Fruits []string
MeatIfCheaper map[string]float64
}
func main() {
shoppingList := &ShoppingList{
Milk: 1,
Fruits: []string{"apple", "peach", "pear"},
MeatIfCheaper: map[string]float64{"mettwurst": 10.23, "bratwurst": 11.3},
}
shoppingListJson, _ := json.Marshal(shoppingList)
expected := `{"Milk":1,"Fruits":["apple","peach","pear"],"MeatIfCheaper":{"bratwurst":11.3,"mettwurst":10.23}}`
if expected != string(shoppingListJson) {
panic("assert failed")
}
}
You can use tags to specify JSON key names.
package main
import (
"encoding/json"
)
type ShoppingList struct {
Milk int `json:"milk"`
Fruits []string `json:"fruits"`
MeatIfCheaper map[string]float64 `json:"meats"`
}
func main() {
shoppingList := &ShoppingList{
Milk: 1,
Fruits: []string{"apple", "peach", "pear"},
MeatIfCheaper: map[string]float64{"mettwurst": 10.23, "bratwurst": 11.3},
}
shoppingListJson, _ := json.Marshal(shoppingList)
expected := `{"milk":1,"fruits":["apple","peach","pear"],"meats":{"bratwurst":11.3,"mettwurst":10.23}}`
if expected != string(shoppingListJson) {
panic("assert failed")
}
}
Decoding JSON.
package main
import (
"encoding/json"
)
func assert(condition bool) {
if !condition {
panic("assert failed")
}
}
func main() {
raw := []byte(`{"name":"John","age":33,"children":["Abel","Cain"]}`)
var person map[string]interface{}
if err := json.Unmarshal(raw, &person); err != nil {
panic(err)
}
// To use the values, you must convert them.
age := person["age"].(float64)
assert(age == 33.0)
children := person["children"].([]interface{})
child1 := children[0].(string)
assert(child1 == "Abel")
}
You can make decoding more safe by specifying a custom type it should follow.
package main
import (
"encoding/json"
)
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Children []string `json:"children"`
}
func assert(condition bool) {
if !condition {
panic("assert failed")
}
}
func main() {
raw := []byte(`{"name":"Ronald", "age": 45, "children": ["Mary", "Suzy"]}`)
person := &Person{}
err := json.Unmarshal(raw, &person)
assert(err == nil)
assert(person.Name == "Ronald")
assert(person.Children[0] == "Mary")
}
You can direct JSON straight to a writer by using JSON encoder.
package main
import (
"encoding/json"
"os"
)
func main() {
encoder := json.NewEncoder(os.Stdout)
data := map[string]int{"apple": 5, "lettuce": 7}
err := encoder.Encode(data)
if err != nil {
panic(err)
}
}
When you JSON data is more complex, containing a lot of nesting etc, you can use mapstructure package.
type User struct {
Name string `jpath:"user.name"`
Id int `jpath:"user.id"`
UserType UserType `jpath:"user.type"`
Session string `jpath:"session"`
}
{
"user": {
"name": "Ruksi",
"id": 13521,
"type": "admin"
},
"session": "dqw87wfqw96d7w86dq7w6d7qwf7q9d78q"
}
You can give two special properties to JSON fields,"-"
and ,omitempty
.
package main
import "encoding/json"
type User struct {
Id string `json:"id"`
Username string `json:"username"`
FullName string `json:"full_name"`
Password string `json:"-"`
SomeField bool `json:"some_field,omitempty"`
}
// - = Don't try to populate the field on parse.
// omitempty = Don't include in JSON if the value is empty.
func assert(condition bool) {
if !condition {
panic("assert failed")
}
}
func main() {
data := []byte(`
{
"id": "LONG-UUID-AND-STUFF",
"username": "ruksi",
"password": "VERY_SECRET",
"not_in_definition": "Not defined in User",
"some_field": false
}
`)
var u User
err := json.Unmarshal(data, &u)
assert(err == nil)
assert(u.Id == "LONG-UUID-AND-STUFF")
assert(u.Username == "ruksi")
assert(u.Password == "") // as it was `-`
assert(u.SomeField == false)
var str []byte
str, err = json.Marshal(u)
assert(err == nil)
assert(string(str) == `{"id":"LONG-UUID-AND-STUFF","username":"ruksi","full_name":""}`)
// notice that "some_field" is not included as it is `omitempty` and `false`
}
JSON definitions can be nested.
package main
import "encoding/json"
type User struct {
Id string `json:"id"`
Username string `json:"username"`
Password string `json:"-"`
Person
}
type Person struct {
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
}
func assert(condition bool) {
if !condition {
panic("assert failed")
}
}
func main() {
data := []byte(`
{
"id": "UUID",
"username": "ruksi",
"password": "VERY SECRET",
"first_name": "Ruksi",
"last_name": "Laine"
}
`)
var u User
err := json.Unmarshal(data, &u)
assert(err == nil)
assert(u.Username == "ruksi")
p := u.Person
assert(p.LastName == "Laine")
var str []byte
str, err = json.Marshal(u)
assert(err == nil)
assert(string(str) == `{"id":"UUID","username":"ruksi","first_name":"Ruksi","last_name":"Laine"}`)
}