diff --git a/checked_yaml/pubspec.yaml b/checked_yaml/pubspec.yaml index 44b447747..f02b54271 100644 --- a/checked_yaml/pubspec.yaml +++ b/checked_yaml/pubspec.yaml @@ -22,5 +22,7 @@ dev_dependencies: test_process: ^2.0.0 dependency_overrides: + json_annotation: + path: ../json_annotation json_serializable: path: ../json_serializable diff --git a/json_annotation/CHANGELOG.md b/json_annotation/CHANGELOG.md index 1bc03904c..753827f52 100644 --- a/json_annotation/CHANGELOG.md +++ b/json_annotation/CHANGELOG.md @@ -1,3 +1,8 @@ +## 4.6.0 + +- Added `JsonSerializable(converters: [])` + ([#1072](https://github.com/google/json_serializable.dart/issues/1072)) + ## 4.5.0 - Added `FieldRename.screamingSnake`. @@ -38,7 +43,7 @@ ## 4.0.1 -- Fix a potential error with `checked: true` when `ArgumentError.message` is +- Fix a potential error with `checked: true` when `ArgumentError.message` is `null`. - Updated `JsonSerializable.fromJson` to handle `null` values. - Deprecate `JsonSerializable` `defaults` and `withDefaults()`. diff --git a/json_annotation/lib/src/json_converter.dart b/json_annotation/lib/src/json_converter.dart index ef98f3f27..73506e3ed 100644 --- a/json_annotation/lib/src/json_converter.dart +++ b/json_annotation/lib/src/json_converter.dart @@ -8,6 +8,37 @@ /// /// [S] is the type of the value stored in JSON. It must be a valid JSON type /// such as [String], [int], or [Map]. +/// +/// +/// [JsonConverter]s can be placed either on the class: +/// +/// ```dart +/// class MyConverter extends JsonConverter { +/// // TODO +/// } +/// +/// @JsonSerializable() +/// @MyJsonConverter() +/// class Example {} +/// ``` +/// +/// or on a property: +/// +/// ```dart +/// @JsonSerializable() +/// @MyJsonConverter() +/// class Example { +/// @MyJsonConverter() +/// final Value property; +/// } +/// ``` +/// +/// Or finally, passed to the annotation: +/// +///```dart +/// @JsonSerializable(converters: [MyConverter()]) +/// class Example {} +/// ``` abstract class JsonConverter { const JsonConverter(); diff --git a/json_annotation/lib/src/json_serializable.dart b/json_annotation/lib/src/json_serializable.dart index 6a2278c8c..34fe2abb6 100644 --- a/json_annotation/lib/src/json_serializable.dart +++ b/json_annotation/lib/src/json_serializable.dart @@ -6,6 +6,7 @@ import 'package:meta/meta_meta.dart'; import 'allowed_keys_helpers.dart'; import 'checked_helpers.dart'; +import 'json_converter.dart'; import 'json_key.dart'; part 'json_serializable.g.dart'; @@ -190,6 +191,40 @@ class JsonSerializable { /// `includeIfNull`, that value takes precedent. final bool? includeIfNull; + /// A list of [JsonConverter] to apply to this class. + /// + /// Writing: + /// + /// ```dart + /// @JsonSerializable(converters: [MyJsonConverter()]) + /// class Example {...} + /// ``` + /// + /// is equivalent to writing: + /// + /// ```dart + /// @JsonSerializable() + /// @MyJsonConverter() + /// class Example {...} + /// ``` + /// + /// The main difference is that this allows reusing a custom + /// [JsonSerializable] over multiple classes: + /// + /// ```dart + /// const myCustomAnnotation = JsonSerializable( + /// converters: [MyJsonConverter()], + /// ); + /// + /// @myCustomAnnotation + /// class Example {...} + /// + /// @myCustomAnnotation + /// class Another {...} + /// ``` + @JsonKey(ignore: true) + final List? converters; + /// Creates a new [JsonSerializable] instance. const JsonSerializable({ @Deprecated('Has no effect') bool? nullable, @@ -203,6 +238,7 @@ class JsonSerializable { this.fieldRename, this.ignoreUnannotated, this.includeIfNull, + this.converters, this.genericArgumentFactories, }); diff --git a/json_annotation/pubspec.yaml b/json_annotation/pubspec.yaml index a192cafa5..7b875c142 100644 --- a/json_annotation/pubspec.yaml +++ b/json_annotation/pubspec.yaml @@ -1,5 +1,5 @@ name: json_annotation -version: 4.5.0 +version: 4.6.0 description: >- Classes and helper functions that support JSON code generation via the `json_serializable` package. diff --git a/json_serializable/CHANGELOG.md b/json_serializable/CHANGELOG.md index 7e79368d3..ba3e21bc9 100644 --- a/json_serializable/CHANGELOG.md +++ b/json_serializable/CHANGELOG.md @@ -2,6 +2,8 @@ - Added support for using a `JsonConverter` on properties of type `MyClass?`. ([#822](https://github.com/google/json_serializable.dart/issues/822)) +- Added support for `JsonSerializable(converters: [])` + ([#1072](https://github.com/google/json_serializable.dart/issues/1072)) ## 6.2.0 diff --git a/json_serializable/README.md b/json_serializable/README.md index 3694c32dd..ffdb481f7 100644 --- a/json_serializable/README.md +++ b/json_serializable/README.md @@ -199,14 +199,14 @@ targets: [`Enum`]: https://api.dart.dev/stable/dart-core/Enum-class.html [`int`]: https://api.dart.dev/stable/dart-core/int-class.html [`Iterable`]: https://api.dart.dev/stable/dart-core/Iterable-class.html -[`JsonConverter`]: https://pub.dev/documentation/json_annotation/4.5.0/json_annotation/JsonConverter-class.html -[`JsonEnum`]: https://pub.dev/documentation/json_annotation/4.5.0/json_annotation/JsonEnum-class.html -[`JsonKey.fromJson`]: https://pub.dev/documentation/json_annotation/4.5.0/json_annotation/JsonKey/fromJson.html -[`JsonKey.toJson`]: https://pub.dev/documentation/json_annotation/4.5.0/json_annotation/JsonKey/toJson.html -[`JsonKey`]: https://pub.dev/documentation/json_annotation/4.5.0/json_annotation/JsonKey-class.html -[`JsonLiteral`]: https://pub.dev/documentation/json_annotation/4.5.0/json_annotation/JsonLiteral-class.html -[`JsonSerializable`]: https://pub.dev/documentation/json_annotation/4.5.0/json_annotation/JsonSerializable-class.html -[`JsonValue`]: https://pub.dev/documentation/json_annotation/4.5.0/json_annotation/JsonValue-class.html +[`JsonConverter`]: https://pub.dev/documentation/json_annotation/4.6.0/json_annotation/JsonConverter-class.html +[`JsonEnum`]: https://pub.dev/documentation/json_annotation/4.6.0/json_annotation/JsonEnum-class.html +[`JsonKey.fromJson`]: https://pub.dev/documentation/json_annotation/4.6.0/json_annotation/JsonKey/fromJson.html +[`JsonKey.toJson`]: https://pub.dev/documentation/json_annotation/4.6.0/json_annotation/JsonKey/toJson.html +[`JsonKey`]: https://pub.dev/documentation/json_annotation/4.6.0/json_annotation/JsonKey-class.html +[`JsonLiteral`]: https://pub.dev/documentation/json_annotation/4.6.0/json_annotation/JsonLiteral-class.html +[`JsonSerializable`]: https://pub.dev/documentation/json_annotation/4.6.0/json_annotation/JsonSerializable-class.html +[`JsonValue`]: https://pub.dev/documentation/json_annotation/4.6.0/json_annotation/JsonValue-class.html [`List`]: https://api.dart.dev/stable/dart-core/List-class.html [`Map`]: https://api.dart.dev/stable/dart-core/Map-class.html [`num`]: https://api.dart.dev/stable/dart-core/num-class.html diff --git a/json_serializable/lib/src/check_dependencies.dart b/json_serializable/lib/src/check_dependencies.dart index ec72bdb0f..873be904a 100644 --- a/json_serializable/lib/src/check_dependencies.dart +++ b/json_serializable/lib/src/check_dependencies.dart @@ -10,7 +10,7 @@ import 'package:pubspec_parse/pubspec_parse.dart'; const _productionDirectories = {'lib', 'bin'}; const _annotationPkgName = 'json_annotation'; -final requiredJsonAnnotationMinVersion = Version.parse('4.5.0'); +final requiredJsonAnnotationMinVersion = Version.parse('4.6.0'); Future pubspecHasRightVersion(BuildStep buildStep) async { final segments = buildStep.inputId.pathSegments; diff --git a/json_serializable/lib/src/json_serializable_generator.dart b/json_serializable/lib/src/json_serializable_generator.dart index 13a4b16e4..69ead4939 100644 --- a/json_serializable/lib/src/json_serializable_generator.dart +++ b/json_serializable/lib/src/json_serializable_generator.dart @@ -20,7 +20,7 @@ class JsonSerializableGenerator extends GeneratorForAnnotation { final Settings _settings; - JsonSerializable get config => _settings.config; + JsonSerializable get config => _settings.config.toJsonSerializable(); JsonSerializableGenerator.fromSettings(this._settings); diff --git a/json_serializable/lib/src/settings.dart b/json_serializable/lib/src/settings.dart index f9561ce65..c73df1b7b 100644 --- a/json_serializable/lib/src/settings.dart +++ b/json_serializable/lib/src/settings.dart @@ -43,38 +43,19 @@ class Settings { GenericFactoryHelper(), ].followedBy(_typeHelpers).followedBy(_coreHelpers); - final JsonSerializable _config; - - // #CHANGE WHEN UPDATING json_annotation - ClassConfig get config => ClassConfig( - checked: _config.checked ?? ClassConfig.defaults.checked, - anyMap: _config.anyMap ?? ClassConfig.defaults.anyMap, - constructor: _config.constructor ?? ClassConfig.defaults.constructor, - createFactory: - _config.createFactory ?? ClassConfig.defaults.createFactory, - createToJson: _config.createToJson ?? ClassConfig.defaults.createToJson, - ignoreUnannotated: - _config.ignoreUnannotated ?? ClassConfig.defaults.ignoreUnannotated, - explicitToJson: - _config.explicitToJson ?? ClassConfig.defaults.explicitToJson, - includeIfNull: - _config.includeIfNull ?? ClassConfig.defaults.includeIfNull, - genericArgumentFactories: _config.genericArgumentFactories ?? - ClassConfig.defaults.genericArgumentFactories, - fieldRename: _config.fieldRename ?? ClassConfig.defaults.fieldRename, - disallowUnrecognizedKeys: _config.disallowUnrecognizedKeys ?? - ClassConfig.defaults.disallowUnrecognizedKeys, - ); + final ClassConfig config; /// Creates an instance of [Settings]. /// /// If [typeHelpers] is not provided, the built-in helpers are used: /// [BigIntHelper], [DateTimeHelper], [DurationHelper], [JsonHelper], and /// [UriHelper]. - const Settings({ + Settings({ JsonSerializable? config, List? typeHelpers, - }) : _config = config ?? ClassConfig.defaults, + }) : config = config != null + ? ClassConfig.fromJsonSerializable(config) + : ClassConfig.defaults, _typeHelpers = typeHelpers ?? defaultHelpers; /// Creates an instance of [Settings]. diff --git a/json_serializable/lib/src/type_helpers/config_types.dart b/json_serializable/lib/src/type_helpers/config_types.dart index 0918b2138..6d51ac475 100644 --- a/json_serializable/lib/src/type_helpers/config_types.dart +++ b/json_serializable/lib/src/type_helpers/config_types.dart @@ -2,6 +2,7 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +import 'package:analyzer/dart/constant/value.dart'; import 'package:json_annotation/json_annotation.dart'; /// Represents values from [JsonKey] when merged with local configuration. @@ -38,41 +39,20 @@ class KeyConfig { /// configuration. /// /// Values are all known, so types are non-nullable. -class ClassConfig implements JsonSerializable { - @override +class ClassConfig { final bool anyMap; - - @override final bool checked; - - @override final String constructor; - - @override final bool createFactory; - - @override final bool createToJson; - - @override final bool disallowUnrecognizedKeys; - - @override final bool explicitToJson; - - @override final FieldRename fieldRename; - - @override final bool genericArgumentFactories; - - @override final bool ignoreUnannotated; - - @override final bool includeIfNull; - final Map ctorParamDefaults; + final List converters; const ClassConfig({ required this.anyMap, @@ -86,9 +66,33 @@ class ClassConfig implements JsonSerializable { required this.genericArgumentFactories, required this.ignoreUnannotated, required this.includeIfNull, + this.converters = const [], this.ctorParamDefaults = const {}, }); + factory ClassConfig.fromJsonSerializable(JsonSerializable config) => + // #CHANGE WHEN UPDATING json_annotation + ClassConfig( + checked: config.checked ?? ClassConfig.defaults.checked, + anyMap: config.anyMap ?? ClassConfig.defaults.anyMap, + constructor: config.constructor ?? ClassConfig.defaults.constructor, + createFactory: + config.createFactory ?? ClassConfig.defaults.createFactory, + createToJson: config.createToJson ?? ClassConfig.defaults.createToJson, + ignoreUnannotated: + config.ignoreUnannotated ?? ClassConfig.defaults.ignoreUnannotated, + explicitToJson: + config.explicitToJson ?? ClassConfig.defaults.explicitToJson, + includeIfNull: + config.includeIfNull ?? ClassConfig.defaults.includeIfNull, + genericArgumentFactories: config.genericArgumentFactories ?? + ClassConfig.defaults.genericArgumentFactories, + fieldRename: config.fieldRename ?? ClassConfig.defaults.fieldRename, + disallowUnrecognizedKeys: config.disallowUnrecognizedKeys ?? + ClassConfig.defaults.disallowUnrecognizedKeys, + // TODO typeConverters = [] + ); + /// An instance of [JsonSerializable] with all fields set to their default /// values. static const defaults = ClassConfig( @@ -105,33 +109,18 @@ class ClassConfig implements JsonSerializable { includeIfNull: true, ); - @override - Map toJson() => _$JsonSerializableToJson(this); - - @override - JsonSerializable withDefaults() => this; + JsonSerializable toJsonSerializable() => JsonSerializable( + checked: checked, + anyMap: anyMap, + constructor: constructor, + createFactory: createFactory, + createToJson: createToJson, + ignoreUnannotated: ignoreUnannotated, + explicitToJson: explicitToJson, + includeIfNull: includeIfNull, + genericArgumentFactories: genericArgumentFactories, + fieldRename: fieldRename, + disallowUnrecognizedKeys: disallowUnrecognizedKeys, + // TODO typeConverters = [] + ); } - -const _$FieldRenameEnumMap = { - FieldRename.none: 'none', - FieldRename.kebab: 'kebab', - FieldRename.snake: 'snake', - FieldRename.pascal: 'pascal', - FieldRename.screamingSnake: 'screamingSnake', -}; - -// #CHANGE WHEN UPDATING json_annotation -Map _$JsonSerializableToJson(JsonSerializable instance) => - { - 'any_map': instance.anyMap, - 'checked': instance.checked, - 'constructor': instance.constructor, - 'create_factory': instance.createFactory, - 'create_to_json': instance.createToJson, - 'disallow_unrecognized_keys': instance.disallowUnrecognizedKeys, - 'explicit_to_json': instance.explicitToJson, - 'field_rename': _$FieldRenameEnumMap[instance.fieldRename], - 'generic_argument_factories': instance.genericArgumentFactories, - 'ignore_unannotated': instance.ignoreUnannotated, - 'include_if_null': instance.includeIfNull, - }; 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 10fa483b4..ffe1627f1 100644 --- a/json_serializable/lib/src/type_helpers/json_converter_helper.dart +++ b/json_serializable/lib/src/type_helpers/json_converter_helper.dart @@ -17,14 +17,14 @@ import '../utils.dart'; /// A [TypeHelper] that supports classes annotated with implementations of /// [JsonConverter]. -class JsonConverterHelper extends TypeHelper { +class JsonConverterHelper extends TypeHelper { const JsonConverterHelper(); @override Object? serialize( DartType targetType, String expression, - TypeHelperContext context, + TypeHelperContextWithConfig context, ) { final converter = _typeConverter(targetType, context); @@ -57,7 +57,7 @@ Json? $converterToJsonName( Object? deserialize( DartType targetType, String expression, - TypeHelperContext context, + TypeHelperContextWithConfig context, bool defaultProvided, ) { final converter = _typeConverter(targetType, context); @@ -143,9 +143,18 @@ class _JsonConvertData { accessor.isEmpty ? '' : '.$accessor'; } -_JsonConvertData? _typeConverter(DartType targetType, TypeHelperContext ctx) { +_JsonConvertData? _typeConverter( + DartType targetType, + TypeHelperContextWithConfig ctx, +) { List<_ConverterMatch> converterMatches(List items) => items - .map((annotation) => _compatibleMatch(targetType, annotation)) + .map( + (annotation) => _compatibleMatch( + targetType, + annotation, + annotation.computeConstantValue()!, + ), + ) .whereType<_ConverterMatch>() .toList(); @@ -157,6 +166,13 @@ _JsonConvertData? _typeConverter(DartType targetType, TypeHelperContext ctx) { if (matchingAnnotations.isEmpty) { matchingAnnotations = converterMatches(ctx.classElement.metadata); + + if (matchingAnnotations.isEmpty) { + matchingAnnotations = ctx.config.converters + .map((e) => _compatibleMatch(targetType, null, e)) + .whereType<_ConverterMatch>() + .toList(); + } } } @@ -174,13 +190,14 @@ _JsonConvertData? _typeConverterFrom( if (matchingAnnotations.length > 1) { final targetTypeCode = typeToCode(targetType); throw InvalidGenerationSourceError( - 'Found more than one matching converter for `$targetTypeCode`.', - element: matchingAnnotations[1].elementAnnotation.element); + 'Found more than one matching converter for `$targetTypeCode`.', + element: matchingAnnotations[1].elementAnnotation?.element, + ); } final match = matchingAnnotations.single; - final annotationElement = match.elementAnnotation.element; + final annotationElement = match.elementAnnotation?.element; if (annotationElement is PropertyAccessorElement) { final enclosing = annotationElement.enclosingElement; @@ -202,8 +219,9 @@ _JsonConvertData? _typeConverterFrom( if (reviver.namedArguments.isNotEmpty || reviver.positionalArguments.isNotEmpty) { throw InvalidGenerationSourceError( - 'Generators with constructor arguments are not supported.', - element: match.elementAnnotation.element); + 'Generators with constructor arguments are not supported.', + element: match.elementAnnotation?.element, + ); } if (match.genericTypeArg != null) { @@ -228,7 +246,7 @@ class _ConverterMatch { final DartObject annotation; final DartType fieldType; final DartType jsonType; - final ElementAnnotation elementAnnotation; + final ElementAnnotation? elementAnnotation; final String? genericTypeArg; _ConverterMatch( @@ -242,10 +260,9 @@ class _ConverterMatch { _ConverterMatch? _compatibleMatch( DartType targetType, - ElementAnnotation annotation, + ElementAnnotation? annotation, + DartObject constantValue, ) { - final constantValue = annotation.computeConstantValue()!; - final converterClassElement = constantValue.type!.element as ClassElement; final jsonConverterSuper = @@ -274,7 +291,7 @@ _ConverterMatch? _compatibleMatch( } if (fieldType is TypeParameterType && targetType is TypeParameterType) { - assert(annotation.element is! PropertyAccessorElement); + assert(annotation?.element is! PropertyAccessorElement); assert(converterClassElement.typeParameters.isNotEmpty); if (converterClassElement.typeParameters.length > 1) { throw InvalidGenerationSourceError( diff --git a/json_serializable/lib/src/utils.dart b/json_serializable/lib/src/utils.dart index 8edebaa5a..470627491 100644 --- a/json_serializable/lib/src/utils.dart +++ b/json_serializable/lib/src/utils.dart @@ -66,7 +66,7 @@ JsonSerializable _valueForAnnotation(ConstantReader reader) => JsonSerializable( includeIfNull: reader.read('includeIfNull').literalValue as bool?, ); -/// Returns a [JsonSerializable] with values from the [JsonSerializable] +/// Returns a [ClassConfig] with values from the [JsonSerializable] /// instance represented by [reader]. /// /// For fields that are not defined in [JsonSerializable] or `null` in [reader], @@ -93,6 +93,8 @@ ClassConfig mergeConfig( .where((element) => element.hasDefaultValue) .map((e) => MapEntry(e.name, e.defaultValueCode!))); + final converters = reader.read('converters'); + return ClassConfig( anyMap: annotation.anyMap ?? config.anyMap, checked: annotation.checked ?? config.checked, @@ -109,6 +111,7 @@ ClassConfig mergeConfig( ignoreUnannotated: annotation.ignoreUnannotated ?? config.ignoreUnannotated, includeIfNull: annotation.includeIfNull ?? config.includeIfNull, ctorParamDefaults: paramDefaultValueMap, + converters: converters.isNull ? const [] : converters.listValue, ); } diff --git a/json_serializable/pubspec.yaml b/json_serializable/pubspec.yaml index 00db66b4f..b2b5caa5d 100644 --- a/json_serializable/pubspec.yaml +++ b/json_serializable/pubspec.yaml @@ -16,7 +16,7 @@ dependencies: # Use a tight version constraint to ensure that a constraint on # `json_annotation` properly constrains all features it provides. - json_annotation: '>=4.5.0 <4.6.0' + json_annotation: '>=4.6.0 <4.7.0' meta: ^1.3.0 path: ^1.8.0 pub_semver: ^2.0.0 @@ -36,3 +36,7 @@ dev_dependencies: test_descriptor: ^2.0.0 test_process: ^2.0.0 yaml: ^3.0.0 + +dependency_overrides: + json_annotation: + path: ../json_annotation diff --git a/json_serializable/test/custom_configuration_test.dart b/json_serializable/test/custom_configuration_test.dart index beb592648..649815905 100644 --- a/json_serializable/test/custom_configuration_test.dart +++ b/json_serializable/test/custom_configuration_test.dart @@ -32,7 +32,7 @@ Future main() async { ); group('without wrappers', () { - _registerTests(ClassConfig.defaults); + _registerTests(ClassConfig.defaults.toJsonSerializable()); }); group('configuration', () { @@ -61,8 +61,10 @@ Future main() async { nullConfig ? null : const JsonSerializable(), className); expect(_ConfigLogger.configurations, hasLength(2)); - expect(_ConfigLogger.configurations.first, - same(_ConfigLogger.configurations.last)); + expect( + _ConfigLogger.configurations.first.toJson(), + _ConfigLogger.configurations.last.toJson(), + ); expect(_ConfigLogger.configurations.first.toJson(), generatorConfigDefaultJson); }); @@ -98,8 +100,10 @@ Future main() async { 'ConfigurationExplicitDefaults'); expect(_ConfigLogger.configurations, hasLength(2)); - expect(_ConfigLogger.configurations.first, - same(_ConfigLogger.configurations.last)); + expect( + _ConfigLogger.configurations.first.toJson(), + _ConfigLogger.configurations.last.toJson(), + ); // The effective configuration should be non-Default configuration, but // with all fields set from JsonSerializable as the defaults @@ -225,14 +229,14 @@ class _ConfigLogger implements TypeHelper { TypeHelperContextWithConfig context, bool defaultProvided, ) { - configurations.add(context.config); + configurations.add(context.config.toJsonSerializable()); return null; } @override Object? serialize(DartType targetType, String expression, TypeHelperContextWithConfig context) { - configurations.add(context.config); + configurations.add(context.config.toJsonSerializable()); return null; } } diff --git a/json_serializable/test/kitchen_sink/json_converters.dart b/json_serializable/test/kitchen_sink/json_converters.dart index aefb246d2..4f31a5b4e 100644 --- a/json_serializable/test/kitchen_sink/json_converters.dart +++ b/json_serializable/test/kitchen_sink/json_converters.dart @@ -55,6 +55,25 @@ class DurationMillisecondConverter implements JsonConverter { int? toJson(Duration? object) => object?.inMilliseconds; } +class TrivialString { + TrivialString(this.value); + + final String? value; +} + +const trivialStringConverter = TrivialStringConverter(); + +class TrivialStringConverter implements JsonConverter { + const TrivialStringConverter(); + + @override + TrivialString? fromJson(String? json) => + json == null ? null : TrivialString(json); + + @override + String? toJson(TrivialString? object) => object?.value; +} + class EpochDateTimeConverter implements JsonConverter { const EpochDateTimeConverter(); diff --git a/json_serializable/test/kitchen_sink/kitchen_sink.dart b/json_serializable/test/kitchen_sink/kitchen_sink.dart index 4eabab0e2..e194dbfbc 100644 --- a/json_serializable/test/kitchen_sink/kitchen_sink.dart +++ b/json_serializable/test/kitchen_sink/kitchen_sink.dart @@ -71,6 +71,7 @@ class _Factory implements k.KitchenSinkFactory { TrivialNumber(0), {}, DateTime.fromMillisecondsSinceEpoch(0), + TrivialString(''), TrivialNumber(0), {}, ); @@ -194,11 +195,14 @@ class KitchenSink implements k.KitchenSink { } } -@JsonSerializable() +@JsonSerializable(converters: [ + // referencing a top-level field should work + durationConverter, + // referencing via a const constructor should work + BigIntStringConverter(), +]) // referencing a top-level field should work -@durationConverter -// referencing via a const constructor should work -@BigIntStringConverter() +@trivialStringConverter @TrivialNumberConverter.instance @EpochDateTimeConverter() class JsonConverterTestClass implements k.JsonConverterTestClass { @@ -212,6 +216,7 @@ class JsonConverterTestClass implements k.JsonConverterTestClass { this.numberSilly, this.numberSillySet, this.dateTime, + this.trivialString, this.nullableNumberSilly, this.nullableNumberSillySet, ); @@ -235,6 +240,8 @@ class JsonConverterTestClass implements k.JsonConverterTestClass { DateTime? dateTime; + TrivialString? trivialString; + TrivialNumber? nullableNumberSilly; Set nullableNumberSillySet; } diff --git a/json_serializable/test/kitchen_sink/kitchen_sink.g.dart b/json_serializable/test/kitchen_sink/kitchen_sink.g.dart index c74f6aee0..c795c6c13 100644 --- a/json_serializable/test/kitchen_sink/kitchen_sink.g.dart +++ b/json_serializable/test/kitchen_sink/kitchen_sink.g.dart @@ -137,9 +137,9 @@ Map _$KitchenSinkToJson(KitchenSink instance) => JsonConverterTestClass _$JsonConverterTestClassFromJson( Map json) => JsonConverterTestClass( - durationConverter.fromJson(json['duration'] as int?), + const DurationMillisecondConverter().fromJson(json['duration'] as int?), (json['durationList'] as List) - .map((e) => durationConverter.fromJson(e as int?)) + .map((e) => const DurationMillisecondConverter().fromJson(e as int?)) .toList(), const BigIntStringConverter().fromJson(json['bigInt'] as String), (json['bigIntMap'] as Map).map( @@ -159,6 +159,7 @@ JsonConverterTestClass _$JsonConverterTestClassFromJson( .map((e) => TrivialNumberConverter.instance.fromJson(e as int?)) .toSet(), const EpochDateTimeConverter().fromJson(json['dateTime'] as int?), + trivialStringConverter.fromJson(json['trivialString'] as String?), TrivialNumberConverter.instance .fromJson(json['nullableNumberSilly'] as int?), (json['nullableNumberSillySet'] as List) @@ -169,9 +170,11 @@ JsonConverterTestClass _$JsonConverterTestClassFromJson( Map _$JsonConverterTestClassToJson( JsonConverterTestClass instance) => { - 'duration': durationConverter.toJson(instance.duration), - 'durationList': - instance.durationList.map(durationConverter.toJson).toList(), + 'duration': + const DurationMillisecondConverter().toJson(instance.duration), + 'durationList': instance.durationList + .map(const DurationMillisecondConverter().toJson) + .toList(), 'bigInt': const BigIntStringConverter().toJson(instance.bigInt), 'bigIntMap': instance.bigIntMap .map((k, e) => MapEntry(k, const BigIntStringConverter().toJson(e))), @@ -187,6 +190,7 @@ Map _$JsonConverterTestClassToJson( .map(TrivialNumberConverter.instance.toJson) .toList(), 'dateTime': const EpochDateTimeConverter().toJson(instance.dateTime), + 'trivialString': trivialStringConverter.toJson(instance.trivialString), 'nullableNumberSilly': _$JsonConverterToJson( instance.nullableNumberSilly, TrivialNumberConverter.instance.toJson), 'nullableNumberSillySet': instance.nullableNumberSillySet 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 9da91757a..cd0abae63 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 @@ -70,6 +70,7 @@ class _Factory implements k.KitchenSinkFactory { TrivialNumber(0), {}, DateTime.fromMillisecondsSinceEpoch(0), + TrivialString(''), TrivialNumber(0), {}, ); @@ -194,13 +195,14 @@ class KitchenSink implements k.KitchenSink { } } -@JsonSerializable( - anyMap: true, -) +@JsonSerializable(anyMap: true, converters: [ + // referencing a top-level field should work + durationConverter, + // referencing via a const constructor should work + BigIntStringConverter(), +]) // referencing a top-level field should work -@durationConverter -// referencing via a const constructor should work -@BigIntStringConverter() +@trivialStringConverter @TrivialNumberConverter.instance @EpochDateTimeConverter() class JsonConverterTestClass implements k.JsonConverterTestClass { @@ -214,6 +216,7 @@ class JsonConverterTestClass implements k.JsonConverterTestClass { this.numberSilly, this.numberSillySet, this.dateTime, + this.trivialString, this.nullableNumberSilly, this.nullableNumberSillySet, ); @@ -237,6 +240,8 @@ class JsonConverterTestClass implements k.JsonConverterTestClass { DateTime? dateTime; + TrivialString? trivialString; + TrivialNumber? nullableNumberSilly; Set 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 638091dcc..ce7520a1e 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,9 +128,9 @@ Map _$KitchenSinkToJson(KitchenSink instance) => JsonConverterTestClass _$JsonConverterTestClassFromJson(Map json) => JsonConverterTestClass( - durationConverter.fromJson(json['duration'] as int?), + const DurationMillisecondConverter().fromJson(json['duration'] as int?), (json['durationList'] as List) - .map((e) => durationConverter.fromJson(e as int?)) + .map((e) => const DurationMillisecondConverter().fromJson(e as int?)) .toList(), const BigIntStringConverter().fromJson(json['bigInt'] as String), (json['bigIntMap'] as Map).map( @@ -150,6 +150,7 @@ JsonConverterTestClass _$JsonConverterTestClassFromJson(Map json) => .map((e) => TrivialNumberConverter.instance.fromJson(e as int?)) .toSet(), const EpochDateTimeConverter().fromJson(json['dateTime'] as int?), + trivialStringConverter.fromJson(json['trivialString'] as String?), TrivialNumberConverter.instance .fromJson(json['nullableNumberSilly'] as int?), (json['nullableNumberSillySet'] as List) @@ -160,9 +161,11 @@ JsonConverterTestClass _$JsonConverterTestClassFromJson(Map json) => Map _$JsonConverterTestClassToJson( JsonConverterTestClass instance) => { - 'duration': durationConverter.toJson(instance.duration), - 'durationList': - instance.durationList.map(durationConverter.toJson).toList(), + 'duration': + const DurationMillisecondConverter().toJson(instance.duration), + 'durationList': instance.durationList + .map(const DurationMillisecondConverter().toJson) + .toList(), 'bigInt': const BigIntStringConverter().toJson(instance.bigInt), 'bigIntMap': instance.bigIntMap .map((k, e) => MapEntry(k, const BigIntStringConverter().toJson(e))), @@ -178,6 +181,7 @@ Map _$JsonConverterTestClassToJson( .map(TrivialNumberConverter.instance.toJson) .toList(), 'dateTime': const EpochDateTimeConverter().toJson(instance.dateTime), + 'trivialString': trivialStringConverter.toJson(instance.trivialString), 'nullableNumberSilly': _$JsonConverterToJson( instance.nullableNumberSilly, TrivialNumberConverter.instance.toJson), 'nullableNumberSillySet': instance.nullableNumberSillySet 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 71812ba2e..b01ef3341 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 @@ -70,6 +70,7 @@ class _Factory implements k.KitchenSinkFactory { TrivialNumber(0), {}, DateTime.fromMillisecondsSinceEpoch(0), + TrivialString(''), TrivialNumber(0), {}, ); @@ -195,14 +196,14 @@ class KitchenSink implements k.KitchenSink { } } -@JsonSerializable( - checked: true, - anyMap: true, -) +@JsonSerializable(checked: true, anyMap: true, converters: [ + // referencing a top-level field should work + durationConverter, + // referencing via a const constructor should work + BigIntStringConverter(), +]) // referencing a top-level field should work -@durationConverter -// referencing via a const constructor should work -@BigIntStringConverter() +@trivialStringConverter @TrivialNumberConverter.instance @EpochDateTimeConverter() class JsonConverterTestClass implements k.JsonConverterTestClass { @@ -216,6 +217,7 @@ class JsonConverterTestClass implements k.JsonConverterTestClass { this.numberSilly, this.numberSillySet, this.dateTime, + this.trivialString, this.nullableNumberSilly, this.nullableNumberSillySet, ); @@ -239,6 +241,8 @@ class JsonConverterTestClass implements k.JsonConverterTestClass { DateTime? dateTime; + TrivialString? trivialString; + TrivialNumber? nullableNumberSilly; Set 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 1d8c4c26b..a9e48d1a6 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) => const DurationMillisecondConverter().fromJson(v as int?)), $checkedConvert( 'durationList', (v) => (v as List) - .map((e) => durationConverter.fromJson(e as int?)) + .map((e) => + const DurationMillisecondConverter().fromJson(e as int?)) .toList()), $checkedConvert('bigInt', (v) => const BigIntStringConverter().fromJson(v as String)), @@ -220,6 +221,8 @@ JsonConverterTestClass _$JsonConverterTestClassFromJson(Map json) => .toSet()), $checkedConvert('dateTime', (v) => const EpochDateTimeConverter().fromJson(v as int?)), + $checkedConvert('trivialString', + (v) => trivialStringConverter.fromJson(v as String?)), $checkedConvert('nullableNumberSilly', (v) => TrivialNumberConverter.instance.fromJson(v as int?)), $checkedConvert( @@ -236,9 +239,11 @@ JsonConverterTestClass _$JsonConverterTestClassFromJson(Map json) => Map _$JsonConverterTestClassToJson( JsonConverterTestClass instance) => { - 'duration': durationConverter.toJson(instance.duration), - 'durationList': - instance.durationList.map(durationConverter.toJson).toList(), + 'duration': + const DurationMillisecondConverter().toJson(instance.duration), + 'durationList': instance.durationList + .map(const DurationMillisecondConverter().toJson) + .toList(), 'bigInt': const BigIntStringConverter().toJson(instance.bigInt), 'bigIntMap': instance.bigIntMap .map((k, e) => MapEntry(k, const BigIntStringConverter().toJson(e))), @@ -254,6 +259,7 @@ Map _$JsonConverterTestClassToJson( .map(TrivialNumberConverter.instance.toJson) .toList(), 'dateTime': const EpochDateTimeConverter().toJson(instance.dateTime), + 'trivialString': trivialStringConverter.toJson(instance.trivialString), 'nullableNumberSilly': _$JsonConverterToJson( instance.nullableNumberSilly, TrivialNumberConverter.instance.toJson), 'nullableNumberSillySet': instance.nullableNumberSillySet 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 a8fd5e1d8..46b2ea3b7 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 @@ -71,6 +71,7 @@ class _Factory implements k.KitchenSinkFactory { TrivialNumber(0), {}, DateTime.fromMillisecondsSinceEpoch(0), + TrivialString(''), TrivialNumber(0), {}, ); @@ -196,13 +197,14 @@ class KitchenSink implements k.KitchenSink { } } -@JsonSerializable( - includeIfNull: false, -) +@JsonSerializable(includeIfNull: false, converters: [ + // referencing a top-level field should work + durationConverter, + // referencing via a const constructor should work + BigIntStringConverter(), +]) // referencing a top-level field should work -@durationConverter -// referencing via a const constructor should work -@BigIntStringConverter() +@trivialStringConverter @TrivialNumberConverter.instance @EpochDateTimeConverter() class JsonConverterTestClass implements k.JsonConverterTestClass { @@ -216,6 +218,7 @@ class JsonConverterTestClass implements k.JsonConverterTestClass { this.numberSilly, this.numberSillySet, this.dateTime, + this.trivialString, this.nullableNumberSilly, this.nullableNumberSillySet, ); @@ -239,6 +242,8 @@ class JsonConverterTestClass implements k.JsonConverterTestClass { DateTime? dateTime; + TrivialString? trivialString; + TrivialNumber? nullableNumberSilly; Set 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 a0246e8bc..017e40a6a 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,9 +145,9 @@ Map _$KitchenSinkToJson(KitchenSink instance) { JsonConverterTestClass _$JsonConverterTestClassFromJson( Map json) => JsonConverterTestClass( - durationConverter.fromJson(json['duration'] as int?), + const DurationMillisecondConverter().fromJson(json['duration'] as int?), (json['durationList'] as List) - .map((e) => durationConverter.fromJson(e as int?)) + .map((e) => const DurationMillisecondConverter().fromJson(e as int?)) .toList(), const BigIntStringConverter().fromJson(json['bigInt'] as String), (json['bigIntMap'] as Map).map( @@ -167,6 +167,7 @@ JsonConverterTestClass _$JsonConverterTestClassFromJson( .map((e) => TrivialNumberConverter.instance.fromJson(e as int?)) .toSet(), const EpochDateTimeConverter().fromJson(json['dateTime'] as int?), + trivialStringConverter.fromJson(json['trivialString'] as String?), TrivialNumberConverter.instance .fromJson(json['nullableNumberSilly'] as int?), (json['nullableNumberSillySet'] as List) @@ -184,9 +185,11 @@ Map _$JsonConverterTestClassToJson( } } - writeNotNull('duration', durationConverter.toJson(instance.duration)); - val['durationList'] = - instance.durationList.map(durationConverter.toJson).toList(); + writeNotNull('duration', + const DurationMillisecondConverter().toJson(instance.duration)); + val['durationList'] = instance.durationList + .map(const DurationMillisecondConverter().toJson) + .toList(); writeNotNull('bigInt', const BigIntStringConverter().toJson(instance.bigInt)); val['bigIntMap'] = instance.bigIntMap .map((k, e) => MapEntry(k, const BigIntStringConverter().toJson(e))); @@ -205,6 +208,8 @@ Map _$JsonConverterTestClassToJson( .toList(); writeNotNull( 'dateTime', const EpochDateTimeConverter().toJson(instance.dateTime)); + writeNotNull( + 'trivialString', trivialStringConverter.toJson(instance.trivialString)); writeNotNull( 'nullableNumberSilly', _$JsonConverterToJson(instance.nullableNumberSilly, 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 f5f13fc7d..02bd2a683 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 @@ -71,6 +71,7 @@ class _Factory implements k.KitchenSinkFactory { TrivialNumber(0), {}, DateTime.fromMillisecondsSinceEpoch(0), + TrivialString(''), TrivialNumber(0), {}, ); @@ -196,13 +197,14 @@ class KitchenSink implements k.KitchenSink { } } -@JsonSerializable( - explicitToJson: true, -) +@JsonSerializable(explicitToJson: true, converters: [ + // referencing a top-level field should work + durationConverter, + // referencing via a const constructor should work + BigIntStringConverter(), +]) // referencing a top-level field should work -@durationConverter -// referencing via a const constructor should work -@BigIntStringConverter() +@trivialStringConverter @TrivialNumberConverter.instance @EpochDateTimeConverter() class JsonConverterTestClass implements k.JsonConverterTestClass { @@ -216,6 +218,7 @@ class JsonConverterTestClass implements k.JsonConverterTestClass { this.numberSilly, this.numberSillySet, this.dateTime, + this.trivialString, this.nullableNumberSilly, this.nullableNumberSillySet, ); @@ -239,6 +242,8 @@ class JsonConverterTestClass implements k.JsonConverterTestClass { DateTime? dateTime; + TrivialString? trivialString; + TrivialNumber? nullableNumberSilly; Set nullableNumberSillySet; } 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 e6f218fa6..a38b42bf0 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,9 +139,9 @@ Map _$KitchenSinkToJson(KitchenSink instance) => JsonConverterTestClass _$JsonConverterTestClassFromJson( Map json) => JsonConverterTestClass( - durationConverter.fromJson(json['duration'] as int?), + const DurationMillisecondConverter().fromJson(json['duration'] as int?), (json['durationList'] as List) - .map((e) => durationConverter.fromJson(e as int?)) + .map((e) => const DurationMillisecondConverter().fromJson(e as int?)) .toList(), const BigIntStringConverter().fromJson(json['bigInt'] as String), (json['bigIntMap'] as Map).map( @@ -161,6 +161,7 @@ JsonConverterTestClass _$JsonConverterTestClassFromJson( .map((e) => TrivialNumberConverter.instance.fromJson(e as int?)) .toSet(), const EpochDateTimeConverter().fromJson(json['dateTime'] as int?), + trivialStringConverter.fromJson(json['trivialString'] as String?), TrivialNumberConverter.instance .fromJson(json['nullableNumberSilly'] as int?), (json['nullableNumberSillySet'] as List) @@ -171,9 +172,11 @@ JsonConverterTestClass _$JsonConverterTestClassFromJson( Map _$JsonConverterTestClassToJson( JsonConverterTestClass instance) => { - 'duration': durationConverter.toJson(instance.duration), - 'durationList': - instance.durationList.map(durationConverter.toJson).toList(), + 'duration': + const DurationMillisecondConverter().toJson(instance.duration), + 'durationList': instance.durationList + .map(const DurationMillisecondConverter().toJson) + .toList(), 'bigInt': const BigIntStringConverter().toJson(instance.bigInt), 'bigIntMap': instance.bigIntMap .map((k, e) => MapEntry(k, const BigIntStringConverter().toJson(e))), @@ -189,6 +192,7 @@ Map _$JsonConverterTestClassToJson( .map(TrivialNumberConverter.instance.toJson) .toList(), 'dateTime': const EpochDateTimeConverter().toJson(instance.dateTime), + 'trivialString': trivialStringConverter.toJson(instance.trivialString), '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 99e45bf40..67e68400a 100644 --- a/json_serializable/test/kitchen_sink/kitchen_sink_test.dart +++ b/json_serializable/test/kitchen_sink/kitchen_sink_test.dart @@ -59,6 +59,7 @@ const _jsonConverterValidValues = { 'numberSilly': 5, 'numberSillySet': [5], 'dateTime': 5, + 'trivialString': '', 'nullableNumberSilly': 5, 'nullableBigInt': '42', 'nullableBigIntMap': {'value': '42'}, @@ -106,6 +107,7 @@ void _nullableTests(KitchenSinkFactory factory) { 'numberSilly': 0, 'numberSillySet': [], 'dateTime': 0, + 'trivialString': '', 'nullableNumberSilly': 0, 'nullableNumberSillySet': [], }); diff --git a/json_serializable/test/shared_config.dart b/json_serializable/test/shared_config.dart index 09311ad10..8ee8ca585 100644 --- a/json_serializable/test/shared_config.dart +++ b/json_serializable/test/shared_config.dart @@ -7,8 +7,9 @@ import 'package:json_serializable/src/type_helpers/config_types.dart'; final jsonSerializableFields = generatorConfigDefaultJson.keys.toList(); -final generatorConfigDefaultJson = - Map.unmodifiable(ClassConfig.defaults.toJson()); +final generatorConfigDefaultJson = Map.unmodifiable( + ClassConfig.defaults.toJsonSerializable().toJson(), +); // #CHANGE WHEN UPDATING json_annotation final generatorConfigNonDefaultJson =