XML
XML KOULUTUSTA VUONNA 2025
Kysy myös tuotekohtaisia koulutuksia, kuten Altova XMLSpy. Kurssit saatavilla kautta Suomen. Tule ja varaa oma koulutustapahtumasi heti! Koulutuksen järjestää asiantuntijasi Koulutus- ja konsultointipalvelu KK Mediat.
Case study: Online-luettelo XML:n ja XSLT:n avulla
Lyhyt projektisuunnitelma
Tämän esimerkkiprojektin tavoitteena on muuntaa relaatiotietokannan sisältö XML-muotoon, validoita se skeeman avulla ja esittää tulos HTML-muodossa XSLT-muunnoksen avulla. Toteutusesimerkkinä toimii online-postimerkkiluettelo.
Projektin vaatimukset:
- Tietokannan rakenne kuvataan XML:llä, ja sitä varten laaditaan XSD-skeema.
- XML-datan täytyy olla sekä hyvin muotoiltua (well-formed) että skeeman mukaista (validia).
- Käyttäjälle näytettävä lopputulos rakennetaan palvelinpuolen XSLT-muunnoksella.
Projektin vaiheet:
- Suunnitellaan tietokannan rakenne ja laaditaan siitä alustava XML-kuvaus. Tämän yhteydessä luodaan XSD-skeema XML-kuvaukselle. Lopullinen XML-rakenne rakennetaan tämän XSD-skeeman mukaisesti.
- Datan noutaminen XML-formaatissa - Toteutetaan tietokantakyselyt ja ohjelmakoodi, joka tulostaa tietokannan tiedot XML-rakenteena.
- XML-skeeman laatiminen - Määritellään XSD, joka varmistaa XML-datan oikeellisuuden ja rakenteen.
- XSL/XSLT-muunnosten toteutus
Tietokantakuvaus
Koska projektin aihe saattaa olla vieras, perehdytään ensin tarvittavaan tietokannan rakenteeseen.
Käytämme tietokantaa nimeltä dbStamps
, joka koostuu viidestä taulusta:
julkaisu
hammaste
tyyppi
valuutta
väri
Päätaulu on nimeltään julkaisu
, muut taulut ovat aputauluja.
Aputaulut yhdistetään päätauluun relaatioiden avulla.
Kenttä | Tietotyyppi | Pituus |
---|---|---|
h_id | int | 5 |
h_vaakahammaste | varchar | 5 |
h_pystyhammaste | varchar | 5 |
Kenttä | Tietotyyppi | Pituus |
---|---|---|
t_id | int | |
t_tyyppi | varchar | 25 |
Kenttä | Tietotyyppi | Pituus |
---|---|---|
v_id | int | |
v_vari | varchar | 25 |
Kenttä | Tietotyyppi | Pituus |
---|---|---|
va_id | int | |
va_vari | varchar | 5 |
Kenttä | Tietotyyppi | Pituus |
---|---|---|
j_id | int | |
j_vuosi | integer | |
j_luettelonro | integer | |
j_tyypi | integer | |
j_vaakahammaste | integer | |
j_pystyhammaste | integer | |
j_aihe | varchar | 50 |
j_kuvaus | varchar | 250 |
j_nimellisarvo | varchar | 5 |
j_valuutta | varchar | 10 |
j_vari | integer | |
j_painos | integer | |
j_ensipaiva | date | |
j_arvopt | float | |
j_arvole | float | |
j_kuva | varchar | 50 |
XML-kuvaus tietokannan rakenteesta
XML-kuvauksen tuottamiseen on olemassa useita työkaluja, jotka integroituvat esimerkiksi MS SQL Serveriin tai Oracleen. Näitä työkaluja suositaan erityisesti laajamittaisissa ratkaisuissa, koska ne perustuvat tietokantavalmistajien luomille standardeille. Tässä projektissa käytämme kuitenkin vaihtoehtoista lähestymistapaa ja luomme yksinkertaisen XML-rakenteen tietokannan kuvaamiseksi.
Ensimmäinen askel on määritellä, mitä elementtejä tarvitaan tietokantarakenteen kuvaamiseen XML-muodossa. Mallin perustana toimivat tietokannan peruselementit:
- db (eli tietokanta)
- table (eli taulukko)
- column (eli sarake)
Jokaisella elementillä (db, table, column) on joukko ominaisuuksia — osa yhteisiä, osa uniikkeja. Yhteistä kaikille elementeille on, että niillä on oltava yksilöllinen nimi. Etenkin taulukon kentät sisältävät useita ominaisuuksia, koska niihin data varastoidaan yksilöitynä. Alla lyhyt luettelo ominaisuuksista, jotka suunnittelussa on huomioitu:
- name (nimi)
- datatype (tietotyyppi)
- length (kentän pituus)
- pk (primary key, avainkenttä)
- unique (pitääkö olla yksilöllinen)
- default (oletusarvo)
- nulls (sallitaanko null-arvot)
- autoidentity (automaattinen kasvava arvo, kuten ID-sarake)
- description (kuvaus)
Näiden tietojen pohjalta laadittu attribuuttipohjainen lähestymistapa voisi näyttää esimerkiksi tältä:
<?xml version="1.0" encoding="UTF-8" ?>
<db name=""
xmlns:xsi="http://www.w3.org/2000/10/XMLSchema-instance">
<table name="">
<column name="" datatype="" length="" pk="" unique="" default="" nulls=""
autoidentidy="" fk="" description=""/>
</table>
</db>
Tai vaihtoehtoisesti saman asian voi ilmaista elementtipohjaisesti:
<?xml version="1.0" encoding="UTF-8" ?>
<db name="" xmlns:xsi="http://www.w3.org/2000/10/XMLSchema-instance">
<table name="">
<column name="" >
<datatype> </datatype>
<length/> </length>
<pk> </pk>
<unique> </unique>
<default> </default>
<nulls> </nulls>
<autoidentidy> </autoidentidy>
<fk> </fk>
<description> </description>
</column>
</table>
</db>
Molemmat rakenteet ovat käyttökelpoisia – valinta riippuu sovelluksen tarpeista. Attribuuttimalli on yleensä tiiviimpi, mutta sen laajennettavuus on rajallista. Elementtipohjainen malli tukee paremmin monimutkaisia arvoja, kuten listoja tai monikielisiä kuvauksia.
Tässä lähestymistavassa on kuitenkin yksi selkeä ongelma: pelkkä XML ei riitä määrittelemään rakenteen sääntöjä muille järjestelmille. Esimerkiksi:
- Kuinka monta table-elementtiä voi olla?
- Voiko pk olla vain true/false?
- Käytetäänkö tietotyyppejä attribuutteina vai erillisinä elementteinä?
Näihin kysymyksiin vastaa XSD-skeema, joka määrittelee XML:n rakenteen ja säännöt. Ilman skeemaa data on rakenteellisesti epäselvää, eikä sen validointi tai koneellinen käsittely muissa sovelluksissa ei ole luotettavaa.
XML-skeeman määrittäminen tietokannan rakenteelle
Jotta XML-pohjainen tietokantakuvaus olisi käyttökelpoinen muille järjestelmille, sen rakenteelle on määriteltävä säännöt. Tämä tehdään XML-skeeman (XSD) avulla. Skeema varmistaa, että XML-dokumentti on rakenteellisesti validi ja noudattaa sovittua rakennetta. Seuraavassa on kuvattu tärkeimmät säännöt suunnitellulle projektille:
Sääntö 1: Vain yksi tietokanta per dokumentti.
XML-dokumentti voi sisältää täsmälleen yhden tietokannan (db-tagi).
Vaikka useampi tietokanta olisi teknisesti mahdollista, rajoitamme määrän yhteen — muuten dokumentti ei ole skeeman mukainen.
Sääntö 2: Taulukoita voi olla ääretön lukumäärä.
Tietokannan sisällä voi olla 0...∞ taulua (table-tagi).
Tämä vastaa yleistä tietokannan toimintamallia: taulujen lukumäärä on käytännössä rajaton, vaikka teknisiä ylärajoja voi järjestelmissä esiintyä.
Sääntö 3: Jokaisella taululla vähintään yksi kenttä.
Jokaisessa taulussa (table) täytyy olla vähintään yksi kenttä (column).
Kenttien määrä voi kuitenkin vaihdella tarpeen mukaan, mikä heijastaa tietokannan joustavuutta.
Sääntö 4: Elementtien järjestys on pakotettu.
Tämä noudattaa tietokantojen rakennetta: tietokanta sisältää tauluja, jotka sisältävät kenttiä.
Sen sijaan kenttä-dataa ei saa sijoittaa esimerkiksi taulukoiden ulkopuolelle.
Sääntö 5: Elementit vai attribuutit?
Mallissa käytetään elementtejä kuvaamaan rakennetta ja attribuutteja kuvaamaan ominaisuuksia.
Elementteinä kuvataan rakenteelliset osat (db, table, column), attribuuteilla kuvataan näiden ominaisuuksia kuten nimi (name), tietotyyppi (datatype) jne.
Elementeillä db ja table voi olla vain ja ainoastaan attibuutti name.
Kunkin attribuutin ilmemenisen pakollisuudesta voimme tehdä tarvittavat säännöt erikseen.
Näiden sääntöjen pohjalta rakennetaan XML Schema Definition (XSD), joka kuvaa sallitun XML-rakenteen tarkasti. Koska skeemojen kuvaamisessa käytettävä XSD-kieli on oma erillinen kokonaisuutensa josta voisi kirjoittaa kymmeniä sivuja, käydään asia tässä lävitse seuraavan esimerkin koodiin upotettujen kommenttien avulla. Lisätietoa XSD skeema-kielestä löytyy W3C:n sivuilta.
<?xml version="1.0" encoding="UTF-8" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2000/10/XMLSchema" elementFormDefault="qualified">
<!-- xsd:annotation mahdollistaa scheman kommentoinnin -->
<xsd:annotation>
<xsd:documentation>
Tämä schema määrittelee tietokantojen kuvauksessa käytettävän perusrakenteen.</xsd:documentation>
</xsd:annotation>
<!-- Käyttämällä simpleType määrittelyä voimme luoda omia tietotyyppejä, joita voimme periyttää -->
<xsd:simpleType name="dataType">
<xsd:annotation>
<xsd:documentation>
Tyyppi dataType sisältää mahdolliset tietotyypit kentille</xsd:documentation>
</xsd:annotation>
<!-- Käyttämällä restriction määrittelyä voimme määrätä tiettyjä reunaehtoja, kuten tietotyypin, sallitut arvot jne -->
<xsd:restriction base="xsd:NMTOKEN">
<xsd:enumeration value="integer"/>
<xsd:enumeration value="varchar"/>
<xsd:enumeration value="datetime"/>
<xsd:enumeration value="bit"/>
<xsd:enumeration value="float"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="dataLength">
<xsd:annotation>
<xsd:documentation>
Tyyppi dataLength määrittelee kentän pituuden. </xsd:documentation>
</xsd:annotation>
<xsd:restriction base="xsd:integer"/>
</xsd:simpleType>
<!-- Sitten alkaa varsinainen rakenteen kuvaus. Huomaa, että olemme joissain elementeissä perittyäneet tyypin SimpleTypesta -->
<xsd:element name="db">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="table" maxOccurs="unbounded">
<xsd:complexType>
<xsd:sequence>
<xsd:annotation>
<xsd:documentation>
Kenttä voi sisältää seuraavat attribuutit, name, datatype, length, pk, description, fk, unique, autoidentity ja description. Näistä kaikki muut paitsi autoid. ovat pakollisia. </xsd:documentation>
</xsd:annotation>
<xsd:element name="column" maxOccurs="unbounded">
<xsd:complexType>
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="datatype" type="dataType" use="required"/>
<xsd:attribute name="length" type="dataLength" use="required"/>
<xsd:attribute name="pk" type="xsd:boolean" use="required"/>
<xsd:attribute name="description" type="xsd:string" use="required"/>
<xsd:attribute name="default" type="xsd:string" use="required"/>
<xsd:attribute name="fk" type="xsd:string" use="required"/>
<xsd:attribute name="unique" type="xsd:boolean" use="required"/>
<xsd:attribute name="nulls" type="xsd:boolean" use="required"/>
<xsd:attribute name="autoidentidy" type="xsd:boolean" use="optional"/>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:schema>
Nyt kun XML-rakenteen säännöt on määritelty, voimme rakentaa XML-kuvauksen tietokannan rakenteesta:
<?xml version="1.0" encoding="UTF-8"?>
<db name="dbStamps" xmlns:xsi="http://www.w3.org/2000/10/XMLSchema-instance">
<table name="hammaste">
<column name="h_id" datatype="integer" length="" pk="true" unique="true" default="max + 1" nulls="false" autoidentidy="true" fk="" description="1"/>
<column name="h_vaaka" datatype="varchar" length="5" pk="false" unique="false" default="" nulls="false" autoidentidy="false" fk="" description="Vaakahammaste, esim.15"/>
<column name="h_pysty" datatype="varchar" length="5" pk="false" unique="false" default="" nulls="false" autoidentidy="false" fk="" description="Pystyhammaste, esim.15"/>
</table>
<table name="tyyppi">
<column name="t_id" datatype="integer" length="" pk="true" unique="true" default="max + 1" nulls="false" autoidentidy="true" fk="" description="123"/>
<column name="t_tyyppi" datatype="varchar" length="25" pk="false" unique="false" default="" nulls="false" autoidentidy="false" fk="" description="Tyypin nimi, esim. juhlamerkki"/>
</table>
<table name="valuutta">
<column name="va_id" datatype="integer" length="" pk="true" unique="true" default="max + 1" nulls="false" autoidentidy="true" fk="" description="123"/>
<column name="va_yksikko" datatype="varchar" length="5" pk="false" unique="false" default="" nulls="false" autoidentidy="false" fk="" description="Merkin valuutta, esim. mk"/>
</table>
<table name="varit">
<column name="v_id" datatype="integer" length="" pk="true" unique="true" default="max + 1" nulls="false" autoidentidy="true" fk="" description="123"/>
<column name="v_vari" datatype="varchar" length="25" pk="false" unique="false" default="" nulls="false" autoidentidy="false" fk="" description="Julkaisun vari, esim. lila"/>
</table>
<table name="julkaisut">
<column name="j_id" datatype="integer" length="" pk="true" unique="true" default="max + 1" nulls="false" autoidentidy="true" fk="" description="123"/>
<column name="j_vuosi" datatype="integer" length="" pk="false" unique="false" default="" nulls="false" autoidentidy="false" fk="" description="1975"/>
<column name="j_luettelonro" datatype="integer" length="" pk="false" unique="false" default="" nulls="false" autoidentidy="false" fk="" description="990"/>
<column name="j_tyyppi" datatype="integer" length="" pk="false" unique="false" default="" nulls="false" autoidentidy="false" fk="t_id" description="2"/>
<column name="j_vaakahammaste" datatype="integer" length="" pk="false" unique="false" default="" nulls="false" autoidentidy="false" fk="h_id" description="5"/>
<column name="j_pystyhammaste" datatype="integer" length="" pk="false" unique="false" default="" nulls="false" autoidentidy="false" fk="h_id" description="5"/>
<column name="j_aihe" datatype="varchar" length="50" pk="false" unique="false" default="" nulls="false" autoidentidy="false" fk="" description="Julkaisun nimi; esim. Hiihdon MM-kisat"/>
<column name="j_kuvaus" datatype="varchar" length="250" pk="false" unique="false" default="" nulls="false" autoidentidy="false" fk="" description="Merkin kuva-aiheen lyhyt kuvaus"/>
<column name="j_nimellisarvo" datatype="varchar" length="10" pk="false" unique="false" default="" nulls="false" autoidentidy="false" fk="" description="Merkin nimellisarvo, esim. 0,40 + 5"/>
<column name="j_valuutta" datatype="integer" length="" pk="false" unique="false" default="" nulls="false" autoidentidy="false" fk="va_id" description="Merkin valuutta, esim. 3"/>
<column name="j_vari" datatype="integer" length="" pk="false" unique="false" default="" nulls="false" autoidentidy="false" fk="v_id" description="Merkin väri(t)"/>
<column name="j_painos" datatype="integer" length="" pk="false" unique="false" default="" nulls="false" autoidentidy="false" fk="" description="Julkaisun painos"/>
<column name="j_ensipaiva" datatype="date" length="" pk="false" unique="false" default="" nulls="false" autoidentidy="false" fk="" description="Ensipäivä, esim. 15.5.1940"/>
<column name="j_arvopt" datatype="float" length="" pk="false" unique="false" default="" nulls="false" autoidentidy="false" fk="" description="Arvo postituoreena, esim. 2,50"/>
<column name="j_arvole" datatype="float" length="" pk="false" unique="false" default="" nulls="false" autoidentidy="false" fk="" description="Arvo leimattuna, esim. 2,50"/>
<column name="j_kuva" datatype="varchar" length="50" pk="false" unique="false" default="" nulls="false" autoidentidy="false" fk="" description="Merkin kuvan nimi / polku"/>
</table>
</db>
Datan konvertoiminen tietokannasta XML-muotoon
Web-sovelluksissa kaikki rakenteellinen tieto kannattaa tallentaa tietokantaan. Projektin tavoitteena on kehittää menetelmä, jonka avulla sovellukset, joissa ei ole mahdollisuutta suoraan tietokantayhteyteen, voivat käsitellä ja esittää tietokannassa olevaa sisältöä XML:n kautta. Yleinen esimerkki tästä ovat mobiili- tai selainkäyttöliittymät, joiden kautta tietokannassa olevaa dataa halutaan tarjota; kumpikaan ei sisällä mahdollisuutta suoraan tietokantayhteyteen, mutta molemmat pystyvät lukemaan XML-muotoista dataa /tietovirtaa.
Aiemmassa vaiheessa määrittelimme XML-datan rakenteen, seuraavaksi haemme tietokannasta siihen sopivaa dataa.
Useimmissa ohjelmointikielissä prosessi etenee seuraavasti:
- Avaa tietokantayhteys (katso esimerkit tietokantayhteyden luomisesta PHP:n kautta)
- Suorita haluttu SQL-kysely (katso esimerkit SQL-kyselystä PHP-kielellä)
- Tulosta tulosjoukko - normaalisti tulosjoukko sijoitetaan HTML-rakenteen sisään, mutta tässä tapauksessa tulosjoukon arvot sijoitetaankin XML-rakenteen sisään.
Seuraava PHP-esimerkki näyttää, miten tietokannasta haettu data muunnetaan XML-muotoon:
<?php
echo ("<?xml version="1.0" encoding="ISO 8859-1"?>" . "\n");
// Avataan tietokantayhteys
mysql_connect("palvelimen_osoite","kayttajatunnus","salasana");
// Valitaan tietokanta
mysql_selectdb("dbStamps");
// Suoritetaan haku tietokannasta
// tallennetaan tulokset muuttujaan $result
$SQL = "SELECT SQL_CACHE j_id,j_luettelonro, j_vuosi....";
$result = mysql_query($SQL);
// tarkistetaan onko tuloksia
if (mysql_num_rows($result)) {
echo ("<julkaisut>");
// käydään tulosjoukko lävitse
while ($row = mysql_fetch_object($result, MYSQL_BOTH)) {
echo "<julkaisu>";
echo "<j_id>$row->j_id</j_id>";
echo "<j_luettelonro>$row->j_luettelonro</j_luettelonro>";
echo "<j_vuosi>$row->j_vuosi</j_vuosi>";
echo "<j_nimellisarvo>$row->j_nimellisarvo</j_nimellisarvo>";
echo "<j_valuutta>$row->j_valuutta</j_valuutta>";
echo "<j_aihe>$row->j_aihe</j_aihe>";
echo "<j_kuvaus>$row->j_kuvaus</j_kuvaus>";
echo "</julkaisu>";
}
echo ("</julkaisut>"); }
}
XML-skeema datalle
PHP-esimerkin tuottama XML-data voi näyttää esimerkiksi tältä:
<?xml version="1.0" encoding="ISO 8859-1"?>
<julkaisut>
<julkaisu>
<j_id>29</j_id>
<j_luettelonro> 20</j_luettelonro>
<j_vuosi>1885</j_vuosi>
<j_nimellisarvo>5</j_nimellisarvo>
<j_valuutta>penniä</j_valuutta>
<j_aihe> M 1885</j_aihe>
<j_kuvaus>Suomen kansallisvaakuna. Leijonan kilvessä 8 tähteä, kolme jalkaa sapelilla.</j_kuvaus>
</julkaisu>
<julkaisu>
<j_id> 30</j_id>
<j_luettelonro>21</j_luettelonro>
<j_vuosi>1885</j_vuosi>
<j_nimellisarvo>10</j_nimellisarvo>
<j_valuutta>penniä</j_valuutta>
<j_aihe> M 1885</j_aihe>
<j_kuvaus>Suomen kansallisvaakuna. Leijonan kilvessä 8 tähteä, kolme jalkaa sapelilla.</j_kuvaus>
</julkaisu>
</julkaisut>
Seuraavaksi luodaan skeema kyseiselle datalle.
Tätä varten julkaisut
-juurielementtiin on lisättävä attribuutit, jotka määrittävät skeeman sijainnin.
Tämä lisäys tehdään XML:n tulostuskoodissa, eli tässä tapauksessa PHP:ssä:
<julkaisut xmlns:xsi="http://www.w3.org/2000/10/XMLSchema-instance" xsi:noNamespaceSchemaLocation="projekti.xsd">
Skeema-tiedoston (projekti.xsd) sisältö on yksinkertainen:
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2000/10/XMLSchema" elementFormDefault="qualified">
<xsd:element name="j_aihe" type="xsd:string"/>
<xsd:element name="j_id" type="xsd:string"/>
<xsd:element name="j_kuvaus" type="xsd:string"/>
<xsd:element name="j_luettelonro" type="xsd:string"/>
<xsd:element name="j_nimellisarvo" type="xsd:byte"/>
<xsd:element name="j_valuutta" type="xsd:string"/>
<xsd:element name="j_vuosi" type="xsd:short"/>
<xsd:element name="julkaisu">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="j_id"/>
<xsd:element ref="j_luettelonro"/>
<xsd:element ref="j_vuosi"/>
<xsd:element ref="j_nimellisarvo"/>
<xsd:element ref="j_valuutta"/>
<xsd:element ref="j_aihe"/>
<xsd:element ref="j_kuvaus"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="julkaisut">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="julkaisu" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
Jo pelkästään tässä muodossa data soveltuu hyödynnettäväksi esimerkiksi mobiilisovelluksissa. Jos datan hyödyntämisen haluaa viedä vielä askelen pitemmälle, voi sen muuntaa XSL/XSLT-muunnoksella esimerkiksi HTML- tai PDF-tiedostoiksi.
XSL/XSLT-muunnos datalle
Nyt kun kaikki tarvittavat elementit ovat käsillä, voimme julkaista työmme tuloksen HTML-sivuna. Tämä onnistuu käyttämällä XSL/XSLT-muunnosta XML-datalle. XSL/XSLT-muunnoksen suorittamiseksi XML-dataan on lisättävä seuraava prosessointiohje:
<?xml-stylesheet type="text/xsl" href="projekti.xsl"?>
Aihetta on käsitelty tarkemmin XSLT-oppaassa, joten tässä yhteydessä tyydymme tarkastelemaan esimerkin koodia:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance">
<html xmlns:xsl="http://www.w3.org/TR/WD-xsl">
<head>
<title>
2K mediat.com : esimerkki palvelinpuolen xslt muunnoksesta</title>
<link href="naytto.css" rel="stylesheet" media="screen" type="text/css"/></head>
<body>
<div id="print">
<img id="logo" src="logo.jpg" alt="2K mediat.com logo"/>
<h1 class="pprint">
www.2kmediat.com</h1>
<div id="toc">
<h1 class="paa">
Sisällysluettelo</h1>
<h1 class="tocserver">
Suomi postimerkkiluettelo</h1>
<p>
<xsl:for-each select="julkaisut/julkaisu">
<xsl:value-of select="j_vuosi"/>
, <xsl:value-of select="j_aihe"/>
<br/>
<br/>
</xsl:for-each>
</p>
</div>
<xsl:for-each select="julkaisut/julkaisu">
<h1 class="paa">
<xsl:value-of select="j_aihe"/>
</h1>
<p class="desc">
<b>
Numero : </b>
<xsl:value-of select="j_luettelonro"/>
</p>
<p class="desc">
<b>
Nimellisarvo: </b>
<xsl:value-of select="j_nimellisarvo"/>
<xsl:value-of select="j_valuutta"/>
</p>
<p class="desc">
<b>
Kuvaus: </b>
<xsl:value-of select="j_kuvaus"/>
</p>
<hr/>
</xsl:for-each>
</div>
</body>
</html>
</xsl:stylesheet>