PageRenderTime 47ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/src/canopy/companion/crx/fable-core/umd/Serialize.js

http://github.com/lefthandedgoat/canopy
JavaScript | 338 lines | 338 code | 0 blank | 0 comment | 123 complexity | 065598c3fb154e756295ba4bcbc61fef MD5 | raw file
Possible License(s): MIT, Apache-2.0
  1. import FableSymbol from "./Symbol";
  2. import { getType } from "./Symbol";
  3. import List from "./List";
  4. import { ofArray as listOfArray } from "./List";
  5. import FableSet from "./Set";
  6. import FableMap from "./Map";
  7. import { create as mapCreate } from "./Map";
  8. import { create as setCreate } from "./Set";
  9. import { hasInterface } from "./Util";
  10. import { getDefinition } from "./Util";
  11. import { NonDeclaredType } from "./Util";
  12. import { fold } from "./Seq";
  13. import { resolveGeneric, getTypeFullName } from "./Reflection";
  14. import { parse as dateParse } from "./Date";
  15. import { fsFormat } from "./String";
  16. export function toJson(o) {
  17. return JSON.stringify(o, function (k, v) {
  18. if (ArrayBuffer.isView(v)) {
  19. return Array.from(v);
  20. }
  21. else if (v != null && typeof v === "object") {
  22. var properties = typeof v[FableSymbol.reflection] === "function" ? v[FableSymbol.reflection]().properties : null;
  23. if (v instanceof List || v instanceof FableSet || v instanceof Set) {
  24. return Array.from(v);
  25. }
  26. else if (v instanceof FableMap || v instanceof Map) {
  27. var stringKeys_1 = null;
  28. return fold(function (o, kv) {
  29. if (stringKeys_1 === null) {
  30. stringKeys_1 = typeof kv[0] === "string";
  31. }
  32. o[stringKeys_1 ? kv[0] : toJson(kv[0])] = kv[1];
  33. return o;
  34. }, {}, v);
  35. }
  36. else if (!hasInterface(v, "FSharpRecord") && properties) {
  37. return fold(function (o, prop) {
  38. return o[prop] = v[prop], o;
  39. }, {}, Object.getOwnPropertyNames(properties));
  40. }
  41. else if (hasInterface(v, "FSharpUnion")) {
  42. if (!v.Fields || !v.Fields.length) {
  43. return v.Case;
  44. }
  45. else if (v.Fields.length === 1) {
  46. var fieldValue = typeof v.Fields[0] === 'undefined' ? null : v.Fields[0];
  47. return _a = {}, _a[v.Case] = fieldValue, _a;
  48. }
  49. else {
  50. return _b = {}, _b[v.Case] = v.Fields, _b;
  51. }
  52. }
  53. }
  54. return v;
  55. var _a, _b;
  56. });
  57. }
  58. function combine(path1, path2) {
  59. return typeof path2 === "number"
  60. ? path1 + "[" + path2 + "]"
  61. : (path1 ? path1 + "." : "") + path2;
  62. }
  63. function isNullable(typ) {
  64. if (typeof typ === "string") {
  65. return typ !== "boolean" && typ !== "number";
  66. }
  67. else if (typ instanceof NonDeclaredType) {
  68. return typ.kind !== "Array" && typ.kind !== "Tuple";
  69. }
  70. else {
  71. var info = typeof typ.prototype[FableSymbol.reflection] === "function"
  72. ? typ.prototype[FableSymbol.reflection]() : null;
  73. return info ? info.nullable : true;
  74. }
  75. }
  76. function invalidate(val, typ, path) {
  77. throw new Error(fsFormat("%A", val) + " " + (path ? "(" + path + ")" : "") + " is not of type " + getTypeFullName(typ));
  78. }
  79. function needsInflate(enclosing) {
  80. var typ = enclosing.head;
  81. if (typeof typ === "string") {
  82. return false;
  83. }
  84. if (typ instanceof NonDeclaredType) {
  85. switch (typ.kind) {
  86. case "Option":
  87. case "Array":
  88. return typ.definition != null || needsInflate(new List(typ.generics, enclosing));
  89. case "Tuple":
  90. return typ.generics.some(function (x) {
  91. return needsInflate(new List(x, enclosing));
  92. });
  93. case "GenericParam":
  94. return needsInflate(resolveGeneric(typ.definition, enclosing.tail));
  95. case "GenericType":
  96. return true;
  97. default:
  98. return false;
  99. }
  100. }
  101. return true;
  102. }
  103. function inflateArray(arr, enclosing, path) {
  104. if (!Array.isArray) {
  105. invalidate(arr, "array", path);
  106. }
  107. return needsInflate(enclosing)
  108. ? arr.map(function (x, i) { return inflate(x, enclosing, combine(path, i)); })
  109. : arr;
  110. }
  111. function inflateMap(obj, keyEnclosing, valEnclosing, path) {
  112. var inflateKey = keyEnclosing.head !== "string";
  113. var inflateVal = needsInflate(valEnclosing);
  114. return Object
  115. .getOwnPropertyNames(obj)
  116. .map(function (k) {
  117. var key = inflateKey ? inflate(JSON.parse(k), keyEnclosing, combine(path, k)) : k;
  118. var val = inflateVal ? inflate(obj[k], valEnclosing, combine(path, k)) : obj[k];
  119. return [key, val];
  120. });
  121. }
  122. function inflateList(val, enclosing, path) {
  123. var ar = [], li = new List(), cur = val, inf = needsInflate(enclosing);
  124. while (cur.tail != null) {
  125. ar.push(inf ? inflate(cur.head, enclosing, path) : cur.head);
  126. cur = cur.tail;
  127. }
  128. ar.reverse();
  129. for (var i = 0; i < ar.length; i++) {
  130. li = new List(ar[i], li);
  131. }
  132. return li;
  133. }
  134. function inflate(val, typ, path) {
  135. var enclosing = null;
  136. if (typ instanceof List) {
  137. enclosing = typ;
  138. typ = typ.head;
  139. }
  140. else {
  141. enclosing = new List(typ, new List());
  142. }
  143. if (val == null) {
  144. if (!isNullable(typ)) {
  145. invalidate(val, typ, path);
  146. }
  147. return val;
  148. }
  149. else if (typeof typ === "string") {
  150. if ((typ === "boolean" || typ === "number" || typ === "string") && (typeof val !== typ)) {
  151. invalidate(val, typ, path);
  152. }
  153. return val;
  154. }
  155. else if (typ instanceof NonDeclaredType) {
  156. switch (typ.kind) {
  157. case "Unit":
  158. return null;
  159. case "Option":
  160. return inflate(val, new List(typ.generics, enclosing), path);
  161. case "Array":
  162. if (typ.definition != null) {
  163. return new typ.definition(val);
  164. }
  165. else {
  166. return inflateArray(val, new List(typ.generics, enclosing), path);
  167. }
  168. case "Tuple":
  169. return typ.generics.map(function (x, i) {
  170. return inflate(val[i], new List(x, enclosing), combine(path, i));
  171. });
  172. case "GenericParam":
  173. return inflate(val, resolveGeneric(typ.definition, enclosing.tail), path);
  174. case "GenericType":
  175. var def = typ.definition;
  176. if (def === List) {
  177. return Array.isArray(val)
  178. ? listOfArray(inflateArray(val, resolveGeneric(0, enclosing), path))
  179. : inflateList(val, resolveGeneric(0, enclosing), path);
  180. }
  181. if (def === FableSet) {
  182. return setCreate(inflateArray(val, resolveGeneric(0, enclosing), path));
  183. }
  184. if (def === Set) {
  185. return new Set(inflateArray(val, resolveGeneric(0, enclosing), path));
  186. }
  187. if (def === FableMap) {
  188. return mapCreate(inflateMap(val, resolveGeneric(0, enclosing), resolveGeneric(1, enclosing), path));
  189. }
  190. if (def === Map) {
  191. return new Map(inflateMap(val, resolveGeneric(0, enclosing), resolveGeneric(1, enclosing), path));
  192. }
  193. return inflate(val, new List(typ.definition, enclosing), path);
  194. default:
  195. return val;
  196. }
  197. }
  198. else if (typeof typ === "function") {
  199. if (typ === Date) {
  200. return dateParse(val);
  201. }
  202. var info = typeof typ.prototype[FableSymbol.reflection] === "function" ? typ.prototype[FableSymbol.reflection]() : {};
  203. if (info.cases) {
  204. var uCase = void 0, uFields = [];
  205. if (typeof val === "string") {
  206. uCase = val;
  207. }
  208. else if (typeof val.Case === "string" && Array.isArray(val.Fields)) {
  209. uCase = val.Case;
  210. uFields = val.Fields;
  211. }
  212. else {
  213. var caseName = Object.getOwnPropertyNames(val)[0];
  214. var fieldTypes = info.cases[caseName];
  215. if (Array.isArray(fieldTypes)) {
  216. var fields = fieldTypes.length > 1 ? val[caseName] : [val[caseName]];
  217. uCase = caseName;
  218. path = combine(path, caseName);
  219. for (var i = 0; i < fieldTypes.length; i++) {
  220. uFields.push(inflate(fields[i], new List(fieldTypes[i], enclosing), combine(path, i)));
  221. }
  222. }
  223. }
  224. if (uCase in info.cases === false) {
  225. invalidate(val, typ, path);
  226. }
  227. return new typ(uCase, uFields);
  228. }
  229. if (info.properties) {
  230. var newObj = new typ();
  231. var properties = info.properties;
  232. var ks = Object.getOwnPropertyNames(properties);
  233. for (var i = 0; i < ks.length; i++) {
  234. var k = ks[i];
  235. newObj[k] = inflate(val[k], new List(properties[k], enclosing), combine(path, k));
  236. }
  237. return newObj;
  238. }
  239. return val;
  240. }
  241. throw new Error("Unexpected type when deserializing JSON: " + typ);
  242. }
  243. function inflatePublic(val, genArgs) {
  244. return inflate(val, genArgs ? genArgs.T : null, "");
  245. }
  246. export { inflatePublic as inflate };
  247. export function ofJson(json, genArgs) {
  248. return inflate(JSON.parse(json), genArgs ? genArgs.T : null, "");
  249. }
  250. export function toJsonWithTypeInfo(o) {
  251. return JSON.stringify(o, function (k, v) {
  252. if (ArrayBuffer.isView(v)) {
  253. return Array.from(v);
  254. }
  255. else if (v != null && typeof v === "object") {
  256. var typeName = typeof v[FableSymbol.reflection] === "function" ? v[FableSymbol.reflection]().type : null;
  257. if (v instanceof List || v instanceof FableSet || v instanceof Set) {
  258. return {
  259. $type: typeName || "System.Collections.Generic.HashSet",
  260. $values: Array.from(v)
  261. };
  262. }
  263. else if (v instanceof FableMap || v instanceof Map) {
  264. return fold(function (o, kv) { o[kv[0]] = kv[1]; return o; }, { $type: typeName || "System.Collections.Generic.Dictionary" }, v);
  265. }
  266. else if (typeName) {
  267. if (hasInterface(v, "FSharpUnion") || hasInterface(v, "FSharpRecord")) {
  268. return Object.assign({ $type: typeName }, v);
  269. }
  270. else {
  271. var proto = Object.getPrototypeOf(v), props = Object.getOwnPropertyNames(proto), o_1 = { $type: typeName };
  272. for (var i = 0; i < props.length; i++) {
  273. var prop = Object.getOwnPropertyDescriptor(proto, props[i]);
  274. if (prop.get)
  275. o_1[props[i]] = prop.get.apply(v);
  276. }
  277. return o_1;
  278. }
  279. }
  280. }
  281. return v;
  282. });
  283. }
  284. export function ofJsonWithTypeInfo(json, genArgs) {
  285. var parsed = JSON.parse(json, function (k, v) {
  286. if (v == null)
  287. return v;
  288. else if (typeof v === "object" && typeof v.$type === "string") {
  289. var type = v.$type.replace('+', '.'), i = type.indexOf('`');
  290. if (i > -1) {
  291. type = type.substr(0, i);
  292. }
  293. else {
  294. i = type.indexOf(',');
  295. type = i > -1 ? type.substr(0, i) : type;
  296. }
  297. if (type === "System.Collections.Generic.List" || (type.indexOf("[]") === type.length - 2)) {
  298. return v.$values;
  299. }
  300. if (type === "Microsoft.FSharp.Collections.FSharpList") {
  301. return listOfArray(v.$values);
  302. }
  303. else if (type == "Microsoft.FSharp.Collections.FSharpSet") {
  304. return setCreate(v.$values);
  305. }
  306. else if (type == "System.Collections.Generic.HashSet") {
  307. return new Set(v.$values);
  308. }
  309. else if (type == "Microsoft.FSharp.Collections.FSharpMap") {
  310. delete v.$type;
  311. return mapCreate(Object.getOwnPropertyNames(v)
  312. .map(function (k) { return [k, v[k]]; }));
  313. }
  314. else if (type == "System.Collections.Generic.Dictionary") {
  315. delete v.$type;
  316. return new Map(Object.getOwnPropertyNames(v)
  317. .map(function (k) { return [k, v[k]]; }));
  318. }
  319. else {
  320. var T = getType(type);
  321. if (T) {
  322. delete v.$type;
  323. return Object.assign(new T(), v);
  324. }
  325. }
  326. }
  327. else if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:[+-]\d{2}:\d{2}|Z)$/.test(v))
  328. return dateParse(v);
  329. else
  330. return v;
  331. });
  332. var expected = genArgs ? genArgs.T : null;
  333. if (parsed != null && typeof expected === "function"
  334. && !(parsed instanceof getDefinition(expected))) {
  335. throw new Error("JSON is not of type " + expected.name + ": " + json);
  336. }
  337. return parsed;
  338. }