Gerando notícias em RSS como WebServices

Truques com o fornecimento de DataSets


Os WebServices chegaram causando uma grande revolução na Web. Antes dos WebServices os consumidores de informações tinham grande trabalho garimpando sites e mais sites com centenas ou milhares de páginas HTML para encontrar a informação que desejavam.

Discretamente os WebServices foram chegando, como quem não quer nada, e tomando seu espaço em meio a esse garimpo de informações, entregando para o consumidor a informações que ele quer, quando ele quer e na forma que ele quer.

Diversos sites de informação nacionais já aderiram ao formato RSS, que é um padrão (?!) para fornecimento de notícias em formato XML que tem se tornado extremamente popular na Web.

A vantagem desta adesão é que o consumidor de notícias não fica mais garimpando inúmeros sites web com centenas ou até milhares de páginas para encontrar o que deseja. A informação que desejamos agora é entregue no momento que desejamos e na forma que desejamos.

Para isso utilizamos aplicações que agem como leitores de RSS. Fiz testes com o FeedReader. Em aplicações como esta podemos cadastrar as origens RSS que desejamos acompanhar e a aplicação nos avisa sempre que houver alguma novidade. No caso do FeedReader ele funciona inclusive como navegador a partir das notícias que traz para nós.

Bem, e como fazer para entrar para o mundo do RSS ? Como fazer para publicar noticias neste formato ?

O formato RSS é um arquivo XML com um conjunto de elementos específicos e é particularmente simples. Possui 3 elementos principais : rss, channel e item . O elemento rss é o root do documento XML, contem todos os demais. Dentro do elemento rss temos apenas um elemento channel.

No elemento channel temos vários outros elementos que descrevem o documento rss : Quem gerou, o que contém, quando foi gerado, etc.

Dentro do elemento channel teremos também vários elementos item. Cada elemento item representa uma notícia, e conterá alguns elementos descrevendo a notícia, tal como descrição, entre outros.

Veja um pequeno exemplo :

<?xml version="1.0"        ?><rss version="2.0"><channel>    <title>DevX        Featured Content</title>       <link>http://www.devx.com</link>       <description>Latest DevX Content</description>       <language>en-us</language>       <copyright>Copyright 2003 DevX</copyright>       <lastBuildDate>Fri, 24 Jan 2003 12:38:45 PST</lastBuildDate>       <item>           <title>Attend to your future. </title>           <description>A future where millions of users are waiting.</description>           <link>http://www.devx.com/content/id/10559</link>           <author> editorial@devx.com </author>           <pubDate>Wed, 22 Jan 2003 11:19:28 PST</pubDate>       </item>       <item>            <title> etc </title>            <description> etc </description>            <link> etc </link>            <pubDate> etc </pubDate>        </item></channel></rss>


Se você já teve experiências com XML a essa altura deve estar imaginando a possibilidade de fazer um loop simples ou algo parecido para gerar este arquivo XML a partir de uma tabela no banco de dados.

Realmente, também foi meu primeiro pensamento. Só que isso seria simples demais, muito manual... vindo do .NET espero algo melhor. Então é o que vamos fazer : Vamos gerar essa estrutura de RSS diretamente a partir de um DataSet, automaticamente.

Então vamos começar ! Vamos criar um novo projeto, ASP.NET WebService. Neste projeto vamos considerar o uso de uma tabela chamada noticias, com a seguinte estrutura :

         idNoticia      int         Titulo          varchar(200)         Sinopse       text         Noticia        text         Data           smalldateTime


Vamos então fazer a geração do dataSet para exportar como RSS. Talvez você não esteja acostumado a gerar um dataSet diretamente, o mais comum em aplicações simples é gera-lo a partir de um adapter, mas podemos fazer a geração direta de um dataset clicando com o botão direito no projeto (solution explorer), add->new item . Na janela que se abrirá uma das opções será DataSet.

A montagem do documento RSS começa já pelo nome do DataSet. O nome dele precisará ser rss, cuidadosamente digitado em minúscula, pois esse nome será usado como root do documento XML gerado.

Feito isso devemos abrir o DataSet. Veremos a tela de designer de um dataset, em branco. Precisamos então criar duas tabelas : Uma tabela para representar o elemento channel outra para representar o elemento item. As tabelas precisarão ter exatamente este nome, em minuscula, pois o nome da tabela será o elemento XML gerado.

Cada elemento dentro de channel será um campo da tabela channel, cada elemento dentro de item será um campo na tabela item.

Porém criaremos as tabelas de uma forma um pouco diferente : A tabela channel criaremos como uma tabela normal, iremos na ToolBox e pegaremos o objeto Element para cria-la. Mas a tabela item não. A tabela item iremos criar como um complexType.

Provavelmente você deve estar se perguntando qual a diferença. A definição de um elemento é a definição de algo que existe, significa que ao jogarmos a tabela channel no documento estamos dizendo que ela existirá nesse local, desta forma.

