La conversion de JSON en classes Dart est l'une des tâches les plus courantes en développement Flutter. Que vous récupériez des données depuis une API REST ou lisiez une configuration locale, vous avez besoin de classes Dart bien structurées.
Pourquoi les classes modèles
Correspondance des types JSON vers Dart
| Type JSON | Type Dart | Exemple |
|---|---|---|
| string | String | "hello" → "hello" |
| number (int) | int | 42 → 42 |
| number (float) | double | 3.14 → 3.14 |
| boolean | bool | true → true |
| null | Null / dynamic | null → null |
| array | List<T> | [1,2,3] → [1,2,3] |
| object | Map / Class | {} → User() |
Classe modèle manuelle (fromJson / toJson)
L'approche la plus directe est d'écrire les classes à la main :
// JSON input
{
"id": 1,
"name": "John Doe",
"email": "john@example.com",
"isActive": true,
"score": 95.5
}
// Dart model class
class User {
final int id;
final String name;
final String email;
final bool isActive;
final double score;
User({
required this.id,
required this.name,
required this.email,
required this.isActive,
required this.score,
});
factory User.fromJson(Map<String, dynamic> json) {
return User(
id: json['id'] as int,
name: json['name'] as String,
email: json['email'] as String,
isActive: json['isActive'] as bool,
score: (json['score'] as num).toDouble(),
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'email': email,
'isActive': isActive,
'score': score,
};
}
}Gestion des objets imbriqués
Créez des classes Dart séparées pour chaque niveau d'imbrication :
// JSON with nested object
{
"id": 1,
"name": "John",
"address": {
"street": "123 Main St",
"city": "Springfield",
"zipCode": "62701"
}
}
// Dart classes
class Address {
final String street;
final String city;
final String zipCode;
Address({required this.street, required this.city, required this.zipCode});
factory Address.fromJson(Map<String, dynamic> json) {
return Address(
street: json['street'] as String,
city: json['city'] as String,
zipCode: json['zipCode'] as String,
);
}
Map<String, dynamic> toJson() => {
'street': street, 'city': city, 'zipCode': zipCode,
};
}
class User {
final int id;
final String name;
final Address address;
User({required this.id, required this.name, required this.address});
factory User.fromJson(Map<String, dynamic> json) {
return User(
id: json['id'] as int,
name: json['name'] as String,
address: Address.fromJson(json['address'] as Map<String, dynamic>),
);
}
Map<String, dynamic> toJson() => {
'id': id, 'name': name, 'address': address.toJson(),
};
}Gestion des listes
Les tableaux JSON correspondent au type List de Dart :
// JSON with arrays
{
"name": "John",
"hobbies": ["coding", "reading", "gaming"],
"orders": [
{"id": 1, "product": "Laptop", "price": 999.99},
{"id": 2, "product": "Mouse", "price": 29.99}
]
}
// Dart
class User {
final String name;
final List<String> hobbies;
final List<Order> orders;
User({required this.name, required this.hobbies, required this.orders});
factory User.fromJson(Map<String, dynamic> json) {
return User(
name: json['name'] as String,
hobbies: List<String>.from(json['hobbies'] as List),
orders: (json['orders'] as List)
.map((e) => Order.fromJson(e as Map<String, dynamic>))
.toList(),
);
}
}Bonnes pratiques Null Safety
Avec le null safety de Dart, gérez correctement les champs nullable :
- Utilisez `required` pour les champs obligatoires
- Utilisez le suffixe `?` pour les types nullable
- Fournissez des valeurs par défaut avec `??`
- Utilisez `late` uniquement quand la valeur sera initialisée
class User {
final int id;
final String name;
final String? bio; // nullable
final String avatarUrl; // with default
User({
required this.id,
required this.name,
this.bio,
this.avatarUrl = 'https://example.com/default-avatar.png',
});
factory User.fromJson(Map<String, dynamic> json) {
return User(
id: json['id'] as int,
name: json['name'] as String,
bio: json['bio'] as String?,
avatarUrl: json['avatarUrl'] as String? ?? 'https://example.com/default-avatar.png',
);
}
}Utiliser json_serializable
Pour les grands projets, json_serializable automatise le code répétitif :
Étape 1 : Ajouter les dépendances
# pubspec.yaml
dependencies:
json_annotation: ^4.8.1
dev_dependencies:
json_serializable: ^6.7.1
build_runner: ^2.4.6Étape 2 : Créer le modèle avec annotations
import 'package:json_annotation/json_annotation.dart';
part 'user.g.dart';
@JsonSerializable()
class User {
final int id;
final String name;
final String email;
@JsonKey(name: 'is_active')
final bool isActive;
@JsonKey(defaultValue: 0.0)
final double score;
User({
required this.id,
required this.name,
required this.email,
required this.isActive,
required this.score,
});
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
}Étape 3 : Exécuter Build Runner
dart run build_runner build --delete-conflicting-outputsUtiliser Freezed pour les modèles immuables
Le package freezed génère des classes immuables avec copyWith et sérialisation JSON :
import 'package:freezed_annotation/freezed_annotation.dart';
part 'user.freezed.dart';
part 'user.g.dart';
@freezed
class User with _$User {
const factory User({
required int id,
required String name,
required String email,
@Default(false) bool isActive,
}) = _User;
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}Erreurs courantes et solutions
| Erreur | Solution |
|---|---|
| Utiliser `json['key']` sans vérification null | Utiliser `json['key'] as String? ?? ''` |
| Oublier de convertir les objets imbriqués | Appeler `NestedClass.fromJson(json['key'])` |
| Utiliser `int` pour tous les nombres | Utiliser `num` et convertir |
| Ne pas gérer les clés manquantes | Utiliser `json.containsKey('key')` |
| Retourner le mauvais type dans toJson | S'assurer que les objets imbriqués appellent `.toJson()` |
FAQ
Quelle est la meilleure façon de convertir JSON en classes Dart ?
Pour les petits projets, la conversion manuelle suffit. Pour les grands projets, utilisez json_serializable ou freezed.
Comment gérer les clés JSON dynamiques en Dart ?
Utilisez Map<String, dynamic>.
json_serializable ou freezed ?
json_serializable pour la sérialisation simple, freezed pour l'immuabilité et copyWith.
Comment gérer les dates ?
Utilisez DateTime.parse() pour les chaînes ISO 8601.