diff --git a/example/example.g.dart b/example/example.g.dart index 880685df..c8782934 100644 --- a/example/example.g.dart +++ b/example/example.g.dart @@ -22,7 +22,7 @@ abstract class _$PersonSerializerMixin { String get middleName; String get lastName; DateTime get dateOfBirth; - List get orders; + List get orders; Map toJson() => { 'firstName': firstName, 'middleName': middleName, diff --git a/lib/generators/json_serializable_generator.dart b/lib/generators/json_serializable_generator.dart index 16c7f205..e322a9a1 100644 --- a/lib/generators/json_serializable_generator.dart +++ b/lib/generators/json_serializable_generator.dart @@ -88,8 +88,7 @@ class JsonSerializableGenerator // write fields fields.forEach((name, field) { //TODO - handle aliased imports - //TODO - write generic types. Now `List` turns into `List` - buffer.writeln(' ${field.type.name} get $name;'); + buffer.writeln(' ${field.type} get $name;'); }); // write toJson method @@ -204,26 +203,33 @@ class JsonSerializableGenerator } } - if (_coreListChecker.isAssignableFromType(fieldType)) { - var indexVal = "i${depth}"; - - var substitute = '${expression}[$indexVal]'; + var isList = false; + if (_coreIterableChecker.isAssignableFromType(fieldType)) { + if (_coreListChecker.isAssignableFromType(fieldType)) { + isList = true; + } + var substitute = "v$depth"; var subFieldValue = _fieldToJsonMapValue(substitute, _getIterableGenericType(fieldType as InterfaceType), depth + 1); - // If we're dealing with `List` where `T` must be serialized, then - // generate a value that does the equivalent of .map(...).toList(), but - // Does so efficiently by creating a known-length List. - //TODO(kevmoo) this might be overkill. I think .map on iterable returns - // an efficient-length iterable, so .map(...).toList() might be fine. :-/ + // In the case of trivial JSON types (int, String, etc), `subFieldValue` + // will be identical to `substitute` – so no explicit mapping is needed. + // If they are not equal, then we to write out the substitution. if (subFieldValue != substitute) { // TODO: the type could be imported from a library with a prefix! - return "${expression} == null ? null : " - "new List.generate(${expression}.length, " - "(int $indexVal) => $subFieldValue)"; + expression = "${expression}?.map(($substitute) => $subFieldValue)"; + + // expression now represents an Iterable (even if it started as a List + // ...resetting `isList` to `false`. + isList = false; } } + if (!isList && _coreIterableChecker.isAssignableFromType(fieldType)) { + // Then we need to add `?.toList() + expression += "?.toList()"; + } + return expression; } @@ -252,6 +258,13 @@ class JsonSerializableGenerator _getIterableGenericType(searchType as InterfaceType); var itemVal = "v$depth"; + var itemSubVal = + _writeAccessToVar(itemVal, iterableGenericType, depth: depth + 1); + + // If `itemSubVal` is the same, then we don't need to do anything fancy + if (itemVal == itemSubVal) { + return varExpression; + } var output = "($varExpression as List)?.map(($itemVal) => " "${_writeAccessToVar(itemVal, iterableGenericType, depth: depth+1)}" @@ -264,7 +277,7 @@ class JsonSerializableGenerator return output; } - if (!searchType.isDynamic) { + if (!searchType.isDynamic && !searchType.isObject) { return "$varExpression as $searchType"; } diff --git a/test/json_serializable_test.dart b/test/json_serializable_test.dart index fc29dd31..a06a2dad 100644 --- a/test/json_serializable_test.dart +++ b/test/json_serializable_test.dart @@ -94,7 +94,7 @@ void main() { await _getClassForCodeString('ParentObjectWithDynamicChildren'); var output = await _generator.generate(element, null); - expect(output, contains('(json[\'children\'] as List)?.map(')); + expect(output, contains('children = json[\'children\'];')); }); test('class with list of int is cast for strong mode', () async { diff --git a/test/test_files/json_test_example.dart b/test/test_files/json_test_example.dart index 09fa4d72..d420c410 100644 --- a/test/test_files/json_test_example.dart +++ b/test/test_files/json_test_example.dart @@ -11,6 +11,26 @@ import 'package:source_gen/generators/json_serializable.dart'; part 'json_test_example.g.dart'; +@JsonSerializable() +class ListGenericTests extends Object with _$ListGenericTestsSerializerMixin { + ListGenericTests(); + + factory ListGenericTests.fromJson(Map json) => + _$ListGenericTestsFromJson(json); + + Iterable iterable; + Iterable dynamicIterable; + Iterable objectIterable; + Iterable intIterable; + Iterable dateTimeIterable; + + List list; + List dynamicList; + List objectList; + List intList; + List dateTimeList; +} + @JsonSerializable() class Person extends Object with _$PersonSerializerMixin { final String firstName, middleName, lastName; diff --git a/test/test_files/json_test_example.g.dart b/test/test_files/json_test_example.g.dart index 2adb3fa1..344832d5 100644 --- a/test/test_files/json_test_example.g.dart +++ b/test/test_files/json_test_example.g.dart @@ -2,6 +2,53 @@ part of source_gen.test.example; +// ************************************************************************** +// Generator: JsonSerializableGenerator +// Target: class ListGenericTests +// ************************************************************************** + +ListGenericTests _$ListGenericTestsFromJson(Map json) => new ListGenericTests() + ..iterable = json['iterable'] + ..dynamicIterable = json['dynamicIterable'] + ..objectIterable = json['objectIterable'] + ..intIterable = (json['intIterable'] as List)?.map((v0) => v0 as int) + ..dateTimeIterable = (json['dateTimeIterable'] as List) + ?.map((v0) => v0 == null ? null : DateTime.parse(v0)) + ..list = json['list'] + ..dynamicList = json['dynamicList'] + ..objectList = json['objectList'] + ..intList = (json['intList'] as List)?.map((v0) => v0 as int)?.toList() + ..dateTimeList = (json['dateTimeList'] as List) + ?.map((v0) => v0 == null ? null : DateTime.parse(v0)) + ?.toList(); + +abstract class _$ListGenericTestsSerializerMixin { + Iterable get iterable; + Iterable get dynamicIterable; + Iterable get objectIterable; + Iterable get intIterable; + Iterable get dateTimeIterable; + List get list; + List get dynamicList; + List get objectList; + List get intList; + List get dateTimeList; + Map toJson() => { + 'iterable': iterable?.toList(), + 'dynamicIterable': dynamicIterable?.toList(), + 'objectIterable': objectIterable?.toList(), + 'intIterable': intIterable?.toList(), + 'dateTimeIterable': + dateTimeIterable?.map((v0) => v0?.toIso8601String())?.toList(), + 'list': list, + 'dynamicList': dynamicList, + 'objectList': objectList, + 'intList': intList, + 'dateTimeList': + dateTimeList?.map((v0) => v0?.toIso8601String())?.toList() + }; +} + // ************************************************************************** // Generator: JsonSerializableGenerator // Target: class Person @@ -40,7 +87,7 @@ Order _$OrderFromJson(Map json) => new Order((json['items'] as List) abstract class _$OrderSerializerMixin { int get count; bool get isRushed; - UnmodifiableListView get items; + UnmodifiableListView get items; Map toJson() => {'count': count, 'isRushed': isRushed, 'items': items}; } @@ -60,15 +107,12 @@ Item _$ItemFromJson(Map json) => new Item(json['price'] as int) abstract class _$ItemSerializerMixin { int get price; int get itemNumber; - List get saleDates; - List get rates; + List get saleDates; + List get rates; Map toJson() => { 'price': price, 'itemNumber': itemNumber, - 'saleDates': saleDates == null - ? null - : new List.generate( - saleDates.length, (int i0) => saleDates[i0]?.toIso8601String()), + 'saleDates': saleDates?.map((v0) => v0?.toIso8601String())?.toList(), 'rates': rates }; }