From 0b4ae8fa42abcf36b7aec565c282553625d1a73d Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Fri, 7 Oct 2022 16:11:45 -0700 Subject: [PATCH] Fix BigInt, DateTime, Uri JsonKey.defaultValue w/ a function Fixes https://github.com/google/json_serializable.dart/issues/1219 Prepare to release v6.5.1 --- json_serializable/CHANGELOG.md | 5 +++ .../lib/src/type_helpers/big_int_helper.dart | 3 +- .../src/type_helpers/date_time_helper.dart | 3 +- .../lib/src/type_helpers/map_helper.dart | 3 +- .../lib/src/type_helpers/to_from_string.dart | 9 ++-- .../lib/src/type_helpers/uri_helper.dart | 3 +- json_serializable/pubspec.yaml | 2 +- .../supported_types/input.type_bigint.dart | 10 +++++ .../supported_types/input.type_bigint.g.dart | 8 ++++ .../supported_types/input.type_datetime.dart | 10 +++++ .../input.type_datetime.g.dart | 8 ++++ .../test/supported_types/input.type_uri.dart | 10 +++++ .../supported_types/input.type_uri.g.dart | 8 ++++ .../type_test.bigint_test.dart | 3 ++ .../type_test.datetime_test.dart | 3 ++ .../supported_types/type_test.uri_test.dart | 3 ++ json_serializable/tool/test_type_builder.dart | 6 +-- json_serializable/tool/test_type_data.dart | 41 +++++++++++++++++-- 18 files changed, 123 insertions(+), 15 deletions(-) diff --git a/json_serializable/CHANGELOG.md b/json_serializable/CHANGELOG.md index 37a261442..9cda69759 100644 --- a/json_serializable/CHANGELOG.md +++ b/json_serializable/CHANGELOG.md @@ -1,3 +1,8 @@ +## 6.5.1 + +- Fixed `BigInt`, `DateTime`, and `Uri` support for `JsonKey.defaultValue` with + a function value. + ## 6.5.0 - Allow constructors to be passed to `JsonKey` parameters that support diff --git a/json_serializable/lib/src/type_helpers/big_int_helper.dart b/json_serializable/lib/src/type_helpers/big_int_helper.dart index 935c75b77..3c5c4316d 100644 --- a/json_serializable/lib/src/type_helpers/big_int_helper.dart +++ b/json_serializable/lib/src/type_helpers/big_int_helper.dart @@ -5,6 +5,7 @@ import 'package:analyzer/dart/element/type.dart'; import 'package:source_helper/source_helper.dart'; +import '../default_container.dart'; import '../type_helper.dart'; import 'to_from_string.dart'; @@ -24,7 +25,7 @@ class BigIntHelper extends TypeHelper { ); @override - String? deserialize( + DefaultContainer? deserialize( DartType targetType, String expression, TypeHelperContext context, diff --git a/json_serializable/lib/src/type_helpers/date_time_helper.dart b/json_serializable/lib/src/type_helpers/date_time_helper.dart index d67b4b2e9..89a526013 100644 --- a/json_serializable/lib/src/type_helpers/date_time_helper.dart +++ b/json_serializable/lib/src/type_helpers/date_time_helper.dart @@ -5,6 +5,7 @@ import 'package:analyzer/dart/element/type.dart'; import 'package:source_helper/source_helper.dart'; +import '../default_container.dart'; import '../type_helper.dart'; import 'to_from_string.dart'; @@ -24,7 +25,7 @@ class DateTimeHelper extends TypeHelper { ); @override - String? deserialize( + DefaultContainer? deserialize( DartType targetType, String expression, TypeHelperContext context, diff --git a/json_serializable/lib/src/type_helpers/map_helper.dart b/json_serializable/lib/src/type_helpers/map_helper.dart index 7c99fcf5a..e84bc5193 100644 --- a/json_serializable/lib/src/type_helpers/map_helper.dart +++ b/json_serializable/lib/src/type_helpers/map_helper.dart @@ -126,7 +126,8 @@ class MapHelper extends TypeHelper { final toFromString = _forType(keyArg); if (toFromString != null) { - keyUsage = toFromString.deserialize(keyArg, keyUsage, false, true)!; + keyUsage = + toFromString.deserialize(keyArg, keyUsage, false, true).toString(); } return '($expression $mapCast)$optionalQuestion.map( ' diff --git a/json_serializable/lib/src/type_helpers/to_from_string.dart b/json_serializable/lib/src/type_helpers/to_from_string.dart index 68ea04a0a..5c6174ad0 100644 --- a/json_serializable/lib/src/type_helpers/to_from_string.dart +++ b/json_serializable/lib/src/type_helpers/to_from_string.dart @@ -5,7 +5,7 @@ import 'package:analyzer/dart/element/type.dart'; import 'package:source_gen/source_gen.dart'; -import '../utils.dart'; +import '../default_container.dart'; final bigIntString = ToFromStringHelper( 'BigInt.parse', @@ -64,7 +64,7 @@ class ToFromStringHelper { return '$expression.$_toString'; } - String? deserialize( + DefaultContainer? deserialize( DartType type, String expression, bool nullable, @@ -78,6 +78,9 @@ class ToFromStringHelper { final output = '$_parse($parseParam)'; - return nullable ? ifNullOrElse(expression, 'null', output) : output; + return DefaultContainer( + expression, + output, + ); } } diff --git a/json_serializable/lib/src/type_helpers/uri_helper.dart b/json_serializable/lib/src/type_helpers/uri_helper.dart index cea9e285e..49dea856a 100644 --- a/json_serializable/lib/src/type_helpers/uri_helper.dart +++ b/json_serializable/lib/src/type_helpers/uri_helper.dart @@ -5,6 +5,7 @@ import 'package:analyzer/dart/element/type.dart'; import 'package:source_helper/source_helper.dart'; +import '../default_container.dart'; import '../type_helper.dart'; import 'to_from_string.dart'; @@ -24,7 +25,7 @@ class UriHelper extends TypeHelper { ); @override - String? deserialize( + DefaultContainer? deserialize( DartType targetType, String expression, TypeHelperContext context, diff --git a/json_serializable/pubspec.yaml b/json_serializable/pubspec.yaml index 926eb4b7d..24c3204d3 100644 --- a/json_serializable/pubspec.yaml +++ b/json_serializable/pubspec.yaml @@ -1,5 +1,5 @@ name: json_serializable -version: 6.5.0 +version: 6.5.1 description: >- Automatically generate code for converting to and from JSON by annotating Dart classes. diff --git a/json_serializable/test/supported_types/input.type_bigint.dart b/json_serializable/test/supported_types/input.type_bigint.dart index 07ab37ba4..f376da7a1 100644 --- a/json_serializable/test/supported_types/input.type_bigint.dart +++ b/json_serializable/test/supported_types/input.type_bigint.dart @@ -10,8 +10,12 @@ part 'input.type_bigint.g.dart'; class SimpleClass { final BigInt value; + @JsonKey(defaultValue: _defaultValueFunc) + BigInt withDefault; + SimpleClass( this.value, + this.withDefault, ); factory SimpleClass.fromJson(Map json) => @@ -24,8 +28,12 @@ class SimpleClass { class SimpleClassNullable { final BigInt? value; + @JsonKey(defaultValue: _defaultValueFunc) + BigInt? withDefault; + SimpleClassNullable( this.value, + this.withDefault, ); factory SimpleClassNullable.fromJson(Map json) => @@ -33,3 +41,5 @@ class SimpleClassNullable { Map toJson() => _$SimpleClassNullableToJson(this); } + +BigInt _defaultValueFunc() => BigInt.parse('12345'); diff --git a/json_serializable/test/supported_types/input.type_bigint.g.dart b/json_serializable/test/supported_types/input.type_bigint.g.dart index 9fe750fc1..39f10c238 100644 --- a/json_serializable/test/supported_types/input.type_bigint.g.dart +++ b/json_serializable/test/supported_types/input.type_bigint.g.dart @@ -10,20 +10,28 @@ part of 'input.type_bigint.dart'; SimpleClass _$SimpleClassFromJson(Map json) => SimpleClass( BigInt.parse(json['value'] as String), + json['withDefault'] == null + ? _defaultValueFunc() + : BigInt.parse(json['withDefault'] as String), ); Map _$SimpleClassToJson(SimpleClass instance) => { 'value': instance.value.toString(), + 'withDefault': instance.withDefault.toString(), }; SimpleClassNullable _$SimpleClassNullableFromJson(Map json) => SimpleClassNullable( json['value'] == null ? null : BigInt.parse(json['value'] as String), + json['withDefault'] == null + ? _defaultValueFunc() + : BigInt.parse(json['withDefault'] as String), ); Map _$SimpleClassNullableToJson( SimpleClassNullable instance) => { 'value': instance.value?.toString(), + 'withDefault': instance.withDefault?.toString(), }; diff --git a/json_serializable/test/supported_types/input.type_datetime.dart b/json_serializable/test/supported_types/input.type_datetime.dart index 880c7ce89..08d2dd797 100644 --- a/json_serializable/test/supported_types/input.type_datetime.dart +++ b/json_serializable/test/supported_types/input.type_datetime.dart @@ -10,8 +10,12 @@ part 'input.type_datetime.g.dart'; class SimpleClass { final DateTime value; + @JsonKey(defaultValue: _defaultValueFunc) + DateTime withDefault; + SimpleClass( this.value, + this.withDefault, ); factory SimpleClass.fromJson(Map json) => @@ -24,8 +28,12 @@ class SimpleClass { class SimpleClassNullable { final DateTime? value; + @JsonKey(defaultValue: _defaultValueFunc) + DateTime? withDefault; + SimpleClassNullable( this.value, + this.withDefault, ); factory SimpleClassNullable.fromJson(Map json) => @@ -33,3 +41,5 @@ class SimpleClassNullable { Map toJson() => _$SimpleClassNullableToJson(this); } + +DateTime _defaultValueFunc() => DateTime.parse('2020-01-01T00:00:00.000'); diff --git a/json_serializable/test/supported_types/input.type_datetime.g.dart b/json_serializable/test/supported_types/input.type_datetime.g.dart index f4873e041..043bce089 100644 --- a/json_serializable/test/supported_types/input.type_datetime.g.dart +++ b/json_serializable/test/supported_types/input.type_datetime.g.dart @@ -10,20 +10,28 @@ part of 'input.type_datetime.dart'; SimpleClass _$SimpleClassFromJson(Map json) => SimpleClass( DateTime.parse(json['value'] as String), + json['withDefault'] == null + ? _defaultValueFunc() + : DateTime.parse(json['withDefault'] as String), ); Map _$SimpleClassToJson(SimpleClass instance) => { 'value': instance.value.toIso8601String(), + 'withDefault': instance.withDefault.toIso8601String(), }; SimpleClassNullable _$SimpleClassNullableFromJson(Map json) => SimpleClassNullable( json['value'] == null ? null : DateTime.parse(json['value'] as String), + json['withDefault'] == null + ? _defaultValueFunc() + : DateTime.parse(json['withDefault'] as String), ); Map _$SimpleClassNullableToJson( SimpleClassNullable instance) => { 'value': instance.value?.toIso8601String(), + 'withDefault': instance.withDefault?.toIso8601String(), }; diff --git a/json_serializable/test/supported_types/input.type_uri.dart b/json_serializable/test/supported_types/input.type_uri.dart index fe625bbff..1be4d40f9 100644 --- a/json_serializable/test/supported_types/input.type_uri.dart +++ b/json_serializable/test/supported_types/input.type_uri.dart @@ -10,8 +10,12 @@ part 'input.type_uri.g.dart'; class SimpleClass { final Uri value; + @JsonKey(defaultValue: _defaultValueFunc) + Uri withDefault; + SimpleClass( this.value, + this.withDefault, ); factory SimpleClass.fromJson(Map json) => @@ -24,8 +28,12 @@ class SimpleClass { class SimpleClassNullable { final Uri? value; + @JsonKey(defaultValue: _defaultValueFunc) + Uri? withDefault; + SimpleClassNullable( this.value, + this.withDefault, ); factory SimpleClassNullable.fromJson(Map json) => @@ -33,3 +41,5 @@ class SimpleClassNullable { Map toJson() => _$SimpleClassNullableToJson(this); } + +Uri _defaultValueFunc() => Uri.parse('https://example.com'); diff --git a/json_serializable/test/supported_types/input.type_uri.g.dart b/json_serializable/test/supported_types/input.type_uri.g.dart index b4c7354df..e224f7036 100644 --- a/json_serializable/test/supported_types/input.type_uri.g.dart +++ b/json_serializable/test/supported_types/input.type_uri.g.dart @@ -10,20 +10,28 @@ part of 'input.type_uri.dart'; SimpleClass _$SimpleClassFromJson(Map json) => SimpleClass( Uri.parse(json['value'] as String), + json['withDefault'] == null + ? _defaultValueFunc() + : Uri.parse(json['withDefault'] as String), ); Map _$SimpleClassToJson(SimpleClass instance) => { 'value': instance.value.toString(), + 'withDefault': instance.withDefault.toString(), }; SimpleClassNullable _$SimpleClassNullableFromJson(Map json) => SimpleClassNullable( json['value'] == null ? null : Uri.parse(json['value'] as String), + json['withDefault'] == null + ? _defaultValueFunc() + : Uri.parse(json['withDefault'] as String), ); Map _$SimpleClassNullableToJson( SimpleClassNullable instance) => { 'value': instance.value?.toString(), + 'withDefault': instance.withDefault?.toString(), }; diff --git a/json_serializable/test/supported_types/type_test.bigint_test.dart b/json_serializable/test/supported_types/type_test.bigint_test.dart index aa35a7d68..579298f0e 100644 --- a/json_serializable/test/supported_types/type_test.bigint_test.dart +++ b/json_serializable/test/supported_types/type_test.bigint_test.dart @@ -95,12 +95,15 @@ final _defaultInput = { final _defaultOutput = { 'value': _defaultValue, + 'withDefault': _defaultValue, }; final _nullableDefaultOutput = { 'value': null, + 'withDefault': _defaultValue, }; final _nonDefaultJson = { 'value': _altValue, + 'withDefault': _altValue, }; diff --git a/json_serializable/test/supported_types/type_test.datetime_test.dart b/json_serializable/test/supported_types/type_test.datetime_test.dart index 2be225554..5e16b81e5 100644 --- a/json_serializable/test/supported_types/type_test.datetime_test.dart +++ b/json_serializable/test/supported_types/type_test.datetime_test.dart @@ -95,12 +95,15 @@ final _defaultInput = { final _defaultOutput = { 'value': _defaultValue, + 'withDefault': _defaultValue, }; final _nullableDefaultOutput = { 'value': null, + 'withDefault': _defaultValue, }; final _nonDefaultJson = { 'value': _altValue, + 'withDefault': _altValue, }; diff --git a/json_serializable/test/supported_types/type_test.uri_test.dart b/json_serializable/test/supported_types/type_test.uri_test.dart index 28903704b..b60773780 100644 --- a/json_serializable/test/supported_types/type_test.uri_test.dart +++ b/json_serializable/test/supported_types/type_test.uri_test.dart @@ -95,12 +95,15 @@ final _defaultInput = { final _defaultOutput = { 'value': _defaultValue, + 'withDefault': _defaultValue, }; final _nullableDefaultOutput = { 'value': null, + 'withDefault': _defaultValue, }; final _nonDefaultJson = { 'value': _altValue, + 'withDefault': _altValue, }; diff --git a/json_serializable/tool/test_type_builder.dart b/json_serializable/tool/test_type_builder.dart index de9df8b2c..9ebd4a440 100644 --- a/json_serializable/tool/test_type_builder.dart +++ b/json_serializable/tool/test_type_builder.dart @@ -15,7 +15,7 @@ import 'test_type_data.dart'; final _formatter = DartFormatter(); const _trivialTypesToTest = { - 'BigInt': TestTypeData( + 'BigInt': TestTypeData.defaultFunc( jsonExpression: "'12345'", altJsonExpression: "'67890'", ), @@ -23,7 +23,7 @@ const _trivialTypesToTest = { defaultExpression: 'true', altJsonExpression: 'false', ), - 'DateTime': TestTypeData( + 'DateTime': TestTypeData.defaultFunc( jsonExpression: "'2020-01-01T00:00:00.000'", altJsonExpression: "'2018-01-01T00:00:00.000'", ), @@ -56,7 +56,7 @@ const _trivialTypesToTest = { defaultExpression: "'a string'", altJsonExpression: "'another string'", ), - 'Uri': TestTypeData( + 'Uri': TestTypeData.defaultFunc( jsonExpression: "'https://example.com'", altJsonExpression: "'https://dart.dev'", ), diff --git a/json_serializable/tool/test_type_data.dart b/json_serializable/tool/test_type_data.dart index bd310a751..f6042fe9c 100644 --- a/json_serializable/tool/test_type_data.dart +++ b/json_serializable/tool/test_type_data.dart @@ -6,6 +6,7 @@ const _annotationImport = "import 'package:json_annotation/json_annotation.dart';"; class TestTypeData { + final bool stringParseType; final String? defaultExpression; final String? jsonExpression; final String? altJsonExpression; @@ -18,7 +19,16 @@ class TestTypeData { this.genericArgs = const {}, }) : jsonExpression = jsonExpression ?? defaultExpression, altJsonExpression = - altJsonExpression ?? jsonExpression ?? defaultExpression; + altJsonExpression ?? jsonExpression ?? defaultExpression, + stringParseType = false; + + const TestTypeData.defaultFunc({ + this.jsonExpression, + required String? altJsonExpression, + }) : altJsonExpression = altJsonExpression ?? jsonExpression, + genericArgs = const {}, + defaultExpression = null, + stringParseType = true; String libContent(String source, String type) { const classAnnotationSplit = '@JsonSerializable()'; @@ -91,16 +101,39 @@ class TestTypeData { ); } + final defaultValueFuncBody = _defaultValueFuncBody(type); + + if (defaultValueFuncBody != null) { + buffer.write(defaultValueFuncBody); + } + return buffer.toString(); } + String? _defaultValueFuncBody(String type) { + if (stringParseType) { + return '$type _defaultValueFunc() => $type.parse($jsonExpression);'; + } + + return null; + } + + String? get _annotationDefaultValue { + if (stringParseType) { + return '_defaultValueFunc'; + } + + return defaultExpression; + } + Iterable _libReplacements(String type) sync* { yield Replacement( 'final dynamic value;', 'final $type value;', ); - final defaultNotSupported = defaultExpression == null // no default provided + final defaultNotSupported = + _annotationDefaultValue == null // no default provided || type.contains('<') // no support for default values and generic args ; @@ -108,7 +141,7 @@ class TestTypeData { final defaultReplacement = defaultNotSupported ? '' : _defaultSource - .replaceFirst('42', defaultExpression!) + .replaceFirst('42', _annotationDefaultValue!) .replaceFirst('dynamic', type); yield Replacement( @@ -187,7 +220,7 @@ final _altValue = $altJsonExpression; ''', ); - if (defaultExpression == null) { + if (defaultExpression == null && !stringParseType) { yield const Replacement( " 'withDefault': _defaultValue,\n", '',