UNPKG

@mos-connection/helper

Version:

Helper functions for the MOS-connection library

244 lines 10.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.xmlToObject = exports.addTextElementInternal = exports.addTextElement = exports.xml2js = void 0; const xml_js_1 = require("xml-js"); const model_1 = require("@mos-connection/model"); function xml2js(messageString) { const object = (0, xml_js_1.xml2js)(messageString, { compact: false, trim: true, nativeType: false, // we want to NOT auto-convert types, to avoid ambiguity }); // common tags we typically want to know the order of the contents of: const orderedTags = new Set(['storyBody', 'mosAbstract', 'description', 'p', 'em', 'span', 'h1', 'h2', 'i', 'b']); /** * Doing a post-order tree traversal we try to make the objectified tree as compact as possible. * Whenever we find an "orderedTag" we keep the order of it's children. * * ps: post-order means we make a node's children as compact as possible first, and then try to make * that node compact. */ const concatChildrenAndTraverseObject = (element) => { if (element.name) { element.$name = element.name; delete element.name; } if (element.type) { element.$type = element.type; delete element.type; } if (element.elements) { if (element.elements.length === 1) { concatChildrenAndTraverseObject(element.elements[0]); const childEl = element.elements[0]; const name = childEl.$name ?? childEl.$type ?? 'unknownElement'; if (childEl.$type && childEl.$type === 'text') { if (childEl.$name === undefined) { // pure text node, hoist it up: element.$type = 'text'; element.text = childEl.text; } else { // leave it as is } } else { delete childEl.$name; delete childEl.$type; element[name] = element.elements[0]; } delete element.elements; if (childEl.$type === 'text') { element[name] = childEl.text; if (childEl.attributes) { for (const key in childEl.attributes) { element[key] = childEl.attributes[key]; } delete childEl.attributes; } } } else if (element.elements.length > 1) { for (const childEl of element.elements) { concatChildrenAndTraverseObject(childEl); } if (!orderedTags.has(element.$name)) { // if the element name is contained in the set of orderedTag names we don't make it any more compact const names = element.elements.map((obj) => obj.$name || obj.$type || 'unknownElement'); const namesSet = new Set(names); if (namesSet.size === 1 && names.length !== 1 && !namesSet.has('type') && !namesSet.has('name')) { // make array compact: const array = []; for (const childEl of element.elements) { if (childEl.$type && childEl.$type === 'text') { if (Object.keys(childEl).length > 2) { array.push(childEl); } else if (childEl.attributes) { childEl.attributes.text = childEl.text; array.push(childEl.attributes); } else { array.push(childEl.text); } } else { if (childEl.$type) delete childEl.$type; if (childEl.$name) delete childEl.$name; if (Object.keys(childEl).length > 1) { // might contain something useful like attributes if (childEl.attributes) { for (const key in childEl.attributes) { childEl[key] = childEl.attributes[key]; } delete childEl.attributes; } array.push(childEl); } else { array.push(childEl[Object.keys(childEl)[0]]); } } } element[names[0]] = array; delete element.elements; } else if (names.length === namesSet.size) { // all elements are unique for (const childEl of element.elements) { if (childEl.$type && childEl.$type === 'text' && (Object.keys(childEl).length <= 3 || (!childEl.$name && Object.keys(childEl).length < 3))) { if (!childEl.text) { element.text = childEl.text; } element[childEl.$name] = childEl.text; } else { if (childEl.attributes) { for (const key in childEl.attributes) { childEl[key] = childEl.attributes[key]; } delete childEl.attributes; } const name = childEl.$name || childEl.$type || 'unknownEl'; if (childEl.$type) delete childEl.$type; if (childEl.$name) delete childEl.$name; element[name] = childEl; } } delete element.elements; } else if (names.length !== namesSet.size) { const holder = {}; for (let childEl of element.elements) { const name = childEl.$name; if (childEl.$type === 'text' && Object.keys(childEl).length <= 3) { childEl = childEl.text; } else if (childEl.attributes) { for (const key in childEl.attributes) { childEl[key] = childEl.attributes[key]; } delete childEl.attributes; } else { if (childEl.$type) delete childEl.$type; if (childEl.$name) delete childEl.$name; } if (holder[name]) { holder[name].push(childEl); } else { holder[name] = [childEl]; } } for (const key in holder) { element[key] = holder[key].length > 1 ? holder[key] : holder[key][0]; } delete element.elements; } } } } }; concatChildrenAndTraverseObject(object); return object; } exports.xml2js = xml2js; function addTextElement(root, elementName, text, attributes, strict = true) { return addTextElementInternal(root, elementName, text, attributes, strict); } exports.addTextElement = addTextElement; function addTextElementInternal(root, elementName, content, attributes, strict) { const mosTypes = (0, model_1.getMosTypes)(strict); let txt; if (content === null) { txt = null; } else if (content !== undefined) { const stringified = (0, model_1.stringifyMosType)(content, mosTypes); if (stringified.isMosType) txt = stringified.stringValue; else txt = content.toString(); } else { txt = undefined; } const element = root.element(elementName, attributes || {}, txt); return element; } exports.addTextElementInternal = addTextElementInternal; /** * Utility-function to convert a XMLBuilder.XMLElement into the generic object which can be sent * into the ***.fromXML(xml:any) methods in MosModel */ function xmlToObject(root) { const obj = {}; let hasAttribs = false; if (root.attribs) { for (const attr of Object.values(root.attribs)) { hasAttribs = true; if (!obj.attributes) obj.attributes = {}; obj.attributes[attr.name] = attr.value; } } // @ts-expect-error hack if (root.children.length === 1 && root.children[0].name === '#text') { if (hasAttribs) { // @ts-expect-error hack obj.text = root.children[0].value; return obj; } else { // @ts-expect-error hack return root.children[0].value; } } for (const child of root.children) { if (child.name) { const ch = child; if (obj[ch.name]) { if (!Array.isArray(obj[ch.name])) { obj[ch.name] = [obj[ch.name]]; // make an array } obj[ch.name].push(xmlToObject(ch)); } else { obj[ch.name] = xmlToObject(ch); } } } return obj; } exports.xmlToObject = xmlToObject; //# sourceMappingURL=Utils.js.map