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 prefixand indent strings is sufficient. For scenarios involving large datasets or direct output to network connections/files, the streaming approach with json.NewEncoderand 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