DevToolBoxGRATUIT
Blog

JSON vers struct Go : Guide complet de conversion avec exemples

9 min de lecturepar DevToolBox

Travailler avec JSON en Go necessite la definition de types struct qui correspondent a votre structure JSON. Ce guide couvre le mapping de types, les structures imbriquees, les champs optionnels et les bonnes pratiques.

Convertissez JSON en structs Go avec notre outil gratuit.

Bases du mapping de types JSON vers Go

Chaque type JSON a un type Go correspondant.

JSON Type          Go Type              Example
─────────────────────────────────────────────────────────
string             string               "hello" → "hello"
number (integer)   int / int64          42 → 42
number (float)     float64              3.14 → 3.14
boolean            bool                 true → true
null               *T (pointer)         null → nil
object             struct               {...} → MyStruct{}
array of strings   []string             ["a","b"] → []string{"a","b"}
array of objects   []MyStruct           [{...}] → []MyStruct{...}
array of mixed     []interface{}        [1,"a",true] → []interface{}{...}
dynamic object     map[string]any       {...} → map[string]any{...}

Pour les valeurs null, utilisez des types pointeurs (*string, *int).

Tags de struct JSON : Guide essentiel

Les tags indiquent au package encoding/json comment mapper les cles JSON aux champs.

package main

import (
    "encoding/json"
    "fmt"
)

// Basic struct with json tags
type User struct {
    // Field name → json key mapping
    ID        int    `json:"id"`              // "id" in JSON
    FirstName string `json:"first_name"`      // "first_name" in JSON
    LastName  string `json:"last_name"`       // "last_name" in JSON
    Email     string `json:"email"`           // "email" in JSON
    Age       int    `json:"age,omitempty"`   // skip if zero value
    Password  string `json:"-"`               // always skip (never serialize)

    // Without tag - uses field name as-is (case-insensitive unmarshal)
    Username string                            // matches "Username", "username", etc.
}

func main() {
    jsonData := `{
        "id": 1,
        "first_name": "Alice",
        "last_name": "Smith",
        "email": "alice@example.com",
        "age": 0,
        "Username": "alice123"
    }`

    var user User
    if err := json.Unmarshal([]byte(jsonData), &user); err != nil {
        panic(err)
    }

    fmt.Printf("Name: %s %s\n", user.FirstName, user.LastName)
    // Output: Name: Alice Smith

    // Marshal back - age is omitted because it's 0 (omitempty)
    output, _ := json.MarshalIndent(user, "", "  ")
    fmt.Println(string(output))
}

Les champs doivent etre exportes (majuscule) pour etre visibles par le package JSON.

Objets JSON imbriques

Les objets imbriques necessitent des definitions de struct separees.

// JSON input:
// {
//   "id": 1,
//   "name": "Acme Corp",
//   "address": {
//     "street": "123 Main St",
//     "city": "Springfield",
//     "state": "IL",
//     "zip": "62701",
//     "coordinates": {
//       "lat": 39.7817,
//       "lng": -89.6501
//     }
//   },
//   "contacts": [
//     { "name": "Alice", "role": "CEO", "email": "alice@acme.com" },
//     { "name": "Bob", "role": "CTO", "email": "bob@acme.com" }
//   ]
// }

// Define separate structs for each nested level
type Coordinates struct {
    Lat float64 `json:"lat"`
    Lng float64 `json:"lng"`
}

type Address struct {
    Street      string      `json:"street"`
    City        string      `json:"city"`
    State       string      `json:"state"`
    Zip         string      `json:"zip"`
    Coordinates Coordinates `json:"coordinates"`
}

type Contact struct {
    Name  string `json:"name"`
    Role  string `json:"role"`
    Email string `json:"email"`
}

type Company struct {
    ID       int       `json:"id"`
    Name     string    `json:"name"`
    Address  Address   `json:"address"`
    Contacts []Contact `json:"contacts"`
}

Definissez chaque type de struct separement pour la lisibilite et la reutilisation.

Tableaux et slices

Les tableaux JSON correspondent aux slices Go.

