JSON (JavaScript Object Notation) is a popular format for exchanging data between a client and a server. In Go, working with JSON strings is straightforward thanks to the encoding/json package, which provides various methods to marshal and unmarshal JSON data.

Basic Usage: Encoding and Decoding JSON

In the most basic form, you can encode Go data structures to JSON and decode JSON back to Go data structures.

Encoding (Marshalling) Go Struct to JSON

package main

import (
    "encoding/json"
    "fmt"
)

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

func main() {
    user := User{
        Name:  "Alice",
        Email: "[email protected]",
        Age:   30,
    }

    jsonData, err := json.Marshal(user)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(string(jsonData)) // Output: {"name":"Alice","email":"[email protected]","age":30}
}

Decoding (Unmarshalling) JSON to Go Struct

package main

import (
    "encoding/json"
    "fmt"
)

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

func main() {
    jsonString := `{"name":"Bob","email":"[email protected]","age":25}`

    var user User
    err := json.Unmarshal([]byte(jsonString), &user)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Printf("%+v\n", user) // Output: {Name:Bob Email:[email protected] Age:25}
}

Intermediate Usage: Working with JSON Data Without Defined Structs

You can also work with arbitrary JSON data without defining a struct using interface{} and type assertions.

Decoding JSON to Map

package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    jsonString := `{"title":"Gopher","tags":["go","programming"]}`
    
    var result map[string]interface{}
    json.Unmarshal([]byte(jsonString), &result)

    fmt.Println(result["title"])            // Output: Gopher
    fmt.Println(result["tags"].([]interface{})[0]) // Output: go
}

Advanced Usage: Handling JSON with Custom Unmarshalling

Sometimes, JSON structures are not straightforward and might require custom unmarshalling logic to populate Go structs.

Custom Unmarshalling with Example

package main

import (
    "encoding/json"
    "fmt"
)

type ComplexUser struct {
    Name      string
    Attributes map[string]string
}

func (cu *ComplexUser) UnmarshalJSON(data []byte) error {
    var tempStruct struct {
        Name      string            `json:"name"`
        Attributes map[string]interface{} `json:"attributes"`
    }
    
    if err := json.Unmarshal(data, &tempStruct); err != nil {
        return err
    }
    
    cu.Name = tempStruct.Name
    cu.Attributes = make(map[string]string)
    for k, v := range tempStruct.Attributes {
        if str, ok := v.(string); ok {
            cu.Attributes[k] = str
        }
    }
    return nil
}

func main() {
    jsonString := `{"name":"Charlie","attributes":{"nickname":"Char","hobby":"Books"}}`

    var user ComplexUser
    json.Unmarshal([]byte(jsonString), &user)

    fmt.Printf("%+v\n", user) // Output: {Name:Charlie Attributes:map[hobby:Books nickname:Char]}
}

By following these steps, you can manage simple and complex JSON data within your Go applications effectively.