Der XmlJdbc Treiber erlaubt es, XML-Dateien über Jdbc zu lesen. Mit Hilfe von SQL-Abfragen können damit Daten aus einer XML-Datei genauso wie aus einer Datenbanktabelle extrahiert und weiterverarbeitet werden.
Zur Abfrage werden Elemente in einem XML-Dokument als Tabellen angesprochen. Dazu verwendet man drei Komponenten in der Abfrage:
Das folgende XML-Dokument kann man zum Beispiel mit Hilfe des Treibers einlesen:
<?xml version="1.0" encoding="UTF-8"?>
<people>
<person id="10" profession="scientist">
<fname>Henry</fname>
<lname>Cavendish</lname>
</person>
<person id="21" profession="scientist">
<fname>Isaac</fname>
<lname>Newton</lname>
</person>
<person id="33" profession="scientist">
<fname>Doctor</fname>
<lname>Seltsam</lname>
</person>
</people>
Eine SQL-Abfrage über den XmlJdbc Treiber sieht dann so aus:
SELECT "@id"
, fname
, lname
, "@profession"
FROM "xmljdbc/src/test/resources/de/softquadrat/jdbc/xml/people.xml"."/people/person"
COLUMNS("@id", fname, lname, "@profession")
Dabei wird der Dateiname "xmljdbc/src/test/resources/de/softquadrat/jdbc/xml/people.xml" als Schema angegeben und "/people/person" ist ein Pfad-Ausdruck, der zu den "person" Elementen im Dokument führt.
Innerhalb der "person" Elemente werden mit "@id" und "@profession" die Attribute und mit "fname" und "lname" die Child-Elemente angesprochen.
Das Ergebnis sieht dann so aus:
@id fname lname @profession
--- ------ --------- -----------
10 Henry Cavendish scientist
21 Isaac Newton scientist
33 Doctor Seltsam scientist
Wie gewohnt können Spaltenaliase vergeben werden, um die Spaltennamen umzubenennen:
SELECT "@id" AS id
, fname AS vorname
, lname AS nachname
, "@profession" AS beruf
FROM "xmljdbc/src/test/resources/de/softquadrat/jdbc/xml/people.xml"."/people/person"
COLUMNS("@id", fname, lname, "@profession")
Das liefert dann:
id vorname nachname beruf
-- ------- --------- ---------
10 Henry Cavendish scientist
21 Isaac Newton scientist
33 Doctor Seltsam scientist
Neben den Datenspalten aus dem XML-Dokument generiert der XmlJdbc Treiber zusätzliche Pseudospalten:
SELECT rownumber AS rownum
, "$id" AS zeilen_id
, "@id" AS id
, fname
, lname
, "@profession"
FROM "xmljdbc/src/test/resources/de/softquadrat/jdbc/xml/people.xml"."/people/person"
COLUMNS("@id", fname, lname, "@profession")
Die Pseudospalte "rownumber" liefert eine fortlaufende Nummer für jeden Datensatz des Ergebnisses. Die Pseudospalte "$id" liefert eine fortlaufende Nummer für das Element im Dokument. Das Ergebnis der Beispielabfrage sieht so aus:
rownum zeilen_id id fname lname @profession
------ --------- -- ------ --------- -----------
1 0 10 Henry Cavendish scientist
2 1 21 Isaac Newton scientist
3 2 33 Doctor Seltsam scientist
Hinweis: Es gibt weiterhin noch die Pseudospalte "$parentid". Sie liefert den Pfad des darüberliegenden Elements, dazu mehr im nächsten Abschnitt.
Ein Element in einem XML-Dokument kann weitere Sub-Elemente enthalten wie in dem folgenden Beispiel:
<?xml version="1.0" encoding="UTF-8"?>
<people>
<person id="10" profession="scientist">
<fname>Henry</fname>
<lname>Cavendish</lname>
<research>
<area>Chemistry</area>
</research>
<research>
<area>Density of the Earth</area>
</research>
<research>
<area>Electricity</area>
</research>
</person>
<person id="21" profession="scientist">
<fname>Isaac</fname>
<lname>Newton</lname>
<research>
<area>Calculus</area>
</research>
<research>
<area>Optics</area>
</research>
<research>
<area>Gravity</area>
</research>
</person>
<person id="33" profession="scientist">
<fname>Doctor</fname>
<lname>Seltsam</lname>
</person>
</people>
Verschachtelte Elemente können auf zwei Arten gelesen werden:
Alle Elemente können direkt über den vollständigen Pfad adressiert werden:
SELECT area
FROM "xmljdbc/src/test/resources/de/softquadrat/jdbc/xml/researcher.xml"."/people/person/research"
COLUMNS(area)
Die Abfrage liefert alle "research" Elemente:
area
--------------------
Chemistry
Density of the Earth
Electricity
Calculus
Optics
Gravity
Das ist das einfachste Verfahren, allerdings fehlt bei diesem Ansatz der Bezug zu den übergeordneten Elementen.
Der XmlJdbc Treiber unterstützt auch komma-separierte Navigation, ähnlich wie der JsonJdbc oder der YamlJdbc Treiber:
SELECT "$parentid", area
FROM "xmljdbc/src/test/resources/de/softquadrat/jdbc/xml/researcher.xml"."/people/person,research"
COLUMNS(area)
Hier navigiert "/people/person" zum ersten Hierarchie-Level und "research" (ohne führenden Slash!) zum zweiten Level relativ zu den gefundenen "person" Elementen.
Das liefert:
$parentid area
--------- --------------------
0 Chemistry
0 Density of the Earth
0 Electricity
1 Calculus
1 Optics
1 Gravity
Die "$parentid" zeigt, zu welchem "person" Element die "research" Elemente gehören (0 = erstes person Element, 1 = zweites person Element, usw.).
Die Pseudospalte "$parentid" ermöglicht die Korrelation zwischen verschachtelten Elementen und ihren Eltern-Elementen.
Best Practice für verschachtelte Hierarchien:
SELECT "$id", "@id" AS person_id, fname, lname
FROM "researcher.xml"."/people/person"
COLUMNS("@id", fname, lname)
Das liefert:
$id person_id fname lname
--- --------- ------ --------
0 10 Henry Cavendish
1 21 Isaac Newton
2 33 Doctor Seltsam
SELECT "$parentid", area
FROM "researcher.xml"."/people/person,research"
COLUMNS(area)
Das liefert:
$parentid area
--------- --------------------
0 Chemistry
0 Density of the Earth
0 Electricity
1 Calculus
1 Optics
1 Gravity
Bei komma-separierter Navigation:
Beispiel:
-- Korrekt:
FROM "file.xml"."/root/element,subelement,subsubelement"
↑ absolut ↑ relativ ↑ relativ
-- Falsch:
FROM "file.xml"."/root/element,/subelement,/subsubelement"
↑ nicht relativ!
Der XmlJdbc Treiber unterscheidet zwischen XML-Attributen und XML-Elementen:
@attributeName
elementName
elementName/@attributeName
referenziertBeispiel mit gemischten Attributen und Elementen:
<?xml version="1.0" encoding="UTF-8"?>
<books>
<book id="1" category="fiction">
<title>The Great Gatsby</title>
<author>F. Scott Fitzgerald</author>
<year>1925</year>
<price currency="EUR">10.99</price>
</book>
<book id="2" category="programming">
<title>Clean Code</title>
<author>Robert C. Martin</author>
<year>2008</year>
<price currency="USD">45.00</price>
</book>
</books>
SELECT "@id" AS id
, "@category" AS kategorie
, title
, author
, year
, price
, "price/@currency" AS waehrung
FROM "books.xml"."/books/book"
COLUMNS("@id", "@category", title, author, year, price, "price/@currency")
Das liefert:
id kategorie title author year price waehrung
-- ------------ ---------------- -------------------- ---- ----- --------
1 fiction The Great Gatsby F. Scott Fitzgerald 1925 10.99 EUR
2 programming Clean Code Robert C. Martin 2008 45.00 USD
Der XmlJdbc Treiber unterstützt Verzeichnisse als Systemtabellen. Das erlaubt in einer Loop-Query eines datasqill Moduls nach Dateien in einem Verzeichnis zu suchen, die dann anschließend geladen werden können.
Die folgende Abfrage benutzt die Funktion "files()" als Systemtabelle und ihre Spalten "directory" und "filename".
SELECT directory, filename
FROM "xmljdbc/src/test/resources/de/softquadrat/jdbc/xml".files()
WHERE filename = 'books.xml' OR filename = 'employees.xml'
ORDER BY filename
Als Ergebnis werden die gesuchten Dateien in dem verwendeten Verzeichnis zurückgeliefert:
directory filename
-------------------------------------------------- -------------
xmljdbc/src/test/resources/de/softquadrat/jdbc/xml books.xml
xmljdbc/src/test/resources/de/softquadrat/jdbc/xml employees.xml
Der XmlJdbc Treiber liefert alle Ergebnisse als Text zurück. Eine Umwandlung in andere Datentypen muss in nachgelagerten Arbeitsschritten erfolgen.
Sollte eine Abfrage keine Datensätze zurückgeben, so ist zu prüfen, ob der Pfad-Ausdruck in der FROM-Klausel tatsächlich zu XML-Elementen navigiert. Ist dies nicht der Fall, ignoriert der XmlJdbc Treiber die Abfrage und gibt keine Ergebnisse zurück.
Der XmlJdbc Treiber liefert für selektierte Spalten in der SELECT-Klausel deren Inhalt zurück. Ist das angesprochene Attribut oder Element nicht vorhanden, wird ein Null-Wert zurückgegeben.
<test>
<item id="1">
<name>Test1</name>
</item>
<item id="2">
<!-- name fehlt -->
</item>
</test>
SELECT "@id"
, name
, description
FROM "test.xml"."/test/item"
COLUMNS("@id", name, description)
@id name description
--- ----- -----------
1 Test1 null
2 null null
Ist das Element in der SELECT-Klausel nicht leer, wird dessen Textinhalt als Ergebnis zurückgegeben. Hat ein Element sowohl Textinhalt als auch Child-Elemente, wird nur der direkte Textinhalt (ohne die Child-Elemente) zurückgegeben.
<test>
<item id="1">
<name>Test1</name>
<details>
<created>17.3.2022</created>
<description>only a test</description>
</details>
</item>
<item id="2">
<name>Test2</name>
<tags>
<tag>important</tag>
<tag>urgent</tag>
</tags>
</item>
</test>
SELECT "@id"
, name
FROM "test.xml"."/test/item"
COLUMNS("@id", name)
@id name
--- -----
1 Test1
2 Test2
Der XmlJdbc Treiber verwendet einen vereinfachten XPath-Syntax für die Navigation im XML-Dokument. Vollständige XPath-Ausdrücke mit Prädikaten, Achsen oder Funktionen werden nicht unterstützt.
Unterstützt werden:
/root/element/subelement
@attributeName
elementName
elementName/@attributeName
Nicht unterstützt werden:
/root/element[@id='1']
//element
oder parent::*
count()
oder text()
Der XmlJdbc Treiber verwendet eine hybride Architektur für optimale Performance und Flexibilität:
Bei einfachen, nicht-hierarchischen Abfragen (ohne Komma-Separierung) wird StAX (Streaming API for XML) verwendet:
-- Verwendet StAX
FROM "file.xml"."/people/person"
Vorteile:
Bei hierarchischen Abfragen mit komma-separierter Navigation wird DOM (Document Object Model) verwendet:
-- Verwendet DOM
FROM "file.xml"."/people/person,research"
Hinweis:
Praktische Richtlinien für Dokumentgrößen:
Empfehlung: Verwenden Sie einfache Pfade ohne Komma-Separierung wann immer möglich, um von der Speichereffizienz von StAX zu profitieren. Für die meisten XML-Dateien in der Praxis (typisch < 100 MB) spielt der Unterschied jedoch keine große Rolle.
Der XmlJdbc Treiber erkennt automatisch die Zeichensatz-Deklaration im XML-Header (<?xml version="1.0" encoding="UTF-8"?>
). UTF-8 ist der Standard-Zeichensatz, wenn keine explizite Deklaration vorhanden ist.
Aktuell unterstützt der XmlJdbc Treiber keine XML-Namespaces. Elemente mit Namespace-Präfixen werden als reguläre Elementnamen behandelt.
Der YamlJdbc Treiber bietet eine einfache und intuitive Möglichkeit, XML-Dateien über SQL-Abfragen zu lesen: