UNPKG

fast-xml-parser

Version:

Validate XML, Parse XML, Build XML without C/C++ based libraries

155 lines (126 loc) 4.02 kB
import { buildOptions, registerCommonValueParsers } from './ParserOptionsBuilder.js'; export default class OutputBuilder { constructor(builderOptions) { this.options = buildOptions(builderOptions); this.registeredParsers = registerCommonValueParsers(this.options); } registerValueParser(name, parserInstance) {//existing name will override the parser without warning this.registeredParsers[name] = parserInstance; } getInstance(parserOptions) { return new JsObjBuilder(parserOptions, this.options, this.registeredParsers); } } import BaseOutputBuilder from './BaseOutputBuilder.js'; const rootName = '^'; class JsObjBuilder extends BaseOutputBuilder { constructor(parserOptions, builderOptions, registeredParsers) { super(); //hold the raw detail of a tag and sequence with reference to the output this.tagsStack = []; this.parserOptions = parserOptions; this.options = builderOptions; this.registeredParsers = registeredParsers; this.root = {}; this.parent = this.root; this.tagName = rootName; this.value = {}; this.textValue = ""; this.attributes = {}; } addTag(tag) { let value = ""; if (!isEmpty(this.attributes)) { value = {}; if (this.options.attributes.groupBy) { value[this.options.attributes.groupBy] = this.attributes; } else { value = this.attributes; } } this.tagsStack.push([this.tagName, this.textValue, this.value]); //parent tag, parent text value, parent tag value (jsobj) this.tagName = tag.name; this.value = value; this.textValue = ""; this.attributes = {}; } /** * Check if the node should be added by checking user's preference * @param {Node} node * @returns boolean: true if the node should not be added */ closeTag() { const tagName = this.tagName; let value = this.value; let textValue = this.textValue; //update tag text value if (typeof value !== "object" && !Array.isArray(value)) { value = this.parseValue(textValue.trim(), this.options.tags.valueParsers); } else if (textValue.length > 0) { value[this.options.nameFor.text] = this.parseValue(textValue.trim(), this.options.tags.valueParsers); } let resultTag = { tagName: tagName, value: value }; if (this.options.onTagClose !== undefined) { //TODO TagPathMatcher resultTag = this.options.onClose(tagName, value, this.textValue, new TagPathMatcher(this.tagsStack, node)); if (!resultTag) return; } //set parent node in scope let arr = this.tagsStack.pop(); let parentTag = arr[2]; parentTag = this._addChildTo(resultTag.tagName, resultTag.value, parentTag); this.tagName = arr[0]; this.textValue = arr[1]; this.value = parentTag; } _addChild(key, val) { if (typeof this.value === "string") { this.value = { [this.options.nameFor.text]: this.value }; } this._addChildTo(key, val, this.value); // this.currentNode.leafType = false; this.attributes = {}; } _addChildTo(key, val, node) { if (typeof node === 'string') node = {}; if (!node[key]) { node[key] = val; } else { //Repeated if (!Array.isArray(node[key])) { //but not stored as array node[key] = [node[key]]; } node[key].push(val); } return node; } /** * Add text value child node * @param {string} text */ addValue(text) { //TODO: use bytes join if (this.textValue.length > 0) this.textValue += " " + text; else this.textValue = text; } addPi(name) { let value = ""; if (!isEmpty(this.attributes)) { value = {}; if (this.options.attributes.groupBy) { value[this.options.attributes.groupBy] = this.attributes; } else { value = this.attributes; } } this._addChild(name, value); } getOutput() { return this.value; } } function isEmpty(obj) { return Object.keys(obj).length === 0; }