Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add JsonConverter(converters) option #1135

Merged
merged 13 commits into from May 24, 2022
Merged
2 changes: 2 additions & 0 deletions checked_yaml/pubspec.yaml
Expand Up @@ -22,5 +22,7 @@ dev_dependencies:
test_process: ^2.0.0

dependency_overrides:
json_annotation:
path: ../json_annotation
json_serializable:
path: ../json_serializable
7 changes: 6 additions & 1 deletion json_annotation/CHANGELOG.md
@@ -1,3 +1,8 @@
## 4.6.0

- Added `JsonSerializable(converters: <JsonConverter>[])`
([#1072](https://github.com/google/json_serializable.dart/issues/1072))

## 4.5.0

- Added `FieldRename.screamingSnake`.
Expand Down Expand Up @@ -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()`.
Expand Down
31 changes: 31 additions & 0 deletions json_annotation/lib/src/json_converter.dart
Expand Up @@ -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<String, dynamic>].
///
///
/// [JsonConverter]s can be placed either on the class:
///
/// ```dart
/// class MyConverter extends JsonConverter<Value, JSON> {
/// // 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<T, S> {
const JsonConverter();

Expand Down
36 changes: 36 additions & 0 deletions json_annotation/lib/src/json_serializable.dart
Expand Up @@ -6,6 +6,7 @@ import 'package:meta/meta_meta.dart';

import 'allowed_keys_helpers.dart';
import 'checked_helpers.dart';
import 'json_converter.dart';
kevmoo marked this conversation as resolved.
Show resolved Hide resolved
import 'json_key.dart';

part 'json_serializable.g.dart';
Expand Down Expand Up @@ -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<JsonConverter>? converters;

/// Creates a new [JsonSerializable] instance.
const JsonSerializable({
@Deprecated('Has no effect') bool? nullable,
Expand All @@ -203,6 +238,7 @@ class JsonSerializable {
this.fieldRename,
this.ignoreUnannotated,
this.includeIfNull,
this.converters,
this.genericArgumentFactories,
});

Expand Down
2 changes: 1 addition & 1 deletion 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.
Expand Down
2 changes: 2 additions & 0 deletions json_serializable/CHANGELOG.md
Expand Up @@ -2,6 +2,8 @@

- Added support for using a `JsonConverter<MyClass, Object>` on properties
of type `MyClass?`. ([#822](https://github.com/google/json_serializable.dart/issues/822))
- Added support for `JsonSerializable(converters: <JsonConverter>[])`
([#1072](https://github.com/google/json_serializable.dart/issues/1072))

## 6.2.0

Expand Down
16 changes: 8 additions & 8 deletions json_serializable/README.md
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion json_serializable/lib/src/check_dependencies.dart
Expand Up @@ -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<void> pubspecHasRightVersion(BuildStep buildStep) async {
final segments = buildStep.inputId.pathSegments;
Expand Down
2 changes: 1 addition & 1 deletion json_serializable/lib/src/json_serializable_generator.dart
Expand Up @@ -20,7 +20,7 @@ class JsonSerializableGenerator
extends GeneratorForAnnotation<JsonSerializable> {
final Settings _settings;

JsonSerializable get config => _settings.config;
JsonSerializable get config => _settings.config.toJsonSerializable();

JsonSerializableGenerator.fromSettings(this._settings);

Expand Down
29 changes: 5 additions & 24 deletions json_serializable/lib/src/settings.dart
Expand Up @@ -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<TypeHelper>? typeHelpers,
}) : _config = config ?? ClassConfig.defaults,
}) : config = config != null
? ClassConfig.fromJsonSerializable(config)
: ClassConfig.defaults,
_typeHelpers = typeHelpers ?? defaultHelpers;

/// Creates an instance of [Settings].
Expand Down
93 changes: 41 additions & 52 deletions json_serializable/lib/src/type_helpers/config_types.dart
Expand Up @@ -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.
Expand Down Expand Up @@ -38,41 +39,20 @@ class KeyConfig {
/// configuration.
///
/// Values are all known, so types are non-nullable.
class ClassConfig implements JsonSerializable {
@override
class ClassConfig {
kevmoo marked this conversation as resolved.
Show resolved Hide resolved
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<String, String> ctorParamDefaults;
final List<DartObject> converters;

const ClassConfig({
required this.anyMap,
Expand All @@ -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(
Expand All @@ -105,33 +109,18 @@ class ClassConfig implements JsonSerializable {
includeIfNull: true,
);

@override
Map<String, dynamic> 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<String, dynamic> _$JsonSerializableToJson(JsonSerializable instance) =>
<String, dynamic>{
'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,
};