@elbstack/xlsx-populate
Version:
Excel XLSX parser/generator written in JavaScript with Node.js and browser support, jQuery/d3-style method chaining, and a focus on keeping existing workbook features and styles in tact.
89 lines (74 loc) • 2.93 kB
JavaScript
;
const sax = require("sax");
const externals = require("./externals");
// Regex to check if string is all whitespace.
const allWhitespaceRegex = /^\s+$/;
/**
* XML parser.
* @private
*/
class XmlParser {
/**
* Parse the XML text into a JSON object.
* @param {string} xmlText - The XML text.
* @returns {{}} The JSON object.
*/
parseAsync(xmlText) {
return new externals.Promise((resolve, reject) => {
// Create the SAX parser.
const parser = sax.parser(true);
// Parsed is the full parsed object. Current is the current node being parsed. Stack is the current stack of
// nodes leading to the current one.
let parsed, current;
const stack = [];
// On error: Reject the promise.
parser.onerror = reject;
// On text nodes: If it is all whitespace, do nothing. Otherwise, try to convert to a number and add as a child.
parser.ontext = text => {
if (allWhitespaceRegex.test(text)) {
if (current && current.attributes['xml:space'] === 'preserve') {
current.children.push(text);
}
} else {
current.children.push(this._covertToNumberIfNumber(text));
}
};
// On open tag start: Create a child element. If this is the root element, set it as parsed. Otherwise, add
// it as a child to the current node.
parser.onopentagstart = node => {
const child = { name: node.name, attributes: {}, children: [] };
if (current) {
current.children.push(child);
} else {
parsed = child;
}
stack.push(child);
current = child;
};
// On close tag: Pop the stack.
parser.onclosetag = node => {
stack.pop();
current = stack[stack.length - 1];
};
// On attribute: Try to convert the value to a number and add to the current node.
parser.onattribute = attribute => {
current.attributes[attribute.name] = this._covertToNumberIfNumber(attribute.value);
};
// On end: Resolve the promise.
parser.onend = () => resolve(parsed);
// Start parsing the text.
parser.write(xmlText).close();
});
}
/**
* Convert the string to a number if it looks like one.
* @param {string} str - The string to convert.
* @returns {string|number} The number if converted or the string if not.
* @private
*/
_covertToNumberIfNumber(str) {
const num = Number(str);
return num.toString() === str ? num : str;
}
}
module.exports = XmlParser;