Skip to content
Back to Blog
jsongolangcode-generationdeveloper-tools

Guide pratique : convertir du JSON en structure Go

Écrire manuellement des structures Go à partir de JSON d'API est source d'erreurs : mauvais types, balises manquantes, imbrications cassées. Ce guide explique comment les générateurs infèrent les types et comment convertir du JSON en structure Go en un clic.

SZ
Founder, Molixa
14 min read
Partager
Guide pratique : convertir du JSON en structure Go
Table of contents7 sections

Convertir du JSON en une structure Go signifie transformer un exemple de charge utile JSON en un type Go avec les bons types de champs, des noms exportés et des balises json: pour pouvoir Unmarshal proprement. Un générateur fait cela en inspectant la valeur de chaque clé et en déduisant un type Go, puis en mappant le nom de la clé JSON à un champ exporté en PascalCase avec une balise correspondante. Ce guide montre exactement comment fonctionne cette déduction, où elle échoue et comment obtenir une structure correcte sans taper une seule accolade à la main.

La plupart des développeurs rencontrent le même obstacle : une API renvoie un objet JSON avec 40 clés, et écrire la structure correspondante à la main est lent et source d'erreurs. Vous oubliez une balise, devinez int alors que l'API envoie un flottant, ou aplatissez un objet imbriqué qui devrait être son propre type. Le résultat est un Unmarshal qui ignore silencieusement des champs. La solution est de comprendre les règles qu'un convertisseur applique, afin de pouvoir faire confiance à sa sortie et corriger les cas qu'il ne peut pas deviner.

Comment un convertisseur JSON vers struct Go déduit les types#

Un convertisseur parcourt la valeur JSON une par une. JSON n'a que six types de valeurs (chaîne, nombre, booléen, objet, tableau, null), mais Go en a beaucoup. La correspondance est généralement déterministe, avec deux cas vraiment ambigus à connaître.

Voici la correspondance de base utilisée par tous les générateurs :

Valeur JSONType Go déduitRemarques
"bonjour"stringToujours sans ambiguïté
true / falseboolToujours sans ambiguïté
42intLes nombres entiers par défaut sont int
3.14float64Tout nombre décimal devient float64
{ ... }struct imbriquéDevient son propre type nommé
[ ... ][]TType d'élément déduit des éléments
nullinterface{}Aucune information de type disponible

Les deux cas qui méritent votre attention sont les nombres et null. Tout le reste, le convertisseur le gère correctement à chaque fois.

Pourquoi int vs float64 est le choix le plus délicat#

JSON n'a pas de type entier. Le nombre 5 et le nombre 5.0 sont tous deux simplement "nombre" pour un analyseur JSON. Un générateur doit deviner : si le littéral n'a pas de point décimal, il choisit int ; s'il voit un point décimal, il choisit float64.

Cette heuristique échoue de manière courante. Imaginez un champ API prix qui est 100 dans votre échantillon mais généralement 100.50. Le convertisseur voit un nombre entier et écrit int. La réponse suivante avec 100.50 échoue alors à se désérialiser en int, ou pire, tronque.

Vérifiez toujours les champs numériques par rapport à la documentation de l'API, pas seulement votre échantillon. Si un champ peut contenir une fraction, changez manuellement le int généré en float64. Un seul échantillon propre n'est pas un schéma.

Il existe aussi l'option json.Number. Si la précision compte (grands IDs, argent, tout ce que vous ne pouvez pas arrondir), décodez en json.Number ou en chaîne et analysez délibérément. Les générateurs utilisent par défaut float64 pour les décimaux, ce qui convient pour la plupart des données mais est imprécis pour les IDs entiers 64 bits au-delà de 2^53.

Quand vous obtenez un type interface vide#

Si une valeur JSON est null, le convertisseur n'a rien pour déduire. Il n'y a pas de type, seulement l'absence de type. Il utilise donc interface{} (écrit any en Go moderne).

La même chose se produit pour les tableaux de types mixtes. Un tableau JSON comme [1, "deux", true] n'a pas de type d'élément unique, donc le générateur produit []interface{}. C'est techniquement correct mais horrible à utiliser, car vous perdez toute sécurité de type à la compilation et devez effectuer une assertion de type sur chaque élément.

