Trabalhando com JSON e XML

1. Introdução aos formatos JSON e XML

JSON (JavaScript Object Notation) e XML (eXtensible Markup Language) são dois dos formatos de intercâmbio de dados mais utilizados no desenvolvimento web. Enquanto o JSON é leve, baseado em texto e nativamente compatível com JavaScript, o XML oferece uma estrutura mais robusta com suporte a namespaces, schemas e validação formal.

Em aplicações PHP, a escolha entre JSON e XML depende do contexto: JSON é ideal para APIs RESTful, comunicação com JavaScript e armazenamento de configurações simples. XML é preferível para documentos complexos, integração com sistemas legados e quando validação rigorosa é necessária.

O PHP oferece funções nativas para ambos os formatos: json_encode()/json_decode() para JSON, e simplexml_load_string()/simplexml_load_file() para XML, além da poderosa extensão DOMDocument para manipulação avançada.

2. Codificando e decodificando JSON

Codificação com json_encode()

<?php
$dados = [
    'nome' => 'Maria Silva',
    'email' => 'maria@exemplo.com',
    'idade' => 32,
    'habilidades' => ['PHP', 'JavaScript', 'Python'],
    'ativo' => true
];

// Codificação básica
$json = json_encode($dados);
echo $json;
// {"nome":"Maria Silva","email":"maria@exemplo.com","idade":32,"habilidades":["PHP","JavaScript","Python"],"ativo":true}

