Processe XML no navegador usando jQuery

Navegue por algumas das maiores armadilhas para obter os benefícios da popular API de aplicativo da Web

A popular biblioteca JavaScript jQuery é mais bem conhecida por seu uso com HTML, mas também é possível usá-la para processar XML, se houver cuidado e atenção com as armadilhas. Este artigo mostra como usar a jQuery para processar o formato de feed da Web Atom. O XML de feed da Web talvez seja o formato XML mais disseminado, e a principal realização da promessa do XML na Web. Mas a maioria desses formatos usa espaço de nomes de XML, o que causa problemas com muitas bibliotecas JavaScript conhecidas, incluindo a jQuery.

Uche Ogbuji, Principal Consultant, Fourthought Inc.

Photo of Uche OgbujiUche Ogbuji é um sócio da Zepheira, LLC, uma firma de soluções especializada na próxima geração de tecnologias da Web. Ogbuji é o desenvolvedor líder da Akara, uma plataforma de software livre para XML e outros serviços de integração de dados. Ele é engenheiro da computação e escritor, nascido na Nigéria, que vive e trabalha em Boulder, no Colorado, Estados Unidos. Ele também escreve poemas e ensaios e é um Editor de Poesia Associado da The Nervousbreakdown. É possível saber mais sobre Uche Ogbuji em seu Weblog Copia.



28/Dez/2009

O XML é um SGML para a Web, mas não causou tanto impacto na Web quanto a comunidade XML gostaria. O esforço mais importante para o XML na Web, o XHTML, foi derrotado por políticos e "design-by-committee" e outras especificações pretensiosas, tecnicamente legítimas, do mesmo modo como o XForms e SVG lutaram contra uma percepção atrasada. O sucesso do XML na Web veio de direções às vezes inesperadas, incluindo a popularidade dos feeds da Web, formatos XML, como os tipos de RSS e Atom.

Acrônimos usados frequentemente

  • Ajax: Asynchronous JavaScript + XML
  • API: Application programming interface
  • CSS: Cascading Stylesheets
  • DOM: Document Object Model
  • HTML: Hypertext Markup Language
  • RSS: Really Simple Syndication
  • SGML: Standard Generalized Markup Language
  • SVG: Scalable Vector Graphics
  • URI: Uniform Resource Identifier
  • URL: Uniform Resource Locator
  • W3C: World Wide Web Consortium
  • XHTML: Extensible Hypertext Markup Language
  • XML: Extensible Markup Language

Para XML na Web, assim como com qualquer outra tecnologia na Web, o navegador é a peça principal, mas a maior discussão sobre processar XML na Web é focada no lado do servidor. Na série Firefox e XML aqui no developerWorks (consulte Recursos), discuti várias maneiras de se trabalhar com XML no navegador Firefox. Infelizmente, processar navegador cruzado de XML é ainda mais esquisito do que processar navegador cruzado de HTML, que faz parte do motivo pelo qual tantos tratamentos de XML na Web param no território relativamente seguro do lado do servidor.

Muitos desenvolvedores de HTML dinâmico estão cansados do esforço com navegador cruzado e de fazer scripts estranhos pelo navegador. O surgimento de várias excelentes bibliotecas JavaScript torna a vida dos desenvolvedores mais fácil. Uma das mais populares dessas bibliotecas é a jQuery, que foi discutida em diversos artigos aqui no developerWorks. Também é possível usar jQuery para processar XML, se aprender a dirigir evitando os buracos do monstro. Neste artigo, demonstrarei o uso da jQuery com XML em um cenário prático, trabalhando com feeds da Web Atom, introduzindo um padrão útil para estabelecer o processamento de XML na jQuery e lidando com os problemas práticos. Será necessária uma familiaridade básica com XML, Espaços de nomes XML, HTML, JavaScript e a biblioteca jQuery (consulte Recursos para ver mais artigos introdutórios à jQuery).

A miséria do espaço de nomes XML

