JSON to Dart
Generera Dart-klasser från JSON med fromJson och toJson
JSON-inmatning
Dart-utdata
Vad är JSON till Dart-konvertering?
JSON till Dart-konvertering tar ett råformat JSON-objekt och producerar Dart-klassdefinitioner med typade fält, en namngiven konstruktor, en fromJson-fabrik och en toJson-metod. Dart saknar körtidsreflektion i Flutter (dart:mirrors är inaktiverat), vilket innebär att du inte kan deserialisera JSON till typade objekt utan att skriva explicit mappningskod. Varje REST API-svar, Firebase-dokument eller konfigurationsnyttolast behöver en motsvarande Dart-modellklass innan du kan komma åt dess fält på ett typsäkert sätt.
En typisk Dart-modellklass för JSON deklarerar final-fält för varje nyckel, en konstruktor med namngivna parametrar (med nyckelordet required för icke-nullbara fält), en fabrikskonstruktor named fromJson som läser från en Map av String till dynamic, och en toJson-metod som returnerar en Map av String till dynamic. Nästlade JSON-objekt blir separata klasser. Arrayer blir typade List-fält. Nullbara JSON-värden använder Darts null-säkerhetssyntax med suffixet ? på typen.
Att skriva dessa modellklasser för hand innebär att du läser varje JSON-nyckel, bestämmer Dart-typen, skapar fromJson-castingen för varje fält (inklusive listmappning med .map().toList()), bygger en toJson-mappningsliteral och upprepar processen för varje nästlat objekt. För ett JSON-objekt med 12 fält och 2 nästlade objekt innebär det 3 klasser, 6 fabriksrader och dussintals cast-uttryck. En konverterare producerar allt detta på millisekunder från en enda inklistring.
Varför använda en JSON till Dart-konverterare?
Att manuellt skriva Dart-modellklasser från JSON innebär att läsa fältnamn, gissa typer från exempelvärden, skriva fromJson-casting med korrekt null-hantering och upprepa processen för nästlade objekt. När API-strukturen förändras berör varje fältuppdatering konstruktorn, fromJson och toJson. En konverterare tar bort det repetitiva arbetet.
Användningsfall för JSON till Dart
Typmappningsreferens för JSON till Dart
Varje JSON-värde mappas till en specifik Dart-typ. Tabellen nedan visar hur konverteraren översätter varje JSON-typ. Kolumnen Alternativ visar typer som används i mindre vanliga eller manuella mappningsscenarier.
| JSON-typ | Exempel | Dart-typ | Alternativ |
|---|---|---|---|
| string | "hello" | String | String |
| number (integer) | 42 | int | int |
| number (float) | 3.14 | double | double |
| boolean | true | bool | bool |
| null | null | dynamic | Null / dynamic |
| object | {"k": "v"} | NestedClass | Map<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> |
Serialiseringsmetoder för Dart JSON
Dart och Flutter erbjuder flera sätt att hantera JSON-serialisering. Manuell fromJson/toJson är det enklaste tillvägagångssättet och kräver ingen kodgenerering. För större projekt genererar build_runner-baserade lösningar som json_serializable och freezed mallkoden vid kompileringstid, vilket minskar fel och håller modellerna konsekventa med JSON-kontraktet.
| Tillvägagångssätt | Beskrivning | Källa |
|---|---|---|
| json_serializable | Code-generation-based JSON serialization. Generates .g.dart files at build time via build_runner. Type-safe and compile-time verified. | Official (Google) |
| freezed | Generates immutable data classes with copyWith, fromJson/toJson, equality, and pattern matching support. Built on top of json_serializable. | Community (rrousselGit) |
| built_value | Generates immutable value types with serialization. Enforces immutability patterns. Used in larger codebases with strict data modeling. | |
| dart_mappable | Annotation-based mapper that generates fromJson, toJson, copyWith, and equality. Simpler setup than freezed with similar features. | Community |
| Manual fromJson/toJson | Hand-written factory constructors and toJson methods. No code generation needed. Full control over the mapping logic. | Built-in Dart |
Manuell vs json_serializable vs freezed
Dart har tre vanliga metoder för JSON-modellklasser. Manuell fromJson/toJson har inga beroenden. json_serializable automatiserar mappningskoden. freezed lägger till oföränderlighet, copyWith och mönstermatchning ovanpå json_serializable.
Kodexempel
Dessa exempel visar hur man använder genererade Dart-klasser för JSON-deserialisering, hur man konfigurerar json_serializable med build_runner, och hur man genererar Dart-klasser programmatiskt från JavaScript och Python.
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
}// 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-outputsfunction 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;
// }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;
# }