Десериализация абстрактных типов в Dart

#dart #serialization #deserialization

#dart #сериализация #десериализация

Вопрос:

Пожалуйста, рассмотрите следующий сегмент кода, объявление одного базового класса и двух расширителей.

 abstract class Animal {
  final bool flag;

  Animal(this.flag);
}

class Cat extends Animal {
  final String name;

  Cat(this.name, bool flag) : super(flag);

  Cat.fromJson(Map<String, dynamic> json)
      : name = json['name'],
        super(json['flag']);
}

class Fish extends Animal {
  final int memory;

  Fish(this.memory, bool flag) : super(flag);

  Fish.fromJson(Map<String, dynamic> json)
      : memory = json['memory'],
        super(json['flag']);
}
  

Какова наилучшая практика сериализации и десериализации следующего списка?

 List<Animal> animals = [Cat('Nancy', true), Fish(10, false)];
  

Итак, как сериализовать информацию о типе для каждого класса, чтобы создать соответствующий экземпляр после десериализации?

Код сериализации опущен для простоты, однако он вернет карту полей-членов. Приведенный выше код является лишь примером — в структуре или данных нет смысла.

РЕДАКТИРОВАТЬ: я бы предпочел не использовать какую-либо библиотеку или генератор кода, а использовать внутренние возможности языка.

Любое предложение приветствуется, спасибо.

Ответ №1:

Мне пришлось это выяснить, поэтому я предоставляю свое наивное решение проблемы. Однако, если кто-нибудь предложит лучший подход, я отмечу это как ответ.

Мое наивное решение состоит в том, чтобы украсить каждый результат JSON полем «тип», которое содержит имя подкласса. Тогда я мог бы создать фабрику для базового типа, которая создает соответствующий экземпляр в зависимости от этого поля «тип». Полный код:

 abstract class Animal {
  final bool flag;

  Animal(this.flag);

  factory Animal.fromJson(Map<String, dynamic> json) {
    switch (json['type']) {
      case 'cat':
        return Cat.fromJson(json);
      case 'fish':
        return Fish.fromJson(json);
      default:
        throw 'Invalid animal type';
    }
  }
}

class Cat extends Animal {
  final String name;

  Cat(this.name, bool flag) : super(flag);

  Cat.fromJson(Map<String, dynamic> json)
      : name = json['name'],
        super(json['flag'].toString() == 'true');

  Map<String, dynamic> toJson() {
    return {'type': 'cat', 'name': name, 'flag': flag.toString()};
  }
}

class Fish extends Animal {
  final int memory;

  Fish(this.memory, bool flag) : super(flag);

  Fish.fromJson(Map<String, dynamic> json)
      : memory = json['memory'],
        super(json['flag'].toString() == 'true');

  Map<String, dynamic> toJson() {
    return {'type': 'fish', 'memory': memory, 'flag': flag.toString()};
  }
  

Очевидно, что во время десериализации элементы списка должны быть сопоставлены с использованием заводского метода следующим образом:

 var data = jsonDecode("[{'type':'cat','name':'Nancy','flag':'true'},{'type':'fish','memory':10,'flag':'false'}]");
List<Animal> list = data.map((e) => Animal.fromJson(e)).cast<Animal>().toList();