Fortgeschrittene Techniken

Werbung
9. Fortgeschrittene Techniken
Inhalt
1.
2.
3.
4.
5.
6.
7.
8.
9.
Einführung
Vorlagen
XPath
Kontrollstrukturen
Sortierung, Gruppierung und
Nummerierung
Parameter und Variablen
Ein- und Ausgabeformate
Auslagerung und Wiederverwendung
Fortgeschrittene Techniken
Eigene XSLT-Funktionen
Benannte Vorlagen
Stylesheet-Funktionen
Einführung
Trotz des sehr stark erweiterten Funktionsumfangs von XPath gibt
es natürlich immer Bedarf an wieder verwendbaren Komponenten,
die man entweder in einer einzigen Transformation mehrfach
verwenden möchte oder die für eine ganze Anwendung in
verschiedenen Transformationen zum Einsatz kommen können.
Mit Hilfe der allgemeinen Vorlagen gibt es bereits ein Konzept, wie
man wieder verwendbare Komponenten in XSLT erstellen kann.
Diese hängen allerdings doch sehr von den Vorlagen-Regeln und
damit der Vorlagenautomatik ab.
Weitere Lösungen sind benannte Vorlagen und StylesheetFunktionen (neu in XSLT 2.0).
Benannte Vorlagen
Die einfachste Möglichkeit, auch in XSLT 1.0 schon solche benannten
wieder verwendbaren Vorlagen zu erstellen, stellen benannte Vorlagen
in Form von Funktionen oder Prozeduren, wie sie auch zur Erledigung
von wiederholt auftretenden Anweisungen in anderen
Programmiersprachen zum Einsatz kommen, dar.
Man wird diesen benannten Vorlagen nicht notwendigerweise ansehen,
dass sie spezielle Vorlagen für unsere Anwendung darstellen. Vielleicht
liegen sie in einer entsprechend benannten Datei, die von mehreren
anderen XSLT-Dateien eingebunden wird, sie weisen einen besonderen
Namensraum auf oder befolgen spezielle Namenskonventionen.
Benannte Vorlagen
Sie bestehen aus folgenden Bestandteilen:
Ein möglichst allgemeiner Name, der auf ihre Wiederverwendbarkeit deutlich
hinweist. Dieser Name könnte genauso gut in einer anderen Programmiersprache für
eine ähnlich aufgebaute oder mit ähnlichen Algorithmen ausgestattete
Methode/Funktion/Routine stehen.
Eine Parameterliste, die einzelne Werte oder ganze Knoten bzw. Knotensätze
erwartet. Diese Parameter tragen auch Namen, die nicht mit Namen aus dem XMLDatenstrom Ähnlichkeiten aufweisen, sondern eher allgemeiner Natur sind. Dies soll
verdeutlichen, dass eine universelle Einsetzbarkeit der Vorlage durch diese
Schnittstellen gegeben ist.
Der Aufruf erfolgt dann gerade nicht über das Ping-Pong-Spiel, sondern mit Hilfe des
Vorlagennamens unter Verwendung der benötigten Parameter.
Der Rückgabewert einer solchen Vorlage erfolgt dann über die Ausgabe im XMLAusgabestrom mit Hilfe von xsl:text, xsl:value-of oder einer Textknoten-Ausgabe in
einem Element.
Benannte Vorlagen
Benannte Vorlagen
Benannte Vorlagen
Eigene XSLT-Funktionen
Benannte Vorlagen
Stylesheet-Funktionen
Stylesheet-Funktionen
Die allgemeine Syntax des neuen xsl:function-Elements lautet:
<xsl:function
name = qname
as = sequence-type
override = "yes" | "no">
<!-- Content: (xsl:param*, sequence-constructor) -->
</xsl:function>
Die Deklaration stellt ein Element der obersten Ebene dar, damit es von XPathAusdrücken im ganzen Transformationsdokument aufgerufen werden kann.
Folgende Attribute sind vorhanden:
name enthält den Namen der Funktion in Form eines QName, der auch ein
Namensraumpräfix enthalten muss.
as gibt optional den Rückgabedatentyp an, in den der erstellte Wert umgewandelt wird,
ansonsten wird einfach der Wert so zurückgeliefert, wie er ermittelt wurde.
override legt optional mit dem Wert yes fest, dass die so auszeichnete Funktion eine
andere Funktion gleichen Namens und die Anzahl der Stellen überschreibt und diese
Funktion stattdessen verwendet wird.
Fragen...
SQL-ähnliche Abfragen
Bedingungen
Verknüpfungen
Mengen
Abfragen
Die einfachsten Abfragen von XML mit Hilfe von XPath, wie sie auch schon in der
Version 1.0 möglich waren, setzen die verschiedenen Operatoren ein. Hier ist eine
große Ähnlichkeit zwischen XPath und SQL, allerdings auch mit vielen anderen
Syntaxstrukturen, die es ermöglichen, Bedingungen zu formulieren. Letztendlich
entsprechen die Operatoren den einfachen Syntaxmöglichkeiten, die in der
WHERE-Klausel von SQL auftauchen können.
Vergleich
Bedeutung
Kalkül
Bedeutung
Logik
Bedeutung
=
Gleichheit
+
Addition
and
Und
!=
Ungleichheit
-
Subtraktion
or
Oder
< bzw. <
kleiner
*
Multiplikation
not
Nicht
> bzw. >
größer
div
Division
<= bzw. <
kleiner gleich
mod
Modulo
>= bzw. &gt
größer gleich
Abfragen
<Umsatzliste>
<Rechnungsliste>
<Rechnung R_Nr="7" R_Datum="31.03.03"
R_Summe="8.61"/>
<Rechnung R_Nr="115" R_Datum="31.05.03"
R_Summe="19.16"/>
...
</Rechnungsliste>
<Postenliste>
<Posten P_Nr="33" R_Nr="7" P_Summe="2.28" T_Nr="2"/>
<Posten P_Nr="34" R_Nr="7" P_Summe="0.17" T_Nr="3"/>
...
<Posten P_Nr="559" R_Nr="115" P_Summe="4.49"
T_Nr="8"/>
<Posten P_Nr="560" R_Nr="115" P_Summe="2.93"
T_Nr="10"/>
</Postenliste>
<Tarifliste>
<Tarif T_Nr="8" T_Name="Schicht2"
T_GueBis="31.12.03"/>
<Tarif T_Nr="9" T_Name="Nachtschicht1"
T_GueBis="31.12.03"/>
<Tarif T_Nr="10" T_Name="Nachtschicht2"
T_GueBis="31.12.03"/>
</Tarifliste>
</Umsatzliste>
Abfragen
Alle Rechnungen mit mehr als fünf Euro Summe
//Rechnung[@R_Summe > 5]
Alle Rechnungen mit einer Summe zwischen 2 und 5
//Rechnung[@R_Summe > 2 and @R_Summe < 5]
Jede zweite Rechnung
//Rechnung[position() mod 2 = 0]
Alle Rechnungen nach dem 01.04.03
//Rechnung[xs:date(string-join((substring(@R_Datum, 7,2), substring(@R_Datum,
4,2), substring(@R_Datum, 1,2)),'-')) > xs:date('03-04-01')]
Alle Posten, deren Summe größer als der Durchschnitt ist
//Posten[@P_Summe > avg(//Posten/@P_Summe)]
SQL-ähnliche Abfragen
Bedingungen
Verknüpfungen
Mengen
Verknüpfungen
Normalerweise dürften die XML-Dokumente hierarchisch aufgebaut sein,
sodass keine besonderen Verknüpfungen wie bei relationalen Daten
notwendig sind.
Dies erleichtert bspw. die reihenweise Verarbeitung der Rechnungen und ihrer
Posten, wobei die Posten alleine für eine Ausgabe wenig Sinn machen.
Nichtsdestoweniger gibt es immer wieder Situationen, in denen man gerade
eine solche aufgetrennte Datenhaltung verwenden möchte. Dies eignet sich
immer dann, wenn allgemeine Elemente zentral gesammelt werden, auf die
sich nachher in Form von Ressourcen bezogen werden soll. Hier wäre eine
doppelte Datenhaltung, die bei lokaler und hierarchisierter Speicherung
notwendig würde, unnütz. Stattdessen verweise man mit Hilfe von Referenzen
auf die global verfügbaren Ressourcen.
Verknüpfungen
<Umsatzliste>
<Rechnung R_Nr="7" R_Datum="31.03.03"
R_Summe="8.61">
<Posten P_Nr="33" R_Nr="7"
P_Summe="2.28" T_Nr="2" />
<Posten P_Nr="34" R_Nr="7"
P_Summe="0.17" T_Nr="3" />
...
</Rechnung>
<Rechnung R_Nr="8" R_Datum="31.03.03"
R_Summe="5.85">
<Posten P_Nr="41" R_Nr="8"
P_Summe="1.18" T_Nr="2" />
<Posten P_Nr="42" R_Nr="8"
P_Summe="0.02" T_Nr="3" />
...
</Rechnung>
...
Verknüpfungen
<Umsatzliste>
<Tarif T_Nr="1" T_Name="Frühstück"
T_GueBis="30.06.03">
<Rechnungsliste/>
</Tarif>
<Tarif T_Nr="2" T_Name="Mittagspause"
T_GueBis="30.06.03">
<Rechnungsliste>
<Rechnung R_Nr="7" R_Datum="31.03.03"
R_Summe="8.61"/>
<Rechnung R_Nr="8" R_Datum="31.03.03"
R_Summe="5.85"/>
...
</Rechnungsliste>
</Tarif>
SQL-ähnliche Abfragen
Bedingungen
Verknüpfungen
Mengen
Mengen
Schnittmenge (Menge der Elemente,
die in beiden Mengen vorhanden ist):
op:intersect($parameter1 as node()*,
$parameter2 as node()*) as node()*
Vereinigungsmenge (Menge aller
Elemente beider Mengen):
op:union($parameter1 as node()*,
$parameter2 as node()*) as node()*
Differenzmenge (Menge der
Elemente, die in der einen, nicht aber
in der anderen Menge enthalten
sind): op:except($parameter1 as
node()*, $parameter2 as node()*) as
node()*
Fragen...
Dynamisches XSLT
XSLT erzeugen
XSLT zusammensetzen
XSLT erzeugen
<?xml-stylesheet type="text/xsl" href="821_01.xslt"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xs:element name="Erfolguebersicht">
<xs:complexType>
<xs:sequence>
<xs:element name="Erfolg" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="Gesamt" type="xs:string"/>
<xs:element name="Neukunden" type="xs:string"/>
</xs:sequence>
<xs:attribute name="Stadt" type="xs:string" use="required"/>
<xs:attribute name="Monat" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
XSLT erzeugen
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fn="http://www.w3.org/2005/02/xpath-functions"
xmlns:xdt="http://www.w3.org/2005/02/xpath-datatypes"
exclude-result-prefixes="fn xdt xs">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<!-- Globale Parameter -->
<xsl:param name="Wurzel"/>
<!-- Startvorlage -->
<xsl:template match="/xs:schema/xs:element[parent::xs:schema]">
<xsl:element name="xsl:stylesheet">
<xsl:attribute name="version">2.0</xsl:attribute>
<xsl:namespace name="fn">http://www.w3.org/2005/02/xpathfunctions</xsl:namespace>
<xsl:namespace name="xdt">http://www.w3.org/2005/02/xpathdatatypes</xsl:namespace>
<xsl:namespace name="xs">http://www.w3.org/2001/XMLSchema</xsl:namespace>
<xsl:element name="xsl:output">
<xsl:attribute name="method">html</xsl:attribute>
<xsl:attribute name="version">1.0</xsl:attribute>
<xsl:attribute name="encoding">UTF-8</xsl:attribute>
<xsl:attribute name="indent">yes</xsl:attribute>
</xsl:element>
Dynamisches XSLT
XSLT erzeugen
XSLT zusammensetzen
XSLT zusammensetzen
Kategorie
Neuerstellung
Zusammensetzen
Speicherort
Es ist kein Speicherort vorhanden. Die
Daten werden auf Basis einer
externen Informationsquelle neu
erstellt.
Die Päckchen (globale komplexe Typen /
Vorlagen) liegen in einzelnen Dateien
oder in einer Datenbank.
Grundprinzip
XSLT generiert neues XSLT, das dann für
Eine Transformation zur Verfügung
steht.
XSLT oder eine andere Sprache setzt neues
XSLT zusammen, das dann für eine
Transformation zur Verfügung steht.
Aufbau
XML Schema kann Datenstrukturen
beschreiben, die dann mit Hilfe von
allgemeinen Vorlagen umgewandelt
werden. Alternativ kann es auch
Zusatzinformationen geben, die die
Ausgabe (Tabelle, Liste,
Summenbildung etc.) steuern.
Für Textdateien stehen die einfachen
Techniken der dateibasierten
Auslagerung zur Verfügung.
Für den Datenbank-Einsatz müssen die
Päckchen zunächst gespeichert und dann
bei Bedarf durch eine Abfrage
zusammengesetzt werden.
XSLT zusammensetzen
<Kundenliste
xmlns:xsi="http://www.w3.org/2001/XMLSchemainstance"
xsi:noNamespaceSchemaLocation="221_01.xsd">
<Kunde Nr="235" Anrede="Frau" Beginn="04.10.03">
<Name>
<Rufname>Verena</Rufname>
<Zuname>Fiegert</Zuname>
</Name>
<Adresse>
<Strasse>Universitätsstr. 40</Strasse>
<PLZ>47051</PLZ>
<Stadt>Duisburg</Stadt>
</Adresse>
</Kunde>
...
XSLT zusammensetzen
Es sind zwei unterschiedliche Möglichkeiten der dateibasierten Auslagerung
vorhanden:
Bei der Einbindung handelt es sich um einen Vorgang, der die Inhalte einer externen Datei
wie bei einem Kopiervorgang in den Quelltext einfügt. Da zudem das xsl:include-Element
überall als Element der obersten Ebene erscheinen darf – d.h. auch zwischen
xsl:template-Elementen –, kann man über die Position bestimmen, ob bei
konkurrierenden Vorlagen die eingebundenen oder die lokal vorhandenen ausgeführt
werden.
<!-- Kategorie: Deklaration -->
<xsl:include
href = uri-reference />
Beim Import handelt es sich um eine Einbettung von externen Vorlagen, die stets eine
geringere Priorität haben als lokal vorhandene Vorlagen. Das xsl:import-Element darf an
keiner anderen Stelle stehen als an erster Stelle aller Elemente der obersten Ebene.
<!-- Kategorie: Deklaration -->
<xsl:import
href = uri-reference />
XSLT zusammensetzen
Import und Inklusion
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" version="1.0" encoding="ISO-8859-1" indent="yes"/>
<xsl:include href="kundenliste.xslt"/>
<xsl:include href="kunde.xslt"/>
<xsl:include href="name.xslt"/>
<xsl:include href="adresse.xslt"/>
</xsl:stylesheet>
XSLT zusammensetzen
Externe Dokumente
zusammensetzen
<paeckchenstruktur>
<ausgabe typ="uebersichtsliste"/>
<paeckchen name="kundenliste.xslt"/>
<paeckchen name="kunde.xslt"/>
<paeckchen name="name.xslt"/>
<paeckchen name="adresse.xslt"/>
</paeckchenstruktur>
XSLT zusammensetzen
Datenbankeinsatz
-- Tabelle BERICHT
DROP TABLE "SCOTT"."BERICHT";
CREATE TABLE "SCOTT"."BERICHT" (
"B_NR" NUMBER(10) NOT NULL,
"B_NAME" VARCHAR2(50) NOT NULL,
"B_WURZEL" "SYS"."XMLTYPE" NOT NULL,
CONSTRAINT "B_Schluessel" PRIMARY KEY("B_NR"));
-- Tabelle XMLPAKET
DROP TABLE "SCOTT"."XMLPAKET";
CREATE TABLE "SCOTT"."XMLPAKET" (
"X_NR" NUMBER(10) NOT NULL,
"X_XMLSCHEMA" "SYS"."XMLTYPE" NOT NULL,
"X_XSLT" "SYS"."XMLTYPE" NOT NULL,
CONSTRAINT "X_Schluessel" PRIMARY KEY("X_NR"));
-- Tabelle BERICHT_ZU_XMLPAKET
DROP TABLE "SCOTT"."BERICHT_ZU_XMLPAKET";
CREATE TABLE "SCOTT"."BERICHT_ZU_XMLPAKET" (
"BZX_NR" NUMBER(10) NOT NULL,
"B_NR" NUMBER(10) NOT NULL,
"X_NR" NUMBER(10) NOT NULL,
CONSTRAINT "BZX_Schluessel" PRIMARY KEY("BZX_NR"));
XSLT zusammensetzen
Datenbankeinsatz
INSERT INTO "SCOTT"."BERICHT"
VALUES (1,
'Kundenliste',
XMLType('<xs:element name="Kundenliste" type="KundenlisteType"
xmlns:xs="http://www.w3.org/2001/XMLSchema"/>'));
XSLT zusammensetzen
Datenbankeinsatz
INSERT INTO "SCOTT"."XMLPAKET"
VALUES (1,
XMLType('<xs:complexType name="KundenlisteType"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:sequence>
<xs:element name="Kunde" type="KundeType" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>'),
XMLType('<xsl:template match="/Kundenliste"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<html>
<head>
<title>Kundenliste</title>
</head>
<body>
<xsl:apply-templates select="Kunde"/>
</body>
</html>
</xsl:template>'));
XSLT zusammensetzen
Datenbankeinsatz
CREATE OR REPLACE FUNCTION "SCOTT".getXSLT (
berichtsnr IN "SCOTT"."BERICHT"."B_NR"%TYPE)
RETURN "SYS"."XMLTYPE"
IS
-- Zwischenspeicher Rückgabewert
v_XSLT VARCHAR2(32767);
-- Cursor für Rückgabedaten
CURSOR c_XSLT IS
SELECT X_XSLT
FROM "SCOTT"."XMLPAKET" xp
INNER JOIN "SCOTT"."BERICHT_ZU_XMLPAKET" bzx
ON xp."X_NR" = bzx."X_NR"
INNER JOIN "SCOTT"."BERICHT" b
ON b."B_NR" = bzx."B_NR"
WHERE b."B_NR" = berichtsnr;
BEGIN
-- Dokumentbeginn
v_XSLT := '<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" version="1.0" encoding="UTF-8"
indent="yes"/>';
-- Auslesen des Cursors
FOR x_daten IN c_XSLT LOOP
v_XSLT := v_XSLT || x_daten.x_xslt.getClobVal();
END LOOP;
-- Dokumentschluss
v_XSLT := v_XSLT || '</xsl:stylesheet>';
-- Rückgabe
RETURN XMLType(v_XSLT);
END getXSLT;
XSLT zusammensetzen
Datenbankeinsatz
XSLT zusammensetzen
Datenbankeinsatz
-- Tabelle XMLDATEN
DROP TABLE "SCOTT"."XMLDATEN";
CREATE TABLE "SCOTT"."XMLDATEN" (
"XD_NR" NUMBER(10) NOT NULL,
"XD_TEXT" "SYS"."XMLTYPE" NOT NULL,
CONSTRAINT "XD_Schluessel" PRIMARY KEY("XD_NR"));
-- Tabelle XMLDATEN
DROP TABLE "SCOTT"."XMLDATEN_ZU_BERICHT";
CREATE TABLE "SCOTT"."XMLDATEN_ZU_BERICHT" (
"XZB_NR" NUMBER(10) NOT NULL,
"XD_NR" NUMBER(10) NOT NULL,
"B_NR" NUMBER(10) NOT NULL,
CONSTRAINT "XZB_Schluessel" PRIMARY KEY("XZB_NR"));
XSLT zusammensetzen
Datenbankeinsatz
INSERT INTO "SCOTT"."XMLDATEN"
VALUES (1,
XMLType('<Kundenliste>
<Kunde Nr="235" Anrede="Frau" Beginn="04.10.03">
<Name>
<Rufname>Verena</Rufname>
<Zuname>Fiegert</Zuname>
</Name>
...
</Kundenliste>'));
XSLT zusammensetzen
Datenbankeinsatz
Fragen...
Herunterladen