Skip to main content
โšก Calmops

Working with JSON in Go

Working with JSON in Go

JSON is a fundamental data format in modern applications. Go provides excellent built-in support for JSON encoding and decoding.

JSON Marshaling

Basic Marshaling

package main

import (
    "encoding/json"
    "fmt"
)

type Person struct {
    Name string
    Age  int
    Email string
}

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

    // Marshal to JSON
    data, err := json.Marshal(p)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    fmt.Println(string(data))
    // Output: {"Name":"Alice","Age":30,"Email":"[email protected]"}
}

Pretty Printing

package main

import (
    "encoding/json"
    "fmt"
)

type Person struct {
    Name string
    Age  int
}

func main() {
    p := Person{Name: "Alice", Age: 30}

    // Pretty print
    data, _ := json.MarshalIndent(p, "", "  ")
    fmt.Println(string(data))
    // Output:
    // {
    //   "Name": "Alice",
    //   "Age": 30
    // }
}

Struct Tags

JSON Tags

package main

import (
    "encoding/json"
    "fmt"
)

type User struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email,omitempty"`
    Age   int    `json:"-"`  // Ignored
}

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

    data, _ := json.Marshal(u)
    fmt.Println(string(data))
    // Output: {"id":1,"name":"Alice","email":"[email protected]"}
}

Tag Options

package main

import (
    "encoding/json"
    "fmt"
)

type Product struct {
    ID       int     `json:"id"`
    Name     string  `json:"name"`
    Price    float64 `json:"price,omitempty"`
    Internal string  `json:"-"`
    Required string  `json:"required"`
}

func main() {
    p := Product{
        ID:       1,
        Name:     "Laptop",
        Price:    999.99,
        Internal: "secret",
        Required: "value",
    }

    data, _ := json.MarshalIndent(p, "", "  ")
    fmt.Println(string(data))
}

JSON Unmarshaling

Basic Unmarshaling

package main

import (
    "encoding/json"
    "fmt"
)

type Person struct {
    Name string
    Age  int
}

func main() {
    jsonStr := `{"Name":"Alice","Age":30}`

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

    fmt.Printf("%+v\n", p)
    // Output: {Name:Alice Age:30}
}

Unmarshaling with Tags

package main

import (
    "encoding/json"
    "fmt"
)

type User struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

func main() {
    jsonStr := `{"id":1,"name":"Alice","email":"[email protected]"}`

    var u User
    json.Unmarshal([]byte(jsonStr), &u)
    fmt.Printf("%+v\n", u)
}

Working with Dynamic JSON

Using map[string]interface

package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    jsonStr := `{"name":"Alice","age":30,"city":"New York"}`

    var data map[string]interface{}
    json.Unmarshal([]byte(jsonStr), &data)

    fmt.Println(data["name"])    // Alice
    fmt.Println(data["age"])     // 30
    fmt.Println(data["city"])    // New York

    // Type assertion
    if name, ok := data["name"].(string); ok {
        fmt.Println("Name:", name)
    }
}

Using json.RawMessage

package main

import (
    "encoding/json"
    "fmt"
)

type Message struct {
    Type string          `json:"type"`
    Data json.RawMessage `json:"data"`
}

func main() {
    jsonStr := `{"type":"user","data":{"name":"Alice","age":30}}`

    var msg Message
    json.Unmarshal([]byte(jsonStr), &msg)

    fmt.Println("Type:", msg.Type)
    fmt.Println("Data:", string(msg.Data))

    // Parse data later
    var userData map[string]interface{}
    json.Unmarshal(msg.Data, &userData)
    fmt.Println("User data:", userData)
}

Streaming JSON

Encoder and Decoder

package main

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

type Person struct {
    Name string
    Age  int
}

func main() {
    // Encoding to string
    var buf strings.Builder
    encoder := json.NewEncoder(&buf)

    p := Person{Name: "Alice", Age: 30}
    encoder.Encode(p)

    fmt.Println(buf.String())

    // Decoding from string
    jsonStr := `{"Name":"Bob","Age":25}`
    decoder := json.NewDecoder(strings.NewReader(jsonStr))

    var person Person
    decoder.Decode(&person)
    fmt.Printf("%+v\n", person)
}

Custom JSON Marshaling

Implementing Marshaler

package main

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

type Event struct {
    Name      string
    Timestamp time.Time
}

func (e Event) MarshalJSON() ([]byte, error) {
    type Alias Event
    return json.Marshal(&struct {
        Timestamp string `json:"timestamp"`
        *Alias
    }{
        Timestamp: e.Timestamp.Format(time.RFC3339),
        Alias:     (*Alias)(&e),
    })
}

func main() {
    e := Event{
        Name:      "Conference",
        Timestamp: time.Now(),
    }

    data, _ := json.MarshalIndent(e, "", "  ")
    fmt.Println(string(data))
}

Best Practices

โœ… Good: Use Struct Tags

// DO: Use struct tags for JSON mapping
type User struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email,omitempty"`
}

โŒ Bad: Rely on Field Names

// DON'T: Assume field names match JSON keys
type User struct {
    ID    int
    Name  string
    Email string
}

โœ… Good: Handle Errors

// DO: Check for unmarshaling errors
var data interface{}
err := json.Unmarshal(jsonBytes, &data)
if err != nil {
    fmt.Println("Invalid JSON:", err)
}

โœ… Good: Use Pointers for Unmarshaling

// DO: Use pointers for unmarshaling
var p Person
json.Unmarshal(data, &p)

Summary

Go’s JSON support provides:

  1. Easy marshaling to JSON strings
  2. Flexible unmarshaling from JSON
  3. Struct tags for field mapping
  4. Dynamic JSON handling with maps
  5. Streaming with Encoder/Decoder
  6. Custom marshaling for complex types

These features make JSON handling straightforward in Go.

Comments