Skip to content

Add JsonConverter(converters) option #1135

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

Merged
merged 13 commits into from
May 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions checked_yaml/pubspec.yaml
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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';
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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 {
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,
};
Loading