Já a definição de um complexType é a definição de um "tipo". Estamos dizendo que o tipo item existe, mas ainda não dissemos onde iremos utiliza-lo.


Tabela channel

         title          string         link           anyUri         description string         language    string         copyright   string         docs         string         lastBuildDate string -> Por causa da formatação

Tabela item

         title          string         description string         link           string         author       string         pubDate     string


Cuidado com a caixa das letras, XML é sensível a caixa das letras.

Bem, temos então as duas tabelas, mas inicialmente montadas como duas tabelas independentes e ainda não dissemos onde a tabela item irá aparecer. É neste momento então que vamos montar a relação existente entre as duas.

Vamos acrescentar na tabela channel um novo elemento, item, e vamos definir o tipo deste elemento como sendo item, o complexType que acabamos de definir. Ele não aparecerá na combo, mas você pode digitar e será aceito.

Após salvar este XSD, o designer mostrará o elemento channel e o complexType item de forma independente, mas aparecerá um outro elemento, item, ligado ao elemento channel e não poderemos altera-lo, pois sua definição encontra-se no complextype item.


E por que não usamos duas tabelas e um relacionamento comum, Relation ?

Porque em um Relation precisariamos da chave e a chave seria exportada no XML, o que não é adequado. Neste formato podemos contornar o problema da chave, na verdade o designer do DataSet nos dá um grande empurrão, como veremos.

image001.gif


Vamos agora montar os objetos de acesso a banco. Inicialmente vamos inserir uma instância de nosso dataset na janela de design do WebService. Fazemos isso inserindo o objeto dataset da toolbox na janela de design. É exibida uma janela perguntando se o dataset será typed ou untyped e no caso de typed, qual o tipo. O tipo rss aparecerá como uma opção, pois o XSD já está salvo.

Precisaremos também de uma conexão com o banco de dados, que vamos chamar de CN, e um dataAdapter, que vou chamar de DA. Não precisaremos criar os commands de insert, update e delete, apenas o command de select.

Precisaremos adaptar a instrução select para trazer o conteúdo que precisamos preencher em cada item. Titulo já temos, description é o campo sinopse, pubDate é o campo data. Falta o campo link e author e mais um. Sim, mais um. Ao realizarmos nosso "truque" com o arquivo XSD o Designer do DataSet (MSDATASETGENERATOR) entendeu que não desejávamos exportar nenhuma chave de ligação nas tabelas. Mas para que os dados sejam montados corretamente, é necessário uma chave. Por isso o DataSetGenerator gerou na nossa classe rss uma chave em cada tabela. A chave no caso se chama channel_id, esse nome é formado com base no nome da tabela pai.

Precisamos então preencher os campos channel_id para executar o relacionamento. Podemos trazer os valores do channel_id já preenchido pela instrução select.

Veja então como fica a instrução select :


SELECT TOP 4 idNoticia, titulo, Sinopse, noticia, data, 'http://www.devaspnet.com.br/noticias.aspx?cod=' + convert(varchar(5), idNoticia) AS link, 'dennes@bufaloinfo.com.br' AS author, 1 as channel_id FROM noticias ORDER BY idNoticia DESC

O link foi montado apontando para a página noticias.aspx, transmitindo o código da notícia como parâmetro. Tornei fixo o campo author e trouxe também fixo o valor de channel_id, como 1. Observe também o top 4 e o order by desc, garantindo que o serviço estará sempre devolvendo as 4 notícias mais recentes.

Vamos então ao código de nosso WebMethod. Teremos primeiramente que criar as informações da tabela channel. Em geral essas informações são fixas, veja como fica :


Dim dtr As rss.channelRow

dtr = Rss1.channel.NewchannelRow
dtr.description = "Últimas notícias .NET"
dtr.copyright = "Copyright 2003 DevASPNet"
dtr.language = "pt-br"
dtr.link = New System.Uri("http://www.devaspnet.com.br")
dtr.title = "DevNoticias - Serviço de noticias dos UGs brasileiros"


Mas temos o elemento lastBuildDate que irá variar, este precisaremos estar lendo da base de dados. Vamos criar para isso um objeto Command para ler a data da última notícia, veja como fica a instrução SQL :


SELECT MAX(data) AS Expr1 FROM noticias

image003.gif
Acrescentamos então o lastBuildDate na montagem do channel :


CN.Open()

Dim ciDe As New System.Globalization.CultureInfo("en-us")
ciDe.DateTimeFormat.FullDateTimePattern = "R"

Dim dtr As rss.channelRow
Dim d As Date

dtr = Rss1.channel.NewchannelRow
dtr.description = "Últimas notícias .NET"
dtr.copyright = "Copyright 2003 DevASPNet"
dtr.language = "pt-br"
dtr.link = New System.Uri("http://www.devaspnet.com.br")
dtr.title = "DevNoticias - Serviço de noticias dos UGs brasileiros"

d = cmdUltimadata.ExecuteScalar
dtr.lastBuildDate = String.Format(ciDe.DateTimeFormat, "{0:R}", d)


