Skip to content
Open
Show file tree
Hide file tree
Changes from 10 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
10 changes: 10 additions & 0 deletions json_annotation/lib/src/json_serializable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'allowed_keys_helpers.dart';
import 'checked_helpers.dart';
import 'enum_helpers.dart';
import 'json_converter.dart';
import 'json_enum.dart' show JsonEnum;
import 'json_key.dart';

part 'json_serializable.g.dart';
Expand Down Expand Up @@ -175,6 +176,12 @@ class JsonSerializable {
/// fields annotated with [JsonKey].
final FieldRename? fieldRename;

/// Default field rename for enum values when not set on [JsonEnum].
///
/// Only applies when configured via `build.yaml`; has no effect when set on
/// [JsonSerializable] annotations in source code.
final FieldRename? enumFieldRename;

/// When `true` on classes with type parameters (generic types), extra
/// "helper" parameters will be generated for `fromJson` and/or `toJson` to
/// support serializing values of those types.
Expand Down Expand Up @@ -285,6 +292,7 @@ class JsonSerializable {
this.disallowUnrecognizedKeys,
this.explicitToJson,
this.fieldRename,
this.enumFieldRename,
this.ignoreUnannotated,
this.includeIfNull,
this.converters,
Expand All @@ -308,6 +316,7 @@ class JsonSerializable {
disallowUnrecognizedKeys: false,
explicitToJson: false,
fieldRename: FieldRename.none,
enumFieldRename: FieldRename.none,
ignoreUnannotated: false,
includeIfNull: true,
genericArgumentFactories: false,
Expand All @@ -329,6 +338,7 @@ class JsonSerializable {
disallowUnrecognizedKeys ?? defaults.disallowUnrecognizedKeys,
explicitToJson: explicitToJson ?? defaults.explicitToJson,
fieldRename: fieldRename ?? defaults.fieldRename,
enumFieldRename: enumFieldRename ?? defaults.enumFieldRename,
ignoreUnannotated: ignoreUnannotated ?? defaults.ignoreUnannotated,
includeIfNull: includeIfNull ?? defaults.includeIfNull,
genericArgumentFactories:
Expand Down
7 changes: 7 additions & 0 deletions json_annotation/lib/src/json_serializable.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions json_serializable/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ targets:
date_time_utc: false
disallow_unrecognized_keys: false
explicit_to_json: false
enum_field_rename: none
field_rename: none
generic_argument_factories: false
ignore_unannotated: false
Expand Down
5 changes: 4 additions & 1 deletion json_serializable/lib/src/encoder_helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,10 @@ mixin EncodeHelper implements HelperCore {
// We can consider enums as kinda like having custom converters
// same rules apply. If `null` is in the set of encoded values, we
// should not write naive
final enumWithNullValue = enumFieldWithNullInEncodeMap(field.type);
final enumWithNullValue = enumFieldWithNullInEncodeMap(
field.type,
defaultEnumFieldRename: config.enumFieldRename,
);
if (enumWithNullValue != null) {
return !enumWithNullValue;
}
Expand Down
30 changes: 25 additions & 5 deletions json_serializable/lib/src/enum_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import 'package:analyzer/dart/constant/value.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:build/build.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:source_gen/source_gen.dart';
import 'package:source_helper/source_helper.dart';
Expand All @@ -19,8 +20,14 @@ String constMapName(DartType targetType) =>
///
/// Otherwise, returns `true` if [targetType] is nullable OR if one of the
/// encoded values of the enum is `null`.
bool? enumFieldWithNullInEncodeMap(DartType targetType) {
final enumMap = _enumMap(targetType);
bool? enumFieldWithNullInEncodeMap(
DartType targetType, {
FieldRename? defaultEnumFieldRename,
}) {
final enumMap = _enumMap(
targetType,
defaultEnumFieldRename: defaultEnumFieldRename,
);

if (enumMap == null) return null;

Expand All @@ -34,10 +41,12 @@ bool? enumFieldWithNullInEncodeMap(DartType targetType) {
String? enumValueMapFromType(
DartType targetType, {
bool nullWithNoAnnotation = false,
FieldRename? defaultEnumFieldRename,
}) {
final enumMap = _enumMap(
targetType,
nullWithNoAnnotation: nullWithNoAnnotation,
defaultEnumFieldRename: defaultEnumFieldRename,
);

if (enumMap == null) return null;
Expand All @@ -56,6 +65,7 @@ String? enumValueMapFromType(
Map<FieldElement, Object?>? _enumMap(
DartType targetType, {
bool nullWithNoAnnotation = false,
FieldRename? defaultEnumFieldRename,
}) {
final targetTypeElement = targetType.element;
if (targetTypeElement == null) return null;
Expand All @@ -68,12 +78,20 @@ Map<FieldElement, Object?>? _enumMap(
return null;
}

if (jsonEnum.valueField != null && jsonEnum.fieldRename != FieldRename.none) {
log.warning(
'`JsonEnum.fieldRename` is ignored when `valueField` is set. '
'Enum values are derived from the `${jsonEnum.valueField}` field.',
);
}

return {
for (var field in enumFields)
field: _generateEntry(
field: field,
jsonEnum: jsonEnum,
targetType: targetType,
defaultEnumFieldRename: defaultEnumFieldRename,
),
};
}
Expand All @@ -82,6 +100,7 @@ Object? _generateEntry({
required FieldElement field,
required JsonEnum jsonEnum,
required DartType targetType,
FieldRename? defaultEnumFieldRename,
}) {
final annotation = const TypeChecker.typeNamed(
JsonValue,
Expand All @@ -91,8 +110,6 @@ Object? _generateEntry({
if (annotation == null) {
final valueField = jsonEnum.valueField;
if (valueField != null) {
// TODO: fieldRename is pointless here!!! At least log a warning!

final fieldElementType = field.type.element as EnumElement;

final e = fieldElementType.getField(valueField);
Expand Down Expand Up @@ -125,7 +142,10 @@ Object? _generateEntry({
);
}
} else {
return encodedFieldName(jsonEnum.fieldRename, field.name!);
final effectiveRename = jsonEnum.fieldRename != FieldRename.none
? jsonEnum.fieldRename
: (defaultEnumFieldRename ?? FieldRename.none);
return encodedFieldName(effectiveRename, field.name!);
}
} else {
final reader = ConstantReader(annotation);
Expand Down
6 changes: 5 additions & 1 deletion json_serializable/lib/src/json_enum_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@ import 'package:json_annotation/json_annotation.dart';
import 'package:source_gen/source_gen.dart';

import 'enum_utils.dart';
import 'settings.dart';

class JsonEnumGenerator extends GeneratorForAnnotation<JsonEnum> {
const JsonEnumGenerator() : super(inPackage: 'json_annotation');
JsonEnumGenerator(this._settings) : super(inPackage: 'json_annotation');

final Settings _settings;

@override
List<String> generateForAnnotatedElement(
Expand All @@ -28,6 +31,7 @@ class JsonEnumGenerator extends GeneratorForAnnotation<JsonEnum> {
final value = enumValueMapFromType(
element.thisType,
nullWithNoAnnotation: true,
defaultEnumFieldRename: _settings.config.enumFieldRename,
);

return [?value];
Expand Down
2 changes: 1 addition & 1 deletion json_serializable/lib/src/json_part_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Builder jsonPartBuilder({
[
_UnifiedGenerator([
JsonSerializableGenerator.fromSettings(settings),
const JsonEnumGenerator(),
JsonEnumGenerator(settings),
]),
const JsonLiteralGenerator(),
],
Expand Down
2 changes: 1 addition & 1 deletion json_serializable/lib/src/settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class Settings {
/// If [typeHelpers] is not provided, the built-in helpers are used:
/// [BigIntHelper], [DateTimeHelper], [DurationHelper], [JsonHelper], and
/// [UriHelper].
Settings({required JsonSerializable? config, List<TypeHelper>? typeHelpers})
Settings({JsonSerializable? config, List<TypeHelper>? typeHelpers})
: config = config != null
? ClassConfig.fromJsonSerializable(config)
: ClassConfig.defaults,
Expand Down
6 changes: 6 additions & 0 deletions json_serializable/lib/src/type_helpers/config_types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class ClassConfig {
final bool disallowUnrecognizedKeys;
final bool explicitToJson;
final FieldRename fieldRename;
final FieldRename enumFieldRename;
final bool genericArgumentFactories;
final bool ignoreUnannotated;
final bool includeIfNull;
Expand All @@ -85,6 +86,7 @@ class ClassConfig {
required this.disallowUnrecognizedKeys,
required this.explicitToJson,
required this.fieldRename,
required this.enumFieldRename,
required this.genericArgumentFactories,
required this.ignoreUnannotated,
required this.includeIfNull,
Expand Down Expand Up @@ -121,6 +123,8 @@ class ClassConfig {
config.genericArgumentFactories ??
ClassConfig.defaults.genericArgumentFactories,
fieldRename: config.fieldRename ?? ClassConfig.defaults.fieldRename,
enumFieldRename:
config.enumFieldRename ?? ClassConfig.defaults.enumFieldRename,
disallowUnrecognizedKeys:
config.disallowUnrecognizedKeys ??
ClassConfig.defaults.disallowUnrecognizedKeys,
Expand All @@ -142,6 +146,7 @@ class ClassConfig {
disallowUnrecognizedKeys: false,
explicitToJson: false,
fieldRename: FieldRename.none,
enumFieldRename: FieldRename.none,
genericArgumentFactories: false,
ignoreUnannotated: false,
includeIfNull: true,
Expand All @@ -162,6 +167,7 @@ class ClassConfig {
includeIfNull: includeIfNull,
genericArgumentFactories: genericArgumentFactories,
fieldRename: fieldRename,
enumFieldRename: enumFieldRename,
disallowUnrecognizedKeys: disallowUnrecognizedKeys,
dateTimeUtc: dateTimeUtc,
// TODO typeConverters = []
Expand Down
16 changes: 13 additions & 3 deletions json_serializable/lib/src/type_helpers/enum_helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ class EnumHelper extends TypeHelper<TypeHelperContextWithConfig> {
String expression,
TypeHelperContextWithConfig context,
) {
final memberContent = enumValueMapFromType(targetType);
final memberContent = enumValueMapFromType(
targetType,
defaultEnumFieldRename: context.config.enumFieldRename,
);

if (memberContent == null) {
return null;
Expand All @@ -30,7 +33,11 @@ class EnumHelper extends TypeHelper<TypeHelperContextWithConfig> {
context.addMember(memberContent);

if (targetType.isNullableType ||
enumFieldWithNullInEncodeMap(targetType) == true) {
enumFieldWithNullInEncodeMap(
targetType,
defaultEnumFieldRename: context.config.enumFieldRename,
) ==
true) {
return '${constMapName(targetType)}[$expression]';
} else {
return '${constMapName(targetType)}[$expression]!';
Expand All @@ -44,7 +51,10 @@ class EnumHelper extends TypeHelper<TypeHelperContextWithConfig> {
TypeHelperContextWithConfig context,
bool defaultProvided,
) {
final memberContent = enumValueMapFromType(targetType);
final memberContent = enumValueMapFromType(
targetType,
defaultEnumFieldRename: context.config.enumFieldRename,
);

if (memberContent == null) {
return null;
Expand Down
2 changes: 2 additions & 0 deletions json_serializable/lib/src/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ JsonSerializable _valueForAnnotation(ConstantReader reader) => JsonSerializable(
reader.read('disallowUnrecognizedKeys').literalValue as bool?,
explicitToJson: reader.read('explicitToJson').literalValue as bool?,
fieldRename: readEnum(reader.read('fieldRename'), FieldRename.values),
enumFieldRename: readEnum(reader.read('enumFieldRename'), FieldRename.values),
genericArgumentFactories:
reader.read('genericArgumentFactories').literalValue as bool?,
ignoreUnannotated: reader.read('ignoreUnannotated').literalValue as bool?,
Expand Down Expand Up @@ -121,6 +122,7 @@ ClassConfig mergeConfig(
annotation.disallowUnrecognizedKeys ?? config.disallowUnrecognizedKeys,
explicitToJson: annotation.explicitToJson ?? config.explicitToJson,
fieldRename: annotation.fieldRename ?? config.fieldRename,
enumFieldRename: annotation.enumFieldRename ?? config.enumFieldRename,
genericArgumentFactories:
annotation.genericArgumentFactories ??
(classElement.typeParameters.isNotEmpty &&
Expand Down
4 changes: 4 additions & 0 deletions json_serializable/test/config_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ void main() {

String lastLine;
lastLine = switch (entry.key) {
'enum_field_rename' =>
'`42` is not one of the supported values: none, kebab, snake, '
'pascal, screamingSnake',
'field_rename' =>
'`42` is not one of the supported values: none, kebab, snake, '
'pascal, screamingSnake',
Expand Down Expand Up @@ -178,6 +181,7 @@ const _invalidConfig = {
'create_to_json': 42,
'date_time_utc': 42,
'disallow_unrecognized_keys': 42,
'enum_field_rename': 42,
'explicit_to_json': 42,
'field_rename': 42,
'generic_argument_factories': 42,
Expand Down
Loading
Loading