Skip to content

Starting on default value work #182

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
May 25, 2018
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
4 changes: 4 additions & 0 deletions json_annotation/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.2.6

* Added `JsonKey.defaultValue`.

## 0.2.5

* Added `CheckedFromJsonException` which is thrown by code generated when
Expand Down
7 changes: 6 additions & 1 deletion json_annotation/lib/src/json_serializable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ class JsonKey {
/// Values returned by [toJson] should "round-trip" through [fromJson].
final Function toJson;

/// The value to use if the source JSON does not contain this key or if the
/// value is `null`.
final Object defaultValue;

/// Creates a new [JsonKey] instance.
///
/// Only required when the default behavior is not desired.
Expand All @@ -126,5 +130,6 @@ class JsonKey {
this.includeIfNull,
this.ignore,
this.fromJson,
this.toJson});
this.toJson,
this.defaultValue});
}
2 changes: 1 addition & 1 deletion json_annotation/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: json_annotation
version: 0.2.5
version: 0.2.6-dev
description: >-
Classes and helper functions that support JSON code generation via the
`json_serializable` package.
Expand Down
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.5.5

* Added support for `JsonKey.defaultValue`.

* Small change to how nullable `Map` values are deserialized.

## 0.5.4+1

* Fixed a bug introduced in `0.5.4` in some cases where enum values are nested
Expand Down
15 changes: 12 additions & 3 deletions json_serializable/lib/src/generator_helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -382,22 +382,31 @@ class ${_wrapperClassName(true)} extends \$JsonMapWrapper {
String _deserializeForField(FieldElement field,
{ParameterElement ctorParam, bool checkedProperty}) {
checkedProperty ??= false;
var jsonKey = _safeNameAccess(field);
var defaultValue = jsonKeyFor(field).defaultValue;
var jsonKeyName = _safeNameAccess(field);

var targetType = ctorParam?.type ?? field.type;

try {
if (_generator.checked) {
// TODO: default value fun here!
var value = _getHelperContext(field).deserialize(targetType, 'v');
if (checkedProperty) {
return value;
}

return '\$checkedConvert(json, $jsonKey, (v) => $value)';
return '\$checkedConvert(json, $jsonKeyName, (v) => $value)';
}
assert(!checkedProperty,
'should only be true if `_generator.checked` is true.');
return _getHelperContext(field).deserialize(targetType, 'json[$jsonKey]');

var value = _getHelperContext(field)
.deserialize(targetType, 'json[$jsonKeyName]');

if (defaultValue != null) {
value = '$value ?? $defaultValue';
}
return value;
} on UnsupportedTypeError catch (e) {
throw _createInvalidGenerationError('fromJson', field, e);
}
Expand Down
45 changes: 43 additions & 2 deletions json_serializable/lib/src/json_key_helpers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import 'package:json_annotation/json_annotation.dart';
import 'package:meta/meta.dart' show alwaysThrows;
import 'package:source_gen/source_gen.dart';

import 'json_literal_generator.dart';

@alwaysThrows
T _throwUnsupported<T>(FieldElement element, String message) =>
throw new InvalidGenerationSourceError(
Expand Down Expand Up @@ -43,11 +45,49 @@ JsonKeyWithConversion _from(FieldElement element) {
var fromJsonName = _getFunctionName(obj, element, true);
var toJsonName = _getFunctionName(obj, element, false);

Object _getLiteral(DartObject dartObject) {
if (dartObject.isNull) {
return null;
}

var reader = new ConstantReader(dartObject);

if (reader.isSymbol) {
_throwUnsupported(element,
'Values of type `Symbol` are not supported for `defaultValue`.');
} else if (reader.isType) {
_throwUnsupported(element,
'Values of type `Type` are not supported for `defaultValue`.');
} else if (!reader.isLiteral) {
_throwUnsupported(
element, 'The provided `defaultValue` is not a literal: $dartObject');
}

var literal = reader.literalValue;
if (literal is num || literal is String || literal is bool) {
return literal;
} else if (literal is List<DartObject>) {
return literal.map(_getLiteral).toList();
} else if (literal is Map<DartObject, DartObject>) {
return literal
.map((k, v) => new MapEntry(_getLiteral(k), _getLiteral(v)));
}
_throwUnsupported(
element, 'The provided value is not supported: $dartObject');
}

var defaultValueLiteral = _getLiteral(obj.getField('defaultValue'));

if (defaultValueLiteral != null) {
defaultValueLiteral = jsonLiteralAsDart(defaultValueLiteral, false);
}

return new JsonKeyWithConversion._(
obj.getField('name').toStringValue(),
obj.getField('nullable').toBoolValue(),
obj.getField('includeIfNull').toBoolValue(),
obj.getField('ignore').toBoolValue(),
defaultValueLiteral,
fromJsonName,
toJsonName);
}
Expand All @@ -69,12 +109,13 @@ class JsonKeyWithConversion extends JsonKey {
super();

JsonKeyWithConversion._(String name, bool nullable, bool includeIfNull,
bool ignore, this.fromJsonData, this.toJsonData)
bool ignore, Object defaultValue, this.fromJsonData, this.toJsonData)
: super(
name: name,
nullable: nullable,
includeIfNull: includeIfNull,
ignore: ignore);
ignore: ignore,
defaultValue: defaultValue);
}

ConvertData _getFunctionName(
Expand Down
9 changes: 4 additions & 5 deletions json_serializable/lib/src/json_literal_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class JsonLiteralGenerator extends GeneratorForAnnotation<JsonLiteral> {

var asConst = annotation.read('asConst').boolValue;

var thing = _jsonLiteralAsDart(content, asConst).toString();
var thing = jsonLiteralAsDart(content, asConst).toString();
var marked = asConst ? 'const' : 'final';

return '$marked _\$${element.name}JsonLiteral = $thing;';
Expand All @@ -43,16 +43,15 @@ class JsonLiteralGenerator extends GeneratorForAnnotation<JsonLiteral> {
///
/// If [asConst] is `true`, the returned [String] is encoded as a `const`
/// literal.
String _jsonLiteralAsDart(dynamic value, bool asConst) {
String jsonLiteralAsDart(dynamic value, bool asConst) {
if (value == null) return 'null';

if (value is String) return escapeDartString(value);

if (value is bool || value is num) return value.toString();

if (value is List) {
var listItems =
value.map((v) => _jsonLiteralAsDart(v, asConst)).join(',\n');
var listItems = value.map((v) => jsonLiteralAsDart(v, asConst)).join(',\n');
return '${asConst ? 'const' : ''}[$listItems]';
}

Expand All @@ -78,7 +77,7 @@ String jsonMapAsDart(Map<String, dynamic> value, bool asConst) {
}
buffer.write(escapeDartString(k));
buffer.write(':');
buffer.write(_jsonLiteralAsDart(v, asConst));
buffer.write(jsonLiteralAsDart(v, asConst));
});

buffer.write('}');
Expand Down
11 changes: 5 additions & 6 deletions json_serializable/lib/src/type_helpers/map_helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import '../constants.dart';
import '../shared_checkers.dart';
import '../type_helper.dart';
import '../type_helper_context.dart';
import '../utils.dart';

class MapHelper extends TypeHelper {
const MapHelper();
Expand Down Expand Up @@ -77,11 +76,11 @@ class MapHelper extends TypeHelper {
}
}

if (valueArgIsAny || simpleJsonTypeChecker.isAssignableFromType(valueArg)) {
// No mapping of the values is required!

var result = 'new Map<String, $valueArg>.from($expression as Map)';
return commonNullPrefix(context.nullable, expression, result);
if (!context.nullable &&
(valueArgIsAny ||
simpleJsonTypeChecker.isAssignableFromType(valueArg))) {
// No mapping of the values or null check required!
return 'new Map<String, $valueArg>.from($expression as Map)';
}

// In this case, we're going to create a new Map with matching reified
Expand Down
8 changes: 6 additions & 2 deletions json_serializable/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: json_serializable
version: 0.5.4+1
version: 0.5.5-dev
author: Dart Team <misc@dartlang.org>
description: Generates utilities to aid in serializing to/from JSON.
homepage: https://github.com/dart-lang/json_serializable
Expand All @@ -12,7 +12,7 @@ dependencies:

# Use a tight version constraint to ensure that a constraint on
# `json_annotation`. Properly constrains all features it provides.
json_annotation: '>=0.2.5 <0.2.6'
json_annotation: '>=0.2.6 <0.2.7'
meta: ^1.1.0
path: ^1.3.2
source_gen: '>=0.8.1 <0.9.0'
Expand All @@ -24,3 +24,7 @@ dev_dependencies:
logging: ^0.11.3+1
test: ^0.12.3
yaml: ^2.1.13

dependency_overrides:
json_annotation:
path: ../json_annotation
48 changes: 48 additions & 0 deletions json_serializable/test/default_value/default_value.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (c) 2018, 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

import 'package:json_annotation/json_annotation.dart';

part 'default_value.g.dart';

const _intValue = 42;

@JsonSerializable()
class DefaultValue extends Object with _$DefaultValueSerializerMixin {
@JsonKey(defaultValue: true)
bool fieldBool;

@JsonKey(defaultValue: 'string')
String fieldString;

@JsonKey(defaultValue: _intValue)
int fieldInt;

@JsonKey(defaultValue: 3.14)
double fieldDouble;

@JsonKey(defaultValue: [])
List fieldListEmpty;

@JsonKey(defaultValue: {})
Map fieldMapEmpty;

@JsonKey(defaultValue: [1, 2, 3])
List<int> fieldListSimple;

@JsonKey(defaultValue: {'answer': 42})
Map<String, int> fieldMapSimple;

@JsonKey(defaultValue: {
'root': ['child']
})
Map<String, List<String>> fieldMapListString;

DefaultValue();

factory DefaultValue.fromJson(Map<String, dynamic> json) =>
_$DefaultValueFromJson(json);
}
56 changes: 56 additions & 0 deletions json_serializable/test/default_value/default_value.g.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright (c) 2018, 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 'default_value.dart';

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

DefaultValue _$DefaultValueFromJson(Map<String, dynamic> json) =>
new DefaultValue()
..fieldBool = json['fieldBool'] as bool ?? true
..fieldString = json['fieldString'] as String ?? 'string'
..fieldInt = json['fieldInt'] as int ?? 42
..fieldDouble = (json['fieldDouble'] as num)?.toDouble() ?? 3.14
..fieldListEmpty = json['fieldListEmpty'] as List ?? []
..fieldMapEmpty = json['fieldMapEmpty'] as Map<String, dynamic> ?? {}
..fieldListSimple =
(json['fieldListSimple'] as List)?.map((e) => e as int)?.toList() ??
[1, 2, 3]
..fieldMapSimple = (json['fieldMapSimple'] as Map<String, dynamic>)
?.map((k, e) => new MapEntry(k, e as int)) ??
{'answer': 42}
..fieldMapListString =
(json['fieldMapListString'] as Map<String, dynamic>)?.map((k, e) =>
new MapEntry(
k, (e as List)?.map((e) => e as String)?.toList())) ??
{
'root': ['child']
};

abstract class _$DefaultValueSerializerMixin {
bool get fieldBool;
String get fieldString;
int get fieldInt;
double get fieldDouble;
List<dynamic> get fieldListEmpty;
Map<dynamic, dynamic> get fieldMapEmpty;
List<int> get fieldListSimple;
Map<String, int> get fieldMapSimple;
Map<String, List<String>> get fieldMapListString;
Map<String, dynamic> toJson() => <String, dynamic>{
'fieldBool': fieldBool,
'fieldString': fieldString,
'fieldInt': fieldInt,
'fieldDouble': fieldDouble,
'fieldListEmpty': fieldListEmpty,
'fieldMapEmpty': fieldMapEmpty,
'fieldListSimple': fieldListSimple,
'fieldMapSimple': fieldMapSimple,
'fieldMapListString': fieldMapListString
};
}
Loading