---
id: 61f2009278a5f7366f79395da59447cf
author: lemonmint
title: Analyse des modifications de comportement de encoding/json/v2 dans Go 1.25 et de la couche de compatibilité avec la v1
description: Comparaison de la correspondance sensible à la casse, des changements de sémantique de omitempty et omitzero, ainsi que de la sérialisation et des options des slices et maps nil dans encoding/json/v2, introduit comme fonctionnalité expérimentale de Go 1.25 (goexperiment.jsonv2).
language: fr
date: 2025-06-20T05:38:20.100520237Z
path: /blog/posts/go-1-25-encoding-json-v1-vs-v2-z74a36b08
category: release
---

Le package `encoding/json` v2 de Go est une nouvelle implémentation destinée à remédier à plusieurs inconvénients de la v1 existante (manque de cohérence, comportements surprenants, problèmes de performance). Il s'agit d'une fonctionnalité expérimentale, activée par la balise de compilation `goexperiment.jsonv2`.

Le point le plus important est que **lorsque la v2 est activée, la v1 fonctionne comme une couche de compatibilité qui émule le comportement de la v1 au-dessus de l'implémentation v2**. Ce mécanisme passe par la fonction `DefaultOptionsV1()` du fichier `v2_v1.go`. Autrement dit, la v2 fournit des options permettant de reproduire fidèlement le comportement de la v1, tout en proposant de nouveaux comportements par défaut plus stricts et plus prévisibles.

Les principaux objectifs de la v2 sont les suivants.

1. **Amélioration de l'exactitude et de la prévisibilité** : en appliquant par défaut des règles plus strictes (par exemple, la distinction entre majuscules et minuscules, l'interdiction des clés en double), elle réduit les comportements inattendus.
2. **Amélioration des performances** : le moteur d'analyse et d'encodage a été repensé afin d'accroître l'efficacité.
3. **Extension de la flexibilité et du contrôle** : un système d'options détaillé est introduit afin que les développeurs puissent contrôler finement le traitement JSON.

---

## Principales différences de sémantique et de comportement

Les différences de comportement entre la v1 et la v2 sont résumées par point, en se fondant principalement sur le fichier `v2_test.go`.

### 1. Correspondance des noms de champs (sensibilité à la casse)

- **Comportement de la v1** : lors du démarshaling d'un membre d'objet JSON vers un champ de structure Go, la correspondance **ignore la casse (insensible à la casse)**. `"FirstName"` et `"firstname"` sont tous deux associés au champ `FirstName`.
- **Comportement de la v2** : par défaut, la correspondance **respecte la casse (sensible à la casse)** et n'associe que les champs dont le nom correspond exactement.
- **Raison du changement** : la correspondance insensible à la casse peut être à l'origine de comportements inattendus et entraîne une dégradation des performances lors du traitement des champs non correspondants. La v2 adopte par défaut un comportement plus clair et plus prévisible.
- **Options associées** : en v2, il est possible d'activer explicitement l'ignorance de la casse champ par champ à l'aide de l'option de balise `json:"...,case:ignore"`, ou de l'appliquer globalement avec `json.MatchCaseInsensitiveNames(true)`.

### 2. Modification de la sémantique de l'option de balise `omitempty`

- **Comportement de la v1** : un champ est omis en fonction de l'« état vide » de la valeur Go. Ici, l'« état vide » désigne `false`, `0`, les pointeurs ou interfaces `nil`, ainsi que les tableaux, slices, maps et chaînes de longueur nulle.
- **Comportement de la v2** : un champ est omis en fonction de l'« état vide » de la valeur JSON encodée. Autrement dit, il est omis lorsqu'il est encodé en `null`, `""`, `{}` ou `[]`.
- **Raison du changement** : la définition de la v1 dépend du système de types de Go. La v2 se fonde sur le système de types JSON afin d'offrir un comportement plus cohérent. Par exemple, en v1, la valeur `false` d'un champ de type `bool` est omise, alors qu'en v2, `false` est une valeur JSON non vide et n'est donc pas omise. La v2 introduit l'option `omitzero`, qui permet de remplacer le comportement de l'`omitempty` de la v1 appliqué à `0` ou à `false`.
- **Options associées** : pour obtenir en v2 le comportement identique à la v1, on utilise l'option `json.OmitEmptyWithLegacyDefinition(true)`.

### 3. Modification du comportement de l'option de balise `string`

- **Comportement de la v1** : s'applique aux champs de types numériques, booléens et chaînes. La valeur correspondante est réencodée à l'intérieur d'une chaîne JSON (par exemple, `int(42)` devient `"42"`). Elle ne s'applique pas de manière récursive aux valeurs contenues dans les types composites (slices, maps, etc.).
- **Comportement de la v2** : s'applique **uniquement aux types numériques** et de manière **récursive**. Ainsi, les nombres contenus dans un slice tel que `[]int` sont également encodés en chaînes JSON.
- **Raison du changement** : l'usage principal de l'option `string` est de représenter les entiers de 64 bits sous forme de chaînes afin d'éviter la perte de précision. Le comportement de la v1 était limité et peu cohérent. La v2 se concentre sur cet usage essentiel et étend le comportement de manière récursive afin de le rendre plus utile.
- **Options associées** : l'option `json.StringifyWithLegacySemantics(true)` permet d'imiter le comportement de la v1.

### 4. Marshaling des slices et maps `nil`

- **Comportement de la v1** : les slices `nil` et les maps `nil` sont marshalés en `null`.
- **Comportement de la v2** : par défaut, les slices `nil` sont marshalés en `[]` (tableau vide) et les maps `nil` en `{}` (objet vide).
- **Raison du changement** : `nil` est un détail d'implémentation du langage Go, et il n'est pas souhaitable de l'exposer dans un format JSON indépendant du langage. Les notations `[]` et `{}`, qui représentent des collections vides, sont des représentations plus universelles.
- **Options associées** : en v2, les options `json.FormatNilSliceAsNull(true)` ou `json.FormatNilMapAsNull(true)` permettent de marshaler en `null` comme dans la v1.

