@@ -36,20 +36,26 @@ class JsonSerializableGenerator
36
36
37
37
final List <TypeHelper > _typeHelpers;
38
38
39
+ final bool useWrappers;
40
+
39
41
/// Creates an instance of [JsonSerializableGenerator] .
40
42
///
41
43
/// If [typeHelpers] is not provided, two built-in helpers are used:
42
44
/// [JsonHelper] and [DateTimeHelper] .
43
- const JsonSerializableGenerator ({List <TypeHelper > typeHelpers})
44
- : this ._typeHelpers = typeHelpers ?? _defaultHelpers;
45
+ const JsonSerializableGenerator (
46
+ {List <TypeHelper > typeHelpers, bool useWrappers: false })
47
+ : this .useWrappers = useWrappers ?? false ,
48
+ this ._typeHelpers = typeHelpers ?? _defaultHelpers;
45
49
46
50
/// Creates an instance of [JsonSerializableGenerator] .
47
51
///
48
52
/// [typeHelpers] provides a set of [TypeHelper] that will be used along with
49
53
/// the built-in helpers: [JsonHelper] and [DateTimeHelper] .
50
54
factory JsonSerializableGenerator .withDefaultHelpers (
51
- Iterable <TypeHelper > typeHelpers) =>
55
+ Iterable <TypeHelper > typeHelpers,
56
+ {bool useWrappers: false }) =>
52
57
new JsonSerializableGenerator (
58
+ useWrappers: useWrappers,
53
59
typeHelpers: new List .unmodifiable (
54
60
[typeHelpers, _defaultHelpers].expand ((e) => e)));
55
61
@@ -125,6 +131,7 @@ class JsonSerializableGenerator
125
131
126
132
if (classAnnotation.createToJson) {
127
133
var mixClassName = '${prefix }SerializerMixin' ;
134
+ var helpClassName = '${prefix }JsonMapWrapper' ;
128
135
129
136
//
130
137
// Generate the mixin class
@@ -138,26 +145,96 @@ class JsonSerializableGenerator
138
145
buffer.writeln (' ${field .type } get ${field .name };' );
139
146
}
140
147
141
- buffer.writeln (' Map<String, dynamic> toJson() ' );
148
+ buffer.write (' Map<String, dynamic> toJson() ' );
142
149
143
150
var writeNaive =
144
151
fieldsList.every ((e) => _writeJsonValueNaive (e, classAnnotation));
145
152
146
- if (writeNaive) {
147
- // write simple `toJson` method that includes all keys...
148
- _writeToJsonSimple (buffer, fields.values, classAnnotation.nullable);
153
+ if (useWrappers) {
154
+ buffer.writeln ('=> new $helpClassName (this);' );
149
155
} else {
150
- // At least one field should be excluded if null
151
- _writeToJsonWithNullChecks (buffer, fields.values, classAnnotation);
156
+ if (writeNaive) {
157
+ // write simple `toJson` method that includes all keys...
158
+ _writeToJsonSimple (buffer, fields.values, classAnnotation.nullable);
159
+ } else {
160
+ // At least one field should be excluded if null
161
+ _writeToJsonWithNullChecks (buffer, fields.values, classAnnotation);
162
+ }
152
163
}
153
164
154
165
// end of the mixin class
155
166
buffer.writeln ('}' );
167
+
168
+ if (useWrappers) {
169
+ _writeWrapper (
170
+ buffer, helpClassName, mixClassName, classAnnotation, fields);
171
+ }
156
172
}
157
173
158
174
return buffer.toString ();
159
175
}
160
176
177
+ void _writeWrapper (
178
+ StringBuffer buffer,
179
+ String helpClassName,
180
+ String mixClassName,
181
+ JsonSerializable classAnnotation,
182
+ Map <String , FieldElement > fields) {
183
+ buffer.writeln ();
184
+ // TODO(kevmoo): write JsonMapWrapper if annotation lib is prefix-imported
185
+ buffer.writeln ('''class $helpClassName extends \$ JsonMapWrapper {
186
+ final $mixClassName _v;
187
+ $helpClassName (this._v);
188
+ ''' );
189
+
190
+ if (fields.values.every ((e) => _writeJsonValueNaive (e, classAnnotation))) {
191
+ // TODO(kevmoo): consider just doing one code path – if it's fast
192
+ // enough
193
+ var jsonKeys = fields.values.map (_safeNameAccess).join (', ' );
194
+
195
+ // TODO(kevmoo): maybe put this in a static field instead?
196
+ // const lists have unfortunate overhead
197
+ buffer.writeln (''' @override
198
+ Iterable<String> get keys => const [${jsonKeys }];
199
+ ''' );
200
+ } else {
201
+ // At least one field should be excluded if null
202
+ buffer.writeln ('@override\n Iterable<String> get keys sync* {' );
203
+
204
+ for (var field in fields.values) {
205
+ var nullCheck = ! _writeJsonValueNaive (field, classAnnotation);
206
+ if (nullCheck) {
207
+ buffer.writeln ('if (_v.${field .name } != null) {' );
208
+ }
209
+ buffer.writeln ('yield ${_safeNameAccess (field )};' );
210
+ if (nullCheck) {
211
+ buffer.writeln ('}' );
212
+ }
213
+ }
214
+
215
+ buffer.writeln ('}\n ' );
216
+ }
217
+
218
+ buffer.writeln ('''@override
219
+ dynamic operator [](Object key) {
220
+ if (key is String) {
221
+ switch(key) {
222
+ ''' );
223
+
224
+ for (var field in fields.values) {
225
+ var valueAccess = '_v.${field .name }' ;
226
+ buffer.write ('''case ${_safeNameAccess (field )}:
227
+ return ${_serializeField (field , classAnnotation .nullable , accessOverride : valueAccess )};''' );
228
+ }
229
+
230
+ buffer.writeln ('''
231
+ }}
232
+ return null;
233
+ }''' );
234
+
235
+ buffer.writeln ('}' );
236
+ }
237
+
161
238
void _writeToJsonWithNullChecks (StringBuffer buffer,
162
239
Iterable <FieldElement > fields, JsonSerializable classAnnotation) {
163
240
buffer.writeln ('{' );
@@ -352,7 +429,8 @@ void $toJsonMapHelperName(String key, dynamic value) {
352
429
/// representing the serialization of a value.
353
430
String _serialize (DartType targetType, String expression, bool nullable) =>
354
431
_allHelpers
355
- .map ((h) => h.serialize (targetType, expression, nullable, _serialize))
432
+ .map ((h) =>
433
+ h.serialize (targetType, expression, nullable, _helperContext))
356
434
.firstWhere ((r) => r != null ,
357
435
orElse: () => throw new UnsupportedTypeError (
358
436
targetType, expression, _notSupportedWithTypeHelpersMsg));
@@ -374,10 +452,35 @@ void $toJsonMapHelperName(String key, dynamic value) {
374
452
String _deserialize (DartType targetType, String expression, bool nullable) =>
375
453
_allHelpers
376
454
.map ((th) =>
377
- th.deserialize (targetType, expression, nullable, _deserialize ))
455
+ th.deserialize (targetType, expression, nullable, _helperContext ))
378
456
.firstWhere ((r) => r != null ,
379
457
orElse: () => throw new UnsupportedTypeError (
380
458
targetType, expression, _notSupportedWithTypeHelpersMsg));
459
+
460
+ _TypeHelperContext get _helperContext => _typeHelperContextExpando[this ] ?? =
461
+ new _TypeHelperContext (_serialize, _deserialize, useWrappers);
462
+ }
463
+
464
+ final _typeHelperContextExpando = new Expando <_TypeHelperContext >();
465
+
466
+ typedef String _TypeHelperGenerator (
467
+ DartType fieldType, String expression, bool nullable);
468
+
469
+ class _TypeHelperContext implements SerializeContext , DeserializeContext {
470
+ final _TypeHelperGenerator _serialize, _deserialize;
471
+
472
+ @override
473
+ final bool useWrappers;
474
+
475
+ _TypeHelperContext (this ._serialize, this ._deserialize, this .useWrappers);
476
+
477
+ @override
478
+ String serialize (DartType fieldType, String expression, bool nullable) =>
479
+ _serialize (fieldType, expression, nullable);
480
+
481
+ @override
482
+ String deserialize (DartType fieldType, String expression, bool nullable) =>
483
+ _deserialize (fieldType, expression, nullable);
381
484
}
382
485
383
486
String _safeNameAccess (FieldElement field) {
0 commit comments