Sunday, December 16, 2018

Flutter - Auto JSON serialization and deserialization like GSON.

To make a big application, we always need some rest API's to provide a dynamic feature to the user. These API often supports JSON as a data format for communication through HTTP protocol.

flutter-json-serialization-deerialization
If you have worked with native Android application development. You definitely missing those POJO classes in Flutter. After hit a request to the server, the server will return a JSON string. If you want to use data flexibly, we need to convert the JSON string into an object.



Are you going to use GSON equivalent libs in Flutter?
No, GSON and other equivalent libs used runtime reflection, which is disabled in Flutter. The runtime reflection interferes with tree shaking, which Dart has supported for quite a long time. Tree shaking helps us to remove unused code from your release builds. This optimizes the app’s size significantly. The reflection makes all code implicitly used by default, it makes tree shaking difficult. The tools cannot know what parts are unused at runtime, so the redundant code is hard to strip away. App sizes cannot be easily optimized when using reflection.

So, Flutter only provides map for parse a JSON with the help of dart:convert. It is a handwritten deserialization that is extremely unstable in large projects. The manual parsing method, we have discussed in our Parse local JSON file in Flutter post. This is the most basic parsing method and it is only recommended if you are starting with Flutter or you’re building a small project. But if you need to work with a larger project. You must use the following method that we going to explain here.

In this post, we will use json_annotation, build_runner, and json_serializable  to parse a big JSON in the Flutter Application that is recommended method by Flutter Team. For the example simplicity, we'll use the following json because we not getting data from the API.   
json sample
{ "page": 1, "per_page": 3, "total": 12, "total_pages": 4, "author":{ "first_name": "Libs", "last_name":"Developer" }, "data": [ { "id": 1, "first_name": "Contact", "last_name": "contact@developerlibs.com", "avatar": "http://www.simonowens.net/wp-content/uploads/2014/12/books-2.png", "images": [ { "id" : 122, "imageName": "377cjsahdh388.jpeg" }, { "id" : 152, "imageName": "1743fsahdh388.jpeg" } ] }] }


Let's create a flutter sample app for parse above JSON file and use above libs.

Creating a new Project
1. Create a new project from File ⇒ New Flutter Project with your development IDE.
2. Create a assets folder in the root directory of the project and put book.json in the assets folder and paste above json in this file. To access this file in the Flutter application, we have to declare it in the pubspec.yaml file. As we have done:


Auto JSON serialization and deserialization like GSON flutter libs.

 3. Now we need to write all dart entity classes manually based on above JSON data. As you can see below, we have written @JsonSerializable() in PODO class. It'll help us to auto-generate serialize code in the Flutter. All the members of a class is the same as written in the JSON raw data file. You can change it according to your requirement but that case, you have to use @JsonKey annotation with each member like if I'll change per_page to perPage then I'll write it such as: 

@JsonKey(name: 'per_page ') 
 int perPage;

But in this post, i'm not doing it as you can see below written enties book.dart, author.dart and user.dart files.
book.dart
import 'package:flutter_load_local_json/author.dart'; import 'package:flutter_load_local_json/user.dart'; import 'package:json_annotation/json_annotation.dart'; part 'book.g.dart';// This line is auto added by following script. @JsonSerializable() class Book { int page; int per_page; int total; int total_pages; Author author; List<User> data; Book( {this.page, this.per_page, this.total, this.total_pages, this.author, this.data }); factory Book.fromJson(Map<String, dynamic> json) => _$BookFromJson(json); // This line is auto added by following script. }


author.dart
import 'package:json_annotation/json_annotation.dart'; part 'author.g.dart';// This line is auto added by following script. @JsonSerializable() class Author extends Object { String first_name; String last_name; Author( { this.first_name, this.last_name, });
// This line is auto added by following script. factory Author.fromJson(Map<String, dynamic> json) => _$AuthorFromJson(json); }

user.dart
import 'package:json_annotation/json_annotation.dart'; part 'user.g.dart';// This line is auto added by following script. @JsonSerializable() class User extends Object { int id; String first_name; String last_name; String avatar; User( {this.id, this.first_name, this.last_name, this.avatar, }); factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json); // This line is auto added by following script. }




4. To generate PODO for the above enties, we have to run the command.

flutter packages pub run build_runner build

as i have run it in the Android Studio terminal.



the above script will generate book.g.dart, author.g.dart, and user.g.dart.  It'll auto map with your manual written classes. If it shows any error. Then you have to add part 'book.g.dart'; in book.dart and part of 'book.dart'; in book.g.dart files respectively to resolve error.
book.g.dart
// GENERATED CODE - DO NOT MODIFY BY HAND part of 'book.dart'; // ************************************************************************** // JsonSerializableGenerator // ************************************************************************** Book _$BookFromJson(Map<String, dynamic> json) { return Book( page: json['page'] as int, per_page: json['per_page'] as int, total: json['total'] as int, total_pages: json['total_pages'] as int, author: json['author'] == null ? null : Author.fromJson(json['author'] as Map<String, dynamic>), data: (json['data'] as List) ?.map((e) => e == null ? null : User.fromJson(e as Map<String, dynamic>)) ?.toList()); } Map<String, dynamic> _$BookToJson(Book instance) => <String, dynamic>{ 'page': instance.page, 'per_page': instance.per_page, 'total': instance.total, 'total_pages': instance.total_pages, 'author': instance.author, 'data': instance.data };

