Skip to content

Commit b70e896

Browse files
committed
Improve error message when a field cannot be (de)serialized
1 parent 6fd06f8 commit b70e896

File tree

6 files changed

+54
-50
lines changed

6 files changed

+54
-50
lines changed

json_serializable/CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
## 0.2.6
1+
## 0.3.0
2+
3+
* **BREAKING** `UnsupportedTypeError` added a new required constructor argument:
4+
`reason`.
25

36
* Make `null` field handling smarter. If a field is classified as not
47
`nullable`, then use this knowledge when generating serialization code – even

json_serializable/lib/src/json_serializable_generator.dart

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -344,13 +344,7 @@ void $toJsonMapHelperName(String key, dynamic value) {
344344
return _serialize(
345345
field.type, accessOverride, _nullable(field, classIncludeNullable));
346346
} on UnsupportedTypeError catch (e) {
347-
var extra = (field.type != e.type) ? ' because of type `${e.type}`' : '';
348-
349-
var message = 'Could not generate `toJson` code for '
350-
'`${friendlyNameForElement(field)}`$extra.';
351-
352-
throw new InvalidGenerationSourceError(message,
353-
todo: 'Make sure all of the types are serializable.');
347+
throw _createInvalidGenerationError('toJson', field, e);
354348
}
355349
}
356350

@@ -360,8 +354,8 @@ void $toJsonMapHelperName(String key, dynamic value) {
360354
_allHelpers
361355
.map((h) => h.serialize(targetType, expression, nullable, _serialize))
362356
.firstWhere((r) => r != null,
363-
orElse: () =>
364-
throw new UnsupportedTypeError(targetType, expression));
357+
orElse: () => throw new UnsupportedTypeError(
358+
targetType, expression, _notSupportedWithTypeHelpersMsg));
365359

366360
String _deserializeForField(FieldElement field, bool classSupportNullable,
367361
{ParameterElement ctorParam}) {
@@ -373,13 +367,7 @@ void $toJsonMapHelperName(String key, dynamic value) {
373367
return _deserialize(
374368
targetType, 'json[$jsonKey]', _nullable(field, classSupportNullable));
375369
} on UnsupportedTypeError catch (e) {
376-
var extra = (field.type != e.type) ? ' because of type `${e.type}`' : '';
377-
378-
var message = 'Could not generate `fromJson` code for '
379-
'`${friendlyNameForElement(field)}`$extra.';
380-
381-
throw new InvalidGenerationSourceError(message,
382-
todo: 'Make sure all of the types are serializable.');
370+
throw _createInvalidGenerationError('fromJson', field, e);
383371
}
384372
}
385373

@@ -388,8 +376,8 @@ void $toJsonMapHelperName(String key, dynamic value) {
388376
.map((th) =>
389377
th.deserialize(targetType, expression, nullable, _deserialize))
390378
.firstWhere((r) => r != null,
391-
orElse: () =>
392-
throw new UnsupportedTypeError(targetType, expression));
379+
orElse: () => throw new UnsupportedTypeError(
380+
targetType, expression, _notSupportedWithTypeHelpersMsg));
393381
}
394382

395383
String _safeNameAccess(FieldElement field) {
@@ -457,3 +445,17 @@ int _offsetFor(FieldElement e) {
457445
}
458446
return e.nameOffset;
459447
}
448+
449+
final _notSupportedWithTypeHelpersMsg =
450+
'None of the provided `TypeHelper` instances support the defined type.';
451+
452+
InvalidGenerationSourceError _createInvalidGenerationError(
453+
String targetMember, FieldElement field, UnsupportedTypeError e) {
454+
var extra = (field.type != e.type) ? ' because of type `${e.type}`' : '';
455+
456+
var message = 'Could not generate `$targetMember` code for '
457+
'`${friendlyNameForElement(field)}`$extra.\n${e.reason}';
458+
459+
return new InvalidGenerationSourceError(message,
460+
todo: 'Make sure all of the types are serializable.');
461+
}

