UNPKG

@hendt/xml2json

Version:

Pure javascript XML to JSON converter with zero dependencies.

173 lines 6.73 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; Object.defineProperty(exports, "__esModule", { value: true }); var defaultOptions = { aloneValueName: '_@attribute' }; /** * Main function. Clears the given xml and then starts the recursion * @param xmlStr * @param options the options */ function xml2json(xmlStr, options) { if (options === void 0) { options = defaultOptions; } var opt = __assign(__assign({}, defaultOptions), options); xmlStr = cleanXML(xmlStr, opt.aloneValueName); return xml2jsonRecurse(xmlStr, opt); } exports.default = xml2json; /** * Recursive function that creates a JSON object with a given XML string. * * @param xmlStr * @param options the options */ function xml2jsonRecurse(xmlStr, options) { var obj = {}; var startTagMatch; while ((startTagMatch = xmlStr.match(/<[^\/][^>]*>/))) { var openingTag = startTagMatch[0]; var tagName = openingTag.substring(1, openingTag.length - 1); var indexClosingTag = xmlStr.indexOf(openingTag.replace('<', '</')); // account for case where additional information in the opening tag var closingTagMatch = void 0; if (indexClosingTag == -1 && (closingTagMatch = openingTag.match(/[^<][\S+$]*/))) { tagName = closingTagMatch[0]; indexClosingTag = xmlStr.indexOf('</' + tagName); if (indexClosingTag == -1) { indexClosingTag = xmlStr.indexOf('<\\/' + tagName); } } var inner_substring = xmlStr.substring(openingTag.length, indexClosingTag); var tempVal = inner_substring.match(/<[^\/][^>]*>/) ? xml2json(inner_substring, options) : inner_substring; // account for array or obj if (obj[tagName] === undefined) { obj[tagName] = tempVal; } else if (Array.isArray(obj[tagName])) { obj[tagName].push(tempVal); } else { obj[tagName] = [obj[tagName], tempVal]; } xmlStr = xmlStr.substring(openingTag.length * 2 + 1 + inner_substring.length); } return obj; } /** * Removes some characters that would break the recursive function. * * @param xmlStr * @param aloneValueName * @returns {string} */ function cleanXML(xmlStr, aloneValueName) { xmlStr = xmlStr.replace(/<!--[\s\S]*?-->/g, ''); // remove commented lines xmlStr = xmlStr.replace(/[\n\t\r]/g, ''); // replace special characters xmlStr = xmlStr.replace(/>[ \t]+</g, '><'); // replace leading spaces and tabs betweet elements xmlStr = xmlStr.replace(/<\?[^>]*\?>/g, ''); // delete docType tags xmlStr = replaceSelfClosingTags(xmlStr); // replace self closing tags xmlStr = replaceAloneValues(xmlStr, aloneValueName); // replace the alone tags values xmlStr = replaceAttributes(xmlStr); // replace attributes return xmlStr; } /** * Replaces all the self closing tags with attributes with another tag containing its attribute as a property. * The function works if the tag contains multiple attributes. * Example : '<tagName attrName="attrValue" />' becomes * '<tagName><attrName>attrValue</attrName></tagName>' * @param xmlStr * @returns {*} */ function replaceSelfClosingTags(xmlStr) { var selfClosingTags = xmlStr.match(/<[^/][^>]*\/>/g); if (!selfClosingTags) { return xmlStr; } for (var i = 0; i < selfClosingTags.length; i++) { var oldTag = selfClosingTags[i]; var match = oldTag.match(/[^<][\S+$]*/); if (match) { var tagName = match[0]; var closingTag = "</" + tagName + ">"; var newTag = extractAttributeValue(tagName, oldTag) + closingTag; xmlStr = xmlStr.replace(oldTag, newTag); } } return xmlStr; } /** * Replaces all the tags with attributes and a value with a new tag. * * Example : '<tagName attrName="attrValue">tagValue</tagName>' becomes * '<tagName><attrName>attrValue</attrName><_@attribute>tagValue</_@attribute></tagName>' * * @param xmlStr * @param aloneValueName * @returns {string} */ function replaceAloneValues(xmlStr, aloneValueName) { var tagsWithAttributesAndValue = xmlStr.match(/<[^\/][^>][^<]+\s+.[^<]+[=][^<]+>([^<]+)/g); if (!tagsWithAttributesAndValue) { return xmlStr; } for (var i = 0; i < tagsWithAttributesAndValue.length; i++) { var oldTag = tagsWithAttributesAndValue[i]; var oldTagName = oldTag.substring(0, oldTag.indexOf(">") + 1); var oldTagValue = oldTag.substring(oldTag.indexOf(">") + 1); var newTag = oldTagName + "<" + aloneValueName + ">" + oldTagValue + "</" + aloneValueName + ">"; xmlStr = xmlStr.replace(oldTag, newTag); } return xmlStr; } function extractAttributeValue(tagName, oldTag) { var newTag = "<" + tagName + ">"; var attrs = oldTag.match(/(\S+)\s?=\s?((?:"[^"]+")|(?:'[^']+'))/g); if (!attrs) { return newTag; } for (var j = 0; j < attrs.length; j++) { var attr = attrs[j]; var attrName = attr.substring(0, attr.indexOf('=')).trim(); var quote = attr[attr.length - 1]; var attrValue = attr.substring(attr.indexOf(quote) + 1, attr.lastIndexOf(quote)); newTag += "<" + attrName + ">" + attrValue + "</" + attrName + ">"; } return newTag; } /** * Replaces all the tags with attributes with another tag containing its attribute as a property. * The function works if the tag contains multiple attributes. * * Example : '<tagName attrName="attrValue"></tagName>' becomes '<tagName><attrName>attrValue</attrName></tagName>' * * @param xmlStr * @returns {*} */ function replaceAttributes(xmlStr) { var tagsWithAttributes = xmlStr.match(/<[^\/><]\S+\s+[^<]+[=][^<]+>/g); if (!tagsWithAttributes) { return xmlStr; } for (var i = 0; i < tagsWithAttributes.length; i++) { var oldTag = tagsWithAttributes[i]; var match = oldTag.match(/[^<]\S*/); if (match) { var tagName = match[0]; var newTag = extractAttributeValue(tagName, oldTag); xmlStr = xmlStr.replace(oldTag, newTag); } } return xmlStr; } //# sourceMappingURL=index.js.map