realm-dart
realm-dart copied to clipboard
JsonSerializable compatibility
I really appreciate this library but how can I use it in combination with the json_serializable library? I need to serialize/deserialize my objects in a fast way but realm generator seems to be not compatible with json_serializable library.
We also hit this same scenario where our 'web' Flutter app needed json_serializable package and our native macOS/Windows/iOS/Andriod applications use realm-dart package... You cant use both simultaneously... Whilst there are workarounds for our specific use case, it would be good if both packages could work alongside each other.
Yes it is really an issue and we will think of providing an easy way to serialize/deserialize RealmObjects. There are some workarounds that could currently help you.
One of them could be for example if you define a model.dart like:
import 'package:realm_dart/realm.dart';
import 'package:json_annotation/json_annotation.dart';
part 'model.g.dart';
@RealmModel()
@JsonSerializable()
class _Person {
late String name;
late int age;
}
extension PersonJ on Person {
static Person toRealmObject(_Person person) {
return Person(
person.name,
person.age,
);
}
static Person fromJson(Map<String, dynamic> json) => toRealmObject(_$PersonFromJson(json));
Map<String, dynamic> toJson() => _$PersonToJson(this);
}
Then run the generator using dart run build_runner build or flutter pub run build_runner build
And then it could be used as follow:
final json = person.toJson();
Person copyPerson = PersonJ.fromJson(json);
Of course you can find even better workarounds. We will take an attention on the issue. Thank you for the feedback!
Thank you @desistefanova for your suggestion - prefer not to duplicate code, but at the same time this is a workaround that will work. Thank you again and hope you have a good weekend.
@desistefanova Thank you for sharing the workaround. Were you able to find a solution for nested class like below.
@RealmModel()
@JsonSerializable(
explicitToJson: true)
class _Patient {
@PrimaryKey()
late String email;
late List<$Telecom> telecom = [];
}
@RealmModel()
@JsonSerializable()
class $Telecom {
late String value;
}
_$PatientFromJson will have a ref to $Telecom.fromJson(e as Map<String, dynamic>). However $Telecom can't have a factory constructor for realm.
Hi @tushkaty!
We still don't have an elegant solution for Json serialization but the good thing is that having methods inside your RealmModel class is not a problem. You are free add methods like fromJson and toJson inside the class $Telecom. Here is the sample:
import 'package:realm_dart/realm.dart';
import 'package:json_annotation/json_annotation.dart';
part 'jsonapp.g.dart';
@RealmModel()
@JsonSerializable(explicitToJson: true)
class _Patient {
@PrimaryKey()
late String email;
late List<$Telecom> telecom = [];
}
@RealmModel()
@JsonSerializable()
class $Telecom {
late String value;
static $Telecom fromJson(Map<String, dynamic> json) => _$$TelecomFromJson(json);
Map<String, dynamic> toJson() => _$$TelecomToJson(this);
}
extension PatientJ on Patient {
static Patient toRealmObject(_Patient patient) {
return Patient(patient.email)..telecom.addAll(patient.telecom.map((e) => Telecom(e.value)));
}
static Patient fromJson(Map<String, dynamic> json) => toRealmObject(_$PatientFromJson(json));
Map<String, dynamic> toJson() => _$PatientToJson(this);
}
extension TelecomJ on Telecom {
static Telecom toRealmObject($Telecom telecom) {
return Telecom(telecom.value);
}
static Telecom fromJson(Map<String, dynamic> json) => toRealmObject(_$$TelecomFromJson(json));
Map<String, dynamic> toJson() => _$$TelecomToJson(this);
}
void main(List<String> arguments) {
final patient = Patient("[email protected]")..telecom.add(Telecom("tlc"));
final json = patient.toJson();
Patient copyPatientn = PatientJ.fromJson(json);
}
Hi All, I have used the above code, thanks @desistefanova, and managed to combine the findings into a really elegant solution which I will try my best to show here: (I will use the example above for easier comprehension)
In a solution with just 1 RealmModel:
import 'package:realm_dart/realm.dart';
import 'package:json_annotation/json_annotation.dart';
part 'model.g.dart';
@RealmModel()
@JsonSerializable()
class _Person {
late String name;
late int age;
Person toRealmObject() {
return Person(
name,
age,
);
}
}
extension PersonJsonHandler on Person {
static Person fromJson(Map<String, dynamic> json) =>
_$PersonFromJson(json).toRealmObject();
Map<String, dynamic> toJson() => _$PersonToJson(this);
}
Now, where it gets really nice is with embedded models. By setting up our classes as above, only the top level model would need to implement the extension method and the following embedded objects can be written using the following approach: (For simplicity I have omitted embedding the model as a list and just referencing the model as an individual model)
import 'package:realm_dart/realm.dart';
import 'package:json_annotation/json_annotation.dart';
part 'model.g.dart';
@RealmModel()
@JsonSerializable()
class _Person {
late String name;
late $Telecom? telecom; //Remember, embedded models need to be nullable
Person toRealmObject() {
return Person(
name,
telecom: telecom?.toRealmObject(),
);
}
}
extension PersonJsonHandler on Person {
static Person fromJson(Map<String, dynamic> json) =>
_$PersonFromJson(json).toRealmObject(); //This is where the magic happens, so no need to create the extension on lower level classes
Map<String, dynamic> toJson() => _$PersonToJson(this);
}
And then $Telecom class looks as follows:
import 'package:realm/realm.dart';
import 'package:json_annotation/json_annotation.dart';
part 'telecom.g.dart';
@RealmModel()
@JsonSerializable()
class $Telecom {
late String value;
Telecom toRealmObject() {
return Telecom(
value,
);
}
//This is mandatory as it will be referenced in the auto-generated class for the upper level model (in this case, person.g.dart)
static Telecom fromJson(Map<String, dynamic> json) =>
_$$TelecomFromJson(json).toRealmObject();
//If you require the toJson function you can add it as follows
static Map<String, dynamic> toJson($Telecom telecom) =>
_$$TelecomToJson(telecom);
}
then in main:
void main(List<String> arguments) {
Patient copyPatientn = PatientJ.fromJson(json);
}
I have not included the full solution for toJson as I did not need it in my solution but it should be pretty easy to infer the solution from my example.
Hope this helps anybody looking for a more simplistic solution for their projects with models embedded on many layers.
Hi @RashadAkoodieRabbit!
Thank you very much for providing this example. I hope it will help people to solve the issue with the serialisation.
Meanwhile, @nielsenko is working on a solution for providing serialization/deserialization to EJSON for complex types like ObjectId, Decimal128 that we support in the RealmModel. Once it is ready we will avoid writing additional code for toJson/fromJson.
Hi @RashadAkoodieRabbit! Thank you very much for providing this example. I hope it will help people to solve the issue with the serialisation. Meanwhile, @nielsenko is working on a solution for providing serialization/deserialization to EJSON for complex types like ObjectId, Decimal128 that we support in the RealmModel. Once it is ready we will avoid writing additional code for
toJson/fromJson.
Glad to share! Please could you update this issue when the fix goes live so we migrate our project to use the latest :) thank you
Any update yet on this? Also on using UInt8List with json?
@RashadAkoodieRabbit As you may know, there is a PR (sitting on top of another) to support EJSON, but currently it is in a bit of a flux.
I'm no longer working for MongoDB, so I won't be able help bring it over the finish line.
any updates on this issue?
Yes, the linked PRs are now merged and will be released with our next release. We're putting the finishing touches there and hope to have it published shortly.
@nirinchev will installation work as normal? I tried using the current alpha and i ran into the following error
Unhandled exception:
Exception: Error parsing package pubspec at Directory: '/home/.../.pub-cache/hosted/pub.dev'. Error FileSystemException: Cannot open file, path = '/home/kingwill101/.pub-cache/hosted/pub.dev/realm-2.0.0-alpha.5/' (OS Error: Is a directory, errno = 21)
#0 InstallCommand.parsePubspec (package:realm_dart/src/cli/install/install_command.dart:119:7)
<asynchronous suspension>
#1 InstallCommand.run (package:realm_dart/src/cli/install/install_command.dart:143:26)
<asynchronous suspension>
#2 CommandRunner.runCommand (package:args/command_runner.dart:212:13)
<asynchronous suspension>
CMake Error at cmake_install.cmake:148 (file):
file INSTALL cannot find
"/home/....../linux/flutter/ephemeral/.plugin_symlinks/realm/linux/binary/linux/librealm_dart.so":
No such file or directory.
downgrading back to the current stable works
dart run realm install -t linux
Building package executable...
Built realm:realm.
Downloading Realm binaries for [email protected] to /tmp/realm-binary-RZIKXQ/linux.tar.gz
Extracting Realm binaries to /home/...../binary/linux
extracting librealm_dart.so
Archive /tmp/realm-binary-RZIKXQ/linux.tar.gz extracted to /home/.../../..../binary/linux
Realm install command finished.
The alpha is still quite raw - we wanted to publish it to run some tests, but wouldn't say it's ready for consumption yet.
That being said, we do appreciate the enthusiasm and the early report ❤️ I'll take a look next week and try to get it resolved before the next drop.
Create a PR with a fix here: https://github.com/realm/realm-dart/pull/1576 - I expect it to be released with the next alpha release.