JSON'dan Dart'a
JSON'dan fromJson ve toJson ile Dart sınıfları oluşturun
JSON Girişi
Dart Çıktısı
JSON'dan Dart'a Dönüşüm Nedir?
JSON'dan Dart'a dönüşüm, ham bir JSON nesnesini alır ve tiplendirilmiş alanlar, adlandırılmış bir yapıcı, fromJson factory ve toJson metodu içeren Dart sınıf tanımları üretir. Dart'ın Flutter'da çalışma zamanı yansıması yoktur (dart:mirrors devre dışı bırakılmıştır); bu nedenle açık eşleme kodu yazmadan JSON'u tiplendirilmiş nesnelere dönüştüremezsiniz. Her REST API yanıtı, Firebase belgesi veya yapılandırma yükü için tip güvenliğiyle alanlara erişebilmek üzere karşılık gelen bir Dart model sınıfı gerekir.
JSON için tipik bir Dart model sınıfı, her anahtar için final alanlar, adlandırılmış parametreli bir yapıcı (null olamayan alanlar için required anahtar kelimesi kullanılarak), Map of String to dynamic okuyan fromJson adlı bir factory yapıcı ve Map of String to dynamic döndüren bir toJson metodu içerir. İç içe JSON nesneleri ayrı sınıflara dönüşür. Diziler tiplendirilmiş List alanlarına dönüşür. Nullable JSON değerleri, türün sonuna ? ekiyle Dart'ın null-safety söz dizimini kullanır.
Bu model sınıflarını elle yazmak; her JSON anahtarını okumayı, Dart türüne karar vermeyi, her alan için fromJson dönüşümü oluşturmayı (.map().toList() ile liste eşleme dahil), toJson map literalini oluşturmayı ve iç içe her nesne için bu işlemi tekrarlamayı gerektirir. 12 alanlı ve 2 iç içe nesneli bir JSON nesnesi için bu 3 sınıf, 6 factory satırı ve düzinelerce dönüşüm ifadesi anlamına gelir. Bir dönüştürücü tek bir yapıştırmayla tüm bunları milisaniyeler içinde üretir.
Neden JSON'dan Dart'a Dönüştürücü Kullanmalısınız?
Dart model sınıflarını JSON'dan elle yazmak; alan adlarını okumayı, örnek değerlerden türleri tahmin etmeyi, doğru null işlemeyle fromJson dönüşümleri yazmayı ve iç içe nesneler için işlemi tekrarlamayı gerektirir. API yapısı değiştiğinde her alan güncellemesi yapıcıya, fromJson'a ve toJson'a dokunur. Bir dönüştürücü bu tekrarlayan işi ortadan kaldırır.
JSON'dan Dart'a Kullanım Senaryoları
JSON'dan Dart'a Tür Eşleme
Her JSON değeri belirli bir Dart türüne eşlenir. Aşağıdaki tablo, dönüştürücünün her JSON türünü nasıl çevirdiğini göstermektedir. Alternatif sütunu, daha az yaygın veya manuel eşleme senaryolarında kullanılan türleri gösterir.
| JSON Türü | Örnek | Dart Türü | Alternatif |
|---|---|---|---|
| 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> |
Dart JSON Serileştirme Yaklaşımları
Dart ve Flutter, JSON serileştirmeyi ele almak için birden fazla yol sunar. Manuel fromJson/toJson en basit yaklaşımdır ve kod üretimi gerektirmez. Daha büyük projeler için json_serializable ve freezed gibi build_runner tabanlı çözümler, derleme zamanında gerekli kodu oluşturarak hataları azaltır ve modelleri JSON sözleşmesiyle tutarlı tutar.
| Yaklaşım | Açıklama | Kaynak |
|---|---|---|
| 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 |
Manuel vs json_serializable vs freezed
Dart'ın JSON model sınıfları için üç yaygın yaklaşımı vardır. Manuel fromJson/toJson sıfır bağımlılıkla çalışır. json_serializable eşleme kodunu otomatikleştirir. freezed, json_serializable'ın üzerine değiştirilemezlik, copyWith ve örüntü eşleştirme ekler.
Kod Örnekleri
Bu örnekler, JSON seri dışı bırakma için oluşturulan Dart sınıflarının nasıl kullanılacağını, build_runner ile json_serializable'ın nasıl kurulacağını ve JavaScript ile Python'dan programatik olarak Dart sınıflarının nasıl oluşturulacağını göstermektedir.
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;
# }