// Com formatação legível
$jsonPretty = json_encode($dados, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
echo $jsonPretty;

Decodificação com json_decode()

<?php
$jsonString = '{"nome":"João","idade":28,"ativo":false}';

// Para array associativo (segundo parâmetro true)
$array = json_decode($jsonString, true);
echo $array['nome']; // João

// Para objeto stdClass (padrão)
$objeto = json_decode($jsonString);
echo $objeto->idade; // 28

// Tratamento de erros
$jsonInvalido = '{"nome": "incompleto"';
$dados = json_decode($jsonInvalido);

if (json_last_error() !== JSON_ERROR_NONE) {
    echo 'Erro JSON: ' . json_last_error_msg();
    // Erro JSON: Syntax error
}

3. Manipulação avançada de JSON

Trabalhando com JSON aninhado

<?php
$complexo = [
    'empresa' => 'TechCorp',
    'departamentos' => [
        'TI' => [
            'funcionarios' => 15,
            'projetos' => ['Alpha', 'Beta']
        ],
        'RH' => [
            'funcionarios' => 5,
            'projetos' => ['Recrutamento 2024']
        ]
    ]
];

$jsonComplexo = json_encode($complexo, JSON_PRETTY_PRINT);
$decodificado = json_decode($jsonComplexo, true);

echo $decodificado['departamentos']['TI']['projetos'][0]; // Alpha

Serialização personalizada com JsonSerializable

<?php
class Usuario implements JsonSerializable {
    private string $nome;
    private string $email;
    private string $senha;

    public function __construct(string $nome, string $email, string $senha) {
        $this->nome = $nome;
        $this->email = $email;
        $this->senha = $senha;
    }

    public function jsonSerialize(): array {
        return [
            'nome' => $this->nome,
            'email' => $this->email,
            // Senha NÃO é incluída na serialização
        ];
    }
}

$usuario = new Usuario('Ana', 'ana@exemplo.com', '123456');
echo json_encode($usuario, JSON_PRETTY_PRINT);
// {"nome":"Ana","email":"ana@exemplo.com"}

Leitura e escrita de arquivos JSON

<?php
// Escrevendo JSON em arquivo
$config = ['db_host' => 'localhost', 'db_port' => 3306];
file_put_contents('config.json', json_encode($config, JSON_PRETTY_PRINT));

// Lendo JSON de arquivo
$jsonLido = file_get_contents('config.json');
$configArray = json_decode($jsonLido, true);
echo $configArray['db_host']; // localhost

4. Parsing de XML com SimpleXML

Carregando XML

<?php
$xmlString = <<<XML
<livros>
    <livro id="1">
        <titulo>PHP Moderno</titulo>
        <autor>João Developer</autor>
        <preco moeda="BRL">89.90</preco>
    </livro>
    <livro id="2">
        <titulo>Clean Code</titulo>
        <autor>Robert Martin</autor>
        <preco moeda="USD">45.00</preco>
    </livro>
</livros>
XML;

$xml = simplexml_load_string($xmlString);

// Ou carregar de arquivo
// $xml = simplexml_load_file('livros.xml');
<?php
// Acessando elementos filhos
foreach ($xml->livro as $livro) {
    echo "ID: " . $livro['id'] . "\n";
    echo "Título: " . $livro->titulo . "\n";
    echo "Autor: " . $livro->autor . "\n";
    echo "Preço: " . $livro->preco . " (" . $livro->preco['moeda'] . ")\n\n";
}

// Usando XPath
$livrosCaros = $xml->xpath("//livro[preco > 50]");
foreach ($livrosCaros as $livro) {
    echo $livro->titulo . " - " . $livro->preco . "\n";
}

Modificação e criação de nós XML

<?php
// Adicionando novo livro
$novoLivro = $xml->addChild('livro');
$novoLivro->addAttribute('id', '3');
$novoLivro->addChild('titulo', 'Design Patterns');
$novoLivro->addChild('autor', 'Gang of Four');
$novoLivro->addChild('preco', '120.00')->addAttribute('moeda', 'BRL');

echo $xml->asXML();

5. Trabalhando com DOMDocument para XML complexo

Criando documentos XML do zero

<?php
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->formatOutput = true;

$root = $dom->createElement('catalogo');
$dom->appendChild($root);

$produto = $dom->createElement('produto');
$produto->setAttribute('codigo', 'P001');
$root->appendChild($produto);

$nome = $dom->createElement('nome', 'Notebook Pro');
$produto->appendChild($nome);

$preco = $dom->createElement('preco', '4999.99');
$preco->setAttribute('moeda', 'BRL');
$produto->appendChild($preco);

echo $dom->saveXML();
// Salvar em arquivo: $dom->save('catalogo.xml');

Manipulação avançada e validação

<?php
$dom = new DOMDocument();
$dom->load('catalogo.xml');

// Remover elemento
$produtos = $dom->getElementsByTagName('produto');
if ($produtos->length > 0) {
    $primeiro = $produtos->item(0);
    $primeiro->parentNode->removeChild($primeiro);
}

// Validação contra DTD
$dom->validate(); // Retorna bool

// Validação contra XSD
$dom->schemaValidate('catalogo.xsd'); // Retorna bool

6. Conversão entre JSON e XML

Implementação manual com recursão

<?php
function jsonToXml($json, DOMDocument $dom, DOMElement $parent = null): void {
    $data = json_decode($json, true);

    if ($parent === null) {
        $parent = $dom->documentElement;
        if (!$parent) {
            $root = $dom->createElement('root');
            $dom->appendChild($root);
            $parent = $root;
        }
    }

    foreach ($data as $key => $value) {
        if (is_array($value)) {
            $element = $dom->createElement(is_numeric($key) ? 'item' : $key);
            if (is_numeric($key)) {
                $element->setAttribute('index', $key);
            }
            $parent->appendChild($element);
            jsonToXml(json_encode($value), $dom, $element);
        } else {
            $element = $dom->createElement(is_numeric($key) ? 'item' : $key, htmlspecialchars((string)$value));
            if (is_numeric($key)) {
                $element->setAttribute('index', $key);
            }
            $parent->appendChild($element);
        }
    }
}

$json = '{"nome":"Produto","preco":100,"tags":["novo","promocao"]}';
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->formatOutput = true;
jsonToXml($json, $dom);
echo $dom->saveXML();

Usando Symfony Serializer

<?php
require 'vendor/autoload.php';

use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Encoder\XmlEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;

$encoders = [new XmlEncoder(), new JsonEncoder()];
$normalizers = [new ObjectNormalizer()];
$serializer = new Serializer($normalizers, $encoders);

$data = ['nome' => 'Exemplo', 'valor' => 99.90];

// JSON para XML
$json = json_encode($data);
$xml = $serializer->decode($json, 'xml');
echo $serializer->encode($xml, 'xml');

7. Boas práticas e segurança

Validação de dados

<?php
// Validar JSON antes de processar
function validarJson(string $json): bool {
    json_decode($json);
    return json_last_error() === JSON_ERROR_NONE;
}

// Validar XML com SimpleXML
function validarXml(string $xml): bool {
    libxml_use_internal_errors(true);
    simplexml_load_string($xml);
    $errors = libxml_get_errors();
    libxml_clear_errors();
    return empty($errors);
}

Prevenção de ataques XXE

<?php
// Desabilitar entidades externas para evitar XXE
$opcoes = LIBXML_NOENT | LIBXML_DTDLOAD;
$xml = simplexml_load_string($xmlString, 'SimpleXMLElement', $opcoes);

// Ou usar DOMDocument com segurança
$dom = new DOMDocument();
$dom->loadXML($xmlString, LIBXML_NOENT | LIBXML_DTDLOAD | LIBXML_NONET);

Performance para grandes arquivos

<?php
// Lazy loading com XMLReader para arquivos grandes
$reader = new XMLReader();
$reader->open('grande.xml');

while ($reader->read()) {
    if ($reader->nodeType === XMLReader::ELEMENT && $reader->name === 'registro') {
        $xml = $reader->readOuterXML();
        processarRegistro(simplexml_load_string($xml));
    }
}
$reader->close();

// Cache de parsing
$cacheFile = 'cache/parsed_' . md5('dados.json');
if (file_exists($cacheFile)) {
    $dados = unserialize(file_get_contents($cacheFile));
} else {
    $dados = json_decode(file_get_contents('grande.json'), true);
    file_put_contents($cacheFile, serialize($dados));
}

Referências