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
JSON serialization with code generation means having an external library generate the encoding boilerplate for you. After some initial setup, you run a file watcher that generates the code from your model classes. For example, json_serializable and built_value are these kinds of libraries.
import 'package:json_annotation/json_annotation.dart';
part
directive after import
statements
part
file is named after your class filename (not the Class name itself), with a g
addedCacheItem
class with ...cache-item.dart
class filename ...part 'cache-item.g.dart';
gets corresponding part
directive.part
directive is not named after your actual Class, but the class file name.@JsonSerializable()
above the class namefromJson
method
factory CacheItem.fromJson(Map<String,dynamic> json) => _CacheItemFromJson(json)
toJson
method
Map<String,dynamic> toJson() => _$CacheItemToJson(this)
_
underscore)$
factory
supplies (Map<String,dynamic> json)
as argumenttoJson()
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.
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 for
someField.
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`.
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"}}
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) {
return 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,
};
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);
}
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'cache-item.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
CacheItem _$CacheItemFromJson(Map<String, dynamic> json) {
return 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,
};
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);
}
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'cache-item.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
CacheItem _$CacheItemFromJson(Map<String, dynamic> json) {
return 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,
};
try running
>flutter packages pub run build_runner build --delete-conflicting-outputs
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With