Pretty-Printing JSON in Go with MarshalIndent
Compact JSON is efficient for transport, but long documents can be difficult to inspect when everything appears on one line. Go’s standard encoding/json package includes json.MarshalIndent for producing human-readable JSON with line breaks and indentation.
Pretty-printed JSON is useful for debugging, generated configuration, logs intended for people, and command-line output.
1. Use json.MarshalIndent
package main
import (
"encoding/json"
"fmt"
)
func main() {
data := map[string]any{
"name": "Alice",
"age": 30,
"address": map[string]string{
"street": "123 Main St",
"city": "Wonderland",
},
"hobbies": []string{"reading", "hiking", "coding"},
}
prettyJSON, err := json.MarshalIndent(data, "", " ")
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println(string(prettyJSON))
}MarshalIndent accepts three arguments:
- The value to encode.
- A prefix added to each output line, often
"". - The indentation string, such as two spaces, four spaces, or a tab.
For example, two-space indentation looks like this:
prettyJSON, err := json.MarshalIndent(data, "", " ")2. Always Handle Encoding Errors
JSON encoding can fail. Unsupported values include channels, functions, complex numbers, and data containing cycles.
prettyJSON, err := json.MarshalIndent(data, "", " ")
if err != nil {
return fmt.Errorf("marshal JSON: %w", err)
}Returning or wrapping the error is usually more useful in library and application code than merely printing it.
3. Pretty-Print JSON from a File
If a file already contains JSON, you can decode and re-encode it:
fileContent, err := os.ReadFile("data.json")
if err != nil {
log.Fatalf("read file: %v", err)
}
var data any
if err := json.Unmarshal(fileContent, &data); err != nil {
log.Fatalf("decode JSON: %v", err)
}
prettyJSON, err := json.MarshalIndent(data, "", " ")
if err != nil {
log.Fatalf("encode JSON: %v", err)
}
fmt.Println(string(prettyJSON))For the specific job of formatting existing JSON bytes, json.Indent can avoid decoding into generic Go values:
var out bytes.Buffer
if err := json.Indent(&out, fileContent, "", " "); err != nil {
log.Fatal(err)
}
fmt.Print(out.String())4. Pretty JSON in an HTTP Response
For debugging or developer-facing endpoints, you can marshal an indented response manually:
func handler(w http.ResponseWriter, r *http.Request) {
data := map[string]any{
"status": "success",
"data": map[string]string{
"message": "Hello, World!",
},
}
prettyJSON, err := json.MarshalIndent(data, "", " ")
if err != nil {
http.Error(w, "could not encode response", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
_, _ = w.Write(prettyJSON)
}For production APIs, compact JSON is usually preferable because indentation increases response size. Pretty output is most useful when humans are expected to inspect the response directly.
Encoder.SetIndent for Streaming Output
When writing directly to an io.Writer, json.Encoder can be more convenient:
encoder := json.NewEncoder(os.Stdout)
encoder.SetIndent("", " ")
if err := encoder.Encode(data); err != nil {
log.Fatal(err)
}This avoids creating the complete encoded byte slice first and automatically writes a trailing newline.
Conclusion
Use json.MarshalIndent when you need an indented JSON byte slice, json.Indent when formatting existing JSON bytes, and json.Encoder.SetIndent when writing JSON directly to a stream. Keep compact output for bandwidth-sensitive APIs, and reserve pretty printing for places where readability matters.