Quand vous voyez interface{} dans la sortie générée, traitez-le comme un indicateur disant "Je n'ai pas pu déterminer cela, décidez vous-même." Généralement, la bonne action est de fournir au convertisseur un échantillon plus riche où ce champ est renseigné, puis de régénérer.

Balises de champ, noms exportés et omitempty#

Bien choisir les types est la moitié du travail. L'autre moitié concerne les balises de structure json: et le nommage des champs, là où les structures écrites à la main échouent le plus souvent.

Pourquoi les champs doivent être exportés (en majuscule)#

Le paquet encoding/json de Go ne peut lire et écrire que les champs de structure exportés, c'est-à-dire ceux dont le nom commence par une majuscule. Un champ non exporté (en minuscule) est invisible pour Unmarshal et Marshal. Il restera silencieusement à sa valeur zéro sans erreur.

Ainsi, une clé JSON user_name ne peut pas correspondre à un champ Go nommé user_name. Le convertisseur le renomme en UserName (exporté) et ajoute une balise pour faire le pont :

type User struct {
    UserName string `json:"user_name"`
    Email    string `json:"email"`
}

La balise json:"user_name" indique au paquet JSON que ce champ exporté UserName correspond à la clé JSON user_name. Sans cette balise, Go chercherait une clé nommée UserName (il effectue une correspondance insensible à la casse en dernier recours, mais s'y fier est risqué). La conversion de snake_case ou camelCase en PascalCase avec une balise explicite est exactement ce qu'un bon générateur automatise.

Ce que change réellement omitempty#

L'option omitempty apparaît dans des balises comme json:"email,omitempty". Elle n'affecte que le marshaling (de la structure Go vers JSON), jamais l'unmarshaling. Lorsque vous encodez une structure, omitempty indique à l'encodeur d'ignorer un champ si sa valeur est la valeur zéro (chaîne vide, 0, false, nil, slice ou map vide).

Cela a son importance pour la sortie d'une API. Comparez la même structure avec et sans :

BaliseValeur du champSortie JSON
json:"middle_name""" (vide)"middle_name": ""
json:"middle_name,omitempty""" (vide)(champ omis)
json:"age,omitempty"0(champ omis)

Le piège avec omitempty est qu'il ne peut pas distinguer "volontairement zéro" de "non défini". Si age est légitimement 0 (un nouveau-né), omitempty le supprime de la sortie. Lorsque vous devez distinguer "absent" de "zéro", utilisez plutôt un champ pointeur (*int), où nil signifie absent et &zero signifie un vrai zéro.

La plupart des convertisseurs n'ajoutent pas omitempty par défaut, car c'est un choix de sérialisation, pas un fait de schéma. Ajoutez-le délibérément aux champs vraiment optionnels dans votre sortie, et utilisez des champs pointeurs lorsque la valeur zéro a un sens.

Gestion des structures imbriquées et répétées#

Les réponses API réelles sont rarement plates. Elles imbriquent des objets dans d'autres objets et contiennent des tableaux d'objets. C'est là que l'écriture manuelle de structures devient vraiment pénible et qu'un convertisseur montre toute son utilité.

Lorsqu'un convertisseur rencontre un objet imbriqué, il a deux stratégies :

  • Structure anonyme (inline) : le type imbriqué est écrit en ligne à l'intérieur de son parent. Compact, mais vous ne pouvez pas référencer le type interne ailleurs.
  • Types nommés (extraits) : chaque objet imbriqué devient sa propre struct nommée, référencée par le parent. Plus verbeux, mais réutilisable et bien plus facile à lire.

Pour tout ce qui dépasse une charge utile triviale, privilégiez les types nommés. Avec ce JSON :

{
  "id": 7,
  "owner": { "name": "Ada", "verified": true },
  "tags": ["go", "json"]
}

Un convertisseur utilisant des types nommés produit :

type Repo struct {
    ID    int      `json:"id"`
    Owner Owner    `json:"owner"`
    Tags  []string `json:"tags"`
}

type Owner struct {
    Name     string `json:"name"`
    Verified bool   `json:"verified"`
}

Remarquez que tags est devenu []string car chaque élément était une chaîne, et owner est devenu son propre type Owner. Pour un tableau d'objets ("items": [ {...}, {...} ]), le convertisseur inspecte le premier élément (ou fusionne entre les éléments s'il est intelligent) et produit []Item avec une structure Item générée.

