Skip to content
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

Migrate implementation of json_serializable to null-safety #841

Merged
merged 3 commits into from
Mar 19, 2021
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
3 changes: 2 additions & 1 deletion json_annotation/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
## 4.0.1-dev
## 4.0.1

- Fix a potential error with `checked: true` when `ArgumentError.message` is
`null`.
- Updated `JsonSerializable.fromJson` to handle `null` values.

## 4.0.0

Expand Down
69 changes: 37 additions & 32 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.

4 changes: 2 additions & 2 deletions json_annotation/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: json_annotation
version: 4.0.1-dev
version: 4.0.1
description: >-
Classes and helper functions that support JSON code generation via the
`json_serializable` package.
Expand All @@ -10,4 +10,4 @@ environment:
# When changing JsonSerializable class.
# dev_dependencies:
# build_runner: ^1.0.0
# json_serializable: ^3.1.0
# json_serializable: ^4.0.0
4 changes: 4 additions & 0 deletions json_serializable/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 5.0.0-dev
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might be able to consider not doing a breaking release here if this package is not intended to be used as a library for any reason?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be used as a lib, but it's not widely supported.

I'll bump this back in a follow-up. I have other fixes I want to get in.


- Implementation is now null-safe.

## 4.0.3

- Correctly handle nullable values with `genericArgumentFactories`.
Expand Down
2 changes: 0 additions & 2 deletions json_serializable/example/example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
// 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.

// @dart=2.12

import 'package:json_annotation/json_annotation.dart';

part 'example.g.dart';
Expand Down
1 change: 0 additions & 1 deletion json_serializable/example/example.g.dart

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

