UNPKG

serializr

Version:

Serialize and deserialize complex object graphs to JSON

74 lines (71 loc) 3.04 kB
import { invariant, isAliasedPropSchema, isPropSchema, isMapLike, processAdditionalPropArgs, MapLike, } from "../utils/utils"; import { _defaultPrimitiveProp } from "../constants"; import list from "./list"; import { PropSchema, AdditionalPropArgs } from "../api/types"; /** * Similar to list, but map represents a string keyed dynamic collection. * This can be both plain objects (default) or ES6 Map like structures. * This will be inferred from the initial value of the targetted attribute. * * For `Map`s which are not string-keyed, check out `mapAsArray`. * * @param additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers */ export default function map( propSchema: PropSchema, additionalArgs?: AdditionalPropArgs ): PropSchema { propSchema = propSchema || _defaultPrimitiveProp; invariant(isPropSchema(propSchema), "expected prop schema as first argument"); invariant( !isAliasedPropSchema(propSchema), "provided prop is aliased, please put aliases first" ); let result: PropSchema = { serializer: function (m: Map<any, any> | { [key: string]: any }) { invariant(m && typeof m === "object", "expected object or Map"); const result2: { [key: string]: any } = {}; if (isMapLike(m)) { m.forEach((value, key) => (result2[key] = propSchema.serializer(value, key, m))); } else { for (const key in m) result2[key] = propSchema.serializer(m[key], key, m); } return result2; }, deserializer: function (jsonObject, done, context, oldValue: MapLike | Record<any, any>) { if (!jsonObject || typeof jsonObject !== "object") return void done("[serializr] expected JSON object"); const keys = Object.keys(jsonObject); list(propSchema, additionalArgs).deserializer( keys.map((key) => jsonObject[key]), function (err, values) { if (err) return void done(err); const isMap = isMapLike(oldValue); let newValue: MapLike | Record<any, any>; if (isMap) { // if the oldValue is a map, we recycle it // there are many variations and this way we don't have to // know about the original constructor oldValue.clear(); newValue = oldValue; } else newValue = {}; for (let i = 0, l = keys.length; i < l; i++) { if (isMap) newValue.set(keys[i], values[i]); else (newValue as Record<any, any>)[keys[i]] = values[i]; } done(null, newValue); }, context ); }, }; result = processAdditionalPropArgs(result, additionalArgs); return result; }