Skip to content

Commit 6babf3a

Browse files
committed
feat: add aliases to JsonValue
1 parent f76e7df commit 6babf3a

27 files changed

+902
-229
lines changed

_test_yaml/test/src/build_config.g.dart

Lines changed: 24 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

json_annotation/lib/src/enum_helpers.dart

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,53 @@ K? $enumDecodeNullable<K extends Enum, V>(
5151
return unknownValue;
5252
}
5353

54+
/// Returns the key associated with value [source] from [decodeMap], if one
55+
/// exists.
56+
///
57+
/// If [unknownValue] is not `null` and [source] is not a value in [decodeMap],
58+
/// [unknownValue] is returned. Otherwise, an [ArgumentError] is thrown.
59+
///
60+
/// If [source] is `null`, `null` is returned.
61+
///
62+
/// Exposed only for code generated by `package:json_serializable`.
63+
/// Not meant to be used directly by user code.
64+
V? $enumDecodeNullableWithDecodeMap<K, V extends Enum>(
65+
Map<K, V> decodeMap,
66+
Object? source, {
67+
Enum? unknownValue,
68+
}) {
69+
if (source == null) {
70+
return null;
71+
}
72+
73+
final decodedValue = decodeMap[source];
74+
75+
if (decodedValue != null) {
76+
return decodedValue;
77+
}
78+
79+
if (unknownValue == JsonKey.nullForUndefinedEnumValue) {
80+
return null;
81+
}
82+
83+
if (unknownValue == null) {
84+
throw ArgumentError(
85+
'`$source` is not one of the supported values: '
86+
'${decodeMap.keys.join(', ')}',
87+
);
88+
}
89+
90+
if (unknownValue is! V) {
91+
throw ArgumentError.value(
92+
unknownValue,
93+
'unknownValue',
94+
'Must by of type `$K` or `JsonKey.nullForUndefinedEnumValue`.',
95+
);
96+
}
97+
98+
return unknownValue;
99+
}
100+
54101
/// Returns the key associated with value [source] from [enumValues], if one
55102
/// exists.
56103
///
@@ -88,3 +135,41 @@ K $enumDecode<K extends Enum, V>(
88135

89136
return unknownValue;
90137
}
138+
139+
/// Returns the key associated with value [source] from [decodeMap], if one
140+
/// exists.
141+
///
142+
/// If [unknownValue] is not `null` and [source] is not a value in [decodeMap],
143+
/// [unknownValue] is returned. Otherwise, an [ArgumentError] is thrown.
144+
///
145+
/// If [source] is `null`, an [ArgumentError] is thrown.
146+
///
147+
/// Exposed only for code generated by `package:json_serializable`.
148+
/// Not meant to be used directly by user code.
149+
V $enumDecodeWithDecodeMap<K, V extends Enum>(
150+
Map<K, V> decodeMap,
151+
Object? source, {
152+
V? unknownValue,
153+
}) {
154+
if (source == null) {
155+
throw ArgumentError(
156+
'A value must be provided. Supported values: '
157+
'${decodeMap.keys.join(', ')}',
158+
);
159+
}
160+
161+
final decodedValue = decodeMap[source];
162+
163+
if (decodedValue != null) {
164+
return decodedValue;
165+
}
166+
167+
if (unknownValue == null) {
168+
throw ArgumentError(
169+
'`$source` is not one of the supported values: '
170+
'${decodeMap.keys.join(', ')}',
171+
);
172+
}
173+
174+
return unknownValue;
175+
}

json_annotation/lib/src/json_value.dart

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,10 @@ class JsonValue {
99
/// Can be a [String] or an [int].
1010
final dynamic value;
1111

12-
const JsonValue(this.value);
12+
/// Optional values that can be used when deserializing.
13+
///
14+
/// The elements of [aliases] must be either [String] or [int].
15+
final List<Object?> aliases;
16+
17+
const JsonValue(this.value, {this.aliases = const []});
1318
}

json_serializable/README.md