2 changes: 1 addition & 1 deletion json_serializable/lib/builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Builder jsonSerializable(BuilderOptions options) {
lines.add('There is a problem with "${e.key}".');
}
if (e.message != null) {
lines.add(e.message);
lines.add(e.message!);
} else if (e.innerError != null) {
lines.add(e.innerError.toString());
}
Expand Down
33 changes: 16 additions & 17 deletions json_serializable/lib/src/decode_helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ abstract class DecodeHelper implements HelperCore {
Map<String, FieldElement> accessibleFields,
Map<String, String> unavailableReasons,
) {
assert(config.createFactory);
assert(config.createFactory!);
final buffer = StringBuffer();

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

if (config.genericArgumentFactories) {
if (config.genericArgumentFactories!) {
for (var arg in element.typeParameters) {
final helperName = fromJsonForType(
arg.instantiate(nullabilitySuffix: NullabilitySuffix.none),
Expand All @@ -49,8 +49,8 @@ abstract class DecodeHelper implements HelperCore {
buffer.write(') {\n');

String deserializeFun(String paramOrFieldName,
{ParameterElement ctorParam}) =>
_deserializeForField(accessibleFields[paramOrFieldName],
{ParameterElement? ctorParam}) =>
_deserializeForField(accessibleFields[paramOrFieldName]!,
ctorParam: ctorParam);

final data = _writeConstructorInvocation(
Expand All @@ -72,7 +72,7 @@ abstract class DecodeHelper implements HelperCore {
final checks = _checkKeys(accessibleFields.values
.where((fe) => data.usedCtorParamsAndFields.contains(fe.name)));

if (config.checked) {
if (config.checked!) {
final classLiteral = escapeDartString(element.name);

buffer..write('''
Expand All @@ -84,12 +84,12 @@ abstract class DecodeHelper implements HelperCore {

for (final field in data.fieldsToSet) {
buffer.writeln();
final safeName = safeNameAccess(accessibleFields[field]);
final safeName = safeNameAccess(accessibleFields[field]!);
buffer
..write('''
\$checkedConvert(json, $safeName, (v) => ''')
..write('val.$field = ')
..write(_deserializeForField(accessibleFields[field],
..write(_deserializeForField(accessibleFields[field]!,
checkedProperty: true))
..write(');');
}
Expand All @@ -98,7 +98,7 @@ abstract class DecodeHelper implements HelperCore {
}''');

final fieldKeyMap = Map.fromEntries(data.usedCtorParamsAndFields
.map((k) => MapEntry(k, nameAccess(accessibleFields[k])))
.map((k) => MapEntry(k, nameAccess(accessibleFields[k]!)))
.where((me) => me.key != me.value));

String fieldKeyMapArg;
Expand Down Expand Up @@ -131,22 +131,22 @@ abstract class DecodeHelper implements HelperCore {
String constantList(Iterable<FieldElement> things) =>
'const ${jsonLiteralAsDart(things.map(nameAccess).toList())}';

if (config.disallowUnrecognizedKeys) {
if (config.disallowUnrecognizedKeys!) {
final allowKeysLiteral = constantList(accessibleFields);

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

final requiredKeys =
accessibleFields.where((fe) => jsonKeyFor(fe).required).toList();
accessibleFields.where((fe) => jsonKeyFor(fe).required!).toList();
if (requiredKeys.isNotEmpty) {
final requiredKeyLiteral = constantList(requiredKeys);

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

final disallowNullKeys = accessibleFields
.where((fe) => jsonKeyFor(fe).disallowNullValue)
.where((fe) => jsonKeyFor(fe).disallowNullValue!)
.toList();
if (disallowNullKeys.isNotEmpty) {
final disallowNullKeyLiteral = constantList(disallowNullKeys);
Expand All @@ -163,18 +163,17 @@ abstract class DecodeHelper implements HelperCore {

String _deserializeForField(
FieldElement field, {
ParameterElement ctorParam,
bool checkedProperty,
ParameterElement? ctorParam,
bool checkedProperty = false,
}) {
checkedProperty ??= false;
final jsonKeyName = safeNameAccess(field);
final targetType = ctorParam?.type ?? field.type;
final contextHelper = getHelperContext(field);
final defaultProvided = jsonKeyFor(field).defaultValue != null;

String value;
try {
if (config.checked) {
if (config.checked!) {
value = contextHelper
.deserialize(
targetType,
Expand Down Expand Up @@ -205,7 +204,7 @@ abstract class DecodeHelper implements HelperCore {
final jsonKey = jsonKeyFor(field);
final defaultValue = jsonKey.defaultValue;
if (defaultValue != null) {
if (jsonKey.disallowNullValue && jsonKey.required) {
if (jsonKey.disallowNullValue! && jsonKey.required!) {
log.warning('The `defaultValue` on field `${field.name}` will have no '
'effect because both `disallowNullValue` and `required` are set to '
'`true`.');
Expand Down
6 changes: 3 additions & 3 deletions json_serializable/lib/src/encoder_helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ abstract class EncodeHelper implements HelperCore {
String _fieldAccess(FieldElement field) => '$_toJsonParamName.${field.name}';

Iterable<String> createToJson(Set<FieldElement> accessibleFields) sync* {
assert(config.createToJson);
assert(config.createToJson!);

final buffer = StringBuffer();

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

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

return jsonKey.includeIfNull ||
return jsonKey.includeIfNull! ||
(!field.type.isNullableType && !_fieldHasCustomEncoder(field));
}

Expand Down
8 changes: 4 additions & 4 deletions json_serializable/lib/src/field_helpers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ class _FieldSet implements Comparable<_FieldSet> {
_FieldSet._(this.field, this.sortField)
: assert(field.name == sortField.name);

factory _FieldSet(FieldElement classField, FieldElement superField) {
factory _FieldSet(FieldElement? classField, FieldElement? superField) {
// At least one of these will != null, perhaps both.
final fields = [classField, superField].where((fe) => fe != null).toList();
final fields = [classField, superField].whereType<FieldElement>().toList();

// Prefer the class field over the inherited field when sorting.
final sortField = fields.first;
Expand Down Expand Up @@ -58,9 +58,9 @@ class _FieldSet implements Comparable<_FieldSet> {
/// Returns the offset of given field/property in its source file – with a
/// preference for the getter if it's defined.
int _offsetFor(FieldElement e) {
if (e.getter != null && e.getter.nameOffset != e.nameOffset) {
if (e.getter != null && e.getter!.nameOffset != e.nameOffset) {
assert(e.nameOffset == -1);
return e.getter.nameOffset;
return e.getter!.nameOffset;
}
return e.nameOffset;
}
Expand Down
8 changes: 4 additions & 4 deletions json_serializable/lib/src/generator_helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class GeneratorHelper extends HelperCore with EncodeHelper, DecodeHelper {
Iterable<String> generate() sync* {
assert(_addedMembers.isEmpty);

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

var accessibleFieldSet = accessibleFields.values.toSet();
if (config.createFactory) {
if (config.createFactory!) {
final createResult = createFactory(accessibleFields, unavailableReasons);
yield createResult.output;

Expand All @@ -108,7 +108,7 @@ class GeneratorHelper extends HelperCore with EncodeHelper, DecodeHelper {
},
);

if (config.createToJson) {
if (config.createToJson!) {
yield* createToJson(accessibleFieldSet);
}

Expand Down
Loading