objectbox / objectbox-dart

Flutter database for super-fast Dart object persistence
https://docs.objectbox.io/getting-started
Apache License 2.0
927 stars 115 forks source link

JSON Serialization with relations #628

Closed badayumut closed 2 weeks ago

badayumut commented 2 weeks ago

Is there an existing issue?

Build info

Steps to reproduce

I'm working on exporting the model to json and retrieving it, and I reviewed the example here. I tried to implement it to my own model in the same way. Simple parameters (String, int) in the model are saved in json, but I cannot extract relations. Is there something I missed? I'm trying to transfer the model as it is with relations and get it back. https://github.com/objectbox/objectbox-dart/blob/main/generator/integration-tests/part-partof/lib/json.dart

import 'package:json_annotation/json_annotation.dart';
import 'package:objectbox/objectbox.dart';

part 'example_model.g.dart';

@Entity()
@JsonSerializable()
class Book {
  @Id()
  int id = 0;

  String content;

  @_PersonRelToOneConverter()
  final author = ToOne<Person>();

  Book({
    required this.content,
  });

  factory Book.fromJson(Map<String, dynamic> json) => _$BookFromJson(json);

  Map<String, dynamic> toJson() => _$BookToJson(this);
}

@Entity()
@JsonSerializable()
class Person {
  @Id()
  int id = 0;

  String name;

  @_BookRelToManyConverter()
  final books = ToMany<Book>();

  Person({
    required this.name,
  });

  factory Person.fromJson(Map<String, dynamic> json) => _$PersonFromJson(json);

  Map<String, dynamic> toJson() => _$PersonToJson(this);
}

class _PersonRelToOneConverter
    implements JsonConverter<ToOne<Person>, Map<String, dynamic>?> {
  const _PersonRelToOneConverter();

  @override
  ToOne<Person> fromJson(Map<String, dynamic>? json) => ToOne<Person>(
      target: json == null ? null : Person.fromJson(json));

  @override
  Map<String, dynamic>? toJson(ToOne<Person> rel) => rel.target?.toJson();
}

class _BookRelToManyConverter
    implements JsonConverter<ToMany<Book>, List<Map<String, dynamic>>?> {
  const _BookRelToManyConverter();

  @override
  ToMany<Book> fromJson(List<Map<String, dynamic>>? json) =>
      ToMany<Book>(
          items: json == null
              ? null
              : json.map((e) => Book.fromJson(e)).toList());

  @override
  List<Map<String, dynamic>>? toJson(ToMany<Book> rel) =>
      rel.map((Book obj) => obj.toJson()).toList();
}

Expected behavior

should be able to access relations after created from json

Actual behavior

relations missing after json serialization

Code

    Person author = Person(name: "author");

    Book book1 = Book(content: "content1")..author.target = author;
    Book book2 = Book(content: "content2")..author.target = author;

    Person person = Person(name: "person");
    person.books.add(book1);
    person.books.add(book2);

    final book1FromJson = Book.fromJson(book1.toJson());
    final personFromJson = Person.fromJson(person.toJson());

    print(book1FromJson.author.target?.name); // null

    print(personFromJson.books.length); // 0

Logs, stack traces

TODO Add relevant logs, a stack trace or crash report.

Logs ```console [Paste your logs here] ```
greenrobot-team commented 2 weeks ago

From what I can see the constructors of the classes you have given do not have parameters for the relation fields:

  Book({
    required this.content, // no parameter for author
  });

  Person({
    required this.name, // no parameter for books
  });

In any case, I would strongly recommend to use a different model for the database and the JSON serializer and map between them. It would avoid this issue and also make any future changes easier to handle.

Let me know if this resolves your issue!

badayumut commented 2 weeks ago

I was about to update the issue with that the generated example_model.g.dart did not have the rel convertion methods. Anyway I couldn't make it work this way. So i implemented a custom map for to-from methods. I think it's the best way to it with more control. Thanks!