Flutter’da JSON Parçalama

Beyza Sunay Güler
Sep 1 · 8 min read

Makalelerini severek takip ettiğim, Flutter GDE Pooja Bhaumik tarafından yazılan “Parsing complex JSON in Flutter” makalesinin Türkçe çevirisini sizlere sunmak istedim. Keyifli okumalar:)

İtiraf etmeliyim ki, Flutter/Dart’da JSON ile çalıştıktan sonra Android’in gson dünyasını özlüyorum. Flutter’da API’lerle çalışmaya başladığımda JSON’ı parse etmek gerçekten beni çok uğraştırdı. Ve belli ki, yeni başlayanların da kafasını karıştıran bir konu.

Bu blog için dart:convert kütüphanesini kullanacağız. Bu en temel ayrıştırma yöntemidir ve yalnızca Flutter ile başlıyorsanız veya küçük bir proje oluşturuyorsanız önerilir. Bununla birlikte, Flutter’da JSON pars etmenin temellerini bilmek oldukça önemlidir. Bu konuda iyiyseniz veya daha büyük bir projeyle birlikte çalışmanız gerekiyorsa, json_serializable, vb. gibi kod üreteci kitaplıkları düşünün. Mümkünse, gelecekteki makalelerde bunları keşfedeceğim.

Bu örnek projeyi inceleyin. Bu blog için deneyebileceğiniz tüm kodlar var.

JSON yapısı #1 : Basit Map

student.json üzerinden basit bir JSON yapısı oluşturarak başlayalım;

{
"id":"487349",
"name":"Pooja Bhaumik",
"score" : 1000
}

Kural #1 : Yapıyı tanımlayın. Json strings bir map’e (key-value çiftleri) veya map listesine sahip olacaktır.

Kural #2 : Kıvrık parantez ile başlar? Bu bir map.
Köşeli parantez ile başlar? Bu map listesidir.

student.json açıkça bir map. ( Örneğin, id bir key, ve 487349 id için bir value)

Hadi bu json yapısı için PODO (Plain Old Dart Object?/Sade Eski Dart Objesi?) dosyası yapalım. Bu kodu örnek projede student_model.dart adresinde bulabilirsiniz.

class Student{
String studentId;
String studentName;
int studentScores;
Student({
this.studentId,
this.studentName,
this.studentScores
});
}

Harika!
Öyle miydi? Çünkü json maps ile PODO dosyası arasında eşleşme yok. Varlık adları bile eşleşmiyor.
Biliyorum, biliyorum. Henüz bitirmedik. Sınıf üyelerini json objesi ile eşleştirmeye çalışmalıyız. Bunun için , bir factory metodu yaratmamız gerekiyor. Dart belgelerine göre , factory anahtar sözcüğünü her zaman sınıfının yeni bir örneğini oluşturmayan bir constructor uygularken kullanırız ve şu anda ihtiyacımız olan şey bu.

factory Student.fromJson(Map<String, dynamic> parsedJson){
return Student(
studentId: parsedJson['id'],
studentName : parsedJson['name'],
studentScores : parsedJson ['score']
);
}

Burada, amacı sadece json’ını seri hale getirmek olan Student.fromJson adlı bir fabrika metodu yaratıyoruz.

Ben yeniyim, bana Deserialization hakkında bilgi verir misin?

Elbette. Öncelikle Serialization ve Deserialization’dan bahsedelim . Serialization basitçe datayı string olarak yazmak anlamına geliyor(bir obje de olabilir) ,ve Deserialization ise tam zıttı. Raw datayı alır ve obje modelini yeniden yapılandırır. Bu makalede, çoğunlukla deserialization kısmını kullanacağız. İlk kısımda , json string’i student.json ‘dan deserializ’e edeceğiz.

Böylece factory metodumuz dönüştürücü metodu olarak adlandırılabilir.

Ayrıca fromJson metodundaki parametreye dikkat etmeliyiz. Bu bir Map<String, dynamic> .Bu bir String key ile birlikte dynamic value/değerini olan bir map. İşte bu yüzden yapıyı tanımlamamız gerekiyor. Eğer bu json yapısı bir map listesi olsaydı, o zaman bu parametre farklı olurdu.

Ama neden dynamic?
Sorunuzu cevaplamak için önce başka bir json yapısına bakalım.

name bir Map<String, String> ,majors bir String map’i, List<String> ve subjects bir String map ve List<Object>

Key her zaman bir string olduğundan ve value her türden olabileceğinden, dynamic kısmını güvenli tarafta tutuyoruz.

student_model.dart kodunun tamamını buradan kontrol edebilirsiniz.

Objeye Erişme

Student.fromJson’u çağırmak ve value Student objesinden almak için kodu alacak bir student_services.dart yazalım.

Snippet #1 : import

