JSON vers Dart

Générer des classes Dart depuis du JSON avec fromJson et toJson

Essayer un exemple
Nom de la classe racine :

Entrée JSON

Sortie Dart

Fonctionne localement · Sûr pour coller des secrets
Les classes Dart apparaîtront ici…

Qu'est-ce que la conversion JSON vers Dart ?

La conversion JSON vers Dart prend un objet JSON brut et produit des définitions de classes Dart avec des champs typés, un constructeur nommé, une factory fromJson et une méthode toJson. Dart ne dispose pas de réflexion à l'exécution dans Flutter (dart:mirrors est désactivé), ce qui signifie qu'on ne peut pas désérialiser du JSON en objets typés sans écrire du code de mapping explicite. Chaque réponse d'API REST, document Firebase ou charge utile de configuration nécessite une classe modèle Dart correspondante pour accéder à ses champs avec la sécurité de type.

Une classe modèle Dart typique pour JSON déclare des champs final pour chaque clé, un constructeur avec des paramètres nommés (en utilisant le mot-clé required pour les champs non nullables), un constructeur factory nommé fromJson qui lit depuis une Map de String vers dynamic, et une méthode toJson qui retourne une Map de String vers dynamic. Les objets JSON imbriqués deviennent des classes séparées. Les tableaux deviennent des champs List typés. Les valeurs JSON nullables utilisent la syntaxe null-safety de Dart avec le suffixe ? sur le type.

Écrire ces classes modèles à la main implique de lire chaque clé JSON, de choisir le type Dart, de créer le cast fromJson pour chaque champ (y compris le mapping de listes avec .map().toList()), de construire un littéral de map toJson, et de répéter le tout pour chaque objet imbriqué. Pour un objet JSON avec 12 champs et 2 objets imbriqués, cela représente 3 classes, 6 lignes de factory et des dizaines d'expressions de cast. Un convertisseur produit tout cela en quelques millisecondes depuis un simple collage.

Pourquoi utiliser un convertisseur JSON vers Dart ?

Écrire manuellement des classes modèles Dart depuis du JSON implique de lire les noms de champs, de deviner les types à partir des valeurs d'exemple, d'écrire les casts fromJson avec une gestion correcte des nulls, et de répéter le processus pour les objets imbriqués. Quand la structure de l'API change, chaque mise à jour de champ touche le constructeur, fromJson et toJson. Un convertisseur élimine ce travail répétitif.

Génération instantanée de classes
Collez du JSON et obtenez des classes Dart complètes avec constructeurs, factories fromJson et méthodes toJson en moins d'une seconde. Les objets imbriqués et les listes sont détectés et mappés automatiquement.
🔒
Traitement respectueux de la vie privée
La conversion s'exécute entièrement dans votre navigateur via JavaScript. Vos données JSON ne quittent jamais votre machine. Les clés API, tokens et charges utiles de production restent privés.
📝
Sortie null-safe
Les classes générées utilisent la null safety stricte de Dart. Les champs JSON nullables reçoivent le suffixe de type ? et omettent le mot-clé required dans les constructeurs. Les champs non nuls sont marqués required.
📦
Sans installation ni inscription
Ouvrez la page et collez votre JSON. Pas de SDK Dart, pas de dépendances pub, pas de compte. Fonctionne sur tout appareil disposant d'un navigateur.

Cas d'usage de JSON vers Dart

Développement d'applications Flutter
Générez des classes modèles pour les réponses d'API REST consommées par les packages http ou dio. Collez le JSON retourné par votre backend et obtenez des classes Dart prêtes pour jsonDecode et le rendu de widgets.
Intégration Firebase / Firestore
Créez des classes modèles typées pour les snapshots de documents Firestore. Collez un document exemple en JSON, générez la classe Dart et utilisez fromJson avec snapshot.data() dans votre couche repository.
Modèles de gestion d'état
Définissez des objets d'état typés pour Riverpod, Bloc ou Provider. Collez la forme d'état attendue en JSON et obtenez des classes Dart immuables avec toJson pour la persistance et l'hydratation d'état.
Génération de code client API
Produisez des modèles de requête et de réponse pour les clients API Retrofit ou Chopper. Collez des exemples de charges utiles JSON et obtenez des classes Dart correspondant à vos schémas de réponse OpenAPI.
Tests automatisés
Construisez des fixtures de test typées depuis de vraies réponses API. Collez un exemple JSON, générez la classe modèle et utilisez-la directement dans des tests unitaires et de widgets sans écrire les structures de fixtures à la main.
Apprentissage des patterns Dart
Les étudiants qui apprennent Flutter peuvent coller n'importe quelle structure JSON et voir comment Dart la représente : champs final, constructeurs nommés, patterns factory fromJson et annotations null-safety en pratique.

