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
|
# Directory created by dartdoc
|
||||||
# If you don't generate documentation locally you can remove this line.
|
# If you don't generate documentation locally you can remove this line.
|
||||||
doc/api/
|
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