Working with JSON in Go

Go has excellent built-in support for JSON through the encoding/json package. This guide covers marshaling, unmarshaling, validation, and best practices for handling JSON in Go.

Unmarshaling JSON (Parsing)

Use json.Unmarshal() to parse JSON into Go structs:

package main

import (
    "encoding/json"
    "fmt"
)

type User struct {
    Name    string `json:"name"`
    Age     int    `json:"age"`
    Email   string `json:"email"`
    IsAdmin bool   `json:"is_admin"`
}

func main() {
    jsonStr := `{"name": "John", "age": 30, "email": "john@example.com", "is_admin": true}`

    var user User
    err := json.Unmarshal([]byte(jsonStr), &user)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    fmt.Println(user.Name)    // John
    fmt.Println(user.Age)     // 30
    fmt.Println(user.IsAdmin) // true
}

Marshaling JSON (Serializing)

Use json.Marshal() to convert Go values to JSON:

package main

import (
    "encoding/json"
    "fmt"
)

type User struct {
    Name    string   `json:"name"`
    Age     int      `json:"age"`
    Hobbies []string `json:"hobbies"`
    Active  bool     `json:"active"`
}

func main() {
    user := User{
        Name:    "Jane",
        Age:     25,
        Hobbies: []string{"reading", "coding"},
        Active:  true,
    }

    // Basic marshaling
    jsonBytes, err := json.Marshal(user)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    fmt.Println(string(jsonBytes))

    // Pretty print with indentation
    prettyBytes, _ := json.MarshalIndent(user, "", "  ")
    fmt.Println(string(prettyBytes))
}

JSON Validation

package main

import (
    "encoding/json"
    "fmt"
)

func isValidJSON(str string) bool {
    var js json.RawMessage
    return json.Unmarshal([]byte(str), &js) == nil
}

type ValidationResult struct {
    Valid   bool   `json:"valid"`
    Error   string `json:"error,omitempty"`
    Type    string `json:"type,omitempty"`
}

func validateJSON(str string) ValidationResult {
    var js interface{}
    err := json.Unmarshal([]byte(str), &js)

    if err != nil {
        return ValidationResult{
            Valid: false,
            Error: err.Error(),
        }
    }

    var jsonType string
    switch js.(type) {
    case map[string]interface{}:
        jsonType = "object"
    case []interface{}:
        jsonType = "array"
    default:
        jsonType = "primitive"
    }

    return ValidationResult{
        Valid: true,
        Type:  jsonType,
    }
}

func main() {
    fmt.Println(isValidJSON(`{"name": "John"}`)) // true
    fmt.Println(isValidJSON("{invalid}"))        // false
}

Working with Files

package main

import (
    "encoding/json"
    "os"
)

type User struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

func main() {
    // Write JSON to file
    user := User{Name: "John", Age: 30}
    file, _ := os.Create("output.json")
    defer file.Close()

    encoder := json.NewEncoder(file)
    encoder.SetIndent("", "  ")
    encoder.Encode(user)

    // Read JSON from file
    readFile, _ := os.Open("output.json")
    defer readFile.Close()

    var readUser User
    json.NewDecoder(readFile).Decode(&readUser)
}

Struct Tags

Go uses struct tags to control JSON field names and behavior:

Best Practices