Lektion 04 - Dart JSON
JSON in Dart: Map, Model, toJson und fromJson
In JavaScript war JSON sehr nah an normalen Objekten. In Dart arbeiten wir strenger mit Typen, Klassen und Maps. Genau das macht Speicherung in Dart etwas sauberer, aber am Anfang auch wichtiger zu verstehen.
1. Was ist in Dart anders als in JavaScript?
JavaScript ist sehr locker. Ein Objekt kann schnell neue Eigenschaften bekommen. Dart ist strenger: Wenn du mit Klassen arbeitest, legst du vorher fest, welche Felder existieren und welche Typen sie haben. Das hilft dir, Fehler frueher zu sehen.
JSON.stringify()jsonEncode()JSON.parse()jsonDecode()ListMap<String, dynamic>2. Das wichtigste Paket: dart:convert
Fuer JSON brauchst du in Dart meistens dart:convert. Dieses Paket ist
bereits Teil von Dart. Du musst es nur importieren.
import 'dart:convert';
Danach kannst du jsonEncode und jsonDecode nutzen.
3. Dart Map als JSON-Struktur
Eine Map speichert Schluessel-Wert-Paare. Fuer JSON nutzt man in Dart
sehr oft Map<String, dynamic>. Der Schluessel ist ein Text, der Wert
kann verschiedene Typen haben.
final userMap = {
'name': 'Mina',
'points': 80,
'isActive': true,
};
print(userMap['name']);
print(userMap['points']);
Diese Map ist noch kein JSON-Text. Sie ist eine Dart-Datenstruktur. Erst durch
jsonEncode wird daraus speicherbarer Text.
4. Map zu JSON-Text: jsonEncode
jsonEncode wandelt Dart-Daten in JSON-Text um. Diesen Text kannst du
speichern, an eine API senden oder in eine Datei schreiben.
import 'dart:convert';
void main() {
final userMap = {
'name': 'Mina',
'points': 80,
'isActive': true,
};
final jsonText = jsonEncode(userMap);
print(jsonText);
}
Ausgabe:
{"name":"Mina","points":80,"isActive":true}
5. JSON-Text zu Dart-Daten: jsonDecode
jsonDecode macht aus JSON-Text wieder Dart-Daten. Meist bekommst du
danach eine Map oder eine List.
import 'dart:convert';
void main() {
const jsonText = '{"name":"Mina","points":80,"isActive":true}';
final decoded = jsonDecode(jsonText);
print(decoded['name']);
print(decoded['points']);
}
Wichtig: jsonDecode gibt erstmal dynamic zurueck. Fuer
kleine Beispiele ist das okay. Fuer richtige Apps solltest du die Daten in eine
Klasse umwandeln.
6. Warum Models wichtig sind
Eine Map ist flexibel, aber auch fehleranfaellig. Wenn du dich beim Schluessel verschreibst, merkt Dart das nicht sofort. Eine Model-Klasse macht die Daten klarer und sicherer.
// Fehler wird erst spaeter sichtbar:
print(userMap['poitns']);
// Besser:
print(user.points);
Ein Model ist eine Klasse, die beschreibt, wie ein Datensatz aussieht. Fuer dein Lernsystem koennte ein User-Model Name, Punkte und erledigte Lektionen enthalten.
7. Model-Klasse erstellen
Diese Klasse ist immutable aufgebaut: Die Felder sind final, und der
Konstruktor setzt alle Werte. Das passt gut zu sauberer Speicherung.
class LearningUser {
final String name;
final int points;
final List<String> completedLessons;
const LearningUser({
required this.name,
required this.points,
required this.completedLessons,
});
}
required bedeutet: Beim Erstellen des Objekts muss dieser Wert
uebergeben werden. Dadurch entstehen keine halb fertigen User-Objekte.
8. toJson: Objekt zu Map machen
JSON kann nicht direkt deine Dart-Klasse speichern. Erst muss dein Objekt in eine
Map umgewandelt werden. Dafuer nutzt man meistens eine Methode toJson.
class LearningUser {
final String name;
final int points;
final List<String> completedLessons;
const LearningUser({
required this.name,
required this.points,
required this.completedLessons,
});
Map<String, dynamic> toJson() {
return {
'name': name,
'points': points,
'completedLessons': completedLessons,
};
}
}
toJson liefert keine JSON-Textdatei, sondern eine Map. Danach kann
jsonEncode daraus JSON-Text machen.
9. fromJson: Map zu Objekt machen
Beim Laden passiert der umgekehrte Weg. Du bekommst eine Map und baust daraus wieder ein stark typisiertes Objekt. Dafuer nutzt man oft einen Named Constructor oder eine Factory.
class LearningUser {
final String name;
final int points;
final List<String> completedLessons;
const LearningUser({
required this.name,
required this.points,
required this.completedLessons,
});
factory LearningUser.fromJson(Map<String, dynamic> json) {
return LearningUser(
name: json['name'] as String,
points: json['points'] as int,
completedLessons: List<String>.from(json['completedLessons'] as List),
);
}
Map<String, dynamic> toJson() {
return {
'name': name,
'points': points,
'completedLessons': completedLessons,
};
}
}
List<String>.from(...) ist wichtig, weil JSON-Listen nach dem
Decodieren erstmal nicht automatisch als exakt typisierte Dart-Listen gelten.
10. Komplettes Dart-Beispiel
Jetzt kommt der ganze Ablauf: Objekt erstellen, zu JSON-Text machen, JSON-Text wieder lesen und daraus ein neues Objekt bauen.
import 'dart:convert';
class LearningUser {
final String name;
final int points;
final List<String> completedLessons;
const LearningUser({
required this.name,
required this.points,
required this.completedLessons,
});
factory LearningUser.fromJson(Map<String, dynamic> json) {
return LearningUser(
name: json['name'] as String,
points: json['points'] as int,
completedLessons: List<String>.from(json['completedLessons'] as List),
);
}
Map<String, dynamic> toJson() {
return {
'name': name,
'points': points,
'completedLessons': completedLessons,
};
}
}
void main() {
const user = LearningUser(
name: 'Mina',
points: 80,
completedLessons: ['json-basics', 'dart-json'],
);
final jsonText = jsonEncode(user.toJson());
print(jsonText);
final decodedMap = jsonDecode(jsonText) as Map<String, dynamic>;
final loadedUser = LearningUser.fromJson(decodedMap);
print(loadedUser.name);
print(loadedUser.points);
}
11. copyWith fuer Updates
Beim Speichern willst du Daten oft aktualisieren. Statt ein Objekt direkt zu
veraendern, kannst du mit copyWith eine neue Version erstellen.
Das passt sehr gut zu Flutter und immutable Daten.
LearningUser copyWith({
String? name,
int? points,
List<String>? completedLessons,
}) {
return LearningUser(
name: name ?? this.name,
points: points ?? this.points,
completedLessons: completedLessons ?? this.completedLessons,
);
}
Beispiel:
final updatedUser = user.copyWith(
points: user.points + 10,
completedLessons: [
...user.completedLessons,
'flutter-storage',
],
);
12. Uebungen
- Erstelle eine Klasse
LearningTaskmittitle,isDoneundpoints. - Schreibe eine Methode
toJson()fuer diese Klasse. - Schreibe
factory LearningTask.fromJson(...). - Erstelle ein Objekt, wandle es mit
jsonEncodein Text um. - Lade den Text mit
jsonDecodewieder. - Erweitere die Klasse um
copyWith. - Erklaere, warum Models besser sind als lose Maps.