Kihagyás

13. gyakorlat

XML technológiák (folytatás)

XML Path Language (XPath)

Az XPath egy olyan lekérdező nyelv, amivel az XML dokumentumban elemeket tudunk kijelölni, illetve az XML tartalma alapján számításokat lehet elvégezni (pl. egy adott elemnek hány gyereke van).

XML, mint fa

Egy XML dokumentumot tekinthetünk úgy is, mint egy fa. Csak egy gyökere lehet, és az elemek egymásba ágyazása jelenti a fában a szülő-gyerek kapcsolatot. Nézzük az alábbi catalog XML-t:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<catalog>
  <product code ="123">
    <price>35.99</price>
  </product>
  <product code ="456">
    <price>22.95</price>
    <discount>
      <amount>25</amount>
    </discount>
  </product>
  <product code ="789">
    <price>185.00 </price >
  </product>
</catalog>

A hozzá tartozó fa az alábbi (a piros kiemelésnek később lesz jelentősége). A fának a gyökere egy speciális ,,elem'', ami valójában nincs a gráfban (mint pl. Linux alatt a gyökér könyvtár). Ennek az elemnek a gyereke az XML gyökere, majd a több elem, de megjelennek az attribútumok és az elemek értékei. Emellett a megjegyzések, feldolgozási utasítások, névterek is részei a fának, de ezekkel most nem foglalkozunk, XPath szempontjából hasonlóak.

jspflow

Elérési útvonalak, tengelyek, kiválasztás

Egy elérési útvonallal a fában egy vagy több elemet jelölhetünk ki. Az elérési útvonalat kétféleképpen adhatjuk meg:

  • Abszolút útvonallal: mindig a gyökérhez viszonyítva adjuk meg. Ilyenkor / jellel kezdődik, például a product kijelölése: /catalog/product
  • Relatív útvonallal: amikor egy adott elemhez képest adjuk meg, például ha a catalog elemhez képest akarjuk megadni a product-ot, akkor elég annyit írni, hogy product, de ha a price-t akarjuk, akkor kijelölni, akkor product/price (hogy melyik, az majd később)

