Skip to content

Commit 7f2784b

Browse files
authored
Use classes with non-nullable fields for core generator classes (#843)
Eliminates most null checks A breaking change to `type_helper.dart`, but this is only a pseudo-supported library
1 parent 67b21d2 commit 7f2784b

19 files changed

+206
-52
lines changed

json_annotation/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
- Fix a potential error with `checked: true` when `ArgumentError.message` is
44
`null`.
55
- Updated `JsonSerializable.fromJson` to handle `null` values.
6+
- Deprecate `JsonSerializable` `defaults` and `withDefaults()`.
67

78
## 4.0.0
89

json_annotation/lib/src/json_serializable.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ class JsonSerializable {
194194

195195
/// An instance of [JsonSerializable] with all fields set to their default
196196
/// values.
197+
@Deprecated('Was only ever included to support builder infrastructure.')
197198
static const defaults = JsonSerializable(
198199
anyMap: false,
199200
checked: false,
@@ -212,6 +213,7 @@ class JsonSerializable {
212213
///
213214
/// Otherwise, the returned value has the default value as defined in
214215
/// [defaults].
216+
@Deprecated('Was only ever included to support builder infrastructure.')
215217
JsonSerializable withDefaults() => JsonSerializable(
216218
anyMap: anyMap ?? defaults.anyMap,
217219
checked: checked ?? defaults.checked,

json_serializable/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
- Correctly handle nullable generic fields (`T?`) with
55
`genericArgumentFactories`.
66

7+
- `type_helper.dart` - **BREAKING changes**
8+
- The API is now null-safe.
9+
- new `KeyConfig` class replaces `JsonKey`.
10+
- new `ClassConfig` class replaces `JsonSerializable`.
11+
712
## 4.0.3
813

914
- Correctly handle nullable values with `genericArgumentFactories`.

json_serializable/lib/src/decode_helper.dart

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,15 @@ abstract class DecodeHelper implements HelperCore {
2525
Map<String, FieldElement> accessibleFields,
2626
Map<String, String> unavailableReasons,
2727
) {
28-
assert(config.createFactory!);
28+
assert(config.createFactory);
2929
final buffer = StringBuffer();
3030

31-
final mapType = config.anyMap! ? 'Map' : 'Map<String, dynamic>';
31+
final mapType = config.anyMap ? 'Map' : 'Map<String, dynamic>';
3232
buffer.write('$targetClassReference '
3333
'${prefix}FromJson${genericClassArgumentsImpl(true)}'
3434
'($mapType json');
3535

36-
if (config.genericArgumentFactories!) {
36+
if (config.genericArgumentFactories) {
3737
for (var arg in element.typeParameters) {
3838
final helperName = fromJsonForType(
3939
arg.instantiate(nullabilitySuffix: NullabilitySuffix.none),
@@ -72,7 +72,7 @@ abstract class DecodeHelper implements HelperCore {
7272
final checks = _checkKeys(accessibleFields.values
7373
.where((fe) => data.usedCtorParamsAndFields.contains(fe.name)));
7474

75-
if (config.checked!) {
75+
if (config.checked) {
7676
final classLiteral = escapeDartString(element.name);
7777

7878
buffer..write('''
@@ -131,22 +131,22 @@ abstract class DecodeHelper implements HelperCore {
131131
String constantList(Iterable<FieldElement> things) =>
132132
'const ${jsonLiteralAsDart(things.map(nameAccess).toList())}';
133133

134-
if (config.disallowUnrecognizedKeys!) {
134+
if (config.disallowUnrecognizedKeys) {
135135
final allowKeysLiteral = constantList(accessibleFields);
136136

137137
args.add('allowedKeys: $allowKeysLiteral');
138138
}
139139

140140
final requiredKeys =
141-
accessibleFields.where((fe) => jsonKeyFor(fe).required!).toList();
141+
accessibleFields.where((fe) => jsonKeyFor(fe).required).toList();
142142
if (requiredKeys.isNotEmpty) {
143143
final requiredKeyLiteral = constantList(requiredKeys);
144144

145145
args.add('requiredKeys: $requiredKeyLiteral');
146146
}
147147

148148
final disallowNullKeys = accessibleFields
149-
.where((fe) => jsonKeyFor(fe).disallowNullValue!)
149+
.where((fe) => jsonKeyFor(fe).disallowNullValue)
150150
.toList();
151151
if (disallowNullKeys.isNotEmpty) {
152152
final disallowNullKeyLiteral = constantList(disallowNullKeys);
@@ -173,7 +173,7 @@ abstract class DecodeHelper implements HelperCore {
173173

174174
String value;
175175
try {
176-
if (config.checked!) {
176+
if (config.checked) {
177177
value = contextHelper
178178
.deserialize(
179179
targetType,
@@ -204,7 +204,7 @@ abstract class DecodeHelper implements HelperCore {
204204
final jsonKey = jsonKeyFor(field);
205205
final defaultValue = jsonKey.defaultValue;
206206
if (defaultValue != null) {
207-
if (jsonKey.disallowNullValue! && jsonKey.required!) {
207+
if (jsonKey.disallowNullValue && jsonKey.required) {
208208
log.warning('The `defaultValue` on field `${field.name}` will have no '
209209
'effect because both `disallowNullValue` and `required` are set to '
210210
'`true`.');

json_serializable/lib/src/encoder_helper.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,15 @@ abstract class EncodeHelper implements HelperCore {
1717
String _fieldAccess(FieldElement field) => '$_toJsonParamName.${field.name}';
1818

1919
Iterable<String> createToJson(Set<FieldElement> accessibleFields) sync* {
20-
assert(config.createToJson!);
20+
assert(config.createToJson);
2121

2222
final buffer = StringBuffer();
2323

2424
final functionName = '${prefix}ToJson${genericClassArgumentsImpl(true)}';
2525
buffer.write('Map<String, dynamic> '
2626
'$functionName($targetClassReference $_toJsonParamName');
2727

28-
if (config.genericArgumentFactories!) {
28+
if (config.genericArgumentFactories) {
2929
for (var arg in element.typeParameters) {
3030
final helperName = toJsonForType(
3131
arg.instantiate(nullabilitySuffix: NullabilitySuffix.none),
@@ -140,7 +140,7 @@ abstract class EncodeHelper implements HelperCore {
140140
bool _writeJsonValueNaive(FieldElement field) {
141141
final jsonKey = jsonKeyFor(field);
142142

143-
return jsonKey.includeIfNull! ||
143+
return jsonKey.includeIfNull ||
144144
(!field.type.isNullableType && !_fieldHasCustomEncoder(field));
145145
}
146146

json_serializable/lib/src/generator_helper.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class GeneratorHelper extends HelperCore with EncodeHelper, DecodeHelper {
4141
Iterable<String> generate() sync* {
4242
assert(_addedMembers.isEmpty);
4343

44-
if (config.genericArgumentFactories! && element.typeParameters.isEmpty) {
44+
if (config.genericArgumentFactories && element.typeParameters.isEmpty) {
4545
log.warning(
4646
'The class `${element.displayName}` is annotated '
4747
'with `JsonSerializable` field `genericArgumentFactories: true`. '
@@ -68,7 +68,7 @@ class GeneratorHelper extends HelperCore with EncodeHelper, DecodeHelper {
6868
unavailableReasons[field.name] =
6969
'Setter-only properties are not supported.';
7070
log.warning('Setters are ignored: ${element.name}.${field.name}');
71-
} else if (jsonKeyFor(field).ignore!) {
71+
} else if (jsonKeyFor(field).ignore) {
7272
unavailableReasons[field.name] =
7373
'It is assigned to an ignored field.';
7474
} else {
@@ -81,7 +81,7 @@ class GeneratorHelper extends HelperCore with EncodeHelper, DecodeHelper {
8181
);
8282

8383
var accessibleFieldSet = accessibleFields.values.toSet();
84-
if (config.createFactory!) {
84+
if (config.createFactory) {
8585
final createResult = createFactory(accessibleFields, unavailableReasons);
8686
yield createResult.output;
8787

@@ -108,7 +108,7 @@ class GeneratorHelper extends HelperCore with EncodeHelper, DecodeHelper {
108108
},
109109
);
110110

111-
if (config.createToJson!) {
111+
if (config.createToJson) {
112112
yield* createToJson(accessibleFieldSet);
113113
}
114114

json_serializable/lib/src/helper_core.dart

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

55
import 'package:analyzer/dart/element/element.dart';
66
import 'package:analyzer/dart/element/type.dart';
7-
import 'package:json_annotation/json_annotation.dart';
87
import 'package:meta/meta.dart';
98
import 'package:source_gen/source_gen.dart';
109

1110
import 'constants.dart';
1211
import 'json_key_utils.dart';
1312
import 'type_helper.dart';
1413
import 'type_helper_ctx.dart';
14+
import 'type_helpers/config_types.dart';
1515
import 'unsupported_type_error.dart';
1616
import 'utils.dart';
1717

1818
abstract class HelperCore {
1919
final ClassElement element;
20-
final JsonSerializable config;
20+
final ClassConfig config;
2121

2222
HelperCore(this.element, this.config);
2323

@@ -30,7 +30,7 @@ abstract class HelperCore {
3030
'${element.name}${genericClassArgumentsImpl(false)}';
3131

3232
@protected
33-
String nameAccess(FieldElement field) => jsonKeyFor(field).name!;
33+
String nameAccess(FieldElement field) => jsonKeyFor(field).name;
3434

3535
@protected
3636
String safeNameAccess(FieldElement field) =>
@@ -48,7 +48,7 @@ abstract class HelperCore {
4848
genericClassArguments(element, withConstraints);
4949

5050
@protected
51-
JsonKey jsonKeyFor(FieldElement field) => jsonKeyForField(field, config);
51+
KeyConfig jsonKeyFor(FieldElement field) => jsonKeyForField(field, config);
5252

5353
@protected
5454
TypeHelperCtx getHelperContext(FieldElement field) =>

json_serializable/lib/src/json_key_utils.dart

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,15 @@ import 'package:source_gen/source_gen.dart';
1010

1111
import 'json_literal_generator.dart';
1212
import 'shared_checkers.dart';
13+
import 'type_helpers/config_types.dart';
1314
import 'utils.dart';
1415

15-
final _jsonKeyExpando = Expando<JsonKey>();
16+
final _jsonKeyExpando = Expando<KeyConfig>();
1617

17-
JsonKey jsonKeyForField(FieldElement field, JsonSerializable classAnnotation) =>
18+
KeyConfig jsonKeyForField(FieldElement field, ClassConfig classAnnotation) =>
1819
_jsonKeyExpando[field] ??= _from(field, classAnnotation);
1920

20-
JsonKey _from(FieldElement element, JsonSerializable classAnnotation) {
21+
KeyConfig _from(FieldElement element, ClassConfig classAnnotation) {
2122
// If an annotation exists on `element` the source is a 'real' field.
2223
// If the result is `null`, check the getter – it is a property.
2324
// TODO: setters: github.com/google/json_serializable.dart/issues/24
@@ -183,8 +184,8 @@ JsonKey _from(FieldElement element, JsonSerializable classAnnotation) {
183184
);
184185
}
185186

186-
JsonKey _populateJsonKey(
187-
JsonSerializable classAnnotation,
187+
KeyConfig _populateJsonKey(
188+
ClassConfig classAnnotation,
188189
FieldElement element, {
189190
Object? defaultValue,
190191
bool? disallowNullValue,
@@ -203,7 +204,7 @@ JsonKey _populateJsonKey(
203204
}
204205
}
205206

206-
final jsonKey = JsonKey(
207+
return KeyConfig(
207208
defaultValue: defaultValue,
208209
disallowNullValue: disallowNullValue ?? false,
209210
ignore: ignore ?? false,
@@ -213,8 +214,6 @@ JsonKey _populateJsonKey(
213214
required: required ?? false,
214215
unknownEnumValue: unknownEnumValue,
215216
);
216-
217-
return jsonKey;
218217
}
219218

220219
String _encodedFieldName(
@@ -245,10 +244,10 @@ String _encodedFieldName(
245244
}
246245
}
247246

248-
bool? _includeIfNull(
247+
bool _includeIfNull(
249248
bool? keyIncludeIfNull,
250249
bool? keyDisallowNullValue,
251-
bool? classIncludeIfNull,
250+
bool classIncludeIfNull,
252251
) {
253252
if (keyDisallowNullValue == true) {
254253
assert(keyIncludeIfNull != true);

json_serializable/lib/src/settings.dart

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'package:json_annotation/json_annotation.dart';
66

77
import 'type_helper.dart';
88
import 'type_helpers/big_int_helper.dart';
9+
import 'type_helpers/config_types.dart';
910
import 'type_helpers/convert_helper.dart';
1011
import 'type_helpers/date_time_helper.dart';
1112
import 'type_helpers/duration_helper.dart';
@@ -44,7 +45,24 @@ class Settings {
4445

4546
final JsonSerializable _config;
4647

47-
JsonSerializable get config => _config.withDefaults();
48+
ClassConfig get config => ClassConfig(
49+
checked: _config.checked ?? ClassConfig.defaults.checked,
50+
anyMap: _config.anyMap ?? ClassConfig.defaults.anyMap,
51+
createFactory:
52+
_config.createFactory ?? ClassConfig.defaults.createFactory,
53+
createToJson: _config.createToJson ?? ClassConfig.defaults.createToJson,
54+
ignoreUnannotated:
55+
_config.ignoreUnannotated ?? ClassConfig.defaults.ignoreUnannotated,
56+
explicitToJson:
57+
_config.explicitToJson ?? ClassConfig.defaults.explicitToJson,
58+
includeIfNull:
59+
_config.includeIfNull ?? ClassConfig.defaults.includeIfNull,
60+
genericArgumentFactories: _config.genericArgumentFactories ??
61+
ClassConfig.defaults.genericArgumentFactories,
62+
fieldRename: _config.fieldRename ?? ClassConfig.defaults.fieldRename,
63+
disallowUnrecognizedKeys: _config.disallowUnrecognizedKeys ??
64+
ClassConfig.defaults.disallowUnrecognizedKeys,
65+
);
4866

4967
/// Creates an instance of [Settings].
5068
///
@@ -54,7 +72,7 @@ class Settings {
5472
const Settings({
5573
JsonSerializable? config,
5674
List<TypeHelper>? typeHelpers,
57-
}) : _config = config ?? JsonSerializable.defaults,
75+
}) : _config = config ?? ClassConfig.defaults,
5876
_typeHelpers = typeHelpers ?? defaultHelpers;
5977

6078
/// Creates an instance of [Settings].

json_serializable/lib/src/type_helper.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
import 'package:analyzer/dart/element/element.dart';
66
import 'package:analyzer/dart/element/type.dart';
7-
import 'package:json_annotation/json_annotation.dart';
7+
8+
import 'type_helpers/config_types.dart';
89

910
/// Context information provided in calls to [TypeHelper.serialize] and
1011
/// [TypeHelper.deserialize].
@@ -30,7 +31,7 @@ abstract class TypeHelperContext {
3031
/// Extended context information with includes configuration values
3132
/// corresponding to `JsonSerializableGenerator` settings.
3233
abstract class TypeHelperContextWithConfig extends TypeHelperContext {
33-
JsonSerializable get config;
34+
ClassConfig get config;
3435
}
3536

3637
abstract class TypeHelper<T extends TypeHelperContext> {

0 commit comments

Comments
 (0)