I am saving an entire class as a json object on my Parse Back4App server. It was a pain getting the object to parse to json because I needed to turn many properties into string in both nested class Book and main class DatabaseSyncItem. Now I have no idea how to turn this json object back into a DatabaseSyncObject. I assume the same would go for getting the nested json book objects back to object model, that is that it wont work this way.
I'm getting errors and have no success. If I try to access a specific index from my list of json objects in main I can get somewhere but this is very messy. What would you do in this situation? Help me out here, my first time working with json.
Tried applying these posts but no workey:
import 'dart:convert';
import 'dart:io';
import 'package:hive/hive.dart';
import 'package:parse_server_sdk/parse_server_sdk.dart';
import 'package:service_database_sync/data/books_hardcoded.dart';
import 'package:service_database_sync/models/book_model.dart';
import 'package:service_database_sync/models/database_sync_model.dart';
import 'package:service_database_sync/services/demo_services.dart';
import 'package:service_database_sync/services/server_database_services.dart';
Future<void> main(List<String> arguments) async {
Hive.init('hive_database');
Hive.registerAdapter(BookAdapter());
await Parse().initialize(
ServerDatabaseServices().keyApplicationId,
ServerDatabaseServices().keyParseServerUrl,
clientKey: ServerDatabaseServices().keyClientKey,
debug: true,
);
final test = updateLocalDatabase();
Future<List> getList() {
return Future.value(test);
}
var list = await getList();
var jsonObject = list[2]['DatabaseSyncItem'];
// var backToObject = jsonDecode(jsonObject); // error: type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'String'
var backToObject = DatabaseSyncItem.fromJson(jsonObject); // error: type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'Book'
print(backToObject);
}
// This method will get server database
Future<List<ParseObject>> updateLocalDatabase() async {
final events = QueryBuilder<ParseObject>(ParseObject('Event'));
final apiResponse = await events.query();
if (apiResponse.success && apiResponse.result != null) {
return apiResponse.results;
} else {
return [];
}
}
part 'database_sync_model.g.dart';
@HiveType(typeId: 1)
class DatabaseSyncItem {
@HiveField(0)
Book previousBookValue;
@HiveField(1)
Book updatedBookValue;
@HiveField(2)
DateTime dateAdded;
@HiveField(3)
DateTime lastModified;
@HiveField(4)
DatabaseAction entryAction;
DatabaseSyncItem({
this.previousBookValue,
this.updatedBookValue,
this.dateAdded,
this.lastModified,
this.entryAction,
});
@override
String toString() {
return '''
previousValue: $previousBookValue
updatedValue: $updatedBookValue
dateAdded: $dateAdded
lastModified: $lastModified
entryAction: $entryAction
''';
}
// Turn json back into data model
DatabaseSyncItem.fromJson(Map<String, dynamic> json)
: previousBookValue = json['previousBookValue'],
updatedBookValue = json['updatedBookValue'],
dateAdded = json['dateAdded'],
lastModified = json['lastModified'],
entryAction = json['entryAction'];
// Turn data model into json
Map<String, dynamic> toJson() => {
'previousBookValue': previousBookValue,
'updatedBookValue': updatedBookValue,
'dateAdded': dateAdded,
'lastModified': lastModified,
'entryAction': entryAction.toString(),
};
}
enum DatabaseAction {
create,
update,
delete,
}
import 'package:hive/hive.dart';
part 'book_model.g.dart';
@HiveType(typeId: 0)
class Book {
@HiveField(0)
String title;
@HiveField(1)
String author;
@HiveField(2)
DateTime publishingDate;
@HiveField(3)
DateTime dateAdded;
@HiveField(4)
DateTime lastModified;
Book({
this.title,
this.author,
this.publishingDate,
this.dateAdded,
this.lastModified,
});
@override
String toString() {
return '''
title: $title
author: $author
publishingDate: $publishingDate
dateAdded: $dateAdded
lastModified $lastModified
''';
}
Book.fromJson(Map<String, dynamic> json)
: title = json['title'],
author = json['author'],
publishingDate = json['publishingDate'],
dateAdded = json['dateAdded'],
lastModified = json['lastModified'];
Map<String, dynamic> toJson() => {
'title': title,
'author': author,
'publishingDate': publishingDate.toIso8601String(),
'dateAdded': dateAdded.toIso8601String(),
'lastModified': lastModified.toIso8601String()
};
}
Seems like this answer is way more complex than I initially anticipated. Need to use json serializable package and json annotation package.
The packages are necessary and you can see why when you look at the generated code for handling types, null as well as a nested class. In case anyone else runs into this.
import 'package:hive/hive.dart';
import 'package:json_annotation/json_annotation.dart';
part 'book_model.g.dart';
@JsonSerializable(includeIfNull: true)
@HiveType(typeId: 0)
class Book {
@HiveField(0)
String title;
@HiveField(1)
String author;
@HiveField(2)
DateTime publishingDate;
@HiveField(3)
DateTime dateAdded;
@HiveField(4)
DateTime lastModified;
Book({
required this.title,
required this.author,
required this.publishingDate,
required this.dateAdded,
required this.lastModified,
});
factory Book.fromJson(Map<String, dynamic> json) => _$BookFromJson(json);
Map<String, dynamic> toJson() => _$BookToJson(this);
@override
String toString() {
return '''
title: $title
author: $author
publishingDate: $publishingDate
dateAdded: $dateAdded
lastModified $lastModified
''';
}
}
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'book_model.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class BookAdapter extends TypeAdapter<Book> {
@override
final int typeId = 0;
@override
Book read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return Book(
title: fields[0] as String,
author: fields[1] as String,
publishingDate: fields[2] as DateTime,
dateAdded: fields[3] as DateTime,
lastModified: fields[4] as DateTime,
);
}
@override
void write(BinaryWriter writer, Book obj) {
writer
..writeByte(5)
..writeByte(0)
..write(obj.title)
..writeByte(1)
..write(obj.author)
..writeByte(2)
..write(obj.publishingDate)
..writeByte(3)
..write(obj.dateAdded)
..writeByte(4)
..write(obj.lastModified);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is BookAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Book _$BookFromJson(Map<String, dynamic> json) {
return Book(
title: json['title'] as String,
author: json['author'] as String,
publishingDate: DateTime.parse(json['publishingDate'] as String),
dateAdded: DateTime.parse(json['dateAdded'] as String),
lastModified: DateTime.parse(json['lastModified'] as String),
);
}
Map<String, dynamic> _$BookToJson(Book instance) => <String, dynamic>{
'title': instance.title,
'author': instance.author,
'publishingDate': instance.publishingDate.toIso8601String(),
'dateAdded': instance.dateAdded.toIso8601String(),
'lastModified': instance.lastModified.toIso8601String(),
};
import 'package:json_annotation/json_annotation.dart';
import 'book_model.dart';
import 'package:hive/hive.dart';
part 'database_sync_model.g.dart';
@JsonSerializable(includeIfNull: true)
@HiveType(typeId: 1)
class DatabaseSyncItem {
@HiveField(0)
Book? previousBookValue;
@HiveField(1)
Book? updatedBookValue;
@HiveField(2)
DateTime dateAdded;
@HiveField(3)
DateTime lastModified;
@HiveField(4)
DatabaseAction entryAction;
DatabaseSyncItem({
this.previousBookValue,
this.updatedBookValue,
required this.dateAdded,
required this.lastModified,
required this.entryAction,
});
factory DatabaseSyncItem.fromJson(Map<String, dynamic> json) => _$DatabaseSyncItemFromJson(json);
Map<String, dynamic> toJson() => _$DatabaseSyncItemToJson(this);
@override
String toString() {
return '''
previousValue: $previousBookValue
updatedValue: $updatedBookValue
dateAdded: $dateAdded
lastModified: $lastModified
entryAction: $entryAction
''';
}
}
enum DatabaseAction {
create,
update,
delete,
}
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'database_sync_model.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class DatabaseSyncItemAdapter extends TypeAdapter<DatabaseSyncItem> {
@override
final int typeId = 1;
@override
DatabaseSyncItem read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return DatabaseSyncItem(
previousBookValue: fields[0] as Book?,
updatedBookValue: fields[1] as Book?,
dateAdded: fields[2] as DateTime,
lastModified: fields[3] as DateTime,
entryAction: fields[4] as DatabaseAction,
);
}
@override
void write(BinaryWriter writer, DatabaseSyncItem obj) {
writer
..writeByte(5)
..writeByte(0)
..write(obj.previousBookValue)
..writeByte(1)
..write(obj.updatedBookValue)
..writeByte(2)
..write(obj.dateAdded)
..writeByte(3)
..write(obj.lastModified)
..writeByte(4)
..write(obj.entryAction);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is DatabaseSyncItemAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
DatabaseSyncItem _$DatabaseSyncItemFromJson(Map<String, dynamic> json) {
return DatabaseSyncItem(
previousBookValue: json['previousBookValue'] == null
? null
: Book.fromJson(json['previousBookValue'] as Map<String, dynamic>),
updatedBookValue: json['updatedBookValue'] == null
? null
: Book.fromJson(json['updatedBookValue'] as Map<String, dynamic>),
dateAdded: DateTime.parse(json['dateAdded'] as String),
lastModified: DateTime.parse(json['lastModified'] as String),
entryAction: _$enumDecode(_$DatabaseActionEnumMap, json['entryAction']),
);
}
Map<String, dynamic> _$DatabaseSyncItemToJson(DatabaseSyncItem instance) =>
<String, dynamic>{
'previousBookValue': instance.previousBookValue,
'updatedBookValue': instance.updatedBookValue,
'dateAdded': instance.dateAdded.toIso8601String(),
'lastModified': instance.lastModified.toIso8601String(),
'entryAction': _$DatabaseActionEnumMap[instance.entryAction],
};
K _$enumDecode<K, V>(
Map<K, V> enumValues,
Object? source, {
K? unknownValue,
}) {
if (source == null) {
throw ArgumentError(
'A value must be provided. Supported values: '
'${enumValues.values.join(', ')}',
);
}
return enumValues.entries.singleWhere(
(e) => e.value == source,
orElse: () {
if (unknownValue == null) {
throw ArgumentError(
'`$source` is not one of the supported values: '
'${enumValues.values.join(', ')}',
);
}
return MapEntry(unknownValue, enumValues.values.first);
},
).key;
}
const _$DatabaseActionEnumMap = {
DatabaseAction.create: 'create',
DatabaseAction.update: 'update',
DatabaseAction.delete: 'delete',
};