// Different array types in JSON → Go
type Product struct {
    Name       string   `json:"name"`
    Tags       []string `json:"tags"`        // ["electronics", "sale"]
    Prices     []float64 `json:"prices"`     // [29.99, 39.99, 49.99]
    Ratings    []int    `json:"ratings"`      // [5, 4, 5, 3]
    IsActive   []bool   `json:"flags"`        // [true, false, true]

    // Array of objects
    Reviews    []Review `json:"reviews"`

    // Nested array of arrays (matrix)
    Matrix     [][]int  `json:"matrix"`       // [[1,2],[3,4]]

    // Mixed-type array (rare but possible)
    Metadata   []interface{} `json:"metadata"` // [1, "hello", true]
}

type Review struct {
    Author  string `json:"author"`
    Rating  int    `json:"rating"`
    Comment string `json:"comment"`
}

// Empty arrays vs null arrays
type Response struct {
    // null in JSON → nil slice (len=0, cap=0, == nil)
    // [] in JSON → empty slice (len=0, cap=0, != nil)
    // omitempty skips nil slices but NOT empty slices
    Items []Item `json:"items,omitempty"`
}

Champs optionnels et nullables

Go a trois patterns pour les champs optionnels :

// Pattern 1: omitempty - skip zero values when marshaling
type UpdateRequest struct {
    Name  string `json:"name,omitempty"`  // "" is skipped
    Age   int    `json:"age,omitempty"`   // 0 is skipped
    Admin bool   `json:"admin,omitempty"` // false is skipped
}

// Problem: Can't distinguish "age not provided" from "age is 0"

// Pattern 2: Pointer types - distinguish null/missing from zero
type UpdateRequestV2 struct {
    Name  *string `json:"name,omitempty"`  // nil = not provided, "" = empty
    Age   *int    `json:"age,omitempty"`   // nil = not provided, 0 = zero
    Admin *bool   `json:"admin,omitempty"` // nil = not provided, false = false
}

// Helper function to create pointers (Go doesn't allow &literal)
func ptr[T any](v T) *T { return &v }

// Usage:
// req := UpdateRequestV2{
//     Name: ptr("Alice"),
//     Age:  ptr(0),      // explicitly set to 0, not nil
// }

// Pattern 3: json.RawMessage - defer parsing
type Event struct {
    Type    string          `json:"type"`
    Payload json.RawMessage `json:"payload"` // raw JSON bytes
}

// Parse payload based on type
func (e *Event) ParsePayload() (interface{}, error) {
    switch e.Type {
    case "user_created":
        var u User
        return &u, json.Unmarshal(e.Payload, &u)
    case "order_placed":
        var o Order
        return &o, json.Unmarshal(e.Payload, &o)
    default:
        var m map[string]interface{}
        return &m, json.Unmarshal(e.Payload, &m)
    }
}

Choisissez le pattern adapte : omitempty pour les valeurs zero acceptables, pointeurs pour distinguer "non fourni" et "valeur zero".

Marshaling JSON personnalise

Implementez json.Marshaler et json.Unmarshaler pour une logique personnalisee.

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

// Custom date format (JSON uses "2006-01-02", not RFC3339)
type Date struct {
    time.Time
}

const dateFormat = "2006-01-02"

func (d *Date) UnmarshalJSON(data []byte) error {
    // Remove quotes from JSON string
    s := strings.Trim(string(data), `"`)
    if s == "null" || s == "" {
        return nil
    }
    t, err := time.Parse(dateFormat, s)
    if err != nil {
        return fmt.Errorf("invalid date format: %s", s)
    }
    d.Time = t
    return nil
}

func (d Date) MarshalJSON() ([]byte, error) {
    if d.Time.IsZero() {
        return []byte("null"), nil
    }
    return json.Marshal(d.Time.Format(dateFormat))
}

// Usage in a struct
type Employee struct {
    Name      string `json:"name"`
    StartDate Date   `json:"start_date"`
    EndDate   *Date  `json:"end_date,omitempty"`
}

// Custom enum type
type Status string

const (
    StatusActive   Status = "active"
    StatusInactive Status = "inactive"
    StatusPending  Status = "pending"
)

func (s *Status) UnmarshalJSON(data []byte) error {
    var str string
    if err := json.Unmarshal(data, &str); err != nil {
        return err
    }
    switch Status(str) {
    case StatusActive, StatusInactive, StatusPending:
        *s = Status(str)
        return nil
    default:
        return fmt.Errorf("invalid status: %s", str)
    }
}

Cas courants : formats de date, conversion de types, enums, aplatissement de structures.

Patterns API reels

Patterns JSON courants avec les APIs REST et leur modelisation en Go.

