Groß-/Kleinschreibungs-Problem, Liste von Anfangsbuchstaben...
MudGuard
- xsl
Hi,
Ich hätte gerne eine Liste der Anfangsbuchstaben (case-insensitive) eines Attributwertes (als Links für ne interne Navigation).
Das funktioniert auch einigermaßen.
Solange ein Anfangsbuchstabe nur in Kleinschreibung oder nur in Großschreibung vorkommt (A und d in untenstehendem XML-Ausschnitt), wird auch nur 1 Link erzeugt.
Kommen aber beide Schreibweisen vor (B,C,F im XML-Ausschnitt), werden 2 Links erzeugt.
Ich erhalte also statt der erwarteten Liste
A B C D F
leider die Liste
A B B C C D F F
Eigentlich hatte ich gedacht, durch das translate-Zeug die Groß-/Klein-Schreibung eliminiert zu haben.
Ach ja - Umlaute kommen in den Namen nicht vor, also reicht a-z.
Den mode benutze ich, da ich die Elemente an mehreren Stellen in verschiedener Weise ausgeben will (eben einmal als Navigation und einmal mit voller Information [die restlichen Attribute hab ich hier im Beispiel entfernt, da für das Problem irrelevant]).
encoding ist in beiden Dateien iso-8859-1 (daher sind die Umlaute in den Kommentaren kein Problem).
Der Ausschnitt aus der XSL:
<!-- das erste Template, das sorgt nur für den Aufruf mit sortierten Namen -->
<xsl:template match="catters" mode="shortnavi">
<p class="subnavi">
<xsl:apply-templates select="catter" mode="shortnavi">
<xsl:sort select="@names"/>
</xsl:apply-templates >
</p>
</xsl:template>
<!-- das Template mit dem Problem -->
<xsl:template match="catter" mode="shortnavi">
<xsl:variable name="current"><xsl:value-of select="translate(substring(@names,1,1),'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/></xsl:variable>
<!-- für das erste Element wird auf jeden Fall ein Link erzeugt (preceding-sibling würde hier fehlschlagen) -->
<xsl:if test="position()=1">
<a href="#label{$current}"><xsl:value-of select="$current"/></a>
</xsl:if>
<!-- für die folgenden Elemente sollte es nur dann ein Link erzeugt werden, wenn sich der 1. Buchstabe vom vorherigen 1. Buchstabe unterscheidet -->
<xsl:if test="position()>1">
<xsl:variable name="previous"><xsl:value-of select="translate(substring(preceding-sibling::catter[1]/@names,1,1),'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')" /></xsl:variable>
<xsl:if test="$previous != $current">
<a href="#label{$current}"><xsl:value-of select="$current"/></a>
</xsl:if>
</xsl:if>
</xsl:template>
Ein Ausschnitt aus der XML:
<catters>
<catter names="A_aaaa" />
<catter names="A_bbbb" />
<catter names="A_cccc" />
<catter names="B_dddd" />
<catter names="B_eeee" />
<catter names="b_ffff" />
<catter names="C_gggg" />
<catter names="C_hhhh" />
<catter names="c_iiii" />
<catter names="d_jjjj" />
<catter names="d_kkkk" />
<catter names="f_llll" />
<catter names="F_mmmm" />
<catter names="f_nnnn" />
</catters>
Wo liegt mein Denkfehler?
cu,
Andreas
Hi,
Problem eingekreist:
<xsl:variable name="previous"><xsl:value-of select="translate(substring(preceding-sibling::catter[1]/@names,1,1),'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')" /></xsl:variable>
preceding-sibling greift auf die Reihenfolge der Elemente im XML-File zu, nicht auf die sortierte Reihenfolge.
Und die Reihenfolge ist (nicht wie im Beispiel), sondern so, daß zuerst alle Elemente, deren names-Attribut mit Großbuchstaben beginnt, sortiert nach diesem Großbuchstaben, vorkommen und dann alle Elemente, deren names-Attribut mit Kleinbuchstaben beginnt, sortiert nach diesem Kleinbuchstaben.
Also
<catters>
<catter names="A_aaaa" />
<catter names="A_bbbb" />
<catter names="A_cccc" />
<catter names="B_dddd" />
<catter names="B_eeee" />
<catter names="C_gggg" />
<catter names="C_hhhh" />
<catter names="F_mmmm" />
<catter names="b_ffff" />
<catter names="c_iiii" />
<catter names="d_jjjj" />
<catter names="d_kkkk" />
<catter names="f_llll" />
<catter names="f_nnnn" />
</catters>
Da muß ich eine kleine Änderung in dem Programm vornehmen, daß das XML erzeugt - damit dort case-insensitiv sortiert wird.
Oder im XSL probieren, mit Position()-1 auf den Vorgänger zuzugreifen, statt mit preceding-sibling
cu,
Andreas
Hi,
Mit
<xsl:template match="catter" mode="shortnavi">
<xsl:variable name="current" select="translate(substring(@names,1,1),'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/>
<xsl:variable name="thispos" select="position()-1"/>
<xsl:if test="$thispos=0">
<a href="#label{$current}"><xsl:value-of select="$current"/></a>
</xsl:if>
<xsl:if test="$thispos>0">
<xsl:variable name="previous" select="translate(substring(../catter[position()=$thispos]/@names,1,1),'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')" />
<xsl:if test="$previous != $current">
<a href="#label{$current}"><xsl:value-of select="$current"/></a>
</xsl:if>
</xsl:if>
</xsl:template>
klappt es - auch wenn die Elemente total unsortiert im XML vorliegen (die Sortierung wird ja im aufrufenden Template vorgenommen):
<xsl:apply-templates select="catter" mode="shortnavi">
<xsl:sort select="@names"/>
</xsl:apply-templates >
Für die nächste XSL- bzw. XPath-Version wünsch ich mir ein paar zusätzliche String-Funktionen!
toUpper, toLower (die translate-Krücke funktioniert zwar, ist aber nicht gerade hübsch) sowie replace (rekursive templates nur für sowas find ich lästig) wären mal die wichtigsten Kandidaten...
cu,
Andreas
Hallo Andreas,
Für die nächste XSL- bzw. XPath-Version wünsch ich mir ein paar zusätzliche String-Funktionen!
toUpper, toLower
fn:upper-case()
fn:lower-case()
»»(die translate-Krücke funktioniert zwar, ist aber nicht gerade hübsch) sowie replace
fn:replace($1.arg, $regExp, $2.arg)
Grüße
Thomas
Hi,
Für die nächste XSL- bzw. XPath-Version wünsch ich mir ein paar zusätzliche String-Funktionen!
toUpper, toLowerfn:upper-case()
fn:lower-case()
Error occurred while compiling stylesheet 'catters.xsl'.
Code: 0x80004005
Reference to undeclared namespace prefix: 'fn'.
-->fn:upper-case(substring(@names,1,1))<--
Nach Erweiterung von
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
zu
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:fn="http://www.w3.org/2003/11/xpath-functions">
(den Namespace hab ich von http://www.w3.org/TR/xpath20/#id-basics)
sagt msxsl:
Error occurred while executing stylesheet 'catters.xsl'.
Code: 0x80004005
Namespace 'http://www.w3.org/2003/11/xpath-functions' does not contain any functions.
cu,
Andreas
Hallo,
sagt msxsl:
^^^^^^
Und was sagt saxon 7.x ? ;-)
Grüße
Thomas
Hi,
sagt msxsl:
^^^^^^
Und was sagt saxon 7.x ? ;-)
Keine Ahnung, hab ich noch nicht probiert.
Ist doch aber ein Java-Tool?!
Und damit vergleichsweise langsam...
Ich hab regelmäßig ein paar Hundert Transformationen durchzuführen, mit msxsl geht das rasend schnell - schneller als _ein_ "HelloWorld.class" mit der Java 1.4.2 auszuführen...
cu,
Andreas
Hi,
sagt msxsl:
^^^^^^
Und was sagt saxon 7.x ? ;-)Keine Ahnung, hab ich noch nicht probiert.
Ist doch aber ein Java-Tool?!
Und damit vergleichsweise langsam...
Jetzt hab ichs probiert (V7.5) - es ist wie befürchtet ein Java-Programm (mit dem Nachteil der langen VM-Startzeit).
Aber selbst wenn ich die in Kauf nähme:
Error at xsl:variable on line 76 of file:/D:/xxx/yyy/catters.xsl:
Unknown function: fn:upper-case
Transformation failed: Run-time errors were reported
Da bleib ich doch lieber beim wesentlich schnelleren msxsl, da ich ja keinerlei Vorteil aus Saxon habe.
cu,
Andreas
Hallo Andreas,
Aber selbst wenn ich die in Kauf nähme:
Error at xsl:variable on line 76 of file:/D:/xxx/yyy/catters.xsl:
Unknown function: fn:upper-case
Transformation failed: Run-time errors were reported
OK, mein Fehler. Natürlich muss du beim XSL 2.0 die Funktionen wie beim XSLt 1.0 angeben:
<autor>
<vorname>AXEL</vorname>
<nachname>bornträger</nachname>
</autor>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
...
<xsl:value-of select="lower-case(vorname)" /> <xsl:value-of select="upper-case(nachname)" />
HTML:
<th>axel BORNTRÄGER</th>
Funktioniert bei mir einwandfrei. ;-)
Grüße
Thomas
Hi,
OK, mein Fehler. Natürlich muss du beim XSL 2.0 die Funktionen wie beim XSLt 1.0 angeben:
Ah - ich hab's mit ner name-space Angabe in einem XSL 1.0 probiert.
Ja, jetzt macht der Saxon das richtig.
Aber die Startup-Zeit der Java-VM (für eine Transformation) ist länger als die (derzeit) 845 Transformationen mit msxsl...
cu,
Andreas
Hallo,
Aber die Startup-Zeit der Java-VM (für eine Transformation) ist länger als die (derzeit) 845 Transformationen mit msxsl...
Hmm... unter Win solltest du so oder so einen JVM installiert haben, bei anderen OS ist das ja auch nicht schlecht.
Dann muss nur noch saxon.jar in die CLASSPATH.
Wie auch immer, wenn die andere Lösung für dich besser ist, dann ist sie besser ;-)
Grüße
Thomas
Hi,
Hmm... unter Win solltest du so oder so einen JVM installiert haben, bei anderen OS ist das ja auch nicht schlecht.
Klar hab ich ne VM installiert.
Aber bis die gestartet ist, braucht es bei mir ca. 15 Sekunden.
cu,
Andreas