Skip to content

Commit f37664a

Browse files
authored
Add JsonSerializable.createPerFieldToJson (#1230)
1 parent def4b1e commit f37664a

22 files changed

+376
-33
lines changed

example/pubspec.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,7 @@ dev_dependencies:
2424
test: ^1.16.0
2525

2626
dependency_overrides:
27+
json_annotation:
28+
path: ../json_annotation
2729
json_serializable:
2830
path: ../json_serializable

json_annotation/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## 4.8.0-dev
2+
3+
- Require Dart SDK `>=2.18.0`.
4+
- Added `JsonSerializable.createPerFieldToJson` which allows generating
5+
a `_$ModelPerFieldToJson`, enabling partial encoding of a model.
6+
17
## 4.7.1-dev
28

39
- Update `JsonKey` documentation to align with new features in

json_annotation/lib/src/json_serializable.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,13 @@ class JsonSerializable {
8787
/// such as [fieldRename].
8888
final bool? createFieldMap;
8989

90+
/// If `true` (defaults to false), a private, static `_$ExamplePerFieldToJson`
91+
/// abstract class will be geenrated in the part file.
92+
///
93+
/// This abstract class will contain one static function per property,
94+
/// exposing a way to encode only this property instead of the entire object.
95+
final bool? createPerFieldToJson;
96+
9097
/// If `true` (the default), A top-level function is created that you can
9198
/// reference from your class.
9299
///
@@ -249,6 +256,7 @@ class JsonSerializable {
249256
this.includeIfNull,
250257
this.converters,
251258
this.genericArgumentFactories,
259+
this.createPerFieldToJson,
252260
});
253261

254262
factory JsonSerializable.fromJson(Map<String, dynamic> json) =>

json_annotation/lib/src/json_serializable.g.dart

Lines changed: 6 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

json_annotation/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: json_annotation
2-
version: 4.7.1-dev
2+
version: 4.8.0-dev
33
description: >-
44
Classes and helper functions that support JSON code generation via the
55
`json_serializable` package.

json_serializable/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
- Require Dart SDK `>=2.18.0`.
44
- Require `analyzer: ^5.2.0`
5+
- Require `json_annotation: '>=4.8.0 <4.9.0'`
56

67
## 6.5.4
78

json_serializable/README.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ targets:
279279
constructor: ""
280280
create_factory: true
281281
create_field_map: false
282+
create_per_field_to_json: false
282283
create_to_json: true
283284
disallow_unrecognized_keys: false
284285
explicit_to_json: false
@@ -299,15 +300,15 @@ targets:
299300
[`Enum`]: https://api.dart.dev/stable/dart-core/Enum-class.html
300301
[`int`]: https://api.dart.dev/stable/dart-core/int-class.html
301302
[`Iterable`]: https://api.dart.dev/stable/dart-core/Iterable-class.html
302-
[`JsonConverter`]: https://pub.dev/documentation/json_annotation/4.7.0/json_annotation/JsonConverter-class.html
303-
[`JsonEnum.valueField`]: https://pub.dev/documentation/json_annotation/4.7.0/json_annotation/JsonEnum/valueField.html
304-
[`JsonEnum`]: https://pub.dev/documentation/json_annotation/4.7.0/json_annotation/JsonEnum-class.html
305-
[`JsonKey.fromJson`]: https://pub.dev/documentation/json_annotation/4.7.0/json_annotation/JsonKey/fromJson.html
306-
[`JsonKey.toJson`]: https://pub.dev/documentation/json_annotation/4.7.0/json_annotation/JsonKey/toJson.html
307-
[`JsonKey`]: https://pub.dev/documentation/json_annotation/4.7.0/json_annotation/JsonKey-class.html
308-
[`JsonLiteral`]: https://pub.dev/documentation/json_annotation/4.7.0/json_annotation/JsonLiteral-class.html
309-
[`JsonSerializable`]: https://pub.dev/documentation/json_annotation/4.7.0/json_annotation/JsonSerializable-class.html
310-
[`JsonValue`]: https://pub.dev/documentation/json_annotation/4.7.0/json_annotation/JsonValue-class.html
303+
[`JsonConverter`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonConverter-class.html
304+
[`JsonEnum.valueField`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonEnum/valueField.html
305+
[`JsonEnum`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonEnum-class.html
306+
[`JsonKey.fromJson`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonKey/fromJson.html
307+
[`JsonKey.toJson`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonKey/toJson.html
308+
[`JsonKey`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonKey-class.html
309+
[`JsonLiteral`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonLiteral-class.html
310+
[`JsonSerializable`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonSerializable-class.html
311+
[`JsonValue`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonValue-class.html
311312
[`List`]: https://api.dart.dev/stable/dart-core/List-class.html
312313
[`Map`]: https://api.dart.dev/stable/dart-core/Map-class.html
313314
[`num`]: https://api.dart.dev/stable/dart-core/num-class.html

json_serializable/lib/src/check_dependencies.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import 'package:pubspec_parse/pubspec_parse.dart';
1010

1111
const _productionDirectories = {'lib', 'bin'};
1212
const _annotationPkgName = 'json_annotation';
13-
final requiredJsonAnnotationMinVersion = Version.parse('4.7.0');
13+
final requiredJsonAnnotationMinVersion = Version.parse('4.8.0-dev');
1414

1515
Future<void> pubspecHasRightVersion(BuildStep buildStep) async {
1616
final segments = buildStep.inputId.pathSegments;

json_serializable/lib/src/decode_helper.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ abstract class DecodeHelper implements HelperCore {
3131

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

3737
if (config.genericArgumentFactories) {

json_serializable/lib/src/encoder_helper.dart

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,32 @@ import 'unsupported_type_error.dart';
1616
abstract class EncodeHelper implements HelperCore {
1717
String _fieldAccess(FieldElement field) => '$_toJsonParamName.${field.name}';
1818

19+
String createPerFieldToJson(Set<FieldElement> accessibleFieldSet) {
20+
final buffer = StringBuffer()
21+
..writeln('// ignore: unused_element')
22+
..writeln('abstract class _\$${element.name.nonPrivate}PerFieldToJson {');
23+
24+
for (final field in accessibleFieldSet) {
25+
buffer
26+
..writeln(' // ignore: unused_element')
27+
..write(
28+
'static Object? ${field.name}'
29+
'${genericClassArgumentsImpl(withConstraints: true)}'
30+
'(${field.type} $_toJsonParamName',
31+
);
32+
33+
if (config.genericArgumentFactories) {
34+
_writeGenericArgumentFactories(buffer);
35+
}
36+
37+
buffer.writeln(') => ${_serializeField(field, _toJsonParamName)};');
38+
}
39+
40+
buffer.writeln('}');
41+
42+
return buffer.toString();
43+
}
44+
1945
/// Generates an object containing metadatas related to the encoding,
2046
/// destined to be used by other code-generators.
2147
String createFieldMap(Set<FieldElement> accessibleFieldSet) {
@@ -42,26 +68,19 @@ abstract class EncodeHelper implements HelperCore {
4268

4369
final buffer = StringBuffer();
4470

45-
final functionName = '${prefix}ToJson${genericClassArgumentsImpl(true)}';
71+
final functionName =
72+
'${prefix}ToJson${genericClassArgumentsImpl(withConstraints: true)}';
4673
buffer.write('Map<String, dynamic> '
4774
'$functionName($targetClassReference $_toJsonParamName');
4875

49-
if (config.genericArgumentFactories) {
50-
for (var arg in element.typeParameters) {
51-
final helperName = toJsonForType(
52-
arg.instantiate(nullabilitySuffix: NullabilitySuffix.none),
53-
);
54-
buffer.write(',Object? Function(${arg.name} value) $helperName');
55-
}
56-
if (element.typeParameters.isNotEmpty) {
57-
buffer.write(',');
58-
}
59-
}
76+
if (config.genericArgumentFactories) _writeGenericArgumentFactories(buffer);
77+
6078
buffer.write(') ');
6179

62-
final writeNaive = accessibleFields.every(_writeJsonValueNaive);
80+
final canWriteAllJsonValuesWithoutNullCheck =
81+
accessibleFields.every(_canWriteJsonWithoutNullCheck);
6382

64-
if (writeNaive) {
83+
if (canWriteAllJsonValuesWithoutNullCheck) {
6584
// write simple `toJson` method that includes all keys...
6685
_writeToJsonSimple(buffer, accessibleFields);
6786
} else {
@@ -72,6 +91,18 @@ abstract class EncodeHelper implements HelperCore {
7291
yield buffer.toString();
7392
}
7493

94+
void _writeGenericArgumentFactories(StringBuffer buffer) {
95+
for (var arg in element.typeParameters) {
96+
final helperName = toJsonForType(
97+
arg.instantiate(nullabilitySuffix: NullabilitySuffix.none),
98+
);
99+
buffer.write(',Object? Function(${arg.name} value) $helperName');
100+
}
101+
if (element.typeParameters.isNotEmpty) {
102+
buffer.write(',');
103+
}
104+
}
105+
75106
void _writeToJsonSimple(StringBuffer buffer, Iterable<FieldElement> fields) {
76107
buffer
77108
..writeln('=> <String, dynamic>{')
@@ -112,7 +143,7 @@ abstract class EncodeHelper implements HelperCore {
112143
}
113144

114145
final expression = _serializeField(field, safeFieldAccess);
115-
if (_writeJsonValueNaive(field)) {
146+
if (_canWriteJsonWithoutNullCheck(field)) {
116147
if (directWrite) {
117148
buffer.writeln(' $safeJsonKeyString: $expression,');
118149
} else {
@@ -160,7 +191,7 @@ abstract class EncodeHelper implements HelperCore {
160191

161192
/// Returns `true` if the field can be written to JSON 'naively' – meaning
162193
/// we can avoid checking for `null`.
163-
bool _writeJsonValueNaive(FieldElement field) {
194+
bool _canWriteJsonWithoutNullCheck(FieldElement field) {
164195
final jsonKey = jsonKeyFor(field);
165196

166197
if (jsonKey.includeIfNull) {

0 commit comments

Comments
 (0)