Grouping e Processamento Condicional
Aula 4 de 5
xsl:for-each-group (XSLT 2.0)
Agrupa elementos por um valor chave de forma nativa e eficiente.
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<h1>Produtos por Categoria</h1>
<xsl:for-each-group select="//produto" group-by="categoria">
<h2>Categoria: <xsl:value-of select="current-grouping-key()"/></h2>
<p>Total: <xsl:value-of select="count(current-group())"/> produtos</p>
<ul>
<xsl:for-each select="current-group()">
<li><xsl:value-of select="concat(nome, ' - R$ ', preco)"/></li>
</xsl:for-each>
</ul>
</xsl:for-each-group>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Muenchian Grouping (XSLT 1.0)
Técnica clássica para agrupar quando não há suporte nativo a grouping.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- Key para Muenchian Grouping -->
<xsl:key name="produtos-por-categoria" match="produto" use="categoria"/>
<xsl:template match="/">
<html>
<body>
<h1>Produtos por Categoria (Muenchian)</h1>
<!-- Seleciona cada categoria uma única vez -->
<xsl:for-each select="//produto
[generate-id() = generate-id(key('produtos-por-categoria', categoria)[1])]">
<h2>Categoria: <xsl:value-of select="categoria"/></h2>
<ul>
<xsl:for-each select="key('produtos-por-categoria', categoria)">
<li><xsl:value-of select="concat(nome, ' - R$ ', preco)"/></li>
</xsl:for-each>
</ul>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Processamento Condicional
<xsl:template match="produto">
<!-- xsl:choose / when / otherwise (switch-case) -->
<xsl:choose>
<xsl:when test="number(preco) > 1000">
<tr class="caro">
<td><xsl:value-of select="nome"/></td>
<td style="color:red"><xsl:value-of select="preco"/></td>
</tr>
</xsl:when>
<xsl:when test="number(preco) > 100">
<tr class="medio">
<td><xsl:value-of select="nome"/></td>
<td><xsl:value-of select="preco"/></td>
</tr>
</xsl:when>
<xsl:otherwise>
<tr class="barato">
<td><xsl:value-of select="nome"/></td>
<td><xsl:value-of select="preco"/></td>
</tr>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Sorting e Numbering
<xsl:template match="/">
<!-- Sorting -->
<xsl:for-each select="//produto">
<xsl:sort select="preco" data-type="number" order="descending"/>
<xsl:sort select="nome" data-type="text" order="ascending"/>
<p><xsl:value-of select="concat(nome, ': R$ ', preco)"/></p>
</xsl:for-each>
<!-- Numbering -->
<ol>
<xsl:for-each select="//livro">
<li value="{position()}">
<xsl:value-of select="titulo"/>
</li>
</xsl:for-each>
</ol>
</xsl:template>
Keys em XSLT
Keys criam índices para acesso rápido a nós.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="por-id" match="produto" use="@id"/>
<xsl:key name="por-nome" match="produto" use="nome"/>
<xsl:template match="/">
<!-- Lookup por key -->
<xsl:for-each select="key('por-id', 'P001')">
<p><xsl:value-of select="nome"/></p>
</xsl:for-each>
<xsl:for-each select="key('por-nome', 'Notebook')">
<p>ID: <xsl:value-of select="@id"/> - Preço: <xsl:value-of select="preco"/></p>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Lab: Relatório com Grouping e Condicionais
# 1. Documento XML
cat << 'EOF' > vendas.xml
<?xml version="1.0"?>
<vendas>
<vendedor nome="Ana">
<mes valor="10000" periodo="jan"/>
<mes valor="15000" periodo="fev"/>
<mes valor="8000" periodo="mar"/>
</vendedor>
<vendedor nome="Carlos">
<mes valor="12000" periodo="jan"/>
<mes valor="9000" periodo="fev"/>
<mes valor="20000" periodo="mar"/>
</vendedor>
<vendedor nome="Beatriz">
<mes valor="7000" periodo="jan"/>
<mes valor="11000" periodo="fev"/>
<mes valor="13000" periodo="mar"/>
</vendedor>
</vendas>
EOF
# 2. Folha XSLT com grouping e condicionais
cat << 'EOF' > relatorio.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:key name="vendedor" match="vendedor" use="nome"/>
<xsl:template match="/">
<html>
<head><title>Relatório de Vendas</title></head>
<body>
<h1>Relatório Trimestral</h1>
<xsl:for-each select="//vendedor">
<xsl:variable name="total"
select="sum(mes/number(@valor))"/>
<h2>Vendedor: <xsl:value-of select="@nome"/></h2>
<table border="1">
<tr><th>Período</th><th>Valor</th><th>Status</th></tr>
<xsl:for-each select="mes">
<xsl:sort select="@periodo"/>
<tr>
<td><xsl:value-of select="@periodo"/></td>
<td><xsl:value-of select="format-number(@valor, '#.00')"/></td>
<td>
<xsl:choose>
<xsl:when test="number(@valor) > 15000">
<span style="color:green">Excelente</span>
</xsl:when>
<xsl:when test="number(@valor) > 10000">
<span style="color:blue">Bom</span>
</xsl:when>
<xsl:otherwise>
<span style="color:orange">Abaixo da meta</span>
</xsl:otherwise>
</xsl:choose>
</td>
</tr>
</xsl:for-each>
<tr>
<td><strong>Total</strong></td>
<td colspan="2"><strong>
<xsl:value-of select="format-number($total, '#.00')"/>
</strong></td>
</tr>
</table>
<xsl:if test="$total > 30000">
<p style="color:green">★ Vendedor destaque do trimestre!</p>
</xsl:if>
</xsl:for-each>
<hr/>
<p><strong>Meta do trimestre: R$ 30.000 por vendedor</strong></p>
<p>Vendedores acima da meta:
<xsl:value-of select="count(//vendedor[sum(mes/number(@valor)) > 30000])"/>
</p>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
EOF
# 3. Executar
xsltproc relatorio.xsl vendas.xml > relatorio.html
type relatorio.html
Muenchian grouping é elegante para XSLT 1.0; xsl:for-each-group simplifica drasticamente no XSLT 2.0.