Tipps & Tricks - XML per Datentransfer einlesen

Da XML-Dateien sehr unterschiedliche Strukturen haben können, kommt man nicht umhin, 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 ein Applikationsexport mit Daten vorgenommen. Ändern Sie die Endung ".lax" der Exportdatei um in ".zip", um die Datei anschließend zu entpacken. Intrexx speichert Daten im Applikations-Export als XML. Das Verzeichnis, in dem Sie die entsprechende Daten-Datei in der entpackten Exportdatei finden können, wird von Intrexx wie folgt erzeugt:

Name-der-Applikation\GUID-der-Applikation\data

In diesem Ordner werden die XML-Dateien, die die Daten enthalten, nach den entsprechenden Datengruppen, die in der Export-Applikation enthalten sind, benannt - z.B. XDATAGROUP00D1A218.data. Die Struktur der XML-Datei sieht in etwa so aus:
<dump>
 <schema>
  …
 </schema>
 <data>
   <r>
    <c n="lid">1</c>
    <c n="luserid">1</c>
    <c n="luseridinsert">1</c>
    <c n="dtinsert"> 2016-11-24T08:59:42.064Z</c>
    <c n="dtedit"> 2016-11-24T08:59:42.064Z</c>
    <c n="str_titel_6a585d30">Erster Eintrag</c>
   </r>
   …
 </data>
</dump>
Für jeden Datensatz kommt ein weiteres <r>-Element dazu.

Groovy-Skript schreiben

Beim Erstellen des Datentransfers muss die Datenquelle 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.



Zunächst muss der Klasse DataSet eine Methode parseXML hinzugefügt werden, die den Inhalt unabhängig vom eigentlichen Datentyp als Text in einen Zwischenspeicher lädt. Diese muss beim Erstellen des DataSet 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. Hier das komplette Groovy-Skript für die Quelle:
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:\\XML_SOURCE_DATA.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, [
            "lid":java.lang.Integer,
            "luserid":java.lang.Integer,
            "luseridinsert":java.lang.Integer,
            "dtinsert":java.sql.Timestamp,
            "dtedit":java.sql.Timestamp,
            "str_titel":java.lang.String,
        ])
        def row = rows[i]
        l_record.setValue("lid", Integer.parseInt(row.get("lid")))
        l_record.setValue("luserid", Integer.parseInt(row.get("luserid")))
        l_record.setValue("luseridinsert", Integer.parseInt(row.get("luseridinsert")))
        l_record.setValue("dtinsert", new java.sql.Timestamp(sdf.parse(row.get("dtinsert")).getTime()))
        l_record.setValue("dtedit", new java.sql.Timestamp(sdf.parse(row.get("dtedit")).getTime()))
        l_record.setValue("str_titel", row.get("str_titel"))

        i++;
        return l_record
    }
    void close(){}
}
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.

Kopieren Sie das Beispiel-Skript und fügen Sie es im Editor ein. Klicken Sie "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".



Ö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. Hier das Groovy-Skript für die Transformation:
g_destination.setValue("lid", g_source.getValue("lid"))
g_destination.setValue("luserid", g_source.getValue("luserid"))
g_destination.setValue("luseridinsert", g_source.getValue("luseridinsert"))
g_destination.setValue("dtinsert", g_source.getValue("dtinsert"))
g_destination.setValue("dtedit", g_source.getValue("dtedit"))
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.