Choisir un outil de diff JSON : diff texte, diff structurel et visualisation
L’essentiel : Le diff texte trouve où les fichiers diffèrent ; le diff structurel décide si les contenus sont équivalents. Choisir la bonne couche évite de prendre l'ordre des clés pour un incident.

L'essentiel : le diff texte découvre le changement (triez d'abord les clés avec jq -S), le diff structurel décide de l'équivalence (alignez les tableaux par clé primaire), et la visualisation ne fait qu'expliquer ou relire ; gardez les trois couches séparées en CI.
Deux documents JSON qui semblent différents ne signifient pas que leurs contenus sont différents. Choisissez la bonne couche de comparaison avant de choisir l'outil.
1. Trois types de « diff » ne sont pas la même chose
Beaucoup d'équipes traitent « comparer JSON » comme une action unique, puis rencontrent les mêmes problèmes dans des scénarios différents. Il aide de séparer trois couches :
| Couche | Ce qui est comparé | Ce qu'elle révèle | Usage typique |
|---|---|---|---|
| Diff texte | Octets bruts et lignes | Indentation, ordre des clés, espaces, retours à la ligne | Dérive de configuration, revue de code |
| Diff structurel | Objets et tableaux parsés | Clés ajoutées ou supprimées, valeurs changées, changements d'éléments de tableau | Régression d'API, migration de données |
| Diff visuel | Comment les différences sont présentées | À quel niveau appartient un changement, quelle est sa portée | Revue humaine, discussion d'équipe |
Le diff texte est sans dépendance, rapide et s'intègre naturellement à Git. Sa faiblesse est qu'il ne connaît rien à la sémantique JSON. Deux documents complètement équivalents s'allumeront en rouge et vert si leur ordre de clés ou leur indentation diffère. Le diff structurel est l'inverse : il parse d'abord puis compare, donc il reconnaît qu'une valeur a simplement bougé. Mais il a besoin de la bonne stratégie d'alignement des tableaux ; sinon une seule insertion est lue comme une réécriture complète.
La visualisation n'est pas un troisième algorithme. Elle rend la sortie des deux autres couches pour qu'une personne confirme rapidement si la différence est attendue. Elle est bonne pour le jugement humain et mauvaise comme remplacement de vérifications automatisées. Mélanger les couches produit généralement un pipeline CI rouge en permanence et une équipe qui a appris à l'ignorer.
2. Diff texte : nettoyez d'abord l'ordre des clés avec jq -S
Pour un coup d'œil rapide sur deux fichiers, la combinaison la moins coûteuse est jq -S plus diff -u. L'option -S trie récursivement les clés d'objet, normalisant les documents dont le contenu est identique mais l'ordre des clés différent :
jq -S . a.json > a.sorted.json
jq -S . b.json > b.sorted.json
diff -u a.sorted.json b.sorted.json
Cela supprime le faux positif le plus courant. La RFC 8259 définit les objets comme des collections non ordonnées, donc {"a":1,"b":2} et {"b":2,"a":1} sont la même valeur. Un diff texte, en revanche, marque chaque ligne comme changée. Trier d'abord rapproche beaucoup les blocs rouges des vrais problèmes.
Notez que jq -S ne trie que les clés ; il ne trie pas les tableaux. L'ordre des tableaux a du sens en JSON, et [1,2] n'est pas la même valeur que [2,1], donc l'outil ne prendra pas cette décision à votre place. Si un tableau est conceptuellement un ensemble non ordonné, triez-le vous-même par une clé primaire avant de comparer.
Un autre détail mérite d'être retenu : diff sort avec le code 1 quand il trouve des différences, mais un fichier manquant ou une erreur de permission produit aussi une sortie non nulle. En CI, ne traitez pas chaque code non nul comme « le contenu a changé », sinon un chemin mal orthographié sera rapporté comme un changement de données. Un schéma plus sûr vérifie d'abord les fichiers puis traite la différence :
if ! diff -u a.sorted.json b.sorted.json; then
echo "JSON content differs" >&2
exit 1
fi
delta rend le diff texte lisible
La sortie brute de diff -u fonctionne, mais les lignes longues et le JSON profondément imbriqué sont fatigants à lire. delta est un filtre de diff qui ajoute coloration syntaxique, numéros de ligne et couleurs de fond plus claires :
diff -u a.sorted.json b.sorted.json | delta
Il a commencé comme pager Git, donc il peut aussi prendre en charge git diff. Pour les gens qui lisent dix diffs de configuration par jour, ce type d'outil améliore la vitesse de lecture plutôt que l'exactitude algorithmique. Un autre choix courant est git diff --no-index --word-diff, qui compare à la granularité du mot et convient bien au JSON compressé stocké sur de très longues lignes.
Un avertissement : l'embellissement ne supprime pas les fausses différences. Si l'ordre des clés n'a pas été normalisé, delta rend simplement le bruit plus joli. Le bon ordre est toujours normaliser d'abord, décorer ensuite.
3. Diff structurel : jsondiffpatch comprend la sémantique
Quand la question est de savoir si deux pièces de données sont sémantiquement équivalentes, utilisez un diff structurel. L'option la plus courante dans l'écosystème JavaScript est jsondiffpatch, qui renvoie un objet delta décrivant les changements plutôt que des lignes de texte avec marqueurs + et - :
import { create } from 'jsondiffpatch';
const instance = create();
const delta = instance.diff(
{ id: 1, tags: ['a', 'b'], enabled: true },
{ id: 1, tags: ['a', 'c'], enabled: false },
);
console.log(JSON.stringify(delta, null, 2));
Le delta distingue les clés ajoutées, les clés supprimées, les valeurs remplacées et les éléments de tableau déplacés. Cela signifie que vous pouvez parcourir le delta directement dans les assertions : undefined quand il n'y a pas de différence, ou une description chemin par chemin quand il y en a. Contrairement à un diff texte, il n'est pas perturbé par l'indentation, l'ordre des clés ou l'orthographe numérique comme 1e3 contre 1000.
Tout aussi important, la sortie d'un diff structurel peut alimenter un workflow de patch. jsondiffpatch fournit patch et reverse, donc un delta peut être appliqué à l'objet original et annulé plus tard. Une mise en garde : un delta généré automatiquement n'a pas le même format qu'un JSON Patch écrit à la main, et convertir entre les deux demande du travail. Pour l'approche de comparaison plus large, voir Diff et comparaison JSON.
4. Comment éliminer les fausses différences dues à l'ordre des clés
L'ordre des clés n'est pas seulement agaçant ; il peut produire de fausses conclusions. Suivez trois étapes lors de l'investigation :
- Avant de comparer, normalisez récursivement l'ordre des clés avec
jq -Sou un outil équivalent. - Supprimez les champs qui changent à chaque exécution, comme
timestamp,requestIdettraceId. - Confirmez qu'il n'y a pas de clés dupliquées. Les clés dupliquées sont résolues par « la dernière gagne » au parsing, donc un diff texte les voit tandis qu'un diff structurel non.
jq -S 'del(.meta.timestamp, .meta.requestId)' a.json > /tmp/a.json
jq -S 'del(.meta.timestamp, .meta.requestId)' b.json > /tmp/b.json
diff -u /tmp/a.json /tmp/b.json
Ce flux convient bien à la dérive de configuration. Passez staging et production par la même normalisation, et tout ce qui reste est la différence qui mérite réellement une explication. Gardez les règles de filtrage dans un script versionné au lieu de les retaper de mémoire. Pour le côté édition et script de la chaîne d'outils, voir Éditeurs, ligne de commande et automatisation.
5. Tableaux : aligner par index ou par id ?
Les tableaux sont la partie la plus sujette aux erreurs de la comparaison JSON. Avec l'alignement par index par défaut, insérer un enregistrement au début décale chaque élément ultérieur, et l'outil les marque tous comme changés. L'outil n'est pas cassé ; la stratégie d'alignement est fausse.
| Alignement | Fonctionne quand | Atout | Risque |
|---|---|---|---|
| Par index | L'ordre du tableau porte un sens métier | Simple, aucun champ supplémentaire nécessaire | Une insertion provoque des faux positifs en cascade |
| Par clé primaire | Les éléments sont un ensemble avec un id unique | Détecte insertion, suppression et déplacements | Exige une clé stable et unique |
| Plus longue sous-séquence commune | L'ordre compte mais les insertions sont permises | Plus robuste que le pur indexage | Plus difficile à expliquer, coût plus élevé |
jsondiffpatch utilise objectHash pour déclarer une clé primaire, indexant les éléments de tableau avant de comparer :
const instance = create({
objectHash: (item: { id?: string; sku?: string }) => item.id ?? item.sku,
});
Avec jq simple, vous pouvez trier d'abord puis faire un diff texte :
jq -S 'sort_by(.id)' items.json > items.sorted.json
Le compromis de l'alignement par id est que les changements de position ne comptent plus comme des différences. Si le métier doit savoir qu'un enregistrement est passé de la position 2 à la position 5, comparez aussi un champ d'ordre explicite ; ne vous fiez pas au contenu de l'ensemble seul.
6. Diff visuel : la couche faite pour les gens
La sortie d'un diff structurel est conçue pour des machines, pas pour des réunions. Quand il faut expliquer un changement à quelqu'un d'autre, la visualisation est plus efficace :
- L'éditeur de diff de VS Code convient à la revue fichier par fichier, avec des panneaux côte à côte et des surlignages en ligne.
- Les visualiseurs d'arbre comme JSON Crack et JSON Hero montrent à quel niveau appartient un changement.
jsondiffpatchfournit un formateur HTML qui rend un delta comme une vue riche repliable.
La valeur de la visualisation est le contexte. Un diff texte dit « la ligne 88 a changé » ; une vue arborescente dit « la ligne 88 appartient à data.items[3].price ». Mais la visualisation charge généralement tout le document dans un navigateur ou une application de bureau. Avant d'ouvrir une charge utile de production, confirmez si l'outil calcule localement ou téléverse vers un serveur.
7. Trois pratiques pour mettre le diff en CI
Premièrement, normalisez avant de comparer. Fixez l'indentation, triez récursivement les clés et filtrez les champs volatils avant que le diff tourne. Sinon la CI échoue sans cesse sur du bruit.
Deuxièmement, choisissez l'alignement selon la sémantique. Si un tableau est un ensemble, alignez par clé primaire. Si c'est une séquence, gardez l'alignement par index. N'appliquez pas une seule règle à toutes les API.
Troisièmement, assertez des valeurs, pas du texte. Comparer des objets parsés dans les tests est bien plus stable que comparer la sortie de JSON.stringify. Quand une vérification échoue, imprimez le chemin de différence et les valeurs avant/après au lieu de vider toute la charge utile dans le journal.
# Print only the differing paths and values, not the entire document
jq -S 'del(.timestamp)' actual.json > /tmp/actual.json
jq -S 'del(.timestamp)' expected.json > /tmp/expected.json
diff -u /tmp/expected.json /tmp/actual.json | head -n 80
Si une différence doit être suivie à long terme, committez ensemble le script de normalisation et le fichier attendu. Si c'est une investigation ponctuelle, supprimez le script temporaire ensuite pour qu'il ne devienne pas discrètement une seconde implémentation de la logique métier.
8. Tableau de sélection d'outils
| Scénario | Premier choix | Alternative | Rappel clé |
|---|---|---|---|
| Comparaison ponctuelle de fichiers | Diff VS Code | diff -u |
Formater d'abord pour retirer le bruit |
| Dérive de configuration | jq -S + diff |
delta |
Filtrer les champs volatils |
| Régression de réponse d'API | jsondiffpatch |
deepdiff |
Aligner les tableaux par clé primaire |
| Expliquer un changement à l'équipe | JSON Crack / JSON Hero | Formateur HTML | Vérifier si les données sont téléversées |
| Très grands fichiers | Extraction de sous-arbre jq |
Navigation jless |
Ne pas charger tout l'arbre en mémoire |
| Vérification avant commit | git diff --no-index |
pre-commit | Gérer explicitement les codes de sortie |
Il n'existe pas un meilleur outil de diff JSON ; il n'existe que le bon outil pour la couche courante. Le diff texte découvre le changement, le diff structurel juge l'équivalence, et la visualisation explique le changement. Garder les trois couches séparées est bien plus fiable que de chercher un outil qui fait tout. Pour la gestion des conflits entre branches, poursuivez avec JSON et fusion Git.
FAQ
jq -S modifie-t-il aussi l'ordre des tableaux ?
Non. -S ne trie que les clés d'objet et laisse les tableaux dans leur ordre original. Les tableaux sont ordonnés en JSON, donc l'outil ne transformera pas [1,2] en [2,1]. Si un tableau est conceptuellement un ensemble non ordonné, triez-le explicitement avec sort_by.
1 et 1.0 sont-ils la même valeur dans un diff structurel ?
La plupart des parseurs les transforment en le même nombre, donc un diff structurel les considère généralement égaux, tandis qu'un diff texte les marque différents. Utilisez le diff structurel pour décider de l'équivalence et le diff texte pour décider si les fichiers sont identiques octet par octet.
La sortie d'un diff peut-elle être utilisée directement comme JSON Patch ?
Pas directement. Un outil de diff rapporte ce qui diffère, tandis que JSON Patch décrit une liste ordonnée d'opérations ; les formats et sémantiques diffèrent. Les patchs auto-générés tendent aussi à référencer des index de tableau, qui deviennent périmés sous concurrence. Ajoutez une opération test avant de les appliquer.
Comment comparer des fichiers JSON de dizaines de mégaoctets ?
Extrayez d'abord le sous-arbre qui vous intéresse, par exemple avec jq '.data.items', puis triez par clé primaire et comparez. Ne chargez pas tout l'arbre en mémoire pour un diff récursif. Les très grands fichiers sont mieux traités par comparaison en flux ou par morceaux, et le coût mémoire nécessite une évaluation séparée.
Les outils de diff en ligne peuvent-ils faire fuiter mes données ?
Cela dépend de l'implémentation. Les outils purement frontend calculent dans le navigateur et ne téléversent pas de données ; les outils rendus côté serveur envoient le contenu à un backend, où il peut entrer dans les journaux. Avant de comparer une configuration de production ou des charges utiles client, confirmez que la page calcule localement, et testez une fois avec le réseau déconnecté si nécessaire.
Comment décider entre un diff texte et un diff structurel ?
Pour un coup d'œil rapide sur où deux fichiers diffèrent, triez les clés avec jq -S et lancez diff -u. Pour décider si deux documents sont sémantiquement équivalents, utilisez un diff structurel comme jsondiffpatch et alignez les tableaux par clé primaire. Le diff texte est sans dépendance et s'intègre à Git, tandis que le diff structurel n'est pas perturbé par l'indentation, l'ordre des clés ou l'orthographe numérique.
Pourquoi la CI ne devrait-elle pas traiter chaque code de sortie diff non nul comme une différence ?
diff sort avec 1 quand il trouve des différences, mais un fichier manquant ou une erreur de permission renvoie aussi non nul. Traiter chaque code non nul comme « contenu changé » rapporte un chemin mal orthographié comme un changement de données. Un schéma plus sûr vérifie d'abord les fichiers puis traite la différence séparément.
Faut-il faire un diff de fichiers de dizaines de mégaoctets dans le navigateur ?
Non. Extrayez d'abord le sous-arbre qui vous intéresse avec jq, puis triez par clé primaire et comparez. Charger tout l'arbre en mémoire pour un diff récursif coûte bien plus de mémoire et de temps. Les très grands fichiers sont mieux traités par comparaison en flux ou par morceaux.