Tipps & Tricks - XML per Datentransfer einlesen

Da XML-Dateien sehr unterschiedliche Strukturen haben können, ist es unumgänglich, für jeden Import einer XML-Datei ein individuelles Import-Skript zu schreiben. Intrexx bietet dafür die Möglichkeit, bei einem Datentransfer ein entsprechendes Groovy-Skript als Datenquelle zu verwenden.

XML-Struktur analysieren

Für dieses Beispiel wird der Export einer Basisapplikation verwendet. Die enthaltenen Daten werden mit exportiert. Dazu wird beim Export die Einstellung "Mit Daten exportieren" gesetzt.

Entpacken Sie die exportierte ZIP-Datei.

Ändern Sie die Endung ".lax" des enthaltenen Applikationsexports um in ".zip" und entpacken Sie auch diese Datei.

Sie finden dann einen Ordner mit der GUID der Applikation. In diesem Ordner finden Sie den Ordner "data", der die Daten der Applikation als XML enthält.

Die XML-Dateien tragen denselben Namen wie die Datengruppen in der Applikation, aus denen die Daten stammen (z.B. "IX_BASIC_DATAGROUP.data"). Die Struktur der XML-Datei sieht in etwa so aus:

<dump>
	<schema>
		...
	</schema>
	<data>
		<r>
			<c n="data-field1-name">1</c>
			<c n="data-field2-name">1</c>
			<c n="data-field3-name">First entry</c>
		</r>
		...
	</data>
</dump>

Für jeden Datensatz kommt ein weiteres <r>-Element dazu.

Groovy-Skript schreiben

Beim Erstellen des Datentransfers muss die Datenquelle und die Transformationsart auf Groovy-Skript geändert werden.

Klicken Sie "Weiter".

Intrexx generiert zu Beginn ein Dummy-Skript, das allgemein gehalten und nicht speziell für XML-Daten gedacht ist. Dieses Skript muss nun noch abgeändert werden. Öffnen Sie dazu den Intrexx-Editor, indem Sie hier auf den entsprechenden Link klicken.

Ersetzen Sie das bereits enthaltene Skript mit dem hier folgenden:

import de.uplanet.lucy.server.datatrans.IDataSet
import de.uplanet.lucy.server.datatrans.IDataObject
import de.uplanet.lucy.server.datatrans.table.DefaultDataRecord
import java.text.SimpleDateFormat
import java.util.TimeZone

void open(){}
void close(){}
/**
 * Returns the data set, from which the data will be read when importing from this data source.
 * @return data set
 */
IDataSet getDataSet()
{
    def ds = new DataSet()
    ds.parseXML()
    return ds
}
/**
 * This class is the data source specific data set implementation.
 */
class DataSet implements IDataSet
{
    def sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
    def fileContents = new File("C://work/IX_BASIC_DATAGROUP.data").getText("UTF-8")
    def content = new XmlParser().parseText(fileContents)
    def xmlRows = content.data.r
    def rows = []
    def i = 0

    void parseXML(){
        for(xmlRow in xmlRows)
        {
            def row = [:]
            def xmlColumns = xmlRow.c

            for(xmlColumn in xmlColumns)
            {
                def strColumnName =  xmlColumn."@n"
                def strValue            =  xmlColumn.text()
                row.put(strColumnName, strValue)
            }

            rows.add(row)
        }
    }
    IDataObject next(){
        if (i >= rows.size())   return null;
        sdf.setTimeZone(TimeZone.getTimeZone("UTC"))
        def l_record = new DefaultDataRecord(null, [
            "dtedit":java.sql.Timestamp,
            "luserid":java.lang.Integer,
            "dtinsert":java.sql.Timestamp,
            "luseridinsert":java.lang.Integer,
            "strid":java.lang.String,
            "str_titel":java.lang.String
        ])
        def row = rows[i]
        l_record.setValue("dtedit", new java.sql.Timestamp(sdf.parse(row.get("dtedit")).getTime()))
        l_record.setValue("luserid", Integer.parseInt(row.get("luserid")))
        l_record.setValue("dtinsert", new java.sql.Timestamp(sdf.parse(row.get("dtinsert")).getTime()))
        l_record.setValue("luseridinsert", Integer.parseInt(row.get("luseridinsert")))
        l_record.setValue("strid", row.get("strid"))
        l_record.setValue("str_titel", row.get("str_titel"))

        i++;
        return l_record
    }
    void close(){}
}

Im Skript wird der Klasse "DataSet" zunächst die Methode "parseXML" hinzugefügt, die den Inhalt unabhängig vom eigentlichen Datentyp als Text in einen Zwischenspeicher lädt. Diese Methode muss beim Erstellen des DataSets in der Methode "getDataSet" auch zwingend aufgerufen werden, bevor das erzeugte Objekt zurückgegeben wird.

Anschließend wird die Methode "next" angepasst, um die Werte aus dem Zwischenspeicher in den tatsächlichen Datentyp zu konvertieren. In diesem Beispiel findet man die korrekten Java-Datentypen in der XML-Datei selbst, dort werden im Schema-Abschnitt die Datentypen der einzelnen Datenfelder mit angegeben.

Die XML-Datei wird bei dieser Methode zuerst komplett in den Arbeitsspeicher geladen und anschließend verarbeitet. Bei großen Dateien kann dies zu einem Speicherüberlauf führen, weshalb in diesem Fall speziellere Methoden genutzt werden sollten.

Speichern Sie das Skript mit Klick auf "OK" und klicken Sie dann "Weiter".

Wählen Sie hier das Ziel für den Import aus und klicken Sie "Weiter".

Wählen Sie hier die Datengruppe aus, in die die Daten importiert werden sollen. Klicken Sie "Weiter".

Zuletzt müssen die Daten noch transformiert werden. Dazu muss die Einstellung "Transformation verwenden, wenn unterstützt" hier gesetzt sein. Klicken Sie "Weiter".

Nun wird noch das Groovy-Skript für die Transformation benötigt. Öffnen Sie den Intrexx-Editor.

Der Assistent beim Anlegen des Datentransfers hat das Feld für die Transformation bereits entsprechend geändert, sodass hier jetzt auch ein Groovy-Skript hinterlegt werden kann. Die Transformation ordnet Datenfelder aus der Quelle den Datenfeldern im Ziel zu. Werden beim Import eigene IDs benötigt, so ist dies die Stelle, an der sie erzeugt und dem ID-Feld zugeordnet werden können.

Für dieses Beispiel wird dieses Vorgehen nicht benötigt. Die Quell- und Zielfelder können hier ganz einfach entsprechend zugeordnet werden. Ersetzen Sie das bereits enthaltene Skript mit dem hier folgenden:

g_destination.setValue("dtedit", g_source.getValue("dtedit"))
g_destination.setValue("luserid", g_source.getValue("luserid"))
g_destination.setValue("dtinsert", g_source.getValue("dtinsert"))
g_destination.setValue("luseridinsert", g_source.getValue("luseridinsert"))
g_destination.setValue("strid", g_source.getValue("strid"))
g_destination.setValue("str_titel", g_source.getValue("str_titel"))

Bitte prüfen Sie die Groß- / Kleinschreibung im Groovy-Skript, wenn Daten nicht übernommen werden. Je nach Servertyp muss u.U. die Groß- / Kleinschreibung sowohl in der Formulierung der Quelle als auch in der Transformation berücksichtigt werden.