Need help with your JSON?
Try our JSON Formatter tool to automatically identify and fix syntax errors in your JSON. JSON Formatter tool
Go Language JSON Formatting
Formatting JSON data is a common task in backend development. It involves taking structured JSON data, often represented as Go structs or maps, and converting it into a string format that adheres to the JSON specification. While simply converting to a JSON string is easy,formatting typically implies adding indentation and newlines to make the output human-readable. Go's standard library provides robust tools for this.
Why Format JSON?
Even though machines don't strictly need formatted JSON (parsers can handle compact JSON), formatting is crucial for:
- Readability: Makes inspecting JSON data in logs, debugging tools, or API responses much easier.
- Debugging: Quickly spot structural issues or incorrect values in complex JSON structures.
- Consistency: Provides a standard way to present JSON output from your application.
The Standard Library: encoding/json
Go's built-in encoding/json
package is the primary tool for working with JSON. It provides functions for both *marshaling* (Go data to JSON) and *unmarshaling* (JSON to Go data). For formatting, we primarily use the marshaling capabilities.
Basic Marshaling vs. Formatted Marshaling
The json.Marshal
function converts a Go value into a compact JSON byte slice.
Basic Marshal Example:
package main import ( "encoding/json" "fmt" ) type User struct { Name string Age int } func main() { user := User{"Alice", 30} // Marshal the struct into a JSON byte slice jsonData, err := json.Marshal(user) if err != nil { fmt.Println("Error marshaling JSON:", err) return } // jsonData will be []byte{'{"Name":"Alice","Age":30}'} fmt.Println(string(jsonData)) }
Output: {"Name":"Alice","Age":30}
To get a human-readable, indented output, you use the json.MarshalIndent
function. It takes the Go value and two additional string arguments: a prefix
and an indent
string.
Using json.MarshalIndent
MarshalIndent
signature:
func MarshalIndent(v any, prefix, indent string) ([]byte, error)
v any
: The Go value to encode.prefix string
: A string prepended to each line of the output. Commonly an empty string""
.indent string
: A string used for one level of indentation. Commonly"\t"
(tab) or" "
(two spaces).
Example with MarshalIndent
Using Two Spaces for Indentation:
package main import ( "encoding/json" "fmt" ) type Product struct { ID int `json:"id"` Name string `json:"name"` Price float64 `json:"price"` Tags []string `json:"tags,omitempty"` // omitempty means omit if slice is empty IsInStock bool `json:"isInStock,string"` // marshal bool as string "true" or "false" } func main() { product := Product{ ID: 101, Name: "Go T-Shirt", Price: 25.99, Tags: []string{"clothing", "go", "programming"}, IsInStock: true, } // Marshal with empty prefix and two spaces for indent jsonData, err := json.MarshalIndent(product, "", " ") if err != nil { fmt.Println("Error marshaling JSON:", err) return } // Output the formatted JSON string fmt.Println(string(jsonData)) }
Expected Output:
{ "id": 101, "name": "Go T-Shirt", "price": 25.99, "tags": [ "clothing", "go", "programming" ], "isInStock": "true" }
Using Tabs for Indentation:
// ... using the same Product struct and value as above ... func main() { product := Product{ /* ... values ... */ } // Marshal with empty prefix and a tab for indent jsonData, err := json.MarshalIndent(product, "", "\t") // Use "\t" for a tab if err != nil { fmt.Println("Error marshaling JSON:", err) return } fmt.Println(string(jsonData)) }
Expected Output:
{ "id": 101, "name": "Go T-Shirt", "price": 25.99, "tags": [ "clothing", "go", "programming" ], "isInStock": "true" }
Using a Prefix:
The prefix
string is added to the beginning of *every* line, including the first and the closing brace/bracket. This is less common for standard JSON formatting but can be useful for log messages or embedding JSON within other text formats.
// ... using the same Product struct and value as above ... func main() { product := Product{ /* ... values ... */ } // Marshal with prefix "LOG: " and two spaces for indent jsonData, err := json.MarshalIndent(product, "LOG: ", " ") if err != nil { fmt.Println("Error marshaling JSON:", err) return } fmt.Println(string(jsonData)) }
Expected Output:
LOG: { LOG: "id": 101, LOG: "name": "Go T-Shirt", LOG: "price": 25.99, LOG: "tags": [ LOG: "clothing", LOG: "go", LOG: "programming" LOG: ], LOG: "isInStock": "true" LOG: }
Handling Different Data Types
json.MarshalIndent
works seamlessly with various Go data types that can be encoded to JSON:
- Structs: Fields are encoded based on their exported names or `json` struct tags.
- Maps: Maps with string keys are encoded as JSON objects.
- Slices/Arrays: Encoded as JSON arrays.
- Primitive Types: Numbers, strings, booleans, null (for `nil` pointers/interfaces) are encoded directly.
Struct tags (`json:"..."`) are crucial for controlling how fields are named, omitted (`omitempty`), or encoded (`string`).
Streaming JSON Encoding
For very large JSON objects or arrays, marshaling the entire structure into memory using Marshal
or MarshalIndent
might consume excessive memory. In such cases, streaming encoders are preferred.
The json.NewEncoder
function creates an encoder that writes directly to an io.Writer
(like os.Stdout
, an http.ResponseWriter
, or a file).
Streaming Encoder Example:
package main import ( "encoding/json" "os" // We'll write to standard output ) type Item struct { Name string `json:"name"` Value int `json:"value"` } func main() { items := []Item{ {Name: "Apple", Value: 1}, {Name: "Banana", Value: 2}, {Name: "Cherry", Value: 3}, } // Create a new encoder that writes to os.Stdout encoder := json.NewEncoder(os.Stdout) // To get indented output with the encoder, use SetIndent encoder.SetIndent("", " ") // Prefix "", Indent " " // Encode the slice of items. It will be written directly to os.Stdout, formatted. err := encoder.Encode(items) if err != nil { // Handle error return } // The output is streamed to os.Stdout, formatted. }
Expected Output (to Standard Output):
[ { "name": "Apple", "value": 1 }, { "name": "Banana", "value": 2 }, { "name": "Cherry", "value": 3 } ]
Using json.NewEncoder
with SetIndent
is the most efficient way to output formatted JSON directly to a stream or response writer without buffering the entire formatted string in memory first.
Conclusion
Go's encoding/json
package provides simple yet powerful functions for formatting JSON output. For most common use cases, json.MarshalIndent
with appropriate prefix
and indent
strings is sufficient. For scenarios involving large datasets or direct output to network connections/files, the streaming approach with json.NewEncoder
and SetIndent
offers a more memory-efficient solution. Understanding these standard library tools is fundamental for effective JSON handling in Go applications.
Need help with your JSON?
Try our JSON Formatter tool to automatically identify and fix syntax errors in your JSON. JSON Formatter tool