A tengelyek segítségével egy adott elemhez képest jelölhetünk ki elemeket. Néhány fontosabb tengely, és hogy mely elemeket jelölik ki a product elemhez képest (illetve a rövid jelölésük, ha van ilyen):

  • self: önmaga, azaz magát az elemet jelöli ki
    • a product esetében ez a product lesz
    • self, vagy röviden csak.`
  • parent: az adott elem szülője
    • a product esetében a szülő a catalog
    • parent, röviden csak ..
  • child: az adott elem gyereke(i)
    • a product esetében több gyerek is van, a price és a discount
    • child vagy röviden csak az adott típusú gyerek, azaz child::price helyett csak price
  • attribute: az adott elem attribútuma(i)
    • a product esetében a code
    • attribute::code helyett elég csak annyit írni, hogy @code
  • descendant-or-self: az adott elem és a leszármazottai
    • a productt esetében a product önmaga, és minden leszármazott elem (price, discount, amount)
    • például, ha a leszármazottak között a price-ra vagyunk kíváncsiak, akkor a /descendant-or-self()/price hivatkozást kell használni, vagy röviden a //price jelölést
  • További tengelyek:
    • ancestors, ancestor-or-self, descendant, following, following-sibling, namespace, preceding, preceding-sibling
Elemek kiválasztása

Útvonalak segítségével a fent leírt módon, azaz például az összes price elem kiválasztása a //price segítségével tehető meg. Azonban lehet egyéb lehet egyéb feltételeket is meg megfogalmazni, általánosan a a képlet axis::node-test[predicate] alakban írható fel, ahol a predicate a feltételeket jelenti. Ezekre néhány példa:

  • Azon product elemek kiválasztása, amelynek van kód attribútuma, és annak az értéke 123 a //product[@code="123"] kifejezéssel választható ki, azaz az attribútum értékére a a feltételt a [@attr="ertek"] mondja meg
  • Ha azokat szeretnénk kiválasztani, amelyeknek van code attribútuma, de az értéke mindegy, akkor a //product[@code] kifejezést kell használni, azaz attribútum létezésére a feltétel [@attr]
  • Ha az utolsó product elemet akarjuk kijelölni, akkor a product[last()] segítségével tehetjük meg, de ha például a harmadikat, akkor product[3] a megoldás
  • De több feltételt is össze lehet fűzni, pl. product[last()][@code] és ilyenkor balról jobbra lesznek kiértékelve

Az elérési útvonalak csomópont gyűjteményeket adnak vissza, amiket felhasználhatunk különböző módon:

  • Össze lehet fűzni, azaz ha egy XML-ben cd és dvdvegyesen szerepel, akkor az összes cd vagy dvd kijelölése cd | dvd, azaz a | jellel lehet összefűzni
  • Meg is számolhatom az összes olyan product elemet, amelynek a kódja legalább 500: count(product[@code >= 500])
    • De lehetne azon price elemeket is kiválasztani, ahol a price értéke legalább 100: /catalog/product[price >= 100]/price
  • Kiválaszthatom azokat az elemeket, amelyeknek a kódja 500 és 600 közé esik: product[@code >500 and @code <600]
  • Szöveges alapú szűrések is lehetnek, például azon vásárlók kiválasztása, amelyeknek a neve "dr"-rel kezdődik: customer[ starts-with(@name, "dr")]

A teljesség igénye nélkül csak néhány példát mutattunk arra, hogy milyen lehetőségek vannak csomópontok kijelölésére (további példák), és ezeket lehet kombinálni is, pl. azon price elemeket is kiválasztani, ahol a price értéke legalább 100: /catalog/product[price >= 100]/price

XSL (eXtensible Stylesheet Language) Transformations (XSLT)

Az XSL az az XML-hez tartozó style sheet, kb az, mint a CSS a HTML számára. Az XSLT egy nyelv, amivel az XML dokumentumokat transzformálni lehet:

  • XML-t XML-lé, ha például át akarjuk alakítani (például a lenti két XML ugyanazt az információ tartalmazza, csak más formátumban), vagy valamilyen műveletet (szűrés, számolás, összefűzés, stb.) is szeretnénk végezni az adatokon.

    1
    2
    3
    4
    5
    6
    7
    <employees>
      <employee>
        <name>Reid</name>
        <salary>91000</salary>
      </employee>
      ...
    </employees>
    
    1
    2
    3
    4
    <staff>
      <staff-member name="Reid" pay="91000"/>
      ...
    </staff>
    
  • XML-t HTML-lé vagy más dokumentummá akarunk alakítani.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <html>
      <table>
        <tr>
          <td>Reid</td>
          <td>91000</td>
        </tr>
        ...
      </table>
    </html>
    

XSLT szabályok

A következőkben bemutatjuk, hogyan lehet megadni egy XSLT transzformációt. Az egészet egy példán keresztük fogjuk bemutatni, amikor is a cél egy XML HTML-lé konvertálása lesz. A feladat a CD katalógus XML átalakítása HTML-lé a következő szempontok szerint:

  • A HTML oldal címe a catalog elem name attribútumának az értéke legyen
  • írjuk ki, hogy hány CD van a katalógusban
  • A CD-k egy táblázatban jelenjenek meg
    • Az artist szerint legyenek rendezve
    • Az akciós elemeknek piros legyen a háttere
    • A CD árát Ft-ban adjuk meg

A CD katalógus egy részlete:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="cd_catalog2Html.xsl"?>
<catalog name="My cds to sell"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="cd_catalog.xsd">
  <cd>
    <title>Empire Burlesque</title>
    <artist>Bob Dylan</artist>
    <country>USA</country>
    <company>Columbia</company>
    <price>10.90</price>
    <year>1985</year>
  </cd>
  <cd>
    <title>Hide your heart</title>
    <artist>Bonnie Tyler</artist>
    <country>UK</country>
    <company>CBS Records</company>
    <price>9.90</price>
    <year>1988</year>
  </cd>
  ...
</catalog>

Az XSLT is egy XML dokumentum, ahol az XSLT transzformációt leíró elemek/szabályok olyan elemek, amelyek az xsl névtérben vannak, de emellett lehetnek további elemek is, mint ahogy a példában is lesznek majd HTML elemek is.

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

A transzformáció definiálása nem más, mint XSLT szabályok megadása, melyek tetszőleges sorrendben végrehajtódhatnak. Az, hogy mire illeszkedik a transzformálandó XML-ben, az XPath segítségével írható le. A szabályokat a <xsl:template match="valami"> segítésével adhatjuk meg, ahol a valami mondja meg, hogy mire illeszkedjen. Például ha a gyökér elemre akarunk definiálni egy szabályt, akkor az alábbi módon tehetjük meg:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="/">

    <html>
      <head>
        <title>CD katalogus</title>
      </head>

      <body>
        TODO
      </body>
    </html>

  </xsl:template>

</xsl:stylesheet>

A fenti példában a gyökér elemre fog ,,végrehajtódni'' a szabály, aminek a belsejében egy HTML oldal kódja látható, és a végrehajtás itt azt jelenti majd, hogy azt a kódot "kiírja", azaz a transzformáció eredménye az a HTML oldal lesz.

Ha egy adott XML elem értékét akarjuk kiolvasni az XSLT segítségével, akkor ezt a <xsl:value-of select="elem"/> segítségével tehetjük meg. Ez kiolvassa az adott elem értékét, és ,,beírja'' az adott helyre, azaz ha az oldal címének a a katalógus gyökér elemének a name attribútumát akarom megadni, akkor a következő módon kell kiegészíteni a fenti példát:

1
2
3
  <head>
    <title><xsl:value-of select="catalog/@name"/></title>
  </head>

Nem szükséges abszolút útvonallal megadni a hivatkozást, mert a minta illesztésekor az adott elemen ,,állunk'', ezért ahhoz képest relatív útvonallal is megadhatjuk. (Ha nem az XML elem értékét, hanem magát az XML elemet akarnánk kiíratni, akkor a <xsl:copy-of select="elem"/> segítségével tehetjük meg, de ezt nem fogjuk használni a példában.)

A fenti megoldásban a gyökérre illeszkedő mintát definiáltunk, de definiálhatunk további mintákat is, és azokra hivatkozhatunk, így egy mintát többször is fel lehet használni, illetve a kód is átlátható marad. A további template-re való hivatkozást a <xsl:apply-templates select="elem"/> segítségével adhatjuk meg.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="/">
    <html>
      <head>
        <title><xsl:value-of select="catalog/@name"/></title>
      </head>

      <body>
        <xsl:apply-templates select="catalog"/>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="catalog">
    TODO
  </xsl:template>
</xsl:stylesheet>

A további feladat a CD-khez elkészíteni egy táblázatot, aminek a fejlécét statikusan beleírhatjuk a transzformációba. Ezután a CD-ez kell bejárni egy ,,for-ciklussal'' de úgy, hogy az artist szerint legyenek rendezve. Erre egy lehetséges megoldás, hogy definiálunk egy szabályt a cd-re is, és azt alkalmazzuk, de ettől még nem lesznek rendezve, ehhez meg kell adnunk, hogy az artist szerint rendezve alkalmazza a szabályt: <xsl:sort select="cd/artist"/>

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
  <xsl:template match="catalog">
    <table border="1">
      <tr>
        <th>Előadó</th><th>Cím</th><th>Ország</th><th>Megjelenési év</th><th>Ár</th>
      </tr>
      <xsl:apply-templates select="cd">
        <xsl:sort select="cd/artist"/>
      </xsl:apply-templates>
    </table>
  </xsl:template>

  <xsl:template match="cd">
    TODO
  </xsl:template>

Minden egyes cd esetében egy új sort szúrunk be a táblázatba, ahol a sor piros hátteret kap, ha az adott cd akciós. Ehhez egy feltételt kell megvizsgálnunk, amit a mi esetünkben a <xsl:if test="price/@discount"> segítségével tehetjük meg, majd beállítjuk a bgcolor értékét, ha a feltétel igaz (azaz a price-nak van discount attribútuma).

1
2
3
4
5
6
7
8
    <tr>
      <xsl:if test="price/@discount">
        <xsl:attribute name="bgcolor">  
          <xsl:value-of select="'#FF9999'"/>
        </xsl:attribute>
      </xsl:if>
      ...
    </tr>

A többi érték beírása a táblázatba már ,,nem nehéz''. Annyit még megteszünk, hogy a dollárban megadott árat átkonvertáljuk forintra 320Ft/$ árfolyamon és az évszámot középre igazítjuk.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
  <xsl:template match="cd">

    <tr>    
      <xsl:if test="price/@discount">
        <xsl:attribute name="bgcolor">  
          <xsl:value-of select="'#FF9999'"/>
        </xsl:attribute>
      </xsl:if>

      <td><xsl:value-of select="artist"/></td>
      <td><xsl:value-of select="title"/></td>
      <td><xsl:value-of select="country"/></td>

      <td align="center"><xsl:value-of select="year"/></td>
      <xsl:variable name="usprice" select="price"/>
      <td><xsl:value-of select="format-number($usprice * 184, '#')"/> FT</td>
    </tr>

  </xsl:template>

A teljes megoldás egyben:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="/">
    <html>

      <head>
        <title><xsl:value-of select="catalog/@name"/></title>
      </head>

      <body>
        <b>A catalógusban szereplő cikkek száma:
           <xsl:value-of select="count(catalog/cd)"/>
        </b>
        <xsl:apply-templates select="catalog"/>
      </body>

    </html>
  </xsl:template>

  <xsl:template match="catalog">
    <table border="1">
      <tr>
        <th>Előadó</th><th>Cím</th><th>Ország</th><th>Megjelenési év</th><th>Ár</th>
      </tr>
      <xsl:apply-templates select="cd">
        <xsl:sort select="cd/artist"/>
      </xsl:apply-templates>
    </table>
  </xsl:template>


  <xsl:template match="cd">

    <tr>    
      <xsl:if test="price/@discount">
        <xsl:attribute name="bgcolor">  
          <xsl:value-of select="'#FF9999'"/>
        </xsl:attribute>
      </xsl:if>

      <td><xsl:value-of select="artist"/></td>
      <td><xsl:value-of select="title"/></td>
      <td><xsl:value-of select="country"/></td>

      <td align="center"><xsl:value-of select="year"/></td>
      <xsl:variable name="usprice" select="price"/>
      <td><xsl:value-of select="format-number($usprice * 184, '#')"/> FT</td>
    </tr>

  </xsl:template>

</xsl:stylesheet>

Gyakorlati rész

Hogyan tudom ezt kipróbálni? Automatikus a transzformáció?

Böngésző

Ha az XML dokumentum elején meg van adva, hogy milyen transzformációt kell alkalmazni a megjelenítésnél, akkor ,,automatikusan végrehajtódik''.

1
2
3
<catalog name="My cds to sell"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="cd_catalog.xsd">

Azonban ez a gyakorlatban nem működik, mert a böngészők biztonsági okokból tiltják a lokális fájlok hozzáférést. Ha ezt kikapcsoljuk, akkor működik, hogy ,,betöltéskor'' (drag&drop a fájlt a böngészőre) a transzformált változat jelenik meg. Ahhoz, hogy ezt ki lehessen próbálni pl. Chrome alatt, a Chrome-ot a –allow-file-access-from-files paraméterrel kell elindítani.

jspflow

Visual Studio

Lehetőség van a Visual Studioba is betölteni és végrehajtani a transzformációt. Ehhez nyissuk meg a transzformációs fájl VS-ben, és a a fájl van kiválasztva, akkor a menüsorban megjelenik egy XML menüpont is. Itt el tudjuk indítani a transzformációt debug-gal vagy anélkül. Ehhez ki kell választani a transzformálandó XML-t, majd lefut rá a transzformáció és az eredmény megjelenik a VS-ben. Lehetőség van break point-ok beszúrásába is, és akkor lépésről lépésre tudjuk követni a transzformációt, látjuk hol állunk a fában, ... Ekkor a már elkészül és értelmezhető kimenet már megjelenik.

jspflow

Forrás: a fenti példa és további részletek a https://www.w3schools.com/xml/xsl_intro.asp oldalon érhetőek el.


Utolsó frissítés: 2020-05-04 23:30:15