Flutter: Safe JSON parsing using extension to avoid redundant code
Hello Folks, in this article you will learn more about parsing JSON in easiest & safest way in Flutter using Google’s brand now Dart programming language.
During Application development, we often need to deal with remote server to send & retrieve data. That required active internet connection and Http request protocol to form the network request.
Usually, network data transmit in the form of JSON data, that needs to be serialized & de-serialized at remote & client end to get the readable data.
In Flutter, there is a dart.convert library that provides JSON serialisation & deserialisation in efficient way.
Let’s have a look inside the Dart code to perform such operation.
We’ll use a test public API to receive JSON. Following is the test base URL to hit the API.
https://jsonplaceholder.typicode.com/users
We’re going to use standard http library from dart.dev community.
Let’s Kick off into code editor to add the http library.
Firstly, navigate the pubspec.yaml file. Add the http: any into dependencies section, where any automatically fetches library version that the compatible with your current flutter framework.
Following the complete guide on pubspec.yaml file.
- dependencies
- flutter
- sdk: flutter
- cupertino_icons: ^1.0.0
- This adds the Cupertino Icons font to your application.
- Use with the CupertinoIcons class for iOS style icons.
- flutter
Now I assume you have a stateful widget in which we’re going to call API to get the JSON response.
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
@override
void initState() {
super.initState();
callAPI();
}
void callAPI() async{
var url = ‘https://jsonplaceholder.typicode.com/users’;
var response = await http.get(url);
var bodyJSON = jsonDecode(response.body) as List<dynamic>;
var arrUser = bodyJSON.map((e) => MDLUser.fromJSON(e)).toList();
}
Following is an example JSON object that we’ll receive during the API response.
Now we’ll create a Model class that converts JSON into human readable object.
class MDLUser {
int id;
String name, username, email, phone, website;
MDLAddress address;
MDLCompany company;
MDLUser({
this.id, this.name, this.username, this.email,
this.phone, this.website, this.address, this.company,
});
Map<String, dynamic> get toJSON {
return {
‘id’:id,
‘name’:name,
‘username’:username,
’email’:email,
‘phone’:phone,
‘website’:website,
‘address’:address.toJSON,
‘company’:company.toJSON,
};
}
factory MDLUser.fromJSON(Map<String, dynamic> json) {
return MDLUser(
id: json[‘id’] ?? 0,
name: json[‘name’] ?? ”,
username: json[‘username’] ?? ”,
email: json[’email’] ?? ”,
phone: json[‘phone’] ?? ”,
website: json[‘website’] ?? ”,
address: MDLAddress.fromJSON(json[‘address’] ?? {}),
company: MDLCompany.fromJSON(json[‘company’] ?? {}),
);
}
}
class MDLAddress {
String street, suite, city, zipcode;
MDLGeo geo;
Map<String, dynamic> get toJSON {
return {
‘street’:street,
‘suite’:suite,
‘city’:city,
‘zipcode’:zipcode,
‘geo’:geo.toJSON,
};
}
MDLAddress({
this.street, this.suite, this.city,
this.zipcode, this.geo,
});
factory MDLAddress.fromJSON(Map<String, dynamic> json) {
return MDLAddress(
city: json[‘city’] ?? ”,
zipcode: json[‘zipcode’] ?? ”,
suite: json[‘suite’] ?? ”,
street: json[‘street’] ?? ”,
geo: MDLGeo.fromJSON(json[‘geo’] ?? {}),
);
}
}
class MDLGeo {
String lat, lng;
Map<String, dynamic> get toJSON {
return {
‘lat’:lat,
‘lng’:lng,
};
}
MDLGeo({
this.lat, this.lng
});
factory MDLGeo.fromJSON(Map<String, dynamic> json) {
return MDLGeo(
lat: json[‘lat’] ?? ”,
lng: json[‘lng’] ?? ”,
);
}
}
class MDLCompany {
String name, bs, catchPhrase;
Map<String, dynamic> get toJSON {
return {
‘name’: name,
‘bs’: bs,
‘catchPhrase’: catchPhrase,
};
}
MDLCompany({
this.name, this.bs, this.catchPhrase,
});
factory MDLCompany.fromJSON(Map<String, dynamic> json) {
return MDLCompany(
name: json[‘name’] ?? ”,
bs: json[‘bs’] ?? ”,
catchPhrase: json[‘catchPhrase’] ?? ”,
);
}
}
As you can see in above model classes, we’ve used ?? Operator to safe parse JSON when a particular JSON key doesn’t found.
But it seems cumbersome to use ?? operator each time when parsing JSON.
In this case, extension in Dart can help to avoid redundant code. Let’s see how we can utilize power of extension.
We will create a new file named extension.dart
extension MapEXT on Map<String, dynamic> {
String safeString(String key){
return this.containsKey(key) ? this[key] : ”;
}
double safeDouble(String key){
return this.containsKey(key) ? this[key] : 0.0;
}
int safeInt(String key){
return this.containsKey(key) ? this[key] : 0;
}
bool safeBool(String key){
return this.containsKey(key) ? this[key] : false;
}
List safeList(String key){
return this.containsKey(key) ? this[key] : [];
}
Map<String, dynamic> safeMap(String key){
return this.containsKey(key) ? this[key] : {};
}
}
Now this is how extension helps to avoid writing redundant code with safety. See below JSON parsing.
MDLCompany(
name: json.safeString(‘name’),
bs: json.safeString(‘bs’),
catchPhrase: json.safeString(‘catchPhrase’),
);
MDLGeo(
lat: json.safeString(‘lat’),
lng: json.safeString(‘lng’),
);
MDLAddress(
city: json.safeString(‘city’),
zipcode: json.safeString(‘zipcode’),
suite: json.safeString(‘suite’),
street: json.safeString(‘street’),
geo: MDLGeo.fromJSON(json.safeMap(‘geo’)),
);
MDLUser(
id: json.safeInt(‘id’),
name: json.safeString(‘name’),
username: json.safeString(‘username’),
email: json.safeString(’email’),
phone: json.safeString(‘phone’),
website: json.safeString(‘website’),
address: MDLAddress.fromJSON(json.safeMap(‘address’)),
company: MDLCompany.fromJSON(json.safeMap(‘company’)),
);
Hope this helps!
Regards,
Rashesh Bosamiya
rashesh.bosamiya@gmail.com