How to Parse JSON in Go: Marshal, Unmarshal & Struct Tags
Parse JSON in Go with encoding/json: Unmarshal into structs, struct tags for field names, Marshal to produce JSON, decoding into maps and interfaces, and the exported-field rule that trips up beginners.
Go's standard library handles JSON through the encoding/json package — no third-party dependency required. The API is small but has a few rules that catch newcomers, especially around exported fields and struct tags. This guide covers parsing JSON in Go the idiomatic way: decoding into structs, controlling field names with tags, handling dynamic data, and encoding back to JSON.
encoding/json: Marshal and Unmarshal
The package gives you two core functions: json.Marshal encodes a Go value to JSON bytes, and json.Unmarshal decodes JSON bytes into a Go value. To parse, pass a pointer to a target: err := json.Unmarshal(data, &result). Always check the returned error — invalid JSON or a type mismatch is reported there, not by panic.
Decoding into a struct
The idiomatic approach decodes JSON into a struct whose fields mirror the JSON keys. Unmarshal matches incoming keys to struct fields case-insensitively. There is one critical rule: the fields you want populated must be exported — they must start with a capital letter. An unexported (lowercase) field is invisible to the json package and stays at its zero value, which is the single most common Go JSON mistake.
type Car struct {
Brand string `json:"brand"`
Doors int `json:"doors"`
}
var car Car
err := json.Unmarshal([]byte(data), &car)Struct tags: controlling field names
JSON keys are usually lowercase or snake_case, while exported Go fields are PascalCase. Struct tags bridge the two. After the field type, in backticks, write the tag json:"field_name" to map a JSON key to that field. So a field written as Name string with the tag json:"name" binds to the JSON key name.
Tag options add behavior: json:"-" omits a field entirely, json:"name,omitempty" skips it during encoding when the value is empty, and newer Go versions add omitzero for zero values. If you only have a JSON sample, a JSON to Go tool generates the struct with tags for you.
Dynamic JSON: maps and interfaces
When the shape is unknown, decode into map[string]interface{} or interface{} and type-assert values as you read them. JSON objects become maps, arrays become []interface{}, numbers become float64, and strings become string. This is flexible but verbose and unsafe compared to structs, so prefer a struct whenever the shape is known and reserve maps for genuinely dynamic data.
Encoding back to JSON
json.Marshal(value) returns the JSON encoding as a byte slice. For human-readable output, json.MarshalIndent(value, "", " ") adds indentation. Struct tags control the output key names and omitempty behavior, so the same tags that drive decoding also shape your encoded JSON — a tidy symmetry.
out, _ := json.MarshalIndent(car, "", " ")Streaming with Decoder and Encoder
For HTTP bodies and files, json.NewDecoder(r).Decode(&v) reads directly from an io.Reader without buffering the whole payload into memory, and json.NewEncoder(w).Encode(v) writes straight to an io.Writer. This streaming style is the standard way to handle request and response bodies in Go web services.
Validate before you debug
If Unmarshal returns an error or fields come back empty, check two things: that the JSON is valid and that your fields are exported with correct tags. Paste the payload into a validator to rule out malformed JSON, and a formatter to confirm the exact key names you need to tag.
Frequently asked questions
Why are my Go struct fields empty after Unmarshal?
Almost always because the fields are not exported. encoding/json can only set fields that start with a capital letter. Capitalize the field names and add json struct tags to map them to the JSON keys.
What are struct tags in Go JSON?
Struct tags are metadata in backticks after a field, like json:"name", that tell encoding/json which JSON key maps to that field. Options include omitempty to skip empty values and a single dash to ignore a field entirely.
How do I parse unknown JSON in Go?
Decode into map[string]interface{} or interface{}, then type-assert values as you access them. Objects become maps, arrays become []interface{}, and numbers become float64. Use this only when the structure is genuinely dynamic.
What is the difference between Unmarshal and Decoder?
json.Unmarshal parses a complete byte slice already in memory. json.NewDecoder(r).Decode reads from a stream such as an HTTP body or file without loading it all at once, which is preferred for request and response handling.
How do I pretty-print JSON in Go?
Use json.MarshalIndent(value, "", " ") with an empty prefix and a two-space indent string. It returns indented JSON bytes suitable for logs and files.
Related tools
Try these tools
JSON to Go Struct Generator
Infer Go struct definitions with JSON field tags from example JSON.
JSON Validator
Validate JSON instantly with precise, human-readable error messages and line numbers.
JSON Formatter & Beautifier
Format, prettify, and beautify messy JSON with configurable indentation and key sorting.
JSON Repair & Fix
Fix invalid JSON: trailing commas, quotes, unquoted keys, and LLM markdown fences.