@@ -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,23 +145,95 @@ class JsonSerializableGenerator
138
145
buffer.writeln (' ${field .type } get ${field .name };' );
139
146
}
140
147
141
- buffer.writeln (' Map<String, dynamic> toJson() ' );
142
- if (fieldsList
143
- .every ((e) => _includeIfNull (e, classAnnotation.includeIfNull))) {
144
- // write simple `toJson` method that includes all keys...
145
- _writeToJsonSimple (buffer, fields.values, classAnnotation.nullable);
148
+ buffer.write (' Map<String, dynamic> toJson() ' );
149
+
150
+ if (useWrappers) {
151
+ buffer.writeln ('=> new $helpClassName (this);' );
146
152
} else {
147
- // At least one field should be excluded if null
148
- _writeToJsonWithNullChecks (buffer, fields.values, classAnnotation);
153
+ if (fieldsList
154
+ .every ((e) => _includeIfNull (e, classAnnotation.includeIfNull))) {
155
+ // write simple `toJson` method that includes all keys...
156
+ _writeToJsonSimple (buffer, fields.values, classAnnotation.nullable);
157
+ } else {
158
+ // At least one field should be excluded if null
159
+ _writeToJsonWithNullChecks (buffer, fields.values, classAnnotation);
160
+ }
149
161
}
150
162
151
163
// end of the mixin class
152
164
buffer.writeln ('}' );
165
+
166
+ if (useWrappers) {
167
+ _writeWrapper (
168
+ buffer, helpClassName, mixClassName, classAnnotation, fields);
169
+ }
153
170
}
154
171
155
172
return buffer.toString ();
156
173
}
157
174
175
+ void _writeWrapper (
176
+ StringBuffer buffer,
177
+ String helpClassName,
178
+ String mixClassName,
179
+ JsonSerializable classAnnotation,
180
+ Map <String , FieldElement > fields) {
181
+ buffer.writeln ();
182
+ // TODO(kevmoo): write JsonMapWrapper if annotation lib is prefix-imported
183
+ buffer.writeln ('''class $helpClassName extends \$ JsonMapWrapper {
184
+ final $mixClassName _v;
185
+ $helpClassName (this._v);
186
+ ''' );
187
+
188
+ if (fields.values
189
+ .every ((e) => _includeIfNull (e, classAnnotation.includeIfNull))) {
190
+ // TODO(kevmoo): consider just doing one code path – if it's fast
191
+ // enough
192
+ var jsonKeys = fields.values.map (_safeNameAccess).join (', ' );
193
+
194
+ // TODO(kevmoo): maybe put this in a static field instead?
195
+ // const lists have unfortunate overhead
196
+ buffer.writeln (''' @override
197
+ Iterable<String> get keys => const [${jsonKeys }];
198
+ ''' );
199
+ } else {
200
+ // At least one field should be excluded if null
201
+ buffer.writeln ('@override\n Iterable<String> get keys sync* {' );
202
+
203
+ for (var field in fields.values) {
204
+ var nullCheck = ! _includeIfNull (field, classAnnotation.includeIfNull);
205
+ if (nullCheck) {
206
+ buffer.writeln ('if (_v.${field .name } != null) {' );
207
+ }
208
+ buffer.writeln ('yield ${_safeNameAccess (field )};' );
209
+ if (nullCheck) {
210
+ buffer.writeln ('}' );
211
+ }
212
+ }
213
+
214
+ buffer.writeln ('}\n ' );
215
+ }
216
+
217
+ buffer.writeln ('''@override
218
+ dynamic operator [](Object key) {
219
+ if (key is String) {
220
+ switch(key) {
221
+ ''' );
222
+
223
+ for (var field in fields.values) {
224
+ var valueAccess = '_v.${field .name }' ;
225
+ buffer.write ('''case ${_safeNameAccess (field )}:
226
+ return ${_serializeField (field , classAnnotation .nullable , accessOverride : valueAccess )};''' );
227
+ }
228
+
229
+ buffer.writeln ('''
230
+ }}
231
+ return null;
232
+ }''' );
233
+
234
+ buffer.writeln ('}' );
235
+ }
236
+
158
237
void _writeToJsonWithNullChecks (StringBuffer buffer,
159
238
Iterable <FieldElement > fields, JsonSerializable classAnnotation) {
160
239
buffer.writeln ('{' );
@@ -352,7 +431,8 @@ void $toJsonMapHelperName(String key, dynamic value) {
352
431
/// representing the serialization of a value.
353
432
String _serialize (DartType targetType, String expression, bool nullable) =>
354
433
_allHelpers
355
- .map ((h) => h.serialize (targetType, expression, nullable, _serialize))
434
+ .map ((h) =>
435
+ h.serialize (targetType, expression, nullable, _helperContext))
356
436
.firstWhere ((r) => r != null ,
357
437
orElse: () =>
358
438
throw new UnsupportedTypeError (targetType, expression));
@@ -377,10 +457,35 @@ void $toJsonMapHelperName(String key, dynamic value) {
377
457
String _deserialize (DartType targetType, String expression, bool nullable) =>
378
458
_allHelpers
379
459
.map ((th) =>
380
- th.deserialize (targetType, expression, nullable, _deserialize ))
460
+ th.deserialize (targetType, expression, nullable, _helperContext ))
381
461
.firstWhere ((r) => r != null ,
382
462
orElse: () =>
383
463
throw new UnsupportedTypeError (targetType, expression));
464
+
465
+ _TypeHelperContext get _helperContext => _typeHelperContextExpando[this ] ?? =
466
+ new _TypeHelperContext (_serialize, _deserialize, useWrappers);
467
+ }
468
+
469
+ final _typeHelperContextExpando = new Expando <_TypeHelperContext >();
470
+
471
+ typedef String _TypeHelperGenerator (
472
+ DartType fieldType, String expression, bool nullable);
473
+
474
+ class _TypeHelperContext implements SerializeContext , DeserializeContext {
475
+ final _TypeHelperGenerator _serialize, _deserialize;
476
+
477
+ @override
478
+ final bool useWrappers;
479
+
480
+ _TypeHelperContext (this ._serialize, this ._deserialize, this .useWrappers);
481
+
482
+ @override
483
+ String serialize (DartType fieldType, String expression, bool nullable) =>
484
+ _serialize (fieldType, expression, nullable);
485
+
486
+ @override
487
+ String deserialize (DartType fieldType, String expression, bool nullable) =>
488
+ _deserialize (fieldType, expression, nullable);
384
489
}
385
490
386
491
String _safeNameAccess (FieldElement field) {
0 commit comments