UNPKG

mini-dom-parser

Version:

Un DOMParser minimalista sin dependencias, compatible con xmldom para casos simples.

120 lines (102 loc) 3.25 kB
// ---------------- DOM CORE ---------------- class Node { constructor(nodeType, nodeName, nodeValue = null) { this.nodeType = nodeType; // 1 = element, 3 = text, 9 = document this.nodeName = nodeName; this.nodeValue = nodeValue; this.childNodes = []; this.attributes = {}; this.parentNode = null; } appendChild(node) { node.parentNode = this; this.childNodes.push(node); } getElementsByTagName(tagName) { let results = []; function traverse(node) { if (node.nodeType === 1 && node.nodeName === tagName) results.push(node); for (const child of node.childNodes) traverse(child); } traverse(this); return results; } } class Document extends Node { constructor() { super(9, "#document"); } } class DOMParser { parseFromString(str, mimeType) { if (!mimeType || !mimeType.includes("xml")) { throw new Error("Unsupported mimeType: " + mimeType); } const doc = new Document(); str = str.replace(/<\?xml.*?\?>/, ""); const tagRegex = /<([^!?\/\s>]+)([^>]*)>|<\/([^>]+)>|([^<]+)/g; let stack = [doc]; let match; while ((match = tagRegex.exec(str))) { if (match[1]) { const nodeName = match[1].includes(":") ? match[1].split(":")[1] : match[1]; const node = new Node(1, nodeName); const attrs = match[2]?.trim(); if (attrs) { const attrRegex = /(\S+)=["']([^"']+)["']/g; let attr; while ((attr = attrRegex.exec(attrs))) { node.attributes[attr[1]] = attr[2]; } } stack[stack.length - 1].appendChild(node); stack.push(node); } else if (match[3]) { if (stack.length > 1) stack.pop(); } else if (match[4] && match[4].trim()) { const textNode = new Node(3, "#text", match[4].trim()); stack[stack.length - 1].appendChild(textNode); } } return doc; } } // ---------------- XML → JSON HELPER ---------------- function nodeToJson(node) { if (node.nodeType === 3) return node.nodeValue; if (node.nodeType === 1) { let obj = {}; for (const child of node.childNodes) { const name = child.nodeType === 1 ? child.nodeName : "#text"; const value = nodeToJson(child); if (name === "#text") { if (!obj) return value; if (!obj.value) obj.value = value; else obj.value += " " + value; } else { if (!obj[name]) obj[name] = value; else { if (!Array.isArray(obj[name])) obj[name] = [obj[name]]; obj[name].push(value); } } } // simplificar nodo de solo texto if (Object.keys(obj).length === 1 && obj.value !== undefined) return obj.value; return obj; } if (node.nodeType === 9) { let result = {}; for (const child of node.childNodes) result[child.nodeName] = nodeToJson(child); return result; } return null; } function xmlToJson(xmlString) { const parser = new DOMParser(); const doc = parser.parseFromString(xmlString, "text/xml"); return nodeToJson(doc); } export { DOMParser, xmlToJson };