Professional Documents
Culture Documents
visie.com.br tableless.com.br
Muito se tem falado de XML e da forma como essa linguagem vai revolucionar a internet, nos dando a Web
Semântica e nossos dados em qualquer lugar. Mas pouca gente está de fato trabalhando com XML na prática e muita
gente começou a estudar o assunto, aprendeu a escrever um arquivo XML e ficou se perguntando o que fazer com
isso.
Precisamos então de um cenário prático, um caso "do mundo real", para que nosso estudo não sejaapenas uma
teoria vaga que você acha interessante mas não sabe exatamente pra que serve.
XML na prática
Vamos analisar alguns exemplos de trabalho com dados em que XML parece ser uma resposta bastante natural:
Tabela de preços
O website de uma escola, em PHP, precisa exibir logo abaixo de suatabela de preços de matrícula e mensalidades, a
tabela de preços dos materiais escolares extraída do site de uma papelaria parceira, que é feito em Java. Os
desenvolvedores do site da papelaria preparam então um aplicativo que atualiza um arquivo XML em seu servidor.
Você pode com facilidade usar a função fopen do PHP para obter este XML. Seu trabalho é então processá-lo para
transformá-lo em uma tabela de preços HTML.
Calendário de Entrevistas
Uma pequena empresa de recursos humanos envia candidatos escolhidospara uma empresa parceira, de grande
porte. A empresa maior marca uma entrevista individual num dia e horário determinado para cadacandidato, e
fornece um arquivo xml com a agenda de entrevistas de todos os candidatos de todas as empresas parceiras. O site
da empresa menor, feito em Java/JSP, precisa exibir uma agenda, em HTML, apenas das entrevistas dos candidatos
indicados por ela.
Gerenciador de Conteúdos
Eis um caso real! O gerenciador de conteúdos da Iônia, indústria de cosméticos, gera um XML do conteúdo, como
você pode ver em http://www.ionia.com.br/xml.asp. Você precisa transformar este conteúdo em um bonito site
HTML.
Xpath - Linguagem de localização de elementos em um arquivo XML. É a primeira que vamos estudar
XSLT - Linguagem de transformação de XML, é esta que faz a mágica toda.
XSL-FO - Linguagem de formatação XML. Ainda não há implementações populares de XSL-FO e seu uso por
enquanto é muito restrito.
Como eram três linguagens distintas, com objetivos diferentes, que compunham a XSL, o W3C mudou a
especificação antes de aprová-la, e hoje a linguagem XSL-FO passou a se chamar XSL (e a antiga XSL, que englobava
as três, não ganhou nome). Apesar disso é conviente notar que, uma vez que XSL (XSL-FO) ainda não foi adotada
pelo mercado, seus usos hoje ainda são muito restritos e ela é praticamente deconhecida dos desenvolvedores,
quando as pessoas dizem "XSL" ou "XSLT" hoje elas geralmente querem dizer a mesma coisa: XSLT.
Diagrama XSLT
Ou seja, tendo um arquivo XML de origem (que nos nossos três exemplos já foram fornecidos por terceiros) o papel
da XSLT é transformar esse arquivo em um outro formato, HTML, XML, PDF ou outro. Assim, tendo um
interpretador XML que suporte XSLT, nos nossos três exemplos bastaria escrever um arquivo XSLT que
transformasse nossos dados em HTML.
A maioria dos ambientes de desenvolvimento hoje oferece interpretadores de XML com suporte a XSL.
● PHP
O PHP já vem com uma extension que trabalha com XSLT. Veja mais em:
http://br2.php.net/manual/pt_BR/ref.xslt.php
● Java
Use o Jax:http://java.sun.com/xml/
● Python
Experimente a libxslt:http://xmlsoft.org/XSLT/python.html
● ASP
Use os componentes do pacote MSXML, versão 3 ou superior.
Para demonstrar como é simples usar um interpretador XML para XSLT, segue um exemplo de como fazer a
transformação em ASP:
'Carrega o XML
xmldoc.LoadXML(codigoxml)
'Executa a transformação
xslproc.input = xmldoc
xslproc.transform
transforma = xslproc.output
end function
Como você pode ver, o código é simples, e precisa ser escrito uma única vez.
<?xml version="1.0"?>
<loja>
<categoria id="1" description="Cadernos">
<produto id="11">
<descricao>Caderno Univ. 100 folhas</descricao>
<preco>25.80</preco>
</produto>
<produto id="12">
<descricao>Caderno Univ. 10 mat.</descricao>
<preco>33.50</preco>
</produto>
</categoria>
<categoria id="2" description="Reguas">
<produto id="21">
<descricao>Regua Comum, 20cm</descricao>
<preco>0.80</preco>
</produto>
<produto id="22">
<descricao>Esquadro 15x20cm</descricao>
<preco>1.10</preco>
</produto>
</categoria>
</loja>
Como você pode ver, o Quanta tem um interpretador de XML que leu meu arquivo e organizou os dadosnuma
estrutura de árvore. Cada elemento naquela estrutura é chamado de nó. Existem três tipos de nós:
● Elementos
Cada tag do XML é um elemento. O primeiro elemento, loja, envolve todos os outros e é chamado de
elemento-raiz.
● Atributos
Embora não sejam mostrados pelo TreeView do Quanta, os atributos de cada elemento também são nós na
estrutura do XML.
● Texto
Como você pode ver na figura, o texto de cada elemento representa um nó separado do próprio elemento.
(Na verdade exitem mais de três tipos de nós. Os comentários, por exemplo, também são nós do XML. Mas para
nosso estudo agora bastam estes três tipos.)
Os nós contidos por um elemento são chamados de filhos (e o elemento que os contém de pai, claro.) Os nós filhos
de um mesmo pai e que estão no mesmo nível são chamados de adjacentes, como descricao e preco.
O melhor meio de começar a entender XPath é através de exemplos práticos. Vamos ao básico:
/loja
O código acima selecionaria, no documento de exemplo, o elemento-raiz loja. Veja este agora:
/loja/categoria/produto
Como você pode ver, a estrutura é semelhante à da construção de URLs em diretórios. Aqui selecionamos todos os
elementos produto que estão dentro de um elemento categoria dentro do elemento raiz loja.
Também você pode notar que sempre começamos nossa seleção no elemento-raiz (/). Dentro da XSLT você vai
precisar usar XPath para selecionar elementos iniciando de um outro nó qualquer. O nó de onde você começa sua
seleção é chamado de nó de contexto. Assim, se o nó de contexto, o nó de onde estivermos iniciando, for um
elemento categoria e quisermos selecionar os precos de seus produtos:
produto/preco
Note a ausência da barra inicial. Isso faz com que a comece no nó de contexto e não no elemento raiz. Você também
pode subir níveis. Digamos de o nó de contexto seja um elemento preco e queiramos selecionar as categorias:
../../../categoria
/loja//produto
Este código localiza todos os produtos dentro de loja, não importa quantos níveis abaixo. Ou seja, ao usar // o
interpretador varre toda a árvore de filhos do elemento procurando por elementos coincidentes. Outro exemplo
interessante:
Este código seleciona qualquer elemento imediatamente dentro de loja. O asterisco significa "qualquer elemento".
Vamos começar a complicar isso. Podemos usar colchetes para fazer testes:
//produto[2]
Este código seleciona o segundo produto em cada categoria. A explicação é óbvia. Um número dentro dos colchetes
seleciona o elemento com aquele índice, assim elemento[3] seleciona o terceiro elemento. Veja este agora:
//produto[preco]
Este código seleciona todos os elementos produto que contém um elemento preco. Podemos usar testes de nó mais
complexos dentro dos colchetes:
//categoria[produto/preco]
Este código seleciona todas as categorias que contém um elemento produto contendo um elemento preco. Veja este
agora:
//produto[preco=0.80]
Este código seleciona todos os produtos que contém um elemento preco com valor 0.80, ou seja, todos os produtos
que custam 80 centavos. A XPath também nos fornece funções que podemos invocar, por exemplo:
count(//produto)
Retorna o valor 4, o número de elementos produto. A função last(), por exemplo, retorna o índice do último
elemento.
/loja/categoria[last()]
/*/categoria[last()]/produto[preco>1]/descricao
Seleciona a descrição dos produtos mais caros que R$1,00 na última categoria. Você também pode selecionar nós em
caminhos diferentes separando-os com o caractere de pipe, assim:
//produto/descricao | //produto/preco
/loja/categoria[1]/@id
O código acima seleciona o atributo id do primeiro elemento categoria. Você também pode usar atributos nos
colchetes de teste, por exemplo:
/loja/categoria[@id=1]/produto[@id]/preco
Esse código seleciona os elementos preco dentro dos elementos produto que possuem um atributo id dentro da
categoria cujo id tem valor 1.
Expressões e funções
Operadores Descrição
+ Adição numérica. 2+3 retorna o valor 5
- Subtração numérica. 3-2 retorna o valor 1.
* Multiplicação numérica. 2*3 retorna o valor 6.
Divisão numérica. O sinal de / não é utilizado porque ele já é usado para definir caminhos
div
(/loja/categoria por exemplo.) 6 div 2 retorna o valor 3.
mod Resto. 11 mod 5 retorna o valor 1.
= Igualdade. 2=2 retorna true e 2=3 retorna false.
!= Diferença. 2!=2 retorna false e 2!=3 retorna true.
Comparadores numéricos. Em um arquivo XSLT, afim de escrever um XML válido, troque os
< <= > >=
operadores < e <= por < e <=.
and or Operadores lógicos and e or.
Você pode encontrar uma referência bastante completa das funções disponíveis na XSLT em
http://www.w3schools.com/xpath/xpath_functions.asp
● count(nodes)
Retorna o número de nós em nodes.
● last()
Retorna o índice do último elemento no conjunto de nós atual.
● name(node)
Retorna o nome da tag de node.
● position()
Retorna o índice do nó de conteto atual.
● contains(str1,str2)
Testa se str1 contém str2 e retorna true ou false
● starts-with(str1,str2)
Testa se str1 começa com str2 e retorna true ou false
● round(num)
Arredondada num para o inteiro mais próximo
● not(exp)
Retorna true se exp for false e vice-versa
● number(var)
Converte var para um número
● string(var)
Converte var para uma string
● boolean(var)
Converte var para um boolean
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
</xsl:stylesheet>
template
O código acima varre todo o arquivo XML, começando pelo nó raiz, elemento por elemento, listando o texto de
cada elemento. Se o aplicarmos ao XML da loja o resultado será:
Ou seja, apenas o texto de todos os elementos. Nós podemos mudar este comportamento. Listar apenas o texto de
cada elemento é chamado de "template padrão". Podemos criar outro template para o elemento que desejarmos. Por
exemplo, se quisermos que o XSLT retorne a palavra "Produto" ao encontrar cada elemento produto podemos fazer:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="produto">Produto
</xsl:template>
</xsl:stylesheet>
Aqui alteramos o template padrão para outro, contendo a palavra Produto e uma quebra de linha. Ao aplicarmos
esse XSLT o resultado será:
Produto
Produto
Produto
Produto
apply-templates
Você deve ter notado acima que foi gerada a palavra "Produto" para cada um dos produtos encontrados e o
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="produto">
Produto: <xsl:apply-templates />
</xsl:template>
</xsl:stylesheet>
Aqui, ao encontrar cada um dos produtos, o interpretador vai escrever "Produto:" e em seguidaaplicar o template
padrão para preco e descricao, o que resultará em:
O atributo match aceita qualquer expressão XPath. Você também pode aplicar um template personalizado para os
elementos filhos:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="produto">
Produto: <xsl:apply-templates />
</xsl:template>
<xsl:template match="preco"> [Preço oculto]</xsl:template>
</xsl:stylesheet>
Aqui, ao encontrar um elemento descricao o interpretador vai aplicar o template padrão, mas ao encontrar um
elemento preco vai aplicar o template personalizado, gerando o seguinte:
Que tal tentar gerar uma tabela HTML com o que aprendemos até aqui? Veja um exemplo:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="loja">
<h1>Tabela de Preços</h1>
<table>
<tr>
<th>Produto</th><th>Preço</th>
</tr>
<xsl:apply-templates />
</table>
</xsl:template>
<xsl:template match="produto">
<tr>
<td><xsl:apply-templates select="descricao" /></td>
<td><xsl:apply-templates select="preco" /></td>
</tr>
</xsl:template>
</xsl:stylesheet>
Aqui temos dois templates. O primeiro, executado quando o interpretador encontra o elemento loja, cria um título
para a página (h1), inicia a tabela e cria a primeira linha, com os cabeçalhos "Produto" e "Preço". O segundo
template, executado em todos os produtos, cria uma linha da tabela com os valores de descricao e preco, um em
copy-of
Suponha que você queira copiar um elemento inteiro, todo o seu código XML, e não apenas seu texto. É fácil:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="loja">
<xsl:copy-of select="categoria[1]" />
</xsl:template>
</xsl:stylesheet>
Se a expressão XPath no atributo select selecionar mais de um elemento o copy-of listará o código XML de todos os
elementos selecionados seqüencialmente, um após o outro.
value-of
O elemento value-of da XSLT exibe o retorno de um valor XPath. Ou seja, ele avalia uma expressão XPath. Se o
resultado dessa expressão for um número, string ou valor booleano, ele o exibe. Se for um atributo, exibe o valor do
atributo. Se for um conjunto de nós, exibe o texto do primeiro nó. Veja este exemplo:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="loja">
<h1>Tabela de Preços</h1>
<table border="1">
<xsl:apply-templates />
</table>
</xsl:template>
<xsl:template match="categoria">
<tr>
<th colspan="3"><h3><xsl:value-of select="@description" /></h3></th>
</tr><tr>
<th>Cód.</th><th>Descrição</th><th>Preço</th></tr>
<xsl:apply-templates />
</xsl:template>
<xsl:template match="produto">
<tr>
<td><xsl:value-of select="@id" /></td>
Vou deixar para você o privilégio de entender o código acima. Veja como fica o HTML gerado, quando visto no
navegador:
attribute
Imagine agora que queremos colocar, em cada linha da nossa tabela de produtos, um link para a página
detalhes_produto.asp?cod={código do produto}. Essa é na verdade uma situaçãomuito comum. O código HTML
para fazê-lo deve ficar, para o produto de id 11.
Como inserir o código do produto dentro do atributo href? Através do elemento attribute, observe:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<img>
<xsl:attribute name="src">logo.gif</xsl:attribute>
</img>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<img align="left">
<xsl:attribute name="src">logo.gif</xsl:attribute>
<xsl:attribute name="border">0</xsl:attribute>
</img>
</xsl:template>
</xsl:stylesheet>
Que gera:
Ou seja, podemos combinar atributos escritos diretamente no elemento com elementos attribute. Agora veja:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<div>
<xsl:attribute name="id">sep1</xsl:attribute>
Teste
<xsl:attribute name="class">sep</xsl:attribute>
</div>
</xsl:template>
</xsl:stylesheet>
Vai gerar:
<div id="sep1">
Teste
</div>
Ou seja, o interpretador vai ignorar elementos attribute se eles não estiverem no início do elemento-pai, antes do
conteúdo.
Podemos inserir outros elementos XSL dentro do elemento attribute, o que resolve nosso problema da listagem de
produtos com links, veja:
<xsl:template match="loja">
<h1>Tabela de Preços</h1>
<table border="1">
<xsl:apply-templates />
</table>
</xsl:template>
<xsl:template match="categoria">
<tr>
<th colspan="3"><h3><xsl:value-of select="@description" /></h3></th>
</tr><tr>
<th>Cód.</th><th>Descrição</th><th>Preço</th>
</tr>
<xsl:apply-templates />
</xsl:template>
<xsl:template match="produto">
<tr>
<td><xsl:value-of select="@id" /></td>
<td>
<a><xsl:attribute
name="href">detalhes_produto.asp?cod=<xsl:value-of
select="@id" /></xsl:attribute><xsl:value-of
select="descricao" /></a>
</td>
<td><xsl:value-of select="preco" /></td>
</tr>
</xsl:template>
</xsl:stylesheet>
call-template
Uma outra maneira de se chamar um template, dando-lhe um nome, assim:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="produto">
<xsl:call-template name="pr" />
</xsl:template>
<xsl:template name="pr">
<xsl:value-of select="descricao" />
- R$ <xsl:value-of select="preco" /> |
</xsl:template>
</xsl:stylesheet>
Aqui usamos o elemento <i>call-template</i> para chamar o segundo template pelo nome. Note que o nó de
contexto ao chamar o segundo template é o mesmo do primeiro (produto).
if
O código do elemento if é bastante simples:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="produto">
<xsl:value-of select="descricao" />
</xsl:stylesheet>
Ou seja, ao encontrar um elemento if o interpretador vai avaliar a expressão XPath test. Se o resultado for
verdadeiro, ele executa o que há dentro do elemento if, caso contrário o ignora. Vamos, com isso, inserir maisum
recurso em nossa tabela de preços:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="loja">
<h1>Tabela de Preços</h1>
<table border="1">
<xsl:apply-templates />
</table>
</xsl:template>
<xsl:template match="categoria">
<tr>
<th colspan="3"><h3><xsl:value-of select="@description" /></h3></th>
</tr><tr>
<th>Cód.</th><th>Descrição</th><th>Preço</th>
</tr>
<xsl:apply-templates />
</xsl:template>
<xsl:template match="produto">
<tr>
</xsl:stylesheet>
choose
O elemento choose funciona de maneira semelhante ao if, dando-nos porém mais flexibilidade. Veja:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:choose>
<xsl:when test="preco < 1"> | Promoção!</xsl:when>
<xsl:when test="preco < 2"> | Preço bom!</xsl:when>
<xsl:otherwise> | 3X no cartão!</xsl:otherwise>
</xsl:choose>|
</xsl:template>
</xsl:stylesheet>
E seu resultado:
Esquadro 15x20cm
R$ 1.10 | Preço bom! |
Ao encontrar um elemento choose o interpretador executa o test no primeiro elemento when. Se for verdadeiro,
executa o conteúdo deste when, caso contrário testa o próximo. Se nenhum dos testes when for verdadeiro o
interpretador executará o conteúdo do bloco otherwise, se houver. Veja o que vamos fazer com nossa tabela de
preços:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="loja">
<h1>Tabela de Preços</h1>
<table border="1">
<xsl:apply-templates />
</table>
</xsl:template>
<xsl:template match="produto">
<tr>
<td><xsl:value-of select="@id" /></td>
<td>
<a><xsl:attribute
name="href">detalhes_produto.asp?cod=<xsl:value-of
select="@id" /></xsl:attribute><xsl:value-of
select="descricao" /></a>
</td>
<td>
R$ <xsl:value-of select="preco" />
<xsl:choose>
<xsl:when test="preco < 1">
<b> Promoção!</b>
</xsl:when>
<xsl:when test="preco < 2">
<b> Preço bom!</b>
</xsl:when>
<xsl:otherwise>
<b> 3X no cartão!</b>
</xsl:otherwise>
</xsl:choose>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="categoria">
== <xsl:value-of select="@description" /> ==
<xsl:for-each select="produto">
- <xsl:value-of select="descricao" /> -
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Veja o resultado:
== Cadernos ==
- Caderno Univ. 100 folhas -
- Caderno Univ. 10 mat. -
== Reguas ==
- Regua Comum, 20cm -
- Esquadro 15x20cm -
A dica é: você pode usar for-each para simplificar o código e não precisar escrever um número enorme de templates
para fazer coisas simples. Se o conteúdo do for-each for complexo, ou você forreutilizá-lo em outros lugares,
escreva-o num template separado e não num for-each.
sort
Provavelmente você estava se perguntando se não havia um meio de ordenar os dados. Há. Veja:
<xsl:template match="loja">
<xsl:apply-templates select="categoria/produto">
<xsl:sort select="descricao" order="descending" />
</xsl:apply-templates>
</xsl:template>
<xsl:template match="produto">
- <xsl:value-of select="descricao" /> -
</xsl:template>
</xsl:stylesheet>
E veja o resultado:
● select
Uma expressão XPath retornando o valor a ordenar
● order
Pode ser ascending (ascendente) ou descending (descendente). O default é ascending.
● data-type
O tipo de dado a ordenar, pode ser number ou text. O default é text.
O elemento sort também pode ser usado dentro de um elemento for-each, desde que seja o primeiro elemento. O
código abaixo faz exatamente a mesma coisa que o anterior:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="loja">
<xsl:for-each select="categoria/produto">
<xsl:sort select="descricao" order="descending" />
...
<xsl:template match="produto">
<xsl:param name="codigo" />
Código: <xsl:value-of select="$codigo" />
</xsl:template>
...
<xsl:apply-templates select="//produto">
<xsl:with-param name="codigo" value="9" />
</xsl:apply-templates>
Seu documento XSL também pode receber um parâmetro, se o elemento param for colocado como filho do
elemento stylesheet, assim:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="codigo" />
<xsl:template match="loja">
<xsl:for-each select="categoria/produto[@id=$codigo]">
<xsl:value-of select="descricao" />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
O arquivo acima vai exibir a descrição do produto cujo id for recebido através do parâmetro "codigo". Você deve
consultar a documentação de sua linguagem de programação ou de seu interpretador de XML para entender como
passar parâmetros para um arquivo XSLT.
<xsl:apply-imports />
Você deve imaginar as possibilidades que isto lhe dá em termos de reuso de código e organização de templates.