import 'dart:async' show Future;
import 'package:flutter/services.dart' show rootBundle;
import 'dart:convert';
import 'package:flutter_json/student_model.dart';

Son import model dosyanızın adı olacak.

Snippet #2 : Json Asset’i Yüklemek(opsiyonel)

Future<String> _loadAStudentAsset() async {
return await rootBundle.loadString('assets/student.json');
}

Bu özel projede, json dosyamızı asset’ler klasöründe bulunduruyoruz, bu yüzden json’u bu şekilde yüklemeliyiz. Ancak json dosyanızı cloud da kullanıyorsanız, bunun yerine network call yapabilirsiniz. Network calls bu makalenin kapsamı dışındadır.

Snippet #3 : yanıtı yüklemek

Future loadStudent() async {
String jsonString = await _loadAStudentAsset();
final jsonResponse = json.decode(jsonString);
Student student = new Student.fromJson(jsonResponse);
print(student.studentScores);
}

loadStudent() metodunda,
Satır 1 : Assets’den raw json String’lerin yüklenmesi.
Satır 2 :Elimizdeki raw json String kodunu çözüyoruz. Bu raw’a göre json String’imizi çözüyoruz.
Satır 3 :Şimdi de, kodları çözülmüş json yanıtını Student.fromJson yöntemini çağırarak seri hale getiriyoruz, böylece varlıklarımıza erişmek için Student objesini kullanabiliriz.
Satır 4 : Burada yaptığımız gibi, Student sınıfından studentScores ‘u print ediyoruz.

Bütün print value’ları görmek için Flutter konsolunuzu kontrol edin.( Android Studio da , Run tab’in altında)

Ve voila! İlk JSON pars’ınızı yaptınız(veya yapamadınız).
Not: Buradaki 3 snippets’ı hatırlayın, bir sonraki json parse etmek için kullanacağız (yalnızca dosya adlarını ve metot adlarını değiştiriyoruz) ve kodu burada tekrarlamayacağım. Ama yine de örnek projede her şeyi bulabilirsiniz.

JSON yapısı # 2: Dizilerle basit yapı

Şimdi yukarıdakiyle benzer bir json yapısını ele geçiriyoruz, ancak sadece tek bir value yerine, aynı zamanda bir dizi value’ya sahip olabilir.

{
"city": "Mumbai",
"streets": [
"address1",
"address2"
]
}

Yani bu address.json,’da basit bir String değerine sahip city varlığımız var, ancak streets bir String dizisi.
Bildiğim kadarıyla, Dart bir dizi veri türüne sahip değil, bunun yerine bir List<datatype>içerdiğinden streets bir List<String> olacak.

Şimdi Kural#1 ve Kural#2'yi kontrol etmeliyiz . Bu kesinlikle bir map çünkü kıvrık parantez ile başlıyor. streets yine bir List , ama onun için daha sonra endişeleniriz.

Yani address_model.dart başlangıçta böyle görünecek

class Address {
final String city;
final List<String> streets;
Address({
this.city,
this.streets
});
}

Map olduğu için,bizim Address.fromJson metodumuz hala Map<String, dynamic> parametresine sahip.

factory Address.fromJson(Map<String, dynamic> parsedJson) {

return new Address(
city: parsedJson['city'],
streets: parsedJson['streets'],
);
}

Şimdi address_services.dart’ı yukarıda belirttiğimiz 3 snippets ekleyerek oluşturun. Uygun dosya adlarını ve metot adlarını koymayı unutmayın.Örnek projede zaten sizin için address_services.dart oluşturulmuş.

Bunu çalıştırdığınız da küçük bir error alıyorsunuz. :/

type 'List<dynamic>' is not a subtype of type 'List<String>'

Size söylemeliyim, bu hatalar Dart ile olan gelişimimin hemen hemen her aşamasında ortaya çıktı. Ve sizde alacaksınız. Öyleyse bunun ne anlama geldiğini açıklayayım. Bir List<String> istiyoruz ancak List<dynamic> alıyoruz çünkü uygulamamız henüz türünü tanımlayamıyor.

Bu yüzden açıkça bunu bir List<String>’e dönüştürmeliyiz

var streetsFromJson = parsedJson['streets'];
List<String> streetsList = new List<String>.from(streetsFromJson);

Burada ilk olarak, değişken streetsFromJson’dan streets varlıklarına eşleştiriyoruz. streetsFromJson hala bir List<dynamic>. Şimdi açıkça streetsFromJson ’ın tüm öğeleri içeren, yeni bir List<String> streetsList oluşturun.

Yenilenen metodu buradan kontrol edebilirsiniz. Return statement ifadesine dikkat edin.
Şimdi
address_services.dart ile birlikte çalıştırabilirsiniz ve mükemmel bir şekilde çalışacak.

