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
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 namefactory .fromJson(json) => _$ClassFromJson(json);
toJson() => _$ClassToJson(this)
_
underscore)$
factory
supplies (Map json)
as argumenttoJson()
returns Map
When all that is complete, try running the generator from the project root directory...
In Flutter:
flutter pub run build_runner build
In pure Dart:
pub run build_runner build
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'
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 json) => _$AccountFromJson(json);
Map toJson() => _$AccountToJson(this);
}
Produces:
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'account.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Account _$AccountFromJson(Map json) {
return Account()
..created = json['created'] as int
..username = json['username'] as String
..password = json['password'] as String
..id = json['id'] as int;
}
Map _$AccountToJson(Account instance) => {
'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 json) => _$CacheItemFromJson(json);
Map toJson() => _$CacheItemToJson(this);
}
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'cache-item.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
CacheItem _$CacheItemFromJson(Map json) {
return CacheItem(
json['created'] as int,
json['keywords'] as String,
json['response'] as String,
);
}
Map _$CacheItemToJson(CacheItem instance) => {
'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 json) => _$CacheItemFromJson(json);
Map toJson() => _$CacheItemToJson(this);
}
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'cache-item.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
CacheItem _$CacheItemFromJson(Map json) {
return CacheItem(
response: json['response'] as String,
)
..created = json['created'] as int
..keywords = json['keywords'] as String;
}
Map _$CacheItemToJson(CacheItem instance) => {
'created': instance.created,
'keywords': instance.keywords,
'response': instance.response,
};