UPDATE I have built a small sample and will add all code to this post. I have to believe there is an answer/explanation to this and am hoping someone can educate me on what I'm missing. Class fields that are type object are not getting converted and I do not understand why.
Here are the model classes I'm working with.
import 'package:json_annotation/json_annotation.dart';
part 'parent.g.dart';
@JsonSerializable()
class Parent {
int id;
final String name;
final int age;
List<Child> children;
Job job;
Parent({this.name, this.age, this.children, this.job});
factory Parent.fromJson(Map<String, dynamic> json) => _$ParentFromJson(json);
Map<String, dynamic> toJson() => _$ParentToJson(this);
}
@JsonSerializable()
class Child{
int id;
final String name;
final int age;
Child({this.name, this.age});
factory Child.fromJson(Map<String, dynamic> json) => _$ChildFromJson(json);
Map<String, dynamic> toJson() => _$ChildToJson(this);
}
@JsonSerializable()
class Job{
int id;
String title;
Job({this.title});
factory Job.fromJson(Map<String, dynamic> json) => _$JobFromJson(json);
Map<String, dynamic> toJson() => _$JobToJson(this);
}
Here is the .g generated file for these classes
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'parent.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Parent _$ParentFromJson(Map<String, dynamic> json) {
return Parent(
name: json['name'] as String,
age: json['age'] as int,
children: (json['children'] as List)
?.map(
(e) => e == null ? null : Child.fromJson(e as Map<String, dynamic>))
?.toList(),
job: json['job'] == null
? null
: Job.fromJson(json['job'] as Map<String, dynamic>),
)..id = json['id'] as int;
}
Map<String, dynamic> _$ParentToJson(Parent instance) => <String, dynamic>{
'id': instance.id,
'name': instance.name,
'age': instance.age,
'children': instance.children,
'job': instance.job,
};
Child _$ChildFromJson(Map<String, dynamic> json) {
return Child(
name: json['name'] as String,
age: json['age'] as int,
)..id = json['id'] as int;
}
Map<String, dynamic> _$ChildToJson(Child instance) => <String, dynamic>{
'id': instance.id,
'name': instance.name,
'age': instance.age,
};
Job _$JobFromJson(Map<String, dynamic> json) {
return Job(
title: json['title'] as String,
)..id = json['id'] as int;
}
Map<String, dynamic> _$JobToJson(Job instance) => <String, dynamic>{
'id': instance.id,
'title': instance.title,
};
Here is the DAO class for the parent class
import 'package:sembast/sembast.dart';
import 'package:json_serial_test/services/app_database.dart';
import 'package:json_serial_test/models/parent.dart';
class ParentDao {
static const String PARENT_STORE_NAME = 'parents';
// A Store with int keys and Map<String, dynamic> values.
// This Store acts like a persistent map, values of which are Parent objects converted to Map
final _parentStore = intMapStoreFactory.store(PARENT_STORE_NAME);
// Private getter to shorten the amount of code needed to get the
// singleton instance of an opened database.
Future<Database> get _db async => await AppDatabase.instance.database;
Future insert(Parent parent) async {
await _parentStore.add(await _db, parent.toJson());
}
Future update(Parent parent) async {
// For filtering by key (ID), RegEx, greater than, and many other criteria,
// we use a Finder.
final finder = Finder(filter: Filter.byKey(parent.id));
await _parentStore.update(
await _db,
parent.toJson(),
finder: finder,
);
}
Future deleteAll() async {
await _parentStore.delete(await _db);
}
Future delete(Parent parent) async {
final finder = Finder(filter: Filter.byKey(parent.id));
await _parentStore.delete(
await _db,
finder: finder,
);
}
Future<List<Parent>> getAllSortedByName() async {
// Finder object can also sort data.
final finder = Finder(sortOrders: [
SortOrder('name'),
]);
final recordSnapshots = await _parentStore.find(
await _db,
finder: finder,
);
// Making a List<Parent> out of List<RecordSnapshot>
return recordSnapshots.map((snapshot) {
final parent = Parent.fromJson(snapshot.value);
// An ID is a key of a record from the database.
parent.id = snapshot.key;
return parent;
}).toList();
}
}
Here is my test
// Setup
final k1 = Child(name: 'Billy', age: 10);
final k2 = Child(name: 'Jannet', age: 9);
final job = Job(title: 'Cook');
final List<Child> kids = [k1, k2];
final dad = Parent(name: 'Dave', age: 52, job: job, children: kids);
await pDao.insert(dad);
List<Parent> dadsInDb = await pDao.getAllSortedByName();
print('Dads from DB: ${dadsInDb.toString()}');
Upon trying to perform an insert of a Parent into my sembast DB, this is the error that shows up.
E/flutter (12986): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: Invalid argument(s): value Instance of 'Child' unsupported type Child E/flutter (12986): #0 cloneValue (package:sembast/src/utils.dart:191:3) E/flutter (12986): #1 cloneValue.<anonymous closure> (package:sembast/src/utils.dart:177:33) E/flutter (12986): #2 MappedListIterable.elementAt (dart:_internal/iterable.dart:417:29) E/flutter (12986): #3 ListIterable.toList (dart:_internal/iterable.dart:221:19) E/flutter (12986): #4 cloneValue (package:sembast/src/utils.dart:177:52) E/flutter (12986): #5 cloneValue.<anonymous closure> (package:sembast/src/utils.dart:174:49) E/flutter (12986): #6 MapMixin.map (dart:collection/maps.dart:165:28) E/flutter (12986): #7 cloneValue (package:sembast/src/utils.dart:173:18) E/flutter (12986):
#8 SembastStore.txnPutSync (package:sembast/src/store_impl.dart:133:15) E/flutter (12986): #9 SembastStore.txnAdd (package:sembast/src/store_impl.dart:117:11) E/flutter (12986): <asynchronous suspension> E/flutter (12986): #10 StoreRefMixin.add.<anonymous closure> (package:sembast/src/store_ref_impl.dart:75:12) E/flutter (12986): #11 SembastDatabase.inTransaction.<anonymous closure> (package:sembast/src/database_impl.dart:1238:34) E/flutter (12986):
#12 SembastDatabase.transaction.<anonymous closure>.<anonymous closure> (package:sembast/src/database_impl.dart:1090:59) E/flutter (12986): #13 new Future.sync (dart:async/future.dart:224:31) E/flutter (12986): #14 SembastDatabase.transaction.<anonymous closure> (package:sembast/src/database_impl.dart:1090:26) E/flutter (12986): #15 BasicLock.synchronized (package:synchronized/src/basic_lock.dart:32:26) E/flutter (12986):
#16 SembastDatabase.transaction (package:sembast/src/database_impl.dart:1073:38) E/flutter (12986):
#17 SembastDatabase.inTransaction (package:sembast/src/database_impl.dart:1238:7) E/flutter (12986): #18 StoreRefMixin.add (package:sembast/src/store_ref_impl.dart:72:25) E/flutter (12986): #19 ParentDao.insert (package:json_serial_test/data/parent_dao.dart:17:24) E/flutter (12986): <asynchronous suspension> E/flutter (12986): #20
_MyHomePageState.build.<anonymous closure> (package:json_serial_test/main.dart:120:22) E/flutter (12986): #21
_InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:706:14) E/flutter (12986):
#22 _InkResponseState.build.<anonymous closure> (package:flutter/src/material/ink_well.dart:789:36) E/flutter (12986):
#23 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:182:24) E/flutter (12986): #24 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:486:11) E/flutter (12986): #25 BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:264:5) E/flutter (12986): #26 BaseTapGestureRecognizer.acceptGesture (package:flutter/src/gestures/tap.dart:236:7) E/flutter (12986): #27 GestureArenaManager.sweep (package:flutter/src/gestures/arena.dart:156:27) E/flutter (12986):
#28 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:222:20) E/flutter (12986):
#29 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:198:22) E/flutter (12986):
#30 GestureBinding._handlePointerEvent (package:flutter/src/gestures/binding.dart:156:7) E/flutter (12986):
#31 GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:102:7) E/flutter (12986):
#32 GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:86:7) E/flutter (12986):
#33 _rootRunUnary (dart:async/zone.dart:1138:13) E/flutter (12986): #34 _CustomZone.runUnary (dart:async/zone.dart:1031:19) E/flutter (12986): #35 _CustomZone.runUnaryGuarded (dart:async/zone.dart:933:7) E/flutter (12986): #36 _invoke1 (dart:ui/hooks.dart:273:10) E/flutter (12986): #37
_dispatchPointerDataPacket (dart:ui/hooks.dart:182:5) E/flutter (12986):
If someone could please assist me in showing what I've missed, done incorrectly, I would greatly appreciate it.
ORIGINAL POST
==============
Should json_serializable
convert ALL of a class to JSON, or are there limitations that I am hitting?
I have decided to try and resolve a slew of issues I've caused myself by using json_serializable
to create the toJson and fromJson methods needed for working with a NoSQL DB
.
If I create a class which includes a field that is a List<Ojb>
, the generated code seems to result in JSON for each field of the class but fails to do so for the objects in the list.
Simple example
class Parent {
final int age;
final String name;
List<Child> children;
Parent({this.age, this.name, this.children});
}
class Child {
final int age;
final String name;
Child({this.age, this.name});
}
When I use json_serializable, which seems to work beautifully, I get my toJson and fromJson methods for the above mentioned classes. At first glance, everything looks perfect.
When I attempt to insert a Parent (containing children) into my NoSQL DB, the insert fails to say that the type (referencing the Child object) is not supported - True statement, have no issues with that.
When I step through this via the debugger, here is what I am seeing.
Parent
is converted to map
age shows up as key with its value
the name shows up as key with its value
Up to this point, I can see that everything is a map[.. and all looks great
Then we get to the list of Child objects.
This part is not converted to a map but still exists as a list of Child objects, hence the failure of the insert.
Both classes have the jsonSerializable
annotation
Both classes are generating the expected code (part of), classes
Everything works perfectly as long as I don't try to use a List<myObject>
in one of my classes.
The entire reason one would use a package like json_serializable is to rely on the code that is automatically generated and not have to build it myself. I don't want to have to manually update automatically generated code to resolve this, which is why I have not pasted in the code. If that's the answer, then I'll take a different route.
My question is... is there some configuration that I am missing or possibly doing incorrectly that would allow ALL items within the class to be converted to map/json, even when the fields of a class are not just simple int and string types. I would expect that I could have a class which includes primitives, along with objects, Lists of objects, etc. and everything should generate correctly, or no?