[XSLT] Optellen van nodeset in variable

Pagina: 1
Acties:
  • 129 views sinds 30-01-2008
  • Reageer

  • MisterData
  • Registratie: September 2001
  • Laatst online: 16-05 23:29
Ik heb een XML-bestand met getallen erin. In een andere xml (settings.xml) kun je kolommen definieren met een sommetje. Voor iedere regel in de data-xml moet de XSLT dus het sommetje uitrekenen. Het sommetje is gedefinieerd als volgt:

XML:
1
2
3
4
5
6
    <CustomColumn name="Som">
        <Operator type="sum">
            <Operand type="column">SA_23</Operand>
            <Operand type="column">SA_19</Operand>
        </Operator>
    </CustomColumn>


De code, die het daarbij behorende sommetje perfect kan maken, is de volgende:

XML:
1
2
3
4
5
6
7
8
9
10
11
<xsl:template match="Operator[@type='sum']">
        <xsl:variable name="sumSet">        
            <xsl:for-each select="*">
                 <xsl:apply-templates select="Operand"/>
            </xsl:for-each>
        </xsl:variable>
        
        <xsl:value-of select="$sumSet"/>
    </xsl:template>
    
    <xsl:template match="Operand">1</xsl:template>


(De 1 wordt later vervangen door iets om het juiste getal uit de data-xml te trekken).

Het probleem is nu dat deze perfect werkt, maar dan op de verkeerde manier. Als er 5 Operand-nodes zijn, dan zou hij dus 1+1+1+1+1 moeten uitrekenen, maar wat 'ie nu doet (logisch ook) is dat $sumSet aan het einde '11111' is. sum($sumSet) werkt niet, omdat $sumSet bestaat uit 'tree fragments' en niet uit een 'node list'. Hoe kan ik het wel oplossen? De bedoeling is om straks ook geneste expressies toe te staan, zodat een Operator tag als Operand kan dienen :)

  • Michali
  • Registratie: Juli 2002
  • Laatst online: 22-03 18:12
Ik denk dat je het toch anders moet gaan oplossen dan met een template. Een template is echt meer bedoeld voor directe XML output en niet voor een berekening zoals je nu probeert. Je zegt dat je de data uit een ander xml bestand gaat trekken. Mischien kun je eens laten zien hoe die er ongeveer uit gaat zien. Het is me nog een beetje onduidelijk zo. Wat ik wel weet is dat het niet gaat lukken zoals je het nu probeert (of ik moet er heel erg naast zitten).

Wat ik nu begrijp is dat je aan een bepaalde kolom een getal koppelt? Wordt het dan iets in de zin van <column-value name="SA_23">1400</column-value> of zie ik het verkeerd zo?

Noushka's Magnificent Dream | Unity


  • MisterData
  • Registratie: September 2001
  • Laatst online: 16-05 23:29
Elke kolom heeft inderdaad een waarde per kolom, ziet er in de settings xml uit als <Operand type="column">SA_23</Operand>. Als ik gewoon de 'uitkomst' van apply-templates zou kunnen optellen, dan was het gewoon gelukt volgens mij... maar dat blijkt niet te kunnen dus?

De data xml ziet er ongeveer zo uit:

XML:
1
2
3
4
5
6
7
8
9
10
11
<Data>
<Object symbol="...">
    <SystemAttribute def-symbol="SA_23">250000</SystemAttribute>
    <SystemAttribute def-symbol="SA_19">210000</SystemAttribute>
</Object>
<Object symbol="...">
    <SystemAttribute def-symbol="SA_23">250440</SystemAttribute>
    <SystemAttribute def-symbol="SA_19">210550</SystemAttribute>
</Object>
...nog een paar Object tags...
</Data>


Je kunt in een variabele wel een nodelist kwijt, die je dan weer zou kunnen sum'en, maar op de manier uit mijn TS krijg ik er geen nodelist uit...

[ Voor 38% gewijzigd door MisterData op 29-11-2004 07:45 ]


  • cam47bubbles
  • Registratie: Januari 2001
  • Laatst online: 09-04 16:55
Welke XML processor gebruik je? Je zou namelijk de node-set functie moeten gebruiken van bijv. xalan of msxml. Je kan dan namelijk jouw XML fragment in die node-set functie kunnen stoppen. Dan kun je er gewoon met Xpath queries doorheen wandelen.

gr.

WJ

  • Michali
  • Registratie: Juli 2002
  • Laatst online: 22-03 18:12
Ik weet niet precies hoe je wilt linken naar die XML data, maar je zou het kunnen proberen op te lossen met een recursieve template. Je zou dan kunnen denken aan iets in de richting van:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<xsl:template match="Operator[@type='sum']">
    <xsl:call-template name="sum">
        <xsl:with-param name="nodes" select="child::Operand"/>
    </xsl:call-template>
</xsl:template>

<xsl:template name="sum">
    <xsl:param name="nodes"/>
    <xsl:param name="result" value="0"/>
    
    <xsl:choose>
        <xsl:when test="$nodes">
            <xsl:call-template name="sum">
                <xsl:with-param name="nodes" select="$nodes[position() != 1]"/>
                <xsl:with-param name="result"
                    select="$result + number(document('data.xml')/Data/Object/SystemAttribute[@def-symbol = $nodes[1]::text()])"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$result"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