// Pattern: Paginated API Response
type PaginatedResponse[T any] struct {
    Data       []T    `json:"data"`
    Total      int    `json:"total"`
    Page       int    `json:"page"`
    PerPage    int    `json:"per_page"`
    TotalPages int    `json:"total_pages"`
    NextURL    string `json:"next_url,omitempty"`
    PrevURL    string `json:"prev_url,omitempty"`
}

// Pattern: API Error Response
type APIError struct {
    Code    int               `json:"code"`
    Message string            `json:"message"`
    Details map[string]string `json:"details,omitempty"`
}

func (e *APIError) Error() string {
    return fmt.Sprintf("[%d] %s", e.Code, e.Message)
}

// Pattern: Wrapper for success/error responses
type APIResponse[T any] struct {
    Success bool      `json:"success"`
    Data    *T        `json:"data,omitempty"`
    Error   *APIError `json:"error,omitempty"`
}

// Pattern: Webhook payload with dynamic event data
type WebhookPayload struct {
    ID        string          `json:"id"`
    Event     string          `json:"event"`
    Timestamp time.Time       `json:"timestamp"`
    Data      json.RawMessage `json:"data"`
}

// Pattern: Configuration file with defaults
type Config struct {
    Host     string `json:"host"`
    Port     int    `json:"port"`
    Debug    bool   `json:"debug"`
    LogLevel string `json:"log_level"`
    Database struct {
        URL             string `json:"url"`
        MaxConnections  int    `json:"max_connections"`
        ConnectTimeout  int    `json:"connect_timeout_ms"`
    } `json:"database"`
}

// Set defaults before unmarshaling
func NewConfig() *Config {
    return &Config{
        Host:     "localhost",
        Port:     8080,
        LogLevel: "info",
    }
}

func LoadConfig(data []byte) (*Config, error) {
    cfg := NewConfig()
    if err := json.Unmarshal(data, cfg); err != nil {
        return nil, err
    }
    return cfg, nil
}

Generation automatique de structs

Notre outil JSON to Go genere automatiquement des definitions de struct correctement taguees.

Convertissez JSON en structs Go avec notre outil gratuit.

Pour CI/CD, utilisez json-to-go en ligne de commande ou go-jsonschema.

# Generate Go structs from a JSON file
cat api-response.json | json-to-go

# Generate from a JSON Schema definition
go install github.com/atombender/go-jsonschema/cmd/gojsonschema@latest
gojsonschema -p models schema.json

# Validate your structs handle the JSON correctly
go test -run TestUnmarshal ./models/...

Questions frequentes

Comment convertir JSON en struct Go ?

Definissez un struct avec des champs exportes et des tags json. Utilisez notre outil en ligne.

Qu'est-ce que omitempty en Go ?

omitempty saute les champs a valeur zero lors du marshaling. Utile pour les requetes PATCH.

Comment gerer les valeurs null ?

Utilisez des types pointeurs (*string, *int, *bool). Le pointeur sera nil quand JSON est null.

Peut-on avoir des noms JSON differents ?

Oui, avec les tags de struct : `json:"user_name"` mappe la cle JSON au champ Go.

Comment gerer les champs JSON dynamiques ?

map[string]interface{} pour du JSON dynamique, json.RawMessage pour le parsing differe.

Convertir JSON en structs Go correctement est fondamental. Utilisez notre outil en ligne ou etablissez des conventions de tags coherentes.

Essayez le convertisseur JSON to Go.

Related Developer Tools and Guides

𝕏 Twitterin LinkedIn
Cet article vous a-t-il aidé ?

Restez informé

Recevez des astuces dev et les nouveaux outils chaque semaine.

Pas de spam. Désabonnez-vous à tout moment.

Essayez ces outils associés

GoJSON to Go Struct{ }JSON Formatter

Articles connexes

JSON vers Go Struct : Stratégies de mapping et bonnes pratiques

Maßtrisez la conversion JSON en struct Go. Tags de struct, types imbriqués, omitempty, marshaling personnalisé et patterns réels.

JSON vs YAML vs TOML : Quel format de config choisir ?

Comparez les formats JSON, YAML et TOML pour vos fichiers de configuration.

REST API Best Practices : Le guide complet pour 2026

Apprenez les meilleures pratiques de conception REST API : conventions de nommage, gestion des erreurs, authentification, pagination et sécurité.