Skip to content

Commit 34fdbbd

Browse files
committed
WIP - subclasses
1 parent 3360abb commit 34fdbbd

7 files changed

+123
-23
lines changed

json_serializable/lib/src/json_serializable_generator.dart

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import 'dart:collection';
77

88
import 'package:analyzer/dart/element/element.dart';
99
import 'package:analyzer/dart/element/type.dart';
10+
// ignore: implementation_imports
11+
import 'package:analyzer/src/dart/resolver/inheritance_manager.dart'
12+
show InheritanceManager;
1013
import 'package:analyzer/analyzer.dart';
1114
import 'package:json_annotation/json_annotation.dart';
1215
import 'package:source_gen/source_gen.dart';
@@ -75,11 +78,10 @@ class JsonSerializableGenerator
7578
}
7679

7780
var classElement = element as ClassElement;
78-
var className = classElement.name;
7981

8082
// Get all of the fields that need to be assigned
8183
// TODO: support overriding the field set with an annotation option
82-
var fieldsList = classElement.fields.where((e) => !e.isStatic).toList();
84+
var fieldsList = _getFields(classElement);
8385

8486
var undefinedFields =
8587
fieldsList.where((fe) => fe.type.isUndefined).toList();
@@ -94,7 +96,7 @@ class JsonSerializableGenerator
9496

9597
// Sort these in the order in which they appear in the class
9698
// Sadly, `classElement.fields` puts properties after fields
97-
fieldsList.sort((a, b) => _offsetFor(a).compareTo(_offsetFor(b)));
99+
fieldsList.sort(_sortByLocation);
98100

99101
// Explicitly using `LinkedHashMap` – we want these ordered.
100102
var fields = new LinkedHashMap<String, FieldElement>.fromIterable(
@@ -103,7 +105,7 @@ class JsonSerializableGenerator
103105

104106
// Get the constructor to use for the factory
105107

106-
var prefix = '_\$$className';
108+
var prefix = '_\$${classElement.name}';
107109

108110
var buffer = new StringBuffer();
109111

@@ -535,14 +537,37 @@ final _jsonKeyExpando = new Expando<JsonKey>();
535537

536538
final _jsonKeyChecker = new TypeChecker.fromRuntime(JsonKey);
537539

538-
/// Returns the offset of given field/property in its source file – with a
539-
/// preference for the getter if it's defined.
540-
int _offsetFor(FieldElement e) {
541-
if (e.getter != null && e.getter.nameOffset != e.nameOffset) {
542-
assert(e.nameOffset == -1);
543-
return e.getter.nameOffset;
540+
final _dartCoreObjectChecker = new TypeChecker.fromRuntime(Object);
541+
542+
int _sortByLocation(FieldElement a, FieldElement b) {
543+
var checkerA = new TypeChecker.fromStatic(a.enclosingElement.type);
544+
545+
if (!checkerA.isExactly(b.enclosingElement)) {
546+
// in this case, you want to prioritize the enclosingElement that is more
547+
// "super".
548+
549+
if (checkerA.isSuperOf(b.enclosingElement)) {
550+
return -1;
551+
}
552+
553+
var checkerB = new TypeChecker.fromStatic(b.enclosingElement.type);
554+
555+
if (checkerB.isSuperOf(a.enclosingElement)) {
556+
return 1;
557+
}
544558
}
545-
return e.nameOffset;
559+
560+
/// Returns the offset of given field/property in its source file – with a
561+
/// preference for the getter if it's defined.
562+
int _offsetFor(FieldElement e) {
563+
if (e.getter != null && e.getter.nameOffset != e.nameOffset) {
564+
assert(e.nameOffset == -1);
565+
return e.getter.nameOffset;
566+
}
567+
return e.nameOffset;
568+
}
569+
570+
return _offsetFor(a).compareTo(_offsetFor(b));
546571
}
547572

548573
final _notSupportedWithTypeHelpersMsg =
@@ -558,3 +583,28 @@ InvalidGenerationSourceError _createInvalidGenerationError(
558583
return new InvalidGenerationSourceError(message,
559584
todo: 'Make sure all of the types are serializable.');
560585
}
586+
587+
/// Returns a list of all instance, [FieldElement] items for [element] and
588+
/// super classes.
589+
List<FieldElement> _getFields(ClassElement element) {
590+
// Get all of the fields that need to be assigned
591+
// TODO: support overriding the field set with an annotation option
592+
var fieldsList = element.fields.where((e) => !e.isStatic).toList();
593+
594+
var manager = new InheritanceManager(element.library);
595+
var things = manager.getMembersInheritedFromClasses(element);
596+
597+
things.forEach((k, v) {
598+
assert(!v.isStatic);
599+
assert(v is! FieldElement);
600+
if (_dartCoreObjectChecker.isExactly(v.enclosingElement)) {
601+
return;
602+
}
603+
604+
if (v is PropertyAccessorElement && v.variable is FieldElement) {
605+
fieldsList.add(v.variable as FieldElement);
606+
}
607+
});
608+
609+
return fieldsList;
610+
}

json_serializable/test/json_serializable_test.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,12 @@ abstract class _$OrderSerializerMixin {
274274
(e) => e.message,
275275
'The class `NoCtorClass` has no default constructor.')));
276276
});
277+
278+
test('super types', () async {
279+
var output = await runForElementNamed('SubType');
280+
281+
print(output);
282+
});
277283
}
278284

279285
final _formatter = new dart_style.DartFormatter();