Json yapısı # 3: Basit İç içe yapılar

Şimdi ya shape.json’dan iç içe geçmiş bir yapıya sahipsek?

{
"shape_name":"rectangle",
"property":{
"width":5.0,
"breadth":10.0
}
}

Burada, property temel data-tipi veri türü yerine bir obje içerir.
Peki PODO nasıl görünecek?

Tamam, hadi biraz daha parçalayalım.
shape_model.dart dosyamızda önce Property için bir sınıf yapalım.

class Property{
double width;
double breadth;
Property({
this.width,
this.breadth
});
}

Şimdi Shape için bir sınıf oluşturalım. Her iki sınıfı da aynı Dart dosyasına koyuyorum.

class Shape{
String shapeName;
Property property;
Shape({
this.shapeName,
this.property
});
}

İkinci data property temelde önceki sınıf property ‘nin bir nesnesi olduğuna dikkat edin.

Kural # 3: İç içe yapılar için, önce sınıfları ve constructor’ları yapın, sonra factory metodunu alt seviyeden ekleyin.

Alt seviye ile, önce Property sınıfını fethettiğimizi, sonra da Shape sınıfının üstüne bir seviye yükseldiğimizi kastettik. Bu sadece benim önerim, Flutter kuralı değil.

factory Property.fromJson(Map<String, dynamic> json){
return Property(
width: json['width'],
breadth: json['breadth']
);
}

Bu basit bir map.

Ama Shape sınıfındaki factory metodumuz için, sadece bunu yapamayız.

factory Shape.fromJson(Map<String, dynamic> parsedJson){
return Shape(
shapeName: parsedJson['shape_name'],
property : parsedJson['property']
);
}

property : parsedJson['property'] İlk olarak, bu tür uyuşmazlığın hatasını verir -

type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'Property'

Ve ikincisi, Property için bu güzel küçük sınıfı yaptık, hiçbir yerde kullanıldığını görmüyorum.

Doğru. Property sınıfımızı burada map yapmalıyız.

factory Shape.fromJson(Map<String, dynamic> parsedJson){
return Shape(
shapeName: parsedJson['shape_name'],
property: Property.fromJson(parsedJson['property'])
);
}

Bu yüzden, basitçe , Property sınıfımızdan Property.fromJson metodunu çağırıyoruz ve karşılığında ne elde edersek, onu map property ile eşitliyoruz. Basit! Buradaki kodu kontrol edin.

Bunu shape_services.dart ile çalıştırın ve gitmeye hazırsınız.

JSON yapısı # 4: Listeleri olan iç içe yapılar

product.json ‘unumuzu kontrol edelim

{
"id":1,
"name":"ProductName",
"images":[
{
"id":11,
"imageName":"xCh-rhy"
},
{
"id":31,
"imageName":"fjs-eun"
}
]
}

Tamam, şimdi daha derine iniyoruz. İçeride bir yerde objelerin listesini görüyorum.