json_serializable/lib/src/type_helper.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,9 @@ const simpleJsonTypeChecker = const TypeChecker.any(const [
7878
class UnsupportedTypeError extends Error {
7979
final String expression;
8080
final DartType type;
81+
final String reason;
8182

82-
UnsupportedTypeError(this.type, this.expression);
83+
UnsupportedTypeError(this.type, this.expression, this.reason);
8384
}
8485

8586
DartType _getImplementationType(DartType type, TypeChecker checker) {

json_serializable/lib/src/type_helpers/map_helper.dart

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,7 @@ class MapHelper extends TypeHelper {
2121
var keyArg = args[0];
2222
var valueType = args[1];
2323

24-
// We're not going to handle converting key types at the moment
25-
// So the only safe types for key are dynamic/Object/String
26-
var safeKey = keyArg.isDynamic ||
27-
keyArg.isObject ||
28-
_stringTypeChecker.isExactlyType(keyArg);
29-
30-
if (!safeKey) {
31-
// TODO: should add some logic to `UnsupportedTypeError` to allow more
32-
// details to be provided – such as this case where the `key` in
33-
// `targetType` is not supported.
34-
throw new UnsupportedTypeError(targetType, expression);
35-
}
24+
_checkSafeKeyType(expression, keyArg);
3625

3726
var subFieldValue = serializeNested(valueType, _closureArg, nullable);
3827

@@ -62,15 +51,7 @@ class MapHelper extends TypeHelper {
6251
var keyArg = typeArgs.first;
6352
var valueArg = typeArgs.last;
6453

65-
// We're not going to handle converting key types at the moment
66-
// So the only safe types for key are dynamic/Object/String
67-
var safeKey = keyArg.isDynamic ||
68-
keyArg.isObject ||
69-
_stringTypeChecker.isExactlyType(keyArg);
70-
71-
if (!safeKey) {
72-
throw new UnsupportedTypeError(keyArg, expression);
73-
}
54+
_checkSafeKeyType(expression, keyArg);
7455

7556
// this is the trivial case. Do a runtime cast to the known type of JSON
7657
// map values - `Map<String, dynamic>`
@@ -96,6 +77,19 @@ class MapHelper extends TypeHelper {
9677

9778
return commonNullPrefix(nullable, expression, result);
9879
}
80+
81+
void _checkSafeKeyType(String expression, DartType keyArg) {
82+
// We're not going to handle converting key types at the moment
83+
// So the only safe types for key are dynamic/Object/String
84+
var safeKey = keyArg.isDynamic ||
85+
keyArg.isObject ||
86+
_stringTypeChecker.isExactlyType(keyArg);
87+
88+
if (!safeKey) {
89+
throw new UnsupportedTypeError(keyArg, expression,
90+
'The type of the Map key must be `String`, `Object` or `dynamic`.');
91+
}
92+
}
9993
}
10094

10195
final _coreMapChecker = const TypeChecker.fromUrl('dart:core#Map');

json_serializable/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: json_serializable
2-
version: 0.2.6-dev
2+
version: 0.3.0-dev
33
author: Dart Team <misc@dartlang.org>
44
description: Generates utilities to aid in serializing to/from JSON.
55
homepage: https://github.com/dart-lang/json_serializable

json_serializable/test/json_serializable_test.dart

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,37 +63,41 @@ void main() {
6363
});
6464

6565
group('unserializable types', () {
66+
final noSupportHelperFyi =
67+
'Could not generate `toJson` code for `Stopwatch watch`.\n'
68+
'None of the provided `TypeHelper` instances support the defined type.';
69+
6670
test('for toJson', () async {
6771
expect(
6872
_runForElementNamed('NoSerializeFieldType'),
69-
throwsInvalidGenerationSourceError(
70-
'Could not generate `toJson` code for `Stopwatch watch`.',
73+
throwsInvalidGenerationSourceError(noSupportHelperFyi,
7174
'Make sure all of the types are serializable.'));
7275
});
7376

7477
test('for fromJson', () async {
7578
expect(
7679
_runForElementNamed('NoDeserializeFieldType'),
7780
throwsInvalidGenerationSourceError(
78-
'Could not generate `fromJson` code for `Stopwatch watch`.',
81+
noSupportHelperFyi.replaceFirst('toJson', 'fromJson'),
7982
'Make sure all of the types are serializable.'));
8083
});
8184

85+
final mapKeyFyi = 'Could not generate `toJson` code for '
86+
'`Map<int, DateTime> intDateTimeMap` because of type `int`.\n'
87+
'The type of the Map key must be `String`, `Object` or `dynamic`.';
88+
8289
test('for toJson in Map key', () async {
8390
expect(
8491
_runForElementNamed('NoSerializeBadKey'),
8592
throwsInvalidGenerationSourceError(
86-
'Could not generate `toJson` code for '
87-
'`Map<int, DateTime> intDateTimeMap`.',
88-
'Make sure all of the types are serializable.'));
93+
mapKeyFyi, 'Make sure all of the types are serializable.'));
8994
});
9095

9196
test('for fromJson', () async {
9297
expect(
9398
_runForElementNamed('NoDeserializeBadKey'),
9499
throwsInvalidGenerationSourceError(
95-
'Could not generate `fromJson` code for '
96-
'`Map<int, DateTime> intDateTimeMap` because of type `int`.',
100+
mapKeyFyi.replaceFirst('toJson', 'fromJson'),
97101
'Make sure all of the types are serializable.'));
98102
});
99103
});

0 commit comments

Comments
 (0)