1

I need to calculate a value which is equal to 2* the lowest two dimensions.

<VALUES>
    <LENGTH>10</LENGTH>
    <WIDTH>12</WIDTH>
    <HEIGHT>11</HEIGHT>
</VALUES>

So in the above example, we would have need GIRTH = 2* (LENGTH + HEIGHT) = 44.

resulting in a new XML looking like the below (Do note, that the input and output have been simplified)

<RESULT>
    <LENGTH>10</LENGTH>
    <WIDTH>12</WIDTH>
    <HEIGHT>11</HEIGHT>
    <GIRTH>44</GIRTH>
</RESULT>

How would be the cleanest way to get the lowest two? (I this case, LENGTH and HEIGHT but it could be any 2 of the three)

I'd rather not use 3 if statements to get the 3 possibilities covered and i am trying to avoid using C# helpers for everything i don't get/find.

Also, i am using this in BIZTALK2010, i am not sure if it is xslt 1 or xslt 2.

Andy
  • 2,248
  • 7
  • 34
  • 57
  • 1
    Could you clarify what exactly "*2* the lowest two*" means? Is it supposed to return two separate results, or a single combined one - and if combined, how exactly? – michael.hor257k May 18 '15 at 11:00
  • @michael.hor257k by the lowest i meant the smallest. it is meant to be used as a way to calculate the GIRTH, which is the 2 shortest dimensions. I am not sure how to better phrase the question in english that keeps the question broad as well (the shortest two feels to limited). Perhaps the smallest two nodes out of 3 nodes numerical? – Andy May 18 '15 at 12:58
  • I understand what "lowest" means; I don't understand how you want to *use* the two lowest values. Why don't edit your question and add the exact expected result of your example. – michael.hor257k May 18 '15 at 13:05
  • I believe the correct result is in your example is 42, not 220. – michael.hor257k May 18 '15 at 13:47
  • @michael.hor257k yes, i don't know why i didn't see that. i am going to test your answer. Thank you for your help as well. – Andy May 18 '15 at 13:55

2 Answers2

1

A solution can look like the following:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

    <xsl:output method="text"/>
    <xsl:template match="/VALUES">
        <xsl:variable name="result">
            <xsl:for-each select="LENGTH|WIDTH|HEIGHT">
                <xsl:sort select="." order="ascending" data-type="number"/>
                <v>
                    <xsl:value-of select="."/>
                </v>
            </xsl:for-each>
        </xsl:variable>

        <xsl:value-of select="2 * $result/v[1] * $result/v[2]"/>
    </xsl:template>
</xsl:stylesheet>
leu
  • 2,051
  • 2
  • 12
  • 25
  • this works well, the only issue i have is that it seems the values as text, meaning a 5,10,10 will result in the 10s being the lowest pair. If you don't have an answer off the top of your head though, i can google that issue myself. – Andy May 18 '15 at 09:06
  • it was as simple as adding a data-type="number" to the sort to get what i wanted. Thanks for your help. – Andy May 18 '15 at 09:13
  • 1
    **No, this is NOT an XSLT 1.0 solution.** In XSLT 1.0, the $result variable will contain a result-tree-fragment, and the expression `$result/v[1]` will generate a fatal error. – michael.hor257k May 18 '15 at 10:49
1

it is meant to be used as a way to calculate the GIRTH, which is the 2 shortest dimensions

Assuming GIRTH is calculated the way explained here, I would do it this way:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:strip-space elements="*"/>

<xsl:template match="/VALUES">
    <xsl:variable name="sorted-dimensions">
        <xsl:for-each select="*">
            <xsl:sort select="." order="ascending" data-type="number"/>
            <xsl:copy-of select="."/>
        </xsl:for-each>
    </xsl:variable>
    <girth>
        <xsl:value-of select="2 * sum(exsl:node-set($sorted-dimensions)/*[position()!=3])"/>
    </girth>
</xsl:template>

</xsl:stylesheet>

This is assuming there are exactly 3 dimensions under VALUES.


If you want to jump directly to step #3, you could do:

<measurement>
    <xsl:value-of select="2 * sum(exsl:node-set($sorted-dimensions)/*[position()!=3]) + exsl:node-set($sorted-dimensions)/*[3]"/>
</measurement>
michael.hor257k
  • 113,275
  • 6
  • 33
  • 51
  • running this, i get the error "cannot find the script or external object that implements prefix exsl". i have tried browsing to the url used on the machine that runs it but it can reach it. i did find the thread at "http://stackoverflow.com/questions/4997421/exception-loading-external-xslt" that states only the common can be used, but your function is part of common so it should be fine. – Andy May 19 '15 at 13:20
  • @Andy At this point I am not sure which XSLT processor you're using. I assumed it was one of Microsoft MSXML ones, which are XSLT 1.0 only. However, if the answer by leu worked for you, then you are not using an XSLT 1.0 processor. This would also explain the error message. -- Note that *some* MSXML processors support the node-set() function as a Microsoft (not EXSLT) extension only, and require it to be in their namespace - see: https://msdn.microsoft.com/en-us/library/hz88kef0%28v=vs.110%29.aspx – michael.hor257k May 19 '15 at 15:36
  • You were correct. the processor BizTalk uses is .NET Xsl Transform. Your code worked perfectly under ".NET XslCompiledTransform" and leu his answer ran perfectly under Saxon (which is 2.0). To make it work in Biztalk (2010), i had to use the MSXML node-set function. Right now, the transform works as i would expect. – Andy Jun 01 '15 at 12:34