Skip to content

Commit 89d75b9

Browse files
authored
Add more documentation about JsonEnum (#1209)
Also added some features to the readme builder, which will be useful later
1 parent c844625 commit 89d75b9

File tree

6 files changed

+145
-27
lines changed

6 files changed

+145
-27
lines changed

json_serializable/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 6.4.1-dev
2+
3+
- Added more documentation `@JsonEnum`.
4+
15
## 6.4.0
26

37
- Add support for `JsonEnum.valueField` which allows specifying a field in an

json_serializable/README.md

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,14 +115,34 @@ Annotate `enum` types with [`JsonEnum`] (new in `json_annotation` 4.2.0) to:
115115
Annotate `enum` values with [`JsonValue`] to specify the encoded value to map
116116
to target `enum` entries. Values can be of type [`String`] or [`int`].
117117

118-
<!-- TODO: hoist out to source code! -->
119-
120118
```dart
121119
enum StatusCode {
122120
@JsonValue(200)
123121
success,
124-
@JsonValue('500')
125-
weird,
122+
@JsonValue(301)
123+
movedPermanently,
124+
@JsonValue(302)
125+
found,
126+
@JsonValue(500)
127+
internalServerError,
128+
}
129+
```
130+
131+
If you are annotating an
132+
[enhanced enum](https://dart.dev/guides/language/language-tour#declaring-enhanced-enums),
133+
you can use [`JsonEnum.valueField`] to specify the field to use for the
134+
serialized value.
135+
136+
```dart
137+
@JsonEnum(valueField: 'code')
138+
enum StatusCodeEnhanced {
139+
success(200),
140+
movedPermanently(301),
141+
found(302),
142+
internalServerError(500);
143+
144+
const StatusCodeEnhanced(this.code);
145+
final int code;
126146
}
127147
```
128148

@@ -201,6 +221,7 @@ targets:
201221
[`int`]: https://api.dart.dev/stable/dart-core/int-class.html
202222
[`Iterable`]: https://api.dart.dev/stable/dart-core/Iterable-class.html
203223
[`JsonConverter`]: https://pub.dev/documentation/json_annotation/4.7.0/json_annotation/JsonConverter-class.html
224+
[`JsonEnum.valueField`]: https://pub.dev/documentation/json_annotation/4.7.0/json_annotation/JsonEnum/valueField.html
204225
[`JsonEnum`]: https://pub.dev/documentation/json_annotation/4.7.0/json_annotation/JsonEnum-class.html
205226
[`JsonKey.fromJson`]: https://pub.dev/documentation/json_annotation/4.7.0/json_annotation/JsonKey/fromJson.html
206227
[`JsonKey.toJson`]: https://pub.dev/documentation/json_annotation/4.7.0/json_annotation/JsonKey/toJson.html

json_serializable/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: json_serializable
2-
version: 6.4.0
2+
version: 6.4.1-dev
33
description: >-
44
Automatically generate code for converting to and from JSON by annotating
55
Dart classes.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import 'package:json_annotation/json_annotation.dart';
2+
3+
// # simple_example
4+
enum StatusCode {
5+
@JsonValue(200)
6+
success,
7+
@JsonValue(301)
8+
movedPermanently,
9+
@JsonValue(302)
10+
found,
11+
@JsonValue(500)
12+
internalServerError,
13+
}
14+
15+
// # enhanced_example
16+
@JsonEnum(valueField: 'code')
17+
enum StatusCodeEnhanced {
18+
success(200),
19+
movedPermanently(301),
20+
found(302),
21+
internalServerError(500);
22+
23+
const StatusCodeEnhanced(this.code);
24+
final int code;
25+
}

json_serializable/tool/readme/readme_template.md

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ To configure your project for the latest released version of
2424
Given a library `example.dart` with an `Person` class annotated with
2525
`ja:JsonSerializable`:
2626

27-
<!-- REPLACE example.dart -->
27+
<!-- REPLACE example/example.dart -->
2828

2929
Building creates the corresponding part `example.g.dart`:
3030

31-
<!-- REPLACE example.g.dart -->
31+
<!-- REPLACE example/example.g.dart -->
3232

3333
# Running the code generator
3434

@@ -75,16 +75,14 @@ Annotate `enum` types with `ja:JsonEnum` (new in `json_annotation` 4.2.0) to:
7575
Annotate `enum` values with `ja:JsonValue` to specify the encoded value to map
7676
to target `enum` entries. Values can be of type `core:String` or `core:int`.
7777

78-
<!-- TODO: hoist out to source code! -->
78+
<!-- REPLACE tool/readme/enum_example.dart-simple_example -->
7979

80-
```dart
81-
enum StatusCode {
82-
@JsonValue(200)
83-
success,
84-
@JsonValue('500')
85-
weird,
86-
}
87-
```
80+
If you are annotating an
81+
[enhanced enum](https://dart.dev/guides/language/language-tour#declaring-enhanced-enums),
82+
you can use `ja:JsonEnum.valueField` to specify the field to use for the
83+
serialized value.
84+
85+
<!-- REPLACE tool/readme/enum_example.dart-enhanced_example -->
8886

8987
# Supported types
9088

json_serializable/tool/readme_builder.dart

Lines changed: 81 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import 'dart:convert';
77

88
import 'package:build/build.dart';
99
import 'package:collection/collection.dart';
10-
import 'package:path/path.dart' as p;
1110
import 'package:pub_semver/pub_semver.dart';
1211
import 'package:yaml/yaml.dart';
1312

@@ -23,8 +22,9 @@ class _ReadmeBuilder extends Builder {
2322
buildStep.assetIdForInputPackage('tool/readme/readme_template.md');
2423

2524
final replacements = {
26-
'example.dart': await buildStep.getExampleContent('example.dart'),
27-
'example.g.dart': await buildStep.getExampleContent('example.g.dart'),
25+
...await buildStep.getExampleContent('example/example.dart'),
26+
...await buildStep.getExampleContent('example/example.g.dart'),
27+
...await buildStep.getExampleContent('tool/readme/enum_example.dart'),
2828
'supported_types': _classCleanAndSort(supportedTypes()),
2929
'collection_types': _classCleanAndSort(collectionTypes()),
3030
'map_key_types': _classCleanAndSort(mapKeyTypes),
@@ -44,7 +44,7 @@ class _ReadmeBuilder extends Builder {
4444
final foundClasses = SplayTreeMap<String, String>(compareAsciiLowerCase);
4545

4646
final theMap = <Pattern, String Function(Match)>{
47-
RegExp(r'<!-- REPLACE ([\w\d\.]+) -->'): (match) {
47+
RegExp(r'<!-- REPLACE ([\/\w\d\._-]+) -->'): (match) {
4848
final replacementKey = match.group(1)!;
4949
availableKeys.remove(replacementKey);
5050
return (replacements[replacementKey] ?? '*MISSING! `$replacementKey`*')
@@ -174,9 +174,9 @@ extension on BuildStep {
174174
return targetVersion;
175175
}
176176

177-
Future<String> getExampleContent(String fileName) async {
177+
Future<Map<String, String>> getExampleContent(String fileName) async {
178178
final content = await readAsString(
179-
assetIdForInputPackage(p.join('example', fileName)),
179+
assetIdForInputPackage(fileName),
180180
);
181181

182182
final lines = LineSplitter.split(content);
@@ -185,7 +185,12 @@ extension on BuildStep {
185185

186186
// All lines with content, except those starting with `/`.
187187
// Also exclude blank lines that follow other blank lines
188-
final cleanedSource = lines.where((l) {
188+
final cleanedSourceLines = lines.where((l) {
189+
if (l.isBlockLine) {
190+
// This is a block!
191+
return true;
192+
}
193+
189194
if (l.startsWith(r'/')) {
190195
return false;
191196
}
@@ -201,11 +206,76 @@ extension on BuildStep {
201206
}
202207

203208
return false;
204-
}).join('\n');
209+
});
210+
211+
if (cleanedSourceLines.any((element) => element.isBlockLine)) {
212+
final result = <String, String>{};
213+
214+
String? currentBlock;
215+
final builder = <String>[];
216+
217+
void finishBlock() {
218+
result[currentBlock!] = builder.join('\n').trim();
219+
builder.clear();
220+
currentBlock = null;
221+
}
222+
223+
for (var line in cleanedSourceLines) {
224+
if (currentBlock == null) {
225+
if (!line.isBlockLine) {
226+
// have not got to a block yet – skip!
227+
} else {
228+
// this is our first block!
229+
currentBlock = line.blockName;
230+
if (result.containsKey(currentBlock)) {
231+
throw StateError('Duplicate block "$currentBlock"');
232+
}
233+
}
234+
continue;
235+
}
236+
237+
if (line.isBlockLine) {
238+
if (builder.isEmpty) {
239+
log.warning('`$currentBlock` is empty');
240+
} else {
241+
finishBlock();
242+
currentBlock = line.blockName;
243+
if (result.containsKey(currentBlock)) {
244+
throw StateError('Duplicate block "$currentBlock"');
245+
}
246+
}
247+
} else {
248+
builder.add(line);
249+
}
250+
}
205251

206-
return '''
252+
if (currentBlock != null) finishBlock();
253+
254+
return result
255+
.map((key, value) => MapEntry('$fileName-$key', value.dartBlock));
256+
} else {
257+
return {
258+
fileName: cleanedSourceLines.join('\n').dartBlock,
259+
};
260+
}
261+
}
262+
}
263+
264+
extension on String {
265+
bool get isBlockLine => trim().startsWith(_blockComment);
266+
267+
String get blockName {
268+
assert(isBlockLine);
269+
final index = indexOf(_blockComment);
270+
assert(index >= 0);
271+
final start = index + _blockComment.length;
272+
return substring(start).trim();
273+
}
274+
275+
String get dartBlock => '''
207276
```dart
208-
$cleanedSource
277+
${trim()}
209278
```''';
210-
}
279+
280+
static const _blockComment = r'// # ';
211281
}

0 commit comments

Comments
 (0)