Lines changed: 95 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -31,22 +31,52 @@ import 'package:json_annotation/json_annotation.dart';
3131
part 'example.g.dart';
3232
3333
@JsonSerializable()
34-
class Person {
35-
/// The generated code assumes these values exist in JSON.
36-
final String firstName, lastName;
34+
class A {
35+
final StatusCode? statusCode;
36+
final StatusCode2 statusCode2;
37+
final StatusCode3 statusCode3;
3738
38-
/// The generated code below handles if the corresponding JSON value doesn't
39-
/// exist or is empty.
40-
final DateTime? dateOfBirth;
39+
A(this.statusCode, this.statusCode2, this.statusCode3);
4140
42-
Person({required this.firstName, required this.lastName, this.dateOfBirth});
41+
factory A.fromJson(Map<String, dynamic> json) => _$AFromJson(json);
4342
44-
/// Connect the generated [_$PersonFromJson] function to the `fromJson`
45-
/// factory.
46-
factory Person.fromJson(Map<String, dynamic> json) => _$PersonFromJson(json);
43+
Map<String, dynamic> toJson() => _$AToJson(this);
44+
}
45+
46+
enum StatusCode {
47+
@JsonValue(200, aliases: [201, 202])
48+
success,
49+
@JsonValue(301)
50+
movedPermanently,
51+
@JsonValue(302)
52+
found,
53+
@JsonValue(500)
54+
internalServerError,
55+
}
56+
57+
@JsonEnum(valueField: 'code')
58+
enum StatusCode2 {
59+
success(200),
60+
movedPermanently(301),
61+
found(302),
62+
internalServerError(500);
4763
48-
/// Connect the generated [_$PersonToJson] function to the `toJson` method.
49-
Map<String, dynamic> toJson() => _$PersonToJson(this);
64+
const StatusCode2(this.code);
65+
66+
final int code;
67+
}
68+
69+
@JsonEnum(valueField: 'code')
70+
enum StatusCode3 {
71+
success(200),
72+
movedPermanently(301),
73+
@JsonValue(1000)
74+
found(302),
75+
internalServerError(500);
76+
77+
const StatusCode3(this.code);
78+
79+
final int code;
5080
}
5181
```
5282

@@ -55,19 +85,62 @@ Building creates the corresponding part `example.g.dart`:
5585
```dart
5686
part of 'example.dart';
5787
58-
Person _$PersonFromJson(Map<String, dynamic> json) => Person(
59-
firstName: json['firstName'] as String,
60-
lastName: json['lastName'] as String,
61-
dateOfBirth: json['dateOfBirth'] == null
62-
? null
63-
: DateTime.parse(json['dateOfBirth'] as String),
88+
A _$AFromJson(Map<String, dynamic> json) => A(
89+
$enumDecodeNullableWithDecodeMap(
90+
_$StatusCodeEnumDecodeMap, json['statusCode']),
91+
$enumDecodeWithDecodeMap(_$StatusCode2EnumDecodeMap, json['statusCode2']),
92+
$enumDecodeWithDecodeMap(_$StatusCode3EnumDecodeMap, json['statusCode3']),
6493
);
6594
66-
Map<String, dynamic> _$PersonToJson(Person instance) => <String, dynamic>{
67-
'firstName': instance.firstName,
68-
'lastName': instance.lastName,
69-
'dateOfBirth': instance.dateOfBirth?.toIso8601String(),
95+
Map<String, dynamic> _$AToJson(A instance) => <String, dynamic>{
96+
'statusCode': _$StatusCodeEnumMap[instance.statusCode],
97+
'statusCode2': _$StatusCode2EnumMap[instance.statusCode2]!,
98+
'statusCode3': _$StatusCode3EnumMap[instance.statusCode3]!,
7099
};
100+
101+
const _$StatusCodeEnumMap = {
102+
StatusCode.success: 200,
103+
StatusCode.movedPermanently: 301,
104+
StatusCode.found: 302,
105+
StatusCode.internalServerError: 500,
106+
};
107+
108+
const _$StatusCodeEnumDecodeMap = {
109+
200: StatusCode.success,
110+
201: StatusCode.success,
111+
202: StatusCode.success,
112+
301: StatusCode.movedPermanently,
113+
302: StatusCode.found,
114+
500: StatusCode.internalServerError,
115+
};
116+
117+
const _$StatusCode2EnumMap = {
118+
StatusCode2.success: 200,
119+
StatusCode2.movedPermanently: 301,
120+
StatusCode2.found: 302,
121+
StatusCode2.internalServerError: 500,
122+
};
123+
124+
const _$StatusCode2EnumDecodeMap = {
125+
200: StatusCode2.success,
126+
301: StatusCode2.movedPermanently,
127+
302: StatusCode2.found,
128+
500: StatusCode2.internalServerError,
129+
};
130+
131+
const _$StatusCode3EnumMap = {
132+
StatusCode3.success: 200,
133+
StatusCode3.movedPermanently: 301,
134+
StatusCode3.found: 1000,
135+
StatusCode3.internalServerError: 500,
136+
};
137+
138+
const _$StatusCode3EnumDecodeMap = {
139+
200: StatusCode3.success,
140+
301: StatusCode3.movedPermanently,
141+
1000: StatusCode3.found,
142+
500: StatusCode3.internalServerError,
143+
};
71144
```
72145

73146
# Running the code generator

json_serializable/example/example.dart

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,50 @@ import 'package:json_annotation/json_annotation.dart';
77
part 'example.g.dart';
88

99
@JsonSerializable()
10-
class Person {
11-
/// The generated code assumes these values exist in JSON.
12-
final String firstName, lastName;
10+
class A {
11+
final StatusCode? statusCode;
12+
final StatusCode2 statusCode2;
13+
final StatusCode3 statusCode3;
1314

14-
/// The generated code below handles if the corresponding JSON value doesn't
15-
/// exist or is empty.
16-
final DateTime? dateOfBirth;
15+
A(this.statusCode, this.statusCode2, this.statusCode3);
1716

18-
Person({required this.firstName, required this.lastName, this.dateOfBirth});
17+
factory A.fromJson(Map<String, dynamic> json) => _$AFromJson(json);
1918

20-
/// Connect the generated [_$PersonFromJson] function to the `fromJson`
21-
/// factory.
22-
factory Person.fromJson(Map<String, dynamic> json) => _$PersonFromJson(json);
19+
Map<String, dynamic> toJson() => _$AToJson(this);
20+
}
21+
22+
enum StatusCode {
23+
@JsonValue(200, aliases: [201, 202])
24+
success,
25+
@JsonValue(301)
26+
movedPermanently,
27+
@JsonValue(302)
28+
found,
29+
@JsonValue(500)
30+
internalServerError,
31+
}
32+
33+
@JsonEnum(valueField: 'code')
34+
enum StatusCode2 {
35+
success(200),
36+
movedPermanently(301),
37+
found(302),
38+
internalServerError(500);
39+
40+
const StatusCode2(this.code);
41+
42+
final int code;
43+
}
44+
45+
@JsonEnum(valueField: 'code')
46+
enum StatusCode3 {
47+
success(200),
48+
movedPermanently(301),
49+
@JsonValue(1000)
50+
found(302),
51+
internalServerError(500);
52+
53+
const StatusCode3(this.code);
2354

24-
/// Connect the generated [_$PersonToJson] function to the `toJson` method.
25-
Map<String, dynamic> toJson() => _$PersonToJson(this);
55+
final int code;
2656
}

0 commit comments

Comments
 (0)