[XSL] xpath opbouwen uit recursieve loop

Pagina: 1
Acties:

  • Clay
  • Registratie: Oktober 1999
  • Laatst online: 25-02 11:17

Clay

cookie erbij?

Topicstarter
Terwijl ik recursief (en zonder de structuur vooraf te weten) door een xml bestand heenloop probeer ik het xpath op te bouwen naar de huidige node, bv /content/link[2]/title. Het probleem waar ik tegenaanloop is dat position() de positie van de huidige child binnen zijn parent geeft - ongeacht de nodename - maar /link[1] kan best /child::*[3] zijn, en position() geeft me dus niet die 1, maar die 3. Stel ik heb de volgende fictieve xml:

code:
1
2
3
4
5
6
<element>
   <melp> lorem </melp>
   <melp> ipsum </melp>
   <woei> dolor </woei>
   <woei> sit amet </woei>
</element>


terwijl ik door de childs (melp en woei) van <element> heenloop kan ik iig wel checken hoeveel nodes er binnen die parent zijn met dezelfde nodename met een if of when:

code:
1
test="count(../*[name()=$currentName])=1"

($currentName is een xsl:variable met de nodename van de huidige processed node)

Als er dan maar 1 child met die naam is heb ik de positie niet nodig voor de xpath. Wat ik zou kunnen doen als er wel meer dan 1 van is, is bv voor /woei[1] niet de nodename aan het pad toevoegen, maar /*[3], maar dat vind ik te ranzig/foutgevoelig.

Ik heb geprobeerd met een for-each door de alleen de child van de parent heen te lopen die dezelfde nodename hebben, en de position() daarvan op te halen als de huidige node tegengekomen wordt, ala:

code:
1
2
3
4
5
6
<xsl:variable name="currentNode" select="." />
<xsl:for-each select="../*[name()=$currentName]">
   <xsl:if test=". = $currentNode">
      <xsl:value-of select="concat('/', name(), '[', position(), ']')" />
   </xsl:if>
</xsl:for-each>


Maar dat gaat redelijk fout (het vergelijkt strings, geen objecten), en hoewel ik inmiddels aardig mn weg weet door xsl en xpath houdt het hier een beetje op :{ Zie ik iets over het hoofd en is hier een veel simpelere oplossing voor? kan het ueberhaupt? help? :)

Instagram | Flickr | "Let my music become battle cries" - Frédéric Chopin


  • Blue-eagle
  • Registratie: September 2000
  • Niet online
Ik heb geen direct antwoord op je vraag, maar je bent op de hoogte van de id() functie in xpath? Deze geeft eigenlijk een uniek node-ID terug gebaseerd op de positie van de huidige selectie. Wellicht dat je hiermee makkelijker een xpath kunt bijhouden?

* Blue-eagle gaat eerst even meer koffie naar binnen gieten

Verwijderd

Misschien dat ik het doel van de uiteindelijke XPath expressie niet goed snap, maar ik begrijp niet waarom /*[1]/*[3] 'ranziger is' dan /melp[1]/woei[2]. Het zijn beide unieke paden die maar een node kunnen bereiken.

  • Clay
  • Registratie: Oktober 1999
  • Laatst online: 25-02 11:17

Clay

cookie erbij?

Topicstarter
klopt, maar stel dat ik om wat voor reden dan ook een 3e melp toevoeg boven de eerste woei, dan is een ooit opgeslagen verwijzing naar /*[3] niet meer gelijk aan /woei[1], en dat is helaas iets wat gebeuren kan. Ranzig is dan misschien niet het juiste woord :) maar foutgevoeliger is het dus wel. Ik moet de verwijzing dus voor later gebruik op kunnen slaan, en de xml kan (binnen redelijke grenzen) veranderen.

Instagram | Flickr | "Let my music become battle cries" - Frédéric Chopin


Verwijderd

Dan zie ik eigenlijk geen andere optie dan ID's genereren voor nodes die je direct wilt kunnen accessen.

  • Clay
  • Registratie: Oktober 1999
  • Laatst online: 25-02 11:17

Clay

cookie erbij?

Topicstarter
Met een beetje extra hulp van google heb ik denk ik toch een oplossing zonder extra id's nodig te hebben:

code:
1
2
3
4
5
6
<xsl:variable name="childPos" select="position()" />
<xsl:for-each select="../*[name()=$currentName]">
   <xsl:if test="count(preceding-sibling::node())+1 = $childPos">
      <xsl:value-of select="concat('/', name(), '[', position(), ']')" />
   </xsl:if>
</xsl:for-each>


Als de position() van de child overeenkomt met het aantal childs dat ervoor zit +1 moet het dezelfde zijn... De position() binnen de for-each is dan de zoveelste child van dat type binnen de parent.

[ Voor 15% gewijzigd door Clay op 24-02-2005 11:32 ]

Instagram | Flickr | "Let my music become battle cries" - Frédéric Chopin


Verwijderd

Maar als het XML-document waarop je de XPath baseert tussentijds nog kan veranderen schiet je daar toch alsnog weinig mee op? (ik vind het wel een hele mooie constructie, trouwens)

  • Clay
  • Registratie: Oktober 1999
  • Laatst online: 25-02 11:17

Clay

cookie erbij?

Topicstarter
Als er nu een melp bijkomt boven de woeis blijft het zo goedgaan, woei[1] is dan 1 child verder, maar de count komt ook 1 hoger uit. De structuur zelf mag idd niet (te bruut) veranderen, maar die is ook gebonden aan een xsd, dus dat valt buiten de "redelijkheid grenzen" :)

Instagram | Flickr | "Let my music become battle cries" - Frédéric Chopin


  • Clay
  • Registratie: Oktober 1999
  • Laatst online: 25-02 11:17

Clay

cookie erbij?

Topicstarter
* kick, puur ter info

Dankzij een bug in een andere parser dan waar ik mee teste was ik gedwongen een andere oplossing te vinden. De uiteindelijke valuof kreeg deze count:

code:
1
count(preceding-sibling::node()[name()=$currentName])+1


oftewel "tel alle preceding siblings met dezelfde nodename". Ietsje korter toch wel :X geen for each meer, geen if meer, gewoon een enkele concat met 1 count :X |:(

[ Voor 9% gewijzigd door Clay op 04-03-2005 13:56 ]

Instagram | Flickr | "Let my music become battle cries" - Frédéric Chopin

Pagina: 1