Transformasjon fra XML til XML
Bare 100m
Vi ønsker å ekstrahere 100m resultatene og lage en ny xml-fil som er organisert på en litt annen måte. En skisse:
<Sprint-100m> <OlympicGame place="Atlanta - 1996"> ... athlets som før men bare fra 100m... </OlympicGame> <OlympicGame place="Barcelona - 1992"> ... athlets som før men bare fra 100m ... </OlympicGame> </Sprint-100m>
Vi ønsker altså:
- Introdusere en ny dokumenttype: Sprint-100m
- Endre, forenkle, attributtliste til elementet:OlympicGame
- Ekstrahere bare 100m resultatene
Dokumenttypen vil vi ha slik:
<?xml version="1.0" encoding="ISO-8859-1"?> <!ELEMENT OlympicGame (athlet+)> <!ATTLIST OlympicGame place CDATA #REQUIRED > <!ELEMENT Sprint-100m (OlympicGame+)> <!ELEMENT athlet (name, nation, result)> <!ELEMENT name (#PCDATA)> <!ELEMENT nation (#PCDATA)> <!ELEMENT result (#PCDATA)>
Skjematisk gjør vi følgende:
Selve transformasjonsjobben, T, utføres i et program. For dette eksperimentet kan vi bruke XMLSpy, eller vi kan bruke en nettleser som transformaerer direkte.
Vi skriver en enkel transformasjonsfil, en olymp3.xsl, som beskriver denne transformasjonen:
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" doctype-system="olymp100.dtd" encoding="UTF-8" /> <xsl:template match="/"> <Sprint-100m> <xsl:for-each select="IOC/OlympicGame"> <OlympicGame> <xsl:attribute name="place"> <xsl:value-of select="@place"/> <xsl:text> - </xsl:text> <xsl:value-of select="@year"/> </xsl:attribute> <xsl:for-each select="event"> <xsl:if test="@dist[.='100m']"> <xsl:for-each select="athlet"> <xsl:copy-of select="."/> </xsl:for-each> </xsl:if> </xsl:for-each> </OlympicGame> </xsl:for-each> </Sprint-100m> </xsl:template> </xsl:stylesheet>
Det er mange måter å gjøre dette på. Her er valgt en eksplisitt form som ligner det resonnementet vi ville bruke i et tradisjonelt språk.
La oss først se litt på linjene i headingen:
1<?xml version="1.0" encoding="utf-8"?> 2 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 3 <xsl:output method="xml" omit-xml-declaration="no" doctype-system="olymp100.dtd"/>
Linje 1 forteller oss at xsl-fila faktisk er en xml-fil ! Linje 2 forteller at vi skal lage et stilsett med versjon 1 av xsl og vi definerer et namespace. Linje 3 sier at vi skal produsere xml (alternativene er html eller text), at vi vil ha xml-angivelse i resultatet, og endelig at det vi produserer skal være et dokument av typen olymp100.dtd. Vi skal altså kunne validere resultatet av transformasjonen mot en dtd-fil som er annerledes enn utgangspunktet.
Så ser vi på innmaten av transformasjonen, templaten:
4 <xsl:template match="/"> 5 <Sprint-100m> 6 <xsl:for-each select="IOC/OlympicGame"> 7 <OlympicGame> 8 <xsl:attribute name="place"> 9 <xsl:value-of select="@place"/> 10 <xsl:text> - </text> 11 <xsl:value-of select="@year"/> 12 </attribute> 13 <xsl:for-each select="event"> 14 <xsl:if test="@dist[.='100m']"> 15 <xsl:for-each select="athlet"> 16 <xsl:copy-of select="."/> 17 </for-each> 18 </if> 19 </for-each> 20 </OlympicGame> 21 </for-each> 22 </Sprint-100m> 23 </template>
Linje 4 - 23 beskriver en template som skal ta fatt i ytterste element i kildedokumnetet, match="/">. Linjene 5 og 22 produserer start og slutt tag for rotelmentet i det dokumentet vi skal produsere. Linjene 6 og 21 avgrenser en løkke over alle olympiske leker og linje 7 og 20 produserer tager for hver olympiade.
Linje 8 tar fatt i attributten place til elementet og linjene 9 - 12 produserer den nye attributten på grunnlag av de to gamle, place og year. I linje 13 starter en løkke over alle øverlser, events, og for de som tilsvarer den geneskapen at deres dist-attributt er lik 100m kopierer vi alle løperne, athlets.
Vi kopler xsl-fila til xml-fila ved følgende linje i toppen på xml-fila, all_results3.xml:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE IOC SYSTEM "olymp.dtd" > <?xml-stylesheet code="text/xsl" href="olymp3.xsl"?> <IOC> ...
Vi oppnår nøyaktig samme resultat med transformasjonen nendenfor. Denne er skrevet med 3 templates og er et bedre alternativ.
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" doctype-system="olymp100.dtd" encoding="UTF-8" indent="yes"/> <xsl:template match="/"> <Sprint-100m> <xsl:apply-templates select="IOC/OlympicGame"/> </Sprint-100m> </xsl:template> <xsl:template match="//OlympicGame"> <OlympicGame> <xsl:attribute name="place"> <xsl:value-of select="@place"/> <xsl:text> - </xsl:text> <xsl:value-of select="@year"/> </xsl:attribute> <xsl:apply-templates select="event[@dist='100m']"/> </OlympicGame> </xsl:template> <xsl:template match="//athlet"> <xsl:copy-of select="."/> </xsl:template> </xsl:stylesheet>
Reorganisering
her tar vi for oss materialet og reorganiserer den slik:
Det betyr at vi skal gå fra:
<?xml version="1.0" encoding="ISO-8859-1"?> <!ELEMENT IOC (OlympicGame+)> <!ELEMENT OlympicGame (event+)> <!ATTLIST OlympicGame place CDATA #REQUIRED year CDATA #REQUIRED > <!ELEMENT athlet (name, nation, result)> <!ELEMENT event (athlet+)> <!ATTLIST event dist (100m | 200m | 400m) #REQUIRED > <!ELEMENT name (#PCDATA)> <!ELEMENT nation (#PCDATA)> <!ELEMENT result (#PCDATA)>
til
<?xml version="1.0" encoding="ISO-8859-1"?> <!ELEMENT IOC (event+)> <!ELEMENT athlet (name, nation, result)> <!ELEMENT event (athlet+)> <!ATTLIST event dist (100m | 200m | 400m) #REQUIRED place (Atlanta | Barcelona | Sidney | Athens | Beijing | London) #REQUIRED year (1992 | 1996 | 2000 | 2004 | 2008 | 2012) #REQUIRED > <!ELEMENT name (#PCDATA)> <!ELEMENT nation (#PCDATA)> <!ELEMENT result (#PCDATA)>
Transformasjonsfila er i sin helhet slik:
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" doctype-system="olympreorg.dtd" encoding="UTF-8"/> <xsl:template match="/"> <IOC> <xsl:apply-templates select="/IOC/OlympicGame/event"/> </IOC> </xsl:template> <xsl:template match="event"> <xsl:element name="event"> <xsl:attribute name="dist"> <xsl:value-of select="@dist"/> </xsl:attribute> <xsl:attribute name="place"> <xsl:value-of select="ancestor::OlympicGame/@place"/> </xsl:attribute> <xsl:attribute name="year"> <xsl:value-of select="ancestor::OlympicGame/@year"/> </xsl:attribute> <xsl:apply-templates select="athlet"/> </xsl:element> </xsl:template> <xsl:template match="athlet"> <xsl:copy-of select="."/> </xsl:template> </xsl:stylesheet>