Zusammenführen mehrerer XML-Dokumente mit DBMS_XMLDOM

Möchte man in PL/SQL XML-Dokumente erstellen und verwenden, hat man mehrere Möglichkeiten. Im Folgenden werden einige Möglichkeiten aufgezeigt und anhand eines Beispiels werden zwei XML-Dokumente zusammengeführt.

Bei sehr kleinen und einfachen Strukturen reicht es, einen passenden String in eine XMLTYPE Variable zu schreiben:

DECLARE
   l_xml_db   XMLTYPE := xmltype('<ORACLE><Version>11g</Version><Version>12c</Version></ORACLE>');
BEGIN
   DBMS_OUTPUT.put_line(l_xml_db.getstringval());
END;
/

Möchte man die Ergebnismenge eines Select Statements direkt in eine XML-Struktur überführen lohnt ein Blick auf  das DBMS_XMLGEN Package:

DECLARE
   l_xml       XMLTYPE;
   lh_xmlctx   DBMS_XMLGEN.ctxhandle;
BEGIN
   lh_xmlctx := DBMS_XMLGEN.newcontext('SELECT sysdate
                                                   systimestamp
                                              FROM dual');

   DBMS_XMLGEN.setrowsettag(lh_xmlctx, 'Uhrzeiten');
   DBMS_XMLGEN.setrowtag(lh_xmlctx, 'Aktuell');

   l_xml := DBMS_XMLGEN.getxmltype(lh_xmlctx);
   DBMS_XMLGEN.closecontext(lh_xmlctx);

   DBMS_OUTPUT.put_line(l_xml.getclobval);
END;
/

Die umfangreichsten Möglichkeiten bekommt man mit dem – in der Oracle XML DB enthaltenen – Package DBMS_XMLDOM. Dieses Package bietet umfangreiche Bearbeitungs- und Validierungs-Möglichkeiten, ist jedoch in der Anwendung komplexer als DBMS_XMLGEN bzw. XMLTYPE.

In dem folgenden Beispiel wird gezeigt wie zwei bereits bestehende XML-Dokumente mittels DBMS_XMLDOM in ein Dokument zusammengeführt werden. Zu Beginn wird das folgende XML-Dokument aufgebaut in welches die weiteren Daten importiert werden sollen.

<Versionen>
  <Datenbanken>
  </Datenbanken>
  <Software>
  </Software>
</Versionen>

Die Aufbau des Dokuments und der Import der Daten wird durch das folgende PL/SQL Programm durchgeführt. Zur Veranschaulichung wird das finale XML-Dokument über DBMS_OUTPUT ausgegeben.

DECLARE
   lh_xmldoc            DBMS_XMLDOM.domdocument;
   l_node_root          DBMS_XMLDOM.domnode;
   l_node_versionen     DBMS_XMLDOM.domnode;
   l_node_datenbanken   DBMS_XMLDOM.domnode;
   l_node_software      DBMS_XMLDOM.domnode;

   --Knoten <Datenbank>
   l_subnode_db         DBMS_XMLDOM.domnode;
   l_xml_db             XMLTYPE := xmltype('<ORACLE><Version>11g</Version><Version>12c</Version></ORACLE>');

   --Knoten <OraInfo>
   l_subnode_orainfo    DBMS_XMLDOM.domnode;
   l_xml_orainfo        CLOB := '<OraInfo><Version>1</Version></OraInfo>';

   --Debug
   l_buffer_root        VARCHAR2(1000);
BEGIN
   --DOM Document erzeugen
   lh_xmldoc := DBMS_XMLDOM.newdomdocument;
   --Root Knoten
   l_node_root := DBMS_XMLDOM.makenode(lh_xmldoc);

   --<oracle> Knoten erzeugen
   l_node_versionen :=
      DBMS_XMLDOM.appendchild(l_node_root, DBMS_XMLDOM.makenode(DBMS_XMLDOM.createelement(lh_xmldoc, 'Versionen')));
   --Knoten <Datenbanken> erzeugen
   l_node_datenbanken :=
      DBMS_XMLDOM.appendchild(l_node_versionen,
                              DBMS_XMLDOM.makenode(DBMS_XMLDOM.createelement(lh_xmldoc, 'Datenbanken')));
   --Knoten <Software> erzeugen
   l_node_software :=
      DBMS_XMLDOM.appendchild(l_node_versionen, DBMS_XMLDOM.makenode(DBMS_XMLDOM.createelement(lh_xmldoc, 'Software')));

   --Inhalt aus l_xml_db in lh_xmldoc importieren
   --Anmerkung: getdocumentelement referenziert den Root-Knoten des Dokuments
   l_subnode_db :=
      DBMS_XMLDOM.appendchild(
         l_node_datenbanken,
         DBMS_XMLDOM.importnode(
            lh_xmldoc,
            DBMS_XMLDOM.makenode(DBMS_XMLDOM.getdocumentelement(DBMS_XMLDOM.newdomdocument(l_xml_db))),
            TRUE));

   --Inhalt aus l_xml_db in lh_xmldoc importieren
   --Anmerkung: getdocumentelement referenziert den Root-Knoten des Dokuments
   l_subnode_orainfo :=
      DBMS_XMLDOM.appendchild(
         l_node_software,
         DBMS_XMLDOM.importnode(
            lh_xmldoc,
            DBMS_XMLDOM.makenode(DBMS_XMLDOM.getdocumentelement(DBMS_XMLDOM.newdomdocument(l_xml_orainfo))),
            TRUE));

   ---DEBUG START---
   DBMS_XMLDOM.writetobuffer(l_node_root, l_buffer_root);
   DBMS_OUTPUT.put_line(l_buffer_root);
---DEBUG ENDE---
EXCEPTION
   WHEN OTHERS THEN
      RAISE;
END;
/

Übersicht der verwendeten Variablen und deren Zweck:

  •  lh_xmldoc
    Referenz auf Ziel XML-Dokument
  • l_node_root
    Referenz auf Rootknoten im Ziel XML-Dokument
  • l_node_versionen
    Referenz auf <Versionen> Knoten
  • l_node_datenbanken
    Referenz auf <Datenbanken> Knoten
  • l_node_software
    Referenz auf <Software> Knoten
  • l_subnode_db
    Referenz auf importierten Knoten <ORACLE>
  • l_xml_db
    Quell XML-Dokument mit <ORACLE> Knoten
  • l_subnode_orainfo
    Referenz auf importierten Knoten <OraInfo>
  • l_xml_orainfo
    Quell XML-Dokument mit <OraInfo> Knoten
  • l_buffer_root
    Debug Buffer-Variable zum Ausgeben des Gesamtdokuments

Das Vorgehen zum Import von XML-Dokumenten ist in beiden Fällen identisch:

DBMS_XMLDOM.appendchild(
         l_node_datenbanken,
         DBMS_XMLDOM.importnode(
            lh_xmldoc,
            DBMS_XMLDOM.makenode(DBMS_XMLDOM.getdocumentelement(DBMS_XMLDOM.newdomdocument(l_xml_db))),
            TRUE));

Mit der DBMS_XMLDOM.newdomdocument Funktion wird die XMLTYPE / CLOB Variable in ein domdocument konvertiert. Anschießend wird über DBMS_XMLDOM.getdocumentelement eine Referenz auf den Rootknoten gesetzt und an die übergeordnete DBMS_XMLDOM.makenode Funktion übergeben. Diese baut einen neuen XML-Knoten welcher nun über die DBMS_XMLDOM.importnode Funktion in das XML-Dokument lh_xmldoc importiert wird. Da der obrige Code die Variable l_xml_db importiert, werden die Daten über DBMS_XMLDOM.appendchild als Subknoten an l_node_datenbanken angehangen.

Es stellt sich jedoch die Frage warum man sich den ganzen Aufwand machen möchte. Wäre es nicht einfacher die XML-Strings bei Bedarf zusammenzubauen und weiter in einer XMLTYPE / CLOB Variable zu speichern? Hier kann gesagt werden, dass das DBMS_XMLDOM  Package einige wesentliche Vorteile mitbringt.

  • Erweiterte XML Fehlerprüfung
    Fehler in der XML Struktur welche in einer XMLTYPE / CLOB Variable nicht immer erkannt werden, werden in einem domdocument als Exception gemeldet und können in einem PL/SQL Exception Block behandelt werden.
  • Es können direkt Dateien in einem frei wählbaren Zeichensatz geschrieben werden
    Zwar können auch XMLTYPE und CLOB Dokumente in das Dateisystem geschrieben werden, dies jedoch nur möglich mit den in NLS_CHARACTERSET / NLS_NCHAR_CHARACTERSET eingetragenen Zeichensätzen.
  • Zugriff auf Oracle XML DB Funktionalität
    Die Oracle XML DB bietet eine vollständige XML Implementierung inklusive Datenmanipulation, Validierung mittels XML Schema Definitionen (XSD), und vielem mehr.

Das oben gezeigte Vorgehen ermöglicht es bereits bestehende Programmstrukturen welche zum Beispiel auf DBMS_XMLGEN aufbauen, bestehen zu lassen und diese in die DBMS_XMLDOM Strukturen zu importieren. Dies kann den Migrationsaufwand erheblich verringern.

Schreibe einen Kommentar