Skip to content

Clarify nullable #66

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

Merged
merged 2 commits into from
Nov 3, 2017
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
6 changes: 6 additions & 0 deletions json_serializable/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 0.2.6

* Make `null` field handling smarter. If a field is classified as not
`nullable`, then use this knowledge when generating serialization code – even
if `includeIfNull` is `false`.

## 0.2.5

* Throw an exception if a duplicate JSON key is detected.
Expand Down
22 changes: 17 additions & 5 deletions json_serializable/lib/src/json_serializable_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,11 @@ class JsonSerializableGenerator
}

buffer.writeln(' Map<String, dynamic> toJson() ');
if (fieldsList
.every((e) => _includeIfNull(e, classAnnotation.includeIfNull))) {

var writeNaive =
fieldsList.every((e) => _writeJsonValueNaive(e, classAnnotation));

if (writeNaive) {
// write simple `toJson` method that includes all keys...
_writeToJsonSimple(buffer, fields.values, classAnnotation.nullable);
} else {
Expand Down Expand Up @@ -180,7 +183,7 @@ class JsonSerializableGenerator

var expression = _serializeField(field, classAnnotation.nullable,
accessOverride: safeFieldAccess);
if (_includeIfNull(field, classAnnotation.includeIfNull)) {
if (_writeJsonValueNaive(field, classAnnotation)) {
if (directWrite) {
buffer.writeln('$safeJsonKeyString : $expression,');
} else {
Expand Down Expand Up @@ -395,8 +398,17 @@ String _safeNameAccess(FieldElement field) {
bool _nullable(FieldElement field, bool parentValue) =>
_jsonKeyFor(field).nullable ?? parentValue;

bool _includeIfNull(FieldElement field, bool parentValue) =>
_jsonKeyFor(field).includeIfNull ?? parentValue;
/// Returns `true` if the field can be written to JSON 'naively' – meaning
/// we can avoid checking for `null`.
///
/// `true` if either:
/// `includeIfNull` is `true`
/// or
/// `nullable` is `false`.
bool _writeJsonValueNaive(
FieldElement field, JsonSerializable parentAnnotation) =>
(_jsonKeyFor(field).includeIfNull ?? parentAnnotation.includeIfNull) ||
!_nullable(field, parentAnnotation.nullable);

JsonKey _jsonKeyFor(FieldElement element) {
var key = _jsonKeyExpando[element];
Expand Down
4 changes: 2 additions & 2 deletions json_serializable/test/test_files/json_test_example.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
// 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.

// ignore_for_file: annotate_overrides, hash_and_equals
library json_serializable.test.example;
library json_serializable.test.json_test_example;

import 'dart:collection';

Expand Down
2 changes: 1 addition & 1 deletion json_serializable/test/test_files/json_test_example.g.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

// GENERATED CODE - DO NOT MODIFY BY HAND

part of json_serializable.test.example;
part of json_serializable.test.json_test_example;

// **************************************************************************
// Generator: JsonSerializableGenerator
Expand Down
135 changes: 135 additions & 0 deletions json_serializable/test/test_files/json_test_example.non_nullable.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
// 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.

// GENERATED CODE - DO NOT MODIFY BY HAND

// **************************************************************************
// Generator: _NonNullableGenerator
// **************************************************************************

// ignore_for_file: annotate_overrides, hash_and_equals
library json_serializable.test.json_test_example_non_nullable;

import 'dart:collection';

import 'package:collection/collection.dart';
import 'package:json_annotation/json_annotation.dart';

part 'json_test_example.non_nullable.g.dart';

enum House { gryffindor, hufflepuff, ravenclaw, slytherin }

@JsonSerializable(nullable: false)
class Person extends Object with _$PersonSerializerMixin {
final String firstName, middleName, lastName;
final DateTime dateOfBirth;
@JsonKey(name: '\$house')
final House house;

Person(this.firstName, this.lastName, this.house,
{this.middleName, this.dateOfBirth});

factory Person.fromJson(Map<String, dynamic> json) => _$PersonFromJson(json);

bool operator ==(Object other) =>
other is Person &&
firstName == other.firstName &&
middleName == other.middleName &&
lastName == other.lastName &&
dateOfBirth == other.dateOfBirth &&
house == other.house;
}

enum Category { top, bottom, strange, charmed, up, down }

@JsonSerializable(nullable: false)
class Order extends Object with _$OrderSerializerMixin {
int count;
bool isRushed;

@JsonKey(nullable: false)
final Category category;
final UnmodifiableListView<Item> items;
Platform platform;
Map<String, Platform> altPlatforms;

int get price => items.fold(0, (total, item) => item.price + total);

Order(this.category, [Iterable<Item> items])
: this.items = new UnmodifiableListView<Item>(
new List<Item>.unmodifiable(items ?? const <Item>[]));

factory Order.fromJson(Map<String, dynamic> json) => _$OrderFromJson(json);

bool operator ==(Object other) =>
other is Order &&
count == other.count &&
isRushed == other.isRushed &&
_deepEquals(items, other.items) &&
_deepEquals(altPlatforms, other.altPlatforms);
}

@JsonSerializable(nullable: false)
class Item extends Object with _$ItemSerializerMixin {
final int price;
@JsonKey(includeIfNull: false, name: 'item-number')
int itemNumber;
List<DateTime> saleDates;
List<int> rates;

Item([this.price]);

factory Item.fromJson(Map<String, dynamic> json) => _$ItemFromJson(json);

bool operator ==(Object other) =>
other is Item &&
price == other.price &&
itemNumber == other.itemNumber &&
_deepEquals(saleDates, other.saleDates);
}

bool _deepEquals(a, b) => const DeepCollectionEquality().equals(a, b);

class Platform {
final String description;

static const Platform foo = const Platform._('foo');
static const Platform undefined = const Platform._('undefined');
const Platform._(this.description);

factory Platform.fromJson(String value) {
switch (value) {
case 'foo':
return foo;
case 'undefined':
return undefined;
default:
throw new ArgumentError.value(value, 'value', 'Not a supported value.');
}
}

String toJson() => description;
}

@JsonSerializable(nullable: false)
class Numbers extends Object with _$NumbersSerializerMixin {
List<int> ints;
List<num> nums;
List<double> doubles;

@JsonKey(nullable: false)
List<double> nnDoubles;

Numbers();

factory Numbers.fromJson(Map<String, dynamic> json) =>
_$NumbersFromJson(json);

bool operator ==(Object other) =>
other is Numbers &&
_deepEquals(ints, other.ints) &&
_deepEquals(nums, other.nums) &&
_deepEquals(doubles, other.doubles) &&
_deepEquals(nnDoubles, other.nnDoubles);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
// 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.

// GENERATED CODE - DO NOT MODIFY BY HAND

part of json_serializable.test.json_test_example_non_nullable;

// **************************************************************************
// Generator: JsonSerializableGenerator
// **************************************************************************

Person _$PersonFromJson(Map<String, dynamic> json) => new Person(
json['firstName'] as String,
json['lastName'] as String,
House.values.singleWhere((x) => x.toString() == "House.${json[r'$house']}"),
middleName: json['middleName'] as String,
dateOfBirth: DateTime.parse(json['dateOfBirth'] as String));

abstract class _$PersonSerializerMixin {
String get firstName;
String get middleName;
String get lastName;
DateTime get dateOfBirth;
House get house;
Map<String, dynamic> toJson() => <String, dynamic>{
'firstName': firstName,
'middleName': middleName,
'lastName': lastName,
'dateOfBirth': dateOfBirth.toIso8601String(),
r'$house': house.toString().split('.')[1]
};
}

Order _$OrderFromJson(Map<String, dynamic> json) => new Order(
Category.values
.singleWhere((x) => x.toString() == "Category.${json['category']}"),
(json['items'] as List)
.map((e) => new Item.fromJson(e as Map<String, dynamic>)))
..count = json['count'] as int
..isRushed = json['isRushed'] as bool
..platform = new Platform.fromJson(json['platform'] as String)
..altPlatforms = new Map<String, Platform>.fromIterables(
(json['altPlatforms'] as Map<String, dynamic>).keys,
(json['altPlatforms'] as Map)
.values
.map((e) => new Platform.fromJson(e as String)));

abstract class _$OrderSerializerMixin {
int get count;
bool get isRushed;
Category get category;
UnmodifiableListView<Item> get items;
Platform get platform;
Map<String, Platform> get altPlatforms;
Map<String, dynamic> toJson() => <String, dynamic>{
'count': count,
'isRushed': isRushed,
'category': category.toString().split('.')[1],
'items': items,
'platform': platform,
'altPlatforms': altPlatforms
};
}

Item _$ItemFromJson(Map<String, dynamic> json) => new Item(json['price'] as int)
..itemNumber = json['item-number'] as int
..saleDates = (json['saleDates'] as List)
.map((e) => DateTime.parse(e as String))
.toList()
..rates = (json['rates'] as List).map((e) => e as int).toList();

abstract class _$ItemSerializerMixin {
int get price;
int get itemNumber;
List<DateTime> get saleDates;
List<int> get rates;
Map<String, dynamic> toJson() => <String, dynamic>{
'price': price,
'item-number': itemNumber,
'saleDates': saleDates.map((e) => e.toIso8601String()).toList(),
'rates': rates
};
}

Numbers _$NumbersFromJson(Map<String, dynamic> json) => new Numbers()
..ints = (json['ints'] as List).map((e) => e as int).toList()
..nums = (json['nums'] as List).map((e) => e as num).toList()
..doubles =
(json['doubles'] as List).map((e) => (e as num).toDouble()).toList()
..nnDoubles =
(json['nnDoubles'] as List).map((e) => (e as num).toDouble()).toList();

abstract class _$NumbersSerializerMixin {
List<int> get ints;
List<num> get nums;
List<double> get doubles;
List<double> get nnDoubles;
Map<String, dynamic> toJson() => <String, dynamic>{
'ints': ints,
'nums': nums,
'doubles': doubles,
'nnDoubles': nnDoubles
};
}
62 changes: 25 additions & 37 deletions json_serializable/test/test_files/kitchen_sink.non_nullable.g.dart
Original file line number Diff line number Diff line change
Expand Up @@ -64,48 +64,36 @@ abstract class _$KitchenSinkSerializerMixin {
Map<String, bool> get val;
bool get writeNotNull;
String get string;
Map<String, dynamic> toJson() {
var val = <String, dynamic>{};

void writeNotNull(String key, dynamic value) {
if (value != null) {
val[key] = value;
}
}

writeNotNull('dateTime', dateTime.toIso8601String());
writeNotNull('iterable', iterable.toList());
val['dynamicIterable'] = dynamicIterable.toList();
val['objectIterable'] = objectIterable.toList();
val['intIterable'] = intIterable.toList();
val['datetime-iterable'] =
dateTimeIterable.map((e) => e.toIso8601String()).toList();
val['list'] = list;
val['dynamicList'] = dynamicList;
val['objectList'] = objectList;
val['intList'] = intList;
writeNotNull(
'dateTimeList', dateTimeList.map((e) => e.toIso8601String()).toList());
val['map'] = map;
val['stringStringMap'] = stringStringMap;
val['stringIntMap'] = stringIntMap;
val['stringDateTimeMap'] = new Map<String, dynamic>.fromIterables(
stringDateTimeMap.keys,
stringDateTimeMap.values.map((e) => e.toIso8601String()));
writeNotNull(
'crazyComplex',
crazyComplex
Map<String, dynamic> toJson() => <String, dynamic>{
'dateTime': dateTime.toIso8601String(),
'iterable': iterable.toList(),
'dynamicIterable': dynamicIterable.toList(),
'objectIterable': objectIterable.toList(),
'intIterable': intIterable.toList(),
'datetime-iterable':
dateTimeIterable.map((e) => e.toIso8601String()).toList(),
'list': list,
'dynamicList': dynamicList,
'objectList': objectList,
'intList': intList,
'dateTimeList': dateTimeList.map((e) => e.toIso8601String()).toList(),
'map': map,
'stringStringMap': stringStringMap,
'stringIntMap': stringIntMap,
'stringDateTimeMap': new Map<String, dynamic>.fromIterables(
stringDateTimeMap.keys,
stringDateTimeMap.values.map((e) => e.toIso8601String())),
'crazyComplex': crazyComplex
.map((e) => new Map<String, dynamic>.fromIterables(
e.keys,
e.values.map((e) => new Map<String, dynamic>.fromIterables(
e.keys,
e.values.map((e) => e
.map((e) => e.map((e) => e.toIso8601String()).toList())
.toList())))))
.toList());
writeNotNull('val', this.val);
val['writeNotNull'] = this.writeNotNull;
val[r'$string'] = string;
return val;
}
.toList(),
'val': val,
'writeNotNull': writeNotNull,
r'$string': string
};
}
Loading