@chubbyts/chubbyts-decode-encode
Version:
A simple decode/encode solution for json / jsonx / url-encoded / xml / yaml.
92 lines (91 loc) • 3.79 kB
JavaScript
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',
};
};