Encoding / decoding complex Json in Flutter
Solution 1
There's a very good article about how to parse complex JSON in Flutter. Here's a quick summary...
Simple Stuff:
{
"id":"487349",
"name":"Pooja Bhaumik",
"score" : 1000
}
becomes...
class Student{
String studentId;
String studentName;
int studentScores;
Student({
this.studentId,
this.studentName,
this.studentScores
});
factory Student.fromJson(Map<String, dynamic> parsedJson){
return Student(
studentId: parsedJson['id'],
studentName : parsedJson['name'],
studentScores : parsedJson ['score']
);
}
}
You create a new Student object like Student.fromJson(your_parsed_json)
.
Sub-objects work in a similar way. For each object inside the parent object you make a new Dart object, each with it's own parser for fromJson. Then inside the parent factory you call that fromJson method (like so)... This also works for lists of objects.
factory Student.fromJson(Map<String, dynamic> parsedJson){
return Student(
studentId: parsedJson['id'],
studentName : parsedJson['name'],
studentScores : Teacher.fromJson(parsedJson['teacher'])
);
Solution 2
There was no question in your question, but I assume the question is:
My Json code is not working - How do I efficiently parse and encode complex json objects in my flutter program.
For complex JSON you may want to consider using code generation to reduce the boiler plate you have to write. The flutter page has a good example using JsonSerializable. Here the basic instructions for your example:
- Add dependencies to
pubspec.yaml
and runflutter pub get
in the command line:
dependencies: json_annotation: ^1.2.0 dev_dependencies: build_runner: ^1.0.0 json_serializable: ^1.5.1
-
Create the basic Object model (similar to what you have done). Except for the following differences:
- You don't have a List of Status for the field statuss, but a single Status object.
- Don't use private fields.
-
To enable json boiler plate code generation do the following three steps:
- add the json-annotations to each class,
- add a factory .fromJson method on each class and
- add a .toJson method on each class:
@JsonSerializable() class Base { List<Device> devices; Base({this.devices}); factory Base.fromJson(Map<String, dynamic> json) => _$BaseFromJson(json); Map<String, dynamic> toJson() => _$BaseToJson(this); } @JsonSerializable() class Device { String device_desc,device_title,image_path; int status_id; List<function> functions; Status statuss ; Device(this.device_desc, this.device_title, this.image_path, this.status_id, this.functions, this.statuss); factory Device.fromJson(Map<String, dynamic> json) => _$DeviceFromJson(json); Map<String, dynamic> toJson() => _$DeviceToJson(this); } @JsonSerializable() class Status { String status_desc, status_title; Status(this.status_desc, this.status_title); factory Status.fromJson(Map<String, dynamic> json) => _$StatusFromJson(json); Map<String, dynamic> toJson() => _$StatusToJson(this); } @JsonSerializable() class function { String function_desc, function_title; int device_id, status; function(this.function_desc, this.function_title, this.device_id, this.status); factory function.fromJson(Map<String, dynamic> json) => _$functionFromJson(json); Map<String, dynamic> toJson() => _$functionToJson(this); }
- Run the command line to start code generation in the project root folder:
flutter packages pub run build_runner watch
- Now an additional source file appears with your generated boiler plate code. Add this file to your own source file using the
part
keyword, for example if your source file ismain.dart
add the following line:
part 'main.g.dart';
And you are done - This is all you need to test your encoding / decoding. For example with the following code:
import 'dart:convert';
void main() => (){
var jsonExample = '{"devices": [{"device_desc": "cooler", "device_title": "cooler", "functions": [{"device_id": 1, "function_desc": "pomp", "function_title": "pomp", "status": 1}, {"device_id": 1, "function_desc": "less", "function_title": "less", "status": 1}, {"device_id": 1, "function_desc": "up", "function_title": "up", "status": 1}], "image_path": "fdfdfsf", "status_id": 1, "statuss": {"status_desc": "device is on", "status_title": "on"}}, {"device_desc": "panke", "device_title": "panke", "functions": [{"device_id": 2, "function_desc": "less", "function_title": "pomp", "status": 2}, {"device_id": 2, "function_desc": "less", "function_title": "less", "status": 2}], "image_path": "vfx", "status_id": 2, "statuss": {"status_desc": "device is off", "status_title": "off"}}]}';
Map base_example = json.decode(jsonExample);
Base base_example_parsed = Base.fromJson(base_example);
var numberDevices = base_example_parsed.devices.length;
var numberFuncs = base_example_parsed.devices[0].functions.length;
print('$base_example_parsed has $numberDevices devices and the first device has $numberFuncs functions');
var base_example_encoded_again = json.encode(base_example_parsed);
print('$base_example_encoded_again');
};
For more information please refer to: 1. the official example. 2. this blog.
ElhamKeshavarz
Updated on December 07, 2022Comments
-
ElhamKeshavarz over 1 year
I am going to use a real json. First of all, I should run the project that is written in Flask, then use the local host to achieve data. Here is the real Json I`m using
{ "devices":[ { "device_desc":"cooler", "device_title":"cooler", "functions":[ { "device_id":1, "function_desc":"pomp", "function_title":"pomp", "status":1 }, { "device_id":1, "function_desc":"less", "function_title":"less", "status":1 }, { "device_id":1, "function_desc":"up", "function_title":"up", "status":1 } ], "image_path":"fdfdfsf", "status_id":1, "statuss":{ "status_desc":"device is on", "status_title":"on" } }, { "device_desc":"panke", "device_title":"panke", "functions":[ { "device_id":2, "function_desc":"less", "function_title":"pomp", "status":2 }, { "device_id":2, "function_desc":"less", "function_title":"less", "status":2 } ], "image_path":"vfx", "status_id":2, "statuss":{ "status_desc":"device is off", "status_title":"off" } } ] }
This is my code:
these are data models for defining json properties:
class Base{ //the type of our object is the array List<Device> _devices; Base(this._devices); List<Device> get devices => _devices; set devices(List<Device> value) { _devices = value; } } class Device { String _device_desc,_device_title,_image_path; int _status_id; List<function> _functions; List<Status> _statuss ; Device(this._device_desc, this._device_title, this._image_path, this._status_id, this._functions, this._statuss); List<Status> get statuss => _statuss; set statuss(List<Status> value) { _statuss = value; } List<function> get functions => _functions; set functions(List<function> value) { _functions = value; } int get status_id => _status_id; set status_id(int value) { _status_id = value; } get image_path => _image_path; set image_path(value) { _image_path = value; } get device_title => _device_title; set device_title(value) { _device_title = value; } String get device_desc => _device_desc; set device_desc(String value) { _device_desc = value; } } class Status { String _status_desc, _status_title; Status(this._status_desc, this._status_title); get status_title => _status_title; set status_title(value) { _status_title = value; } String get status_desc => _status_desc; set status_desc(String value) { _status_desc = value; }} class function { String _function_desc, _function_title; int _device_id, _status; function(this._function_desc, this._function_title, this._device_id, this._status); get status => _status; set status(value) { _status = value; } int get device_id => _device_id; set device_id(int value) { _device_id = value; } get function_title => _function_title; set function_title(value) { _function_title = value; } String get function_desc => _function_desc; set function_desc(String value) { _function_desc = value; }}
and this is the stateful class :
class MyHomePage extends StatefulWidget { var title; MyHomePage({Key key, this.title}) : super(key: key); @override _MyHomePageState createState() => new _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { Future<Base> _getBase() async { var data = await http.get(Uri.encodeFull("http://192.168.1.111:5000/mobile-home")); var jsonData = json.decode(data.body); Base base = Base(jsonData); return Base(jsonData[0]); } @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text(widget.title), ), body: Container( child: FutureBuilder( future: _getBase(), builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.data == null) { return Container( child: Center( child: Text("Loading..."), ), ); } else { return ListView.builder( itemCount: snapshot.data.devices.length, itemBuilder: (BuildContext context, int index) { snapshot.data.devices.map<Widget>((devices){ return ListTile( subtitle: Text(devices[index].device_desc.toString()), title: Text(devices[index].device_title), /*leading: CircleAvatar( // ignore: argument_type_not_assignable backgroundImage: NetworkImage(snapshot.data[index].thumbnailUrl), )*/ ); } ); }, ); } }, ), ), ); } }
I got an error when while debugging:
"type 'List<dynamic>' is not a subtype of type 'List<Device>'"
I can not get the data from json.
-
ElhamKeshavarz over 5 yearsI added the error that it displays,"type 'List' is not a subtype of type 'List'"
-
ElhamKeshavarz over 5 yearsActually, I am a beginner, and I want to write the code my self just for practice, but honestly saying that your answer was awesome, cause it is clarified and is just as simple as it can be!tnx for your great answer, and I`ll use this method for sure
-
ElhamKeshavarz over 5 yearsThank you so much;) The article exactly mentioned my error, so I could solve my problem