Ik refereer zo direct naar een SystemAttribute met de juiste def-symbol, maar ik zie dat je die ook meerdere keren defineerd. Welke van de twee moet gekozen worden? Of moeten deze waardes ook weer opgeteld worden? Zoals het nu is heb je kans dat er meerdere SystemAttributes worden geselecteerd wat niet echt de bedoeling is voor zo'n berekening. Mischien moet number() door sum() vervangen worden?

[ Voor 16% gewijzigd door Michali op 29-11-2004 11:20 ]

Noushka's Magnificent Dream | Unity


  • MisterData
  • Registratie: September 2001
  • Laatst online: 16-05 23:29
code:
1
2
 <xsl:with-param name="result"
                    select="$result + ..."/>


Mag je zomaar een parameter een andere waarde geven :?

  • Michali
  • Registratie: Juli 2002
  • Laatst online: 22-03 18:12
Ik geef hem geen andere waarde. Ik defineer een nieuwe waarde voor een nieuwe call. Hij blijft dus zichzelf aanroepen en dus de waarde verhogen totdat alle nodes zijn opgeteld. Pas dan wordt xsl:value-of aangeroepen en het resultaat uitgeprint. Je moet wel uitkijken dat je niet te veel nodes gaat optellen anders wordt het traag en heb je zelfs kan dat de parser vast loopt.

Hier staat er wel een leuk stukje over: http://topxml.com/xsl/articles/recurse/

Noushka's Magnificent Dream | Unity


  • MisterData
  • Registratie: September 2001
  • Laatst online: 16-05 23:29
Michali schreef op maandag 29 november 2004 @ 11:19:
code:
1
2
                <xsl:with-param name="result"
                    select="$result + number(document('data.xml')/Data/Object/SystemAttribute[@def-symbol = $nodes[1]::text

Ik refereer zo direct naar een SystemAttribute met de juiste def-symbol, maar ik zie dat je die ook meerdere keren defineerd. Welke van de twee moet gekozen worden? Of moeten deze waardes ook weer opgeteld worden? Zoals het nu is heb je kans dat er meerdere SystemAttributes worden geselecteerd wat niet echt de bedoeling is voor zo'n berekening. Mischien moet number() door sum() vervangen worden?
Ik heb ergens een $currentObject-variabele die ik dan gewoon als parameter mee kan geven, zodat het werkt :) Gaat zoiets ook werken met meerdere operators trouwens? ga het vanavond eens testen :)
cam47bubbles schreef op maandag 29 november 2004 @ 08:22:
Welke XML processor gebruik je? Je zou namelijk de node-set functie moeten gebruiken van bijv. xalan of msxml. Je kan dan namelijk jouw XML fragment in die node-set functie kunnen stoppen. Dan kun je er gewoon met Xpath queries doorheen wandelen.
Ik gebruik inderdaad Xalan (vanuit een Java-programma dat met 'smart report classes' rapporten kan maken, ik schrijf een XSL-rapport), dus dat is misschien wel een veel nettere optie dan :)

offtopic:
oh, hier op GoT schrijven we eigenlijk nooit groetjes onderaan posts, dus als je hier nieuw bent, lees de FAQ dan even :*

[ Voor 31% gewijzigd door MisterData op 29-11-2004 15:57 ]


  • Michali
  • Registratie: Juli 2002
  • Laatst online: 22-03 18:12
Je kan per operator een andere template maken of beter nog aan de hand van een extra operator parameter een andere berekening doen voor de nieuwe result parameter.
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<xsl:call-template name="OperandCalculation">
    <xsl:choose>
        <xsl:when test="$operator = 'sum'">
            <xsl:with-param name="result" value="$result + ..."/>
        </xsl:when>
        <xsl:when test="$operator = 'subtract'">
            <xsl:with-param name="result" value="$result - ..."/>
        </xsl:when>
        <xsl:when test="$operator = 'multiply'">
            <xsl:with-param name="result" value="$result * ..."/>
        </xsl:when>
        <xsl:when test="$operator = 'devide'">
            <xsl:with-param name="result" value="$result / ..."/>
        </xsl:when>
    </xsl:choose>
    <xsl:with-param name="nodes" value="$nodes[position() != 1]"/>
    <xsl:with-param name="operator" value="$operator"/>
</xsl:call-template>

[ Voor 25% gewijzigd door Michali op 29-11-2004 16:03 ]

Noushka's Magnificent Dream | Unity


  • MisterData
  • Registratie: September 2001
  • Laatst online: 16-05 23:29
Het was nog even puzzelen, maar uiteindelijk is het gelukt met dank aan Michali _/-\o_ Verbaas me echt iedere dag weer over wat XSL allemaal kan :) Sommetjes gaan snel zat en werken zo te zien oneindig diep.

  • Michali
  • Registratie: Juli 2002
  • Laatst online: 22-03 18:12
Soms is het even zoeken, maar veel problemen zijn wel op te lossen. Erg netjes vind ik het eerlijk gezegd niet, maar je moet wat inleveren voor een hoop andere geweldige functionaliteit.

Noushka's Magnificent Dream | Unity

Pagina: 1