Go uses the case of the first letter to designate whether the entity is exported or not. However, this does not effect the decoding because it is not sensitive to case. For example {“firstname”: “Bob”, “lastname”: “Smith”} decoded into:

type Person struct {

FirstName string

LastName string

}

Will still map the keys correctly. Just be careful if you need to encode this data back to JSON the keys will change case which will potentially break other systems. There is a simple way around this with tags explained later.

Encoding JSON

Encoding JSON is very easy. We simply provide the variable to be encoded to the json.Marshal function:

type Person struct {

FirstName string

LastName string

} func main() {

person := Person{"James", "Bond"}

theJson, _ := json.Marshal(person) fmt.Printf("%+v

", string(theJson))

} {"FirstName":"James","LastName":"Bond”}

Mapping Keys

If you need to map completely different JSON keys to a struct you can use tags. Tags are text-based annotations added to variables:

type Person struct {

FirstName string `json:"fn"`

LastName string `json:"ln"`

} func main() {

theJson := `{"fn": "Bob", "ln": "Smith"}` var person Person

json.Unmarshal([]byte(theJson), &person) fmt.Printf("%+v

", person)

}

This also applies to encoding the object so that the keys will be mapped back to their original names.

Default Values

The json.Unmarshal function takes in an object rather than a type. Any values decoded in the JSON will replace values found in the object, but otherwise it will leave the values as they were which is ideal for setting up default properties for your object:

type Person struct {

FirstName string

LastName string

} func newPerson() Person {

return Person{"<No First>", "<No Last>"}

} func main() {

theJson := `{"FirstName": "Bob"}`

person := newPerson()

json.Unmarshal([]byte(theJson), &person) fmt.Printf("%+v

", person)

} {FirstName:Bob LastName:<No Last>}

Handling Errors

If the JSON is unable to be parsed (like a syntax error) the erroris returned from the json.Unmarshal function:

func main() {

theJson := `invalid json`

err := json.Unmarshal([]byte(theJson), nil) fmt.Printf("%+v

", err)

} invalid character 'i' looking for beginning of value

More commonly is that the JSON values are of a different type than those defined in the Go structures and variables. Here is an example of trying to encode a JSON string into an integer variable:

func main() {

theJson := `"123"` var value int

err := json.Unmarshal([]byte(theJson), &value) fmt.Printf("%+v

", err)

} json: cannot unmarshal string into Go value of type int

How you handle these errors is up to you, but if your dealing with JSON that can be messy or dynamic you need to keep reading…

Dynamic JSON

Dynamic JSON is the most difficult to deal with because you have to investigate the type of data before you can reliably use it.

There are two approaches to dealing with dynamic JSON:

1. Create a flexible skeleton to unserialise into and test the types inside that.

type PersonFlexible struct {

Name interface{}

} type Person struct {

Name string

} func main() {

theJson := `{"Name": 123}` var personFlexible PersonFlexible

json.Unmarshal([]byte(theJson), &personFlexible) if _, ok := personFlexible.Name.(string); !ok {

panic("Name must be a string.")

} // When validation passes we can use the real object and types.

// This code will never be reached because the above will panic()...

// But if you make the Name above a string it will run the following:

var person Person

json.Unmarshal([]byte(theJson), &person) fmt.Printf("%+v

", person)

}

2. Go totally rogue (pun intended) and investigate the entire value one step at a time.

This is akin to dealing with JSON in JavaScript with lots of typeofconditions. You can do the same thing in Go like above or use the beauty of the switch:

func main() {

theJson := `123` var anything interface{}

json.Unmarshal([]byte(theJson), &anything) switch v := anything.(type) {

case float64:

// v is an float64

fmt.Printf("NUMBER: %f

", v) case string:

// v is a string

fmt.Printf("STRING: %s

", v) default:

panic("I don't know how to handle this!")

}

}

Important: Go uses fixed types for JSON values. Even though you would think 123would be decoded as some type of intit will actually always be a float64. This simplifies the type switches but may require you to also round or test for an actual integer value if that’s a requirement.

Validating JSON Schemas

If you have a complex JSON structure and/or more complex rules about how that data should be formatted it is easier to use the JSON Schema format to do the validation for you.

I won’t go into too much detail here because JSON Schema options could fill more than a whole article. However, here is an quick example using the xeipuuv/gojsonschema package with the example they provide: