Serialize base
This commit is contained in:
parent
cbe4f40447
commit
94e45dccda
11 changed files with 740 additions and 0 deletions
2
.analysis-options
Normal file
2
.analysis-options
Normal file
|
@ -0,0 +1,2 @@
|
|||
analyzer:
|
||||
strong-mode: true
|
44
.gitignore
vendored
44
.gitignore
vendored
|
@ -10,3 +10,47 @@ pubspec.lock
|
|||
# Directory created by dartdoc
|
||||
# If you don't generate documentation locally you can remove this line.
|
||||
doc/api/
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff:
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/dictionaries
|
||||
|
||||
# Sensitive or high-churn files:
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.xml
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
|
||||
# Gradle:
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Mongo Explorer plugin:
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
## File-based project format:
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
/out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
|
19
lib/angel_serialize.dart
Normal file
19
lib/angel_serialize.dart
Normal file
|
@ -0,0 +1,19 @@
|
|||
/// Excludes a field from being excluded.
|
||||
class Exclude {
|
||||
const Exclude();
|
||||
}
|
||||
|
||||
const Exclude exclude = const Exclude();
|
||||
|
||||
/// Marks a class as eligible for serialization.
|
||||
class Serializable {
|
||||
const Serializable();
|
||||
}
|
||||
|
||||
const Serializable serializable = const Serializable();
|
||||
|
||||
/// Specifies an alias for a field within its JSON representation.
|
||||
class Alias {
|
||||
final String name;
|
||||
const Alias(this.name);
|
||||
}
|
275
lib/builder.dart
Normal file
275
lib/builder.dart
Normal file
|
@ -0,0 +1,275 @@
|
|||
import 'dart:async';
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/dart/element/type.dart';
|
||||
import 'package:build/build.dart';
|
||||
import 'package:code_builder/code_builder.dart';
|
||||
import 'package:code_builder/dart/core.dart';
|
||||
import 'package:recase/recase.dart';
|
||||
import 'package:source_gen/src/annotation.dart';
|
||||
import 'package:source_gen/source_gen.dart';
|
||||
import 'angel_serialize.dart';
|
||||
|
||||
class JsonModelGenerator extends GeneratorForAnnotation<Serializable> {
|
||||
const JsonModelGenerator();
|
||||
|
||||
@override
|
||||
Future<String> generateForAnnotatedElement(
|
||||
Element element, Serializable annotation, BuildStep buildStep) async {
|
||||
if (element.kind != ElementKind.CLASS)
|
||||
throw 'Only classes can be annotated with a @Serializable() annotation.';
|
||||
var lib = generateSerializerLibrary(element);
|
||||
return prettyToSource(lib.buildAst());
|
||||
}
|
||||
|
||||
LibraryBuilder generateSerializerLibrary(ClassElement clazz) {
|
||||
var lib = new LibraryBuilder();
|
||||
lib.addMember(generateBaseModelClass(clazz));
|
||||
return lib;
|
||||
}
|
||||
|
||||
ClassBuilder generateBaseModelClass(ClassElement clazz) {
|
||||
if (!clazz.name.startsWith('_'))
|
||||
throw 'Classes annotated with @Serializable() must have names starting with a leading underscore.';
|
||||
|
||||
var genClassName = clazz.name.substring(1);
|
||||
var genClass =
|
||||
new ClassBuilder(genClassName, asExtends: new TypeBuilder(clazz.name));
|
||||
Map<String, DartType> fields = {};
|
||||
Map<String, String> aliases = {};
|
||||
|
||||
// Find all fields
|
||||
for (var field in clazz.fields) {
|
||||
// Skip if annotated with @exclude
|
||||
var excludeAnnotation = field.metadata.firstWhere(
|
||||
(ann) => matchAnnotation(Exclude, ann),
|
||||
orElse: () => null);
|
||||
|
||||
if (excludeAnnotation == null) {
|
||||
// Register the field
|
||||
fields[field.name] = field.type;
|
||||
|
||||
// Search for Alias
|
||||
var aliasAnnotation = field.metadata.firstWhere(
|
||||
(ann) => matchAnnotation(Alias, ann),
|
||||
orElse: () => null);
|
||||
if (aliasAnnotation != null) {
|
||||
var alias = instantiateAnnotation(aliasAnnotation) as Alias;
|
||||
aliases[field.name] = alias.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now, add all fields to the base class
|
||||
clazz.fields.forEach((field) {
|
||||
genClass.addField(
|
||||
varField(field.name, type: new TypeBuilder(field.type.displayName))
|
||||
..addAnnotation(reference('override')));
|
||||
});
|
||||
|
||||
// Create convenience constructor
|
||||
var convenienceConstructor = constructor(clazz.fields.map((field) {
|
||||
return thisField(named(parameter(field.name)));
|
||||
}));
|
||||
genClass.addConstructor(convenienceConstructor);
|
||||
|
||||
// Create toJson
|
||||
Map<String, ExpressionBuilder> toJsonFields = {};
|
||||
|
||||
fields.forEach((fieldName, type) {
|
||||
var resolvedName =
|
||||
aliases.containsKey(fieldName) ? aliases[fieldName] : fieldName;
|
||||
ExpressionBuilder value;
|
||||
|
||||
// DateTime
|
||||
if (type.name == 'DateTime') {
|
||||
value = reference(fieldName).equals(literal(null)).ternary(
|
||||
literal(null), reference(fieldName).invoke('toIso8601String', []));
|
||||
}
|
||||
|
||||
// Anything else
|
||||
else {
|
||||
value = reference(fieldName);
|
||||
}
|
||||
|
||||
toJsonFields[resolvedName] = value;
|
||||
});
|
||||
|
||||
var toJson = new MethodBuilder('toJson',
|
||||
returnType: new TypeBuilder('Map', genericTypes: [
|
||||
new TypeBuilder('String'),
|
||||
new TypeBuilder('dynamic')
|
||||
]),
|
||||
returns: map(toJsonFields));
|
||||
genClass.addMethod(toJson);
|
||||
|
||||
// Create factory [name].fromJson
|
||||
var fromJson = new ConstructorBuilder(name: 'fromJson', asFactory: true);
|
||||
fromJson.addPositional(parameter('data', [new TypeBuilder('Map')]));
|
||||
var namedParams =
|
||||
fields.keys.fold<Map<String, ExpressionBuilder>>({}, (out, fieldName) {
|
||||
var resolvedName =
|
||||
aliases.containsKey(fieldName) ? aliases[fieldName] : fieldName;
|
||||
var mapKey = reference('data')[literal(resolvedName)];
|
||||
ExpressionBuilder value = mapKey;
|
||||
var type = fields[fieldName];
|
||||
|
||||
// DateTime
|
||||
if (type.name == 'DateTime') {
|
||||
// map['foo'] is DateTime ? map['foo'] : (map['foo'] is String ? DateTime.parse(map['foo']) : null)
|
||||
var dt = new TypeBuilder('DateTime');
|
||||
value = mapKey.isInstanceOf(dt).ternary(
|
||||
mapKey,
|
||||
(mapKey.isInstanceOf(new TypeBuilder('String')).ternary(
|
||||
new TypeBuilder('DateTime').invoke('parse', [mapKey]),
|
||||
literal(null)))
|
||||
.parentheses());
|
||||
}
|
||||
|
||||
bool done = false;
|
||||
|
||||
// Handle List
|
||||
if (type.toString().contains('List') && type is ParameterizedType) {
|
||||
var listType = type.typeArguments.first;
|
||||
if (listType.element is ClassElement) {
|
||||
var genericClass = listType.element as ClassElement;
|
||||
String fromJsonClassName;
|
||||
bool hasFromJson =
|
||||
genericClass.constructors.any((c) => c.name == 'fromJson');
|
||||
|
||||
if (hasFromJson) {
|
||||
fromJsonClassName = genericClass.displayName;
|
||||
} else {
|
||||
// If it has a serializable annotation, act accordingly.
|
||||
if (genericClass.metadata
|
||||
.any((ann) => matchAnnotation(Serializable, ann))) {
|
||||
fromJsonClassName = genericClass.displayName.substring(1);
|
||||
hasFromJson = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for fromJson
|
||||
if (hasFromJson) {
|
||||
var outputType = new TypeBuilder(fromJsonClassName);
|
||||
var x = reference('x');
|
||||
value = mapKey.isInstanceOf(lib$core.List).ternary(
|
||||
mapKey.invoke('map', [
|
||||
new MethodBuilder.closure(
|
||||
returns: x.equals(literal(null)).ternary(
|
||||
literal(null),
|
||||
(x.isInstanceOf(outputType).ternary(
|
||||
x,
|
||||
outputType.newInstance([reference('x')],
|
||||
constructor: 'fromJson')))
|
||||
.parentheses()))
|
||||
..addPositional(parameter('x'))
|
||||
]).invoke('toList', []),
|
||||
literal(null));
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for fromJson
|
||||
if (!done && type.element is ClassElement) {
|
||||
String fromJsonClassName;
|
||||
var genericClass = type.element as ClassElement;
|
||||
bool hasFromJson =
|
||||
genericClass.constructors.any((c) => c.name == 'fromJson');
|
||||
|
||||
if (hasFromJson) {
|
||||
fromJsonClassName = type.displayName;
|
||||
} else {
|
||||
// If it has a serializable annotation, act accordingly.
|
||||
if (genericClass.metadata
|
||||
.any((ann) => matchAnnotation(Serializable, ann))) {
|
||||
fromJsonClassName = type.displayName.substring(1);
|
||||
hasFromJson = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for fromJson
|
||||
if (hasFromJson) {
|
||||
var outputType = new TypeBuilder(fromJsonClassName);
|
||||
value = mapKey.equals(literal(null)).ternary(
|
||||
literal(null),
|
||||
(mapKey.isInstanceOf(outputType).ternary(
|
||||
mapKey,
|
||||
outputType
|
||||
.newInstance([mapKey], constructor: 'fromJson')))
|
||||
.parentheses());
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Map...
|
||||
if (!done &&
|
||||
type.toString().contains('Map') &&
|
||||
type is ParameterizedType &&
|
||||
type.typeArguments.length >= 2) {
|
||||
var targetType = type.typeArguments[1];
|
||||
if (targetType.element is ClassElement) {
|
||||
String fromJsonClassName;
|
||||
var genericClass = targetType.element as ClassElement;
|
||||
bool hasFromJson =
|
||||
genericClass.constructors.any((c) => c.name == 'fromJson');
|
||||
|
||||
if (hasFromJson) {
|
||||
fromJsonClassName = targetType.displayName;
|
||||
} else {
|
||||
// If it has a serializable annotation, act accordingly.
|
||||
if (genericClass.metadata
|
||||
.any((ann) => matchAnnotation(Serializable, ann))) {
|
||||
fromJsonClassName = targetType.displayName.substring(1);
|
||||
hasFromJson = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for fromJson
|
||||
if (hasFromJson) {
|
||||
var outputType = new TypeBuilder(fromJsonClassName);
|
||||
var v = mapKey[reference('k')];
|
||||
value = mapKey.isInstanceOf(lib$core.Map).ternary(
|
||||
mapKey.property('keys').invoke('fold', [
|
||||
map({}),
|
||||
new MethodBuilder.closure()
|
||||
..addStatements([
|
||||
v
|
||||
.equals(literal(null))
|
||||
.ternary(
|
||||
literal(null),
|
||||
(v.isInstanceOf(outputType).ternary(
|
||||
v,
|
||||
outputType.newInstance([v],
|
||||
constructor: 'fromJson')))
|
||||
.parentheses())
|
||||
.asAssign(reference('out')[reference('k')]),
|
||||
reference('out').asReturn()
|
||||
])
|
||||
..addPositional(parameter('out'))
|
||||
..addPositional(parameter('k'))
|
||||
]),
|
||||
literal(null));
|
||||
} else {
|
||||
value = mapKey
|
||||
.isInstanceOf(lib$core.Map)
|
||||
.ternary(mapKey, literal(null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return out..[fieldName] = value;
|
||||
});
|
||||
fromJson.addStatement(new TypeBuilder(genClassName)
|
||||
.newInstance([], named: namedParams).asReturn());
|
||||
genClass.addConstructor(fromJson);
|
||||
|
||||
// Create `parse` to just forward
|
||||
var parseMethod = new MethodBuilder('parse',
|
||||
returnType: new TypeBuilder(genClassName),
|
||||
returns: new TypeBuilder(genClassName)
|
||||
.newInstance([reference('map')], constructor: 'fromJson'));
|
||||
parseMethod.addPositional(parameter('map', [new TypeBuilder('Map')]));
|
||||
genClass.addMethod(parseMethod, asStatic: true);
|
||||
|
||||
return genClass;
|
||||
}
|
||||
}
|
14
pubspec.yaml
Normal file
14
pubspec.yaml
Normal file
|
@ -0,0 +1,14 @@
|
|||
name: angel_serialize
|
||||
version: 0.0.0
|
||||
description: Model serialization generators.
|
||||
author: Tobe O <thosakwe@gmail.com>
|
||||
homepage: https://github.com/angel-dart/serialize
|
||||
dependencies:
|
||||
code_builder: ^1.0.0
|
||||
id: ^1.0.0
|
||||
recase: ^1.0.0
|
||||
source_gen: ^0.5.8
|
||||
dev_dependencies:
|
||||
angel_framework: ^1.0.0
|
||||
build_runner: ^0.3.0
|
||||
test: ">= 0.12.13 < 0.13.0"
|
114
test/book_test.dart
Normal file
114
test/book_test.dart
Normal file
|
@ -0,0 +1,114 @@
|
|||
import 'dart:convert';
|
||||
import 'package:test/test.dart';
|
||||
import 'models/book.dart';
|
||||
|
||||
const String DEATHLY_HALLOWS_ISBN = '0-545-01022-5';
|
||||
|
||||
main() {
|
||||
var deathlyHallows = new Book(
|
||||
id: '0',
|
||||
author: 'J.K. Rowling',
|
||||
title: 'Harry Potter and the Deathly Hallows',
|
||||
description: 'The 7th book.',
|
||||
pageCount: 759,
|
||||
updatedAt: new DateTime.now());
|
||||
var serializedDeathlyHallows = deathlyHallows.toJson();
|
||||
print('Deathly Hallows: $serializedDeathlyHallows');
|
||||
|
||||
var jkRowling = new Author(
|
||||
id: '1',
|
||||
name: 'J.K. Rowling',
|
||||
age: 51,
|
||||
books: [deathlyHallows],
|
||||
newestBook: deathlyHallows);
|
||||
Map serializedJkRowling = JSON.decode(JSON.encode(jkRowling.toJson()));
|
||||
Map deathlyHallowsMap = JSON.decode(JSON.encode(serializedDeathlyHallows));
|
||||
print('J.K. Rowling: $serializedJkRowling');
|
||||
|
||||
var library = new Library(collection: {DEATHLY_HALLOWS_ISBN: deathlyHallows});
|
||||
var serializedLibrary = JSON.decode(JSON.encode(library.toJson()));
|
||||
print('Library: $serializedLibrary');
|
||||
|
||||
group('serialization', () {
|
||||
test('serialization sets proper fields', () {
|
||||
expect(serializedDeathlyHallows['id'], deathlyHallows.id);
|
||||
expect(serializedDeathlyHallows['author'], deathlyHallows.author);
|
||||
expect(
|
||||
serializedDeathlyHallows['description'], deathlyHallows.description);
|
||||
expect(serializedDeathlyHallows['page_count'], deathlyHallows.pageCount);
|
||||
expect(serializedDeathlyHallows['createdAt'], isNull);
|
||||
expect(serializedDeathlyHallows['updatedAt'],
|
||||
deathlyHallows.updatedAt.toIso8601String());
|
||||
});
|
||||
|
||||
test('heeds @Alias', () {
|
||||
expect(serializedDeathlyHallows['page_count'], deathlyHallows.pageCount);
|
||||
expect(serializedDeathlyHallows.keys, isNot(contains('pageCount')));
|
||||
});
|
||||
|
||||
test('heeds @exclude', () {
|
||||
expect(serializedJkRowling.keys, isNot(contains('secret')));
|
||||
});
|
||||
|
||||
test('nested @serializable class is serialized', () {
|
||||
expect(serializedJkRowling['newest_book'], deathlyHallowsMap);
|
||||
});
|
||||
|
||||
test('list of nested @serializable class is serialized', () {
|
||||
expect(serializedJkRowling['books'], [deathlyHallowsMap]);
|
||||
});
|
||||
|
||||
test('map with @serializable class as second key is serialized', () {
|
||||
expect(serializedLibrary['collection'],
|
||||
{DEATHLY_HALLOWS_ISBN: deathlyHallowsMap});
|
||||
});
|
||||
});
|
||||
|
||||
group('deserialization', () {
|
||||
test('deserialization sets proper fields', () {
|
||||
var book = new Book.fromJson(deathlyHallowsMap);
|
||||
expect(book.id, deathlyHallows.id);
|
||||
expect(book.author, deathlyHallows.author);
|
||||
expect(book.description, deathlyHallows.description);
|
||||
expect(book.pageCount, deathlyHallows.pageCount);
|
||||
expect(book.createdAt, isNull);
|
||||
expect(book.updatedAt, deathlyHallows.updatedAt);
|
||||
});
|
||||
|
||||
group('nested @serializable', () {
|
||||
var author = new Author.fromJson(serializedJkRowling);
|
||||
|
||||
test('nested @serializable class is deserialized', () {
|
||||
var newestBook = author.newestBook;
|
||||
expect(newestBook, isNotNull);
|
||||
expect(newestBook.id, deathlyHallows.id);
|
||||
expect(newestBook.pageCount, deathlyHallows.pageCount);
|
||||
expect(newestBook.updatedAt, deathlyHallows.updatedAt);
|
||||
});
|
||||
|
||||
test('list of nested @serializable class is deserialized', () {
|
||||
expect(author.books, allOf(isList, isNotEmpty, hasLength(1)));
|
||||
var book = author.books.first;
|
||||
expect(book.id, deathlyHallows.id);
|
||||
expect(book.author, deathlyHallows.author);
|
||||
expect(book.description, deathlyHallows.description);
|
||||
expect(book.pageCount, deathlyHallows.pageCount);
|
||||
expect(book.createdAt, isNull);
|
||||
expect(book.updatedAt, deathlyHallows.updatedAt);
|
||||
});
|
||||
|
||||
test('map with @serializable class as second key is deserialized', () {
|
||||
var lib = new Library.fromJson(serializedLibrary);
|
||||
expect(lib.collection, allOf(isNotEmpty, hasLength(1)));
|
||||
expect(lib.collection.keys.first, DEATHLY_HALLOWS_ISBN);
|
||||
var book = lib.collection[DEATHLY_HALLOWS_ISBN];
|
||||
expect(book.id, deathlyHallows.id);
|
||||
expect(book.author, deathlyHallows.author);
|
||||
expect(book.description, deathlyHallows.description);
|
||||
expect(book.pageCount, deathlyHallows.pageCount);
|
||||
expect(book.createdAt, isNull);
|
||||
expect(book.updatedAt, deathlyHallows.updatedAt);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
50
test/models/book.dart
Normal file
50
test/models/book.dart
Normal file
|
@ -0,0 +1,50 @@
|
|||
library angel_serialize.test.models.book;
|
||||
|
||||
import 'package:angel_framework/common.dart';
|
||||
import 'package:angel_serialize/angel_serialize.dart';
|
||||
part 'book.g.dart';
|
||||
|
||||
@serializable
|
||||
abstract class _Book extends Model {
|
||||
@override
|
||||
String id;
|
||||
String author, title, description;
|
||||
|
||||
@Alias('page_count')
|
||||
int pageCount;
|
||||
|
||||
@override
|
||||
DateTime createdAt, updatedAt;
|
||||
}
|
||||
|
||||
@serializable
|
||||
abstract class _Author extends Model {
|
||||
@override
|
||||
String id;
|
||||
|
||||
String name;
|
||||
|
||||
int age;
|
||||
|
||||
@override
|
||||
DateTime createdAt, updatedAt;
|
||||
|
||||
List<_Book> books;
|
||||
|
||||
@Alias('newest_book')
|
||||
_Book newestBook;
|
||||
|
||||
@exclude
|
||||
String secret;
|
||||
}
|
||||
|
||||
@serializable
|
||||
abstract class _Library extends Model {
|
||||
@override
|
||||
String id;
|
||||
|
||||
@override
|
||||
DateTime createdAt, updatedAt;
|
||||
|
||||
Map<String, _Book> collection;
|
||||
}
|
207
test/models/book.g.dart
Normal file
207
test/models/book.g.dart
Normal file
|
@ -0,0 +1,207 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of angel_serialize.test.models.book;
|
||||
|
||||
// **************************************************************************
|
||||
// Generator: JsonModelGenerator
|
||||
// Target: abstract class _Book
|
||||
// **************************************************************************
|
||||
|
||||
class Book extends _Book {
|
||||
@override
|
||||
String id;
|
||||
|
||||
@override
|
||||
String author;
|
||||
|
||||
@override
|
||||
String title;
|
||||
|
||||
@override
|
||||
String description;
|
||||
|
||||
@override
|
||||
int pageCount;
|
||||
|
||||
@override
|
||||
DateTime createdAt;
|
||||
|
||||
@override
|
||||
DateTime updatedAt;
|
||||
|
||||
Book(
|
||||
{this.id,
|
||||
this.author,
|
||||
this.title,
|
||||
this.description,
|
||||
this.pageCount,
|
||||
this.createdAt,
|
||||
this.updatedAt});
|
||||
|
||||
factory Book.fromJson(Map data) {
|
||||
return new Book(
|
||||
id: data['id'],
|
||||
author: data['author'],
|
||||
title: data['title'],
|
||||
description: data['description'],
|
||||
pageCount: data['page_count'],
|
||||
createdAt: data['createdAt'] is DateTime
|
||||
? data['createdAt']
|
||||
: (data['createdAt'] is String
|
||||
? DateTime.parse(data['createdAt'])
|
||||
: null),
|
||||
updatedAt: data['updatedAt'] is DateTime
|
||||
? data['updatedAt']
|
||||
: (data['updatedAt'] is String
|
||||
? DateTime.parse(data['updatedAt'])
|
||||
: null));
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'id': id,
|
||||
'author': author,
|
||||
'title': title,
|
||||
'description': description,
|
||||
'page_count': pageCount,
|
||||
'createdAt': createdAt == null ? null : createdAt.toIso8601String(),
|
||||
'updatedAt': updatedAt == null ? null : updatedAt.toIso8601String()
|
||||
};
|
||||
|
||||
static Book parse(Map map) => new Book.fromJson(map);
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
// Generator: JsonModelGenerator
|
||||
// Target: abstract class _Author
|
||||
// **************************************************************************
|
||||
|
||||
class Author extends _Author {
|
||||
@override
|
||||
String id;
|
||||
|
||||
@override
|
||||
String name;
|
||||
|
||||
@override
|
||||
int age;
|
||||
|
||||
@override
|
||||
DateTime createdAt;
|
||||
|
||||
@override
|
||||
DateTime updatedAt;
|
||||
|
||||
@override
|
||||
List<_Book> books;
|
||||
|
||||
@override
|
||||
_Book newestBook;
|
||||
|
||||
@override
|
||||
String secret;
|
||||
|
||||
Author(
|
||||
{this.id,
|
||||
this.name,
|
||||
this.age,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
this.books,
|
||||
this.newestBook,
|
||||
this.secret});
|
||||
|
||||
factory Author.fromJson(Map data) {
|
||||
return new Author(
|
||||
id: data['id'],
|
||||
name: data['name'],
|
||||
age: data['age'],
|
||||
createdAt: data['createdAt'] is DateTime
|
||||
? data['createdAt']
|
||||
: (data['createdAt'] is String
|
||||
? DateTime.parse(data['createdAt'])
|
||||
: null),
|
||||
updatedAt: data['updatedAt'] is DateTime
|
||||
? data['updatedAt']
|
||||
: (data['updatedAt'] is String
|
||||
? DateTime.parse(data['updatedAt'])
|
||||
: null),
|
||||
books: data['books'] is List
|
||||
? data['books']
|
||||
.map((x) =>
|
||||
x == null ? null : (x is Book ? x : new Book.fromJson(x)))
|
||||
.toList()
|
||||
: null,
|
||||
newestBook: data['newest_book'] == null
|
||||
? null
|
||||
: (data['newest_book'] is Book
|
||||
? data['newest_book']
|
||||
: new Book.fromJson(data['newest_book'])));
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'id': id,
|
||||
'name': name,
|
||||
'age': age,
|
||||
'createdAt': createdAt == null ? null : createdAt.toIso8601String(),
|
||||
'updatedAt': updatedAt == null ? null : updatedAt.toIso8601String(),
|
||||
'books': books,
|
||||
'newest_book': newestBook
|
||||
};
|
||||
|
||||
static Author parse(Map map) => new Author.fromJson(map);
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
// Generator: JsonModelGenerator
|
||||
// Target: abstract class _Library
|
||||
// **************************************************************************
|
||||
|
||||
class Library extends _Library {
|
||||
@override
|
||||
String id;
|
||||
|
||||
@override
|
||||
DateTime createdAt;
|
||||
|
||||
@override
|
||||
DateTime updatedAt;
|
||||
|
||||
@override
|
||||
Map<String, _Book> collection;
|
||||
|
||||
Library({this.id, this.createdAt, this.updatedAt, this.collection});
|
||||
|
||||
factory Library.fromJson(Map data) {
|
||||
return new Library(
|
||||
id: data['id'],
|
||||
createdAt: data['createdAt'] is DateTime
|
||||
? data['createdAt']
|
||||
: (data['createdAt'] is String
|
||||
? DateTime.parse(data['createdAt'])
|
||||
: null),
|
||||
updatedAt: data['updatedAt'] is DateTime
|
||||
? data['updatedAt']
|
||||
: (data['updatedAt'] is String
|
||||
? DateTime.parse(data['updatedAt'])
|
||||
: null),
|
||||
collection: data['collection'] is Map
|
||||
? data['collection'].keys.fold({}, (out, k) {
|
||||
out[k] = data['collection'][k] == null
|
||||
? null
|
||||
: (data['collection'][k] is Book
|
||||
? data['collection'][k]
|
||||
: new Book.fromJson(data['collection'][k]));
|
||||
return out;
|
||||
})
|
||||
: null);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'id': id,
|
||||
'createdAt': createdAt == null ? null : createdAt.toIso8601String(),
|
||||
'updatedAt': updatedAt == null ? null : updatedAt.toIso8601String(),
|
||||
'collection': collection
|
||||
};
|
||||
|
||||
static Library parse(Map map) => new Library.fromJson(map);
|
||||
}
|
4
tool/build.dart
Normal file
4
tool/build.dart
Normal file
|
@ -0,0 +1,4 @@
|
|||
import 'package:build_runner/build_runner.dart';
|
||||
import 'phases.dart';
|
||||
|
||||
main() => build(PHASES, deleteFilesByDefault: true);
|
7
tool/phases.dart
Normal file
7
tool/phases.dart
Normal file
|
@ -0,0 +1,7 @@
|
|||
import 'package:build_runner/build_runner.dart';
|
||||
import 'package:source_gen/source_gen.dart';
|
||||
import 'package:angel_serialize/builder.dart';
|
||||
|
||||
final PhaseGroup PHASES = new PhaseGroup.singleAction(
|
||||
new GeneratorBuilder([const JsonModelGenerator()]),
|
||||
new InputSet('angel_serialize', const ['test/models/*.dart']));
|
4
tool/watch.dart
Normal file
4
tool/watch.dart
Normal file
|
@ -0,0 +1,4 @@
|
|||
import 'package:build_runner/build_runner.dart';
|
||||
import 'phases.dart';
|
||||
|
||||
main() => watch(PHASES, deleteFilesByDefault: true);
|
Loading…
Reference in a new issue