Correspondance de types JSON vers Dart

Chaque valeur JSON correspond à un type Dart spécifique. Le tableau ci-dessous montre comment le convertisseur traduit chaque type JSON. La colonne Alternative présente les types utilisés dans des scénarios moins courants ou de mapping manuel.

Type JSONExempleType DartAlternative
string"hello"StringString
number (integer)42intint
number (float)3.14doubledouble
booleantrueboolbool
nullnulldynamicNull / dynamic
object{"k": "v"}NestedClassMap<String, dynamic>
array of strings["a", "b"]List<String>List<String>
array of objects[{"id": 1}]List<Item>List<Item>
mixed array[1, "a"]List<dynamic>List<dynamic>

Approches de sérialisation JSON en Dart

Dart et Flutter proposent plusieurs façons de gérer la sérialisation JSON. Le fromJson/toJson manuel est l'approche la plus simple et ne nécessite pas de génération de code. Pour les projets plus importants, les solutions basées sur build_runner comme json_serializable et freezed génèrent le code répétitif à la compilation, réduisant les erreurs et maintenant les modèles cohérents avec le contrat JSON.

ApprocheDescriptionSource
json_serializableCode-generation-based JSON serialization. Generates .g.dart files at build time via build_runner. Type-safe and compile-time verified.Official (Google)
freezedGenerates immutable data classes with copyWith, fromJson/toJson, equality, and pattern matching support. Built on top of json_serializable.Community (rrousselGit)
built_valueGenerates immutable value types with serialization. Enforces immutability patterns. Used in larger codebases with strict data modeling.Google
dart_mappableAnnotation-based mapper that generates fromJson, toJson, copyWith, and equality. Simpler setup than freezed with similar features.Community
Manual fromJson/toJsonHand-written factory constructors and toJson methods. No code generation needed. Full control over the mapping logic.Built-in Dart

Manuel vs json_serializable vs freezed

Dart propose trois approches courantes pour les classes modèles JSON. Le fromJson/toJson manuel n'a aucune dépendance. json_serializable automatise le code de mapping. freezed ajoute l'immutabilité, copyWith et le pattern matching par-dessus json_serializable.

Manual fromJson/toJson
Écrivez les factories fromJson et les méthodes toJson à la main. Zéro dépendance, pas d'étape build_runner. Vous avez le contrôle total sur la logique de cast et la gestion des erreurs. Idéal pour les petits projets avec peu de modèles, ou quand vous avez besoin d'une désérialisation personnalisée que les générateurs de code ne peuvent pas exprimer. C'est le format de sortie que cet outil génère.
json_serializable
Annotez votre classe avec @JsonSerializable() et exécutez dart run build_runner build. Le générateur crée un fichier .g.dart avec les fonctions _$ClassFromJson et _$ClassToJson. Gère @JsonKey pour le renommage de champs, les valeurs par défaut et les convertisseurs personnalisés. Le choix standard pour les projets Flutter de taille moyenne à grande.
freezed
Annotez avec @freezed et obtenez des classes immuables avec fromJson, toJson, copyWith, l'égalité (== et hashCode) et toString générés automatiquement. Prend en charge les types union pour les patterns de classes scellées. Nécessite freezed et json_serializable comme dépendances de développement. Idéal pour les modèles de gestion d'état et les objets de domaine complexes.

Exemples de code

