JSON serialization and deserialization to objects in Flutter
I was hoping for more details from the answers provided. Even though they were good suggestions, they were too general for me to understand. So after doing my own research, I'll share my implementation to the above JSON example I provided in hope that it would save someone's else's time. So here are the steps I followed:
- In my Flutter project, first I imported the following libraries:
dependencies:
built_value: ^1.0.1
built_collection: ^1.0.0dev_dependencies:
build_runner: ^0.3.0
built_value_generator:^1.0.1
- I created a folder called tool. In it, I put 2 files: build.dart and watch.dart. There implementations of those files are show below
build.dart
// Copyright (c) 2015, Google Inc. Please see the AUTHORS file for details.// All rights reserved. Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.import 'dart:async';import 'package:build_runner/build_runner.dart';import 'package:built_value_generator/built_value_generator.dart';import 'package:source_gen/source_gen.dart';/// Example of how to use source_gen with [BuiltValueGenerator].////// Import the generators you want and pass them to [build] as shown,/// specifying which files in which packages you want to run against.Future main(List<String> args) async { await build( new PhaseGroup.singleAction( new GeneratorBuilder([new BuiltValueGenerator()]), new InputSet('built_value_example', const [ 'lib/model/*.dart', 'lib/*.dart', ])), deleteFilesByDefault: true);}
watch.dart
// Copyright (c) 2016, Google Inc. Please see the AUTHORS file for details.// All rights reserved. Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.import 'dart:async';import 'package:build_runner/build_runner.dart';import 'package:built_value_generator/built_value_generator.dart';import 'package:source_gen/source_gen.dart';/// Example of how to use source_gen with [BuiltValueGenerator].////// This script runs a watcher that continuously rebuilds generated source.////// Import the generators you want and pass them to [watch] as shown,/// specifying which files in which packages you want to run against.Future main(List<String> args) async { watch( new PhaseGroup.singleAction( new GeneratorBuilder([new BuiltValueGenerator()]), new InputSet('built_value_example', const [ 'lib/model/*.dart', 'lib/*.dart'])), deleteFilesByDefault: true);}
- I created a serializers.dart file that would serialize my json string to my custom dart object, and my model object person.dart
serializers.dart
library serializers;import 'package:built_collection/built_collection.dart';import 'package:built_value/serializer.dart';import 'package:built_value/standard_json_plugin.dart';import 'model/person.dart';part 'serializers.g.dart';Serializers serializers = ( _$serializers.toBuilder()..addPlugin(new StandardJsonPlugin())).build();
person.dart
library person;import 'package:built_collection/built_collection.dart';import 'package:built_value/built_value.dart';import 'package:built_value/serializer.dart';part 'person.g.dart';abstract class Person implements Built<Person, PersonBuilder> { String get name; int get age; BuiltList<Car> get cars; Person._(); factory Person([updates(PersonBuilder b)]) = _$Person; static Serializer<Person> get serializer => _$personSerializer;}abstract class Car implements Built<Car, CarBuilder> { String get name; BuiltList<String> get models; Car._(); factory Car([updates(CarBuilder b)]) = _$Car; static Serializer<Car> get serializer => _$carSerializer;}
After creating the 4 files above, it will show some compiler errors. Don't mind them yet. This is because the build.dart file hasn't been run yet. So in this step, run build.dart. If you're using Webstorm, simply right click on build.dart and hit "Run build.dart". This will create 2 files: "person.g.dart" and "serializers.g.dart". If you notice carefully, in our build.dart file, we put 'lib/model/.dart' and 'lib/.dart'. The build knows where to look for those files by going through the paths specified and looks for files which have part "something" included. So it's important to keep that line in those files before running the build.dart file
Finally, now I can use the serializer in my main.dart file to serialize the json string to my custom dart object class Person. In my main.dart, I added the following code in initState()
main.dart
Person _person; @override void initState() { super.initState(); String json = "{" "\"name\":\"John\",\"age\":30,\"cars\": " "[" "{ \"name\":\"Ford\", \"models\":[ \"Fiesta\", \"Focus\", \"Mustang\" ] }," "{ \"name\":\"BMW\", \"models\":[ \"320\", \"X3\", \"X5\" ] }," "{ \"name\":\"Fiat\", \"models\":[ \"500\", \"Panda\" ] }" "]}"; setState(() { _person = serializers.deserializeWith( Person.serializer, JSON.decode(json)); }); }
My sample project is also available on Github Built value sample project
json_serialization
This package by the Dart Team generates everything needed for the fromJson
constructor and toJson
method in a seprate file.
Dependencies
Add the following dependencies:
dependencies: json_annotation: ^2.0.0dev_dependencies: build_runner: ^1.0.0 json_serializable: ^2.0.0
Model class
Adapt your model class to have the following parts:
import 'package:json_annotation/json_annotation.dart';// will be generated laterpart 'person.g.dart';@JsonSerializable()class Person { Person(this.name, this.age); final String name; final int age; factory Person.fromJson(Map<String, dynamic> json) => _$PersonFromJson(json); Map<String, dynamic> toJson() => _$PersonToJson(this);}
Generate code
Generate the person.g.dart file from the terminal:
flutter packages pub run build_runner build
Use it
Then use it like this:
JSON → object
String rawJson = '{"name":"Mary","age":30}';Map<String, dynamic> map = jsonDecode(rawJson);Person person = Person.fromJson(map);
Object → JSON
Person person = Person('Mary', 30);Map<String, dynamic> map = person.toJson();String rawJson = jsonEncode(map);
Notes
- In a Dart project use
pub run build_runner build
. - See this answer for more ways to serialize JSON.
From the Dart web site:
The dart:convert library provides a JsonCodec class, which you can use to convert simple types (map, list, int, num, string) automatically from a and to a JSON string. The two key static methods are
JSON.encode(object)
andJSON.decode(string)
.
Decoding example:
import 'dart:convert';... Map<String, dynamic> parsedMap = JSON.decode(json);print(parsedMap['name']); // Johnprint(parsedMap['age']); // 30
Encoding example:
Map<String, dynamic> mapData = <String, dynamic>{ 'hello': 'world!' };String jsonData = JSON.encode(mapData); // convert map to String
If you want to have your JSON inflate into custom Dart classes instead of a tree of primitive objects, Hadrien's answer should point you in the right direction, but I just wanted to leave this here in case anyone else is trying to get basic JSON serialization/deserialization working.