Le cas particulier de la fusion de tableaux#

Voici une subtilité que les générateurs bon marché oublient. Si un tableau contient des objets où certains éléments ont un champ et d'autres non, un convertisseur naïf ne lit que le premier élément et manque les champs qui apparaissent plus tard. Un générateur robuste fusionne les clés de tous les éléments du tableau pour que la structure capture chaque champ, marquant ceux qui sont parfois absents comme candidats pour des pointeurs ou omitempty. Si votre structure générée omet des champs qui existent plus loin dans une liste, c'en est la cause.

Générer une structure Go à partir de JSON, étape par étape#

Vous pouvez le faire dans votre navigateur en moins d'une minute, sans chaîne d'outils Go ni go install. Le formateur et convertisseur JSON gratuit de Molixa valide d'abord votre JSON, puis génère une structure Go (avec TypeScript, Zod et d'autres cibles) pour détecter les problèmes de syntaxe avant qu'ils ne deviennent des erreurs de compilation Go.

Étape 1 : Coller et valider votre JSON#

Déposez un échantillon représentatif dans l'éditeur. Le mot le plus important ici est représentatif : utilisez un exemple avec tous les champs remplis et des valeurs réalistes, pas une version simplifiée. Les échantillons vides ou avec beaucoup de valeurs nulles produisent exactement des champs interface{}. Le formateur signale immédiatement les virgules finales, les guillemets simples et les clés non entre guillemets, ce qui est bien moins pénible que de traquer une erreur de désérialisation Go plus tard.

Étape 2 : Générer la sortie de la structure Go#

Basculez la sortie vers la cible de structure Go. Le convertisseur parcourt votre JSON, déduit chaque type, exporte et renomme les champs en PascalCase, et écrit les balises json: correspondantes. Les objets imbriqués deviennent des types nommés et les tableaux deviennent des slices automatiquement. Copiez le résultat directement dans votre package.

Étape 3 : Corriger les champs ambigus à la main#

C'est l'étape que les générateurs ne peuvent pas faire pour vous, et la sauter est la raison pour laquelle les gens pensent que les convertisseurs ne sont pas fiables. Parcourez la sortie pour trois choses :

  • Les champs int qui peuvent contenir des décimales : passez à float64 (ou json.Number pour la précision).
  • Les champs interface{} : ils proviennent de valeurs null ou de tableaux mixtes. Remplacez par un type concret, ou fournissez un échantillon plus riche et régénérez.
  • Les grands identifiants entiers : si un ID dépasse 2^53, un float64 perdra en précision. Utilisez int64, string ou json.Number selon l'API.

Étape 4 : Ajouter omitempty et les pointeurs là où il faut#

Décidez, champ par champ, s'il est vraiment facultatif en sortie. Ajoutez ,omitempty aux champs qui doivent disparaître quand ils sont vides. Pour les champs où une valeur nulle est significative (un vrai 0, un vrai false), passez à un type pointeur pour que nil signale proprement "non fourni". Ensuite, écrivez un petit test Unmarshal avec votre échantillon réel pour confirmer que rien n'est silencieusement ignoré.

Faire confiance au résultat : vérifiez avant de déployer#

Une structure générée est une première ébauche solide, pas une garantie. Le processus honnête est : générer, puis prouver qu'elle fait un aller-retour avec vos données réelles. Décodez une réponse réelle dans la structure, ré-encodez-la, et comparez-la à l'original. Les champs manquants ou réordonnés apparaissent immédiatement.

Si vous travaillez aussi en TypeScript sur la même API, le même échantillon JSON peut servir pour les deux. Notre guide sur la conversion JSON en interface TypeScript couvre les règles d'inférence de type côté front-end, et si vous déboguez d'abord la charge utile brute, comment formater du JSON en JavaScript explique le pretty-printing et les options JSON.stringify qui rendent une réponse désordonnée lisible avant même de la convertir.

La raison pour laquelle un convertisseur surpasse la saisie manuelle n'est pas seulement la rapidité. Il applique la règle d'export, le mappage de balises et l'imbrication de manière cohérente à chaque fois, de sorte qu'il ne vous reste qu'un petit ensemble de décisions subjectives (int vs float, optionnel vs requis, précision) qu'aucun outil ne peut déduire d'un seul échantillon. Cette division du travail est tout l'intérêt.

