serialization_util.dart 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. import 'dart:convert';
  2. import 'package:flutter/material.dart';
  3. import 'package:from_css_color/from_css_color.dart';
  4. import '/backend/schema/structs/index.dart';
  5. import '../../flutter_flow/lat_lng.dart';
  6. import '../../flutter_flow/place.dart';
  7. import '../../flutter_flow/uploaded_file.dart';
  8. import 'package:debug_panel_proto/debug_panel_proto.dart';
  9. /// SERIALIZATION HELPERS
  10. String dateTimeToString(DateTime dateTime) =>
  11. '${dateTime.isUtc ? 'u' : 'l'}${dateTime.millisecondsSinceEpoch}';
  12. String dateTimeRangeToString(DateTimeRange dateTimeRange) {
  13. final start = dateTimeRange.start;
  14. final end = dateTimeRange.end;
  15. final startStr = '${start.isUtc ? 'u' : 'l'}${start.millisecondsSinceEpoch}';
  16. final endStr = '${end.isUtc ? 'u' : 'l'}${end.millisecondsSinceEpoch}';
  17. return '$startStr|$endStr';
  18. }
  19. String placeToString(FFPlace place) => jsonEncode({
  20. 'latLng': place.latLng.serialize(),
  21. 'name': place.name,
  22. 'address': place.address,
  23. 'city': place.city,
  24. 'state': place.state,
  25. 'country': place.country,
  26. 'zipCode': place.zipCode,
  27. });
  28. String uploadedFileToString(FFUploadedFile uploadedFile) =>
  29. uploadedFile.serialize();
  30. String? serializeParam(
  31. dynamic param,
  32. ParamType paramType, {
  33. bool isList = false,
  34. }) {
  35. try {
  36. if (param == null) {
  37. return null;
  38. }
  39. if (isList) {
  40. final serializedValues = (param as Iterable)
  41. .map((p) => serializeParam(p, paramType, isList: false))
  42. .where((p) => p != null)
  43. .map((p) => p!)
  44. .toList();
  45. return json.encode(serializedValues);
  46. }
  47. String? data;
  48. switch (paramType) {
  49. case ParamType.int:
  50. data = param.toString();
  51. case ParamType.double:
  52. data = param.toString();
  53. case ParamType.String:
  54. data = param;
  55. case ParamType.bool:
  56. data = param ? 'true' : 'false';
  57. case ParamType.DateTime:
  58. data = dateTimeToString(param as DateTime);
  59. case ParamType.DateTimeRange:
  60. data = dateTimeRangeToString(param as DateTimeRange);
  61. case ParamType.LatLng:
  62. data = (param as LatLng).serialize();
  63. case ParamType.Color:
  64. data = (param as Color).toCssString();
  65. case ParamType.FFPlace:
  66. data = placeToString(param as FFPlace);
  67. case ParamType.FFUploadedFile:
  68. data = uploadedFileToString(param as FFUploadedFile);
  69. case ParamType.JSON:
  70. data = json.encode(param);
  71. case ParamType.DataStruct:
  72. data = param is BaseStruct ? param.serialize() : null;
  73. default:
  74. data = null;
  75. }
  76. return data;
  77. } catch (e) {
  78. print('Error serializing parameter: $e');
  79. return null;
  80. }
  81. }
  82. DebugDataField debugSerializeParam(
  83. dynamic param,
  84. ParamType paramType, {
  85. bool isList = false,
  86. String? link,
  87. String? searchReference,
  88. String? name,
  89. bool? nullable,
  90. }) {
  91. try {
  92. if (isList) {
  93. final values = (param as Iterable?)
  94. ?.map((p) => debugSerializeParam(
  95. p,
  96. paramType,
  97. isList: false,
  98. ))
  99. .toList();
  100. return DebugDataField(
  101. type: _kParamTypeProtoMap[paramType],
  102. listValue: ListDebugDataField(values: values),
  103. link: link,
  104. searchReference: searchReference,
  105. name: name,
  106. nullable: nullable,
  107. );
  108. }
  109. String? data;
  110. var type = _kParamTypeProtoMap[paramType];
  111. if (param != null) {
  112. switch (paramType) {
  113. case ParamType.int:
  114. data = param.toString();
  115. case ParamType.double:
  116. data = param.toString();
  117. case ParamType.String:
  118. data = param;
  119. case ParamType.bool:
  120. data = param ? 'true' : 'false';
  121. case ParamType.DateTime:
  122. data = (param as DateTime).millisecondsSinceEpoch.toString();
  123. case ParamType.DateTimeRange:
  124. data = dateTimeRangeToString(param as DateTimeRange);
  125. case ParamType.LatLng:
  126. data = (param as LatLng).serialize();
  127. case ParamType.Color:
  128. data = (param as Color).toCssString();
  129. case ParamType.FFPlace:
  130. data = placeToString(param as FFPlace);
  131. case ParamType.FFUploadedFile:
  132. data = uploadedFileToString(param as FFUploadedFile);
  133. case ParamType.JSON:
  134. data = json.encode(param);
  135. case ParamType.DataStruct:
  136. if (param is BaseStruct) {
  137. return DebugDataField(
  138. type: DebugDataField_ParamType.DATA_STRUCT,
  139. mapValue: MapDebugDataField(
  140. values: param.toDebugSerializableMap().entries,
  141. ),
  142. link: link,
  143. searchReference: searchReference,
  144. name: name,
  145. nullable: nullable,
  146. );
  147. } else {
  148. return DebugDataField();
  149. }
  150. case ParamType.Action:
  151. case ParamType.Widget:
  152. data = param.runtimeType.toString();
  153. case ParamType.ApiResponse:
  154. return DebugDataField(
  155. type: DebugDataField_ParamType.DATA_STRUCT,
  156. mapValue: MapDebugDataField(
  157. values: {
  158. 'statusCode':
  159. debugSerializeParam(param.statusCode, ParamType.int),
  160. 'body': debugSerializeParam(param.bodyText, ParamType.String),
  161. }.entries,
  162. ),
  163. link: link,
  164. name: name,
  165. nullable: nullable,
  166. );
  167. default:
  168. data = null;
  169. }
  170. }
  171. return DebugDataField(
  172. serializedValue: data,
  173. type: type,
  174. link: link,
  175. searchReference: searchReference,
  176. name: name,
  177. nullable: nullable,
  178. );
  179. } catch (e) {
  180. print('Error debug serializing parameter: $e');
  181. return DebugDataField();
  182. }
  183. }
  184. /// END SERIALIZATION HELPERS
  185. /// DESERIALIZATION HELPERS
  186. DateTime? dateTimeFromString(String? dateTimeStr) {
  187. if (dateTimeStr == null || dateTimeStr.isEmpty) {
  188. return null;
  189. }
  190. final hasPrefix = dateTimeStr.startsWith('u') || dateTimeStr.startsWith('l');
  191. final milliseconds = int.tryParse(
  192. hasPrefix ? dateTimeStr.substring(1) : dateTimeStr,
  193. );
  194. return milliseconds != null
  195. ? DateTime.fromMillisecondsSinceEpoch(
  196. milliseconds,
  197. isUtc: hasPrefix ? dateTimeStr.startsWith('u') : false,
  198. )
  199. : null;
  200. }
  201. DateTimeRange? dateTimeRangeFromString(String dateTimeRangeStr) {
  202. final pieces = dateTimeRangeStr.split('|');
  203. if (pieces.length != 2) {
  204. return null;
  205. }
  206. DateTime? parseDateTime(String value) {
  207. final hasPrefix = value.startsWith('u') || value.startsWith('l');
  208. final milliseconds = int.tryParse(hasPrefix ? value.substring(1) : value);
  209. return milliseconds != null
  210. ? DateTime.fromMillisecondsSinceEpoch(
  211. milliseconds,
  212. isUtc: hasPrefix ? value.startsWith('u') : false,
  213. )
  214. : null;
  215. }
  216. final start = parseDateTime(pieces.first);
  217. final end = parseDateTime(pieces.last);
  218. if (start == null || end == null) {
  219. return null;
  220. }
  221. return DateTimeRange(
  222. start: start,
  223. end: end,
  224. );
  225. }
  226. LatLng? latLngFromString(String? latLngStr) {
  227. final pieces = latLngStr?.split(',');
  228. if (pieces == null || pieces.length != 2) {
  229. return null;
  230. }
  231. return LatLng(
  232. double.parse(pieces.first.trim()),
  233. double.parse(pieces.last.trim()),
  234. );
  235. }
  236. FFPlace placeFromString(String placeStr) {
  237. final serializedData = jsonDecode(placeStr) as Map<String, dynamic>;
  238. final data = {
  239. 'latLng': serializedData.containsKey('latLng')
  240. ? latLngFromString(serializedData['latLng'] as String)
  241. : const LatLng(0.0, 0.0),
  242. 'name': serializedData['name'] ?? '',
  243. 'address': serializedData['address'] ?? '',
  244. 'city': serializedData['city'] ?? '',
  245. 'state': serializedData['state'] ?? '',
  246. 'country': serializedData['country'] ?? '',
  247. 'zipCode': serializedData['zipCode'] ?? '',
  248. };
  249. return FFPlace(
  250. latLng: data['latLng'] as LatLng,
  251. name: data['name'] as String,
  252. address: data['address'] as String,
  253. city: data['city'] as String,
  254. state: data['state'] as String,
  255. country: data['country'] as String,
  256. zipCode: data['zipCode'] as String,
  257. );
  258. }
  259. FFUploadedFile uploadedFileFromString(String uploadedFileStr) =>
  260. FFUploadedFile.deserialize(uploadedFileStr);
  261. enum ParamType {
  262. int,
  263. double,
  264. String,
  265. bool,
  266. DateTime,
  267. DateTimeRange,
  268. LatLng,
  269. Color,
  270. FFPlace,
  271. FFUploadedFile,
  272. JSON,
  273. Action,
  274. Widget,
  275. ApiResponse,
  276. DataStruct,
  277. }
  278. const _kParamTypeProtoMap = {
  279. ParamType.int: DebugDataField_ParamType.INT,
  280. ParamType.double: DebugDataField_ParamType.DOUBLE,
  281. ParamType.String: DebugDataField_ParamType.STRING,
  282. ParamType.bool: DebugDataField_ParamType.BOOL,
  283. ParamType.DateTime: DebugDataField_ParamType.DATE_TIME,
  284. ParamType.DateTimeRange: DebugDataField_ParamType.DATE_TIME_RANGE,
  285. ParamType.LatLng: DebugDataField_ParamType.LAT_LNG,
  286. ParamType.Color: DebugDataField_ParamType.COLOR,
  287. ParamType.FFPlace: DebugDataField_ParamType.FF_PLACE,
  288. ParamType.FFUploadedFile: DebugDataField_ParamType.FF_UPLOADED_FILE,
  289. ParamType.JSON: DebugDataField_ParamType.JSON,
  290. ParamType.Action: DebugDataField_ParamType.ACTION,
  291. ParamType.Widget: DebugDataField_ParamType.WIDGET,
  292. ParamType.DataStruct: DebugDataField_ParamType.DATA_STRUCT,
  293. };
  294. dynamic deserializeParam<T>(
  295. String? param,
  296. ParamType paramType,
  297. bool isList, {
  298. StructBuilder<T>? structBuilder,
  299. }) {
  300. try {
  301. if (param == null) {
  302. return null;
  303. }
  304. if (isList) {
  305. final paramValues = json.decode(param);
  306. if (paramValues is! Iterable || paramValues.isEmpty) {
  307. return null;
  308. }
  309. return paramValues
  310. .where((p) => p is String)
  311. .map((p) => p as String)
  312. .map((p) => deserializeParam<T>(
  313. p,
  314. paramType,
  315. false,
  316. structBuilder: structBuilder,
  317. ))
  318. .where((p) => p != null)
  319. .map((p) => p! as T)
  320. .toList();
  321. }
  322. switch (paramType) {
  323. case ParamType.int:
  324. return int.tryParse(param);
  325. case ParamType.double:
  326. return double.tryParse(param);
  327. case ParamType.String:
  328. return param;
  329. case ParamType.bool:
  330. return param == 'true';
  331. case ParamType.DateTime:
  332. return dateTimeFromString(param);
  333. case ParamType.DateTimeRange:
  334. return dateTimeRangeFromString(param);
  335. case ParamType.LatLng:
  336. return latLngFromString(param);
  337. case ParamType.Color:
  338. return fromCssColor(param);
  339. case ParamType.FFPlace:
  340. return placeFromString(param);
  341. case ParamType.FFUploadedFile:
  342. return uploadedFileFromString(param);
  343. case ParamType.JSON:
  344. return json.decode(param);
  345. case ParamType.DataStruct:
  346. final data = json.decode(param) as Map<String, dynamic>? ?? {};
  347. return structBuilder != null ? structBuilder(data) : null;
  348. default:
  349. return null;
  350. }
  351. } catch (e) {
  352. print('Error deserializing parameter: $e');
  353. return null;
  354. }
  355. }