From 6611e284c21196349fed67bd32f290fadd46cd91 Mon Sep 17 00:00:00 2001 From: Remi Rousselet Date: Thu, 21 Apr 2022 16:53:36 +0200 Subject: [PATCH 1/6] Support using non-nullable JsonConverter on nullable properties --- json_serializable/lib/src/type_helper.dart | 6 ++ .../type_helpers/json_converter_helper.dart | 56 ++++++++++++++-- .../test/generic_files/generic_class.g.dart | 43 ++++++++---- .../test/kitchen_sink/kitchen_sink.dart | 14 ++++ .../test/kitchen_sink/kitchen_sink.g.dart | 58 +++++++++++++++-- .../kitchen_sink/kitchen_sink.g_any_map.dart | 14 ++++ .../kitchen_sink.g_any_map.g.dart | 58 +++++++++++++++-- .../kitchen_sink.g_any_map__checked.dart | 14 ++++ .../kitchen_sink.g_any_map__checked.g.dart | 65 +++++++++++++++++-- .../kitchen_sink.g_exclude_null.dart | 14 ++++ .../kitchen_sink.g_exclude_null.g.dart | 59 +++++++++++++++-- .../kitchen_sink.g_explicit_to_json.dart | 14 ++++ .../kitchen_sink.g_explicit_to_json.g.dart | 58 +++++++++++++++-- .../test/kitchen_sink/kitchen_sink_test.dart | 3 +- 14 files changed, 425 insertions(+), 51 deletions(-) diff --git a/json_serializable/lib/src/type_helper.dart b/json_serializable/lib/src/type_helper.dart index b01249334..d8e553a1f 100644 --- a/json_serializable/lib/src/type_helper.dart +++ b/json_serializable/lib/src/type_helper.dart @@ -35,6 +35,12 @@ abstract class TypeHelperContextWithConfig extends TypeHelperContext { ClassConfig get config; } +class Expression { + Expression(this.expression, this.isNullable); + final String expression; + final bool isNullable; +} + abstract class TypeHelper { const TypeHelper(); diff --git a/json_serializable/lib/src/type_helpers/json_converter_helper.dart b/json_serializable/lib/src/type_helpers/json_converter_helper.dart index 8c0df9f3a..1ea792151 100644 --- a/json_serializable/lib/src/type_helpers/json_converter_helper.dart +++ b/json_serializable/lib/src/type_helpers/json_converter_helper.dart @@ -32,6 +32,19 @@ class JsonConverterHelper extends TypeHelper { return null; } + if (!converter.fieldType.isNullableType && targetType.isNullableType) { + // Hacky way to hoist the expression to avoid casting + // Ideally we should be able to cleanly declare a variable in the + // parent code-block, instead of using (){}() + return ''' +() { + final val = $expression; + return val == null + ? null + : ${converter.accessString}.toJson(val); +}()'''; + } + return LambdaResult(expression, '${converter.accessString}.toJson'); } @@ -49,6 +62,19 @@ class JsonConverterHelper extends TypeHelper { final asContent = asStatement(converter.jsonType); + if (!converter.jsonType.isNullableType && targetType.isNullableType) { + // Hacky way to hoist the expression to avoid casting + // Ideally we should be able to cleanly declare a variable in the + // parent code-block, instead of using (){}() + return ''' +() { + final val = $expression; + return val == null + ? null + : ${converter.accessString}.fromJson(val$asContent); +}()'''; + } + return LambdaResult( expression, '${converter.accessString}.fromJson', @@ -60,11 +86,13 @@ class JsonConverterHelper extends TypeHelper { class _JsonConvertData { final String accessString; final DartType jsonType; + final DartType fieldType; _JsonConvertData.className( String className, String accessor, this.jsonType, + this.fieldType, ) : accessString = 'const $className${_withAccessor(accessor)}()'; _JsonConvertData.genericClass( @@ -72,9 +100,14 @@ class _JsonConvertData { String genericTypeArg, String accessor, this.jsonType, + this.fieldType, ) : accessString = '$className<$genericTypeArg>${_withAccessor(accessor)}()'; - _JsonConvertData.propertyAccess(this.accessString, this.jsonType); + _JsonConvertData.propertyAccess( + this.accessString, + this.jsonType, + this.fieldType, + ); static String _withAccessor(String accessor) => accessor.isEmpty ? '' : '.$accessor'; @@ -127,7 +160,11 @@ _JsonConvertData? _typeConverterFrom( accessString = '${enclosing.name}.$accessString'; } - return _JsonConvertData.propertyAccess(accessString, match.jsonType); + return _JsonConvertData.propertyAccess( + accessString, + match.jsonType, + match.fieldType, + ); } final reviver = ConstantReader(match.annotation).revive(); @@ -145,6 +182,7 @@ _JsonConvertData? _typeConverterFrom( match.genericTypeArg!, reviver.accessor, match.jsonType, + match.fieldType, ); } @@ -152,11 +190,13 @@ _JsonConvertData? _typeConverterFrom( match.annotation.type!.element!.name!, reviver.accessor, match.jsonType, + match.fieldType, ); } class _ConverterMatch { final DartObject annotation; + final DartType fieldType; final DartType jsonType; final ElementAnnotation elementAnnotation; final String? genericTypeArg; @@ -166,6 +206,7 @@ class _ConverterMatch { this.annotation, this.jsonType, this.genericTypeArg, + this.fieldType, ); } @@ -191,9 +232,15 @@ _ConverterMatch? _compatibleMatch( final fieldType = jsonConverterSuper.typeArguments[0]; - if (fieldType == targetType) { + // Allow assigning T to T? + if (fieldType == targetType.promoteNonNullable()) { return _ConverterMatch( - annotation, constantValue, jsonConverterSuper.typeArguments[1], null); + annotation, + constantValue, + jsonConverterSuper.typeArguments[1], + null, + fieldType, + ); } if (fieldType is TypeParameterType && targetType is TypeParameterType) { @@ -212,6 +259,7 @@ _ConverterMatch? _compatibleMatch( constantValue, jsonConverterSuper.typeArguments[1], '${targetType.element.name}${targetType.isNullableType ? '?' : ''}', + fieldType, ); } diff --git a/json_serializable/test/generic_files/generic_class.g.dart b/json_serializable/test/generic_files/generic_class.g.dart index e45e840d4..249802c86 100644 --- a/json_serializable/test/generic_files/generic_class.g.dart +++ b/json_serializable/test/generic_files/generic_class.g.dart @@ -39,14 +39,24 @@ GenericClassWithConverter ..fieldObject = json['fieldObject'] ..fieldDynamic = json['fieldDynamic'] ..fieldInt = json['fieldInt'] as int? - ..fieldT = _SimpleConverter() - .fromJson(json['fieldT'] as Map) - ..fieldS = _SimpleConverter() - .fromJson(json['fieldS'] as Map) - ..duration = const _DurationMillisecondConverter.named() - .fromJson(json['duration'] as int?) - ..listDuration = const _DurationListMillisecondConverter() - .fromJson(json['listDuration'] as int?); + ..fieldT = () { + final val = json['fieldT']; + return val == null + ? null + : _SimpleConverter().fromJson(val as Map); + }() + ..fieldS = () { + final val = json['fieldS']; + return val == null + ? null + : _SimpleConverter().fromJson(val as Map); + }() + ..duration = json['duration'] == null + ? null + : Duration(microseconds: json['duration'] as int) + ..listDuration = (json['listDuration'] as List?) + ?.map((e) => Duration(microseconds: e as int)) + .toList(); Map _$GenericClassWithConverterToJson( GenericClassWithConverter instance) => @@ -54,12 +64,17 @@ Map _$GenericClassWithConverterToJson( 'fieldObject': instance.fieldObject, 'fieldDynamic': instance.fieldDynamic, 'fieldInt': instance.fieldInt, - 'fieldT': _SimpleConverter().toJson(instance.fieldT), - 'fieldS': _SimpleConverter().toJson(instance.fieldS), - 'duration': - const _DurationMillisecondConverter.named().toJson(instance.duration), - 'listDuration': const _DurationListMillisecondConverter() - .toJson(instance.listDuration), + 'fieldT': () { + final val = instance.fieldT; + return val == null ? null : _SimpleConverter().toJson(val); + }(), + 'fieldS': () { + final val = instance.fieldS; + return val == null ? null : _SimpleConverter().toJson(val); + }(), + 'duration': instance.duration?.inMicroseconds, + 'listDuration': + instance.listDuration?.map((e) => e.inMicroseconds).toList(), }; Issue980ParentClass _$Issue980ParentClassFromJson(Map json) => diff --git a/json_serializable/test/kitchen_sink/kitchen_sink.dart b/json_serializable/test/kitchen_sink/kitchen_sink.dart index 92bd1c94d..4eabab0e2 100644 --- a/json_serializable/test/kitchen_sink/kitchen_sink.dart +++ b/json_serializable/test/kitchen_sink/kitchen_sink.dart @@ -66,9 +66,13 @@ class _Factory implements k.KitchenSinkFactory { [], BigInt.zero, {}, + BigInt.zero, + {}, TrivialNumber(0), {}, DateTime.fromMillisecondsSinceEpoch(0), + TrivialNumber(0), + {}, ); k.JsonConverterTestClass jsonConverterFromJson(Map json) => @@ -203,9 +207,13 @@ class JsonConverterTestClass implements k.JsonConverterTestClass { this.durationList, this.bigInt, this.bigIntMap, + this.nullableBigInt, + this.nullableBigIntMap, this.numberSilly, this.numberSillySet, this.dateTime, + this.nullableNumberSilly, + this.nullableNumberSillySet, ); factory JsonConverterTestClass.fromJson(Map json) => @@ -219,10 +227,16 @@ class JsonConverterTestClass implements k.JsonConverterTestClass { BigInt bigInt; Map bigIntMap; + BigInt? nullableBigInt; + Map nullableBigIntMap; + TrivialNumber numberSilly; Set numberSillySet; DateTime? dateTime; + + TrivialNumber? nullableNumberSilly; + Set nullableNumberSillySet; } @JsonSerializable() diff --git a/json_serializable/test/kitchen_sink/kitchen_sink.g.dart b/json_serializable/test/kitchen_sink/kitchen_sink.g.dart index ec7fa097c..25085663a 100644 --- a/json_serializable/test/kitchen_sink/kitchen_sink.g.dart +++ b/json_serializable/test/kitchen_sink/kitchen_sink.g.dart @@ -137,37 +137,83 @@ Map _$KitchenSinkToJson(KitchenSink instance) => JsonConverterTestClass _$JsonConverterTestClassFromJson( Map json) => JsonConverterTestClass( - durationConverter.fromJson(json['duration'] as int?), + json['duration'] == null + ? null + : Duration(microseconds: json['duration'] as int), (json['durationList'] as List) - .map((e) => durationConverter.fromJson(e as int?)) + .map((e) => e == null ? null : Duration(microseconds: e as int)) .toList(), const BigIntStringConverter().fromJson(json['bigInt'] as String), (json['bigIntMap'] as Map).map( (k, e) => MapEntry(k, const BigIntStringConverter().fromJson(e as String)), ), + () { + final val = json['nullableBigInt']; + return val == null + ? null + : const BigIntStringConverter().fromJson(val as String); + }(), + (json['nullableBigIntMap'] as Map).map( + (k, e) => MapEntry(k, () { + final val = e; + return val == null + ? null + : const BigIntStringConverter().fromJson(val as String); + }()), + ), TrivialNumberConverter.instance.fromJson(json['numberSilly'] as int?), (json['numberSillySet'] as List) .map((e) => TrivialNumberConverter.instance.fromJson(e as int?)) .toSet(), - const EpochDateTimeConverter().fromJson(json['dateTime'] as int?), + json['dateTime'] == null + ? null + : DateTime.parse(json['dateTime'] as String), + TrivialNumberConverter.instance + .fromJson(json['nullableNumberSilly'] as int?), + (json['nullableNumberSillySet'] as List) + .map((e) => TrivialNumberConverter.instance.fromJson(e as int?)) + .toSet(), ); Map _$JsonConverterTestClassToJson( JsonConverterTestClass instance) => { - 'duration': durationConverter.toJson(instance.duration), + 'duration': instance.duration?.inMicroseconds, 'durationList': - instance.durationList.map(durationConverter.toJson).toList(), + instance.durationList.map((e) => e?.inMicroseconds).toList(), 'bigInt': const BigIntStringConverter().toJson(instance.bigInt), 'bigIntMap': instance.bigIntMap .map((k, e) => MapEntry(k, const BigIntStringConverter().toJson(e))), + 'nullableBigInt': () { + final val = instance.nullableBigInt; + return val == null ? null : const BigIntStringConverter().toJson(val); + }(), + 'nullableBigIntMap': + instance.nullableBigIntMap.map((k, e) => MapEntry(k, () { + final val = e; + return val == null + ? null + : const BigIntStringConverter().toJson(val); + }())), 'numberSilly': TrivialNumberConverter.instance.toJson(instance.numberSilly), 'numberSillySet': instance.numberSillySet .map(TrivialNumberConverter.instance.toJson) .toList(), - 'dateTime': const EpochDateTimeConverter().toJson(instance.dateTime), + 'dateTime': instance.dateTime?.toIso8601String(), + 'nullableNumberSilly': () { + final val = instance.nullableNumberSilly; + return val == null ? null : TrivialNumberConverter.instance.toJson(val); + }(), + 'nullableNumberSillySet': instance.nullableNumberSillySet + .map((e) => () { + final val = e; + return val == null + ? null + : TrivialNumberConverter.instance.toJson(val); + }()) + .toList(), }; JsonConverterGeneric _$JsonConverterGenericFromJson( diff --git a/json_serializable/test/kitchen_sink/kitchen_sink.g_any_map.dart b/json_serializable/test/kitchen_sink/kitchen_sink.g_any_map.dart index bf82df50d..9da91757a 100644 --- a/json_serializable/test/kitchen_sink/kitchen_sink.g_any_map.dart +++ b/json_serializable/test/kitchen_sink/kitchen_sink.g_any_map.dart @@ -65,9 +65,13 @@ class _Factory implements k.KitchenSinkFactory { [], BigInt.zero, {}, + BigInt.zero, + {}, TrivialNumber(0), {}, DateTime.fromMillisecondsSinceEpoch(0), + TrivialNumber(0), + {}, ); k.JsonConverterTestClass jsonConverterFromJson(Map json) => @@ -205,9 +209,13 @@ class JsonConverterTestClass implements k.JsonConverterTestClass { this.durationList, this.bigInt, this.bigIntMap, + this.nullableBigInt, + this.nullableBigIntMap, this.numberSilly, this.numberSillySet, this.dateTime, + this.nullableNumberSilly, + this.nullableNumberSillySet, ); factory JsonConverterTestClass.fromJson(Map json) => @@ -221,10 +229,16 @@ class JsonConverterTestClass implements k.JsonConverterTestClass { BigInt bigInt; Map bigIntMap; + BigInt? nullableBigInt; + Map nullableBigIntMap; + TrivialNumber numberSilly; Set numberSillySet; DateTime? dateTime; + + TrivialNumber? nullableNumberSilly; + Set nullableNumberSillySet; } @JsonSerializable( diff --git a/json_serializable/test/kitchen_sink/kitchen_sink.g_any_map.g.dart b/json_serializable/test/kitchen_sink/kitchen_sink.g_any_map.g.dart index 16d995c98..6cf1c078f 100644 --- a/json_serializable/test/kitchen_sink/kitchen_sink.g_any_map.g.dart +++ b/json_serializable/test/kitchen_sink/kitchen_sink.g_any_map.g.dart @@ -128,37 +128,83 @@ Map _$KitchenSinkToJson(KitchenSink instance) => JsonConverterTestClass _$JsonConverterTestClassFromJson(Map json) => JsonConverterTestClass( - durationConverter.fromJson(json['duration'] as int?), + json['duration'] == null + ? null + : Duration(microseconds: json['duration'] as int), (json['durationList'] as List) - .map((e) => durationConverter.fromJson(e as int?)) + .map((e) => e == null ? null : Duration(microseconds: e as int)) .toList(), const BigIntStringConverter().fromJson(json['bigInt'] as String), (json['bigIntMap'] as Map).map( (k, e) => MapEntry( k as String, const BigIntStringConverter().fromJson(e as String)), ), + () { + final val = json['nullableBigInt']; + return val == null + ? null + : const BigIntStringConverter().fromJson(val as String); + }(), + (json['nullableBigIntMap'] as Map).map( + (k, e) => MapEntry(k as String, () { + final val = e; + return val == null + ? null + : const BigIntStringConverter().fromJson(val as String); + }()), + ), TrivialNumberConverter.instance.fromJson(json['numberSilly'] as int?), (json['numberSillySet'] as List) .map((e) => TrivialNumberConverter.instance.fromJson(e as int?)) .toSet(), - const EpochDateTimeConverter().fromJson(json['dateTime'] as int?), + json['dateTime'] == null + ? null + : DateTime.parse(json['dateTime'] as String), + TrivialNumberConverter.instance + .fromJson(json['nullableNumberSilly'] as int?), + (json['nullableNumberSillySet'] as List) + .map((e) => TrivialNumberConverter.instance.fromJson(e as int?)) + .toSet(), ); Map _$JsonConverterTestClassToJson( JsonConverterTestClass instance) => { - 'duration': durationConverter.toJson(instance.duration), + 'duration': instance.duration?.inMicroseconds, 'durationList': - instance.durationList.map(durationConverter.toJson).toList(), + instance.durationList.map((e) => e?.inMicroseconds).toList(), 'bigInt': const BigIntStringConverter().toJson(instance.bigInt), 'bigIntMap': instance.bigIntMap .map((k, e) => MapEntry(k, const BigIntStringConverter().toJson(e))), + 'nullableBigInt': () { + final val = instance.nullableBigInt; + return val == null ? null : const BigIntStringConverter().toJson(val); + }(), + 'nullableBigIntMap': + instance.nullableBigIntMap.map((k, e) => MapEntry(k, () { + final val = e; + return val == null + ? null + : const BigIntStringConverter().toJson(val); + }())), 'numberSilly': TrivialNumberConverter.instance.toJson(instance.numberSilly), 'numberSillySet': instance.numberSillySet .map(TrivialNumberConverter.instance.toJson) .toList(), - 'dateTime': const EpochDateTimeConverter().toJson(instance.dateTime), + 'dateTime': instance.dateTime?.toIso8601String(), + 'nullableNumberSilly': () { + final val = instance.nullableNumberSilly; + return val == null ? null : TrivialNumberConverter.instance.toJson(val); + }(), + 'nullableNumberSillySet': instance.nullableNumberSillySet + .map((e) => () { + final val = e; + return val == null + ? null + : TrivialNumberConverter.instance.toJson(val); + }()) + .toList(), }; JsonConverterGeneric _$JsonConverterGenericFromJson( diff --git a/json_serializable/test/kitchen_sink/kitchen_sink.g_any_map__checked.dart b/json_serializable/test/kitchen_sink/kitchen_sink.g_any_map__checked.dart index 0a5119aa5..71812ba2e 100644 --- a/json_serializable/test/kitchen_sink/kitchen_sink.g_any_map__checked.dart +++ b/json_serializable/test/kitchen_sink/kitchen_sink.g_any_map__checked.dart @@ -65,9 +65,13 @@ class _Factory implements k.KitchenSinkFactory { [], BigInt.zero, {}, + BigInt.zero, + {}, TrivialNumber(0), {}, DateTime.fromMillisecondsSinceEpoch(0), + TrivialNumber(0), + {}, ); k.JsonConverterTestClass jsonConverterFromJson(Map json) => @@ -207,9 +211,13 @@ class JsonConverterTestClass implements k.JsonConverterTestClass { this.durationList, this.bigInt, this.bigIntMap, + this.nullableBigInt, + this.nullableBigIntMap, this.numberSilly, this.numberSillySet, this.dateTime, + this.nullableNumberSilly, + this.nullableNumberSillySet, ); factory JsonConverterTestClass.fromJson(Map json) => @@ -223,10 +231,16 @@ class JsonConverterTestClass implements k.JsonConverterTestClass { BigInt bigInt; Map bigIntMap; + BigInt? nullableBigInt; + Map nullableBigIntMap; + TrivialNumber numberSilly; Set numberSillySet; DateTime? dateTime; + + TrivialNumber? nullableNumberSilly; + Set nullableNumberSillySet; } @JsonSerializable( diff --git a/json_serializable/test/kitchen_sink/kitchen_sink.g_any_map__checked.g.dart b/json_serializable/test/kitchen_sink/kitchen_sink.g_any_map__checked.g.dart index 8be98d6cb..9930711e7 100644 --- a/json_serializable/test/kitchen_sink/kitchen_sink.g_any_map__checked.g.dart +++ b/json_serializable/test/kitchen_sink/kitchen_sink.g_any_map__checked.g.dart @@ -183,12 +183,13 @@ JsonConverterTestClass _$JsonConverterTestClassFromJson(Map json) => json, ($checkedConvert) { final val = JsonConverterTestClass( - $checkedConvert( - 'duration', (v) => durationConverter.fromJson(v as int?)), + $checkedConvert('duration', + (v) => v == null ? null : Duration(microseconds: v as int)), $checkedConvert( 'durationList', (v) => (v as List) - .map((e) => durationConverter.fromJson(e as int?)) + .map((e) => + e == null ? null : Duration(microseconds: e as int)) .toList()), $checkedConvert('bigInt', (v) => const BigIntStringConverter().fromJson(v as String)), @@ -198,6 +199,25 @@ JsonConverterTestClass _$JsonConverterTestClassFromJson(Map json) => (k, e) => MapEntry(k as String, const BigIntStringConverter().fromJson(e as String)), )), + $checkedConvert( + 'nullableBigInt', + (v) => () { + final val = v; + return val == null + ? null + : const BigIntStringConverter().fromJson(val as String); + }()), + $checkedConvert( + 'nullableBigIntMap', + (v) => (v as Map).map( + (k, e) => MapEntry(k as String, () { + final val = e; + return val == null + ? null + : const BigIntStringConverter() + .fromJson(val as String); + }()), + )), $checkedConvert('numberSilly', (v) => TrivialNumberConverter.instance.fromJson(v as int?)), $checkedConvert( @@ -207,7 +227,15 @@ JsonConverterTestClass _$JsonConverterTestClassFromJson(Map json) => TrivialNumberConverter.instance.fromJson(e as int?)) .toSet()), $checkedConvert('dateTime', - (v) => const EpochDateTimeConverter().fromJson(v as int?)), + (v) => v == null ? null : DateTime.parse(v as String)), + $checkedConvert('nullableNumberSilly', + (v) => TrivialNumberConverter.instance.fromJson(v as int?)), + $checkedConvert( + 'nullableNumberSillySet', + (v) => (v as List) + .map((e) => + TrivialNumberConverter.instance.fromJson(e as int?)) + .toSet()), ); return val; }, @@ -216,18 +244,41 @@ JsonConverterTestClass _$JsonConverterTestClassFromJson(Map json) => Map _$JsonConverterTestClassToJson( JsonConverterTestClass instance) => { - 'duration': durationConverter.toJson(instance.duration), + 'duration': instance.duration?.inMicroseconds, 'durationList': - instance.durationList.map(durationConverter.toJson).toList(), + instance.durationList.map((e) => e?.inMicroseconds).toList(), 'bigInt': const BigIntStringConverter().toJson(instance.bigInt), 'bigIntMap': instance.bigIntMap .map((k, e) => MapEntry(k, const BigIntStringConverter().toJson(e))), + 'nullableBigInt': () { + final val = instance.nullableBigInt; + return val == null ? null : const BigIntStringConverter().toJson(val); + }(), + 'nullableBigIntMap': + instance.nullableBigIntMap.map((k, e) => MapEntry(k, () { + final val = e; + return val == null + ? null + : const BigIntStringConverter().toJson(val); + }())), 'numberSilly': TrivialNumberConverter.instance.toJson(instance.numberSilly), 'numberSillySet': instance.numberSillySet .map(TrivialNumberConverter.instance.toJson) .toList(), - 'dateTime': const EpochDateTimeConverter().toJson(instance.dateTime), + 'dateTime': instance.dateTime?.toIso8601String(), + 'nullableNumberSilly': () { + final val = instance.nullableNumberSilly; + return val == null ? null : TrivialNumberConverter.instance.toJson(val); + }(), + 'nullableNumberSillySet': instance.nullableNumberSillySet + .map((e) => () { + final val = e; + return val == null + ? null + : TrivialNumberConverter.instance.toJson(val); + }()) + .toList(), }; JsonConverterGeneric _$JsonConverterGenericFromJson( diff --git a/json_serializable/test/kitchen_sink/kitchen_sink.g_exclude_null.dart b/json_serializable/test/kitchen_sink/kitchen_sink.g_exclude_null.dart index 5927b1aaa..a8fd5e1d8 100644 --- a/json_serializable/test/kitchen_sink/kitchen_sink.g_exclude_null.dart +++ b/json_serializable/test/kitchen_sink/kitchen_sink.g_exclude_null.dart @@ -66,9 +66,13 @@ class _Factory implements k.KitchenSinkFactory { [], BigInt.zero, {}, + BigInt.zero, + {}, TrivialNumber(0), {}, DateTime.fromMillisecondsSinceEpoch(0), + TrivialNumber(0), + {}, ); k.JsonConverterTestClass jsonConverterFromJson(Map json) => @@ -207,9 +211,13 @@ class JsonConverterTestClass implements k.JsonConverterTestClass { this.durationList, this.bigInt, this.bigIntMap, + this.nullableBigInt, + this.nullableBigIntMap, this.numberSilly, this.numberSillySet, this.dateTime, + this.nullableNumberSilly, + this.nullableNumberSillySet, ); factory JsonConverterTestClass.fromJson(Map json) => @@ -223,10 +231,16 @@ class JsonConverterTestClass implements k.JsonConverterTestClass { BigInt bigInt; Map bigIntMap; + BigInt? nullableBigInt; + Map nullableBigIntMap; + TrivialNumber numberSilly; Set numberSillySet; DateTime? dateTime; + + TrivialNumber? nullableNumberSilly; + Set nullableNumberSillySet; } @JsonSerializable( diff --git a/json_serializable/test/kitchen_sink/kitchen_sink.g_exclude_null.g.dart b/json_serializable/test/kitchen_sink/kitchen_sink.g_exclude_null.g.dart index 6afeb29a2..f2246b379 100644 --- a/json_serializable/test/kitchen_sink/kitchen_sink.g_exclude_null.g.dart +++ b/json_serializable/test/kitchen_sink/kitchen_sink.g_exclude_null.g.dart @@ -145,20 +145,43 @@ Map _$KitchenSinkToJson(KitchenSink instance) { JsonConverterTestClass _$JsonConverterTestClassFromJson( Map json) => JsonConverterTestClass( - durationConverter.fromJson(json['duration'] as int?), + json['duration'] == null + ? null + : Duration(microseconds: json['duration'] as int), (json['durationList'] as List) - .map((e) => durationConverter.fromJson(e as int?)) + .map((e) => e == null ? null : Duration(microseconds: e as int)) .toList(), const BigIntStringConverter().fromJson(json['bigInt'] as String), (json['bigIntMap'] as Map).map( (k, e) => MapEntry(k, const BigIntStringConverter().fromJson(e as String)), ), + () { + final val = json['nullableBigInt']; + return val == null + ? null + : const BigIntStringConverter().fromJson(val as String); + }(), + (json['nullableBigIntMap'] as Map).map( + (k, e) => MapEntry(k, () { + final val = e; + return val == null + ? null + : const BigIntStringConverter().fromJson(val as String); + }()), + ), TrivialNumberConverter.instance.fromJson(json['numberSilly'] as int?), (json['numberSillySet'] as List) .map((e) => TrivialNumberConverter.instance.fromJson(e as int?)) .toSet(), - const EpochDateTimeConverter().fromJson(json['dateTime'] as int?), + json['dateTime'] == null + ? null + : DateTime.parse(json['dateTime'] as String), + TrivialNumberConverter.instance + .fromJson(json['nullableNumberSilly'] as int?), + (json['nullableNumberSillySet'] as List) + .map((e) => TrivialNumberConverter.instance.fromJson(e as int?)) + .toSet(), ); Map _$JsonConverterTestClassToJson( @@ -171,19 +194,41 @@ Map _$JsonConverterTestClassToJson( } } - writeNotNull('duration', durationConverter.toJson(instance.duration)); + writeNotNull('duration', instance.duration?.inMicroseconds); val['durationList'] = - instance.durationList.map(durationConverter.toJson).toList(); + instance.durationList.map((e) => e?.inMicroseconds).toList(); writeNotNull('bigInt', const BigIntStringConverter().toJson(instance.bigInt)); val['bigIntMap'] = instance.bigIntMap .map((k, e) => MapEntry(k, const BigIntStringConverter().toJson(e))); + writeNotNull('nullableBigInt', () { + final val = instance.nullableBigInt; + return val == null ? null : const BigIntStringConverter().toJson(val); + }()); + val['nullableBigIntMap'] = + instance.nullableBigIntMap.map((k, e) => MapEntry(k, () { + final val = e; + return val == null + ? null + : const BigIntStringConverter().toJson(val); + }())); writeNotNull('numberSilly', TrivialNumberConverter.instance.toJson(instance.numberSilly)); val['numberSillySet'] = instance.numberSillySet .map(TrivialNumberConverter.instance.toJson) .toList(); - writeNotNull( - 'dateTime', const EpochDateTimeConverter().toJson(instance.dateTime)); + writeNotNull('dateTime', instance.dateTime?.toIso8601String()); + writeNotNull('nullableNumberSilly', () { + final val = instance.nullableNumberSilly; + return val == null ? null : TrivialNumberConverter.instance.toJson(val); + }()); + val['nullableNumberSillySet'] = instance.nullableNumberSillySet + .map((e) => () { + final val = e; + return val == null + ? null + : TrivialNumberConverter.instance.toJson(val); + }()) + .toList(); return val; } diff --git a/json_serializable/test/kitchen_sink/kitchen_sink.g_explicit_to_json.dart b/json_serializable/test/kitchen_sink/kitchen_sink.g_explicit_to_json.dart index bce55d5f0..f5f13fc7d 100644 --- a/json_serializable/test/kitchen_sink/kitchen_sink.g_explicit_to_json.dart +++ b/json_serializable/test/kitchen_sink/kitchen_sink.g_explicit_to_json.dart @@ -66,9 +66,13 @@ class _Factory implements k.KitchenSinkFactory { [], BigInt.zero, {}, + BigInt.zero, + {}, TrivialNumber(0), {}, DateTime.fromMillisecondsSinceEpoch(0), + TrivialNumber(0), + {}, ); k.JsonConverterTestClass jsonConverterFromJson(Map json) => @@ -207,9 +211,13 @@ class JsonConverterTestClass implements k.JsonConverterTestClass { this.durationList, this.bigInt, this.bigIntMap, + this.nullableBigInt, + this.nullableBigIntMap, this.numberSilly, this.numberSillySet, this.dateTime, + this.nullableNumberSilly, + this.nullableNumberSillySet, ); factory JsonConverterTestClass.fromJson(Map json) => @@ -223,10 +231,16 @@ class JsonConverterTestClass implements k.JsonConverterTestClass { BigInt bigInt; Map bigIntMap; + BigInt? nullableBigInt; + Map nullableBigIntMap; + TrivialNumber numberSilly; Set numberSillySet; DateTime? dateTime; + + TrivialNumber? nullableNumberSilly; + Set nullableNumberSillySet; } @JsonSerializable( diff --git a/json_serializable/test/kitchen_sink/kitchen_sink.g_explicit_to_json.g.dart b/json_serializable/test/kitchen_sink/kitchen_sink.g_explicit_to_json.g.dart index cb0415623..ab3fc4b9e 100644 --- a/json_serializable/test/kitchen_sink/kitchen_sink.g_explicit_to_json.g.dart +++ b/json_serializable/test/kitchen_sink/kitchen_sink.g_explicit_to_json.g.dart @@ -139,37 +139,83 @@ Map _$KitchenSinkToJson(KitchenSink instance) => JsonConverterTestClass _$JsonConverterTestClassFromJson( Map json) => JsonConverterTestClass( - durationConverter.fromJson(json['duration'] as int?), + json['duration'] == null + ? null + : Duration(microseconds: json['duration'] as int), (json['durationList'] as List) - .map((e) => durationConverter.fromJson(e as int?)) + .map((e) => e == null ? null : Duration(microseconds: e as int)) .toList(), const BigIntStringConverter().fromJson(json['bigInt'] as String), (json['bigIntMap'] as Map).map( (k, e) => MapEntry(k, const BigIntStringConverter().fromJson(e as String)), ), + () { + final val = json['nullableBigInt']; + return val == null + ? null + : const BigIntStringConverter().fromJson(val as String); + }(), + (json['nullableBigIntMap'] as Map).map( + (k, e) => MapEntry(k, () { + final val = e; + return val == null + ? null + : const BigIntStringConverter().fromJson(val as String); + }()), + ), TrivialNumberConverter.instance.fromJson(json['numberSilly'] as int?), (json['numberSillySet'] as List) .map((e) => TrivialNumberConverter.instance.fromJson(e as int?)) .toSet(), - const EpochDateTimeConverter().fromJson(json['dateTime'] as int?), + json['dateTime'] == null + ? null + : DateTime.parse(json['dateTime'] as String), + TrivialNumberConverter.instance + .fromJson(json['nullableNumberSilly'] as int?), + (json['nullableNumberSillySet'] as List) + .map((e) => TrivialNumberConverter.instance.fromJson(e as int?)) + .toSet(), ); Map _$JsonConverterTestClassToJson( JsonConverterTestClass instance) => { - 'duration': durationConverter.toJson(instance.duration), + 'duration': instance.duration?.inMicroseconds, 'durationList': - instance.durationList.map(durationConverter.toJson).toList(), + instance.durationList.map((e) => e?.inMicroseconds).toList(), 'bigInt': const BigIntStringConverter().toJson(instance.bigInt), 'bigIntMap': instance.bigIntMap .map((k, e) => MapEntry(k, const BigIntStringConverter().toJson(e))), + 'nullableBigInt': () { + final val = instance.nullableBigInt; + return val == null ? null : const BigIntStringConverter().toJson(val); + }(), + 'nullableBigIntMap': + instance.nullableBigIntMap.map((k, e) => MapEntry(k, () { + final val = e; + return val == null + ? null + : const BigIntStringConverter().toJson(val); + }())), 'numberSilly': TrivialNumberConverter.instance.toJson(instance.numberSilly), 'numberSillySet': instance.numberSillySet .map(TrivialNumberConverter.instance.toJson) .toList(), - 'dateTime': const EpochDateTimeConverter().toJson(instance.dateTime), + 'dateTime': instance.dateTime?.toIso8601String(), + 'nullableNumberSilly': () { + final val = instance.nullableNumberSilly; + return val == null ? null : TrivialNumberConverter.instance.toJson(val); + }(), + 'nullableNumberSillySet': instance.nullableNumberSillySet + .map((e) => () { + final val = e; + return val == null + ? null + : TrivialNumberConverter.instance.toJson(val); + }()) + .toList(), }; JsonConverterGeneric _$JsonConverterGenericFromJson( diff --git a/json_serializable/test/kitchen_sink/kitchen_sink_test.dart b/json_serializable/test/kitchen_sink/kitchen_sink_test.dart index 8044c29d7..3e02126e7 100644 --- a/json_serializable/test/kitchen_sink/kitchen_sink_test.dart +++ b/json_serializable/test/kitchen_sink/kitchen_sink_test.dart @@ -57,7 +57,8 @@ const _jsonConverterValidValues = { 'bigIntMap': {'vaule': '5'}, 'numberSilly': 5, 'numberSillySet': [5], - 'dateTime': 5 + 'dateTime': 5, + 'nullableNumberSilly': 5, }; void _nonNullableTests(KitchenSinkFactory factory) { From 42c0397f41c8bf808f23802d7e2521e48f1ce9b5 Mon Sep 17 00:00:00 2001 From: Remi Rousselet Date: Mon, 25 Apr 2022 21:56:24 +0200 Subject: [PATCH 2/6] Refactor JsonConverter ternary invocation to helper functions --- .../type_helpers/json_converter_helper.dart | 76 +++++++++++++------ .../test/generic_files/generic_class.g.dart | 40 +++++----- .../test/kitchen_sink/kitchen_sink.g.dart | 61 +++++++-------- .../kitchen_sink.g_any_map.g.dart | 61 +++++++-------- .../kitchen_sink.g_any_map__checked.g.dart | 62 +++++++-------- .../kitchen_sink.g_exclude_null.g.dart | 65 ++++++++-------- .../kitchen_sink.g_explicit_to_json.g.dart | 61 +++++++-------- 7 files changed, 217 insertions(+), 209 deletions(-) diff --git a/json_serializable/lib/src/type_helpers/json_converter_helper.dart b/json_serializable/lib/src/type_helpers/json_converter_helper.dart index 1ea792151..6e611084f 100644 --- a/json_serializable/lib/src/type_helpers/json_converter_helper.dart +++ b/json_serializable/lib/src/type_helpers/json_converter_helper.dart @@ -33,16 +33,21 @@ class JsonConverterHelper extends TypeHelper { } if (!converter.fieldType.isNullableType && targetType.isNullableType) { - // Hacky way to hoist the expression to avoid casting - // Ideally we should be able to cleanly declare a variable in the - // parent code-block, instead of using (){}() - return ''' -() { - final val = $expression; - return val == null - ? null - : ${converter.accessString}.toJson(val); -}()'''; + const converterToJsonName = r'_$JsonConverterToJson'; + context.addMember(''' +Json? $converterToJsonName( + Value? value, + Json? Function(Value value) toJson, +) => ${ifNullOrElse('value', 'null', 'toJson(value)')}; +'''); + + return _nullableJsonConverterLambdaResult( + converter, + name: converterToJsonName, + targetType: targetType, + expression: expression, + callback: '${converter.accessString}.toJson', + ); } return LambdaResult(expression, '${converter.accessString}.toJson'); @@ -63,16 +68,21 @@ class JsonConverterHelper extends TypeHelper { final asContent = asStatement(converter.jsonType); if (!converter.jsonType.isNullableType && targetType.isNullableType) { - // Hacky way to hoist the expression to avoid casting - // Ideally we should be able to cleanly declare a variable in the - // parent code-block, instead of using (){}() - return ''' -() { - final val = $expression; - return val == null - ? null - : ${converter.accessString}.fromJson(val$asContent); -}()'''; + const converterFromJsonName = r'_$JsonConverterFromJson'; + context.addMember(''' +Value? $converterFromJsonName( + Object? json, + Value? Function(Json json) fromJson, +) => ${ifNullOrElse('json', 'null', 'fromJson(json as Json)')}; +'''); + + return _nullableJsonConverterLambdaResult( + converter, + name: converterFromJsonName, + targetType: targetType, + expression: expression, + callback: '${converter.accessString}.fromJson', + ); } return LambdaResult( @@ -83,17 +93,35 @@ class JsonConverterHelper extends TypeHelper { } } +String _nullableJsonConverterLambdaResult( + _JsonConvertData converter, { + required String name, + required DartType targetType, + required String expression, + required String callback, +}) { + final jsonDisplayString = typeToCode(converter.jsonType); + final fieldTypeDisplayString = converter.isGeneric + ? typeToCode(targetType) + : typeToCode(converter.fieldType); + + return '$name<$jsonDisplayString, $fieldTypeDisplayString>(' + '$expression, $callback)'; +} + class _JsonConvertData { final String accessString; final DartType jsonType; final DartType fieldType; + final bool isGeneric; _JsonConvertData.className( String className, String accessor, this.jsonType, this.fieldType, - ) : accessString = 'const $className${_withAccessor(accessor)}()'; + ) : accessString = 'const $className${_withAccessor(accessor)}()', + isGeneric = false; _JsonConvertData.genericClass( String className, @@ -101,13 +129,15 @@ class _JsonConvertData { String accessor, this.jsonType, this.fieldType, - ) : accessString = '$className<$genericTypeArg>${_withAccessor(accessor)}()'; + ) : accessString = + '$className<$genericTypeArg>${_withAccessor(accessor)}()', + isGeneric = true; _JsonConvertData.propertyAccess( this.accessString, this.jsonType, this.fieldType, - ); + ) : isGeneric = false; static String _withAccessor(String accessor) => accessor.isEmpty ? '' : '.$accessor'; diff --git a/json_serializable/test/generic_files/generic_class.g.dart b/json_serializable/test/generic_files/generic_class.g.dart index 249802c86..9e108bd49 100644 --- a/json_serializable/test/generic_files/generic_class.g.dart +++ b/json_serializable/test/generic_files/generic_class.g.dart @@ -39,18 +39,10 @@ GenericClassWithConverter ..fieldObject = json['fieldObject'] ..fieldDynamic = json['fieldDynamic'] ..fieldInt = json['fieldInt'] as int? - ..fieldT = () { - final val = json['fieldT']; - return val == null - ? null - : _SimpleConverter().fromJson(val as Map); - }() - ..fieldS = () { - final val = json['fieldS']; - return val == null - ? null - : _SimpleConverter().fromJson(val as Map); - }() + ..fieldT = _$JsonConverterFromJson, T>( + json['fieldT'], _SimpleConverter().fromJson) + ..fieldS = _$JsonConverterFromJson, S>( + json['fieldS'], _SimpleConverter().fromJson) ..duration = json['duration'] == null ? null : Duration(microseconds: json['duration'] as int) @@ -64,19 +56,27 @@ Map _$GenericClassWithConverterToJson( 'fieldObject': instance.fieldObject, 'fieldDynamic': instance.fieldDynamic, 'fieldInt': instance.fieldInt, - 'fieldT': () { - final val = instance.fieldT; - return val == null ? null : _SimpleConverter().toJson(val); - }(), - 'fieldS': () { - final val = instance.fieldS; - return val == null ? null : _SimpleConverter().toJson(val); - }(), + 'fieldT': _$JsonConverterToJson, T>( + instance.fieldT, _SimpleConverter().toJson), + 'fieldS': _$JsonConverterToJson, S>( + instance.fieldS, _SimpleConverter().toJson), 'duration': instance.duration?.inMicroseconds, 'listDuration': instance.listDuration?.map((e) => e.inMicroseconds).toList(), }; +Value? _$JsonConverterFromJson( + Object? json, + Value? Function(Json json) fromJson, +) => + json == null ? null : fromJson(json as Json); + +Json? _$JsonConverterToJson( + Value? value, + Json? Function(Value value) toJson, +) => + value == null ? null : toJson(value); + Issue980ParentClass _$Issue980ParentClassFromJson(Map json) => Issue980ParentClass( (json['list'] as List) diff --git a/json_serializable/test/kitchen_sink/kitchen_sink.g.dart b/json_serializable/test/kitchen_sink/kitchen_sink.g.dart index 25085663a..d3147db20 100644 --- a/json_serializable/test/kitchen_sink/kitchen_sink.g.dart +++ b/json_serializable/test/kitchen_sink/kitchen_sink.g.dart @@ -148,19 +148,13 @@ JsonConverterTestClass _$JsonConverterTestClassFromJson( (k, e) => MapEntry(k, const BigIntStringConverter().fromJson(e as String)), ), - () { - final val = json['nullableBigInt']; - return val == null - ? null - : const BigIntStringConverter().fromJson(val as String); - }(), + _$JsonConverterFromJson( + json['nullableBigInt'], const BigIntStringConverter().fromJson), (json['nullableBigIntMap'] as Map).map( - (k, e) => MapEntry(k, () { - final val = e; - return val == null - ? null - : const BigIntStringConverter().fromJson(val as String); - }()), + (k, e) => MapEntry( + k, + _$JsonConverterFromJson( + e, const BigIntStringConverter().fromJson)), ), TrivialNumberConverter.instance.fromJson(json['numberSilly'] as int?), (json['numberSillySet'] as List) @@ -185,37 +179,38 @@ Map _$JsonConverterTestClassToJson( 'bigInt': const BigIntStringConverter().toJson(instance.bigInt), 'bigIntMap': instance.bigIntMap .map((k, e) => MapEntry(k, const BigIntStringConverter().toJson(e))), - 'nullableBigInt': () { - final val = instance.nullableBigInt; - return val == null ? null : const BigIntStringConverter().toJson(val); - }(), - 'nullableBigIntMap': - instance.nullableBigIntMap.map((k, e) => MapEntry(k, () { - final val = e; - return val == null - ? null - : const BigIntStringConverter().toJson(val); - }())), + 'nullableBigInt': _$JsonConverterToJson( + instance.nullableBigInt, const BigIntStringConverter().toJson), + 'nullableBigIntMap': instance.nullableBigIntMap.map((k, e) => MapEntry( + k, + _$JsonConverterToJson( + e, const BigIntStringConverter().toJson))), 'numberSilly': TrivialNumberConverter.instance.toJson(instance.numberSilly), 'numberSillySet': instance.numberSillySet .map(TrivialNumberConverter.instance.toJson) .toList(), 'dateTime': instance.dateTime?.toIso8601String(), - 'nullableNumberSilly': () { - final val = instance.nullableNumberSilly; - return val == null ? null : TrivialNumberConverter.instance.toJson(val); - }(), + 'nullableNumberSilly': _$JsonConverterToJson( + instance.nullableNumberSilly, TrivialNumberConverter.instance.toJson), 'nullableNumberSillySet': instance.nullableNumberSillySet - .map((e) => () { - final val = e; - return val == null - ? null - : TrivialNumberConverter.instance.toJson(val); - }()) + .map((e) => _$JsonConverterToJson( + e, TrivialNumberConverter.instance.toJson)) .toList(), }; +Value? _$JsonConverterFromJson( + Object? json, + Value? Function(Json json) fromJson, +) => + json == null ? null : fromJson(json as Json); + +Json? _$JsonConverterToJson( + Value? value, + Json? Function(Value value) toJson, +) => + value == null ? null : toJson(value); + JsonConverterGeneric _$JsonConverterGenericFromJson( Map json) => JsonConverterGeneric( diff --git a/json_serializable/test/kitchen_sink/kitchen_sink.g_any_map.g.dart b/json_serializable/test/kitchen_sink/kitchen_sink.g_any_map.g.dart index 6cf1c078f..adcea21dc 100644 --- a/json_serializable/test/kitchen_sink/kitchen_sink.g_any_map.g.dart +++ b/json_serializable/test/kitchen_sink/kitchen_sink.g_any_map.g.dart @@ -139,19 +139,13 @@ JsonConverterTestClass _$JsonConverterTestClassFromJson(Map json) => (k, e) => MapEntry( k as String, const BigIntStringConverter().fromJson(e as String)), ), - () { - final val = json['nullableBigInt']; - return val == null - ? null - : const BigIntStringConverter().fromJson(val as String); - }(), + _$JsonConverterFromJson( + json['nullableBigInt'], const BigIntStringConverter().fromJson), (json['nullableBigIntMap'] as Map).map( - (k, e) => MapEntry(k as String, () { - final val = e; - return val == null - ? null - : const BigIntStringConverter().fromJson(val as String); - }()), + (k, e) => MapEntry( + k as String, + _$JsonConverterFromJson( + e, const BigIntStringConverter().fromJson)), ), TrivialNumberConverter.instance.fromJson(json['numberSilly'] as int?), (json['numberSillySet'] as List) @@ -176,37 +170,38 @@ Map _$JsonConverterTestClassToJson( 'bigInt': const BigIntStringConverter().toJson(instance.bigInt), 'bigIntMap': instance.bigIntMap .map((k, e) => MapEntry(k, const BigIntStringConverter().toJson(e))), - 'nullableBigInt': () { - final val = instance.nullableBigInt; - return val == null ? null : const BigIntStringConverter().toJson(val); - }(), - 'nullableBigIntMap': - instance.nullableBigIntMap.map((k, e) => MapEntry(k, () { - final val = e; - return val == null - ? null - : const BigIntStringConverter().toJson(val); - }())), + 'nullableBigInt': _$JsonConverterToJson( + instance.nullableBigInt, const BigIntStringConverter().toJson), + 'nullableBigIntMap': instance.nullableBigIntMap.map((k, e) => MapEntry( + k, + _$JsonConverterToJson( + e, const BigIntStringConverter().toJson))), 'numberSilly': TrivialNumberConverter.instance.toJson(instance.numberSilly), 'numberSillySet': instance.numberSillySet .map(TrivialNumberConverter.instance.toJson) .toList(), 'dateTime': instance.dateTime?.toIso8601String(), - 'nullableNumberSilly': () { - final val = instance.nullableNumberSilly; - return val == null ? null : TrivialNumberConverter.instance.toJson(val); - }(), + 'nullableNumberSilly': _$JsonConverterToJson( + instance.nullableNumberSilly, TrivialNumberConverter.instance.toJson), 'nullableNumberSillySet': instance.nullableNumberSillySet - .map((e) => () { - final val = e; - return val == null - ? null - : TrivialNumberConverter.instance.toJson(val); - }()) + .map((e) => _$JsonConverterToJson( + e, TrivialNumberConverter.instance.toJson)) .toList(), }; +Value? _$JsonConverterFromJson( + Object? json, + Value? Function(Json json) fromJson, +) => + json == null ? null : fromJson(json as Json); + +Json? _$JsonConverterToJson( + Value? value, + Json? Function(Value value) toJson, +) => + value == null ? null : toJson(value); + JsonConverterGeneric _$JsonConverterGenericFromJson( Map json) => JsonConverterGeneric( diff --git a/json_serializable/test/kitchen_sink/kitchen_sink.g_any_map__checked.g.dart b/json_serializable/test/kitchen_sink/kitchen_sink.g_any_map__checked.g.dart index 9930711e7..d665b52fc 100644 --- a/json_serializable/test/kitchen_sink/kitchen_sink.g_any_map__checked.g.dart +++ b/json_serializable/test/kitchen_sink/kitchen_sink.g_any_map__checked.g.dart @@ -201,22 +201,15 @@ JsonConverterTestClass _$JsonConverterTestClassFromJson(Map json) => )), $checkedConvert( 'nullableBigInt', - (v) => () { - final val = v; - return val == null - ? null - : const BigIntStringConverter().fromJson(val as String); - }()), + (v) => _$JsonConverterFromJson( + v, const BigIntStringConverter().fromJson)), $checkedConvert( 'nullableBigIntMap', (v) => (v as Map).map( - (k, e) => MapEntry(k as String, () { - final val = e; - return val == null - ? null - : const BigIntStringConverter() - .fromJson(val as String); - }()), + (k, e) => MapEntry( + k as String, + _$JsonConverterFromJson( + e, const BigIntStringConverter().fromJson)), )), $checkedConvert('numberSilly', (v) => TrivialNumberConverter.instance.fromJson(v as int?)), @@ -250,37 +243,38 @@ Map _$JsonConverterTestClassToJson( 'bigInt': const BigIntStringConverter().toJson(instance.bigInt), 'bigIntMap': instance.bigIntMap .map((k, e) => MapEntry(k, const BigIntStringConverter().toJson(e))), - 'nullableBigInt': () { - final val = instance.nullableBigInt; - return val == null ? null : const BigIntStringConverter().toJson(val); - }(), - 'nullableBigIntMap': - instance.nullableBigIntMap.map((k, e) => MapEntry(k, () { - final val = e; - return val == null - ? null - : const BigIntStringConverter().toJson(val); - }())), + 'nullableBigInt': _$JsonConverterToJson( + instance.nullableBigInt, const BigIntStringConverter().toJson), + 'nullableBigIntMap': instance.nullableBigIntMap.map((k, e) => MapEntry( + k, + _$JsonConverterToJson( + e, const BigIntStringConverter().toJson))), 'numberSilly': TrivialNumberConverter.instance.toJson(instance.numberSilly), 'numberSillySet': instance.numberSillySet .map(TrivialNumberConverter.instance.toJson) .toList(), 'dateTime': instance.dateTime?.toIso8601String(), - 'nullableNumberSilly': () { - final val = instance.nullableNumberSilly; - return val == null ? null : TrivialNumberConverter.instance.toJson(val); - }(), + 'nullableNumberSilly': _$JsonConverterToJson( + instance.nullableNumberSilly, TrivialNumberConverter.instance.toJson), 'nullableNumberSillySet': instance.nullableNumberSillySet - .map((e) => () { - final val = e; - return val == null - ? null - : TrivialNumberConverter.instance.toJson(val); - }()) + .map((e) => _$JsonConverterToJson( + e, TrivialNumberConverter.instance.toJson)) .toList(), }; +Value? _$JsonConverterFromJson( + Object? json, + Value? Function(Json json) fromJson, +) => + json == null ? null : fromJson(json as Json); + +Json? _$JsonConverterToJson( + Value? value, + Json? Function(Value value) toJson, +) => + value == null ? null : toJson(value); + JsonConverterGeneric _$JsonConverterGenericFromJson( Map json) => $checkedCreate( diff --git a/json_serializable/test/kitchen_sink/kitchen_sink.g_exclude_null.g.dart b/json_serializable/test/kitchen_sink/kitchen_sink.g_exclude_null.g.dart index f2246b379..42cdd3eb8 100644 --- a/json_serializable/test/kitchen_sink/kitchen_sink.g_exclude_null.g.dart +++ b/json_serializable/test/kitchen_sink/kitchen_sink.g_exclude_null.g.dart @@ -156,19 +156,13 @@ JsonConverterTestClass _$JsonConverterTestClassFromJson( (k, e) => MapEntry(k, const BigIntStringConverter().fromJson(e as String)), ), - () { - final val = json['nullableBigInt']; - return val == null - ? null - : const BigIntStringConverter().fromJson(val as String); - }(), + _$JsonConverterFromJson( + json['nullableBigInt'], const BigIntStringConverter().fromJson), (json['nullableBigIntMap'] as Map).map( - (k, e) => MapEntry(k, () { - final val = e; - return val == null - ? null - : const BigIntStringConverter().fromJson(val as String); - }()), + (k, e) => MapEntry( + k, + _$JsonConverterFromJson( + e, const BigIntStringConverter().fromJson)), ), TrivialNumberConverter.instance.fromJson(json['numberSilly'] as int?), (json['numberSillySet'] as List) @@ -200,38 +194,43 @@ Map _$JsonConverterTestClassToJson( writeNotNull('bigInt', const BigIntStringConverter().toJson(instance.bigInt)); val['bigIntMap'] = instance.bigIntMap .map((k, e) => MapEntry(k, const BigIntStringConverter().toJson(e))); - writeNotNull('nullableBigInt', () { - final val = instance.nullableBigInt; - return val == null ? null : const BigIntStringConverter().toJson(val); - }()); - val['nullableBigIntMap'] = - instance.nullableBigIntMap.map((k, e) => MapEntry(k, () { - final val = e; - return val == null - ? null - : const BigIntStringConverter().toJson(val); - }())); + writeNotNull( + 'nullableBigInt', + _$JsonConverterToJson( + instance.nullableBigInt, const BigIntStringConverter().toJson)); + val['nullableBigIntMap'] = instance.nullableBigIntMap.map((k, e) => MapEntry( + k, + _$JsonConverterToJson( + e, const BigIntStringConverter().toJson))); writeNotNull('numberSilly', TrivialNumberConverter.instance.toJson(instance.numberSilly)); val['numberSillySet'] = instance.numberSillySet .map(TrivialNumberConverter.instance.toJson) .toList(); writeNotNull('dateTime', instance.dateTime?.toIso8601String()); - writeNotNull('nullableNumberSilly', () { - final val = instance.nullableNumberSilly; - return val == null ? null : TrivialNumberConverter.instance.toJson(val); - }()); + writeNotNull( + 'nullableNumberSilly', + _$JsonConverterToJson(instance.nullableNumberSilly, + TrivialNumberConverter.instance.toJson)); val['nullableNumberSillySet'] = instance.nullableNumberSillySet - .map((e) => () { - final val = e; - return val == null - ? null - : TrivialNumberConverter.instance.toJson(val); - }()) + .map((e) => _$JsonConverterToJson( + e, TrivialNumberConverter.instance.toJson)) .toList(); return val; } +Value? _$JsonConverterFromJson( + Object? json, + Value? Function(Json json) fromJson, +) => + json == null ? null : fromJson(json as Json); + +Json? _$JsonConverterToJson( + Value? value, + Json? Function(Value value) toJson, +) => + value == null ? null : toJson(value); + JsonConverterGeneric _$JsonConverterGenericFromJson( Map json) => JsonConverterGeneric( diff --git a/json_serializable/test/kitchen_sink/kitchen_sink.g_explicit_to_json.g.dart b/json_serializable/test/kitchen_sink/kitchen_sink.g_explicit_to_json.g.dart index ab3fc4b9e..f3cb5c356 100644 --- a/json_serializable/test/kitchen_sink/kitchen_sink.g_explicit_to_json.g.dart +++ b/json_serializable/test/kitchen_sink/kitchen_sink.g_explicit_to_json.g.dart @@ -150,19 +150,13 @@ JsonConverterTestClass _$JsonConverterTestClassFromJson( (k, e) => MapEntry(k, const BigIntStringConverter().fromJson(e as String)), ), - () { - final val = json['nullableBigInt']; - return val == null - ? null - : const BigIntStringConverter().fromJson(val as String); - }(), + _$JsonConverterFromJson( + json['nullableBigInt'], const BigIntStringConverter().fromJson), (json['nullableBigIntMap'] as Map).map( - (k, e) => MapEntry(k, () { - final val = e; - return val == null - ? null - : const BigIntStringConverter().fromJson(val as String); - }()), + (k, e) => MapEntry( + k, + _$JsonConverterFromJson( + e, const BigIntStringConverter().fromJson)), ), TrivialNumberConverter.instance.fromJson(json['numberSilly'] as int?), (json['numberSillySet'] as List) @@ -187,37 +181,38 @@ Map _$JsonConverterTestClassToJson( 'bigInt': const BigIntStringConverter().toJson(instance.bigInt), 'bigIntMap': instance.bigIntMap .map((k, e) => MapEntry(k, const BigIntStringConverter().toJson(e))), - 'nullableBigInt': () { - final val = instance.nullableBigInt; - return val == null ? null : const BigIntStringConverter().toJson(val); - }(), - 'nullableBigIntMap': - instance.nullableBigIntMap.map((k, e) => MapEntry(k, () { - final val = e; - return val == null - ? null - : const BigIntStringConverter().toJson(val); - }())), + 'nullableBigInt': _$JsonConverterToJson( + instance.nullableBigInt, const BigIntStringConverter().toJson), + 'nullableBigIntMap': instance.nullableBigIntMap.map((k, e) => MapEntry( + k, + _$JsonConverterToJson( + e, const BigIntStringConverter().toJson))), 'numberSilly': TrivialNumberConverter.instance.toJson(instance.numberSilly), 'numberSillySet': instance.numberSillySet .map(TrivialNumberConverter.instance.toJson) .toList(), 'dateTime': instance.dateTime?.toIso8601String(), - 'nullableNumberSilly': () { - final val = instance.nullableNumberSilly; - return val == null ? null : TrivialNumberConverter.instance.toJson(val); - }(), + 'nullableNumberSilly': _$JsonConverterToJson( + instance.nullableNumberSilly, TrivialNumberConverter.instance.toJson), 'nullableNumberSillySet': instance.nullableNumberSillySet - .map((e) => () { - final val = e; - return val == null - ? null - : TrivialNumberConverter.instance.toJson(val); - }()) + .map((e) => _$JsonConverterToJson( + e, TrivialNumberConverter.instance.toJson)) .toList(), }; +Value? _$JsonConverterFromJson( + Object? json, + Value? Function(Json json) fromJson, +) => + json == null ? null : fromJson(json as Json); + +Json? _$JsonConverterToJson( + Value? value, + Json? Function(Value value) toJson, +) => + value == null ? null : toJson(value); + JsonConverterGeneric _$JsonConverterGenericFromJson( Map json) => JsonConverterGeneric( From fe7d10f7f3b9024792640c4b5de0304ec433f3c1 Mon Sep 17 00:00:00 2001 From: Remi Rousselet Date: Mon, 25 Apr 2022 22:01:11 +0200 Subject: [PATCH 3/6] Remove dead code --- json_serializable/lib/src/type_helper.dart | 6 ------ 1 file changed, 6 deletions(-) diff --git a/json_serializable/lib/src/type_helper.dart b/json_serializable/lib/src/type_helper.dart index d8e553a1f..b01249334 100644 --- a/json_serializable/lib/src/type_helper.dart +++ b/json_serializable/lib/src/type_helper.dart @@ -35,12 +35,6 @@ abstract class TypeHelperContextWithConfig extends TypeHelperContext { ClassConfig get config; } -class Expression { - Expression(this.expression, this.isNullable); - final String expression; - final bool isNullable; -} - abstract class TypeHelper { const TypeHelper(); From 663238e35d5bcd6868e4dca99433bf9d0ec29cca Mon Sep 17 00:00:00 2001 From: Remi Rousselet Date: Tue, 26 Apr 2022 10:24:29 +0200 Subject: [PATCH 4/6] Fix tests --- .../src/type_helpers/json_converter_helper.dart | 2 +- .../test/generic_files/generic_class.g.dart | 17 ++++++++--------- .../test/kitchen_sink/kitchen_sink.g.dart | 16 ++++++---------- .../kitchen_sink/kitchen_sink.g_any_map.g.dart | 16 ++++++---------- .../kitchen_sink.g_any_map__checked.g.dart | 15 +++++++-------- .../kitchen_sink.g_exclude_null.g.dart | 17 +++++++---------- .../kitchen_sink.g_explicit_to_json.g.dart | 16 ++++++---------- .../test/kitchen_sink/kitchen_sink_test.dart | 11 +++++++++-- 8 files changed, 50 insertions(+), 60 deletions(-) diff --git a/json_serializable/lib/src/type_helpers/json_converter_helper.dart b/json_serializable/lib/src/type_helpers/json_converter_helper.dart index 6e611084f..10fa483b4 100644 --- a/json_serializable/lib/src/type_helpers/json_converter_helper.dart +++ b/json_serializable/lib/src/type_helpers/json_converter_helper.dart @@ -263,7 +263,7 @@ _ConverterMatch? _compatibleMatch( final fieldType = jsonConverterSuper.typeArguments[0]; // Allow assigning T to T? - if (fieldType == targetType.promoteNonNullable()) { + if (fieldType == targetType || fieldType == targetType.promoteNonNullable()) { return _ConverterMatch( annotation, constantValue, diff --git a/json_serializable/test/generic_files/generic_class.g.dart b/json_serializable/test/generic_files/generic_class.g.dart index 9e108bd49..c2f216483 100644 --- a/json_serializable/test/generic_files/generic_class.g.dart +++ b/json_serializable/test/generic_files/generic_class.g.dart @@ -43,12 +43,10 @@ GenericClassWithConverter json['fieldT'], _SimpleConverter().fromJson) ..fieldS = _$JsonConverterFromJson, S>( json['fieldS'], _SimpleConverter().fromJson) - ..duration = json['duration'] == null - ? null - : Duration(microseconds: json['duration'] as int) - ..listDuration = (json['listDuration'] as List?) - ?.map((e) => Duration(microseconds: e as int)) - .toList(); + ..duration = const _DurationMillisecondConverter.named() + .fromJson(json['duration'] as int?) + ..listDuration = const _DurationListMillisecondConverter() + .fromJson(json['listDuration'] as int?); Map _$GenericClassWithConverterToJson( GenericClassWithConverter instance) => @@ -60,9 +58,10 @@ Map _$GenericClassWithConverterToJson( instance.fieldT, _SimpleConverter().toJson), 'fieldS': _$JsonConverterToJson, S>( instance.fieldS, _SimpleConverter().toJson), - 'duration': instance.duration?.inMicroseconds, - 'listDuration': - instance.listDuration?.map((e) => e.inMicroseconds).toList(), + 'duration': + const _DurationMillisecondConverter.named().toJson(instance.duration), + 'listDuration': const _DurationListMillisecondConverter() + .toJson(instance.listDuration), }; Value? _$JsonConverterFromJson( diff --git a/json_serializable/test/kitchen_sink/kitchen_sink.g.dart b/json_serializable/test/kitchen_sink/kitchen_sink.g.dart index d3147db20..c74f6aee0 100644 --- a/json_serializable/test/kitchen_sink/kitchen_sink.g.dart +++ b/json_serializable/test/kitchen_sink/kitchen_sink.g.dart @@ -137,11 +137,9 @@ Map _$KitchenSinkToJson(KitchenSink instance) => JsonConverterTestClass _$JsonConverterTestClassFromJson( Map json) => JsonConverterTestClass( - json['duration'] == null - ? null - : Duration(microseconds: json['duration'] as int), + durationConverter.fromJson(json['duration'] as int?), (json['durationList'] as List) - .map((e) => e == null ? null : Duration(microseconds: e as int)) + .map((e) => durationConverter.fromJson(e as int?)) .toList(), const BigIntStringConverter().fromJson(json['bigInt'] as String), (json['bigIntMap'] as Map).map( @@ -160,9 +158,7 @@ JsonConverterTestClass _$JsonConverterTestClassFromJson( (json['numberSillySet'] as List) .map((e) => TrivialNumberConverter.instance.fromJson(e as int?)) .toSet(), - json['dateTime'] == null - ? null - : DateTime.parse(json['dateTime'] as String), + const EpochDateTimeConverter().fromJson(json['dateTime'] as int?), TrivialNumberConverter.instance .fromJson(json['nullableNumberSilly'] as int?), (json['nullableNumberSillySet'] as List) @@ -173,9 +169,9 @@ JsonConverterTestClass _$JsonConverterTestClassFromJson( Map _$JsonConverterTestClassToJson( JsonConverterTestClass instance) => { - 'duration': instance.duration?.inMicroseconds, + 'duration': durationConverter.toJson(instance.duration), 'durationList': - instance.durationList.map((e) => e?.inMicroseconds).toList(), + instance.durationList.map(durationConverter.toJson).toList(), 'bigInt': const BigIntStringConverter().toJson(instance.bigInt), 'bigIntMap': instance.bigIntMap .map((k, e) => MapEntry(k, const BigIntStringConverter().toJson(e))), @@ -190,7 +186,7 @@ Map _$JsonConverterTestClassToJson( 'numberSillySet': instance.numberSillySet .map(TrivialNumberConverter.instance.toJson) .toList(), - 'dateTime': instance.dateTime?.toIso8601String(), + 'dateTime': const EpochDateTimeConverter().toJson(instance.dateTime), 'nullableNumberSilly': _$JsonConverterToJson( instance.nullableNumberSilly, TrivialNumberConverter.instance.toJson), 'nullableNumberSillySet': instance.nullableNumberSillySet diff --git a/json_serializable/test/kitchen_sink/kitchen_sink.g_any_map.g.dart b/json_serializable/test/kitchen_sink/kitchen_sink.g_any_map.g.dart index adcea21dc..638091dcc 100644 --- a/json_serializable/test/kitchen_sink/kitchen_sink.g_any_map.g.dart +++ b/json_serializable/test/kitchen_sink/kitchen_sink.g_any_map.g.dart @@ -128,11 +128,9 @@ Map _$KitchenSinkToJson(KitchenSink instance) => JsonConverterTestClass _$JsonConverterTestClassFromJson(Map json) => JsonConverterTestClass( - json['duration'] == null - ? null - : Duration(microseconds: json['duration'] as int), + durationConverter.fromJson(json['duration'] as int?), (json['durationList'] as List) - .map((e) => e == null ? null : Duration(microseconds: e as int)) + .map((e) => durationConverter.fromJson(e as int?)) .toList(), const BigIntStringConverter().fromJson(json['bigInt'] as String), (json['bigIntMap'] as Map).map( @@ -151,9 +149,7 @@ JsonConverterTestClass _$JsonConverterTestClassFromJson(Map json) => (json['numberSillySet'] as List) .map((e) => TrivialNumberConverter.instance.fromJson(e as int?)) .toSet(), - json['dateTime'] == null - ? null - : DateTime.parse(json['dateTime'] as String), + const EpochDateTimeConverter().fromJson(json['dateTime'] as int?), TrivialNumberConverter.instance .fromJson(json['nullableNumberSilly'] as int?), (json['nullableNumberSillySet'] as List) @@ -164,9 +160,9 @@ JsonConverterTestClass _$JsonConverterTestClassFromJson(Map json) => Map _$JsonConverterTestClassToJson( JsonConverterTestClass instance) => { - 'duration': instance.duration?.inMicroseconds, + 'duration': durationConverter.toJson(instance.duration), 'durationList': - instance.durationList.map((e) => e?.inMicroseconds).toList(), + instance.durationList.map(durationConverter.toJson).toList(), 'bigInt': const BigIntStringConverter().toJson(instance.bigInt), 'bigIntMap': instance.bigIntMap .map((k, e) => MapEntry(k, const BigIntStringConverter().toJson(e))), @@ -181,7 +177,7 @@ Map _$JsonConverterTestClassToJson( 'numberSillySet': instance.numberSillySet .map(TrivialNumberConverter.instance.toJson) .toList(), - 'dateTime': instance.dateTime?.toIso8601String(), + 'dateTime': const EpochDateTimeConverter().toJson(instance.dateTime), 'nullableNumberSilly': _$JsonConverterToJson( instance.nullableNumberSilly, TrivialNumberConverter.instance.toJson), 'nullableNumberSillySet': instance.nullableNumberSillySet diff --git a/json_serializable/test/kitchen_sink/kitchen_sink.g_any_map__checked.g.dart b/json_serializable/test/kitchen_sink/kitchen_sink.g_any_map__checked.g.dart index d665b52fc..1d8c4c26b 100644 --- a/json_serializable/test/kitchen_sink/kitchen_sink.g_any_map__checked.g.dart +++ b/json_serializable/test/kitchen_sink/kitchen_sink.g_any_map__checked.g.dart @@ -183,13 +183,12 @@ JsonConverterTestClass _$JsonConverterTestClassFromJson(Map json) => json, ($checkedConvert) { final val = JsonConverterTestClass( - $checkedConvert('duration', - (v) => v == null ? null : Duration(microseconds: v as int)), + $checkedConvert( + 'duration', (v) => durationConverter.fromJson(v as int?)), $checkedConvert( 'durationList', (v) => (v as List) - .map((e) => - e == null ? null : Duration(microseconds: e as int)) + .map((e) => durationConverter.fromJson(e as int?)) .toList()), $checkedConvert('bigInt', (v) => const BigIntStringConverter().fromJson(v as String)), @@ -220,7 +219,7 @@ JsonConverterTestClass _$JsonConverterTestClassFromJson(Map json) => TrivialNumberConverter.instance.fromJson(e as int?)) .toSet()), $checkedConvert('dateTime', - (v) => v == null ? null : DateTime.parse(v as String)), + (v) => const EpochDateTimeConverter().fromJson(v as int?)), $checkedConvert('nullableNumberSilly', (v) => TrivialNumberConverter.instance.fromJson(v as int?)), $checkedConvert( @@ -237,9 +236,9 @@ JsonConverterTestClass _$JsonConverterTestClassFromJson(Map json) => Map _$JsonConverterTestClassToJson( JsonConverterTestClass instance) => { - 'duration': instance.duration?.inMicroseconds, + 'duration': durationConverter.toJson(instance.duration), 'durationList': - instance.durationList.map((e) => e?.inMicroseconds).toList(), + instance.durationList.map(durationConverter.toJson).toList(), 'bigInt': const BigIntStringConverter().toJson(instance.bigInt), 'bigIntMap': instance.bigIntMap .map((k, e) => MapEntry(k, const BigIntStringConverter().toJson(e))), @@ -254,7 +253,7 @@ Map _$JsonConverterTestClassToJson( 'numberSillySet': instance.numberSillySet .map(TrivialNumberConverter.instance.toJson) .toList(), - 'dateTime': instance.dateTime?.toIso8601String(), + 'dateTime': const EpochDateTimeConverter().toJson(instance.dateTime), 'nullableNumberSilly': _$JsonConverterToJson( instance.nullableNumberSilly, TrivialNumberConverter.instance.toJson), 'nullableNumberSillySet': instance.nullableNumberSillySet diff --git a/json_serializable/test/kitchen_sink/kitchen_sink.g_exclude_null.g.dart b/json_serializable/test/kitchen_sink/kitchen_sink.g_exclude_null.g.dart index 42cdd3eb8..a0246e8bc 100644 --- a/json_serializable/test/kitchen_sink/kitchen_sink.g_exclude_null.g.dart +++ b/json_serializable/test/kitchen_sink/kitchen_sink.g_exclude_null.g.dart @@ -145,11 +145,9 @@ Map _$KitchenSinkToJson(KitchenSink instance) { JsonConverterTestClass _$JsonConverterTestClassFromJson( Map json) => JsonConverterTestClass( - json['duration'] == null - ? null - : Duration(microseconds: json['duration'] as int), + durationConverter.fromJson(json['duration'] as int?), (json['durationList'] as List) - .map((e) => e == null ? null : Duration(microseconds: e as int)) + .map((e) => durationConverter.fromJson(e as int?)) .toList(), const BigIntStringConverter().fromJson(json['bigInt'] as String), (json['bigIntMap'] as Map).map( @@ -168,9 +166,7 @@ JsonConverterTestClass _$JsonConverterTestClassFromJson( (json['numberSillySet'] as List) .map((e) => TrivialNumberConverter.instance.fromJson(e as int?)) .toSet(), - json['dateTime'] == null - ? null - : DateTime.parse(json['dateTime'] as String), + const EpochDateTimeConverter().fromJson(json['dateTime'] as int?), TrivialNumberConverter.instance .fromJson(json['nullableNumberSilly'] as int?), (json['nullableNumberSillySet'] as List) @@ -188,9 +184,9 @@ Map _$JsonConverterTestClassToJson( } } - writeNotNull('duration', instance.duration?.inMicroseconds); + writeNotNull('duration', durationConverter.toJson(instance.duration)); val['durationList'] = - instance.durationList.map((e) => e?.inMicroseconds).toList(); + instance.durationList.map(durationConverter.toJson).toList(); writeNotNull('bigInt', const BigIntStringConverter().toJson(instance.bigInt)); val['bigIntMap'] = instance.bigIntMap .map((k, e) => MapEntry(k, const BigIntStringConverter().toJson(e))); @@ -207,7 +203,8 @@ Map _$JsonConverterTestClassToJson( val['numberSillySet'] = instance.numberSillySet .map(TrivialNumberConverter.instance.toJson) .toList(); - writeNotNull('dateTime', instance.dateTime?.toIso8601String()); + writeNotNull( + 'dateTime', const EpochDateTimeConverter().toJson(instance.dateTime)); writeNotNull( 'nullableNumberSilly', _$JsonConverterToJson(instance.nullableNumberSilly, diff --git a/json_serializable/test/kitchen_sink/kitchen_sink.g_explicit_to_json.g.dart b/json_serializable/test/kitchen_sink/kitchen_sink.g_explicit_to_json.g.dart index f3cb5c356..e6f218fa6 100644 --- a/json_serializable/test/kitchen_sink/kitchen_sink.g_explicit_to_json.g.dart +++ b/json_serializable/test/kitchen_sink/kitchen_sink.g_explicit_to_json.g.dart @@ -139,11 +139,9 @@ Map _$KitchenSinkToJson(KitchenSink instance) => JsonConverterTestClass _$JsonConverterTestClassFromJson( Map json) => JsonConverterTestClass( - json['duration'] == null - ? null - : Duration(microseconds: json['duration'] as int), + durationConverter.fromJson(json['duration'] as int?), (json['durationList'] as List) - .map((e) => e == null ? null : Duration(microseconds: e as int)) + .map((e) => durationConverter.fromJson(e as int?)) .toList(), const BigIntStringConverter().fromJson(json['bigInt'] as String), (json['bigIntMap'] as Map).map( @@ -162,9 +160,7 @@ JsonConverterTestClass _$JsonConverterTestClassFromJson( (json['numberSillySet'] as List) .map((e) => TrivialNumberConverter.instance.fromJson(e as int?)) .toSet(), - json['dateTime'] == null - ? null - : DateTime.parse(json['dateTime'] as String), + const EpochDateTimeConverter().fromJson(json['dateTime'] as int?), TrivialNumberConverter.instance .fromJson(json['nullableNumberSilly'] as int?), (json['nullableNumberSillySet'] as List) @@ -175,9 +171,9 @@ JsonConverterTestClass _$JsonConverterTestClassFromJson( Map _$JsonConverterTestClassToJson( JsonConverterTestClass instance) => { - 'duration': instance.duration?.inMicroseconds, + 'duration': durationConverter.toJson(instance.duration), 'durationList': - instance.durationList.map((e) => e?.inMicroseconds).toList(), + instance.durationList.map(durationConverter.toJson).toList(), 'bigInt': const BigIntStringConverter().toJson(instance.bigInt), 'bigIntMap': instance.bigIntMap .map((k, e) => MapEntry(k, const BigIntStringConverter().toJson(e))), @@ -192,7 +188,7 @@ Map _$JsonConverterTestClassToJson( 'numberSillySet': instance.numberSillySet .map(TrivialNumberConverter.instance.toJson) .toList(), - 'dateTime': instance.dateTime?.toIso8601String(), + 'dateTime': const EpochDateTimeConverter().toJson(instance.dateTime), 'nullableNumberSilly': _$JsonConverterToJson( instance.nullableNumberSilly, TrivialNumberConverter.instance.toJson), 'nullableNumberSillySet': instance.nullableNumberSillySet diff --git a/json_serializable/test/kitchen_sink/kitchen_sink_test.dart b/json_serializable/test/kitchen_sink/kitchen_sink_test.dart index 3e02126e7..e9d7f025f 100644 --- a/json_serializable/test/kitchen_sink/kitchen_sink_test.dart +++ b/json_serializable/test/kitchen_sink/kitchen_sink_test.dart @@ -54,11 +54,14 @@ const _jsonConverterValidValues = { 'duration': 5, 'durationList': [5], 'bigInt': '5', - 'bigIntMap': {'vaule': '5'}, + 'bigIntMap': {'value': '5'}, 'numberSilly': 5, 'numberSillySet': [5], 'dateTime': 5, 'nullableNumberSilly': 5, + 'nullableBigInt': '42', + 'nullableBigIntMap': {'value': '42'}, + 'nullableNumberSillySet': [42], }; void _nonNullableTests(KitchenSinkFactory factory) { @@ -97,9 +100,13 @@ void _nullableTests(KitchenSinkFactory factory) { 'durationList': [], 'bigInt': '0', 'bigIntMap': {}, + 'nullableBigInt': '0', + 'nullableBigIntMap': {}, 'numberSilly': 0, 'numberSillySet': [], - 'dateTime': 0 + 'dateTime': 0, + 'nullableNumberSilly': 0, + 'nullableNumberSillySet': [], }); expect(json.keys, unorderedEquals(_jsonConverterValidValues.keys)); From 8b273f8a143bb05a951fffe10c7e47f0ac02c57a Mon Sep 17 00:00:00 2001 From: Remi Rousselet Date: Tue, 26 Apr 2022 11:37:45 +0200 Subject: [PATCH 5/6] Add more tests --- .../test/json_serializable_test.dart | 1 + .../kitchen_sink/kitchen_sink_interface.dart | 3 +++ .../test/kitchen_sink/kitchen_sink_test.dart | 13 +++++++++++ .../test/src/json_converter_test_input.dart | 22 +++++++++++++++++++ 4 files changed, 39 insertions(+) diff --git a/json_serializable/test/json_serializable_test.dart b/json_serializable/test/json_serializable_test.dart index 9c9d8d834..00c950cbd 100644 --- a/json_serializable/test/json_serializable_test.dart +++ b/json_serializable/test/json_serializable_test.dart @@ -84,6 +84,7 @@ const _expectedAnnotatedTests = { 'JsonConverterCtorParams', 'JsonConverterDuplicateAnnotations', 'JsonConverterNamedCtor', + 'JsonConverterNullableToNonNullable', 'JsonConverterOnGetter', 'JsonConverterWithBadTypeArg', 'JsonValueValid', diff --git a/json_serializable/test/kitchen_sink/kitchen_sink_interface.dart b/json_serializable/test/kitchen_sink/kitchen_sink_interface.dart index f69ade258..9f8740943 100644 --- a/json_serializable/test/kitchen_sink/kitchen_sink_interface.dart +++ b/json_serializable/test/kitchen_sink/kitchen_sink_interface.dart @@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import '../test_utils.dart'; +import 'json_converters.dart'; import 'simple_object.dart'; /// A key name that requires special encoding @@ -41,6 +42,8 @@ abstract class KitchenSinkFactory { } abstract class JsonConverterTestClass { + TrivialNumber? nullableNumberSilly; + Map toJson(); } diff --git a/json_serializable/test/kitchen_sink/kitchen_sink_test.dart b/json_serializable/test/kitchen_sink/kitchen_sink_test.dart index e9d7f025f..99e45bf40 100644 --- a/json_serializable/test/kitchen_sink/kitchen_sink_test.dart +++ b/json_serializable/test/kitchen_sink/kitchen_sink_test.dart @@ -6,6 +6,7 @@ import 'package:json_annotation/json_annotation.dart'; import 'package:test/test.dart'; import '../test_utils.dart'; +import 'json_converters.dart'; import 'kitchen_sink.factories.dart'; import 'kitchen_sink_interface.dart'; import 'kitchen_sink_test_shared.dart'; @@ -191,6 +192,18 @@ void _sharedTests(KitchenSinkFactory factory) { roundTripObject(item, factory.fromJson); }); + test('JsonConverters with nullable JSON keys handle `null` JSON values', () { + final item = factory.jsonConverterFromJson({ + ..._jsonConverterValidValues, + 'nullableNumberSilly': null, + }); + + expect( + item.nullableNumberSilly, + isA().having((e) => e.value, 'value', isNull), + ); + }); + test('list and map of DateTime - not null', () { final now = DateTime.now(); final item = factory.ctor(dateTimeIterable: [now]) diff --git a/json_serializable/test/src/json_converter_test_input.dart b/json_serializable/test/src/json_converter_test_input.dart index 10d029b41..fbe7da28c 100644 --- a/json_serializable/test/src/json_converter_test_input.dart +++ b/json_serializable/test/src/json_converter_test_input.dart @@ -189,3 +189,25 @@ class _NeedsConversionConverter @override int toJson(_NeedsConversion object) => 0; } + +@ShouldThrow( + ''' +Could not generate `fromJson` code for `value`. +To support the type `_NeedsConversion` you can: +$converterOrKeyInstructions''', +) +@_NullableConverter() +@JsonSerializable() +class JsonConverterNullableToNonNullable { + late _NeedsConversion value; +} + +class _NullableConverter implements JsonConverter<_NeedsConversion?, Object?> { + const _NullableConverter(); + + @override + _NeedsConversion? fromJson(Object? json) => null; + + @override + Object? toJson(_NeedsConversion? object) => null; +} From bd48ef3fa5a6aa258b9e9ce3b8f8435b0eb28a59 Mon Sep 17 00:00:00 2001 From: Remi Rousselet Date: Thu, 28 Apr 2022 09:31:05 +0200 Subject: [PATCH 6/6] Upgrade to 6.3.0-dev --- json_serializable/CHANGELOG.md | 10 ++++++++++ json_serializable/pubspec.yaml | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/json_serializable/CHANGELOG.md b/json_serializable/CHANGELOG.md index 7be8bd13e..7e79368d3 100644 --- a/json_serializable/CHANGELOG.md +++ b/json_serializable/CHANGELOG.md @@ -1,3 +1,13 @@ +## 6.3.0-dev + +- Added support for using a `JsonConverter` on properties + of type `MyClass?`. ([#822](https://github.com/google/json_serializable.dart/issues/822)) + +## 6.2.0 + +- Added support for the new `FieldRename.screamingSnake` field in + `package:json_annotation`. + ## 6.1.6 - Allow latest `package:analyzer`. diff --git a/json_serializable/pubspec.yaml b/json_serializable/pubspec.yaml index 524f9ad08..6aca13687 100644 --- a/json_serializable/pubspec.yaml +++ b/json_serializable/pubspec.yaml @@ -1,5 +1,5 @@ name: json_serializable -version: 6.1.6 +version: 6.3.0-dev description: >- Automatically generate code for converting to and from JSON by annotating Dart classes.