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:
- Easy marshaling to JSON strings
- Flexible unmarshaling from JSON
- Struct tags for field mapping
- Dynamic JSON handling with maps
- Streaming with Encoder/Decoder
- Custom marshaling for complex types
These features make JSON handling straightforward in Go.
Comments