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>