Começarei com o maior problema. A jQuery não lida com espaços de nomes XML. Esse é um problema bem conhecido há muito tempo, e as pessoas aparecem com todo tipo de opções insatisfatórias e completamente furadas. A solução ideal seria que a jQuery suportasse seletores de nomes de espaço de Nível 3 de CSS (ainda um esboço de trabalho W3C, consulte Recursos), que adiciona uma nova classe de seletores, como segue:

@namespace ex url(http://example.com);
ex|quote { font-weight: bold }

A primeira linha é uma declaração de prefixo de espaço de nomes para o http://example.com e a segunda linha é um seletor de tipo que usa o novo componente de espaço de nomes, em que um prefixo declarado é separado do nome do local por um caractere de barra vertical. Infelizmente, isso não é suportado na jQuery, então as pessoas têm utilizado todo tipo de pirataria eletrônica para lidar com o espaço de nomes.

Simulando que o prefixo é significativo

Uma das piratarias mais comuns usada para lidar com XML e espaços de nomes na jQuery é ignorar o espaço de nomes e selecionar o qname inteiro (o prefixo e também a parte do local).

    $(xml).find("x\\:quote").each(function() {
      //process each node
    });

Esse código seleciona, pelo conceito da jQuery de nome de nó, qual a propriedade nodeName do DOM. Ele contém dois pontos, que é um caractere reservado nos seletores jQuery, e deve ser evitado usando uma contra barra. A contra barra é um caractere reservado nas cadeias JavaScript e deve ser duplicada. Esse truque não funciona no caso de documentos com espaços de nome equivalentes que usam prefixos diferentes.

Brincando com filtros de atributo

Algumas pessoas relataram sucesso com variações da abordagem a seguir, usando filtros de atributo da jQuery no pseudo-atributonodeName:

    $(xml).find("[nodeName=x:quote]").each(function() {
      //process each node
    });

Nas versões anteriores da jQuery 1.3.x, se adicionaria @ logo antes do nodeName. Mas, isso apresenta o mesmo problema fundamental da abordagem da seção anterior, Simulando que o prefixo é significativo. Ele interromperá muitos cenários de espaço de nomes do mundo real. Tentei a variação a seguir, que faz mais sentido:

    $(xml).find("[namespaceURI='http://example.com'][localName='quote']")
    .each(function() {
      //process each node
    });

Infelizmente, isso não funciona.

Em busca de um bom plug-in

Essa bagunça não é inteiramente culpa da jQuery. O DOM fornece métodos eficientes para encontrar nós: getElementsByTagName e getElementsByTagNameNS. Este último foi projeto para ser a percepção do espaço de nomes, aceitando o espaço de nomes URI e ignorando o prefixo, mas infelizmente todo navegador o suporta, com exceção do ® Internet Explorer® da Microsoft. No entanto, a pretensão da jQuery é lidar com essa desigualdade entre os navegadores, para que as pessoas não tenham que fazer isso. Uma desculpa fraca e possível é que a jQuery baseia seus seletores em grande parte em CSS e que mesmo os seletores de espaço de nomes de Nível w de CSS W3C infelizmente não conseguiram fazê-la passar do estágio de preparação. jQuery bug #155, "Obtenha Elementos de Espaço de Nomes em Documentos XML" (Consulte Recursos), discute esses problemas, mas não tem sido abordado em três anos.

Ryan Kelly se deparou com esse problema e fez uma tentativa valiosa de criar um plug-in jQuery, jquery.xmlns.js, para Seletores de Nomes de Espaço XML (consulte Recursos). Ele parece suportar códigos como os a seguir.

$.xmlns["ex"] = "http://example.com";
$(doc).find("ex|quote").each(...);

A primeira linha é uma declaração global de espaço de nomes para o plug-in—global devido às limitações do mecanismo adjacente da jQuery. Ele realmente fornece um bloco não global em um idioma típico da jQuery para definição do escopo do espaço de nomes. Infelizmente, obtive sucessos muito variáveis com essa extensão. Espero que isso mude e que finalmente se possa superar as dificuldades da jQuery de forma apropriada.

Um plug-in mais simples

A solução que finalmente escolhi foi criar um plug-in simples que não faz nada de especial com os seletores da jQuery, mas adiciona um novo filtro para o qual passar diretamente um espaço de nomes e nome do local para adaptar um conjunto de resultado a nós de correspondência. Use como a seguir:

  $(xml).find('*').ns_filter('http://example.com', 'quote').each(function(){
  .each(function() {
    //process each node
  });

ns_filter é o filtro especial que escrevi. A necessidade de fazer um find('*') separado pode parecer deselegante, e uma variante mais simples poderia ser:

  $(xml).find('quote').ns_filter('http://example.com').each(function(){
  .each(function() {
    //process each node
  });

No entanto, isso não é viável porque não é possível confiar na jQuery para tratar uma pesquisa como find('quote') de uma maneira neutra de espaço de nomes (ou seja, como um seletor de nome de local). A implementação do meu filtro é fornecida na próxima seção, como parte de um sistema geral para configurar a jQuery para processar XML. Eu o testei no Firefox 3.5.5 e Safari 4.0.4 no SO Mac X Snow Leopard, e nas recentes versões do Internet Explore 7 e Internet Explorer 8 no Windows® XP.

O ambiente de trabalho XML da jQuery

Os problemas de espaço de nomes são apenas um sintoma do fato de que, afinal de contas, a jQuery é uma ferramenta HTML. Descobri que o padrão mais útil para usar a jQuery com XML é criar um ambiente de trabalho HTML para documentos XML, que invoque o script através de meios confiáveis de navegador-cruzado e depois configurar e contornar qualquer problema necessário, como os espaços de nomes XML. É possível usar o padrão de ambiente de trabalho para preparar e testar os padrões e técnicas para o processamento de XML baseado em navegador, e até usar o ambiente de trabalho como base do próprio aplicativo baseado em navegador.

Listagem 1 (quotes.html) é um exemplo simples de HTML usando o ambiente de trabalho. Ele carrega algumas cotações dinamicamente de um arquivo XML.

Listagem 1 (quotes.html). Exemplo de HTML usando o ambiente de trabalho XML da jQuery
<html>
        <head>
                <title>jQuery XML workbench</title>
                <script type="text/javascript" src="jquery.js"></script>
                <script type="text/javascript" src="workbench.js"></script>
                <script type="text/javascript" src="quotes.js"></script>
                <!-- Put the XML file or URL in the href attribute below: -->
        <link href="quotes1.xml" type="application/xml" rel="target_XML" />
        </head>
        <body>
        <h1>A few quotations for your enjoyment</h1>
        <div id="update-target"><ol></ol></div>
        </body>
</html>

É preciso escrever elementos de script para carregar a própria jQuery, o JavaScript do ambiente de trabalho e o script específico do aplicativo. Também é necessário um elemento de link que identifique o arquivo de XML a ser movido usando target_XML. Se for necessário trabalhar com mais de um arquivo XML, é bem mais fácil ampliar a configuração do ambiente de trabalho. Listagem 2 (workbench.js) é o script do ambiente de trabalho.

Listagem 2 (workbench.js). JavaScript do ambiente de trabalho XML da jQuery
/*
workbench.js
*/
// The jQuery hook invoked once the DOM is fully ready
$(document).ready(function(){ 
        // Get the target XML file contents (Ajax call)
        var fileurl = $("link[rel='target_XML']").attr('href');
    $.ajax({
        url: fileurl,
        type: "GET",
        dataType: "xml",
        complete: xml_ready,
        error: error_func
     });
});

// Callback for when the Ajax call results in an error
function error_func(result) {
    alert(result.responseText);
}

// ns_filter, a jQuery extension for XML namespace queries.
(function($) {
  $.fn.ns_filter = function(namespaceURI, localName) {
    return $(this).filter(function() {
        var domnode = $(this)[0];
        return (domnode.namespaceURI == namespaceURI && domnode.localName == localName);
    });
  };

})(jQuery);

O código do ambiente de trabalho é bem comentado, mas aí vão algumas observações adicionais. O filtro de espaço de nomes é a última função na listagem. A primeira função é o gancho usual jQuery invocado quando o DOM da página principal está completamente pronto. Ele mantém um URL para o XML de destino e faz uma chamada Ajax para carregar o arquivo. Observe o dataType: "xml", que instrui o mecanismo Ajax a analisar o documento de resposta como XML. Se houver um erro, ele invoca o retorno de chamada error_func ou xml_ready, que o usuário fornece para implementar o comportamento do aplicativo. Essa chamada de retorno pega a estrutura do resultado através do qual é possível puxar o XML na forma de propriedade responseXML. Listagem 3 (quotes.js) é o código de aplicativo para este caso.

Listagem 3. (quotes.js) Código de aplicativo para visualização de cotações dinâmicas
/*
quotes.js
*/
function xml_ready(result){
    var xml = result.responseXML;
        //Make sure the target area for inserting data is clear
        $("#update-target ol").empty();
    $(xml).find('*').ns_filter('http://example.com', 'q').each(function(){
        var quote_text = $(this).text()

        $('<li></li>')
            .html(quote_text)
            .appendTo('#update-target ol');
    }); //close each(
}

Listagem 4 (quotes1.xml) é o arquivo XML com a lista de cotações.

Listagem 4. (quotes1.xml) arquivo XML com uma lista de cotações
<?xml version="1.0" encoding="utf-8"?>
<x:quotes xmlns:x='http://example.com'>
  <x:q>Words have meaning and names have power</x:q>
  <x:q>Sticks and stones will break my bones, but names will never hurt me.</x:q>
  <x:q>The beginning of wisdom is to call things by their right names.</x:q>
  <x:q>Better to see the face than to hear the name. </x:q>
</x:quotes>

Observe que usei o prefixo x, o que significa que, em teoria, eu deveria tentar um dos truques baseados em prefixo que mencionei acima. Mas, se fizesse isso, ele iria quebrar se eu substituísse o arquivo de cotações pela Listagem 5 (quotes2.xml), que é 100% equivalente em espaço de nomes à Listagem 4, e o mesmo XML Canônico (consulte Recursos).

Listagem 5. (quotes2.xml) Arquivo XML equivalente à Listagem 4, com uma lista de cotações
<?xml version="1.0" encoding="utf-8"?>
<quotes xmlns='http://example.com'>
  <q>Words have meaning and names have power</q>
  <q>Sticks and stones will break my bones, but names will never hurt me.</q>
  <q>The beginning of wisdom is to call things by their right names.</q>
  <q>Better to see the face than to hear the name. </q>
</quotes>

Se quotes2.xml for substituído pela Listagem 1, isso funcionará bem, o que é um teste chave para processamento de espaço de nomes. A Figura 1 é o monitor do navegador de quotes.html.

Figura 1. Cotações exibidas usando o ambiente de trabalho XML da jQuery
Quotes (from Listing 5) displayed using the jQuery XML workbench

Exibição dinâmica do XML Atom

Quando tiver quebrado o processamento do espaço de nomes XML na jQuery, é possível lidar com muitos outros formatos XML úteis, incluindo formatos de feed da Web, como RSS e Atom. Nesta seção, usarei o ambiente de trabalho XML da jQuery para exibir as últimas entradas de um feed Atom em uma página da Web. A Listagem 6 é a página HTML.

Listagem 6. (home.html) Página da Web que hospeda o XML dinâmico
<html>
        <head>
                <title>jQuery XML workbench</title>
                <script type="text/javascript" src="jquery.js"></script>
                <script type="text/javascript" src="workbench.js"></script>
                <script type="text/javascript" src="home.js"></script>
                <!-- Put the XML file or URL in the href attribute below: -->
        <link href="atom1.xml" type="application/xml" rel="target_XML" />
        </head>
        <body>
        <h1>Caesar's home page</h1>
        <p>GALLIA est omnis divisa in partes tres, quarum unam incolunt Belgae,
    aliam Aquitani, tertiam qui ipsorum lingua Celtae, nostra Galli
    appellantur. Hi omnes lingua, institutis, legibus inter se differunt.
    </p>

    <p>Gallos ab Aquitanis Garumna flumen, a Belgis Matrona et Sequana dividit.
    </p>

    <p>Horum omnium fortissimi sunt Belgae, propterea quod a cultu atque
    humanitate provinciae longissime absunt, minimeque ad eos mercatores saepe
    commeant atque ea quae ad effeminandos animos pertinent important,
    proximique sunt Germanis, qui trans Rhenum incolunt, quibuscum continenter
    bellum gerunt. Qua de causa Helvetii quoque reliquos Gallos virtute
    praecedunt, quod fere cotidianis proeliis cum Germanis contendunt, cum aut
    suis finibus eos prohibent aut ipsi in eorum finibus bellum gerunt.</p>

        <h2>My <a href="feed.xml">Web feed</a></h2>
        <div id="update-target"></div>
        </body>
</html>

A Listagem 7 (atom1.xml) é o arquivo Atom mencionado.

Listagem 7. (atom1.xml) Arquivo de amostra de Atom
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"
      xml:lang="en"
      xml:base="http://www.example.org">
  <id>http://www.example.org/myfeed</id>
  <title>My Simple Feed</title>
  <updated>2005-07-15T12:00:00Z</updated>
  <link href="/blog" />
  <link rel="self" href="/myfeed" />
  <author><name>Uche Ogbuji</name></author>
  <entry>
    <id>http://www.example.org/entries/1</id>
    <title>A simple blog entry</title>
    <link href="/blog/2005/07/1" />
    <updated>2005-07-14T12:00:00Z</updated>
    <summary>This is a simple blog entry</summary>
  </entry>
  <entry>
    <id>http://www.example.org/entries/2</id>
    <title />
    <link href="/blog/2005/07/2" />
    <updated>2005-07-15T12:00:00Z</updated>
    <summary>This is simple blog entry without a title</summary>
  </entry>
</feed>

A Listagem 8 é home.js, que contém o código de aplicativo dinâmico montado no ambiente de trabalho.

Listagem 8. (home.js) código de aplicativo para a exibição do feed da Web da página principal
/*
home.js
*/
var ATOM_NS = 'http://www.w3.org/2005/Atom';

function xml_ready(result){
    var xml = result.responseXML;
        //Make sure the target area for inserting data is clear
        $("#update-target").empty();
    $(xml).find('*').ns_filter(ATOM_NS, 'entry').each(function(){
        var title_elem = $(this).find('*').ns_filter(ATOM_NS, 'title').clone();
        var link_text = $(this).find('[rel="alternate"]')
                            .ns_filter(ATOM_NS, 'link')
                            .attr('href');
        var summary_elem = $(this).find('*').ns_filter(ATOM_NS, 'summary').clone();

        //Deal with the case of a missing title
        if (!title_elem.text()){
            title_elem = '[No title]';
        }

        //Deal with the case where rel='alternate' is omitted
        if (!link_text){
            link_text = $(this).find('*')
                                .ns_filter(ATOM_NS, 'link')
                                .not('[rel]')
                                .attr('href');
        }

        //Update the target area with the entry information
        $('<p></p>')
            .append(
                $('<a href="' + link_text + '"></a>')
                .append(title_elem)
            )
            .append(' - ')
            .append(summary_elem.clone())
            .fadeIn('slow') //bonus animation
            .appendTo('#update-target');
    }); //close each(
}

Novamente comentei o arquivo, mas alguns pontos merecem uma atenção especial. O Atom possui muitas variações aceitáveis em seus elementos, muitos dos quais são opcionais. Isso significa que é preciso fazer alguma manipulação dos casos excepcionais. Vou ilustrar dois desses casos comuns: o rel="alternate" opcional no link requerido e o fato de que os títulos são opcionais. Como podemos ver, a jQuery fornece muita flexibilidade para lidar com esses casos, de modo que é possível lidar até com esses formatos irregulares de XML. Em alguns casos, copio conceitos diretamente do XML para o documento principal (o HTML host). Isso requer algum cuidado, e será possível perceber onde uso o método clone() para ter certeza de que não estou implantando nós de um documento para outro, um erro que seria sinalizado pela exceção do DOM WRONG_DOCUMENT_ERR. Como bônus, usei o método jQuery fadeIn de modo que o conteúdo adicionado desaparece visualmente de forma lenta. A Figura 2 é o monitor do navegador de home.html.

Figura 2. Página principal com conteúdo de feed da Web adicionado dinamicamente
Caesar's home page (text from Listing 6) with dynamically added Web feed content

Relatório final

A jQuery tem tudo a ver com empacotar todos os truques e contornos para lidar com estranhezas do navegador da Web, e o ambiente de trabalho XML que introduzi neste artigo é uma primeira etapa em direção a essa ferramenta reutilizável para aqueles que precisam lidar com XML. Vimos que um dos maiores problemas é lidar com os espaços de nome. Assim que vencer esse obstáculo, a jQuery fornece as ferramentas para lidar com muitos tipos de documentos irregulares tão competentemente expressos em XML. Será possível descobrir o quão rapidamente as técnicas desenvolvidas processando feeds da Web podem ser aplicadas a muitos outros formatos de XML no navegador.

Se considerar a jQuery e contornar problemas relacionados inadequados, uma opção é usar a biblioteca JavaScript mais diretamente direcionada ao processamento de XML, como a Sarissa, que merece um artigo sobre ela, mas que não é tão amplamente usada, nem tão fácil de se implementar quanto a jQuery.


Download

DescriçãoNomeTamanho
Code listings for this articlecode.zip6KB

Recursos

Aprender

Obter produtos e tecnologias

  • jQuery: Obtenha a jQuery e muitos recursos de suporte para simplificar o transporte de documento HTML, manipulação de eventos, animação e interações Ajax para um rápido desenvolvimento da Web.
  • Plug-in jquery.xmlns.js: Fique de olho no plug-in de Ryan Kelly, que tenta suportar Seletores de Espaço de Nomes XML CSS 3, mas que, até o momento dessa redação, talvez precise de um pouco mais de trabalho.
  • Sarissa: Pense em usar essa biblioteca JavaScript destinada diretamente ao processamento de XML, incluindo suporte a espaço de nomes. Ela não é tão usada, nem tão fácil de implementar como a jQuery, mas é uma opção atraente para aplicativos pesados de XML.
  • Versões de avaliação de produtos IBM: Faça download ou explore os testes on-line no IBM SOA Sandbox e use as ferramentas de desenvolvimento de aplicativos e produtos de middleware do DB2®, Lotus®, Rational®, Tivoli® e WebSphere®.

Discutir

Comentários

developerWorks: Conecte-se

Los campos obligatorios están marcados con un asterisco (*).


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


A primeira vez que você entrar no developerWorks, um perfil é criado para você. Informações no seu perfil (seu nome, país / região, e nome da empresa) é apresentado ao público e vai acompanhar qualquer conteúdo que você postar, a menos que você opte por esconder o nome da empresa. Você pode atualizar sua conta IBM a qualquer momento.

Todas as informações enviadas são seguras.

Elija su nombre para mostrar



Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Los campos obligatorios están marcados con un asterisco (*).

(Escolha um nome de exibição de 3 - 31 caracteres.)

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


Todas as informações enviadas são seguras.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Software livre
ArticleID=459253
ArticleTitle=Processe XML no navegador usando jQuery
publish-date=12282009