Introduction au travail avec JSON en Go
JSON (JavaScript Object Notation) est un format d'échange de données léger largement utilisé dans les applications web, les API et les microservices. Le langage Go (Golang) fournit une bibliothèque intégrée puissante encoding/json qui facilite l'analyse (désérialisation) et la sérialisation des données. Dans cet article, nous allons examiner en détail comment travailler avec JSON en Go, en couvrant les principales fonctions, structures et bonnes pratiques.
Bases de la sérialisation et de la désérialisation
La sérialisation est le processus de conversion d'une structure de données Go (par exemple, une struct ou une slice) en une chaîne JSON. La désérialisation est le processus inverse — conversion du JSON en une structure Go.
Sérialisation (Marshal)
Pour la sérialisation, on utilise la fonction json.Marshal(). Elle prend une valeur Go et retourne une slice d'octets ([]byte) avec la représentation JSON.
package main
import ( "encoding/json" "fmt")
type User struct { Name string `json:"name"` Age int `json:"age"` Email string `json:"email,omitempty"`}
func main() { user := User{Name: "Alexey", Age: 30, Email: "alex@example.com"} jsonData, err := json.Marshal(user) if err != nil { fmt.Println("Serialization error:", err) return } fmt.Println(string(jsonData)) // Output: {"name":"Alexey","age":30,"email":"alex@example.com"}Notez les tags json:"field_name". Ils permettent de contrôler les noms des clés dans le JSON, ainsi que d'ajouter des options comme omitempty (ignorer le champ s'il est vide).
Désérialisation (Unmarshal)
Pour analyser du JSON dans une structure Go, on utilise la fonction json.Unmarshal(). Elle prend une slice d'octets et un pointeur vers la structure dans laquelle les données doivent être écrites.
func main() { jsonString := `{"name":"Maria","age":25,"email":"maria@example.com"}` var user User err := json.Unmarshal([]byte(jsonString), &user) if err != nil { fmt.Println("Parsing error:", err) return } fmt.Printf("Name: %s, Age: %d, Email: %s", user.Name, user.Age, user.Email)}Important : la structure doit être prédéfinie et ses champs doivent correspondre aux clés JSON (en tenant compte des tags).
Travail avec des objets JSON arbitraires
Parfois, la structure JSON est inconnue à l'avance ou est trop complexe. Dans ce cas, vous pouvez utiliser map[string]interface{} ou interface{}.
Utilisation de map[string]interface{}
func main() { jsonString := `{"name":"Ivan","age":35,"address":{"city":"Moscow","street":"Lenina"}}` var result map[string]interface{} err := json.Unmarshal([]byte(jsonString), &result) if err != nil { fmt.Println("Error:", err) return } fmt.Println(result["name"]) // Ivan fmt.Println(result["age"]) // 35 (type float64) // Pour les objets imbriqués, une assertion de type est nécessaire address := result["address"].(map[string]interface{}) fmt.Println(address["city"]) // Moscow}L'inconvénient de cette approche est la nécessité d'une assertion de type manuelle (.(map[string]interface{})), ce qui peut provoquer une panique si les types ne correspondent pas.
Utilisation de json.RawMessage
Si vous devez différer l'analyse d'une partie du JSON, utilisez json.RawMessage. Cela permet d'analyser d'abord la structure principale, puis de traiter les données imbriquées séparément.
type Response struct { Status string `json:"status"` Data json.RawMessage `json:"data"`}
func main() { jsonString := `{"status":"ok","data":{"user":"Peter","score":100}}` var resp Response json.Unmarshal([]byte(jsonString), &resp)
// Maintenant, Data peut être analysé séparément var userData map[string]interface{} json.Unm