@hendt/xml2json
Version:
Pure javascript XML to JSON converter with zero dependencies.
173 lines • 6.73 kB
JavaScript
;
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