UNPKG

@chubbyts/chubbyts-decode-encode

Version:

A simple decode/encode solution for json / jsonx / url-encoded / xml / yaml.

92 lines (91 loc) 3.79 kB
import { XMLParser } from 'fast-xml-parser'; import { throwableToError } from '@chubbyts/chubbyts-throwable-to-error/dist/throwable-to-error'; import { DATATYPE_ARRAY, DATATYPE_BOOLEAN, DATATYPE_NULL, DATATYPE_NUMBER, DATATYPE_OBJECT, DATATYPE_STRING, } from '../jsonx-datatypes.js'; import { DecodeError } from './index.js'; const decodeHtmlEntities = (string) => string.replace(/&#\d+;/gm, (code) => String.fromCharCode(code.match(/\d+/)[0])); const isWithName = (node) => typeof node === 'object' && null !== node && typeof node[':@'] === 'object' && node[':@'] !== null && typeof node[':@']['@_name'] === 'string'; const isObjectNode = (node) => Object.keys(node)[0] === DATATYPE_OBJECT && Array.isArray(node[DATATYPE_OBJECT]); const isArrayNode = (node) => Object.keys(node)[0] === DATATYPE_ARRAY && Array.isArray(node[DATATYPE_ARRAY]); const isStringNode = (node) => Object.keys(node)[0] === DATATYPE_STRING && Array.isArray(node[DATATYPE_STRING]); const isNumberNode = (node) => Object.keys(node)[0] === DATATYPE_NUMBER && Array.isArray(node[DATATYPE_NUMBER]); const isBooleanNode = (node) => Object.keys(node)[0] === DATATYPE_BOOLEAN && Array.isArray(node[DATATYPE_BOOLEAN]); const isNullNode = (node) => Object.keys(node)[0] === DATATYPE_NULL && Array.isArray(node[DATATYPE_NULL]); const convertNullNode = () => { return null; }; const convertBooleanNode = (node) => { return node['json:boolean'][0]['#text']; }; const convertNumberNode = (node) => { return node['json:number'][0]['#text']; }; const convertStringNode = (node) => { return decodeHtmlEntities(node['json:string'][0]['#text']); }; const convertNode = (node) => { if (isObjectNode(node)) { return convertObjectNode(node); } else if (isArrayNode(node)) { return convertArrayNode(node); } else if (isStringNode(node)) { return convertStringNode(node); } else if (isNumberNode(node)) { return convertNumberNode(node); } else if (isBooleanNode(node)) { return convertBooleanNode(node); } else if (isNullNode(node)) { return convertNullNode(); } throw new Error(`Unsupported node: ${JSON.stringify(node)}`); }; const convertArrayNode = (node) => { return node['json:array'].map(convertNode); }; const convertNodeWithName = (node) => { if (isObjectNode(node) && isWithName(node)) { return [node[':@']['@_name'], convertObjectNode(node)]; } else if (isArrayNode(node) && isWithName(node)) { return [node[':@']['@_name'], convertArrayNode(node)]; } else if (isStringNode(node) && isWithName(node)) { return [node[':@']['@_name'], convertStringNode(node)]; } else if (isNumberNode(node) && isWithName(node)) { return [node[':@']['@_name'], convertNumberNode(node)]; } else if (isBooleanNode(node) && isWithName(node)) { return [node[':@']['@_name'], convertBooleanNode(node)]; } else if (isNullNode(node) && isWithName(node)) { return [node[':@']['@_name'], convertNullNode()]; } throw new Error(`Unsupported node: ${JSON.stringify(node)}`); }; const convertObjectNode = (node) => { return Object.fromEntries(node['json:object'].map(convertNodeWithName)); }; export const createJsonxTypeDecoder = () => { return { decode: (encodedData) => { try { const parser = new XMLParser({ preserveOrder: true, ignoreAttributes: false, htmlEntities: false }); return convertNode(parser.parse(encodedData)[1]); } catch (e) { const error = throwableToError(e); throw new DecodeError(error.message, error.stack); } }, contentType: 'application/jsonx+xml', }; };