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>
Related
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.
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).
I have a problem when parsing dynamic xml data into a html table. The XML is as follow.
<results>
<result id="1" desc="Voltage and current">
<measure desc="VOLT" value="1.0" />
<measure desc="AMPERE" value="2.0" />
</result>
<result id="2" desc="Current-1">
<measure desc="AMPERE" value="5.0" />
</result>
</results>
from which I would like a html table like:
ID DESC VOLT AMPERE
1 Voltage and current 1.0 2.0
2 Current-1 5.0
Notice the empty cell at second voltage column. ID and DESC is taken from result/#id and result/#desc and the rest of the column names should come from measure/#desc
No column name should be duplicate, I managed to code that far, but when I start adding my measures I need to match each measure/#desc to correct column in the table. I tried double nested loops to first match all unique column names, and then loop all measures again to match the column header. But the xslt parser threw a NPE on me!
Sorry that I can't show any code as it is on a non-connected computer.
I've browsed so many Q/A here on SO but to no help for my specific problem.
Thanks in advance
Note: I am able to change the XML format in any way to make parsing easier if anyone come up with a neater format.
If you are using XSLT1.0, you can use a technique called 'Muenchian' grouping to get the distinct measure descriptions, which will form the basis of your head row, and also be used to output the values of each row.
Firstly, you define a key to look up measure elements by their #desc attribute
<xsl:key name="measures" match="measure" use="#desc" />
Then, to get the distinct measure descriptions you can iterate over the measure elements that appear first in the group for their given #desc attribute
<xsl:apply-templates
select="result/measure[generate-id() = generate-id(key('measures', #desc)[1])]"
mode="header" />
Then, for your header, you would simply have a template to output the description.
<xsl:template match="measure" mode="header">
<th>
<xsl:value-of select="#desc" />
</th>
</xsl:template>
For each result row, would do a similar thing, and iterate over all distinct measure values, but the only difference is you would have to pass in the current result element as a parameter, for later use.
<xsl:apply-templates
select="/results/result/measure[generate-id() = generate-id(key('measures', #desc)[1])]"
mode="data">
<xsl:with-param name="result" select="." />
</xsl:apply-templates>
Then, in the template that matched the measure this time, you could access the measure within the result element with a matching #desc attribute (and id there is no such attribute, nothing is output for the cell)
<xsl:template match="measure" mode="data">
<xsl:param name="result" />
<td>
<xsl:value-of select="$result/measure[#desc = current()/#desc]/#value" />
</td>
</xsl:template>
Here is the full XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="measures" match="measure" use="#desc" />
<xsl:template match="/results">
<table>
<tr>
<th>ID</th>
<th>DESC</th>
<xsl:apply-templates select="result/measure[generate-id() = generate-id(key('measures', #desc)[1])]" mode="header" />
</tr>
<xsl:apply-templates select="result" />
</table>
</xsl:template>
<xsl:template match="result">
<tr>
<td><xsl:value-of select="#id" /></td>
<td><xsl:value-of select="#desc" /></td>
<xsl:apply-templates select="/results/result/measure[generate-id() = generate-id(key('measures', #desc)[1])]" mode="data">
<xsl:with-param name="result" select="." />
</xsl:apply-templates>
</tr>
</xsl:template>
<xsl:template match="measure" mode="header">
<th>
<xsl:value-of select="#desc" />
</th>
</xsl:template>
<xsl:template match="measure" mode="data">
<xsl:param name="result" />
<td>
<xsl:value-of select="$result/measure[#desc = current()/#desc]/#value" />
</td>
</xsl:template>
</xsl:stylesheet>
Note the use of the mode attributes because you have two templates matching the measure element, which function in different ways.
When applied to your input XML, the following is output
<table>
<tr>
<th>ID</th>
<th>DESC</th>
<th>VOLT</th>
<th>AMPERE</th>
</tr>
<tr>
<td>1</td>
<td>Voltage and current</td>
<td>1.0</td>
<td>2.0</td>
</tr>
<tr>
<td>2</td>
<td>Current-1</td>
<td/>
<td>5.0</td>
</tr>
</table>
I have an xml dump of a transaction that I want to create a report from.
The (simplified) xml structure looks like:
Subject
id
name
license
event id=key
result
eventtype
date
action
charge id=key
result
code
date
...etc
There is only one id, name, and license for every subject; but there could be many events, and many charges for each subject--each one having a different result.
The report output is a table type, with each subject having a header of name, id, license, and multiple blocks of data transactions listed below the header. Each data block has different values and structure based on the type of transaction.
It works perfectly for the header, and one each of the data transaction types, but only the first event transaction and the first charge transaction will print out.
The (simplified) xsl looks like:
<xsl:for-each select="file/subject">
<xsl:if test="not (#subject = preceding-sibling::subject[1])">
<table width="754" border="2" cellpadding="0">
<tr>
<td width="172">ctn: <strong><xsl:value-of select="id"/></strong></td>
<td width="364">defendant: <strong><xsl:value-of select="name"/></strong></td>
<td width="200">sid: <strong><xsl:value-of select="license"/></strong></td>
</tr>
</table>
</xsl:if>
<blockquote>
<xsl:if test="event/eventType != ' '">
<table width="695" border="1">
<tr>
<td>Event<xsl:value-of select="event/result"/></td>
<td>Eventtype<xsl:value-of select="event/eventtype"/></td>
<td>Date<xsl:value-of select="event/date"/></td>
</tr>
</table>
</xsl:if>
<xsl:if test="charge/code != ' '">
<table width="695" border="1">
<tr>
<td>Charge<xsl:value-of select="charge/result"/></td>
<td>Code<xsl:value-of select="charge/code"/></td>
<td>Date<xsl:value-of select="charge/date"/></td>
</tr>
</table>
</xsl:if>
</blockquote>
</xsl:for-each>
I assume that I need an additional for-each to scan the child nodes, but adding the additional loop just gives me the headers.
Any thoughts?
(and thanks for taking the time to read this far. :o)
Where possible in XSLT, avoid for-each and apply your nodes to transformation templates instead.
I came up with this, which you can run at this playground session - not sure if it's broadly what you're after.
<!-- root and static content -->
<xsl:template match="/">
<xsl:apply-templates select='root/Subject' />
</xsl:template>
<!-- iteration content: subject -->
<xsl:template match='Subject'>
<table>
<tr>
<td>ctn: <strong><xsl:value-of select="id"/></strong></td>
<td>defendant: <strong><xsl:value-of select="name"/></strong></td>
<td>sid: <strong><xsl:value-of select="license"/></strong></td>
</tr>
</table>
<xsl:apply-templates select='event' />
<xsl:apply-templates select='charge' />
</xsl:template>
<!-- iteration content: event -->
<xsl:template match='event'>
<blockquote>
<table>
<tr>
<td>Event: <xsl:value-of select="result"/></td>
<td>Event type: <xsl:value-of select="eventtype"/></td>
<td>Date: <xsl:value-of select="date"/></td>
</tr>
</table>
</blockquote>
</xsl:template>
<!-- iteration content: charge -->
<xsl:template match='charge'>
<blockquote>
<table>
<tr>
<td>Charge: <xsl:value-of select="result"/></td>
<td>Code: <xsl:value-of select="code"/></td>
<td>Date: <xsl:value-of select="date"/></td>
</tr>
</table>
</blockquote>
</xsl:template>
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>