Why does JSON in Go look like a mess and how to fix it?
\nHave you ever written a Go program that outputs JSON and gotten something like this in your terminal?
\n{"name":"PythonLib","year":2025,"tags":["go","json","tutorial"],"active":true}\nAgreed, reading this without special tools is not exactly a pleasure. Especially when the data structure is complex and the nesting goes several levels deep. Your eyes start to wander, and finding the right field becomes a real quest.
\nThe good news: Go has built-in tools that turn this "mess" into a neat, structured document. And today we'll cover all the ways to beautifully format JSON — from simple use of the standard library to creating your own pretty-print tool.
\nThis material is based on a real question from Stack Overflow (over 50,000 views), so the topic is truly in demand. Let's go!
\n\nWhat is pretty-print and why is it needed?
\nPretty-print (or "beautiful printing") is formatting data with indentation, line breaks, and alignment to make it human-readable. In the context of JSON, this means:
\n- \n
- Adding spaces or tabs for nested elements \n
- Putting each key-value pair on a new line \n
- Aligning values for clarity \n
Why is this needed in Go? Imagine you are debugging an API response, writing a logger, or simply outputting a configuration. Unformatted JSON takes up less space but is completely unreadable. Pretty-print solves this problem in a couple of lines of code.
\n\nMethod 1: json.MarshalIndent — the standard solution
\nThe simplest and recommended way is to use the json.MarshalIndent function from the standard encoding/json package. It does exactly what you need: takes data and returns formatted JSON with indentation.
Here is a basic example:
\npackage main\n\nimport (\n "encoding/json"\n "fmt"\n "log"\n)\n\ntype Site struct {\n Name string `json:"name"`\n Year int `json:"year"`\n Tags []string `json:"tags"`\n Active bool `json:"active"`\n}\n\nfunc main() {\n site := Site{\n Name: "PythonLib",\n Year: 2025,\n Tags: []string{"go", "json", "tutorial"},\n Active: true,\n }\n\n // Prefix — "", indent — " " (two spaces)\n prettyJSON, err := json.MarshalIndent(site, "", " ")\n if err != nil {\n log.Fatalf("Marshaling error: %v", err)\n }\n\n fmt.Println(string(prettyJSON))\n}\nResult:
\n{\n "name": "PythonLib",\n "year": 2025,\n "tags": [\n "go",\n "json",\n "tutorial"\n ],\n "active": true\n}\nBeautiful, right? Notice the second and third parameters of MarshalIndent:
- \n
- Prefix — a string added to the beginning of each line. Usually an empty string
"". \n - Indent — a string for each nesting level. Most often two spaces
" "or a tab"\\t"are used. \n
Configuring indentation: spaces vs tabs
\nYou can easily change the indentation style. For example, with a tab:
\nprettyJSON, _ := json.MarshalIndent(site, "", "\\t")\nfmt.Println(string(prettyJSON))\nResult:
\n{\n\t"name": "PythonLib",\n\t"year": 2025,\n\t"tags": [\n\t\t"go",\n\t\t"json",\n\t\t"tutorial"\n\t],\n\t"active": true\n}\nWhich option to choose? If your project follows Go standards (and they recommend tabs for code), use "\\t". If you work in a team where spaces are the norm, two spaces will be more appropriate. The main thing is consistency.
Method 2: Pretty-print for raw JSON strings
\nWhat if you already have a string with unformatted