author.g.dart
// GENERATED CODE - DO NOT MODIFY BY HAND part of 'author.dart'; // ************************************************************************** // JsonSerializableGenerator // ************************************************************************** Author _$AuthorFromJson(Map<String, dynamic> json) { return Author( first_name: json['first_name'] as String, last_name: json['last_name'] as String); } Map<String, dynamic> _$AuthorToJson(Author instance) => <String, dynamic>{ 'first_name': instance.first_name, 'last_name': instance.last_name };

user.g.dart
// GENERATED CODE - DO NOT MODIFY BY HAND part of 'user.dart'; // ************************************************************************** // JsonSerializableGenerator // ************************************************************************** User _$UserFromJson(Map<String, dynamic> json) { return User( id: json['id'] as int, first_name: json['first_name'] as String, last_name: json['last_name'] as String, avatar: json['avatar'] as String); } Map<String, dynamic> _$UserToJson(User instance) => <String, dynamic>{ 'id': instance.id, 'first_name': instance.first_name, 'last_name': instance.last_name, 'avatar': instance.avatar }; }
as you can see above. Our PODO is ready for parse a json string.



5. Now, let's see how to start parse json and create a object. To create a object of json, we getting json string from local asset folder of the project.
getting json from asset
DefaultAssetBundle.of(context) .loadString('assets/book.json'),

the above snippet return a json string. To parse it we have to call root entity method.

creating object from json string
Book parseJson(String response) { if (response == null) { return null; } final parsed = new JsonDecoder().convert(response.toString()); return Book.fromJson(parsed); }

here, we have final code snippet of this project. Where we have used some Flutter widgets to display content of json.

main.dart
import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_load_local_json/book.dart'; void main() { runApp(new MaterialApp( debugShowCheckedModeBanner: false, theme: new ThemeData( primaryColor: const Color(0xFF02BB9F), primaryColorDark: const Color(0xFF167F67), accentColor: const Color(0xFF167F67), ), home: new MyApp(), )); } class MyApp extends StatefulWidget { @override MyAppState createState() => new MyAppState(); } class MyAppState extends State<MyApp> { List data; @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text( "Auto local JSON parse", style: new TextStyle(color: Colors.white), ), ), body: new Container( child: new Center( child: new FutureBuilder( future: DefaultAssetBundle.of(context) .loadString('assets/book.json'), builder: (context, snapshot) { var book = parseJson(snapshot.data); return book == null ? new Text("Pasing json") : new Stack( children: <Widget>[ bookCoverWidget( book.data[0].last_name, EdgeInsets.all(20.0), const Color(0xFFA11B00), const Color(0xFFFFFFFF)), new Column( children: <Widget>[ new Center( child: new Container( margin: EdgeInsets.only(top: 80.0,bottom: 70.0), height: 160.0, width: 160.0, decoration: new BoxDecoration( color: const Color(0xff7c94b6), image: new DecorationImage( image: new NetworkImage( book.data[0].avatar), fit: BoxFit.cover, ), border: Border.all( color: Colors.white, width: 5.0), borderRadius: new BorderRadius.all( const Radius.circular(80.0)), ), ), ), Text( book.author.last_name+" "+ book.author.first_name, style: new TextStyle( color: Colors.white, fontSize: 20.0, fontWeight: FontWeight.bold), ), ], ), ], ); }), ), )); } Widget bookCoverWidget( String buttonLabel, EdgeInsets margin, Color bgColor, Color textColor) { var loginBtn = new Container( margin: margin, padding: EdgeInsets.all(15.0), alignment: FractionalOffset.center, decoration: new BoxDecoration( color: bgColor, borderRadius: new BorderRadius.all(const Radius.circular(10.0)), boxShadow: <BoxShadow>[ BoxShadow( color: const Color(0xFF696969), offset: Offset(1.0, 6.0), blurRadius: 0.001, ), ], ), child: Text( buttonLabel, style: new TextStyle( color: textColor, fontSize: 20.0, fontWeight: FontWeight.bold), ), ); return loginBtn; } Book parseJson(String response) { if (response == null) { return null; } final parsed = new JsonDecoder().convert(response.toString()); return Book.fromJson(parsed); } }x


Now run the above snippet and you will the following output of project and it's showing above json file data.

Auto JSON serialization and deserialization like GSON.

If you have followed the article carefully, you can see the app running very smoothly as shown in the above. But if you are facing any problem or you have any quires, please feel free to ask it from below comment section and you can ask it on SLACK

Share:

Get it on Google Play

React Native - Start Development with Typescript

React Native is a popular framework for building mobile apps for both Android and iOS. It allows developers to write JavaScript code that ca...