I am new to flutter and the objective is to serialise complex JSON objects which contain other smaller objects.
Using the json_serializable: ^2.0.0 and the pubspec.yaml file looks something like this.
dependencies:
intl: ^0.15.7
json_annotation: ^2.0.0
built_value: ^6.7.1
flutter:
sdk: flutter
dev_dependencies:
build_runner: ^1.0.0
json_serializable: ^2.0.0
built_value_generator: ^6.7.1
flutter_test:
sdk: flutter
The user.dart look like this
import 'package:json_annotation/json_annotation.dart';
part 'user.g.dart';
#JsonSerializable(nullable: false)
class User {
final String firstName;
final String lastName;
final DateTime dateOfBirth;
User({this.firstName, this.lastName, this.dateOfBirth});
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
}
I have tried flutter pub run build_runner build yet the file user.g.dart doesn't get created and I am facing issue with that.
I also have added the build.yaml file with following code
targets:
$default:
builders:
built_value_generator|built_value:
generate_for:
- model/*.dart
json_serializable|json_serializable:
generate_for:
- model/*.dart
Can anyone let me know what I am missing here. Thanks
Checklist
pubspec.yaml includes 3 packages:
dependencies:
json_annotation: ^4.6.0
dev_dependencies:
build_runner: ^2.2.0
json_serializable: ^6.3.1
you've run flutter pub get
if you get errors about versions at this stage, try older versions of json_serializable, json_annotation, build_runner
note the Min Dart SDK listed on those linked pages
your pubspec.yaml environment: sdk: ">=a.bb.c < x.y.z", your a.bb.c version must be >= than the Min Dart SDK listed on pub.dev for the package
if your Dart version is lower, try an older version of the json package which matches or is below your Dart version
your class files are underneath /lib or /bin
can be subdirectories under those
json_serializable won't search every directory for files to generate.
added import for json_annotation:
import 'package:json_annotation/json_annotation.dart';
you've added a part directive after import statements
your part file is named after your class filename (not the Class name itself), with a g added
e.g. for CacheItem class with ...
cache-item.dart class filename ...
part 'cache-item.g.dart'; gets corresponding part directive.
the part directive is not named after your actual Class, but the class file name.
if Android Studio Dart Analyzer continues to give errors about missing .g.dart file after you're sure the file has been generated & named correctly, shut down Android Studio and restart it (analyzer sometimes goes insane & this may fix it. Restarting just the analyzer may not work.)
you've added #JsonSerializable() above the class name
you've created a default constructor for your class.
It can be empty, have optional named parameters, or positional parameters.
As long as your class fields are accessible (either through constructor or public setters & getters), json_serializable can handle it. (i.e. don't have only _private properties and an empty constructor)
you've written two methods calling private stub methods:
a factory fromJson method
e.g.: factory CacheItem.fromJson(Map<String,dynamic> json) => _CacheItemFromJson(json)
a toJson method
e.g.: Map<String,dynamic> toJson() => _$CacheItemToJson(this)
stub methods are private (start with _ underscore)
$tub methods have the $
stub methods have proper CaSe (i.e. Pascal Case)
stub factory supplies (Map<String,dynamic> json) as argument
stub toJson() returns Map<String,dynamic>
When all that is complete, try running the generator from the command line or terminal in the project root directory...
In Flutter:
flutter pub run build_runner build
In pure Dart, this depends on your version, but one of these should work:
dart run build_runner build
pub run build_runner build
dart pub run build_runner build
If all goes well, click around in your project file explorer or Reload from disk and new files should show up such as cache-item.g.dart for the above example.
Common Errors
Bad state: Unexpected diagnostics:
Seeing this output when running the build_runner is likely a problem with flutter and json_annotation depending on an incompatible version of analyzer. This happens with json_serializable versions prior to 3.5 requiring a dependency_override of analyzer to 0.39.14 or 0.39.17.
Your first move should be to try the latest version of json_serilizable from pub.dev which apparently doesn't have this dependency problem.
If you can't upgrade json_serializable you can try placing the override lines underneath dev_dependences:
dev_dependencies:
build_runner: ^1.9.0
flutter_test:
sdk: flutter
json_serializable: 3.3.0
test: ^1.14.3
dependency_overrides:
analyzer: '0.39.14'
[SEVERE] Nothing can be built, yet a build was requested.
This error can happen when performing a flutter pub run build_runner build when we've added dependency in pubspec.yaml for json_annotation but are missing a dependency/dev_dependency for json_serializable:
dependencies:
flutter:
sdk: flutter
get:
json_annotation: ^4.3.0
some_other_packages:
Make sure you've got json_serializable package added as either a dependency or dev_dependency:
dependencies:
flutter:
sdk: flutter
get:
json_annotation: ^4.3.0
dev_dependencies:
build_runner: ^2.1.4
flutter_test:
sdk: flutter
json_serializable: ^6.0.1 #// ← do not forget
test:
Could not generate fromJsoncode forsomeField.
If you're json serializing a class that contains someField which is a Type for another custom class you've created, have you #JsonSerializable() that other custom class?
#JsonSerializable(explicitToJson: true)
class BuildingStatus {
final Building building; // another custom class
BuildingStatus(Building building);
factory BuildingStatus.fromJson(Map<String,dynamic> json) => _$BuildingStatusFromJson(json);
Map<String,dynamic> toJson() => _$BuildingStatusToJson(this);
}
/// This guy needs serialization too.
#JsonSerializable()
class Building {
final String name;
const Building(this.name);
factory Building.fromJson(Map<String,dynamic> json) => _$BuildingFromJson(json);
Map<String,dynamic> toJson() => _$BuildingToJson(this);
}
Without serializing the nested Building class, we'd see an error when running build_runner like:
Could not generate `fromJson` code for `building` because of type `Building`.
Instance of 'SomeNestedClass'
If we have nested serializable classes, we generally want the serialization to occur recursively. i.e. the nested classes are also serialized.
To do that we would annotate our containing class with explicitToJson: true like:
#JsonSerializable(explicitToJson: true)
So when we toJson() our BuildingStatus instance, instead of getting:
{"building": Instance of 'Building'}
... we would get:
{"building": {"name": "Empire State"}}
Notes
Subclass / Parent Class
If your class is a child of a parent class and you want to Serialize fields/properties of child only, you can annotate only the subclass. The parent class fields will automatically be found and included in the generated class files for the subclass.
If you want to be able to serialize/deserialize both parent and child separately, go ahead and annotate the base / parent classes with #JsonSerializable as well.
e.g. filename account.dart
import 'package:json_annotation/json_annotation.dart';
part 'account.g.dart';
class AccountBase {
int? created;
String? username;
String? password;
}
#JsonSerializable()
class Account extends AccountBase {
int? id;
Account();
factory Account.fromJson(Map<String,dynamic> json) => _$AccountFromJson(json);
Map<String,dynamic> toJson() => _$AccountToJson(this);
}
Produces:
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'account.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Account _$AccountFromJson(Map<String, dynamic> json) => Account()
..created = json['created'] as int?
..username = json['username'] as String?
..password = json['password'] as String?
..id = json['id'] as int?;
Map<String, dynamic> _$AccountToJson(Account instance) => <String, dynamic>{
'created': instance.created,
'username': instance.username,
'password': instance.password,
'id': instance.id,
};
Reference & Docs
Example project on github, relevant files under /bin/ and packages in pubspec.yaml
Flutter & Json
Json_Serializable package
Example from the package authors
Example
import 'package:json_annotation/json_annotation.dart';
part 'cache-item.g.dart';
#JsonSerializable()
class CacheItem {
int created;
String keywords;
String response;
CacheItem(this.created, this.keywords, this.response);
factory CacheItem.fromJson(Map<String,dynamic> json) => _$CacheItemFromJson(json);
Map<String,dynamic> toJson() => _$CacheItemToJson(this);
}
Output
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'cache-item.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
CacheItem _$CacheItemFromJson(Map<String, dynamic> json) => CacheItem(
json['created'] as int,
json['keywords'] as String,
json['response'] as String,
);
Map<String, dynamic> _$CacheItemToJson(CacheItem instance) => <String, dynamic>{
'created': instance.created,
'keywords': instance.keywords,
'response': instance.response,
};
Example Constructor Variant
This example is the same as above except the constructor is missing some fields and has response as optional.
It's fine.
The generator will just use the public (implicit) setters after instantiating the object to assign the values.
import 'package:json_annotation/json_annotation.dart';
part 'cache-item.g.dart';
#JsonSerializable()
class CacheItem {
int? created;
String? keywords;
String? response;
CacheItem({this.response});
factory CacheItem.fromJson(Map<String,dynamic> json) => _$CacheItemFromJson(json);
Map<String,dynamic> toJson() => _$CacheItemToJson(this);
}
Output
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'cache-item.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
CacheItem _$CacheItemFromJson(Map<String, dynamic> json) => CacheItem(
response: json['response'] as String?,
)
..created = json['created'] as int?
..keywords = json['keywords'] as String?;
Map<String, dynamic> _$CacheItemToJson(CacheItem instance) => <String, dynamic>{
'created': instance.created,
'keywords': instance.keywords,
'response': instance.response,
};
The constructor's argument shouldn't be optional
User({this.firstName, this.lastName, this.dateOfBirth});
They should be obligatory:
User(this.firstName, this.lastName, this.dateOfBirth);
And the part
'user.g.dart';
should be matching the Uppercase User class:
part 'User.g.dart';
try running
>flutter packages pub run build_runner build --delete-conflicting-outputs
The file name, class, and part 'Book.g.dart'; should all match.
I got to know that , file has to be saved before running the command
flutter packages pub run build_runner build
I know answer seems so simple.
I had this following error
error
[WARNING] json_serializable:json_serializable on lib/day10/models/sentence.dart:
Missing "part 'sentence.g.dart';".
I noticed that I had to change on the model file this part 'Sentence.g.dart'; to this part 'sentence.g.dart'; in other words, I had to lowercase it.
in my case I had to add hive_generator package, along with the build_runner package.
If your model class name is for example transaction.dart, then it would be like this-
part 'transaction.g.dart'
Run the command-
flutter packages pub run build_runner build it will generate the transaction model for you.
adding
json_serializable: ^6.0.0
to dev_dependencies solve it for me
If you are trying to generate .g files by just running the command flutter packages pub run build_runner build and you are not getting your desired result then just try:
flutter clean
'flutter pub cache repair'
flutter packages pub run build_runner build --delete-conflicting-outputs
delete all the gradle temp files
then run flutter pub get
flutter packages pub run build_runner build.
Some times we forgot to save the files, please save the file then run the command.
flutter packages pub run build_runner build
Step 1: Add "part 'your_class_name.g.dart'"
Step 2: Run this command on Terminal: flutter pub run build_runner build
Example:
part 'student.g.dart'
#JsonSerializable()
class Student{
....
}
Do not forget to save the file that has the class even if it still has errors
Then run the command on any terminal (o integrated terminal)...
But for Dart:
pub run build_runner build
And for Flutter:
flutter pub run build_runner build
Official reference link:
https://pub.dev/packages/json_serializable
https://github.com/google/json_serializable.dart/tree/master/example
Related
I am using freezed with json generator. i am facing this error on generating the code.
Could not generate fromJson code for images because of type
Asset.
The Code Is:
abstract class ProductDTO with _$ProductDTO {
factory ProductDTO({
required String description,
required List<Asset> images, // error
}) = _ProductDTO;
// from Json
factory ProductDTO.fromJson(Map<String, dynamic> json) =>
_$ProductDTOFromJson(json);
}
Custom data types require specific converters to handle their toJson and fromJson implementations. This question and its answers provide some nice examples.
I have this json that I am fetching from firebase realtime database
[{image: https://cdn.searchenginejournal.com/wp-content/uploads/2019/07/the-essential-guide-to-using-images-legally-online-1520x800.png, shopId: 1},
{image: https://cdn.searchenginejournal.com/wp-content/uploads/2019/07/the-essential-guide-to-using-images-legally-online-1520x800.png, shopId: 2}]
When I try to parse it, I am receiving different errors:
First way:
List<HomeSlider> posts = List<HomeSlider>.from(l.map((model)=> HomeSlider.fromJson(model)));
imgList.addAll(List<HomeSlider>.from(data.value) => HomeSlider.HomeSlider.fromJson(json));
Second way:
Map<dynamic, dynamic> yearMap = data.value;
yearMap.forEach((key, value) {
imgList.add(HomeSlider.fromJson(value));
});
I have the home slider object:
import 'package:json_annotation/json_annotation.dart';
/// This allows the `User` class to access private members in
/// the generated file. The value for this is *.g.dart, where
/// the star denotes the source file name.
part 'HomeSlider.g.dart';
#JsonSerializable()
class HomeSlider{
HomeSlider(this.image, this.shopId);
String image="";
String shopId="";
/// A necessary factory constructor for creating a new User instance
/// from a map. Pass the map to the generated `_$UserFromJson()` constructor.
/// The constructor is named after the source class, in this case, User.
factory HomeSlider.fromJson(Map<String, dynamic> json) => _$HomeSliderFromJson(json);
/// `toJson` is the convention for a class to declare support for serialization
/// to JSON. The implementation simply calls the private, generated
/// helper method `_$UserToJson`.
Map<String, dynamic> toJson() => _$HomeSliderToJson(this);
}
Errors are:
'String' is not a subtype of type 'Map<String, dynamic>'
or w json exception if I try to encode it
I have generated the class as by the documentation using flutter CLI
Any suggestions? Thanks
First: Your Json looks wrong, it's missing the double quotes:
[{"image": "https://cdn.searchenginejournal.com/wp-content/uploads/2019/07/the-essential-guide-to-using-images-legally-online-1520x800.png", "shopId": 1},
{"image": "https://cdn.searchenginejournal.com/wp-content/uploads/2019/07/the-essential-guide-to-using-images-legally-online-1520x800.png", "shopId": 2}]
Second:
In your Model shopId probably should be an int.
If string is intended, you also need to put the id in double quotes.
Then use jsonDecode to convert the String into a List<Map<String,dynamic>> and convert it like that:
List<HomeSlider> homeSliderList;
homeSliderList= (jsonDecode(yourJsonString) as List)
.map((i) => HomeSlider.fromJson(i))
.toList();
I am trying to convert the object "Week" to json.
https://flutter.dev/docs/development/data-and-backend/json this is the source that i used
class Week{
DateTime _startDate;
DateTime _endDate;
List<Goal> _goalList;
String _improvement;
Week(this._startDate, this._endDate){
this._goalList = List<Goal>();
this._improvement = "";
}
Week.fromJson(Map<String, dynamic> json)
: _startDate = json['startDate'],
_endDate = json['endDate'],
_goalList = json['goalList'],
_improvement = json['improvement'];
Map<String, dynamic> toJson() =>
{
'startDate': _startDate,
'endDate': _endDate,
'goalList': _goalList,
'improvement': _improvement,
};
}
I used this:
DateTime startDate = currentDate.subtract(new Duration(days:(weekday-1)));
DateTime endDate = currentDate.add(new Duration(days:(7-weekday)));
Week week = new Week(startDate, endDate);
var json = jsonEncode(week);
But the problem is that I get this result:
Unhandled Exception: Converting object to an encodable object failed: Instance of 'Week'
#0 _JsonStringifier.writeObject (dart:convert/json.dart:647:7)
#1 _JsonStringStringifier.printOn (dart:convert/json.dart:834:17)
#2 _JsonStringStringifier.stringify (dart:convert/json.dart:819:5)
#3 JsonEncoder.convert (dart:convert/json.dart:255:30)
#4 JsonCodec.encode (dart:convert/json.dart:166:45)
#5 jsonEncode (dart:convert/json.dart:80:10)
jsonEncode requires a Map<String, dynamic>, not a Week object. Calling your toJson() method should do the trick.
var json = jsonEncode(week.toJson());
However, keep in mind that your toJson() method is also incorrect, as things like _goalList and the dates are still objects, not Maps or Lists. You'll need to implement toJson methods on those as well.
To answer your specific questions:
Because dart is not javascript / typescript. Dart checks types at runtime, therefore you have to explicitly tell it how to convert things - also there is no reflection in dart, so it can't figure it out by itself.
You can use a library that uses code generation to do these things automatically for you - it still wont be possible at runtime though - read more about JSON serialization.
The easiest way would be to implement the methods directly in the classes, as that's where you have access to in your root object. Keep in mind that the structure that jsonEncode needs is a Map<String, dynamic>, but the dynamic part really means List<dynamic>, Map<String, dynamic> or a primitive that is json compatible such as String or double - if you try to imagine how such a nested structure of said types looks, you'll realise that it's basically json. So when you do something like 'goalList': _goalList, you're giving it an object, which is not one of the allowed types.
Hope this clears things up a bit.
for anyone wondering: I got my solution.
To make my code work I needed to implement the toJson() methods at my class Goal as well (because I used List<Goal> in Week).
class Goal{
String _text;
bool _reached;
Map<String, dynamic> toJson() =>
{
'text': _text,
'reached': _reached,
};
}
Also, I needed to add .toIso8601String() to the DateTime objects like that in the Week class:
Map<String, dynamic> toJson() =>
{
'startDate': _startDate.toIso8601String(),
'endDate': _endDate.toIso8601String(),
'goalList': _goalList,
'improvement': _improvement,
};
Now the output is:
{"startDate":"2019-05-27T00:00:00.000Z","endDate":"2019-06-02T00:00:00.000Z","goalList":[],"improvement":""}
Taking suggestion 2 from #Phillip's answer above for Json serialization, the Freezed package I believe you can skip the #JsonSerializable annotation and just used the #Freezed annotation because Freezed "will automatically ask json_serializable to generate all the necessary fromJson/toJson."
So the example here: https://flutter.dev/docs/development/data-and-backend/json#use-code-generation-for-medium-to-large-projects
becomes:
//import 'package:json_annotation/json_annotation.dart';
import 'freezed_annotation/freezed_annotation.dart';
/// This allows the `User` class to access private members in
/// the generated file. The value for this is *.g.dart, where
/// the star denotes the source file name.
part 'user.g.dart';
part 'user.freezed.dart';
/// An annotation for the code generator to know that this class needs the
/// JSON serialization logic to be generated.
#freezed
class User {
User(this.name, this.email);
String name;
String email;
/// A necessary factory constructor for creating a new User instance
/// from a map. Pass the map to the generated `_$UserFromJson()` constructor.
/// The constructor is named after the source class, in this case, User.
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
/// `toJson` is the convention for a class to declare support for serialization
/// to JSON. The implementation simply calls the private, generated
/// helper method `_$UserToJson`.
Map<String, dynamic> toJson() => _$UserToJson(this);
}
Freezed: https://pub.dev/packages/freezed
Don't forget to edit pubspec.yaml for freezed and freezed_annotation
I'm trying to use json_serializable with my class "Trail".
class Trail {
String trailId;
String trailName;
String userID;
String userName;
double distanceInMeters;
String description;
int dificulty;
int likes;
List<Marker> markers;
List<Vote> votesList;
List<String> likedBy;
}
When I try the command:
flutter pub run build_runner build
I get this error:
Error running JsonSerializableGenerator
Could not generate `fromJson` code for `markers` because of type `Marker`.
None of the provided `TypeHelper` instances support the defined type.
How the hell do I make Markers serializable?
easy my friend,
install a plugin json2dart if you are using android studio ,
,
now right click on lib folder -> new->json to dart (the last option)
.
paste your Jason object,
a sample of your json object to it,
give it a name and click generate,
a correctly mapped dart file will be created,
,
make a constructor for that class , and pass your values to it,
.
sending any data to the server as json ....
use json.encode(object)
.
'object' it is the object of that generated class
When it comes to JSON encoding in Dart, per Seth Ladd's accouncement the finally approved now official way to go is dart:convert + JSON.Encode.
Let's say we have a bunch of model classes (PODOs) such as:
class Customer
{
int Id;
String Name;
}
Now, I'd love to be able to just JSON-encode my domain objects like this:
var customer = new Customer()
..Id = 17
..Name = "John";
var json = JSON.encode(customer);
Unfortunately, this won't work...
Uncaught Error: Converting object to an encodable object failed.
Stack Trace:
#0 _JsonStringifier.stringifyValue (dart:convert/json.dart:416)
#1 _JsonStringifier.stringify (dart:convert/json.dart:336)
#2 JsonEncoder.convert (dart:convert/json.dart:177)
....
... unless we explicitly tell dart:convert how to encode:
class Customer
{
int Id;
String Name;
Map toJson() {
Map map = new Map();
map["Id"] = Id;
map["Name"] = Name;
return map;
}
}
Do I really have to add a toJson method to every single one of my model classes, or is there a better way?
EDIT: this is the simple serialization I'm looking for:
{
"Id": 17,
"Name": "John"
}
Compare to ToJson in ServiceStack.Text, for instance.
Dart's serialization library (see Matt B's answer below) seems like a step in the right direction. However, this ...
var serialization = new Serialization()
..addRuleFor(Customer);
var json = JSON.encode(serialization.write(customer, format: new SimpleJsonFormat()));
... produces just an array with the values (no keys):
[17,"John"]
Using the default SimpleMapFormat on the other hand generates this complex representation.
Still haven't found what I'm looking for...
EDIT 2: Adding some context: I'm building a RESTful web service in Dart, and I'm looking for a JSON serialization which can easily be consumed by any client, not just another Dart client. For instance, querying the Stack Exchange API for this very question will create this JSON response. This is the serialization format I'm looking for. - Or, look at typical JSON responses returned by the Twitter REST API or the Facebook Graph API.
EDIT 3: I wrote a small blog post about this. See also the discussion on Hacker News.
IMO this is a major short-coming in Dart, surprising given its Web Application focus. I would've thought that having JSON support in the standard libraries would've meant that serializing classes to and from JSON would work like water, unfortunately the JSON support seems incomplete, where it appears the choices are to work with loosely typed maps or suffer through un-necessary boilerplate to configure your standard (PODO) classes to serialize as expected.
Without Reflection and Mirrors support
As popular Dart platforms like Flutter doesn't support Reflection/Mirrors your only option is to use a code-gen solution. The approach we've taken in ServiceStack's native support for Dart and Flutter lets you generate typed Dart models for all your ServiceStack Services from a remote URL, e.g:
$ npm install -g #servicestack/cli
$ dart-ref https://techstacks.io
Supported in .NET Core and any of .NET's popular hosting options.
The example above generates a Typed API for the .NET TechStacks project using the generated DTOs from techstacks.io/types/dart endpoint. This generates models following Dart's JsonCodec pattern where you can customize serialization for your Dart models by providing a fromJson named constructor and a toJson() instance method, here's an example of one of the generated DTOs:
class UserInfo implements IConvertible
{
String userName;
String avatarUrl;
int stacksCount;
UserInfo({this.userName,this.avatarUrl,this.stacksCount});
UserInfo.fromJson(Map<String, dynamic> json) { fromMap(json); }
fromMap(Map<String, dynamic> json) {
userName = json['userName'];
avatarUrl = json['avatarUrl'];
stacksCount = json['stacksCount'];
return this;
}
Map<String, dynamic> toJson() => {
'userName': userName,
'avatarUrl': avatarUrl,
'stacksCount': stacksCount
};
TypeContext context = _ctx;
}
With this model you can use Dart's built-in json:convert APIs to serialize and deserialize your model to JSON, e.g:
//Serialization
var dto = new UserInfo(userName:"foo",avatarUrl:profileUrl,stacksCount:10);
String jsonString = json.encode(dto);
//Deserialization
Map<String,dynamic> jsonObj = json.decode(jsonString);
var fromJson = new UserInfo.fromJson(jsonObj);
The benefit of this approach is that it works in all Dart platforms, including Flutter and AngularDart or Dart Web Apps with and without Dart 2’s Strong Mode.
The generated DTOs can also be used with servicestack's Dart package to enable an end to end typed solution which takes care JSON serialization into and out of your typed DTOs, e.g:
var client = new JsonServiceClient("https://www.techstacks.io");
var response = await client.get(new GetUserInfo(userName:"mythz"));
For more info see docs for ServiceStack's native Dart support.
Dart with Mirrors
If you're using Dart in a platform where Mirrors support is available I've found using a Mixin requires the least effort, e.g:
import 'dart:convert';
import 'dart:mirrors';
abstract class Serializable {
Map toJson() {
Map map = new Map();
InstanceMirror im = reflect(this);
ClassMirror cm = im.type;
var decls = cm.declarations.values.where((dm) => dm is VariableMirror);
decls.forEach((dm) {
var key = MirrorSystem.getName(dm.simpleName);
var val = im.getField(dm.simpleName).reflectee;
map[key] = val;
});
return map;
}
}
Which you can mixin with your PODO classes with:
class Customer extends Object with Serializable
{
int Id;
String Name;
}
Which you can now use with JSON.encode:
var c = new Customer()..Id = 1..Name = "Foo";
print(JSON.encode(c));
Result:
{"Id":1,"Name":"Foo"}
Note: see caveats with using Mirrors
I wrote the Exportable library to solve such things like converting to Map or JSON. Using it, the model declaration looks like:
import 'package:exportable/exportable.dart';
class Customer extends Object with Exportable {
#export int id;
#export String name;
}
And if you want to convert to JSON, you may:
String jsonString = customer.toJson();
Also, it's easy to initialize new object from a JSON string:
Customer customer = new Customer()..initFromJson(jsonString);
Or alternatively:
Customer customer = new Exportable(Customer, jsonString);
Please, see the README for more information.
An alternative is to use the Serialization package and add rules for your classes. The most basic form uses reflection to get the properties automatically.
Redstone mapper is the best serialization library I've used. JsonObject and Exportable have the downside that you have to extend some of their classes. With Redstone Mapper you can have structures like this
class News
{
#Field() String title;
#Field() String text;
#Field() List<FileDb> images;
#Field() String link;
}
It works with getters and setters, you can hide information by not annotating it with #Field(), you can rename field from/to json, have nested objects, it works on the server and client. It also integrates with the Redstone Server framework, where it has helpers to encode/decode to MongoDB.
The only other framework I've seen thats on the right direction is Dartson, but it still lack some features compared to Redstone Mapper.
I have solved with:
class Customer extends JsonObject
{
int Id;
String Name;
Address Addr;
}
class Address extends JsonObject{
String city;
String State;
String Street;
}
But my goal is bind data from/to json from/to model classes; This solution work if you can modify model classes, in a contrast you must use solution "external" to convert model classes;
see also: Parsing JSON list with JsonObject library in Dart
Another package solving this problem is built_value:
https://github.com/google/built_value.dart
With built_value your model classes look like this:
abstract class Account implements Built<Account, AccountBuilder> {
static Serializer<Account> get serializer => _$accountSerializer;
int get id;
String get name;
BuiltMap<String, JsonObject> get keyValues;
factory Account([updates(AccountBuilder b)]) = _$Account;
Account._();
}
Note that built_value isn't just about serialization -- it also provides operator==, hashCode, toString, and a builder class.
I have achieve with this:
To make this work, pass explicitToJson: true in the #JsonSerializable() annotation over the class declaration. The User class now looks as follows:
import 'address.dart';
import 'package:json_annotation/json_annotation.dart';
part 'user.g.dart';
#JsonSerializable(explicitToJson: true)
class User {
String firstName;
Address address;
User(this.firstName, this.address);
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
}
You can check here: https://flutter.dev/docs/development/data-and-backend/json#generating-code-for-nested-classes
I prefer using https://ashamp.github.io/jsonToDartModel/ online tool write by myself.
It has features below:
online use, without plugin
support multidimensional list
support complex json
support convert all props to String type
empty props warning
single file
dart keyword protected
instant convert
I think it's better than other tools.Welcome if you have any suggestion, issue or bug report.
Some of the answers are no longer applicable to Flutter 2; here is the process for automatically creating toJson and fromJson methods:
https://flutter.dev/docs/development/data-and-backend/json#creating-model-classes-the-json_serializable-way
PS: I wish this would be as simple as using Newtonsoft library in Asp.Net, this solution is closest to an automated solution