json_serializable/test/src/json_serializable_test_input.dart

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,27 @@ class DupeKeys {
147147
@JsonKey(name: 'a')
148148
String str;
149149
}
150+
151+
@JsonSerializable()
152+
class SubType extends SuperType {
153+
final int subTypeViaCtor;
154+
int subTypeReadWrite;
155+
156+
SubType(this.subTypeViaCtor, int superTypeViaCtor) : super(superTypeViaCtor);
157+
}
158+
159+
// NOTE: `SuperType` is intentionally after `SubType` in the source file to
160+
// validate field ordering semantics.
161+
class SuperType {
162+
final int superTypeViaCtor;
163+
int superTypeReadWrite;
164+
165+
SuperType(this.superTypeViaCtor);
166+
167+
/// Add a property to try to throw-off the generator
168+
int get priceHalf => priceFraction(2);
169+
170+
/// Add a method to try to throw-off the generator
171+
int priceFraction(int other) =>
172+
superTypeViaCtor == null ? null : superTypeViaCtor ~/ other;
173+
}

json_serializable/test/test_files/json_test_example.dart

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,13 @@ class Order extends Object with _$OrderSerializerMixin {
6666
}
6767

6868
@JsonSerializable()
69-
class Item extends Object with _$ItemSerializerMixin {
70-
final int price;
69+
class Item extends ItemCore with _$ItemSerializerMixin {
7170
@JsonKey(includeIfNull: false, name: 'item-number')
7271
int itemNumber;
7372
List<DateTime> saleDates;
7473
List<int> rates;
7574

76-
Item([this.price]);
75+
Item([int price]) : super(price);
7776

7877
factory Item.fromJson(Map<String, dynamic> json) => _$ItemFromJson(json);
7978

@@ -84,6 +83,12 @@ class Item extends Object with _$ItemSerializerMixin {
8483
_deepEquals(saleDates, other.saleDates);
8584
}
8685

86+
abstract class ItemCore {
87+
final int price;
88+
89+
ItemCore(this.price);
90+
}
91+
8792
bool _deepEquals(a, b) => const DeepCollectionEquality().equals(a, b);
8893

8994
class Platform {

json_serializable/test/test_files/json_test_example.non_nullable.dart

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,13 @@ class Order extends Object with _$OrderSerializerMixin {
7272
}
7373

7474
@JsonSerializable(nullable: false)
75-
class Item extends Object with _$ItemSerializerMixin {
76-
final int price;
75+
class Item extends ItemCore with _$ItemSerializerMixin {
7776
@JsonKey(includeIfNull: false, name: 'item-number')
7877
int itemNumber;
7978
List<DateTime> saleDates;
8079
List<int> rates;
8180

82-
Item([this.price]);
81+
Item([int price]) : super(price);
8382

8483
factory Item.fromJson(Map<String, dynamic> json) => _$ItemFromJson(json);
8584

@@ -90,6 +89,12 @@ class Item extends Object with _$ItemSerializerMixin {
9089
_deepEquals(saleDates, other.saleDates);
9190
}
9291

92+
abstract class ItemCore {
93+
final int price;
94+
95+
ItemCore(this.price);
96+
}
97+
9398
bool _deepEquals(a, b) => const DeepCollectionEquality().equals(a, b);
9499

95100
class Platform {

json_serializable/test/test_files/json_test_example.non_nullable.wrapped.dart

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,13 @@ class Order extends Object with _$OrderSerializerMixin {
7878
}
7979

8080
@JsonSerializable(nullable: false)
81-
class Item extends Object with _$ItemSerializerMixin {
82-
final int price;
81+
class Item extends ItemCore with _$ItemSerializerMixin {
8382
@JsonKey(includeIfNull: false, name: 'item-number')
8483
int itemNumber;
8584
List<DateTime> saleDates;
8685
List<int> rates;
8786

88-
Item([this.price]);
87+
Item([int price]) : super(price);
8988

9089
factory Item.fromJson(Map<String, dynamic> json) => _$ItemFromJson(json);
9190

@@ -96,6 +95,12 @@ class Item extends Object with _$ItemSerializerMixin {
9695
_deepEquals(saleDates, other.saleDates);
9796
}
9897

98+
abstract class ItemCore {
99+
final int price;
100+
101+
ItemCore(this.price);
102+
}
103+
99104
bool _deepEquals(a, b) => const DeepCollectionEquality().equals(a, b);
100105

101106
class Platform {

json_serializable/test/test_files/json_test_example.wrapped.dart

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,13 @@ class Order extends Object with _$OrderSerializerMixin {
7272
}
7373

7474
@JsonSerializable()
75-
class Item extends Object with _$ItemSerializerMixin {
76-
final int price;
75+
class Item extends ItemCore with _$ItemSerializerMixin {
7776
@JsonKey(includeIfNull: false, name: 'item-number')
7877
int itemNumber;
7978
List<DateTime> saleDates;
8079
List<int> rates;
8180

82-
Item([this.price]);
81+
Item([int price]) : super(price);
8382

8483
factory Item.fromJson(Map<String, dynamic> json) => _$ItemFromJson(json);
8584

@@ -90,6 +89,12 @@ class Item extends Object with _$ItemSerializerMixin {
9089
_deepEquals(saleDates, other.saleDates);
9190
}
9291

92+
abstract class ItemCore {
93+
final int price;
94+
95+
ItemCore(this.price);
96+
}
97+
9398
bool _deepEquals(a, b) => const DeepCollectionEquality().equals(a, b);
9499

95100
class Platform {

0 commit comments

Comments
 (0)