Com o advento de
integração SOA e Web Services, cada dia mais temos que adaptar o
conteúdo de nossos dados com agilidade. No meu caso, eu atuo em uma
empresa na qual temos vários parceiros comerciais que consomem nosso
catálogo de produtos, e a forma de integração que temos é com o
uso de XML – bom para acelerar o desenvolvimento do XML para cada
parceiro.
Nós criávamos um XML para cada um, só que isso não era
feito de forma automática. A codificação de cada XML era feita
individualmente e os objetos eram reutilizados. Só que então tivemos
problemas com performance pois a consulta era feita em tempo real,
ou seja, os parceiros concorriam com os clientes que navegavam no
site.
O XML demorava para ser carregado e, quando era necessário
efetuar algum filtro por categoria, gênero, marca, isso era
feito na aplicação, ou seja, o catálogo era carregado inteiro e
filtrado através de um delegate. Quando eram poucos parceiros e poucos clientes, tudo bem. Mas um ano depois com muito mais acessos o
negócio ficou ruim. É isso que vou mostrar como fazer aqui, como
esse problema foi resolvido e os passos abaixo:
- Não acessar mais
o banco diretamente, mas sim um XML base; - Efetuar a
formatação dos dados baseado em um XSL e não mais fazer isso na
codificação; - Colocar os
critérios dentro do XSL em vez de fazer na aplicação ou em
query.
Neste artigo, utilizei
Visual C# 2010 Express, Framework 4.0 e para o aplicativo teste fiz
um console application.
O que é um arquivo XSL e para que serve
Segundo a Wikpedia, XSL Transformations ou XSLT (eXtensible Stylesheet Language for
Transformation – linguagem extensível para folhas de estilo de
transformações) é uma linguagem de marcação XML usada para
criar documentos XSL que, por sua vez, definem a apresentação dos
documentos XML nos browsers e outros aplicativos que a suportem. Ou seja, serve para formatar a saída de dados do XML
Fonte: XSLT – Wikipédia
Agora que estamos
alinhados com o cenário, vamos começar.
XML Base
Isso é algo muito
tranquilo. Foi criado um JOB que gera um XML em uma pasta visível
pela aplicação com todo catálogo de produtos.
Para esse estudo,
vamos usar o exemplo abaixo:
<?xml version="1.0"
encoding="utf-8" ?>
<catalogo>
<item>
<Sku>500135</Sku>
<Descricao><![CDATA[Bola de Tênis Wilson
Championship ]]></Descricao>
<CodigoCategoria>102</CodigoCategoria>
<Categoria>RACKET
</Categoria>
<CodigoMarca>15</CodigoMarca>
<Marca>WILSON</Marca>
</item>
<item>
<Sku>500139</Sku>
<Descricao><![CDATA[Haltere Ferro Centauro Meio
Kg]]></Descricao>
<CodigoCategoria>107</CodigoCategoria>
<Categoria>BODY
</Categoria>
<CodigoMarca>999</CodigoMarca>
<Marca>FORTE</Marca>
</item>
<item>
<Sku>500140</Sku>
<Descricao><![CDATA[Halteres Ferro Centauro 1 Kg
]]></Descricao>
<CodigoCategoria>107</CodigoCategoria>
<Categoria>BODY
</Categoria>
<CodigoMarca>999</CodigoMarca>
<Marca>FORTE</Marca>
</item>
<item>
<Sku>500141</Sku>
<Descricao><![CDATA[Halteres Ferro Centauro 2Kg
]]></Descricao>
<CodigoCategoria>107</CodigoCategoria>
<Categoria>BODY
</Categoria>
<CodSubGrupo>34</CodSubGrupo>
<Marca>FORTE</Marca>
</item>
<item>
<Sku>500142</Sku>
<Descricao><![CDATA[Halteres Ferro Centauro 3
kg]]></Descricao>
<CodigoCategoria>107</CodigoCategoria>
<Categoria>BODY
</Categoria>
<CodigoMarca>999</CodigoMarca>
<Marca>FORTE</Marca>
</item>
<item>
<Sku>500143</Sku>
<Descricao><![CDATA[Halteres Ferro Centauro 4Kg
]]></Descricao>
<CodigoCategoria>107</CodigoCategoria>
<Categoria>BODY
</Categoria>
<CodigoMarca>999</CodigoMarca>
<Marca>FORTE</Marca>
</item>
</catalogo>
XSL
Bom, o XSL pode ficar
ao gosto de cada um, eu vou colocar dois exemplos aqui. O primeiro
nos dará a seguinte saída do XML Base:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output
method='xml' indent='yes' version='1.0' encoding='UTF-8'
standalone='yes' cdata-section-elements='NOME'/>
<xsl:template
match="/">
<produtos>
<xsl:for-each
select="catalogo/item[CodigoCategoria='102' and
CodigoMarca='999']">
<PRODUTO>
<SKU>
<xsl:value-of select="Sku"/>
</SKU>
<NOME>
<xsl:value-of select="Descricao"/>
</NOME>
<CATEGORIA>
<xsl:value-of select="Categoria"/>
</CATEGORIA>
</PRODUTO>
</xsl:for-each>
</produtos>
</xsl:template>
</xsl:stylesheet>
O segundo oferece a
saída abaixo:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output
method='xml' indent='yes' version='1.0' encoding='UTF-8'
standalone='yes' cdata-section-elements='NOME'/>
<xsl:template
match="/">
<produtos>
<xsl:for-each
select="catalogo/item[CodigoCategoria='102']">
<ITEM>
<CODIGO>
<xsl:value-of select="Sku"/>
</CODIGO>
<DESCRICAO>
<xsl:value-of select="Descricao"/>
</DESCRICAO>
</ITEM>
</xsl:for-each>
</produtos>
</xsl:template>
</xsl:stylesheet>
Esses arquivos devem
ser criados em um local no qual a aplicação possa lê-los.
Agora vamos entender
algumas tags do XSL:
Aqui é feita a
declaração do XSL, a versão e qual namespace (xmlns) é utilizado,
a tag deve ser fechada.
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
A saída do arquivo é
definida aqui, além do método/formato (method), a versão do formato
(version), a codificação (encoding) e as tags que terão a saída
com CDATA (cdata-section-elements).
<xsl:output
method='xml' version='1.0' encoding='UTF-8'
cdata-section-elements='NOME'/>
Nesse momento, o nó
raiz é escolhido para trabalhar, a tag deve ser fechada.
<xsl:template
match="/">
Tudo o que não
estiver usando o prefixo xsl será escrito da mesma forma como
estiver no XSL. Vamos ver o loop agora. Aqui é o começo do
loop:
<xsl:for-each
select="catalogo/item">
E aqui é o seu final:
</xsl:for-each>
Tudo o que estiver
entre essas tags será repetido até que todos os nós tenham sido
lidos. Para escrever os
valores que estão no XML, basta usar a tag value-of e, no atributo
select, colocar o campo que deseja, conforme o exemplo abaixo.
<xsl:value-of
select="Sku"/>
Adicionando critérios ao XSL
Assim como em uma query, é possível adicionar
critérios a XSL, basta adicioná-los conforme o código abaixo na
tag for-each.
No primeiro exemplo, o XSL trará apenas os produtos com
o campo CodigoCategoria igual a 102, já no segundo exemplo todos com
CodigoCategoria igual a 102 e o CodigoMarca igual 999.
<xsl:for-each
select="catalogo/item[CodigoCategoria='102']">
<xsl:for-each
select="catalogo/item[CodigoCategoria='102' and
CodigoMarca='999']">
Usando XSL para formatar o XML
O .NET Framework
nos dá suporte para trabalhar com arquivos XML através do Namespace
System.Xml e para trabalhar com XSL com o Namespace System.Xml.Xsl.
Vamos utilizar a
classe XslCompiledTransform
para formatar o XML com nosso XSL. Essa classe é
um processador de XSL e XSLT. Ela ainda é a sucessora da classe
XslTransform e, segundo a MSDN, oferece ganhos de performance com
relação à classe antiga (e obsoleta na versão 4.0 do .NET Framework).
Uma das sobrecargas do
construtor do XslCompiledTransform recebe um XmlReader, então
carreguei o XSL dentro de um XmlReader e criei o objeto do
XslCompiledTransform.
O método Transform de nosso objeto
XslCompiledTransform tem uma sobrecarga que recebe o xml base, uma
lista de argumentos de XSLT que será passada como nulo e um objeto
que terá o resultado da transformação – nesse caso, é passado um
StringWriter. Pronto, agora o objeto StringWriter tem o XML carregado
conforme as definições do XSL, tudo bem simples.
Código
Aqui está todo o
código utilizado para a aplicação funcionar corretamente. Confira abaixo:
using System;
using System.Xml;
using System.Xml.Xsl;
using System.IO;
namespace
GeradorXmlParceiro
{
class Program
{
static string
caminhoXmlBase = @"C:XmlBaseXmlBase.xml";
static string
caminhoXsl = @"C:XslXsl1.xsl";
static string
caminhoXmlFormatado = @"C:XmlTransformadoXmlTransformado.xml";
static void
Main(string[] args)
{
CriarXmlComXsl();
}
static void
CriarXmlComXsl()
{
XmlDocument
xml = new XmlDocument();
xml =
FormartarXmlBaseParaFormatoComXsl(caminhoXsl);
xml.Save(caminhoXmlFormatado);
}
static
XmlDocument FormartarXmlBaseParaFormatoComXsl(string caminhoXsl)
{
XmlDocument
xml = new XmlDocument();
XslCompiledTransform xsl = new XslCompiledTransform();
XmlDocument
xmlBase = ObterXmlBase();
try
{
XmlReader reader = XmlReader.Create(caminhoXsl);
xsl.Load(reader);
StringWriter writer = new StringWriter();
xsl.Transform(xmlBase, null, writer);
xml.LoadXml(writer.ToString());
}
catch
(Exception ex)
{
throw
new Exception("Ocorreu um erro ao efetuar a transformação do
XML.", ex.InnerException);
}
return xml;
}
static
XmlDocument ObterXmlBase()
{
XmlDocument
xml = new XmlDocument();
try
{
xml.Load(caminhoXmlBase);
}
catch
(Exception ex)
{
throw
new Exception("Ocorreu um erro ao carregar o XML.",
ex.InnerException);
}
return xml;
}
}
}
É isso aí! Espero que
este artigo possa contribuir para o crescimento da comunidade. Até a
próxima!