### 5. Démarshaling des tableaux

- **Comportement de la v1** : lors du démarshaling vers un tableau Go (`[N]T`), aucune erreur n'est levée si la longueur du tableau JSON diffère de celle du tableau Go. Si le tableau JSON est plus court, les éléments restants sont remplis avec la valeur zéro ; s'il est plus long, l'excédent est ignoré.
- **Comportement de la v2** : la longueur du tableau JSON doit **correspondre exactement** à celle du tableau Go. Dans le cas contraire, une erreur est levée.
- **Raison du changement** : en Go, la longueur d'un tableau de taille fixe revêt souvent une signification importante. Le comportement de la v1 peut entraîner une perte silencieuse de données. La v2 adopte des règles plus strictes afin d'améliorer l'exactitude.
- **Options associées** : l'option `json.UnmarshalArrayFromAnyLength(true)` permet d'imiter le comportement de la v1.

### 6. Traitement de `time.Duration`

- **Comportement de la v1** : `time.Duration` est traité en interne comme un `int64` et encodé sous forme de nombre JSON exprimé en nanosecondes.
- **Comportement de la v2** : `time.Duration` est encodé sous forme de chaîne JSON au format `"1h2m3s"`, à l'aide de la méthode `time.Duration.String()`.
- **Raison du changement** : les nombres exprimés en nanosecondes sont peu lisibles, et la représentation textuelle standard de `time.Duration` est plus utile.
- **Options associées** : il est possible de retrouver le comportement de la v1 grâce à l'option de balise `json:",format:nano"` ou à l'option `json.FormatTimeWithLegacySemantics(true)`.

### 7. Traitement de l'UTF-8 invalide

- **Comportement de la v1** : lors du marshaling ou du démarshaling, les octets UTF-8 invalides présents dans une chaîne sont **remplacés silencieusement** par le caractère de remplacement Unicode (`\uFFFD`).
- **Comportement de la v2** : par défaut, une erreur est renvoyée lorsqu'un UTF-8 invalide est rencontré.
- **Raison du changement** : il s'agit d'éviter la corruption silencieuse des données et de se conformer à la norme JSON plus stricte (RFC 7493).
- **Options associées** : l'option `jsontext.AllowInvalidUTF8(true)` permet d'imiter le comportement de la v1.

### 8. Traitement des noms de membres d'objet en double

- **Comportement de la v1** : la présence de membres portant le même nom au sein d'un objet JSON est autorisée. La dernière valeur rencontrée remplace les précédentes.
- **Comportement de la v2** : par défaut, la présence de noms de membres en double entraîne une **erreur**.
- **Raison du changement** : la norme RFC 8259 ne définit pas le comportement en cas de noms dupliqués, ce qui peut conduire à des comportements différents selon les implémentations. Cela peut être à l'origine de vulnérabilités de sécurité. La v2 rejette explicitement ces cas afin d'améliorer l'exactitude et la sécurité.
- **Options associées** : l'option `jsontext.AllowDuplicateNames(true)` permet d'imiter le comportement de la v1.

---

## Différences d'implémentation et d'architecture

- **v1** : repose largement sur `decodeState` dans `decode.go` et sur une machine à états écrite manuellement dans `scanner.go`. Il s'agit d'une structure monolithique dans laquelle l'analyse syntaxique et l'analyse sémantique sont fortement couplées.
- **v2** : l'architecture est plus modulaire.
  - **`encoding/json/jsontext`** : fournit un tokenizer JSON (`Decoder`) et un encodeur (`Encoder`) de bas niveau et hautes performances. Ce package se concentre uniquement sur l'aspect syntaxique de JSON.
  - **`encoding/json/v2`** : s'appuie sur `jsontext` pour gérer la conversion sémantique entre les types Go et les valeurs JSON.
  - Cette séparation dissocie l'analyse syntaxique de l'analyse sémantique, ce qui améliore la clarté du code et ses performances.

---

## Nouvelles API et fonctionnalités de la v2

La v2 offre un contrôle très souple grâce au système `json.Options`.

- **`json.Options`** : ensemble d'options modifiant le comportement du marshaling et du démarshaling.
- **`json.JoinOptions(...)`** : fusionne plusieurs options en une seule.
- **`WithMarshalers` / `WithUnmarshalers`** : fonctionnalité puissante permettant d'injecter une logique de sérialisation et de désérialisation pour un type donné, sans que ce type n'implémente les interfaces `Marshaler` ou `Unmarshaler`. Elle est particulièrement utile pour traiter des types provenant de packages externes.
- **Nouvelles options** : `RejectUnknownMembers`, `Deterministic(false)`, `FormatNilSliceAsNull`, etc., qui permettent un contrôle du comportement impossible en v1.

---

## Conclusion

`encoding/json` v2 est une implémentation moderne qui améliore nettement l'**exactitude, les performances et la flexibilité** à partir de l'expérience acquise avec la v1. Le comportement par défaut est devenu plus strict, mais le système `Options` permet de reproduire intégralement tous les comportements de la v1. Il est ainsi possible de préserver la compatibilité avec le code existant tout en adoptant progressivement les avantages de la v2.

- Pour un **nouveau projet**, il est recommandé d'utiliser la v2 par défaut.
- Pour un **projet existant**, on peut continuer à utiliser `jsonv1` tel quel, ou migrer vers `jsonv2` en introduisant progressivement le comportement strict de la v2 via `DefaultOptionsV1()`.