Conclusion : Du JSON brut à une structure Go fiable#

Convertir du JSON en structure Go est mécanique pour la plupart des champs et nécessite un jugement pour quelques-uns. Un générateur gère les 90% mécaniques : exporter les champs, écrire les balises json:, nommer les types imbriqués et transformer les tableaux en slices. Vous gérez les 10% qu'il ne peut pas déduire, principalement la précision numérique, les champs interface{} dérivés de null, et la question de savoir si omitempty ou un pointeur convient pour chaque champ optionnel.

Maîtrisez ces règles et l'aller-retour JSON vers structure Go devient ennuyeux dans le bon sens du terme. Collez un échantillon représentatif dans le formateur JSON et convertisseur de structure Go, générez le type, corrigez les quelques champs ambigus, et confirmez qu'il fait l'aller-retour avec vos données réelles. C'est la différence entre une structure qui compile et une à laquelle vous pouvez vraiment faire confiance en production.

Foire Aux Questions#

Comment convertir du JSON en structure Go ? Collez un échantillon JSON représentatif dans un convertisseur, qui déduit un type Go pour chaque valeur, exporte et renomme les clés en PascalCase, et ajoute les balises json: correspondantes. Vérifiez ensuite manuellement les champs numériques et les types interface{} que l'outil n'a pas pu déduire. Vous pouvez faire le tout gratuitement dans le navigateur avec le formateur et convertisseur JSON de Molixa, sans avoir besoin de la chaîne d'outils Go.

Pourquoi ma structure générée a-t-elle des champs interface{} ? Un champ interface{} (ou any) signifie que le convertisseur n'a pas pu déduire un type. Cela se produit lorsque la valeur JSON était null, ou lorsqu'un tableau contenait des types mixtes. La solution est de fournir un échantillon où ce champ est rempli avec une valeur réelle, puis de régénérer, ou de remplacer interface{} par le type concret que vous savez qu'il devrait être.

Les champs d'une structure Go doivent-ils être en majuscule ? Oui, si vous voulez que encoding/json les lise ou les écrive. Le paquet JSON de Go ne voit que les champs exportés, c'est-à-dire ceux commençant par une lettre majuscule. Un champ en minuscule est ignoré lors de Marshal et Unmarshal sans erreur, donc il reste silencieusement à sa valeur zéro. Les convertisseurs exportent chaque champ et utilisent la balise json: pour faire correspondre le nom de clé d'origine.

Que fait omitempty dans une balise JSON ? L'option omitempty n'affecte que le marshaling (encodage d'une structure en JSON). Elle indique à l'encodeur d'ignorer un champ lorsque sa valeur est la valeur zéro : chaîne vide, 0, false, nil, ou une tranche ou carte vide. Elle n'a aucun effet sur l'unmarshaling. Soyez prudent, car elle ne peut pas distinguer un zéro intentionnel d'un champ non défini, donc utilisez un type pointeur lorsque la valeur zéro est significative.

Dois-je utiliser int ou float64 pour les nombres JSON en Go ? Utilisez float64 pour tout champ pouvant contenir un nombre décimal, et int uniquement pour les valeurs toujours entières. Comme JSON n'a pas de type entier, les convertisseurs devinent à partir de votre échantillon, donc un champ qui est 100 dans l'échantillon mais parfois 100.50 obtient le mauvais type. Pour les grands IDs ou l'argent où la précision compte, préférez int64, string, ou json.Number pour éviter les arrondis flottants.

Comment gérer les objets JSON imbriqués dans une structure Go ? Convertissez chaque objet imbriqué en son propre type de structure nommé référencé par le parent, plutôt que de tout intégrer en ligne. Cela rend le code lisible et vous permet de réutiliser le type interne. Un bon convertisseur le fait automatiquement et gère également les tableaux d'objets en générant une tranche d'une structure nommée, en fusionnant idéalement les clés de tous les éléments du tableau pour qu'aucun champ ne soit manqué.

jsongolangcode-generationdeveloper-tools

More from Molixa

Try Molixa Tools

50+ free AI tools for content creation, SEO, coding, and more. No signup, no watermark.

Explore all tools