XSLT calculations where grouping is on grand-children - xslt

i have a XML datadump from a database that i want to present using xslt.
The structure of the data is not 'straightforward' for the layout that i want (i also use the XML data for another report).
What i want is to do some calculations on data from Level-A where i need to group on Level-C children.
I know i can probably select the data again into an XML file where the structure is 'easy' for my report, but that is my last resort because i have the feeling that it can also be accomplished in XSLT itself. Most probbaly i need some 'Muenchian' trick to get it done, but since i am a 'Muenchian Virgin' i get stuck in every attempt (that i try to 'steal' and change ...).
Does anybody know if Muenchian is the way to proceed and can somebody help me to get on the right track ?
I did some reading (including Jeni Tennison's), but the stuff i have seen so far is not covering my problem as far as i know ...
Below is a simplified XML structure that is (more or less) representative for my real problem.
Any ideas?
Kind regards, Henk
Simplyfied XML:
<data>
<a>
<a_id>A1</a_id>
<a_desc>A one</a_desc>
<a_val>1</a_val>
<b>
<c>
<c_id>C2</c_id>
<c_desc>C two</c_desc>
</c>
</b>
</a>
<a>
<a_id>A2</a_id>
<a_desc>A two</a_desc>
<a_val>2</a_val>
<b>
<c>
<c_id>C2</c_id>
<c_desc>C two</c_desc>
</c>
</b>
</a>
<a>
<a_id>A3</a_id>
<a_desc>A three</a_desc>
<a_val>3</a_val>
<b>
<c>
<c_id>C1</c_id>
<c_desc>C one</c_desc>
</c>
</b>
</a>
<a>
<a_id>A4</a_id>
<a_desc>A four</a_desc>
<a_val>7</a_val>
<b>
<c>
<c_id>C3</c_id>
<c_desc>C three</c_desc>
</c>
</b>
</a>
<a>
<a_id>A5</a_id>
<a_desc>A five</a_desc>
<a_val>11</a_val>
<b>
<c>
<c_id>C1</c_id>
<c_desc>C one</c_desc>
</c>
</b>
</a>
</data>
Required output should be something like:
C_desc Count() Sum(a_val) Avg(a_val)
------ ------- ---------- ----------
C one 3 15 5
C two 1 2 2
C three 1 7 7

As you mention, Muenchian grouping is the way to go (in XSLT1.0). You say you want to group a elements, using values in a c element. Therefore you would define a key like so:
<xsl:key name="a" match="a" use="b/c/c_desc" />
Then, you need to get 'distinct' a elements, which is done by selecting a elements that happen to be the first element in the group for a given key. You do this with this fairly scary expression
<xsl:apply-templates
select="//a[generate-id() = generate-id(key('a', b/c/c_desc)[1])]" />
Here, key('a', b/c/c_desc)[1] will find the first element in the key's group, and then you use generate-id to compare the elements.
Then, you would have a template to match the distinct a elements, and within in this you can then do calculations on the group. For example, to get the sum:
<xsl:value-of select="sum(key('a', b/c/c_desc)/a_val)" />
Here is the full XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="exsl">
<xsl:output method="html" indent="yes"/>
<xsl:key name="a" match="a" use="b/c/c_desc" />
<xsl:template match="/">
<table>
<tr>
<td>C_desc</td>
<td>Count</td>
<td>Sum</td>
<td>Avg</td>
</tr>
<xsl:apply-templates select="//a[generate-id() = generate-id(key('a', b/c/c_desc)[1])]" />
</table>
</xsl:template>
<xsl:template match="a">
<xsl:variable name="c_desc" select="b/c/c_desc" />
<tr>
<td><xsl:value-of select="count(key('a', $c_desc))" /></td>
<td><xsl:value-of select="sum(key('a', $c_desc)/a_val)" /></td>
<td><xsl:value-of select="sum(key('a', $c_desc)/a_val) div count(key('a', $c_desc))" /></td>
</tr>
</xsl:template>
</xsl:stylesheet>
When applied to your sample XML, the following is output
<table>
<tr>
<td>C_desc</td>
<td>Count</td>
<td>Sum</td>
<td>Avg</td>
</tr>
<tr>
<td>3</td>
<td>15</td>
<td>5</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>2</td>
</tr>
<tr>
<td>1</td>
<td>7</td>
<td>7</td>
</tr>
</table>

Related

xsl:for-each inside xsl:for-each in table rows and columns

I've just started learning XML/XSL and I've hit a roadblock in one of my assignments. Tried Googling and searching over here but I can't seem to find a question that has a solution that is basic. so what I am trying is to display rows of bucket-type and room-types associated with it. can somebody please help
<list-inventory list-count="2">
<list list-type="Standard" list-order = "1" count-Types = "3">
<types type="BEN2D"></room>
<types type="BESH2D"></room>
<types type="HNK"></room>
</list>
<list list-type="Deluxe" list-order = "2" count-Types = "3">
<types type="SNK"></room>
<types type="TESTKD"></room>
<types type="TESTKD"></room>
</list>
<list-inventory>
I want table as below
Standard | Deluxe
BEN2D |SNK
BESH2D |TESTKD
HNK |TESTKD
I tried below xsl code but i see all list-type in single column and only 1st is being printing for all list-type:
<xsl:for-each select="/contents/list-inventory/list">
<tr>
<td class="alt-th" style="border:1px solid black">
<xsl:value-of select="#list-type"/>
</td>
</tr>
<tr>
<td style="border:1px solid black">
<xsl:for-each select="/contents/list-inventory/list/types">
<span><xsl:value-of select="#type"/></span>
<xsl:if test="position()!=last()">
<br/>
</xsl:if>
</xsl:for-each>
</td>
</tr>
</xsl:for-each>
Can someone help me with xsl:for-each inside a xsl:for-each
It's too bad you did not post your expected result as code. I would assume that you want a separate row for each pair of values. As I stated in the comments, this is far from being trivial.
However, you could make it simpler if you are willing to settle for a single data row, where each cell contains all the values of the corresponding list (your attempt seems to suggest that this is what you actually tried to accomplish).
So, given a well-formed XML input:
XML
<list-inventory list-count="2">
<list list-type="Standard" list-order = "1" count-Types = "3">
<types type="BEN2D"/>
<types type="BESH2D"/>
<types type="HNK"/>
</list>
<list list-type="Deluxe" list-order = "2" count-Types = "3">
<types type="SNK"/>
<types type="TESTKD"/>
<types type="TESTKD"/>
</list>
</list-inventory>
you could do simply:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/list-inventory">
<table border="1">
<!-- header -->
<tr>
<xsl:for-each select="list">
<th>
<xsl:value-of select="#list-type"/>
</th>
</xsl:for-each>
</tr>
<!-- body -->
<tr>
<xsl:for-each select="list">
<td>
<xsl:for-each select="types">
<xsl:value-of select="#type"/>
<br/>
</xsl:for-each>
</td>
</xsl:for-each>
</tr>
</table>
</xsl:template>
</xsl:stylesheet>
to get:
Result
<?xml version="1.0" encoding="UTF-8"?>
<table border="1">
<tr>
<th>Standard</th>
<th>Deluxe</th>
</tr>
<tr>
<td>BEN2D<br/>BESH2D<br/>HNK<br/>
</td>
<td>SNK<br/>TESTKD<br/>TESTKD<br/>
</td>
</tr>
</table>
which would render as:
I'm not sure how general you want your solution to be (i.e what inputs does it have to handle other than the example shown), but I would do something like:
<xsl:template match="list-inventory">
<xsl:variable name="list2" select="list[2]/types"/>
<xsl:for-each select="list[1]/types">
<xsl:variable name="position" select="position()"/>
<tr>
<td><xsl:value-of select="#type"/></td>
<td><xsl:value-of select="$list2[$position]/#type"/></td>
</tr>
</xsl:for-each>
</xsl:template>
Both the variables here are needed to avoid problems with context: the effect of an XPath expression depends on the context at the time it is evaluated, so you can evaluate a variable to capture information at the time you're in the right context.

XML to HTML table using XSL

I have an XML file that I export from my DB which is structured as below,
'
<output>
<row>
<Month>October</Month>
<Location>kansas</Location>
<bus_name>bus1</bus_name>
<bus_type>volvo</bus_type>
<bus_colour>red</bus_colour>
<bus_count>10</bus_count>
</row>
<row>
<Month>October</Month>
<Location>kansas</Location>
<bus_name>bus1</bus_name>
<bus_type>Volvo</bus_type>
<bus_colour>green</bus_colour>
<bus_count>11</bus_count>
</row>
<Month>October</Month>
<Location>kansas</Location>
<bus_name>bus1</bus_name>
<bus_type>Merc</bus_type>
<bus_colour>blue</bus_colour>
<bus_count>5</bus_count>
</row>
So on...
</output>
I need the table to look like the image attached below. The XSL and XML file will be refreshed periodically.The cells will have similar color's based on bus type.
I'm new to XSL thus having a really hard time coming up with a solution. Any help would be appreciated.
Just as a different approach and also handling the colours for the same bus_types.
Demo
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html"/>
<xsl:template match="/">
<table>
<tr>
<td colspan="9">ACME BUS SERVICE</td>
</tr>
<tr>
<td colspan="9">
Month: <xsl:value-of select="//Month"/>
</td>
</tr>
<tr>
<td>Season</td>
<td>Location</td>
<td>Bus Name</td>
<td colspan="2">RED</td>
<td colspan="2">GREEN</td>
<td colspan="2">BLUE</td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td>Bus Type</td>
<td>Bus Count</td>
<td>Bus Type</td>
<td>Bus Count</td>
<td>Bus Type</td>
<td>Bus Count</td>
</tr>
<xsl:for-each select="//row[Location[not(preceding::Location/. = .)]]" >
<xsl:variable name="currentLocation" select="./Location"/>
<tr>
<xsl:attribute name="class">
<xsl:value-of select="$currentLocation"/>
</xsl:attribute>
<td>
<xsl:if test="position()=1">Winter</xsl:if>
</td>
<td>
<xsl:value-of select="$currentLocation"/>
</td>
<td>
<xsl:value-of select="./bus_name"/>
</td>
<td>
<xsl:if test="count(//row[Location= $currentLocation]
[bus_type = //row[Location= $currentLocation]
[bus_colour = 'red']/bus_type]) > 1">
<xsl:attribute name="class">color</xsl:attribute>
</xsl:if>
<xsl:value-of select="//row[Location= $currentLocation]
[bus_colour = 'red']/bus_type"/>
</td>
<td>
<xsl:value-of select="//row[Location= $currentLocation]
[bus_colour = 'red']/bus_count"/>
</td>
<td>
<xsl:if test="count(//row[Location= $currentLocation]
[bus_type = //row[Location=$currentLocation]
[bus_colour = 'green']/bus_type]) > 1">
<xsl:attribute name="class">color</xsl:attribute>
</xsl:if>
<xsl:value-of select="//row[Location= $currentLocation]
[bus_colour = 'green']/bus_type"/>
</td>
<td>
<xsl:value-of select="//row[Location= $currentLocation]
[bus_colour = 'green']/bus_count"/>
</td>
<td>
<xsl:if test="count(//row[Location= $currentLocation]
[bus_type = //row[Location=$currentLocation]
[bus_colour = 'blue']/bus_type]) > 1">
<xsl:attribute name="class">color</xsl:attribute>
</xsl:if>
<xsl:value-of select="//row[Location= $currentLocation]
[bus_colour = 'blue']/bus_type"/>
</td>
<td>
<xsl:value-of select="//row[Location= $currentLocation]
[bus_colour = 'blue']/bus_count"/>
</td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
For every location, the location is set as classname to the <tr>, e.g. <tr class="kansas">. Every td with a bus type that is used more than once at a location gets the class="color". So to display the table with different colors, you can just add CSS like e.g. .kansas .color { background-color: blue; }. In case you want to display the same color based on the bus_type, just adjust the classname "color" in the xslt to the current bus_type.
Note: In the linked example I've only added one row for Texas to show that the XSLT displays multiple locations, only sets the season for the first one, and will also work in case not all colors are provided for a location. And the output is not valid HTML (no html-, head-, body-tags etc provided). As you mentioned you'd like to get HTML ouput, you probably already have an XSLT generating valid HTML where you can adjust/include the part you need for the table.
Update for the question in the comments: To set the class name for a <tr> to the name of the bus_type (in case a bus_type is used more than once at a location) instead of the location:
Change this in above XSLT:
<tr>
<xsl:attribute name="class">
<xsl:value-of select="$currentLocation"/>
</xsl:attribute>
into
<tr>
<xsl:if test="count(//row[Location=$currentLocation]) >
count(//row[Location=$currentLocation]/
bus_type[not(. = preceding::bus_type)])">
<xsl:attribute name="class">
<xsl:value-of select="//row[Location=$currentLocation]/
bus_type[ . = preceding::bus_type]"/>
</xsl:attribute>
</xsl:if>
Updated Demo 2 for this.
Additional notes and questions: One adjustment to the OP XML was to change the lowercase "volvo" to "Volvo". In case the original export from DB really mixes upper- and lowercase names, this can be handled in the XSLT to lowercase all bus_names (to get the unique values) and uppercase the first letter for the value in the <td>. Also it would be good to know if you use XSLT 2.0 or XSLT 1.0 as XSLT 2.0 provides functionality to simplify some tasks - e.g. 2.0 provides a lower-case() function where in 1.0 the same can be achieved using translate() - as reference for this as you mentioned you're new to XSL: How can I convert a string to upper- or lower-case with XSLT?
Further question is - as the XML example is only a part of the DB export - if there will be only one row for each location or if it is possible that there are various rows, e.g. kansas bus1, kansas bus2 etc.
Update 2 for the second question in the comments: I can add an (almost) line by line explanation and will drop a comment when done. I assume it's not necessary to cover the HTML part but only the XSLT. In the meantime, as you mentioned you're new to XSLT, maybe the following can be of use:
for <xsl:template match="/"> - https://stackoverflow.com/questions/3127108/xsl-xsltemplate-match
for XPath axes - http://www.xmlplease.com/axis
for some basics, e.g. - In what order do templates in an XSLT document execute, and do they match on the source XML or the buffered output?
Note that it should be avoided at SO to have extended comments - when there are too many comments below a post, an automated message will be displayed that suggests to move to chat. Because you need a reputation of 20 to chat (https://stackoverflow.com/help/privileges), this won't be possible at the moment.
The first three node has to be mapped with the first row
Now that is a specific question. Assuming your input is arranged so that each group of 3 consecutive <row> elements maps to a single table row (with internal group positions matching the column positions), try it this way:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:template match="/output">
<table border="1">
<tr>
<!-- build your header here -->
</tr>
<xsl:for-each select="row[position() mod 3 = 1]" >
<tr>
<td><xsl:value-of select="Location"/></td>
<td><xsl:value-of select="bus_name"/></td>
<xsl:for-each select=". | following-sibling::row[position() < 3]">
<td><xsl:value-of select="bus_type"/></td>
<td><xsl:value-of select="bus_colour"/></td>
<td><xsl:value-of select="bus_count"/></td>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
I suggest you ask a separate question regarding the coloring. Make sure we understand exactly what is known beforehand (e.g. a list of known bus types?) and what is the required output (post it as code).

XSLT: multiple tables

I come across this small problem while creating a xslt file... I have this generic xml file:
<data>
<folder>
<file>
<name>file1</name>
<date>2000</date>
<index1>1</index1>
<index2>1</index2>
</file>
<file>
<name>file2</name>
<date>2001</date>
<index1>1</index1>
<index2>1</index2>
</file>
<file>
<name>file3</name>
<date>2004</date>
<index1>2</index1>
<index2>1</index2>
</file>
</folder>
</data>
Given this abstract example, I have to transform it into something like:
<table>
<tr>
<td>Name</td>
<td>Date</td>
</tr>
<tr>
<td>file1</td>
<td>2000</td>
</tr>
<tr>
<td>file2</td>
<td>2001</td>
</tr>
</table>
<table>
<tr>
<td>Name</td>
<td>Date</td>
</tr>
<tr>
<td>file3</td>
<td>2004</td>
</tr>
</table>
I have to group the file elements per table based on their index1 and index2 (like an ID pair). I am able to create a table for every separated file, but I can't come with a solution to create a table for every file sharing index1 and index2. Any idea or suggestion?
Since you are using XSLT 2.0 you can use the xsl:for-each-group statement. You have two choices here, depending on whether you wish to keep groups together and respect the sequence or whether you just want to group regardless of sequence.
That is, given aabaab would you want groups of (aaaa, bb) or (aa, b, aa, b)?
This first groups all file elements with the same index1 and index2 regardless of order in the document (I've put in the body element just to make it well-formed)
<xsl:template match="folder">
<body>
<xsl:for-each-group select="file" group-by="concat(index1, '-', index2)">
<!-- xsl:for-each-group sets the first element in the group as the context node -->
<xsl:apply-templates select="."/>
</xsl:for-each-group>
</body>
</xsl:template>
<xsl:template match="file">
<table>
<tr>
<td>Name</td>
<td>Date</td>
</tr>
<xsl:apply-templates select="current-group()" mode="to-row"/>
</table>
</xsl:template>
<xsl:template match="file" mode="to-row">
<tr>
<xsl:apply-templates select="name|date"/>
</tr>
</xsl:template>
<xsl:template match="name|date">
<td><xsl:apply-templates/></td>
</xsl:template>
The second version would only need the first template changed to:
<xsl:template match="folder">
<body>
<xsl:for-each-group select="file" group-adjacent="concat(index1, '-', index2)">
<xsl:apply-templates select="."/>
</xsl:for-each-group>
</body>
</xsl:template>

Need to find the parent node id

This is my sample input
<table id="1" style="width=100%">
<tr>
<td id="1">
<table id="2" style="width=50%">
<tr>
<td id="2">
</td>
</tr>
</table>
</td>
</tr>
</table>
I am using xslt1.0. when ever the template match 'td' is matched, i need to find the corresponding table id value..For example, If the td with id=1 is matched,i want to take style attribute value from table(id=1) and if the td with id=2 is matched,i want to take style attribute value from table(id=2). I have written ancestor::table/#style in my template, but both td are referring the styles of table with id=1.
I have written
ancestor::table/#style in my
template
You were close. Because there can be more than one table in the ancestor axis, you need to get the first one like in ancestor::table[1]/#style. Of course, if you are absolute sure there is always a chain of table -> tr -> td (not optional tbody) then you could go with #Flack's answer.
Assuming you are in 'td' context, use this XPath:
../../#style
Test XSLT against your sample:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="td">
<xsl:value-of select="../../#style"/>
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
Result:
width=100%width=50%
Try this XPath it works perfectly
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="td">
<xsl:value-of select="ancestor::table[1]/#style"/>
<xsl:apply-templates/>
</xsl:template>
Result:
width=100%width=50%

How to Iterate Over Unknown Columns in XSLT

The Title pretty much says it all. I have an XML document that I am processing with XSLT .... but I don't know how many columns (fields) that are in the XML document (therefore, obviously, I don't know their names). How can I determine the number of "unknown" fields there are? Also, how can I read the attributes, if any, for the unknown fields?
Example data ....
<Dataset>
<Row>
<UnknownCol1 Msg="HowDoIGetThisMsgAttribute?"/>
<UnknownCol2 />
<UnknownCol3 />
</Row>
</Dataset>
How can I determine the number of
"unknown" fields there are?
This XPath expression:
count(/*/*/*)
evalutes to the count of the elements, that a re children of the elements that are children of the top node of the XML document -- exactly what is wanted in this case.
If the "Row" element can have children whose name does not start with "UnknownCol",
then this XPath expression provides the count of elements, whose name starts with "UnknownCol", and that are children of elements that are children of the top element:
count(/*/*/*[starts-with(name(), "UnknownCol")])
In case the top element may have other children than "Row", then an XPath expression giving the required count is:
count(/*/Row/*[starts-with(name(), "UnknownCol")])
Also, how can I read the attributes,
if any, for the unknown fields?
By knowing XPath :)
/*/Row/*[starts-with(name(), "UnknownCol")]/#*
selects all the attributes of all "UnknownCol"{String} elements
This XPath expression gives us the number of these attributes:
count(/*/Row/*[starts-with(name(), "UnknownCol")]/#*)
This Xpath expression gives us the name of the k-th such attribute ($ind must be set to the number k):
name( (/*/Row/*[starts-with(name(), "UnknownCol")]/#*)[$ind] )
And finally, this XPath expression produces the value of the k-th such attribute:
string( (/*/Row/*[starts-with(name(), "UnknownCol")]/#*)[$ind] )
Edit: The OP commented that he completely doesn't lnow the names of the children element.
The fix is easy: simply remove the predicate from all expressions:
count(/*/*/*)
count(/*/Row/*)
/*/Row/*/#*
count(/*/Row/*/#*)
name( (/*/Row/*/#*)[$ind] )
string( (/*/Row/*/#*)[$ind] )
There's easy ways around this problem.
For the example you provided you could use the following XPath:
select='/Dataset/Row/*/#Msg'
If you wanted a more specific example of this, you may want to make your question clearer on exactly what you'd like to do with the data and the unknown columns. Are you copying it exactly? What specifically do you want to transform it into? Do you have any keys you'd like to match?
That kind of thing.
Here is a small sample of XSLT 1.0 code that transforms your input into a HTML table.
<xsl:template match="Dataset">
<table>
<thead>
<tr>
<xsl:apply-templates select="Row[1]/*" mode="th" />
</tr>
</thead>
<tbody>
<xsl:apply-templates select="Row" />
</tbody>
</table>
</xsl:template>
<xsl:template match="Row">
<tr>
<xsl:apply-templates select="*" mode="td" />
</tr>
</xsl:template>
<xsl:template match="Row/*" mode="th">
<th>
<xsl:value-of select="local-name()" />
</th>
</xsl:template>
<xsl:template match="Row/*" mode="td">
<td>
<xsl:value-of select="#Msg" />
</td>
</xsl:template>
When applied to this sample input:
<Dataset>
<Row>
<UnknownCol1 Msg="Data_1_1" />
<UnknownCol2 Msg="Data_1_2" />
<UnknownCol3 Msg="Data_1_3" />
</Row>
<Row>
<UnknownCol1 />
<UnknownCol2 Msg="Data_2_2" />
<UnknownCol3 Msg="" />
</Row>
</Dataset>
this output is returned:
<table>
<thead>
<tr>
<th>UnknownCol1</th>
<th>UnknownCol2</th>
<th>UnknownCol3</th>
</tr>
</thead>
<tbody>
<tr>
<td>Data_1_1</td>
<td>Data_1_2</td>
<td>Data_1_3</td>
</tr>
<tr>
<td></td>
<td>Data_2_2</td>
<td></td>
</tr>
</tbody>
</table>
This can also help someone:
Going through unkown nodes that start-with() AMT_ and check if any of them are empty
XML:
<INCOME_FARMING_OPERATIONS>
<PERSONAL_FARMING>
<IND_SCHEDULE>Y</IND_SCHEDULE>
<DESCRIPTION>FARMING OPERATIONS</DESCRIPTION>
<IDENTIFIER>111111111111</IDENTIFIER>
<AMT_FARM_INC_GROSS>22222222</AMT_FARM_INC_GROSS>
<AMT_FARM_INC_PARTNER></AMT_FARM_INC_PARTNER>
<AMT_BAL_LIVESTOCK>99999999</AMT_BAL_LIVESTOCK>
</PERSONAL_FARMING>
</INCOME_FARMING_OPERATIONS>
XSLT
<xsl:variable name ="NodeValueIsEmpty">
<xsl:for-each select ="//INCOME_FARMING_OPERATIONS/PERSONAL_FARMING/*[starts-with(name(),'AMT_')]">
<xsl:if test ="string-length(.)=0">
empty found
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:value-of select="$IncomeFieldValues"/>
output:
empty found because of the empty
you can take it further:
<xsl:if test=string-length($NodeValueIsEmpty)>0>
//todo if any of the AMT_???? fields is not set
</xsl:if>