Showing posts with label xml. Show all posts
Showing posts with label xml. Show all posts

Monday, March 05, 2007

Tuttocittá

Ho scoperto una cosa curiosa riguardo il sito di Tuttocittá.
Se inviate una query HTTP del tipo

http://www.tuttocitta.it/tcoln/action?msez=500&com=milano&in=via%20solferino&nc=16&_=

(per esempio con curl) riceverete una risposta fatta cosí (ho aggiunto qualche a capo per rendere il tutto piú leggibile):

<code>0</code>
<x>9.187145</x>
<y>
45.4745</y>
<z>
0.6</z>
<zcent>
0.6</zcent>
<fraz></fraz>
<c>
Milano</c>
<com>
Milano (MI)/com>
<prov>
mi</prov>
<topo>
Via Solferino</topo>
<civ>
16</civ>
<cdloc>
51557</cdloc>
<reg>
Lombardia</reg>
<ul></ul>

Insomma, un frammento di XML. Frammento perché non ha un elemento radice. L'origine della cosa é ovviamente l'approccio Ajax della pagina di Tuttocittá.

Bene. Questo si presta ad essere utilizzato in modi interessanti. Se fosse XML e non un frammento tutto sarebbe piú semplice.

Supponiamo per esempio di avere un elenco di indirizzi e volerli geocodificare (recuperare le coordinate geografiche) tutti. E magari avere come risultato un file GPX per poter visualizzare i diversi punti in Google Earth. O ancora inviare i punti al proprio GPS.

Supponiamo inoltre che gli indirizzi siano giá in formato XML. Per esempio così:

<results>
  <match>
    <group id="nome" value="AMANTE CASELLA CENTRO SRL" />
    <group id="indirizzo" value="C.SO MATTEOTTI 61 , TORINO 10121 (TO) 011/540641 " />
    <group id="tipo" value="C.SO" />
    <group id="via" value="MATTEOTTI" />
    <group id="civico" value="61" />
    <group id="citta" value="TORINO" />
    <group id="CAP" value="10121" />
    <group id="prov" value="(TO)" />
    <group id="tel" value="011/540641" />
  </match>
  ...
</results>

(Questo esempio é stato effettivamente prodotto da uno strumento di prova di espressioni regolari con possibilitá di esportare i risultati in XML).

Se l'output di Tuttocittá fosse effettivamente XML potremmo scrivere uno stylesheet XSLT che, facendo leva sulla funcione XPath document() recupera latitudine e longitudine per ogni indirizzo. Cosí non é, ma possiamo scrivere una estensione XSL che ci consenta di trattare opportunamente frammenti XML. Le poche righe di codice C# che seguono servono allo scopo:

using System;
using
System.IO;
using
System.Text;
using
System.Xml;
using
System.Xml.XPath;
using
System.Xml.Xsl;
using
System.Net;
public class DocFrag {
   
public XPathNodeIterator documentFragment(string url) {
       
WebClient wc=new WebClient();
       
string s=wc.DownloadString(url);
       
s="<root>"+s+"</root>";
       
XPathDocument xd=new XPathDocument(new StringReader(s));
       
XPathNavigator xpn=xd.CreateNavigator();
       
return xpn.Select("/");
   
}
}

(Da compilare con il comando "csc -t:library -debug+ -D:DEBUG docfrag.cs")

Con questa ulteriore arma siamo pronti per scrivere lo stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

               xmlns:ext="http://www.docfrag.org">

       <xsl:output indent='yes'/>

 

       <xsl:template match="*"/>

      

       <xsl:template match="/">

               <gpx version="1.1" creator="GPX.XSL"

                               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

                               xmlns="http://www.topografix.com/GPX/1/1"

                               xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">

                       <xsl:for-each select="results/match">

                               <xsl:variable name='url'>

                                       <xsl:text>http://www.tuttocitta.it/tcoln/action?msez=500&amp;com=</xsl:text>

                                       <xsl:value-of select="group[@id='citta']/@value"/>

                                       <xsl:text>&amp;in=</xsl:text>

                                       <xsl:value-of select="group[@id='tipo']/@value"/>

                                       <xsl:text>%20</xsl:text>

                                       <xsl:value-of select="group[@id='via']/@value"/>

                                       <xsl:text>&amp;nc=</xsl:text>

                                       <xsl:value-of select="group[@id='civico']/@value"/>

                                       <xsl:text>&amp;_=</xsl:text>

                               </xsl:variable>

 

                               <xsl:variable name='doc' select="ext:documentFragment($url)"/>

 

                               <xsl:choose>

                                       <xsl:when test='$doc/root/x and $doc/root/y'>

                                               <wpt lon='{$doc/root/x}' lat='{$doc/root/y}'>

                                                       <name><xsl:value-of select="group[@id='nome']/@value"/></name>

                                                       <desc><xsl:value-of select="group[@id='indirizzo']/@value"/></desc>

                                               </wpt>

                                       </xsl:when>

                                       <xsl:otherwise>

                                               <xsl:message>Non trovato: <xsl:value-of select="group[@id='indirizzo']/@value"/></xsl:message>

                                       </xsl:otherwise>

                               </xsl:choose>

                       </xsl:for-each>

               </gpx>

       </xsl:template>

</xsl:stylesheet>

Per applicare lo stylesheet utilizzeró l'ottimo nxslt di Oleg Tkachecnko con il comando:

nxslt indirizzi.xml gpx.xsl -af docfrag.dll -ext DocFrag xmlns=http://www.docfrag.org -o p.gpx

...e il gioco é fatto.

del.icio.us tags: , , , ,