How to Marshal (Encode) JSON in Go

Use the `encoding/json` package's `Marshal` function to convert Go data structures into JSON byte slices, or `MarshalIndent` if you need pretty-printed output with indentation.

Use the encoding/json package's Marshal function to convert Go data structures into JSON byte slices, or MarshalIndent if you need pretty-printed output with indentation. Remember that Marshal returns an error, so always check it before using the resulting bytes.

Here is a standard example using a struct with proper JSON tags to control field names and omit empty values:

package main

import (
	"encoding/json"
	"fmt"
	"log"
)

type User struct {
	ID    int    `json:"id"`
	Name  string `json:"name"`
	Email string `json:"email,omitempty"` // Omit if empty
}

func main() {
	user := User{ID: 1, Name: "Alice", Email: ""}

	// Encode to bytes
	data, err := json.Marshal(user)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(string(data))
	// Output: {"id":1,"name":"Alice"}
}

If you need the JSON formatted for human readability (e.g., for logging or debugging), use json.MarshalIndent instead. This function takes a prefix string and an indent string to control the formatting:

package main

import (
	"encoding/json"
	"fmt"
	"log"
)

type Config struct {
	Debug bool   `json:"debug"`
	Host  string `json:"host"`
	Port  int    `json:"port"`
}

func main() {
	cfg := Config{Debug: true, Host: "localhost", Port: 8080}

	// Encode with indentation
	data, err := json.MarshalIndent(cfg, "", "  ")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(string(data))
	// Output:
	// {
	//   "debug": true,
	//   "host": "localhost",
	//   "port": 8080
	// }
}

Key things to keep in mind:

  1. Tags are optional but recommended: Without json:"fieldName" tags, Go uses the struct field name in lowercase. Tags allow you to map to camelCase or other formats required by APIs.
  2. Error handling is mandatory: Marshal can fail if the data contains unsupported types (like channels or functions) or circular references. Never ignore the error return.
  3. Output is a byte slice: The result is []byte, not a string. Convert it with string(data) only when printing or logging; keep it as bytes if writing to a file or network stream to avoid unnecessary copying.
  4. Special types: Go automatically handles maps, slices, and pointers. If a pointer is nil, the field is omitted unless you use the omitempty tag logic differently or handle it explicitly.