Skip to content

Options to enable formatting #179

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 3 commits into from
May 24, 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_serializable/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.5.5

* Added named `formatOutput` parameter to `jsonPartBuilder`.

## 0.5.4

* Add `checked` configuration option. If `true`, generated `fromJson` functions
Expand Down
117 changes: 63 additions & 54 deletions json_serializable/lib/src/generator_helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,10 @@ class _GeneratorHelper {
});

if (_annotation.createFactory) {
_buffer.writeln();
var mapType = _generator.anyMap ? 'Map' : 'Map<String, dynamic>';
_buffer.writeln('$_targetClassReference '
'${_prefix}FromJson${_genericClassArguments(true)}($mapType json) =>');
_buffer.write('$_targetClassReference '
'${_prefix}FromJson${_genericClassArguments(true)}'
'($mapType json) =>');

String deserializeFun(String paramOrFieldName,
{ParameterElement ctorParam}) =>
Expand All @@ -152,7 +152,10 @@ class _GeneratorHelper {
if (_generator.checked) {
var classLiteral = escapeDartString(_element.name);

_buffer.writeln('\$checkedNew($classLiteral, json, ()');
_buffer.write(''' \$checkedNew(
$classLiteral,
json,
()''');

var data = writeConstructorInvocation(
_element,
Expand All @@ -174,14 +177,16 @@ class _GeneratorHelper {
// If there are fields to set, create a full function body and
// create a temporary variable to hold the instance so we can make
// wrapped calls to all of the fields value assignments.
_buffer.writeln('{ var val = ');
_buffer.write(''' {
var val = ''');
_buffer.write(data.content);
_buffer.writeln(';');

for (var field in data.fieldsToSet) {
_buffer.writeln();
_buffer.write('\$checkedConvert(json, ${_safeNameAccess(
accessibleFields[field])}, (v) => ');
var safeName = _safeNameAccess(accessibleFields[field]);
_buffer.write('''
\$checkedConvert(json, $safeName, (v) => ''');
_buffer.write('val.$field = ');
_buffer.write(_deserializeForField(accessibleFields[field],
checkedProperty: true));
Expand All @@ -201,7 +206,7 @@ class _GeneratorHelper {
fieldKeyMapArg = ', fieldKeyMap: $mapLiteral';
}

_buffer.writeln('$fieldKeyMapArg)');
_buffer.write('$fieldKeyMapArg)');
} else {
var data = writeConstructorInvocation(
_element,
Expand All @@ -215,15 +220,15 @@ class _GeneratorHelper {

fieldsSetByFactory = data.usedCtorParamsAndFields;

_buffer.writeln(data.content);
_buffer.write(' ${data.content}');
for (var field in data.fieldsToSet) {
_buffer.writeln();
_buffer.write(' ..$field = ');
_buffer.write(' ..$field = ');
_buffer.write(deserializeFun(field));
}
_buffer.writeln();
}
_buffer.writeln(';');
_buffer.writeln();

// If there are fields that are final – that are not set via the generated
// constructor, then don't output them when generating the `toJson` call.
Expand All @@ -236,11 +241,11 @@ class _GeneratorHelper {
void _writeWrapper(Iterable<FieldElement> fields) {
_buffer.writeln();
// TODO(kevmoo): write JsonMapWrapper if annotation lib is prefix-imported
_buffer
.writeln('''class ${_wrapperClassName(true)} extends \$JsonMapWrapper {
final ${_mixinClassName(false)} _v;
${_wrapperClassName()}(this._v);
''');
_buffer.writeln('''
class ${_wrapperClassName(true)} extends \$JsonMapWrapper {
final ${_mixinClassName(false)} _v;
${_wrapperClassName()}(this._v);
''');

if (fields.every(_writeJsonValueNaive)) {
// TODO(kevmoo): consider just doing one code path – if it's fast
Expand All @@ -249,52 +254,54 @@ class _GeneratorHelper {

// TODO(kevmoo): maybe put this in a static field instead?
// const lists have unfortunate overhead
_buffer.writeln(''' @override
Iterable<String> get keys => const [$jsonKeys];
''');
_buffer.writeln('''
@override
Iterable<String> get keys => const [$jsonKeys];
''');
} else {
// At least one field should be excluded if null
_buffer.writeln('@override\nIterable<String> get keys sync* {');
_buffer.writeln(' @override\n Iterable<String> get keys sync* {');

for (var field in fields) {
var nullCheck = !_writeJsonValueNaive(field);
if (nullCheck) {
_buffer.writeln('if (_v.${field.name} != null) {');
_buffer.write(' if (_v.${field.name} != null) {\n ');
}
_buffer.writeln('yield ${_safeNameAccess(field)};');
_buffer.writeln(' yield ${_safeNameAccess(field)};');
if (nullCheck) {
_buffer.writeln('}');
_buffer.writeln(' }');
}
}

_buffer.writeln('}\n');
_buffer.writeln(' }\n');
}

_buffer.writeln('''@override
dynamic operator [](Object key) {
_buffer.writeln('''
@override
dynamic operator [](Object key) {
if (key is String) {
switch(key) {
''');
switch (key) {''');

for (var field in fields) {
var valueAccess = '_v.${field.name}';
_buffer.write('''case ${_safeNameAccess(field)}:
return ${_serializeField(
field, accessOverride: valueAccess)};''');
_buffer.writeln('''
case ${_safeNameAccess(field)}:
return ${_serializeField(field, accessOverride: valueAccess)};''');
}

_buffer.writeln('''
}}
return null;
}''');
}
}
return null;
}''');

_buffer.writeln('}');
}

void _writeToJsonWithNullChecks(Iterable<FieldElement> fields) {
_buffer.writeln('{');

_buffer.writeln('var $generatedLocalVarName = <String, dynamic>{');
_buffer.writeln(' var $generatedLocalVarName = <String, dynamic>{');

// Note that the map literal is left open above. As long as target fields
// don't need to be intercepted by the `only if null` logic, write them
Expand All @@ -316,48 +323,50 @@ class _GeneratorHelper {
var expression = _serializeField(field, accessOverride: safeFieldAccess);
if (_writeJsonValueNaive(field)) {
if (directWrite) {
_buffer.writeln('$safeJsonKeyString : $expression,');
_buffer.writeln(' $safeJsonKeyString: $expression,');
} else {
_buffer.writeln(
'$generatedLocalVarName[$safeJsonKeyString] = $expression;');
' $generatedLocalVarName[$safeJsonKeyString] = $expression;');
}
} else {
if (directWrite) {
// close the still-open map literal
_buffer.writeln('};');
_buffer.writeln(' };');
_buffer.writeln();

// write the helper to be used by all following null-excluding
// fields
_buffer.writeln('''
void $toJsonMapHelperName(String key, dynamic value) {
if (value != null) {
$generatedLocalVarName[key] = value;
}
}''');
void $toJsonMapHelperName(String key, dynamic value) {
if (value != null) {
$generatedLocalVarName[key] = value;
}
}
''');
directWrite = false;
}
_buffer
.writeln('$toJsonMapHelperName($safeJsonKeyString, $expression);');
_buffer.writeln(
' $toJsonMapHelperName($safeJsonKeyString, $expression);');
}
}

_buffer.writeln('return $generatedLocalVarName;');

_buffer.writeln('}');
_buffer.writeln(' return $generatedLocalVarName;');
_buffer.writeln(' }');
}

void _writeToJsonSimple(Iterable<FieldElement> fields) {
_buffer.writeln('=> <String, dynamic>{');

var pairs = <String>[];
for (var field in fields) {
pairs.add('${_safeNameAccess(field)}: ${_serializeField(
field)}');
_buffer.writeAll(fields.map((field) {
var value = '${_safeNameAccess(field)}: ${_serializeField(field)}';
return ' $value';
}), ',\n');

if (fields.isNotEmpty) {
_buffer.write('\n ');
}
_buffer.writeAll(pairs, ',\n');

_buffer.writeln(' };');
_buffer.writeln('};');
}

String _serializeField(FieldElement field, {String accessOverride}) {
Expand Down
6 changes: 5 additions & 1 deletion json_serializable/lib/src/json_part_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,20 @@ import 'json_serializable_generator.dart';
/// If `null`, the content of [defaultFileHeader] is used.
/// If [header] is an empty `String` no header is added.
///
/// [formatOutput] is called to format the generated code. If not provided,
/// the default Dart code formatter is used.
///
/// For details on [useWrappers], [anyMap], and [checked] see
/// [JsonSerializableGenerator].
Builder jsonPartBuilder(
{String header,
String formatOutput(String code),
bool useWrappers: false,
bool anyMap: false,
bool checked: false}) {
return new PartBuilder([
new JsonSerializableGenerator(
useWrappers: useWrappers, anyMap: anyMap, checked: checked),
const JsonLiteralGenerator()
], header: header);
], header: header, formatOutput: formatOutput);
}
30 changes: 19 additions & 11 deletions json_serializable/lib/src/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ String genericClassArguments(ClassElement element, bool withConstraints) {
}
var values = element.typeParameters
.map((t) => withConstraints ? t.toString() : t.name)
.join(',');
.join(', ');
return '<$values>';
}

Expand Down Expand Up @@ -275,17 +275,25 @@ CtorData writeConstructorInvocation(

var buffer = new StringBuffer();
buffer.write('new $className${genericClassArguments(classElement, false)}(');
buffer.writeAll(
constructorArguments.map((paramElement) =>
deserializeForField(paramElement.name, ctorParam: paramElement)),
', ');
if (constructorArguments.isNotEmpty && namedConstructorArguments.isNotEmpty) {
buffer.write(', ');
if (constructorArguments.isNotEmpty) {
buffer.writeln();
buffer.writeAll(constructorArguments.map((paramElement) {
var content =
deserializeForField(paramElement.name, ctorParam: paramElement);
return ' $content';
}), ',\n');
if (namedConstructorArguments.isNotEmpty) {
buffer.write(',');
}
}
if (namedConstructorArguments.isNotEmpty) {
buffer.writeln();
buffer.writeAll(namedConstructorArguments.map((paramElement) {
var value =
deserializeForField(paramElement.name, ctorParam: paramElement);
return ' ${paramElement.name}: $value';
}), ',\n');
}
buffer.writeAll(namedConstructorArguments.map((paramElement) {
var value = deserializeForField(paramElement.name, ctorParam: paramElement);
return '${paramElement.name}: $value';
}), ', ');

buffer.write(')');

Expand Down
2 changes: 1 addition & 1 deletion 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
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 Down
35 changes: 27 additions & 8 deletions json_serializable/tool/build.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,34 @@

import 'dart:io' show exitCode;

import 'package:build/build.dart';
import 'package:build_config/build_config.dart';
import 'package:build_runner/build_runner.dart';
import 'package:build_test/builder.dart';
import 'package:json_serializable/src/json_part_builder.dart';
import 'package:json_serializable/src/json_part_builder.dart' as jpb;

import 'builder.dart';

Builder _jsonPartBuilder(
{bool useWrappers: false,
bool anyMap: false,
bool checked: false,
bool format}) {
format ??= true;
String Function(String code) formatOutput;

if (!format) {
formatOutput = (s) => s;
}

return jpb.jsonPartBuilder(
header: copyrightHeader,
formatOutput: formatOutput,
useWrappers: useWrappers,
anyMap: anyMap,
checked: checked);
}

final List<BuilderApplication> builders = [
applyToRoot(nonNull(),
generateFor: const InputSet(include: const [
Expand All @@ -30,7 +51,7 @@ final List<BuilderApplication> builders = [
'test/test_files/json_test_example.dart',
'test/test_files/json_test_example.non_nullable.dart',
])),
applyToRoot(jsonPartBuilder(header: copyrightHeader),
applyToRoot(_jsonPartBuilder(),
generateFor: const InputSet(
include: const [
'example/example.dart',
Expand All @@ -40,31 +61,29 @@ final List<BuilderApplication> builders = [
'test/test_files/json_test_example.non_nullable.dart'
],
)),
applyToRoot(jsonPartBuilder(header: copyrightHeader, anyMap: true),
applyToRoot(_jsonPartBuilder(anyMap: true),
generateFor: const InputSet(
include: const [
'test/kitchen_sink_test_files/kitchen_sink.dart',
'test/kitchen_sink_test_files/kitchen_sink.non_nullable.dart',
'test/kitchen_sink_test_files/simple_object.dart'
],
)),
applyToRoot(
jsonPartBuilder(header: copyrightHeader, checked: true, anyMap: true),
applyToRoot(_jsonPartBuilder(checked: true, anyMap: true),
generateFor: const InputSet(
include: const [
'test/yaml_test_files/build_config.dart',
'test/kitchen_sink_test_files/kitchen_sink.non_nullable.checked.dart'
],
)),
applyToRoot(jsonPartBuilder(useWrappers: true, header: copyrightHeader),
applyToRoot(_jsonPartBuilder(useWrappers: true),
generateFor: const InputSet(
include: const [
'test/generic_files/generic_class*wrapped.dart',
'test/test_files/json_test_example*wrapped.dart',
],
)),
applyToRoot(
jsonPartBuilder(useWrappers: true, anyMap: true, header: copyrightHeader),
applyToRoot(_jsonPartBuilder(useWrappers: true, anyMap: true),
generateFor: const InputSet(
include: const [
'test/kitchen_sink_test_files/kitchen_sink*wrapped.dart',
Expand Down