JSON Kurs Naechste Lektion

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.

JavaScriptDart
Objekte sind sehr flexibel.Klassen haben feste Felder und Typen.
JSON.stringify()jsonEncode()
JSON.parse()jsonDecode()
ArraysList
Objekte als DatenstrukturMap<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

  1. Erstelle eine Klasse LearningTask mit title, isDone und points.
  2. Schreibe eine Methode toJson() fuer diese Klasse.
  3. Schreibe factory LearningTask.fromJson(...).
  4. Erstelle ein Objekt, wandle es mit jsonEncode in Text um.
  5. Lade den Text mit jsonDecode wieder.
  6. Erweitere die Klasse um copyWith.
  7. Erklaere, warum Models besser sind als lose Maps.
Weiter zu Flutter JSON Speicherung