Ces exemples montrent comment utiliser les classes Dart générées pour la désérialisation JSON, comment configurer json_serializable avec build_runner, et comment générer des classes Dart par programmation depuis JavaScript et Python.

Dart (manual fromJson / toJson)
import 'dart:convert';

class User {
  final int id;
  final String name;
  final String email;
  final bool active;
  final Address address;
  final List<String> tags;

  User({
    required this.id,
    required this.name,
    required this.email,
    required this.active,
    required this.address,
    required this.tags,
  });

  factory User.fromJson(Map<String, dynamic> json) => User(
    id: json['id'] as int,
    name: json['name'] as String,
    email: json['email'] as String,
    active: json['active'] as bool,
    address: Address.fromJson(json['address'] as Map<String, dynamic>),
    tags: (json['tags'] as List<dynamic>).map((e) => e as String).toList(),
  );

  Map<String, dynamic> toJson() => {
    'id': id,
    'name': name,
    'email': email,
    'active': active,
    'address': address.toJson(),
    'tags': tags,
  };
}

class Address {
  final String street;
  final String city;
  final String zip;

  Address({required this.street, required this.city, required this.zip});

  factory Address.fromJson(Map<String, dynamic> json) => Address(
    street: json['street'] as String,
    city: json['city'] as String,
    zip: json['zip'] as String,
  );

  Map<String, dynamic> toJson() => {'street': street, 'city': city, 'zip': zip};
}

void main() {
  final jsonStr = '{"id":1,"name":"Alice","email":"alice@example.com","active":true,"address":{"street":"123 Main","city":"Springfield","zip":"12345"},"tags":["admin","user"]}';
  final user = User.fromJson(jsonDecode(jsonStr));
  print(user.name); // -> Alice
  print(jsonEncode(user.toJson())); // -> round-trip back to JSON
}
Dart (json_serializable + build_runner)
// pubspec.yaml dependencies:
//   json_annotation: ^4.8.0
//   dev_dependencies:
//     build_runner: ^2.4.0
//     json_serializable: ^6.7.0

import 'package:json_annotation/json_annotation.dart';
part 'user.g.dart'; // generated by: dart run build_runner build

@JsonSerializable()
class User {
  final int id;
  final String name;
  final String email;
  @JsonKey(name: 'is_active')
  final bool isActive;
  final List<String> tags;

  User({
    required this.id,
    required this.name,
    required this.email,
    required this.isActive,
    required this.tags,
  });

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
  Map<String, dynamic> toJson() => _$UserToJson(this);
}

// Run code generation:
// dart run build_runner build --delete-conflicting-outputs
JavaScript (generate Dart classes from JSON)
function jsonToDart(obj, name = "Root") {
  const classes = [];
  function infer(val, fieldName) {
    if (val === null) return "dynamic";
    if (typeof val === "string") return "String";
    if (typeof val === "number") return Number.isInteger(val) ? "int" : "double";
    if (typeof val === "boolean") return "bool";
    if (Array.isArray(val)) {
      const first = val.find(v => v !== null);
      if (!first) return "List<dynamic>";
      return `List<${infer(first, fieldName)}>`;
    }
    if (typeof val === "object") {
      const cls = fieldName.charAt(0).toUpperCase() + fieldName.slice(1);
      build(val, cls);
      return cls;
    }
    return "dynamic";
  }
  function build(obj, cls) {
    const fields = Object.entries(obj).map(([k, v]) =>
      `  final ${infer(v, k)} ${k};`
    );
    classes.push(`class ${cls} {\n${fields.join("\n")}\n}`);
  }
  build(obj, name);
  return classes.join("\n\n");
}

console.log(jsonToDart({ id: 1, name: "Alice", scores: [98, 85] }, "User"));
// class User {
//   final int id;
//   final String name;
//   final List<int> scores;
// }
Python (generate Dart model from JSON)
import json

