Go
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions and Methods
-
Section 4: Concurrency
-
Section 5: Packages and Tooling
-
Section 6: More Standard Library
-
Section 7: Building Services
-
Section 8: Advanced Go
-
Section 9: Go in the Cloud-Native Ecosystem
-
Section 10: Data Structures and Algorithms in Go
-
Section 11: Testing and Deployment
-
Section 12: Practical Projects
-
Section 13: More Standard Library Practice
-
Section 14: More Practice Projects
-
Section 15: Design Patterns in Go
-
Section 16: Interview Practice
-
Section 17: Package fmt In Depth
-
Section 18: Package strings and strconv
-
Section 19: Package os and io
-
Section 20: Package time
-
Section 21: Package sort and container
-
Section 22: Package encoding
-
Section 23: Package net/http In Depth
-
Section 24: Package context
-
Section 25: Package regexp and bytes
-
Section 26: Package errors In Depth
-
Section 27: Package crypto and hash
-
Section 28: Package flag and log
-
Section 29: Package sync In Depth
-
Section 30: More Practice Exercises
-
Section 31: Go Modules and Workspaces In Depth
-
Section 32: Generics Deep Dive (Go 1.18+)
-
Section 33: Testing Package In Depth
-
Section 34: More Interview and Whiteboard Practice
-
Section 35: Package math and unicode
-
Section 36: Package path and filepath
-
Section 37: Package database/sql
-
Section 38: Package text/template and html/template
-
Section 39: Package archive and compress
-
Section 40: Lower-Level net Package
-
Section 41: Package runtime and reflect
-
Section 42: Package embed and io/fs
-
Section 43: Go Assembly and CGO Basics
-
Section 44: Building CLIs and TUIs
-
Section 45: Go Performance Tuning
-
Section 46: More Real-World Projects
-
Section 47: Go in Production
-
Section 48: Go Design Patterns
-
Section 49: Go Interfaces Deep Dive
-
Section 50: Final Practice and Review
49: Encoding and Decoding with encoding/json
Almost every Go service you write will eventually need to talk to the outside world, and in 99% of cases, that conversation happens in JSON. The encoding/json package is the standard tool for this, but there is a massive gap between "making it work" and "making it maintainable."
The convenience of map[string]any
When I first started with Go, I treated JSON like I did in JavaScript. I just wanted the data, and I didn't want to spend time defining a rigid structure for a response that might change. The naive approach is to unmarshal your JSON into a map[string]any (or map[string]interface{} in older versions of Go). It feels liberating because you don't have to define a struct; you just throw the bytes at a map and start digging.
// The "I'm in a hurry" approach
data := []byte(`{"city": "Seattle", "temp": 52.5, "conditions": "Rainy"}`)
var result map[string]any
if err := json.Unmarshal(data, &result); err != nil {
log.Fatal(err)
}
// Now comes the pain...
city := result["city"].(string)
temp := result["temp"].(float64)
This works fine for a tiny script, but it's a nightmare in a production codebase. Notice that type assertion .(string)? That's a ticking time bomb. If the API suddenly returns a null or an integer where you expected a string, your entire program panics. Plus, you lose all the benefits of Go's type system. You're essentially writing dynamically typed code inside a statically typed language, which defeats the purpose of using Go in the first place.
The type-safety of Structs
The better way—the way you should do it for any real feature—is to define a struct that mirrors the shape of the JSON. By doing this, you move the validation to the Unmarshal step. If the data doesn't fit the struct, you get an error immediately rather than a panic deep in your business logic three function calls later.
type WeatherResponse struct {
City string `json:"city"`
Temperature float64 `json:"temp"`
Conditions string `json:"conditions"`
}
var weather WeatherResponse
if err := json.Unmarshal(data, &weather); err != nil {
log.Fatal(err)
}
fmt.Printf("It is %f degrees in %s", weather.Temperature, weather.City)
Now, the compiler knows exactly what weather.Temperature is. No assertions, no guessing, and your IDE can actually autocomplete the fields for you. It's a bit more boilerplate up front, but it saves you from hours of debugging "interface conversion" panics.
Handling the gaps with JSON tags
You'll notice those backticked strings like `json:"city"`. These are struct tags, and they are where the real power lies. JSON keys are usually camelCase or snake_case, but Go requires fields to be PascalCase (exported) to be visible to the json package. Tags bridge that gap.
I often see developers struggle when an API returns a field that is sometimes missing. If you use a standard type, Go will just fill it with the zero value (like an empty string or 0). If you need to distinguish between "the API sent a 0" and "the API didn't send the field at all," you should use a pointer.
type UserProfile struct {
Username string `json:"username"`
Bio *string `json:"bio,omitempty"` // Pointer handles null/missing
}
The omitempty tag is another lifesaver. When you're encoding (converting a struct back to JSON), omitempty tells Go to leave the key out of the resulting JSON string if the value is empty. This keeps your API responses clean and prevents you from sending a bunch of "field": null values to your frontend clients.
📋 Practical Task
Build a GitHub Issue Parser
You need to write a small utility that consumes a JSON payload representing a GitHub issue. The payload contains nested data and some fields that might be missing.
Requirements:
- Create a struct
Issuethat maps to the JSON provided below. - The
userfield in the JSON is a nested object; create a separateUserstruct to handle this. - The
bodyof the issue is optional. Use a pointer and theomitemptytag so that if the body is empty, it doesn't appear when you encode the struct back to JSON. - Unmarshal the provided JSON string into your structs, then print the
User's loginand theIssue title.
// Use this input for your test:
jsonInput := `{"title": "Bug: Login fails on Safari", "body": "Happens every time I click submit", "user": {"login": "octocat", "id": 1}}`
There are no comments for now.