1+ import 'dart:convert' show JsonEncoder, JsonDecoder;
2+
3+ import 'package:dart_json_mapper/dart_json_mapper.dart' ;
4+
5+ import '../class_info.dart' ;
6+ import '../errors.dart' ;
7+ import '../identifier_casing.dart' ;
8+ import '../json_map.dart' ;
9+ import '../model/index.dart' ;
10+ import 'field_handler.dart' ;
11+ import 'reflection_handler.dart' ;
12+
13+ class DeserializationHandler {
14+ final JsonMapper _mapper;
15+
16+ DeserializationHandler (this ._mapper);
17+
18+ Object ? deserializeObject (
19+ dynamic jsonValue, DeserializationContext context) {
20+ if (jsonValue == null ) {
21+ return null ;
22+ }
23+ var typeInfo = context.typeInfo! ;
24+ final converter = _mapper.getConverter (context.jsonPropertyMeta, typeInfo);
25+ if (converter != null ) {
26+ _mapper.configureConverter (converter, context);
27+ if (typeInfo.isIterable &&
28+ (converter is ICustomIterableConverter &&
29+ converter is ! DefaultIterableConverter )) {
30+ return _mapper.applyValueDecorator (
31+ _mapper.getConvertedValue (converter, jsonValue, context), typeInfo);
32+ }
33+ return _mapper.applyValueDecorator (
34+ _mapper.getConvertedValue (converter, jsonValue, context), typeInfo);
35+ }
36+
37+ dynamic convertedJsonValue =
38+ JsonMap .isValidJSON (jsonValue) ? JsonDecoder ().convert (jsonValue) : jsonValue;
39+
40+ if (convertedJsonValue is String && typeInfo.type is Type ) {
41+ return _mapper.typeInfoHandler.getTypeByStringName (convertedJsonValue.replaceAll ("\" " , "" ));
42+ }
43+
44+ final jsonMap = JsonMap (
45+ convertedJsonValue, null , context.parentJsonMaps as List <JsonMap >? );
46+ typeInfo =
47+ _detectObjectType (null , context.typeInfo! .type, jsonMap, context) ??
48+ typeInfo;
49+ final classInfo = _mapper.classes[typeInfo.type] ??
50+ _mapper.classes[typeInfo.genericType] ??
51+ _mapper.classes[typeInfo.mixinType];
52+ if (classInfo == null ) {
53+ throw MissingAnnotationOnTypeError (typeInfo.type);
54+ }
55+ jsonMap.jsonMeta = classInfo.getMeta (context.options.scheme);
56+
57+ final namedArguments =
58+ _getNamedArguments (classInfo, jsonMap, context);
59+ final positionalArgumentNames = < String > [];
60+ final positionalArguments = _getPositionalArguments (
61+ classInfo, jsonMap, context, positionalArgumentNames);
62+ dynamic objectInstance;
63+ try {
64+ objectInstance = context.options.template ??
65+ (classInfo.classMirror.isEnum
66+ ? null
67+ : classInfo.classMirror.newInstance (
68+ classInfo
69+ .getJsonConstructor (context.options.scheme)!
70+ .constructorName,
71+ positionalArguments,
72+ namedArguments));
73+ } on TypeError catch (typeError) {
74+ final positionalNullArguments = positionalArgumentNames.where ((element) =>
75+ positionalArguments[positionalArgumentNames.indexOf (element)] ==
76+ null );
77+ final namedNullArguments = Map <Symbol , dynamic >.from (namedArguments);
78+ namedNullArguments.removeWhere ((key, value) => value != null );
79+ throw CannotCreateInstanceError (
80+ typeError, classInfo, positionalNullArguments, namedNullArguments);
81+ }
82+
83+ final im = ReflectionHandler .safeGetInstanceMirror (objectInstance)! ;
84+ final inheritedPublicFieldNames = classInfo.inheritedPublicFieldNames;
85+ final mappedFields = namedArguments.keys
86+ .map ((Symbol symbol) =>
87+ RegExp ('"(.+)"' ).allMatches (symbol.toString ()).first.group (1 ))
88+ .toList ()
89+ ..addAll (positionalArgumentNames);
90+
91+ _mapper.enumeratePublicProperties (im, jsonMap, context, (name, property,
92+ isGetterOnly, JsonProperty ? meta, converter, TypeInfo typeInfo) {
93+ final propertyContext = context.reBuild (
94+ parentObjectInstances: [
95+ ...(context.parentObjectInstances ?? []),
96+ objectInstance
97+ ],
98+ typeInfo: typeInfo,
99+ jsonPropertyMeta: meta,
100+ parentJsonMaps: < JsonMap > [
101+ ...(context.parentJsonMaps ?? []),
102+ jsonMap
103+ ]);
104+ final defaultValue = meta? .defaultValue;
105+ final hasJsonProperty = jsonMap.hasProperty (property.name);
106+ var fieldValue = jsonMap.getPropertyValue (property.name);
107+ if (! hasJsonProperty || mappedFields.contains (name)) {
108+ if (meta? .flatten == true ) {
109+ if (mappedFields.contains (name) || isGetterOnly) {
110+ return ;
111+ }
112+ final fieldValue = jsonMap.getPropertyValue (property.name);
113+ final metaName = meta? .name;
114+ final objectToDeserialize = metaName != null && fieldValue is Map
115+ ? fieldValue.map ((key, value) => MapEntry (skipPrefix (metaName, key, propertyContext.caseStyle), value))
116+ : fieldValue;
117+
118+ im.invokeSetter (name, this .deserializeObject (objectToDeserialize, propertyContext));
119+ return ;
120+ }
121+ if (im.invokeGetter (name) == null &&
122+ defaultValue != null &&
123+ ! isGetterOnly) {
124+ im.invokeSetter (name, defaultValue);
125+ }
126+ if (meta? .inject != true ) {
127+ return ;
128+ }
129+ }
130+ fieldValue = property.raw
131+ ? this .deserializeObject (fieldValue, propertyContext)
132+ : property.value;
133+ if (isGetterOnly) {
134+ if (inheritedPublicFieldNames.contains (name) &&
135+ ! mappedFields.contains (property.name)) {
136+ mappedFields.add (property.name);
137+ }
138+ } else {
139+ if (meta? .rawJson == true && typeInfo.type == String ) {
140+ if (fieldValue is Map || fieldValue is List ) {
141+ fieldValue = JsonEncoder ().convert (fieldValue);
142+ } else if (fieldValue == null ) {
143+ fieldValue = null ;
144+ } else {
145+ fieldValue = fieldValue.toString ();
146+ }
147+ }
148+
149+ fieldValue = _mapper.applyValueDecorator (fieldValue, typeInfo) ?? defaultValue;
150+ im.invokeSetter (name, fieldValue);
151+ mappedFields.add (property.name);
152+ }
153+ });
154+
155+ final discriminatorPropertyName =
156+ _mapper.typeInfoHandler.getDiscriminatorProperty (classInfo, context.options);
157+ final unmappedFields = jsonMap.map.keys
158+ .where ((field) =>
159+ ! mappedFields.contains (field) && field != discriminatorPropertyName)
160+ .toList ();
161+ if (unmappedFields.isNotEmpty) {
162+ final jsonAnySetter = classInfo.getJsonAnySetter (context.options.scheme);
163+ for (var field in unmappedFields) {
164+ final jsonSetter =
165+ classInfo.getJsonSetter (field, context.options.scheme) ??
166+ jsonAnySetter;
167+ final params = jsonSetter == jsonAnySetter
168+ ? [field, jsonMap.map[field]]
169+ : [jsonMap.map[field]];
170+ if (jsonSetter != null ) {
171+ im.invoke (jsonSetter.simpleName, params);
172+ }
173+ }
174+ }
175+
176+ return _mapper.applyValueDecorator (objectInstance, typeInfo);
177+ }
178+
179+ TypeInfo ? _detectObjectType (dynamic objectInstance, Type ? objectType,
180+ JsonMap objectJsonMap, DeserializationContext context) {
181+ final objectClassInfo = _mapper.classes[objectType];
182+ if (objectClassInfo == null ) {
183+ return null ;
184+ }
185+ final meta = objectClassInfo.getMeta (context.options.scheme);
186+
187+ if (objectInstance is Map <String , dynamic >) {
188+ objectJsonMap = JsonMap (objectInstance, meta);
189+ }
190+ final typeInfo = _mapper.typeInfoHandler.getTypeInfo (objectType ?? objectInstance.runtimeType);
191+
192+ final discriminatorProperty =
193+ _mapper.typeInfoHandler.getDiscriminatorProperty (objectClassInfo, context.options);
194+ final discriminatorValue = discriminatorProperty != null &&
195+ objectJsonMap.hasProperty (discriminatorProperty)
196+ ? objectJsonMap.getPropertyValue (discriminatorProperty)
197+ : null ;
198+ if (discriminatorProperty != null && discriminatorValue != null ) {
199+ final declarationMirror =
200+ objectClassInfo.getDeclarationMirror (discriminatorProperty);
201+ if (declarationMirror != null ) {
202+ final discriminatorType = ReflectionHandler .getDeclarationType (declarationMirror);
203+ final value = this .deserializeObject (discriminatorValue,
204+ context.reBuild (typeInfo: _mapper.typeInfoHandler.getTypeInfo (discriminatorType)));
205+ if (value is Type ) {
206+ return _mapper.typeInfoHandler.getTypeInfo (value);
207+ }
208+
209+ if (_mapper.discriminatorToType[value] == null ) {
210+ final validDiscriminators = ClassInfo .getAllSubTypes (
211+ _mapper.classes, objectClassInfo)
212+ .map ((e) => e.getMeta (context.options.scheme)! .discriminatorValue)
213+ .toList ();
214+ throw JsonMapperSubtypeError (
215+ discriminatorValue,
216+ validDiscriminators,
217+ objectClassInfo,
218+ );
219+ }
220+
221+ return _mapper.typeInfoHandler.getTypeInfo (_mapper.discriminatorToType[value]! );
222+ }
223+ }
224+ if (discriminatorValue != null ) {
225+ final targetType = _mapper.typeInfoHandler.getTypeByStringName (discriminatorValue);
226+ return _mapper.classes[targetType] != null
227+ ? _mapper.typeInfoHandler.getTypeInfo (_mapper.classes[targetType]! .reflectedType! )
228+ : typeInfo;
229+ }
230+ return typeInfo;
231+ }
232+
233+ void _enumerateConstructorParameters (ClassInfo classInfo, JsonMap jsonMap,
234+ DeserializationContext context, Function filter, Function visitor) {
235+ final classMeta = classInfo.getMeta (context.options.scheme);
236+ final scheme =
237+ classMeta != null ? classMeta.scheme : context.options.scheme;
238+ final methodMirror = classInfo.getJsonConstructor (scheme);
239+ if (methodMirror == null ) {
240+ return ;
241+ }
242+ for (var param in methodMirror.parameters) {
243+ if (! filter (param)) {
244+ return ;
245+ }
246+ final name = param.simpleName;
247+ final declarationMirror = classInfo.getDeclarationMirror (name) ?? param;
248+ final paramType = param.hasReflectedType
249+ ? param.reflectedType
250+ : param.hasDynamicReflectedType
251+ ? param.dynamicReflectedType
252+ : _mapper.typeInfoHandler.getGenericParameterTypeByIndex (
253+ methodMirror.parameters.indexOf (param),
254+ context.typeInfo! ) ??
255+ dynamic ;
256+ var paramTypeInfo = _mapper.typeInfoHandler.getTypeInfo (paramType);
257+ paramTypeInfo = paramTypeInfo.isDynamic
258+ ? _mapper.typeInfoHandler.getTypeInfo (ReflectionHandler .getDeclarationType (declarationMirror))
259+ : paramTypeInfo;
260+ final meta = classInfo.getDeclarationMeta (
261+ declarationMirror, context.options.scheme) ??
262+ classInfo.getDeclarationMeta (param, context.options.scheme);
263+ final propertyContext = context.reBuild (
264+ classMeta: classMeta,
265+ jsonPropertyMeta: meta,
266+ typeInfo: paramTypeInfo,
267+ parentJsonMaps: < JsonMap > [
268+ ...(context.parentJsonMaps ?? []),
269+ jsonMap
270+ ]);
271+
272+ dynamic finalValueForVisitor;
273+ String ? jsonNameForVisitor;
274+
275+ if (meta? .flatten == true ) {
276+ finalValueForVisitor = this .deserializeObject (jsonMap.map, propertyContext.reBuild (jsonPropertyMeta: null ));
277+ jsonNameForVisitor = context.transformIdentifier (meta? .name ?? name);
278+ } else {
279+ final property = _mapper.resolveProperty (
280+ name,
281+ jsonMap,
282+ propertyContext,
283+ classMeta,
284+ meta,
285+ (_, resolvedJsonNameFromCallback, defaultValueFromCallback) =>
286+ jsonMap.hasProperty (resolvedJsonNameFromCallback)
287+ ? jsonMap.getPropertyValue (resolvedJsonNameFromCallback) ?? defaultValueFromCallback
288+ : defaultValueFromCallback);
289+ jsonNameForVisitor = property.name;
290+ if (property.raw) {
291+ finalValueForVisitor = this .deserializeObject (property.value, propertyContext);
292+ } else {
293+ finalValueForVisitor = property.value;
294+ }
295+ }
296+
297+ visitor (param, name, jsonNameForVisitor, classMeta, meta, finalValueForVisitor, paramTypeInfo);
298+ }
299+ }
300+
301+ Map <Symbol , dynamic > _getNamedArguments (
302+ ClassInfo cm, JsonMap jsonMap, DeserializationContext context) {
303+ final result = < Symbol , dynamic > {};
304+
305+ _enumerateConstructorParameters (
306+ cm, jsonMap, context, (param) => param.isNamed, (param, name, jsonName,
307+ classMeta, JsonProperty ? meta, value, TypeInfo typeInfo) {
308+ if (! FieldHandler .isFieldIgnoredByValue (value, classMeta, meta, context.options)) {
309+ result[Symbol (name)] = value;
310+ }
311+ });
312+
313+ return result;
314+ }
315+
316+ List _getPositionalArguments (ClassInfo cm, JsonMap jsonMap,
317+ DeserializationContext context, List <String > positionalArgumentNames) {
318+ final result = [];
319+
320+ _enumerateConstructorParameters (
321+ cm, jsonMap, context, (param) => ! param.isOptional && ! param.isNamed,
322+ (param, name, jsonName, classMeta, JsonProperty ? meta, value,
323+ TypeInfo typeInfo) {
324+ positionalArgumentNames.add (name);
325+ result.add (FieldHandler .isFieldIgnoredByValue (value, classMeta, meta, context.options)
326+ ? null
327+ : value);
328+ });
329+
330+ return result;
331+ }
332+ }
0 commit comments