← Go, End to End

Go, End to End

JSON and Encoding

encoding/json is the standard library's JSON support, and it leans entirely on struct tags and reflection rather than requiring code generation or annotations processed at build time.

Marshal and Unmarshal

go · live, editable, runnable

Notice the output uses Name and Age, capitalized, matching the Go field names exactly. That's rarely what you want in a real API, which is where struct tags come in.

Struct tags

go · live, editable, runnable

json:"name" controls the output key. omitempty drops the field entirely if it's the zero value (here, Age is 0 and omitted). json:"-" excludes the field from JSON entirely, which is exactly what you want for something like an SSN or a password hash that should never leave the process in a JSON response.

The unexported-fields gotcha

This one causes real confusion: encoding/json silently skips any struct field that isn't exported (doesn't start with a capital letter), because reflection can't access unexported fields from outside the package. There's no error, no warning, the field is just absent from the output.

go · live, editable, runnable

If a field is missing from your JSON and you can't figure out why, check capitalization before anything else.

Custom marshaling

For types that need a JSON representation different from their natural struct shape (a custom date format, an enum serialized as a string instead of its underlying int), implement MarshalJSON() ([]byte, error) and UnmarshalJSON([]byte) error on the type. This is the escape hatch when tags alone aren't expressive enough, used sparingly since it's more code to maintain than a tag.

Try it yourself: define a struct with a field tagged json:"-" and confirm it never appears in the marshaled output no matter what value you set it to.