UNPKG

rapid-xml-to-json

Version:

Fast, dependency-free XML-parser for parsing XML structures into JSON. Optimised for speed and low memory usage.

161 lines (158 loc) 6.61 kB
'use strict'; function xmlToJson(xml) { var o = {}; var cur = o; var curName = ''; var curContentList = []; var hasContent = false; for (var i = 0; i < xml.length; i++) { var curChar = xml[i]; if (curChar === '<') { // Inside an opening or closing tag if (xml[i + 1] === '/') { // Inside a closing tag cur = cur.__parent; if (hasContent) { // Insert content instead of current object if (cur[curName] instanceof Array) { var lastIdx = cur[curName].length - 1; cur[curName][lastIdx]['#text'] = ''; for (var i_1 = 0; i_1 < curContentList.length; i_1++) { var curContent = curContentList[i_1]; cur[curName][lastIdx]['#text'] += xml.slice(curContent[0], curContent[1]); } } else { cur[curName]['#text'] = ''; for (var i_2 = 0; i_2 < curContentList.length; i_2++) { var curContent = curContentList[i_2]; cur[curName]['#text'] += xml.slice(curContent[0], curContent[1]); } } } while (xml[i] !== '>') { i++; } curName = ''; } else if (xml[i + 1] === '?' || xml[i + 1] === '!') { // Ignore processing instructions, stylesheets, DTD validation, if (xml[i + 1] === '!') { // Handle declarations // TODO: Create skipUntil utility function if (xml.startsWith('<!DOCTYPE', i)) { // Doctype i += 9; while (xml[i] !== '>') { i++; } } else if (xml.startsWith('<!--', i)) { // Comment i += 4; while (!xml.startsWith('-->', i)) { i++; } i += 2; } else if (xml.startsWith('<![CDATA[', i)) { // Handle CDATA i += 9; var contentFrom = i; while (!xml.startsWith(']]>', i)) { i++; } var contentTo = i; curContentList.push([contentFrom, contentTo]); i += 2; hasContent = true; continue; // Skip hasContent = false } } else { // Ignore processing instructions and stylesheets while (xml[i] !== '>') { i++; } } } else { // Opening // Handle next i++; // Move to first char var name_1 = ''; // Create a name while (xml[i] !== ' ' && xml[i] !== '>' && xml[i] !== '/') { // Go until attributes or closing. TODO: Handle XML attributes name_1 += xml[i]; i++; } var isSelfClosing = false; var attrs = []; while (xml[i] !== '>') { // Go until closing tag i++; if (xml[i] !== ' ' && xml[i] !== '\n' && xml[i] !== '>' && xml[i] !== '/') { var attrNameStart = i; var attrNameEnd = -1; while (xml[i] !== '=') { i++; } attrNameEnd = i; i += 2; // Skip =" var attrValueStart = i; while (xml[i] !== '"' || xml[i - 1] === '\\') { i++; } attrs.push([attrNameStart, attrNameEnd, attrValueStart, i]); } } if (xml[i - 1] === '/') { isSelfClosing = true; } if (cur[name_1]) { // Already defined, create an array or add to existing array if (!(cur[name_1] instanceof Array)) { cur[name_1] = [cur[name_1]]; } var next = {}; cur[name_1].push(next); next.__parent = cur; if (!isSelfClosing) { cur = next; curName = name_1; } } else { // Not defined yet. Create new property on json object cur[name_1] = {}; cur[name_1].__parent = cur; if (!isSelfClosing) { cur = cur[name_1]; curName = name_1; } } if (attrs.length) { if (isSelfClosing) { // If it's self-closing with attributes, hop down into it if (cur[name_1] instanceof Array) { cur = cur[name_1][cur[name_1].length - 1]; } else { cur = cur[name_1]; } } cur['$attrs'] = {}; for (var i_3 = 0; i_3 < attrs.length; i_3++) { cur['$attrs'][xml.slice(attrs[i_3][0], attrs[i_3][1])] = xml.slice(attrs[i_3][2], attrs[i_3][3]); } if (isSelfClosing) { // And hop back up cur = cur.__parent; } } } hasContent = false; curContentList = []; } else if (curName) { // Inside curName var contentFrom = i; // TODO: Handle mixed content while (xml[i] !== '<') { if (!hasContent && (xml[i] !== ' ' && xml[i] !== '\n')) { hasContent = true; } i++; } var contentTo = i; i--; curContentList.push([contentFrom, contentTo]); // TODO: Trim spaces } } return o; } exports.xmlToJson = xmlToJson;