Funktion zum Testen, ob id in idrefs ist, gesucht
MudGuard
- xsl
Hi,
Ich verwende in einem xml für einige Elemente (namens event) das id-Attribut (ok, nix ungewöhnliches hier).
Für andere Elemente (namens participant) benutze ich ein Attribut vom Typ IDREFS, das dann eine Liste von id-Werten enthält.
Damit hab ich eine 1:n-Beziehung vom participant zu den events hergestellt.
Bei der XSL-Transformation will ich jetzt zu einem der Elemente mit IDREFS-Attribut alle referenzierten Elemente auflisten.
Bisher mach ich das so:
<data>
<events>
<event id="e01" name="bla"/>
<event id="e02" name="blubb"/>
<event id="e03" name="laber"/>
</events>
<participants>
<participant events="e01,e03" name="Alpha"/>
<participant events="e02" name="Bravo"/>
<participant events="e02,e03" name="Charlie"/>
</participants>
</data>
<xsl:template match="participant">
<xsl:variable name="eventids" select="@eventrefs"/>
<xsl:apply-templates select="//event[contains($eventids, @id)]"/>
</xsl:template>
event sind die Elemente, die die id-Attribute enthalten.
eventrefs ist das IDREFS Attribut.
3 Fragen dazu:
1.) geht das auch ohne Variable eventids?
Wenn ich <xsl:apply-templates select="//event[contains(./@eventrefs, @id)]"/> schreibe, bezieht sich das ./eventrefs doch auf das event, nicht auf den participant - oder täusche ich mich da?
2.) Gibt es eine fertige Funktion, die mir die Anzahl der ids in einem idref liefert?
3.) die wichtigste Frage: im Moment hab ich das so gelöst, daß die id-Werte mit einem Buchstaben beginnen und mit einer festen Anzahl von Ziffern aufhören.
Damit funktioniert die contains-Abfrage. Ok, damit kann ich in diesem Fall leben.
Aber: gibt es eine Funktion, die mir für beliebige id-Werte und ein idref-Attribut true oder false zurückliefert, je nachdem, ob die id nicht nur im Sinne der String-contains, sondern als echte ID im idrefs steckt?
Denn wenn ich die id-Werte ohne führende Nullen machen würde, also e1, e2, ... e10, e11, ... würde ja e1 mit contains auch gefunden werden, wenn das idrefs-Attribut "e10,e20" wäre.
Falls das eine Rolle spielt: ich verwende msxml 4.0 SDK und das msxsl-Commandline-Tool (gibt es was besseres nicht-Java-basiertes? Java braucht bei mir immer viel zu lange, um die VM zu starten)
Vielen Dank im Voraus
cu,
Andreas
Hallom Andreas,
Für andere Elemente (namens participant) benutze ich ein Attribut vom Typ IDREFS, das dann eine Liste von id-Werten enthält.
Ein als IDREFS deklariertes Attribut muss eine duch _Leerzeichen_ getrennte Liste von IDs enthalten. So ist dein events-Attribute kein gültiger IDREFS Typ.
Bei der XSL-Transformation will ich jetzt zu einem der Elemente mit IDREFS-Attribut alle referenzierten Elemente auflisten.
3 Fragen dazu:
1.) geht das auch ohne Variable eventids?
Ja, aber nur wenn du auch eine DTD hast wo zumindest das Attribut id als Typ ID deklariert ist (events muss du dann nicht unbedingt als Typ IDREFS deklarieren).
Dann geht es z.B. so:
<xsl:template match="participant">
<xsl:apply-templates select="id(@events)" />
</xsl:template>
Wenn ich <xsl:apply-templates select="//event[contains(./@eventrefs, @id)]"/> schreibe, bezieht sich das ./eventrefs doch auf das event, nicht auf den participant - oder täusche ich mich da?
Ja, Bezug wird hier auf das <event>-Element selbst genommen.
2.) Gibt es eine fertige Funktion, die mir die Anzahl der ids in einem idref liefert?
Ja. id() behandelt eine durch Leerzeichen getrennte Liste von IDs auf einmal. Also sie findet Knoten deren ID in der Liste vorkommt.
3.) die wichtigste Frage: im Moment hab ich das so gelöst, daß die id-Werte mit einem Buchstaben beginnen und mit einer festen Anzahl von Ziffern aufhören.
Damit funktioniert die contains-Abfrage. Ok, damit kann ich in diesem Fall leben.
Aber: gibt es eine Funktion, die mir für beliebige id-Werte und ein idref-Attribut true oder false zurückliefert, je nachdem, ob die id nicht nur im Sinne der String-contains, sondern als echte ID im idrefs steckt?
Siehe oben.
Voraussetzung ist aber wirkich, dass dein XML valide ist (sprich DTD!)
Denn wenn ich die id-Werte ohne führende Nullen machen würde, also e1, e2, ... e10, e11, ... würde ja e1 mit contains auch gefunden werden, wenn das idrefs-Attribut "e10,e20" wäre.
Ja.
Falls das eine Rolle spielt: ich verwende msxml 4.0 SDK und das msxsl-Commandline-Tool (gibt es was besseres nicht-Java-basiertes? Java braucht bei mir immer viel zu lange, um die VM zu starten)
Vielleicht http://www.elcel.com/products/xmlvalid.html oder http://xml.apache.org/xerces-c/index.html
sonst http://www.xmlsoftware.com/parsers.html
Grüße
Thomas
Hi,
Hallom Andreas,
Für andere Elemente (namens participant) benutze ich ein Attribut vom Typ IDREFS, das dann eine Liste von id-Werten enthält.
Ein als IDREFS deklariertes Attribut muss eine duch _Leerzeichen_ getrennte Liste von IDs enthalten. So ist dein events-Attribute kein gültiger IDREFS Typ.
Ups, keine DTD ==> keine Validierung.
Frag mich nicht, wo ich das mit den Kommata herhab.
Wird umgehend korrigiert.
Hehe - brauch ich gar nicht - ist mir nur hier im Beispiel-XML passiert.
Bei der XSL-Transformation will ich jetzt zu einem der Elemente mit IDREFS-Attribut alle referenzierten Elemente auflisten.
3 Fragen dazu:
1.) geht das auch ohne Variable eventids?
Ja, aber nur wenn du auch eine DTD hast wo zumindest das Attribut id als Typ ID deklariert ist (events muss du dann nicht unbedingt als Typ IDREFS deklarieren).
Dann geht es z.B. so:
<xsl:template match="participant">
<xsl:apply-templates select="id(@events)" />
</xsl:template>
Ok, habs grad mal so ohne DTD ausprobiert, da wird nix selektiert. Werd ich halt noch ne DTD aufsetzen - hat ja auch noch andere Vorteile (Validierbarkeit)
Wenn ich <xsl:apply-templates select="//event[contains(./@eventrefs, @id)]"/> schreibe, bezieht sich das ./eventrefs doch auf das event, nicht auf den participant - oder täusche ich mich da?
Ja, Bezug wird hier auf das <event>-Element selbst genommen.
Ok, mit der id()-Konstruktion ist das ja eh ganz weg.
2.) Gibt es eine fertige Funktion, die mir die Anzahl der ids in einem idref liefert?
Ja. id() behandelt eine durch Leerzeichen getrennte Liste von IDs auf einmal. Also sie findet Knoten deren ID in der Liste vorkommt.
ok, darauf dann count() angewendet, sollte das gewünschte liefern.
3.) die wichtigste Frage: im Moment hab ich das so gelöst, daß die id-Werte mit einem Buchstaben beginnen und mit einer festen Anzahl von Ziffern aufhören.
Aber: gibt es eine Funktion, die mir für beliebige id-Werte und ein idref-Attribut true oder false zurückliefert, je nachdem, ob die id nicht nur im Sinne der String-contains, sondern als echte ID im idrefs steckt?
Siehe oben.
Voraussetzung ist aber wirkich, dass dein XML valide ist (sprich DTD!)
Ok - ich hab's verstanden.
Falls das eine Rolle spielt: ich verwende msxml 4.0 SDK und das msxsl-Commandline-Tool (gibt es was besseres nicht-Java-basiertes? Java braucht bei mir immer viel zu lange, um die VM zu starten)
Vielleicht http://www.elcel.com/products/xmlvalid.html oder http://xml.apache.org/xerces-c/index.html
sonst http://www.xmlsoftware.com/parsers.html
Werd ich mir mal angucken.
Ein dickes Danke mal wieder an Dich!
cu,
Andreas
Hallo,
Falls das eine Rolle spielt: ich verwende msxml 4.0 SDK und das msxsl-Commandline-Tool (gibt es was besseres nicht-Java-basiertes? Java braucht bei mir immer viel zu lange, um die VM zu starten)
Mein bevorzugtes Kommandozeilentool fuer XML ist XMLStarlet: http://xmlstar.sourceforge.net.
Aufrufbeispiele:
XML-Validierung gegen eine DTD:
xml val -d bla.dtd blub.xml
XML-Validierung gegen ein Schema:
xml val -s bla.xsd blub.xml
XSL-Transformation:
xml tr bla.xsl blub.xml > blablub.xyz
Anzeige von Hilfeinformationen:
xml val --help
xml tr --help
MfG, Thomas
Hi,
Mein bevorzugtes Kommandozeilentool fuer XML ist XMLStarlet: http://xmlstar.sourceforge.net.
Danke, werd ich mir auch angucken.
(hatte ich vorhin nur die Vorschau generiert? Ich war mir eigentlich sicher, das vorhin schon mal geschrieben zu haben...)
cu,
Andreas
Hallo,
Danke, werd ich mir auch angucken.
(hatte ich vorhin nur die Vorschau generiert? Ich war mir eigentlich sicher, das vorhin schon mal geschrieben zu haben...)
Dann muss ich deine Vorschau gelesen haben ;-)
Weiss auch nicht ob was passiert ist, aber ich habe schon deine Antwort gelesen. Gelöscht ist die Posting nicht ... hmmm.
Grüße
Thomas
Hi,
(hatte ich vorhin nur die Vorschau generiert? Ich war mir eigentlich sicher, das vorhin schon mal geschrieben zu haben...)
Dann muss ich deine Vorschau gelesen haben ;-)
Haben wir beide das selbe schlechte Kraut geraucht?
Dabei bin ich doch absoluter Nichtraucher...
Und wehe, Du liest nochmal meine Vorschau! Warte wenigstens, bis ich fertig bin mit dem Posting! ;-)
Weiss auch nicht ob was passiert ist, aber ich habe schon deine Antwort gelesen. Gelöscht ist die Posting nicht ... hmmm.
Ist vielleicht mal wieder das Forum gecrasht?
Egal...
cu,
Andreas
Hi,
ok, funktioniert jetzt mit:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE data [
<!ELEMENT data (events,participants)>
<!ELEMENT events (event)+>
<!ELEMENT event EMPTY>
<!ATTLIST event
id ID #REQUIRED
name CDATA #REQUIRED >
<!ELEMENT participant (participant)+>
<!ELEMENT participant EMPTY>
<!ATTLIST participant
name CDATA #REQUIRED
events IDREFS #REQUIRED >
]>
<data>
<events>
<event id="e01" name="bla"/>
<event id="e02" name="blubb"/>
<event id="e03" name="laber"/>
</events>
<participants>
<participant events="e01 e03" name="Alpha"/>
<participant events="e02" name="Bravo"/>
<participant events="e02 e03" name="Charlie"/>
</participants>
</data>
und diesem XSL-Ausschnitt:
<xsl:template match="participant" mode="byparticipant">
<h3><xsl:value-of select="@name"/></h3>
<xsl:apply-templates select="id(@events)" mode="byparticipant"/>
</xsl:template>
<xsl:template match="event" mode="byparticipant">
<xsl:if test="position() > 1">, </xsl:if>
<xsl:value-of select="@name"/>
</xsl:template>
Damit krieg ich jetzt zu jedem participant alle events, bei denen er dabei war.
An anderer Stelle will ich aber die Auflistung genau andersrum:
zu jedem Event die Liste aller participants.
Klar, ich könnte jetzt jedem participant ne id verpassen und im event wieder ein IDREFS-Attribut und das ganze symmetrisch aufbauen.
Aber: das erzeugt Redundanz - weil die Information, welche participants bei welchen events waren, steckt ja schon im XML drin.
Redundanz will ich vermeiden, denn das ist ein erhöhter Pflegeaufwand, und das ganze wird fehleranfälliger - wenn z.B. bei einem participant zwar ein event eingetragen wird, der participant aber nicht beim event eingetragen wird.
Gibt es eine Umkehrfunktion zu id()?
Also eine Funktion, die alle Elemente liefert, die in einem per IDREFS deklarierten Attribut eine gegebene ID haben?
Ich hab nichts gefunden, weder in XSL noch in XPath. Hab ich was übersehen?
Im Moment nutze ich wieder den contains-"Hack" - aber der funktioniert ja nur, wenn keine ID in einer anderen ID enthalten ist (also bei i1 und i11 nicht):
<xsl:template match="event" mode="byevent">
<h3><xsl:value-of select="@name"/></h3>
<ul>
<xsl:variable name="eventid" select="@id"/>
<xsl:apply-templates select="//participants/participant[contains(@events, $eventid)]" mode="byevent"/>
</ul>
</xsl:template>
<xsl:template match="participant" mode="byevent">
<li><xsl:value-of select="@name"/></li>
</xsl:template>
Gibt es eine _sauberere_ Lösung dafür? Also eine, die mit beliebigen id-Werten funktioniert?
cu,
Andreas
Hallo Andreas,
Gibt es eine Umkehrfunktion zu id()?
Also eine Funktion, die alle Elemente liefert, die in einem per IDREFS deklarierten Attribut eine gegebene ID haben?
Indirekt. ;-)
Gibt es eine _sauberere_ Lösung dafür? Also eine, die mit beliebigen id-Werten funktioniert?
Ja:
<xsl:key name="patizip2event" match="participant" use="id(@events)/@id" />
....
<xsl:template match="event">
<h3><xsl:value-of select="@name"/></h3>
<ul>
<xsl:apply-templates select="key('patizip2event', @id)" mode="byevent"/>
</ul>
</xsl:template>
<xsl:template match="participant" mode="byevent">
<li><xsl:value-of select="@name"/></li>
</xsl:template>
----------------------------
liefert :
<h3>bla</h3>
<ul>
<li>Alpha</li>
</ul>
<h3>blubb</h3>
<ul>
<li>Bravo</li>
<li>Charlie</li>
</ul>
<h3>laber</h3>
<ul>
<li>Alpha</li>
<li>Charlie</li>
</ul>
Grüße
Thomas
Hi,
Also eine Funktion, die alle Elemente liefert, die in einem per IDREFS deklarierten Attribut eine gegebene ID haben?
Gibt es eine _sauberere_ Lösung dafür? Also eine, die mit beliebigen id-Werten funktioniert?
<xsl:key name="patizip2event" match="participant" use="id(@events)/@id" />
Ich hätte das eigentlich wissen sollen, daß es mit einem key geht.
Nochmal Nachfrage, ob ich das jetzt endlich richtig verstanden habe:
Das key-Element liefert mir sowas (natürlich nicht als Strings, sondern als Nodeset, aber zur Visualisierung einfach mal die Elemente hingeschrieben):
e01 -> <participant events="e01 e03" name="Alpha"/>
e02 -> <participant events="e02" name="Bravo"/>,<participant events="e02 e03" name="Charlie"/>
e03 -> <participant events="e01 e03" name="Alpha"/>,<participant events="e02 e03" name="Charlie"/>
also zu jedem key (hier die ids der Events) eine Liste der participants (bzw. der in match aufgelisteten Knoten), auf die das use-Attribut zutrifft
<xsl:apply-templates select="key('patizip2event', @id)" mode="byevent"/>
und hier wird mit der key-Funktion die entsprechende Liste zurückgegeben.
Ich danke Dir wieder mal, Thomas!
cu,
Andreas
Hallo Andreas,
<xsl:key name="patizip2event" match="participant" use="id(@events)/@id" />
Ich hätte das eigentlich wissen sollen, daß es mit einem key geht.
id() und key() sind sehr nützlich wenn man Knoten gruppieren möchte.
Nochmal Nachfrage, ob ich das jetzt endlich richtig verstanden habe:
Das key-Element liefert mir sowas (natürlich nicht als Strings, sondern als Nodeset, aber zur Visualisierung einfach mal die Elemente hingeschrieben):
*hehe* ich brauche es auch immer wieder zu "sehen" was key() macht.
also zu jedem key (hier die ids der Events) eine Liste der participants (bzw. der in match aufgelisteten Knoten), auf die das use-Attribut zutrifft
Genau.
Wenn du es testest:
<xsl:template match="event">
<h3><xsl:value-of select="@name"/></h3>
<xsl:copy-of select="key('patizip2event', @id)" />
Kommt genau das zurück:
<h3>bla</h3>
<participant events="e01 e03" name="Alpha"></participant>
<h3>blubb</h3>
<participant events="e02" name="Bravo"></participant>
<participant events="e02 e03" name="Charlie"></participant>
<h3>laber</h3>
<participant events="e01 e03" name="Alpha"></participant>
<participant events="e02 e03" name="Charlie"></participant>
<xsl:apply-templates select="key('patizip2event', @id)" mode="byevent"/>
und hier wird mit der key-Funktion die entsprechende Liste zurückgegeben.
In xslt 2.0 wäre das dann einfacher:
<xsl:apply-templates select="idref(@id)" mode="byevent"/>
Aber die Implementierung in Saxon 7.9 scheint noch nicht wirklich zu funktionieren.
Grüße
Thomas
Hi,
*hehe* ich brauche es auch immer wieder zu "sehen" was key() macht.
;-)
In xslt 2.0 wäre das dann einfacher:
<xsl:apply-templates select="idref(@id)" mode="byevent"/>
Aber die Implementierung in Saxon 7.9 scheint noch nicht wirklich zu funktionieren.
Wie schon mal erwähnt: ich hab immer mal wieder einige 100 Transformationen vorzunehmen.
Mit msxsl dauert das für ALLE Transformationen nur unwesentlich länger als mit dem JAVA-Teil Saxon. Aus irgendeinem Grund dauert es bei mir ca. 30 Sekunden, bis die JAVA-VM (1.4.2 genauso wie auch 1.4.0 oder 1.3.1) sich bereiterklärt, mit der Abarbeitung eines Java-Programms zu beginnen.
Und für mehrere 100 Transformationen jeweils 30 Sekunden Wartezeit zu haben, summiert sich...
Daher bleib ich vorerst bei msxsl - und das kann derzeit auch nur xslt 1.0
cu,
Andreas
Hallo,
In xslt 2.0 wäre das dann einfacher:
<xsl:apply-templates select="idref(@id)" mode="byevent"/>
Aber die Implementierung in Saxon 7.9 scheint noch nicht wirklich zu funktionieren.
Ich habe jetzt mal nachgefragt und ausprobiert, es funktioniert so:
<xsl:apply-templates select="idref(@id)/.." mode="byevent"/>
Dabei verstehe ich nicht wirklich, warum hier nicht die selbe Funktionweise vom W3C vorgesehen ist, wie beim id(). (Bzw es ist, aber id() funktioniert trozdem dann anders) Aber vielleicht ändert sich die WD noch.
Wie schon mal erwähnt: ich hab immer mal wieder einige 100 Transformationen vorzunehmen.
Mit msxsl dauert das für ALLE Transformationen nur unwesentlich länger als mit dem JAVA-Teil Saxon. Aus irgendeinem Grund dauert es bei mir ca. 30 Sekunden, bis die JAVA-VM (1.4.2 genauso wie auch 1.4.0 oder 1.3.1) sich bereiterklärt, mit der Abarbeitung eines Java-Programms zu beginnen.
30 Sek. sind viel. Liegt vielleicht am Rechner? Da M$ mit winXP keine eigene VM mehr ausliefert, muss ich auch auf die "normale" Java VM zurückgreifen (1.4.2_03) aber es dauert bei mir trotzdem nur sehr kurz:
------------------------------
Dein Beispiel XML:
SAXON 7.9 from Saxonica
Java version 1.4.2_03
Processing file:/data.xml using associated stylesheet
Prepared associated stylesheet file:/data.xsl
Building tree for file:/data.xml using class net.sf.sa
xon.tinytree.TinyBuilder
Tree built in 16 milliseconds
Tree size: 22 nodes, 46 characters, 12 attributes
-------------------------------
MSXSL von der commandline:
Microsoft (R) XSLT Processor Version 4.0
Source document load time: 7.831 milliseconds
Stylesheet document load time: .703 milliseconds
Stylesheet compile time: .616 milliseconds
Stylesheet execution time: .914 milliseconds
Daher bleib ich vorerst bei msxsl - und das kann derzeit auch nur xslt 1.0
Würde ich auch, schon allein aus dem Grund, weil xslt 2 eben noch keine Rec. ist.
Grüße
Thomas
Hi,
Aus irgendeinem Grund dauert es bei mir ca. 30 Sekunden, bis die JAVA-VM (1.4.2 genauso wie auch 1.4.0 oder 1.3.1) sich bereiterklärt, mit der Abarbeitung eines Java-Programms zu beginnen.
30 Sek. sind viel. Liegt vielleicht am Rechner?
Glaub ich nicht, daß es am Rechner liegt (2,7GHz, 512MB Ram usw.)
Die eigentliche Abarbeitung eines Java-Programms erfolgt in "normaler" Geschwindigkeit.
Es ist einzig der VM-Start, der ne Ewigkeit dauert.
Unabhängig davon, welches Java-Programm dann ausgeführt werden soll.
Die Start-Zeit kann ich akzeptieren, wenn ich nachher länger mit dem laufenden Programm arbeite (z.B: NetBeans Java-Entwicklungsumgebung), aber bei einem XSLT-Vorgang, der in ca. 1 Sekunde erledigt ist, ist mir der Overhead dann doch zu groß...
cu,
Andreas