Evet, öyleyse bu yapının bir obje listesi var, ama yine de kendisi bir map. (Bkz. Kural # 1 ve Kural # 2). Şimdi product_model.dart‘ı kurarak, Kural 3’e atıfta bulunalım.

Product ve Image olarak iki sınıf yarattık.
Not: Product , Image Listesi olan bir veri üyesi olacak.

class Product {
final int id;
final String name;
final List<Image> images;
Product({this.id, this.name, this.images});
}
class Image {
final int imageId;
final String imageName;
Image({this.imageId, this.imageName});
}

Image için factory metodu oldukça basit ve temel olacak.

factory Image.fromJson(Map<String, dynamic> parsedJson){
return Image(
imageId:parsedJson['id'],
imageName:parsedJson['imageName']
);
}

Şimdi Product için factory metodu

factory Product.fromJson(Map<String, dynamic> parsedJson){  return Product(
id: parsedJson['id'],
name: parsedJson['name'],
images: parsedJson['images']
);
}

Bu çalıştığında hata verecektir,

type 'List<dynamic>' is not a subtype of type 'List<Image>'

Ve bunu yaparsak,

images: Image.fromJson(parsedJson['images'])

Bu da kesinlikle yanlıştır ve hemen bir hata verecektir çünkü List<Image>’a Image objesi atayamazsınız.

Bu yüzden bir List<Image> yaratmalı ve images atamalıyız.

var list = parsedJson['images'] as List;
print(list.runtimeType); //returns List<dynamic>
List<Image> imagesList = list.map((i) => Image.fromJson(i)).toList();

Buradaki list bir List<dynamic>’tir. Şimdi listeyi tekrar sıralıyoruz ve list’de ki her objeyi Image.fromJson ’ı çağırarak Image ile eşleştiriyoruz ardından her harita objesini toList() ile yeni bir listeye koyuyoruz ve List<Image> , imagesList ’e koyuyoruz. Kodun tamamını buradan bulabilirsiniz.

JSON yapısı # 5: Map listesi

Şimdi photo.json ’a geçelim.

[
{
"albumId": 1,
"id": 1,
"title": "accusamus beatae ad facilis cum similique qui sunt",
"url": "http://placehold.it/600/92c952",
"thumbnailUrl": "http://placehold.it/150/92c952"
},
{
"albumId": 1,
"id": 2,
"title": "reprehenderit est deserunt velit ipsam",
"url": "http://placehold.it/600/771796",
"thumbnailUrl": "http://placehold.it/150/771796"
},
{
"albumId": 1,
"id": 3,
"title": "officia porro iure quia iusto qui ipsa ut modi",
"url": "http://placehold.it/600/24f355",
"thumbnailUrl": "http://placehold.it/150/24f355"
}
]

Kural#1 ve Kural #2 bana bunun map olamayacağını söylüyor çünkü json dizisi köşeli parantez ile başlıyor. Yani bu bir obje listesi mi? Evet. Burada olan obje Photo’dur.(ya da ne demek isterseniz).

class Photo{
final String id;
final String title;
final String url;
Photo({
this.id,
this.url,
this.title
}) ;
factory Photo.fromJson(Map<String, dynamic> json){
return new Photo(
id: json['id'].toString(),
title: json['title'],
url: json['json'],
);
}
}

Ama bu bir Photo listesi, yani bu List<Photo> içeren bir sınıf oluşturmam gerektiği anlamına mı geliyor?

Evet, bunu tavsiye ederim.

class PhotosList {
final List<Photo> photos;
PhotosList({
this.photos,
});
}

Ayrıca dikkat edin, bu json dizesi bir map listesidir. Bu nedenle, factory metodumuz da, liste olduğu için Map<String, dynamic> parametremiz yok. Bu yüzden ilk önce yapının tanımlanması önemli. Dolayısıyla yeni bir parametremiz List<dynamic> olacak.

factory PhotosList.fromJson(List<dynamic> parsedJson) {    List<Photo> photos = new List<Photo>();    return new PhotosList(
photos: photos,
);
}

Bu bir hata verir

Invalid value: Valid value range is empty: 0

Hey, çünkü Photo.fromJson metodunu asla kullanamayız.
Ya listenin başlangıcından sonra bu kod satırını eklersek?

photos = parsedJson.map((i)=>Photo.fromJson(i)).toList();

Daha önceki konseptin aynısı, bunu json dizisindeki herhangi bir tuşla eşleştirmek zorunda değiliz çünkü bu bir liste, map değil. Kodu burada.

JSON yapısı # 6: Karmaşık iç içe yapılar

İşte page.json.

Bunu çözmenizi isteyeceğim. Örnek projeye zaten dahil edilmiştir. Bunun için model ve servisler dosyası oluşturmalısınız. Ancak size ipuçları ve püf noktalarını vermeden sonucu söylemeyeceğim. ( eğer ihtiyacınız varsa)

Kural#1 ve Kural#2 her zaman ki gibi geçerlidir. Önce yapıyı tanımlayın. İşte map. Böylece 1–5 arasındaki tüm json yapıları yardımcı olacaktır.

Kural #3 sizden önce sınıfları ve yapıları yapmanızı ardından factory metodunu bottom seviyeden eklemenizi ister. Başka bir ipucu. Ayrıca sınıfları deep/bottom seviyesinden ekleyin. Örneğin, bu json yapısı için, önce Image sınıfını, ardından Data , Author ve Page sınıfını yapınız. Ve factory metodunu de aynı sırada ekleyin.

Image ve Data sınıfı için Json yapısı #4'e bakınız.
Author sınıfı için Json yapısı #3'e bakınız.

Yeni başlayanlar için ipucu: Herhangi bir asset ile deneme yaparken , pubspec.yaml dosyasında bildirmeyi unutmayın.

Ve bu Fluttery makalesi için bu kadar. Bu makale, belki en iyi JSON ayrıştırma makalesi olmayabilir (çünkü hala çok şey öğreniyorum) ancak umarım başlamanıza sebep olur.

Flutter Türkiye Slack kanalı dahil olup sorular sormak ve tanışmak için hepinizi Slack kanalımıza bekliyoruz.:)Bizleri takip etmek isterseniz Twitter hesabımıza da bakabilirsiniz.

Flutter Türkiye

Flutter SDK için Türkçe ve İngilizce kaynaklarin yayinlayan topluluk

Thanks to Adem Furkan ÖZCAN

Beyza Sunay Güler

Written by

Flutter Türkiye

Flutter SDK için Türkçe ve İngilizce kaynaklarin yayinlayan topluluk

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade