OO-Calc-Exportfilter - Probleme mit number-columns-repeated
Joerg
- xsl
Hallo,
ich bekomme ca. 1x wöchtenlich die (händisch) aktualisierte Version eines ExcelSheets, in dem in ca. 1000 Zeilen Versionsverwaltung betrieben wird. Wir arbeiten mit OO-Calc 3.0. Mittels eines Exportfilters baue ich mir eine xml-Datei für andere Zwecke - klappt alles prima.
Ein Problem habe ich immer in Zeilen, in denen mehrere leere Felder hintereinander auftreten. Diese werden per "table:number-columns-repeated" zu einem Feld zusammengefasst gespeichert. Wird dies nicht aufgefangen, erhalte ich unsinnige Werte.
Wie kann ich dieses Problem möglichst elegant lösen, wenn ich potentiell mehrere "number-columns-repeated"-Attribute in einer Zeile behandeln muss und dabei den Restriktionen von xslt 1.0 unterworfen bin?
Ich hoffe, dass ich mich verständlich machen konnte und freue mich für jede Anregung.
Danke im Voraus,
Joerg
Oh ja,
das "Problem" kenne ich, ist aber eigentlich eine elegante Loesung und im nachhinein, eine elegante Loesung. Man muesste aber mehr wissen, wo genau dein Problem damit liegt. Also wenn du mal ein Beispiel geben koenntest, dann wuerde man auch in der Lage sein, etwas genauer Hilfestellung zu leisten.
Ein paar Punkte noch dazu angenmerkt:
Beachte, dass nach jeder table:table-cell/
ein entsprechendes Leerelement kommt um die "cover" table:covered-table-cell/
also etwa:
<table:table-cell table:number-columns-repeated="3"/>
<table:covered-table-cell table:number-columns-repeated="2"/>
<table:table-cell table:number-columns-repeated="2"/>
table:covered-table-cell/
Das hilft schon manchmal, um die verdeckten Zellen besser zu berechnen.
Moeglich loesungen:
Da du den export filter von OO benutzt (?) kannst du mit exslt:node-set erweiterung arbeiten, da diese fuer den Xalan bei OO zu verfuegung steht, d.h. du kannst in einem vorgelagerten Schritt deinen table-row "normalisieren" und die spanned cells mit leeren table-cells ersetzten.
Also etwa eine globale Veriable fuer alle Tabellen also normalisiertes nodeset
<xsl:stylesheet ... xmlns:xalan="http://xml.apache.org/xalan" >
<!-- ** globale variable **-->
<xsl:variable name="NormalTable">
<xsl:apply-templates selectt="//table:table" mode="cleanUp"/>
</xsl:variable>
<!-- ** template zum normalisieren **-->
<xsl:template match="table:table" mode="cleanUp">
<!-- hier code zur normalisierung -->
</xsl:template>
<!--** transformiere mit bestehenden xsl code **-->
<xsl:apply-templates select="xalan:nodeset($NormalTable/*)"/>
<!--** bestehende templates **-->
<xsl:template match="table:table">
<!-- do whetever you want to do -->
</xsl:template>
</xsl:stylesheet>
Alternative sind in solchen Faellen immer ganz gut mit rekursive Schleifen zu loesen.
Gruss, Holge r
Hallo,
ich bekomme ca. 1x wöchtenlich die (händisch) aktualisierte Version eines ExcelSheets, in dem in ca. 1000 Zeilen Versionsverwaltung betrieben wird. Wir arbeiten mit OO-Calc 3.0. Mittels eines Exportfilters baue ich mir eine xml-Datei für andere Zwecke - klappt alles prima.
Ein Problem habe ich immer in Zeilen, in denen mehrere leere Felder hintereinander auftreten. Diese werden per "table:number-columns-repeated" zu einem Feld zusammengefasst gespeichert. Wird dies nicht aufgefangen, erhalte ich unsinnige Werte.
Wie kann ich dieses Problem möglichst elegant lösen, wenn ich potentiell mehrere "number-columns-repeated"-Attribute in einer Zeile behandeln muss und dabei den Restriktionen von xslt 1.0 unterworfen bin?
Ich hoffe, dass ich mich verständlich machen konnte und freue mich für jede Anregung.
Danke im Voraus,
Joerg
Hi Holger,
danke für die Antwort.
<table:table-cell table:number-columns-repeated="3"/>
<table:covered-table-cell table:number-columns-repeated="2"/>
<table:table-cell table:number-columns-repeated="2"/>
table:covered-table-cell/
Dies konnte ich nicht beobachten. Wie gesagt, ich bekomme ein ExcelSheet, das von der Anlage her schon einige Jahre alt ist, das ich in Calc weiter bearbeite.
Die Idee mit exslt:node-set hat mich sofort gepackt, bringe das aber nicht richtig zustande. Und ob ich unter Calc mit xmlns:xalan arbeiten kann, hmm...
Hier mein (abgespecktes) StyleSheet:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"
xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0"
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0"
exclude-result-prefixes="office table text">
<xsl:output method = "xml" indent = "yes" encoding = "UTF-8" omit-xml-declaration = "yes"/>
<xsl:param name="targetURL"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<datasheet>
<xsl:apply-templates select="//table:table[1]"/> <!-- Nur 1. Tabellenblatt verwenden! -->
</datasheet>
</xsl:template>
<xsl:template match="table:table">
<xsl:variable name="col-repeater" select="4"/><!-- Spalte mit Repeated-Attribut -->
<xsl:variable name="col-master" select="3"/> <!-- Spalte mit Master -->
<xsl:variable name="col-lang" select="12"/><!-- Spalte mit Sprachkuerzel -->
<xsl:for-each select="table:table-row">
<!-- einige Spalten haben einen 'Repeater'-Wert -->
<xsl:variable name="repeater">
<xsl:choose>
<xsl:when test="table:table-cell[$col-repeater]/@table:number-columns-repeated">
<xsl:value-of select="table:table-cell[$col-repeater]/@table:number-columns-repeated"/>
</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!-- Ermittlen, wo sich die Sprache befindet: -->
<xsl:variable name="repeater_lang">
<xsl:choose>
<xsl:when test="$repeater>0">
<xsl:value-of select="$col-lang - $repeater + 1"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$col-lang"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<-- Sprache speichern -->
<xsl:variable name="land" select="table:table-cell[position()=$repeater_lang]/text:p"/>
<!-- Spalten quasi konstant definieren -->
<xsl:variable name="colclientid" select="$repeater_lang - 3"/>
<xsl:variable name="colversion" select="$repeater_lang - 2"/>
<xsl:variable name="colorder" select="$repeater_lang - 1"/>
<xsl:variable name="colrelease" select="$repeater_lang + 2"/>
<!-- selektiere alle Eintraege mit gueltigem Land -->
<xsl:if test="$land">
<record>
<Name><xsl:value-of select="table:table-cell/text:p"/></Name>
<Referenz-Spalte><xsl:value-of select="$repeater_lang"/></Referenz-Spalte>
<Korrektur><xsl:value-of select="$repeater"/></Korrektur>
<Version><xsl:value-of select="table:table-cell[$colversion]/text:p"/></Version>
<Sprache><xsl:value-of select="$land"/></Sprache>
<xsl:if test="table:table-cell[$colrelease]/text:p">
<xsl:if test="not (table:table-cell[$colversion]/text:p = table:table-cell[$colrelease]/text:p)" >
<Release><xsl:value-of select="table:table-cell[$colrelease]/text:p"/></Release>
</xsl:if>
</xsl:if>
<xsl:if test="table:table-cell[$colclientid]/text:p">
<ClientId><xsl:value-of select="table:table-cell[$colclientid]/text:p"/></ClientId>
</xsl:if>
</record>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Ein kleines muster, wie du etwa eine tabellenreihe ersetzten koenntest , indem das span attribut durch leere dummy-zellen ersetzt. Ob die nodset funktion verfuegbar ist, kannst du einfach mit einem function-available ueberpreufen (siehe beispiel unten) - beachte die xalan nodset unterscheidet sich von der exslt nod-set funtion in der schreibweise.
Das nachfolgende stylesheet etwa durchlauf jede tabellen reihe, wobei die reihe "bereinigt" wird und spanned attribute entfernt und durch leere dummy zellen ersetzt wird. Danach wird die Auswertung ganz normal auf die entsprechende reihe durchgefuehrt (siehe kleiner test im 3. schritt wieviel zellen in der reihe vorhanden sind.)
Gruss, Holge r
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xalan="http://xml.apache.org/xalan" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:exslt="http://exslt.org/common" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" exclude-result-prefixes="office exslt xalan table text">
<xsl:output method="xml" indent="yes" encoding="UTF-8" omit-xml-declaration="no"/>
<xsl:param name="targetURL"/>
<xsl:strip-space elements="*"/>
<!-- 1. Schritt -->
<xsl:template match="/">
<datasheet>
<xsl:apply-templates select="//table:table[1]"/>
<!-- Nur 1. Tabellenblatt verwenden! -->
</datasheet>
</xsl:template>
<!-- 2. Schritt -->
<xsl:template match="table:table">
<xsl:for-each select="table:table-row">
<xsl:variable name="AdjustedTableRow">
<table:table-row>
<!-- soweit ich gesehen habe benoetigst du nur die table:table-cell, beachte, dass gruppiereungen in spreadseets unterelemente erzeugen -->
<xsl:call-template name="CleanUpRow"/>
</table:table-row>
</xsl:variable>
<xsl:choose>
<xsl:when test="function-available('xalan:nodeset')">
<xsl:apply-templates select="xalan:nodeset($AdjustedTableRow)/*" mode="Adjusted"/>
</xsl:when>
<xsl:when test="function-available('exslt:node-set')">
<xsl:apply-templates select="exslt:node-set($AdjustedTableRow)/*" mode="Adjusted"/>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:template>
<!-- 2.1. Schritt -->
<xsl:template name="CleanUpRow">
<xsl:for-each select="table:table-cell">
<!-- erstelle bereinigte zeile -->
<table:table-cell>
<xsl:for-each select="@*[not(local-name() = 'number-columns-spanned')]">
<xsl:attribute name="{name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:for-each>
<!-- kopiere all unterelemente -->
<xsl:copy-of select="*"/>
</table:table-cell>
<!-- ueberpruefe ob number-columns-spanned besteht -->
<xsl:if test="@table:number-columns-spanned">
<!-- erstelle leere table-cell elemente -->
<xsl:call-template name="CreateDummyCell">
<xsl:with-param name="Number" select="@table:number-columns-spanned - 1"/>
</xsl:call-template>
</xsl:if>
</xsl:for-each>
</xsl:template>
<!-- 2.1.1. Schritt -->
<xsl:template name="CreateDummyCell">
<xsl:param name="Number"/>
<xsl:if test="$Number > 0">
<table:table-cell/>
<xsl:call-template name="CreateDummyCell">
<xsl:with-param name="Number" select="$Number - 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<!-- 3. Schritt -->
<xsl:template match="table:table-row" mode="Adjusted">
<!-- hier deine auswertung fuer die reihe -->
<!-- kleiner test -->
<row>
<xsl:for-each select="table:table-cell">
<cell number="{position()}"/>
</xsl:for-each>
</row>
</xsl:template>
</xsl:stylesheet>
Hallo,
ich brauchte etwas Zeit um zu verstehen, was da abläuft. Das Ding mit den Node-Sets ist mir noch nicht klar, da muss ich noch mal nachschlagen. Xalan funktioniert bei mir nicht, exslt schon.
Dein Muster hat auf Anhieb geklappt, leider wurde das Problem nicht gelöst. Ich zeige jetzt zuerst, wie ich es gemacht habe und weiter unten, wie meine "Quelle" aussieht.
Zuerst benenne ich die einzelnen Spalten:
<xsl:variable name="cName" select="1"/>
<xsl:variable name="cMaster" select="3"/>
<xsl:variable name="cLang" select="12"/>
[...]
Dann:
<!-- 3. Schritt -->
<xsl:template match="table:table-row" mode="Adjusted">
<record>
<Name><xsl:value-of select="table:table-cell[$cName]/text:p"/></Name>
<Master><xsl:value-of select="table:table-cell[$cMaster]/text:p"/></Master>
<Sprache><xsl:value-of select="table:table-cell[$cLang]/text:p"/></Sprache>
[...]
</record>
</xsl:template>
Soweit erstmal, für deine Antwort vielen Dank im Voraus,
Gruss Joerg
Anhang:
Hier bereinigte Auszüge aus der "content.xml" meines ods-Files. Wichtig: In Spalte 12 wird die Sprache erwartet. So mal rein gefühlsmäßig: Kann es sein, dass in der Zeile #1 die beiden table:table-cell/ den Fehler verursachen?
Beispiel-Zeile #1: -----------------------------------------------------
<table:table-row>
<table:table-cell><text:p>strKunde 1291 de</text:p></table:table-cell>
<table:table-cell/>
<table:table-cell><text:p>1291</text:p></table:table-cell>
<table:table-cell table:number-columns-repeated="3"><text:p>0</text:p></table:table-cell>
<table:table-cell><text:p>12543</text:p></table:table-cell>
<table:table-cell/>
<table:table-cell><text:p>38</text:p></table:table-cell>
<table:table-cell><text:p>3.2.1</text:p></table:table-cell>
<table:table-cell><text:p>BOTH</text:p></table:table-cell>
<table:table-cell><text:p>DE</text:p></table:table-cell> <!-- Sprache -->
<table:table-cell><text:p>Willy.Wutz@example.de</text:p></table:table-cell>
<table:table-cell><text:p>Wutz 4.12.08</text:p></table:table-cell>
<table:table-cell><text:p>3.2.1</text:p></table:table-cell>
<table:table-cell><text:p>file-321</text:p></table:table-cell>
<table:table-cell><text:p>file-321b</text:p></table:table-cell>
<table:table-cell><text:p>url-to-321b</text:p></table:table-cell>
<table:table-cell><text:p>url-to-321</text:p></table:table-cell>
<table:table-cell/>
<table:table-cell table:number-columns-repeated="1004"/>
</table:table-row>
Dies wird daraus gemacht:
~~~xml
<record>
<Name>strKunde 1291 de</Name>
<Master>1291</Master>
<ClientId>BOTH</ClientId>
<Version>DE</Version>
<Bestellwege>Willy.Wutz@example.de</Bestellwege>
<Sprache>Wutz 19.05.09</Sprache>
<KeyAccounter>321</KeyAccounter>
<Release/>
<Uploaddateiname>url-to-321</Uploaddateiname>
</record>
So sollte es sein:
~~~xml
<record>
<Name>strKunde 1291 de</Name>
<Master>1291</Master>
<ClientId>38</ClientId>
<Version>3.2.1</Version>
<Bestellwege>BOTH</Bestellwege>
<Sprache>DE</Sprache>
<KeyAccounter>Willy.Wutz@example.de</KeyAccounter>
<Release>3.2.1</Release>
<Uploaddateiname>file-321</Uploaddateiname>
</record>
Beispiel-Zeile #2: -----------------------------------------------------
</table:table-row>
<table:table-row>
<table:table-cell><text:p>strKunde 13</text:p></table:table-cell>
<table:table-cell><text:p>26.05.2008</text:p></table:table-cell>
<table:table-cell><text:p>13</text:p></table:table-cell>
<table:table-cell><text:p>2643</text:p></table:table-cell>
<table:table-cell><text:p>2939</text:p></table:table-cell>
<table:table-cell><text:p>11896</text:p></table:table-cell>
<table:table-cell><text:p>6130</text:p></table:table-cell>
<table:table-cell><text:p>15209</text:p></table:table-cell>
<table:table-cell><text:p>38</text:p></table:table-cell>
<table:table-cell><text:p>4.3.2</text:p></table:table-cell>
<table:table-cell><text:p>BOTH</text:p></table:table-cell>
<table:table-cell><text:p>DE</text:p></table:table-cell> <!-- Sprache -->
<table:table-cell><text:p>Ulla.Trulla@example.de</text:p></table:table-cell>
<table:table-cell><text:p>Trulla 05.06.08</text:p></table:table-cell>
<table:table-cell><text:p>4.3.2</text:p></table:table-cell>
<table:table-cell><text:p>file-432</text:p></table:table-cell>
<table:table-cell><text:p>file-432b</text:p></table:table-cell>
<table:table-cell><text:p>url-to-432b</text:p></table:table-cell>
<table:table-cell><text:p>url-to-432</text:p></table:table-cell>
<table:table-cell><text:p>15210</text:p></table:table-cell>
<table:table-cell table:number-columns-repeated="2"/>
<table:table-cell table:number-columns-repeated="231"/>
<table:table-cell table:number-columns-repeated="2"/>
<table:table-cell table:number-columns-repeated="768"/>
</table:table-row>
Dies wird daraus (korrekt) gemacht:
~~~xml
<record>
<Name>strKunde 13</Name>
<Master>13</Master>
<ClientId>38</ClientId>
<Version>4.3.2</Version>
<Bestellwege>NET</Bestellwege>
<Sprache>DE</Sprache>
<KeyAccounter>Ulla.Trulla@example.de</KeyAccounter>
<Release>4.3.2</Release>
<Uploaddateiname>file-432</Uploaddateiname>
</record>
Eigentlich muesste es so funktionieren:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xalan="http://xml.apache.org/xalan" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:exslt="http://exslt.org/common" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" exclude-result-prefixes="office exslt xalan table text">
<xsl:output method="xml" indent="yes" encoding="UTF-8" omit-xml-declaration="no"/>
<xsl:param name="targetURL"/>
<xsl:strip-space elements="*"/>
<!-- 1. Schritt -->
<xsl:template match="/">
<datasheet>
<xsl:apply-templates select="//table:table[1]"/>
<!-- Nur 1. Tabellenblatt verwenden! -->
</datasheet>
</xsl:template>
<!-- 2. Schritt -->
<xsl:template match="table:table">
<xsl:for-each select="table:table-row">
<xsl:variable name="AdjustedTableRow">
<table:table-row>
<!-- soweit ich gesehen habe benoetigst du nur die table:table-cell, beachte, dass gruppiereungen in spreadseets unterelemente erzeugte -->
<xsl:call-template name="CleanUpRow"/>
</table:table-row>
</xsl:variable>
<xsl:choose>
<xsl:when test="function-available('xalan:nodeset')">
<xsl:apply-templates select="xalan:nodeset($AdjustedTableRow)/*" mode="Adjusted"/>
</xsl:when>
<xsl:when test="function-available('exslt:node-set')">
<xsl:apply-templates select="exslt:node-set($AdjustedTableRow)/*" mode="Adjusted"/>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:template>
<!-- 2.1. Schritt -->
<xsl:template name="CleanUpRow">
<xsl:for-each select="table:table-cell">
<!-- erstelle bereinigte zeile -->
<table:table-cell>
<xsl:for-each select="@*[not(local-name() = 'number-columns-spanned')]">
<xsl:attribute name="{name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:for-each>
<!-- kopiere all unterelemente -->
<xsl:copy-of select="*"/>
</table:table-cell>
<!-- ueberpruefe ob columns repeated besteht -->
<xsl:if test="@table:number-columns-spanned">
<!-- erstelle leere table-cell elemente -->
<xsl:call-template name="CreateDummyCell">
<xsl:with-param name="Number" select="@table:number-columns-spanned - 1"/>
</xsl:call-template>
</xsl:if>
</xsl:for-each>
</xsl:template>
<!-- 2.1.1. Schritt -->
<xsl:template name="CreateDummyCell">
<xsl:param name="Number"/>
<xsl:if test="$Number > 0">
<table:table-cell/>
<xsl:call-template name="CreateDummyCell">
<xsl:with-param name="Number" select="$Number - 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<!-- 3. Schritt -->
<xsl:template match="table:table-row" mode="Adjusted">
<xsl:variable name="cName" select="1"/>
<xsl:variable name="cMaster" select="3"/>
<xsl:variable name="cLang" select="12"/>
<record>
<Name>
<xsl:value-of select="table:table-cell[$cName]/text:p"/>
</Name>
<Master>
<xsl:value-of select="table:table-cell[$cMaster]/text:p"/>
</Master>
<Sprache>
<xsl:value-of select="table:table-cell[$cLang]/text:p"/>
</Sprache>
</record>
</xsl:template>
</xsl:stylesheet>
Sorry, ich hatte vergessen das repeat attribute zu beruecksichtigen (siehe schritt 2.1.1). Nachfolgendes Beispiel sollte auch die zellen entsprechend duplizieren (repeated = gleiche zelle nacheinander, spanned = zelle ueber mehrere spalten aufgespannt).
Gruss, Holge r
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xalan="http://xml.apache.org/xalan" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:exslt="http://exslt.org/common" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" exclude-result-prefixes="office exslt xalan table text">
<xsl:output method="xml" indent="yes" encoding="UTF-8" omit-xml-declaration="no"/>
<xsl:param name="targetURL"/>
<xsl:strip-space elements="*"/>
<!-- 1. Schritt -->
<xsl:template match="/">
<datasheet>
<xsl:apply-templates select="//table:table[1]"/>
<!-- Nur 1. Tabellenblatt verwenden! -->
</datasheet>
</xsl:template>
<!-- 2. Schritt -->
<xsl:template match="table:table">
<xsl:for-each select="table:table-row">
<xsl:variable name="AdjustedTableRow">
<table:table-row>
<!-- soweit ich gesehen habe benoetigst du nur die table:table-cell, beachte, dass gruppiereungen in spreadseets unterelemente erzeugte -->
<xsl:call-template name="CleanUpRow"/>
</table:table-row>
</xsl:variable>
<xsl:choose>
<xsl:when test="function-available('xalan:nodeset')">
<xsl:apply-templates select="xalan:nodeset($AdjustedTableRow)/*" mode="Adjusted"/>
<xsl:copy-of select="xalan:nodeset($AdjustedTableRow)/*"/>
</xsl:when>
<xsl:when test="function-available('exslt:node-set')">
<xsl:apply-templates select="exslt:node-set($AdjustedTableRow)/*" mode="Adjusted"/>
<xsl:copy-of select="exslt:node-set($AdjustedTableRow)/*"/>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:template>
<!-- 2.1. Schritt -->
<xsl:template name="CleanUpRow">
<xsl:for-each select="table:table-cell">
<!-- erste rekursion um zellen gegebenenfalls zu duplizieren -->
<xsl:call-template name="CreateCell">
<xsl:with-param name="Repeat">
<xsl:choose>
<xsl:when test="@table:number-columns-repeated">
<xsl:value-of select="@table:number-columns-repeated"/>
</xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:with-param>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<!-- 2.1.1. Schritt - Schreibe Cell -->
<xsl:template name="CreateCell">
<xsl:param name="Repeat"/>
<xsl:if test="$Repeat > 0">
<table:table-cell>
<xsl:for-each select="@*[not(local-name() = 'number-columns-spanned' or 'number-columns-repeated')]">
<xsl:attribute name="{name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:for-each>
<!-- kopiere all unterelemente -->
<xsl:copy-of select="*"/>
</table:table-cell>
<!-- ueberpruefe ob columns spanned besteht -->
<xsl:if test="@table:number-columns-spanned">
<!-- erstelle leere table-cell elemente -->
<xsl:call-template name="CreateDummyCell">
<xsl:with-param name="Number" select="@table:number-columns-spanned - 1"/>
</xsl:call-template>
</xsl:if>
<!-- wiederhole nochmal den schreibprozess sofern die zelle repeated groesser eins -->
<xsl:call-template name="CreateCell">
<xsl:with-param name="Repeat" select="$Repeat - 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<!-- 2.1.2. Schritt -->
<xsl:template name="CreateDummyCell">
<xsl:param name="Number"/>
<xsl:if test="$Number > 0">
<table:table-cell/>
<!-- wiederhole nochmal den schreibprozess sofern die zelle spanned groesser eins -->
<xsl:call-template name="CreateDummyCell">
<xsl:with-param name="Number" select="$Number - 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<!-- 3. Schritt -->
<xsl:template match="table:table-row" mode="Adjusted">
<xsl:variable name="cName" select="1"/>
<xsl:variable name="cMaster" select="3"/>
<xsl:variable name="cLang" select="12"/>
<record>
<Name>
<xsl:value-of select="table:table-cell[$cName]/text:p"/>
</Name>
<Master>
<xsl:value-of select="table:table-cell[$cMaster]/text:p"/>
</Master>
<Sprache>
<xsl:value-of select="table:table-cell[$cLang]/text:p"/>
</Sprache>
</record>
</xsl:template>
</xsl:stylesheet>
Hallo Holge r,
danke, danke, danke. Es läuft.
Ich hatte zuerst nur deinen ersten Post beachtet, das an mein ExcelSheet angepasst - und lief nicht. Dann auch auf <table:number-columns-repeated/>
gekommen und korrigiert, dann lief es auf Anhieb.
Mit der zweiten Version gibt es Probleme, die typische Ausgabe sah so aus:
<table:table-cell>
<text:p xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"
xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0"
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0"
xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0"
xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0"
xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0"
xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0"
xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"
xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0"
xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0"
xmlns:math="http://www.w3.org/1998/Math/MathML"
xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0"
xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0"
xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0"
xmlns:ooo="http://openoffice.org/2004/office"
xmlns:ooow="http://openoffice.org/2004/writer"
xmlns:oooc="http://openoffice.org/2004/calc"
xmlns:dom="http://www.w3.org/2001/xml-events"
xmlns:xforms="http://www.w3.org/2002/xforms"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:rpt="http://openoffice.org/2005/report"
xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2"
xmlns:rdfa="http://docs.oasis-open.org/opendocument/meta/rdfa#"
xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0">NET</text:p>
</table:table-cell>
Hier bist du mit der Anweisung
<xsl:when test="function-available('exslt:node-set')">
<xsl:apply-templates select="exslt:node-set($AdjustedTableRow)/*" mode="Adjusted"/>
<xsl:copy-of select="exslt:node-set($AdjustedTableRow)/*"/>
</xsl:when>
über das Ziel hinaus geschossen :) Muss natürlich so aussehen:
<xsl:when test="function-available('exslt:node-set')">
<xsl:apply-templates select="exslt:node-set($AdjustedTableRow)/*" mode="Adjusted"/>
</xsl:when>
Ohne diesen Zusatz läuft auch die zweite Version, kann jedoch den Gegebeweis für <table:cell @table:number-columns-spanned/>
mangels Masse nicht antreten.
Jetzt muss ich nur noch verstehen, was <xsl:apply-templates select="exslt:node-set($AdjustedTableRow)/*" mode="Adjusted"/>
bewirkt, dann werde ich ruhig schlafen können ;)
Vielen Dank noch einmal, habe wieder viel gelernt.
Gruß Joerg
Ups,
<xsl:choose>
<xsl:when test="function-available('xalan:nodeset')">
<xsl:apply-templates select="xalan:nodeset($AdjustedTableRow)/*" mode="Adjusted"/>
<xsl:copy-of select="xalan:nodeset($AdjustedTableRow)/*"/>
</xsl:when>
<xsl:when test="function-available('exslt:node-set')">
<xsl:apply-templates select="exslt:node-set($AdjustedTableRow)/*" mode="Adjusted"/>
<xsl:copy-of select="exslt:node-set($AdjustedTableRow)/*"/>
</xsl:when>
</xsl:choose>
den copy-of hatte ich vergessen rauszunehmen. Das diente nur kurz dazu, um zu schauen, wie der inhalt der Variable aussieht (also ob tatsaechlich alle zellen normalisiert wurden. Hast du ja aber selber gefunden, dass diese beiden Zeilen weg muessen.
<xsl:apply-templates select="exslt:node-set($AdjustedTableRow)/*" mode="Adjusted"/>
Das bewirkt ganz einfach, dass der Kontext sich aendert. Zu Deutsch etwa so zu verstehen: Wende alle Templates, die den Titel "Adjusted" haben, auf den ausgewaehlten Knoten an. Und der Knoten ist eben anstatt eines Knotens in der XML Datei eine Knoten innerhalb der Variable. Mit XSLT2.0 braucht man etwa nicht mehr die haessliche node-set function reinzuschreiben, da unter XSLT2.0 automatisch davon ausgegangen wird, dass eine Variable auch einen Knoten darstellen kann.
Gruss, Holge r
Hallo,
so, jetzt habe ich die Abfrage meiner ganzen Nebenbedingungen optimiert (Kommentarzeilen, obsolete oder doppelte Einträge, etc.) - alles Bestens.
Eine Frage treibt mich aber noch um. Die ganzen spanned- und repeated-Zellen werden ja entsprechend vervielfältigt. Gerade wenn diese am Ende einer table-row stehen, könnten sie doch einfach eleminiert werden. Gibt es da nicht auch einen eleganten Weg? Das würde ja auch den ganzen Transformations-Prozess beschleunigen können. Braucht bei mir schon einige Sekunden (8400 Dual-Core), außerdem kann es Probleme mit der max. Zahl erlaubter Rekursionen geben:
[...]
<table:table-cell table:number-columns-repeated="2"/>
<table:table-cell table:number-columns-repeated="231"/>
<table:table-cell table:number-columns-repeated="2"/>
<table:table-cell table:number-columns-repeated="768"/>
</table:table-row>
Mit welchen Programmen arbeitest du eigentlich? Ich nehme zum Editieren Notepad++ (Texteditor) und lasse mir i.d.R. zu Testzwecken alles im Browser ausgeben. Da bekomme ich das Problem, dass bei einem Fehler im Stylesheet schon mal nur eine blanke Seite angezeigt wird, hätte aber gerne eine zutreffende Fehlermeldung.
Ansonsten danke ich dir noch vielmals und wünsche einen schönen Sonntag.
Gruß,
Joerg
Hi,
also wie gesagt, dass mit dem Nodeset war als eine moegliche Loesung gedacht. Man muesste dein komplettes Problem sehen, um abzuschaetzen was die einfachste Loesung (und performanteste) ist, hinsichtlich der letzten Zelle koenntest du etwa diese Bedingung einbinden:
<xsl:template name="CleanUpRow">
<xsl:for-each select="table:table-cell">
<xsl:choose>
<xsl:when test="position() = last()">
<xsl:copy-of select="self::*"/>
<xsl:when>
<xsl:otherwise>
<!-- erste rekursion um zellen gegebenenfalls zu duplizieren -->
<xsl:call-template name="CreateCell">
<xsl:with-param name="Repeat">
<xsl:choose>
<xsl:when test="@table:number-columns-repeated">
<xsl:value-of select="@table:number-columns-repeated"/>
</xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:with-param>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
Hinsichtlich verwendeter software: Da ich allgemein unter Linux arbeite sind die gaengigen Editor meisst vom gleichen Komfort wie notepadd++. Wenn das Problem groesser ist, kommen programme wie Oxygen oder Eclipse mit XML Tools zum Einsatz.
Auf die Parser von Browsern wuerde ich persoenlich nicht vertrauen, da diese oft "buggy" sind bzw. teilweise angepasste Erweiterungen haben.
Gruss, Holge r
Hallo Holger,
da haben sich unsere Posts wohl überschnitten :)
Die Variante mit <xsl:when test="position() = last()">
funktioniert gut und gefällt mir auch besser, weil eben keine willkürlich gesetzte Konstante benötigt wird.
Damit ist das Thema für mich eigentlich abgeschlossen - vielen, vielen Dank für deine konstruktive Unterstützung.
Gruß,
Joerg
PS: Ich bin zum Entwickeln wieder von Linux (Ubuntu / Mint) nach Vista umgezogen - Windows fühlt sich IMO einfach angenehmer an. Wenn ich in Windows die rechte Maustaste betätige, dann passiert da richtig was - bei Linux wird die Taste auch erkannt ;) Nein, soll jetzt kein Flame-War werden, ich sehe das ganz nüchtern. KDE und Gnome brauchen einfach noch etwas bis sie soweit sind, wie Windows jetzt.
Meine Sachen teste ich immer in Firefox und Opera gleichzeitig, zusammen ergibt es ein halbwegs ordentliches Bild dessen, was dabei herauskommen soll. Alle anderen Browser, IE8 inklusive, kann mit hinsichtlich XSLT völlig knicken :( I.d.R. muss ich aber nur xml-Daten visualisieren, dafür reicht das dann.
Hallo,
die im vorigen Post angesprochenen "repeated"-Felder am Ende einer Zeile stellen teilweise ein Problem dar, weil diese eine "Laufzeitfehler" verursachen: Zu viele Rekursionen.
Habe daraufhin ein paar kleine Änderungen vorgenommen:
xsl:output method="xml" indent="yes" encoding="UTF-8" omit-xml-declaration="no"/>
<xsl:variable name="MaxRecursion" select="20"/>
[...]
<!-- 2.1. Schritt -->
<xsl:template name="CleanUpRow">
[...]
<xsl:choose>
<xsl:when test="@table:number-columns-repeated and (@table:number-columns-repeated < $MaxRecursion)">
<xsl:value-of select="@table:number-columns-repeated"/>
</xsl:when>
[...]
</xsl:template>
<!-- 2.1.1. Schritt - Schreibe Cell -->
<xsl:template name="CreateCell">
<xsl:param name="Repeat"/>
<xsl:if test="$Repeat > 0">
<table:table-cell>
[...]
</table:table-cell>
<!-- ueberpruefe ob columns spanned besteht -->
<xsl:if test="@table:number-columns-spanned and (@table:number-columns-spanned < $MaxRecursion)">
<!-- erstelle leere table-cell elemente -->
[...]
</xsl:if>
[...]
</xsl:if>
</xsl:template>
Das hat den angenehmen Nebeneffekt, dass die Transformation wesentlich schneller läuft :) Darüber hinaus kann davon ausgegangen werden, dass unter normalen Umständen innerhalb der ersten 30 Spalten eines ExcelSheets keine 20 Leerzeilen vorkommen - wenn doch, dann kann die ganze Zeile gleich ignoiert werden, weil leer.
Wie gesagt, gäbe es eine elegante Möglichkeit, diese Zellen am Ende einer Zeile zu eleminieren, wäre das schon eine schöne Sache.
Gruß,
Joerg
Hi Holger,
danke für die Antwort.
<table:table-cell table:number-columns-repeated="3"/>
<table:covered-table-cell table:number-columns-repeated="2"/>
<table:table-cell table:number-columns-repeated="2"/>
table:covered-table-cell/
Dies konnte ich nicht beobachten:
[...]
</table:table-cell>
<table:table-cell table:number-columns-repeated="3">
<text:p>0</text:p>
</table:table-cell>
[...]
Mein ExcelSheet ist von der Anlage her schon einige Jahre alt und bearbeite das in Calc weiter.
Die Idee mit exslt:node-set hat mich sofort gepackt, bringe das aber nicht richtig zustande. Und ob mein Calc mit xmlns:xalan klar kommt, hmm...
Hier mein (abgespecktes) StyleSheet:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"
xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0"
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0"
exclude-result-prefixes="office table text">
<xsl:output method = "xml" indent = "yes" encoding = "UTF-8" omit-xml-declaration = "yes"/>
<xsl:param name="targetURL"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<datasheet>
<xsl:apply-templates select="//table:table[1]"/> <!-- Nur 1. Tabellenblatt verwenden! -->
</datasheet>
</xsl:template>
<xsl:template match="table:table">
<xsl:variable name="col-repeater" select="4"/><!-- Spalte mit Repeated-Attribut -->
<xsl:variable name="col-master" select="3"/> <!-- Spalte mit Master -->
<xsl:variable name="col-lang" select="12"/><!-- Spalte mit Sprachkuerzel -->
<xsl:for-each select="table:table-row">
<!-- einige Spalten haben einen 'Repeater'-Wert -->
<xsl:variable name="repeater">
<xsl:choose>
<xsl:when test="table:table-cell[$col-repeater]/@table:number-columns-repeated">
<xsl:value-of select="table:table-cell[$col-repeater]/@table:number-columns-repeated"/>
</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!-- Ermittlen, wo sich die Sprache befindet: -->
<xsl:variable name="repeater_lang">
<xsl:choose>
<xsl:when test="$repeater>0">
<xsl:value-of select="$col-lang - $repeater + 1"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$col-lang"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<-- Sprache speichern -->
<xsl:variable name="land" select="table:table-cell[position()=$repeater_lang]/text:p"/>
<!-- Spalten quasi konstant definieren -->
<xsl:variable name="colclientid" select="$repeater_lang - 3"/>
<xsl:variable name="colversion" select="$repeater_lang - 2"/>
<xsl:variable name="colorder" select="$repeater_lang - 1"/>
<xsl:variable name="colrelease" select="$repeater_lang + 2"/>
<!-- selektiere alle Eintraege mit gueltigem Land -->
<xsl:if test="$land">
<record>
<Name><xsl:value-of select="table:table-cell/text:p"/></Name>
<Referenz-Spalte><xsl:value-of select="$repeater_lang"/></Referenz-Spalte>
<Korrektur><xsl:value-of select="$repeater"/></Korrektur>
<Version><xsl:value-of select="table:table-cell[$colversion]/text:p"/></Version>
<Sprache><xsl:value-of select="$land"/></Sprache>
<xsl:if test="table:table-cell[$colrelease]/text:p">
<xsl:if test="not (table:table-cell[$colversion]/text:p = table:table-cell[$colrelease]/text:p)" >
<Release><xsl:value-of select="table:table-cell[$colrelease]/text:p"/></Release>
</xsl:if>
</xsl:if>
<xsl:if test="table:table-cell[$colclientid]/text:p">
<ClientId><xsl:value-of select="table:table-cell[$colclientid]/text:p"/></ClientId>
</xsl:if>
</record>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>