JSON (JavaScript Object Notation) is a lightweight data interchange format. Go's encoding/json package provides built-in support for encoding Go data structures to JSON and decoding JSON into Go structures. Go uses struct tags to control JSON marshaling and unmarshaling behavior.
💡 Key Points
- Use struct tags to control JSON field names and behavior
- Exported (capitalized) fields are included in JSON encoding
- Use
json.Marshal() to encode and json.Unmarshal() to decode - The
omitempty tag omits zero-value fields from output - Use
json.Encoder and json.Decoder for streaming - Custom marshaling can be implemented via MarshalJSON/UnmarshalJSON interfaces
- Time values are encoded as RFC3339 strings by default
Encoding to JSON
Convert Go data structures to JSON using json.Marshal:
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Email string `json:"email"`
Address string `json:"address,omitempty"`
}
func main() {
p := Person{
Name: "Alice",
Age: 30,
Email: "alice@example.com",
}
jsonData, err := json.Marshal(p)
if err != nil {
fmt.Println("error:", err)
return
}
fmt. Println(string(jsonData))
}
Output:{"name":"Alice","age":30,"email":"alice@example.com"}
Decoding from JSON
Parse JSON into Go structures using json.Unmarshal:
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Email string `json:"email"`
}
func main() {
jsonStr := `{"name":"Bob","age":25,"email":"bob@example.com"}`
var p Person
err := json. Unmarshal([]byte(jsonStr), &p)
if err != nil {
fmt. Println("error:", err)
return
}
fmt.Printf("Name: %s, Age: %d, Email: %s\n", p. Name, p.Age, p. Email)
}
Output: Name: Bob, Age: 25, Email: bob@example.com
Working with Maps and Slices
JSON can be encoded/decoded to/from maps and slices for dynamic data:
package main
import (
"encoding/json"
"fmt"
)
func main() {
// Encode slice to JSON
colors := []string{"red", "green", "blue"}
colorJSON, _ := json.Marshal(colors)
fmt.Println("colors:", string(colorJSON))
// Encode map to JSON
data := map[string]interface{}{
"name": "Charlie",
"active": true,
"score": 95.5,
}
mapJSON, _ := json.Marshal(data)
fmt.Println("data:", string(mapJSON))
// Decode to map
var result map[string]interface{}
json.Unmarshal(mapJSON, &result)
fmt.Printf("decoded: %+v\n", result)
}
Output:colors: ["red","green","blue"]
data: {"active":true,"name":"Charlie","score":95.5}
decoded: map[active: true name:Charlie score:95.5]
Struct Tag Options
| Tag | Example | Description |
| Field Name | ` + "`json:\"field_name\"`" + ` | Sets JSON key name |
| omitempty | ` + "`json:\"field,omitempty\"`" + ` | Omit field if zero value |
| Ignore Field | ` + "`json:\"-\"`" + ` | Skip field entirely |
| Embedded Struct | ` + "`json:\",inline\"`" + ` | Flatten embedded struct fields |
| String Encoding | ` + "`json:\"field,string\"`" + ` | Encode number as string |
JSON Encoding Functions
| Function | Signature | Use Case |
| Marshal | Marshal(v interface{}) ([]byte, error) | Encode to compact JSON |
| MarshalIndent | MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) | Encode to formatted JSON |
| Unmarshal | Unmarshal(data []byte, v interface{}) error | Decode JSON into struct/map |
| NewEncoder | NewEncoder(w io.Writer) *Encoder | Create streaming encoder |
| NewDecoder | NewDecoder(r io.Reader) *Decoder | Create streaming decoder |
| Valid | Valid(data []byte) bool | Check if JSON is valid |
Type Mappings
| Go Type | JSON Type | Notes |
| bool | boolean | true or false |
| int, int8, int16, int32, int64 | number | Integer values |
| uint, uint8, uint16, uint32, uint64 | number | Unsigned integer values |
| float32, float64 | number | Decimal values |
| string | string | UTF-8 encoded text |
| []T, [n]T | array | Arrays and slices |
| map[string]T | object | String keys only |
| struct | object | Field names as keys |
| *T | null or value | Nil pointer becomes null |
| interface | any | Dynamic type based on value |
| time.Time | string | RFC3339 format |
Common JSON Patterns
| Pattern | Code | Use Case |
| Pretty Print | json. MarshalIndent(v, "", " ") | Human-readable JSON output |
| Stream Encoding | json.NewEncoder(w).Encode(v) | Write directly to io.Writer |
| Stream Decoding | json.NewDecoder(r).Decode(&v) | Read from io.Reader |
| Partial Decode | var data map[string]json.RawMessage | Decode parts of JSON later |
| Custom Marshal | func (t T) MarshalJSON() ([]byte, error) | Custom encoding logic |
| Custom Unmarshal | func (t *T) UnmarshalJSON(data []byte) error | Custom decoding logic |
| Validate JSON | if !json.Valid(data) { return err } | Check JSON syntax |
| Handle Unknown Fields | dec. DisallowUnknownFields() | Strict decoding |
Error Handling
| Error Type | Cause | Solution |
| SyntaxError | Invalid JSON syntax | Validate JSON format before unmarshaling |
| UnmarshalTypeError | JSON type doesn't match Go type | Check type compatibility or use interface |
| UnsupportedTypeError | Cannot marshal type (e.g., chan, func) | Use custom MarshalJSON or exclude field |
| UnsupportedValueError | Cannot encode value (e.g., NaN, Inf) | Check for invalid values before encoding |
| InvalidUnmarshalError | Unmarshal target is not pointer | Pass pointer to Unmarshal: &v |