fromMap
This commit is contained in:
parent
64cd59c345
commit
2210cde81c
5 changed files with 171 additions and 23 deletions
|
@ -24,6 +24,7 @@ TypeReference convertTypeReference(DartType t) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Determines if a type supports `package:angel_serialize`.
|
||||||
bool isModelClass(DartType t) {
|
bool isModelClass(DartType t) {
|
||||||
if (t == null) return false;
|
if (t == null) return false;
|
||||||
|
|
||||||
|
@ -35,4 +36,11 @@ bool isModelClass(DartType t) {
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determines if a [DartType] is a `Map` with the second type argument being a `Model`.
|
||||||
|
bool isMapToModelType(InterfaceType t) {
|
||||||
|
return t.name == 'Map' &&
|
||||||
|
t.typeArguments.length == 2 &&
|
||||||
|
isModelClass(t.typeArguments[1]);
|
||||||
}
|
}
|
|
@ -40,13 +40,12 @@ class SerializerGenerator extends GeneratorForAnnotation<Serializable> {
|
||||||
..abstract = true;
|
..abstract = true;
|
||||||
|
|
||||||
if (serializers.contains(Serializers.map)) {
|
if (serializers.contains(Serializers.map)) {
|
||||||
// TODO: Generate fromMap
|
generateFromMapMethod(clazz, ctx, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (serializers.contains(Serializers.map) ||
|
if (serializers.contains(Serializers.map) ||
|
||||||
serializers.contains(Serializers.json)) {
|
serializers.contains(Serializers.json)) {
|
||||||
generateToMapMethod(clazz, ctx, file);
|
generateToMapMethod(clazz, ctx, file);
|
||||||
// TODO: Generate toJson
|
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -80,27 +79,26 @@ class SerializerGenerator extends GeneratorForAnnotation<Serializable> {
|
||||||
|
|
||||||
// Serialize dates
|
// Serialize dates
|
||||||
if (dateTimeTypeChecker.isAssignableFromType(field.type))
|
if (dateTimeTypeChecker.isAssignableFromType(field.type))
|
||||||
serializedRepresentation = 'model.${field.name}.toIso8601String()';
|
serializedRepresentation = 'model.${field.name}?.toIso8601String()';
|
||||||
|
|
||||||
// Serialize model classes via `XSerializer.toMap`
|
// Serialize model classes via `XSerializer.toMap`
|
||||||
else if (isModelClass(field.type)) {
|
else if (isModelClass(field.type)) {
|
||||||
var rc = new ReCase(field.type.name);
|
var rc = new ReCase(field.type.name);
|
||||||
serializedRepresentation =
|
serializedRepresentation =
|
||||||
'${rc.pascalCase}Serializer.toMap(model.${field.name})';
|
'${rc.pascalCase}Serializer.toMap(model.${field.name})';
|
||||||
}
|
} else if (field.type is InterfaceType) {
|
||||||
|
|
||||||
else if (field.type is InterfaceType) {
|
|
||||||
var t = field.type as InterfaceType;
|
var t = field.type as InterfaceType;
|
||||||
|
|
||||||
if (t.name == 'List' && t.typeArguments.length == 1) {
|
if (t.name == 'List' && t.typeArguments.length == 1) {
|
||||||
var rc = new ReCase(t.typeArguments[0].name);
|
var rc = new ReCase(t.typeArguments[0].name);
|
||||||
serializedRepresentation = 'model.${field.name}.map(${rc.pascalCase}Serializer.toMap).toList()';
|
serializedRepresentation = 'model.${field.name}?.map(${rc
|
||||||
}
|
.pascalCase}Serializer.toMap)?.toList()';
|
||||||
|
} else if (isMapToModelType(t)) {
|
||||||
else if (t.name == 'Map' && t.typeArguments.length == 2 && isModelClass(t.typeArguments[1])) {
|
|
||||||
var rc = new ReCase(t.typeArguments[1].name);
|
var rc = new ReCase(t.typeArguments[1].name);
|
||||||
serializedRepresentation = '''model.${field.name}.keys.fold({}, (map, key) {
|
serializedRepresentation =
|
||||||
return map..[key] = ${rc.pascalCase}Serializer.toMap(model.${field.name}[key]);
|
'''model.${field.name}.keys?.fold({}, (map, key) {
|
||||||
|
return map..[key] = ${rc.pascalCase}Serializer.toMap(model.${field
|
||||||
|
.name}[key]);
|
||||||
})''';
|
})''';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,4 +110,77 @@ class SerializerGenerator extends GeneratorForAnnotation<Serializable> {
|
||||||
method.body = new Code(buf.toString());
|
method.body = new Code(buf.toString());
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void generateFromMapMethod(
|
||||||
|
ClassBuilder clazz, BuildContext ctx, FileBuilder file) {
|
||||||
|
clazz.methods.add(new Method((method) {
|
||||||
|
method
|
||||||
|
..static = true
|
||||||
|
..name = 'fromMap'
|
||||||
|
..returns = ctx.modelClassType
|
||||||
|
..requiredParameters.add(
|
||||||
|
new Parameter((b) => b
|
||||||
|
..name = 'map'
|
||||||
|
..type = new Reference('Map')),
|
||||||
|
);
|
||||||
|
|
||||||
|
var buf = new StringBuffer('return new ${ctx.modelClassName}(');
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
// Add named parameters
|
||||||
|
for (var field in ctx.fields) {
|
||||||
|
if (ctx.excluded[field.name] == true) continue;
|
||||||
|
|
||||||
|
var alias = ctx.resolveFieldName(field.name);
|
||||||
|
method.optionalParameters.add(new Parameter((b) {
|
||||||
|
b
|
||||||
|
..name = field.name
|
||||||
|
..named = true
|
||||||
|
..type = convertTypeReference(field.type);
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (i++ > 0) buf.write(', ');
|
||||||
|
|
||||||
|
String deserializedRepresentation = "map['$alias']";
|
||||||
|
|
||||||
|
// Deserialize dates
|
||||||
|
if (dateTimeTypeChecker.isAssignableFromType(field.type))
|
||||||
|
deserializedRepresentation =
|
||||||
|
"map['$alias'] != null ? DateTime.parse(map['$alias']) : null";
|
||||||
|
|
||||||
|
// Serialize model classes via `XSerializer.toMap`
|
||||||
|
else if (isModelClass(field.type)) {
|
||||||
|
var rc = new ReCase(field.type.name);
|
||||||
|
deserializedRepresentation = "map['$alias'] != null"
|
||||||
|
" ? ${rc.pascalCase}Serializer.fromMap(map['$alias'])"
|
||||||
|
" : null";
|
||||||
|
} else if (field.type is InterfaceType) {
|
||||||
|
var t = field.type as InterfaceType;
|
||||||
|
|
||||||
|
if (t.name == 'List' && t.typeArguments.length == 1) {
|
||||||
|
var rc = new ReCase(t.typeArguments[0].name);
|
||||||
|
deserializedRepresentation = "map['$alias'] is Iterable"
|
||||||
|
" ? map['$alias'].map(${rc
|
||||||
|
.pascalCase}Serializer.fromMap).toList()"
|
||||||
|
" : null";
|
||||||
|
} else if (isMapToModelType(t)) {
|
||||||
|
var rc = new ReCase(t.typeArguments[1].name);
|
||||||
|
deserializedRepresentation = '''
|
||||||
|
map['$alias'] is Map
|
||||||
|
? map['$alias'].keys.fold({}, (out, key) {
|
||||||
|
return out..[key] = ${rc
|
||||||
|
.pascalCase}Serializer.fromMap(map['$alias'][key]);
|
||||||
|
})
|
||||||
|
: null
|
||||||
|
''';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.write('${field.name}: $deserializedRepresentation');
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.write(');');
|
||||||
|
method.body = new Code(buf.toString());
|
||||||
|
}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,7 +66,7 @@ main() {
|
||||||
|
|
||||||
group('deserialization', () {
|
group('deserialization', () {
|
||||||
test('deserialization sets proper fields', () {
|
test('deserialization sets proper fields', () {
|
||||||
var book = new Book.fromJson(deathlyHallowsMap);
|
var book = BookSerializer.fromMap(deathlyHallowsMap);
|
||||||
expect(book.id, deathlyHallows.id);
|
expect(book.id, deathlyHallows.id);
|
||||||
expect(book.author, deathlyHallows.author);
|
expect(book.author, deathlyHallows.author);
|
||||||
expect(book.description, deathlyHallows.description);
|
expect(book.description, deathlyHallows.description);
|
||||||
|
@ -76,7 +76,7 @@ main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
group('nested @serializable', () {
|
group('nested @serializable', () {
|
||||||
var author = new Author.fromJson(serializedJkRowling);
|
var author = AuthorSerializer.fromMap(serializedJkRowling);
|
||||||
|
|
||||||
test('nested @serializable class is deserialized', () {
|
test('nested @serializable class is deserialized', () {
|
||||||
var newestBook = author.newestBook;
|
var newestBook = author.newestBook;
|
||||||
|
@ -98,7 +98,7 @@ main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('map with @serializable class as second key is deserialized', () {
|
test('map with @serializable class as second key is deserialized', () {
|
||||||
var lib = new Library.fromJson(serializedLibrary);
|
var lib = LibrarySerializer.fromMap(serializedLibrary);
|
||||||
expect(lib.collection, allOf(isNotEmpty, hasLength(1)));
|
expect(lib.collection, allOf(isNotEmpty, hasLength(1)));
|
||||||
expect(lib.collection.keys.first, deathlyHallowsIsbn);
|
expect(lib.collection.keys.first, deathlyHallowsIsbn);
|
||||||
var book = lib.collection[deathlyHallowsIsbn];
|
var book = lib.collection[deathlyHallowsIsbn];
|
||||||
|
|
|
@ -7,28 +7,75 @@ part of angel_serialize.test.models.author;
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
abstract class AuthorSerializer {
|
abstract class AuthorSerializer {
|
||||||
|
static Author fromMap(Map map,
|
||||||
|
{String id,
|
||||||
|
String name,
|
||||||
|
int age,
|
||||||
|
List<Book> books,
|
||||||
|
Book newestBook,
|
||||||
|
DateTime createdAt,
|
||||||
|
DateTime updatedAt}) {
|
||||||
|
return new Author(
|
||||||
|
id: map['id'],
|
||||||
|
name: map['name'],
|
||||||
|
age: map['age'],
|
||||||
|
books: map['books'] is Iterable
|
||||||
|
? map['books'].map(BookSerializer.fromMap).toList()
|
||||||
|
: null,
|
||||||
|
newestBook: map['newest_book'] != null
|
||||||
|
? BookSerializer.fromMap(map['newest_book'])
|
||||||
|
: null,
|
||||||
|
createdAt: map['created_at'] != null
|
||||||
|
? DateTime.parse(map['created_at'])
|
||||||
|
: null,
|
||||||
|
updatedAt: map['updated_at'] != null
|
||||||
|
? DateTime.parse(map['updated_at'])
|
||||||
|
: null);
|
||||||
|
}
|
||||||
|
|
||||||
static Map<String, dynamic> toMap(Author model) {
|
static Map<String, dynamic> toMap(Author model) {
|
||||||
return {
|
return {
|
||||||
'id': model.id,
|
'id': model.id,
|
||||||
'name': model.name,
|
'name': model.name,
|
||||||
'age': model.age,
|
'age': model.age,
|
||||||
'books': model.books.map(BookSerializer.toMap).toList(),
|
'books': model.books?.map(BookSerializer.toMap)?.toList(),
|
||||||
'newest_book': BookSerializer.toMap(model.newestBook),
|
'newest_book': BookSerializer.toMap(model.newestBook),
|
||||||
'created_at': model.createdAt.toIso8601String(),
|
'created_at': model.createdAt?.toIso8601String(),
|
||||||
'updated_at': model.updatedAt.toIso8601String()
|
'updated_at': model.updatedAt?.toIso8601String()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class LibrarySerializer {
|
abstract class LibrarySerializer {
|
||||||
|
static Library fromMap(Map map,
|
||||||
|
{String id,
|
||||||
|
Map<String, Book> collection,
|
||||||
|
DateTime createdAt,
|
||||||
|
DateTime updatedAt}) {
|
||||||
|
return new Library(
|
||||||
|
id: map['id'],
|
||||||
|
collection: map['collection'] is Map
|
||||||
|
? map['collection'].keys.fold({}, (out, key) {
|
||||||
|
return out
|
||||||
|
..[key] = BookSerializer.fromMap(map['collection'][key]);
|
||||||
|
})
|
||||||
|
: null,
|
||||||
|
createdAt: map['created_at'] != null
|
||||||
|
? DateTime.parse(map['created_at'])
|
||||||
|
: null,
|
||||||
|
updatedAt: map['updated_at'] != null
|
||||||
|
? DateTime.parse(map['updated_at'])
|
||||||
|
: null);
|
||||||
|
}
|
||||||
|
|
||||||
static Map<String, dynamic> toMap(Library model) {
|
static Map<String, dynamic> toMap(Library model) {
|
||||||
return {
|
return {
|
||||||
'id': model.id,
|
'id': model.id,
|
||||||
'collection': model.collection.keys.fold({}, (map, key) {
|
'collection': model.collection.keys?.fold({}, (map, key) {
|
||||||
return map..[key] = BookSerializer.toMap(model.collection[key]);
|
return map..[key] = BookSerializer.toMap(model.collection[key]);
|
||||||
}),
|
}),
|
||||||
'created_at': model.createdAt.toIso8601String(),
|
'created_at': model.createdAt?.toIso8601String(),
|
||||||
'updated_at': model.updatedAt.toIso8601String()
|
'updated_at': model.updatedAt?.toIso8601String()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,28 @@ part of angel_serialize.test.models.book;
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
abstract class BookSerializer {
|
abstract class BookSerializer {
|
||||||
|
static Book fromMap(Map map,
|
||||||
|
{String id,
|
||||||
|
String author,
|
||||||
|
String title,
|
||||||
|
String description,
|
||||||
|
int pageCount,
|
||||||
|
DateTime createdAt,
|
||||||
|
DateTime updatedAt}) {
|
||||||
|
return new Book(
|
||||||
|
id: map['id'],
|
||||||
|
author: map['author'],
|
||||||
|
title: map['title'],
|
||||||
|
description: map['description'],
|
||||||
|
pageCount: map['page_count'],
|
||||||
|
createdAt: map['created_at'] != null
|
||||||
|
? DateTime.parse(map['created_at'])
|
||||||
|
: null,
|
||||||
|
updatedAt: map['updated_at'] != null
|
||||||
|
? DateTime.parse(map['updated_at'])
|
||||||
|
: null);
|
||||||
|
}
|
||||||
|
|
||||||
static Map<String, dynamic> toMap(Book model) {
|
static Map<String, dynamic> toMap(Book model) {
|
||||||
return {
|
return {
|
||||||
'id': model.id,
|
'id': model.id,
|
||||||
|
@ -14,8 +36,8 @@ abstract class BookSerializer {
|
||||||
'title': model.title,
|
'title': model.title,
|
||||||
'description': model.description,
|
'description': model.description,
|
||||||
'page_count': model.pageCount,
|
'page_count': model.pageCount,
|
||||||
'created_at': model.createdAt.toIso8601String(),
|
'created_at': model.createdAt?.toIso8601String(),
|
||||||
'updated_at': model.updatedAt.toIso8601String()
|
'updated_at': model.updatedAt?.toIso8601String()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue