Skip to content

Commit b2baaad

Browse files
committed
Adding case insensitive to enum based on loading JsonEnum config
1 parent 8aadb45 commit b2baaad

File tree

9 files changed

+62
-3
lines changed

9 files changed

+62
-3
lines changed

json_annotation/lib/src/enum_helpers.dart

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,19 @@
44

55
import 'json_key.dart';
66

7+
/// Compare an enum value against a source using case-insensitive.
8+
///
9+
/// Exposed only for code generated by `package:json_serializable`.
10+
/// Not meant to be used directly by user code.
11+
bool $enumCompareCaseInsensitive(String arg1, Object arg2) => arg2 is String &&
12+
(arg1.toLowerCase() == arg2.toLowerCase());
13+
14+
/// Compare an enum value against a source.
15+
///
16+
/// Exposed only for code generated by `package:json_serializable`.
17+
/// Not meant to be used directly by user code.
18+
bool $enumCompareStandard<V>(V arg1, Object arg2) => arg1 == arg2;
19+
720
/// Returns the key associated with value [source] from [enumValues], if one
821
/// exists.
922
///
@@ -18,13 +31,16 @@ K? $enumDecodeNullable<K extends Enum, V>(
1831
Map<K, V> enumValues,
1932
Object? source, {
2033
Enum? unknownValue,
34+
bool Function(V arg1, Object arg2)? comparator,
2135
}) {
2236
if (source == null) {
2337
return null;
2438
}
2539

40+
comparator ??= $enumCompareStandard;
41+
2642
for (var entry in enumValues.entries) {
27-
if (entry.value == source) {
43+
if (comparator(entry.value, source)) {
2844
return entry.key;
2945
}
3046
}
@@ -65,6 +81,7 @@ K $enumDecode<K extends Enum, V>(
6581
Map<K, V> enumValues,
6682
Object? source, {
6783
K? unknownValue,
84+
bool Function(V arg1, Object arg2)? comparator,
6885
}) {
6986
if (source == null) {
7087
throw ArgumentError(
@@ -73,8 +90,9 @@ K $enumDecode<K extends Enum, V>(
7390
);
7491
}
7592

93+
comparator ??= $enumCompareStandard;
7694
for (var entry in enumValues.entries) {
77-
if (entry.value == source) {
95+
if (comparator(entry.value, source)) {
7896
return entry.key;
7997
}
8098
}

json_annotation/lib/src/json_enum.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class JsonEnum {
1313
const JsonEnum({
1414
this.alwaysCreate = false,
1515
this.fieldRename = FieldRename.none,
16+
this.caseInsensitive = false,
1617
});
1718

1819
/// If `true`, `_$[enum name]EnumMap` is generated for in library containing
@@ -34,4 +35,16 @@ class JsonEnum {
3435
/// Note: the value for [JsonValue.value] takes precedence over this option
3536
/// for entries annotated with [JsonValue].
3637
final FieldRename fieldRename;
38+
39+
/// if `true`, enum comparison will be done using case-insensitive.
40+
///
41+
/// The default, `false`, means enum comparison will be done using
42+
/// case-sensitive.
43+
final bool caseInsensitive;
44+
45+
factory JsonEnum.fromJson(Map<String, dynamic> json) {
46+
final caseInsensitive = json['case_insensitive'] as bool?;
47+
return // caseInsensitive == null ? null :
48+
JsonEnum(caseInsensitive: caseInsensitive ?? false);
49+
}
3750
}

json_serializable/lib/src/enum_utils.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ JsonEnum _fromAnnotation(DartObject? dartObject) {
7575
final reader = ConstantReader(dartObject);
7676
return JsonEnum(
7777
alwaysCreate: reader.read('alwaysCreate').literalValue as bool,
78+
caseInsensitive: reader.read('caseInsensitive').literalValue as bool,
7879
fieldRename: enumValueForDartObject(
7980
reader.read('fieldRename').objectValue,
8081
FieldRename.values,

json_serializable/lib/src/json_key_utils.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,15 @@ KeyConfig _from(FieldElement element, ClassConfig classAnnotation) {
3030
final ctorParamDefault = classAnnotation.ctorParamDefaults[element.name];
3131

3232
if (obj.isNull) {
33+
final enumObj = jsonEnumAnnotation(element);
34+
3335
return _populateJsonKey(
3436
classAnnotation,
3537
element,
3638
defaultValue: ctorParamDefault,
3739
ignore: classAnnotation.ignoreUnannotated,
40+
caseInsensitive: enumObj.isNull ? null :
41+
enumObj.read('caseInsensitive').literalValue as bool?,
3842
);
3943
}
4044

@@ -241,6 +245,7 @@ KeyConfig _populateJsonKey(
241245
String? readValueFunctionName,
242246
bool? required,
243247
String? unknownEnumValue,
248+
bool? caseInsensitive,
244249
}) {
245250
if (disallowNullValue == true) {
246251
if (includeIfNull == true) {
@@ -261,6 +266,7 @@ KeyConfig _populateJsonKey(
261266
readValueFunctionName: readValueFunctionName,
262267
required: required ?? false,
263268
unknownEnumValue: unknownEnumValue,
269+
caseInsensitive: caseInsensitive,
264270
);
265271
}
266272

json_serializable/lib/src/type_helpers/config_types.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ class KeyConfig {
2121

2222
final String? unknownEnumValue;
2323

24+
final bool? caseInsensitive;
25+
2426
final String? readValueFunctionName;
2527

2628
KeyConfig({
@@ -32,6 +34,7 @@ class KeyConfig {
3234
required this.readValueFunctionName,
3335
required this.required,
3436
required this.unknownEnumValue,
37+
required this.caseInsensitive,
3538
});
3639
}
3740

json_serializable/lib/src/type_helpers/enum_helper.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ class EnumHelper extends TypeHelper<TypeHelperContextWithConfig> {
7171
expression,
7272
if (jsonKey.unknownEnumValue != null)
7373
'unknownValue: ${jsonKey.unknownEnumValue}',
74+
if ((jsonKey.caseInsensitive ?? false) == true)
75+
r'comparator: $enumCompareCaseInsensitive',
7476
];
7577

7678
return '$functionName(${args.join(', ')})';

json_serializable/lib/src/utils.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import 'type_helpers/config_types.dart';
1313

1414
const _jsonKeyChecker = TypeChecker.fromRuntime(JsonKey);
1515

16+
const _jsonEnumChecker = TypeChecker.fromRuntime(JsonEnum);
17+
1618
DartObject? _jsonKeyAnnotation(FieldElement element) =>
1719
_jsonKeyChecker.firstAnnotationOf(element) ??
1820
(element.getter == null
@@ -22,6 +24,14 @@ DartObject? _jsonKeyAnnotation(FieldElement element) =>
2224
ConstantReader jsonKeyAnnotation(FieldElement element) =>
2325
ConstantReader(_jsonKeyAnnotation(element));
2426

27+
DartObject? _jsonEnumAnnotation(Element? element) =>
28+
(element != null && element is ClassElement) ?
29+
_jsonEnumChecker.firstAnnotationOf(element)
30+
: null;
31+
32+
ConstantReader jsonEnumAnnotation(FieldElement element) =>
33+
ConstantReader(_jsonEnumAnnotation(element.type.element));
34+
2535
/// Returns `true` if [element] is annotated with [JsonKey].
2636
bool hasJsonKeyAnnotation(FieldElement element) =>
2737
_jsonKeyAnnotation(element) != null;

json_serializable/test/integration/integration_test.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,12 @@ void main() {
8888
roundTripOrder(order);
8989
});
9090

91+
test('case insensitive map', () {
92+
final jsonOrder = {'category': 'CHaRmED'};
93+
final order = Order.fromJson(jsonOrder);
94+
expect(order.category, Category.charmed);
95+
});
96+
9197
test('required, but missing enum value fails', () {
9298
expect(
9399
() => Person.fromJson({

json_serializable/test/integration/json_test_common.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import 'dart:collection';
66

77
import 'package:json_annotation/json_annotation.dart';
88

9-
@JsonEnum(fieldRename: FieldRename.kebab)
9+
@JsonEnum(fieldRename: FieldRename.kebab, caseInsensitive: true)
1010
enum Category {
1111
top,
1212
bottom,

0 commit comments

Comments
 (0)