JavaScript Python PHP
import * as __biblioteca0 from "saxes";
function executar(input = '<root><usuario id="00123"><nome>João</nome></usuario></root>') {
const SaxesParser = __biblioteca0["SaxesParser"];
function xmlCharacters(value) {
for (const character of value) {
const cp = character.codePointAt(0);
if (!(cp === 9 || cp === 10 || cp === 13 || (cp >= 32 && cp <= 0xD7FF) || (cp >= 0xE000 && cp <= 0xFFFD) || (cp >= 0x10000 && cp <= 0x10FFFF)))
throw new Error('Caractere não permitido pelo XML 1.0.');
}
}
function xmlToJson(input) {
if (!input.trim())
throw new Error('Informe um documento XML.');
if (input.length > 1000000)
throw new Error('A entrada excede o limite de 1 milhão de caracteres.');
xmlCharacters(input);
const parser = new SaxesParser({ defaultXMLVersion: '1.0', forceXMLVersion: true });
const stack = [];
let result;
let count = 0;
function fail(message) { throw new Error(`${message} (linha ${parser.line}, coluna ${parser.column}).`); }
parser.on('error', error => fail(`XML inválido: ${error.message}`));
parser.on('xmldecl', declaration => {
if (declaration.version !== '1.0')
fail('Somente XML 1.0 é suportado');
});
parser.on('doctype', () => fail('DTDs e entidades personalizadas não são permitidos; recursos externos não são carregados'));
parser.on('opentag', tag => {
if (++count > 100000 || stack.length >= 100)
fail('Documento excede o limite de 100 níveis ou 100 mil elementos');
if (tag.name.includes(':') || Object.keys(tag.attributes).some(name => name === 'xmlns' || name.includes(':')))
fail('Namespaces não são suportados nesta etapa');
const attributes = Object.create(null);
for (const [key, value] of Object.entries(tag.attributes))
attributes[key] = value;
stack.push({ name: tag.name, attributes, text: '', cdata: false, children: [] });
});
parser.on('text', text => {
if (stack.length)
stack[stack.length - 1].text += text;
});
parser.on('cdata', text => {
if (stack.length) {
stack[stack.length - 1].text += text;
if (text)
stack[stack.length - 1].cdata = true;
}
});
parser.on('closetag', () => {
const node = stack.pop();
const hasAttributes = Object.keys(node.attributes).length > 0;
let value;
if (!node.children.length && !hasAttributes)
value = node.text;
else {
const object = Object.create(null);
if (hasAttributes)
object['@attributes'] = node.attributes;
if (!node.children.length)
object['#text'] = node.text;
else {
if (node.cdata || /[^\t\r\n ]/.test(node.text))
fail('Conteúdo misto não é suportado: texto junto a elementos filhos perde a ordem');
let previous = '';
const seen = new Set();
for (const [name, child] of node.children) {
if (name !== previous && seen.has(name))
fail('Elementos repetidos intercalados não são suportados: a ordem seria perdida');
if (Object.hasOwn(object, name)) {
const prior = object[name];
if (Array.isArray(prior))
prior.push(child);
else
object[name] = [prior, child];
}
else
object[name] = child;
seen.add(name);
previous = name;
}
}
value = object;
}
if (stack.length)
stack[stack.length - 1].children.push([node.name, value]);
else {
result = Object.create(null);
result[node.name] = value;
}
});
parser.write(input).close();
if (!result)
throw new Error('XML deve conter um elemento raiz.');
return JSON.stringify(result, null, 2);
}
return xmlToJson(input);
}
import json
from xml.parsers import expat
def executar(input='<root><usuario id="00123"><nome>João</nome></usuario></root>'):
if not input.strip() or len(input.encode("utf-16-le", errors="surrogatepass")) // 2 > 1000000:
raise ValueError("Informe XML com até 1 milhão de caracteres.")
parser = expat.ParserCreate()
stack, result = [], {}
count = 0
in_cdata = False
def fail(message): raise ValueError(f"{message} (linha {parser.CurrentLineNumber}, coluna {parser.CurrentColumnNumber + 1}).")
def opened(name, attributes):
nonlocal count
count += 1
if count > 100000 or len(stack) >= 100: fail("Limite de 100 níveis ou 100 mil elementos.")
if ":" in name or any(key == "xmlns" or ":" in key for key in attributes): fail("Namespaces não são suportados.")
stack.append({"name":name, "attributes":attributes, "text":"", "cdata":False, "children":[]})
def text(value):
if stack:
stack[-1]["text"] += value
if in_cdata and value: stack[-1]["cdata"] = True
def cdata_start():
nonlocal in_cdata
in_cdata = True
def cdata_end():
nonlocal in_cdata
in_cdata = False
def closed(name):
node = stack.pop()
children, attributes = node["children"], node["attributes"]
if not children and not attributes: value = node["text"]
else:
value = {}
if attributes: value["@attributes"] = attributes
if not children: value["#text"] = node["text"]
else:
if node["cdata"] or node["text"].strip(" \t\r\n"): fail("Conteúdo misto não é suportado.")
previous = None
for child, item in children:
if child in value:
if previous != child: fail("Elementos repetidos intercalados perdem a ordem.")
if not isinstance(value[child], list): value[child] = [value[child]]
value[child].append(item)
else: value[child] = item
previous = child
if stack: stack[-1]["children"].append((name, value))
else: result[name] = value
def declaration(version, encoding, standalone):
if version != "1.0": fail("Somente XML 1.0 é suportado.")
parser.XmlDeclHandler = declaration
parser.StartElementHandler, parser.EndElementHandler = opened, closed
parser.CharacterDataHandler = text
parser.StartCdataSectionHandler, parser.EndCdataSectionHandler = cdata_start, cdata_end
parser.StartDoctypeDeclHandler = lambda *args: fail("DTDs e entidades personalizadas não são permitidos.")
parser.ExternalEntityRefHandler = lambda *args: fail("Recursos externos não são carregados.")
try: parser.Parse(input, True)
except expat.ExpatError as error: fail("XML inválido: " + str(error))
return json.dumps(result, ensure_ascii=False, indent=2)
<?php
function executar($input = '<root><usuario id="00123"><nome>João</nome></usuario></root>') {
if (trim($input) === '' || !mb_check_encoding($input, 'UTF-8') || mb_strlen(mb_convert_encoding($input, 'UTF-16LE', 'UTF-8'), 'UTF-16LE') > 1000000) throw new InvalidArgumentException('Informe XML UTF-8 com até 1 milhão de caracteres.');
// A conservative preflight blocks declarations before libxml can resolve them.
if (str_contains($input, '<!DOCTYPE') || str_contains($input, '<!ENTITY')) throw new InvalidArgumentException('DTDs e entidades personalizadas não são permitidos.');
$previous = libxml_use_internal_errors(true);
$document = new DOMDocument('1.0', 'UTF-8');
try {
$ok = $document->loadXML($input, LIBXML_NONET);
$errors = libxml_get_errors();
if (!$ok || $errors) {
$error = $errors[0] ?? null;
throw new InvalidArgumentException('XML inválido'.($error ? ' (linha '.$error->line.', coluna '.$error->column.'): '.trim($error->message) : '.'));
}
} finally { libxml_clear_errors(); libxml_use_internal_errors($previous); }
if ($document->xmlVersion !== '1.0') throw new InvalidArgumentException('Somente XML 1.0 é suportado.');
$count = 0;
$convert = function ($node, $depth = 0) use (&$convert, &$count) {
if (++$count > 100000 || $depth >= 100) throw new InvalidArgumentException('Limite de profundidade ou elementos.');
if ($node->namespaceURI !== null || str_contains($node->nodeName, ':') || $node->hasAttribute('xmlns') || (new DOMXPath($node->ownerDocument))->query('namespace::*', $node)->length > 1) throw new InvalidArgumentException('Namespaces não são suportados.');
$attributes = new stdClass();
foreach ($node->attributes as $attribute) {
if ($attribute->name === 'xmlns' || str_contains($attribute->nodeName, ':')) throw new InvalidArgumentException('Namespaces não são suportados.');
$attributes->{$attribute->name} = $attribute->value;
}
$text = ''; $children = []; $cdata = false;
foreach ($node->childNodes as $child) {
if ($child->nodeType === XML_ELEMENT_NODE) $children[] = $child;
elseif ($child->nodeType === XML_TEXT_NODE || $child->nodeType === XML_CDATA_SECTION_NODE) {
$text .= $child->nodeValue;
if ($child->nodeType === XML_CDATA_SECTION_NODE && $child->nodeValue !== '') $cdata = true;
}
}
if (!$children && !(array)$attributes) return $text;
$value = new stdClass();
if ((array)$attributes) $value->{'@attributes'} = $attributes;
if (!$children) $value->{'#text'} = $text;
else {
if ($cdata || trim($text, " \t\r\n") !== '') throw new InvalidArgumentException('Conteúdo misto não é suportado.');
$previous = '';
foreach ($children as $child) {
$name = $child->nodeName; $item = $convert($child, $depth + 1);
if (property_exists($value, $name)) {
if ($previous !== $name) throw new InvalidArgumentException('Elementos repetidos intercalados perdem a ordem.');
if (!is_array($value->{$name})) $value->{$name} = [$value->{$name}];
$value->{$name}[] = $item;
} else $value->{$name} = $item;
$previous = $name;
}
}
return $value;
};
$result = new stdClass(); $result->{$document->documentElement->nodeName} = $convert($document->documentElement);
return preg_replace_callback('/^( +)/m', fn($match) => str_repeat(' ', strlen($match[1]) / 2), json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR));
}