25.6 log/slog: Structured Logging Built Into the Standard Library

Finally. For years, logging in Go felt like we were all collectively duct-taping our fmt.Printf statements into something resembling a professional application. We’d bolt on logrus or zap, which are fantastic, but it created a fragmented ecosystem. The Go team, in their infinite and sometimes frustrating wisdom, decided it was time to bring order to the chaos. Enter log/slog in Go 1.21: structured logging, right there in the standard library.

25.5 Handling Dynamic JSON: json.RawMessage and map[string]any

Alright, let’s talk about the moment every Go developer faces: when your JSON isn’t a nice, tidy struct. It’s a moving target. Maybe it’s a third-party API that sends different object shapes based on some "type" field, or a config file with deeply nested, arbitrary blobs. You can’t define a static struct ahead of time, so you reach for the universal key: interface{}. Or, as it’s been mercifully renamed in Go 1.18, any.

25.4 Custom JSON Marshaling: MarshalJSON and UnmarshalJSON

Right, so you’ve hit the point where encoding/json’s default behavior just isn’t cutting it. Maybe you need to send data in a snake_case API but your structs are in Go’s CamelCase. Maybe you need to parse a date string that looks nothing like time.RFC3339. Or perhaps you need to marshal a struct into something that isn’t a JSON object for once. This is where you roll up your sleeves and implement the json.Marshaler and json.Unmarshaler interfaces. They’re your escape hatch from the library’s sometimes overly-opinionated defaults.

25.3 json.Encoder and json.Decoder: Streaming JSON

Alright, let’s get our hands dirty with the real workhorses of the encoding/json package: json.Encoder and json.Decoder. You’ve probably met json.Marshal and json.Unmarshal—they’re fine for small, self-contained jobs. But when you’re dealing with streams of data, whether it’s from an HTTP response body, a file on disk, or a network socket, the Marshal/Unmarshal duo starts to feel like using a sledgehammer to crack a nut. They need the whole nut in your hand at once.

25.2 Struct Tags: json:"name,omitempty" and json:"-"

Right, let’s talk about struct tags. You’ve probably seen these little string literals clinging to your struct fields like metadata remoras. They look like magic incantations, and honestly, they kind of are. The encoding/json package uses them to figure out how to map your beautifully named Go struct fields to the often-absurdly named keys in the JSON you’re marshaling or unmarshaling. Without them, you’re at the mercy of the encoder’s default behavior, which is about as subtle as a brick.

25.1 json.Marshal and json.Unmarshal: Basic Serialization

Alright, let’s get our hands dirty with the workhorses of Go’s JSON story: json.Marshal and json.Unmarshal. These two functions are your primary gateway between the structured, type-safe world of Go and the flexible, but loosey-goosey, world of JSON. They seem simple on the surface, but the devil—and the real power—is in the details. Think of Marshal as your meticulous packer. You give it a Go thing (a struct, a map, a slice), and it carefully wraps it up into a neat []byte parcel, ready to be shipped over the network or dumped into a file. Unmarshal is the unpacker on the other side. It takes that []byte parcel and, with a bit of guidance from you on what you expect to find inside, tries to reassemble it into a Go thing on your side.

— joke —

...