Observe o recurso utilizado para realizar a formatação da data : A formatação exigida por um documento RSS é uma formatação muito específica, não podemos deixar que a aplicação dependa da configuração do servidor. Então podemos criar um objeto cultureInfo para formatar a data e configurar a formatação deste objeto CultureInfo. A formatação "R" determina a formatação da data exatamente como necessário para a montagem de um RSS.


Feito isso vamos inserir a linha dtr na tabela channel e fazer o preenchimento da tabela item. Devemos lembrar, antes de inserir a linha na tabela channel, de preencher o campo de relacionamento com um valor. Lembre-se que trouxemos o valor fixo 1 no select das notícias, portanto preencheremos o campo de relacionamento com o valor 1. Veja como fica tudo isso em código :

CN.Open()

Dim ciDe As New System.Globalization.CultureInfo("en-us")
ciDe.DateTimeFormat.FullDateTimePattern = "R"

Dim dtr As rss.channelRow
Dim d As Date

dtr = Rss1.channel.NewchannelRow
dtr.description = "Últimas notícias .NET"
dtr.copyright = "Copyright 2003 DevASPNet"
dtr.language = "pt-br"
dtr.link = New System.Uri("http://www.devaspnet.com.br")
dtr.title = "DevNoticias - Serviço de noticias dos UGs brasileiros"

d = cmdUltimadata.ExecuteScalar
dtr.lastBuildDate = String.Format(ciDe.DateTimeFormat, "{0:R}", d)

dtr("channel_id") = 1
Rss1.channel.Rows.Add(dtr)

DA.Fill(Rss1.item)
CN.Close()


Mas o truque para que o fill da tabela item funcione é que precisamos configurar corretamente o tableMappings do dataAdapter para que os campos do select sejam jogados para os elementos certos no rss. Ao clicar nos "..." na propriedade tableMappings do Adapter podemos pedir para fazer o mapeamento com base no dataset existente, então basta selecionar nas combos o nome da tabela item e os campos adequados.

image005.gif

Porém existe um problema que o Fill o adapter não resolve. Precisamos formatar a data da noticia no formato necessário a um RSS. Então precisaremos montar um loop para isso. Veja como fica :

  CN.Open() Dim ciDe As        New System.Globalization.CultureInfo("en-us") ciDe.DateTimeFormat.FullDateTimePattern = "R" Dim dtr As        rss.channelRow Dim dtr2 As rss.itemRow Dim d As Date dtr = Rss1.channel.NewchannelRow dtr.description = "Últimas notícias .NET" dtr.copyright = "Copyright 2003 DevASPNet" dtr.language = "pt-br" dtr.link = New System.Uri("http://www.devaspnet.com.br") dtr.title = "DevNoticias - Serviço de noticias dos UGs brasileiros" d = cmdUltimadata.ExecuteScalar dtr.lastBuildDate = String.Format(ciDe.DateTimeFormat, "{0:R}",        d) dtr("channel_id")        = 1 Rss1.channel.Rows.Add(dtr) DA.Fill(Rss1.item)  CN.Close() For Each dtr2        In Rss1.item.Rows       dtr2.pubDate = String.Format(ciDe.DateTimeFormat, "{0:R}",  _

                         CType(dtr2.pubDate,  Date))  Next


Por fim, precisamos retornar o XML do rss . Mas não podemos retornar o DataSet, pois se tentarmos retornaremos não apenas o rss mas também seu schema, o que não desejamos. Então precisamos usar o método getXML e retornar um XMLDocument. Veja como fica :

  CN.Open() Dim ciDe As        New System.Globalization.CultureInfo("en-us") ciDe.DateTimeFormat.FullDateTimePattern = "R" Dim dtr As        rss.channelRow Dim d As Date dtr = Rss1.channel.NewchannelRow dtr.description = "Últimas notícias .NET" dtr.copyright = "Copyright 2003 DevASPNet" dtr.docs = "http://backend.userland.com/rss" dtr.language = "pt-br" dtr.link = New System.Uri("http://www.devaspnet.com.br") dtr.title = "DevNoticias - Serviço de noticias dos UGs brasileiros" d = cmdUltimadata.ExecuteScalar dtr.lastBuildDate = String.Format(ciDe.DateTimeFormat, "{0:R}",        d) dtr("channel_id")        = 1 Rss1.channel.Rows.Add(dtr) DA.Fill(Rss1.item) CN.Close() For Each dtr2        In Rss1.item.Rows       dtr2.pubDate = String.Format(ciDe.DateTimeFormat, "{0:R}", _

                   CType(dtr2.pubDate,        Date)) Next Rss1.Relations(0).Nested = Truedoc.LoadXml(Rss1.GetXml)Return (doc)


Pronto ! Temos uma fonte RSS ! Podemos então registrar essa fonte RSS em um leitor de RSS, como o FeedReader, e ele irá mostar as notícias normalmente.


O que está esperando ? Crie já um serviço de notícias para o seu site !

Dennes Torres
MCAD,MCSD,MCSE,MCBA