def json_to_dart(obj: dict, class_name: str = "Root") -> str:
    classes = []

    def infer(val, name):
        if val is None:
            return "dynamic"
        if isinstance(val, bool):
            return "bool"
        if isinstance(val, int):
            return "int"
        if isinstance(val, float):
            return "double"
        if isinstance(val, str):
            return "String"
        if isinstance(val, list):
            if not val:
                return "List<dynamic>"
            return f"List<{infer(val[0], name)}>"
        if isinstance(val, dict):
            cls = name[0].upper() + name[1:]
            build(val, cls)
            return cls
        return "dynamic"

    def build(obj, cls):
        fields = [f"  final {infer(v, k)} {k};" for k, v in obj.items()]
        classes.append(f"class {cls} {{\n" + "\n".join(fields) + "\n}")

    build(obj, class_name)
    return "\n\n".join(classes)

data = json.loads('{"id": 1, "name": "Alice", "active": true}')
print(json_to_dart(data, "User"))
# class User {
#   final int id;
#   final String name;
#   final bool active;
# }

Questions fréquentes

Pourquoi Flutter ne peut-il pas utiliser dart:mirrors pour la désérialisation JSON ?
Flutter désactive dart:mirrors (la bibliothèque de réflexion) car le tree-shaking nécessite de savoir à la compilation quel code est utilisé. La réflexion empêcherait le compilateur de supprimer les classes inutilisées, augmentant ainsi la taille de l'application. C'est pourquoi les projets Flutter ont besoin de méthodes fromJson/toJson explicites ou de génération de code plutôt que de réflexion à l'exécution.
Faut-il utiliser json_serializable ou écrire fromJson à la main ?
Pour les projets avec moins de 10 classes modèles, un fromJson/toJson écrit à la main est simple et n'ajoute aucune dépendance de compilation. Dès que vous avez plus de 10 modèles, ou des modèles qui changent fréquemment, json_serializable fait gagner du temps et réduit les erreurs de copier-coller. Il génère le code de mapping depuis les annotations, donc l'ajout de champs nécessite seulement de relancer build_runner.
Comment le convertisseur gère-t-il les objets JSON imbriqués ?
Chaque objet JSON imbriqué devient une classe Dart séparée. Si un champ JSON nommé "address" contient un objet avec les clés "street" et "city", le convertisseur crée une classe Address avec ces champs et type le champ parent comme Address. La factory fromJson caste la map imbriquée et la passe à Address.fromJson.
Que se passe-t-il quand un champ JSON est null ?
Les champs nuls sont typés dynamic avec un suffixe ? car le convertisseur ne peut pas déterminer le type voulu depuis une valeur null seule. Remplacez dynamic? par le type correct (String?, int?, etc.) une fois que vous le connaissez à partir de votre contrat API. La null safety de Dart imposera alors les vérifications de null correctes à la compilation.
Comment gérer les clés JSON qui ne sont pas des identifiants Dart valides ?
Les clés JSON comme "first-name" ou "2nd_place" ne sont pas des noms de champs Dart valides. Avec fromJson manuel, vous pouvez mapper n'importe quelle clé JSON vers n'importe quel nom de champ Dart dans le constructeur factory. Avec json_serializable, utilisez @JsonKey(name: 'first-name') pour annoter le champ. Le convertisseur convertit automatiquement les noms de clés en champs Dart en camelCase.
Puis-je utiliser freezed avec la sortie de cet outil ?
L'outil génère des classes Dart standard avec fromJson/toJson manuels. Pour convertir vers freezed, modifiez la classe pour utiliser l'annotation @freezed, supprimez le constructeur et la factory écrits à la main, et ajoutez le mixin freezed. Exécutez ensuite build_runner pour générer les fichiers .freezed.dart et .g.dart. Les noms de champs et les types de la sortie générée se transfèrent directement.
Quelle est la différence entre une map brute et une classe Dart typée pour JSON ?
Une map brute (Map de String vers dynamic) donne un accès brut aux données JSON sans vérification de type. Vous devez caster chaque valeur au point d'appel, et les fautes de frappe dans les noms de clés échouent silencieusement à l'exécution. Une classe Dart typée détecte les incompatibilités de type à la frontière fromJson, fournit l'autocomplétion IDE pour les noms de champs et rend le refactoring sûr. La différence de performance est négligeable ; le bénéfice est entièrement lié à la correction et à la maintenabilité.