XPath required:Checking values inside Each row of particular column - xslt

I have a below scenario.I need the xpath for the below:
We have a table with thead Name,Code and Conduct.
Also we or may not have blank column next to code.It may be any number.
But the table Starts with Name and ends with Conduct.
Thead example:
1) <Row>
<Cell><Element>Name</Element></Cell>
<Cell><Element>Code</Element></Cell>
<Cell><Element>Conduct</Element></Cell>
</Row>
2)<Row>
<Cell><Element>Name</Element></Cell>
<Cell><Element>Code</Element></Cell>
<Cell><Element></Element></Cell>
<Cell><Element>Conduct</Element></Cell>
</Row>
3) <Row>
<Cell><Element>Name</Element></Cell>
<Cell><Element>Code</Element></Cell>
<Cell><Element></Element></Cell>
<Cell><Element></Element></Cell>
<Cell><Element>Conduct</Element></Cell>
</Row>
Tbody's first row should have value in all column.Mainly in Code and Empty thead column.
Also Cell may contain inner element Element1.
Tbody First Row example:
ex 1) <Row>
<Cell><Element>abc</Element></Cell>
<Cell><Element>23</Element></Cell>
<Cell><Element>Good</Element></Cell>
</Row>
ex 2)<Row>
<Cell><Element>ttt</Element></Cell>
<Cell><Element>34</Element></Cell>
<Cell><Element>45</Element></Cell>
<Cell><Element>good</Element></Cell>
</Row>
ex 3) <Row>
<Cell><Element>yyy</Element></Cell>
<Cell><Element>22</Element></Cell>
<Cell><Element>33</Element></Cell>
<Cell><Element><Element1>4</Element1>4</Element></Cell>
<Cell><Element>Conduct</Element></Cell>
</Row>
From Row 2 Onwards we need to check at-least any one element in the code column and the adjacent empty column should have value.
If any one row has value then we should convert the table as stated in Sample Output1:(Positive case).
ie) Table name should be changed to XYZ and combile all the element insed cell for Code column and the adjacent empty column.
Sample Input1:(Positive case)
<Table Name="abc">
<Thead>
<Row>
<Cell><Element>Name</Element></Cell>
<Cell><Element>Code</Element></Cell>
<Cell><Element></Element></Cell>
<Cell><Element>Conduct</Element></Cell>
</Row>
</Thead>
<Tbody>
<Row>
<Cell><Element>Sam</Element></Cell>
<Cell><Element>1</Element><Element>2</Element><Element>1</Element></Cell>
<Cell><Element>1</Element><Element></Element><Element>1</Element></Cell>
<Cell><Element>Good</Element></Cell>
</Row>
<Row>
<Cell><Element>xyz</Element></Cell>
<Cell><Element>123</Element></Cell>
<Cell><Element></Element><Element></Element><Element><Element1>1<Element1></Element></Cell>
<Cell><Element>Good</Element></Cell>
</Row>
</Tbody>
</Table>
Sample Output1:(Positive case)
<Table Name="XYZ">
<Thead>
<Row>
<Cell><Element>Name</Element></Cell>
<Cell><Element>Code</Element></Cell>
<Cell><Element></Element></Cell>
<Cell><Element>Conduct</Element></Cell>
</Row>
</Thead>
<Tbody>
<Row>
<Cell><Element>Sam</Element></Cell>
<Cell><Element>121</Element></Cell>
<Cell><Element>11</Element></Cell>
<Cell><Element>Good</Element></Cell>
</Row>
<Row>
<Cell><Element>TTT</Element></Cell>
<Cell><Element>123</Element></Cell>
<Cell><Element>1</Element></Cell>
<Cell><Element>Good</Element></Cell>
</Row>
<Row>
<Cell><Element>PPP</Element></Cell>
<Cell><Element>123</Element></Cell>
<Cell><Element>1</Element></Cell>
<Cell><Element>Good</Element></Cell>
</Row>
</Tbody>
</Table>
If all the rows has empty value in any one column then we do nothing.
Sample Input2:(Negative case)
<Table Name="abc">
<Thead>
<Row>
<Cell><Element>Name</Element></Cell>
<Cell><Element>Code</Element></Cell>
<Cell><Element></Element></Cell>
<Cell><Element>Conduct</Element></Cell>
</Row>
</Thead>
<Tbody>
<Row>
<Cell><Element>Sam</Element></Cell>
<Cell><Element>1</Element><Element>2</Element><Element>3</Element></Cell>
<Cell><Element></Element><Element></Element><Element></Element></Cell>
<Cell><Element>Good</Element></Cell>
</Row>
<Row>
<Cell><Element>xyz</Element></Cell>
<Cell><Element>123</Element></Cell>
<Cell><Element></Element><Element></Element><Element><Element1><Element1></Element></Cell>
<Cell><Element>Good</Element></Cell>
</Row>
</Tbody>
</Table>
Sample Output2:(Negative case)
same as input.
What we tried:
count(Tbody//Row[count(./Cell[2]/Element[number(normalize-space(.)) and number(normalize-space(.)) > 0]) != 0])
This work for Only Code column but for we dont know how many number of adjucent empty columns will come.For that condition we need to know xpath.

Your description is very difficult to follow, and I am mainly guessing here. Would the following test work for you to distinguish between the two cases?
<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:strip-space elements="*"/>
<xsl:template match="Table">
<xsl:choose>
<xsl:when test="Tbody/Row[position() > 1]/Cell[position() > 2 and position() != last()][.//text()]">POSITIVE</xsl:when>
<xsl:otherwise>NEGATIVE</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
What this does is look at every row of the body, starting at row #2, and see if there is a value inside any cell, starting at cell #3 and ending at the cell before the last one. If any such cell is found, the result is positive.

Related

How to perform pagination of table for following case using xslt?

enter image description here
I want to paginate this table using xslt such that the last two p elements, and first two p elements should be together.
My input xml is somewhat like this:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<table>
<tgroup>
<row>
<entry>Parameter</entry>
<entry>Description</entry>
</row>
<row>
<entry>A</entry>
<entry><p>A1</p>
<p>A2</p>
<p>A3</p>
<p>A4</p>
<p>A5</p>
</entry>
</row>
<row>
<entry>B</entry>
<entry><p>B1</p>
<p>B2</p>
<p>B3</p>
<p>B4</p>
<p>B5</p>
</entry>
</row>
</tgroup>
</table>
And I want to automate adding outputclass attributes to p elements for pagination as shown here:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<table>
<tgroup>
<row>
<entry>Parameter</entry>
<entry>Description</entry>
</row>
<row>
<entry>A</entry>
<entry><p>A1</p>
<p>A2 outputclass="keep-with-previous"</p>
<p>A3</p>
<p>A4 outputclass="keep-with-next"</p>
<p>A5</p>
</entry>
</row>
<row>
<entry>B</entry>
<entry><p>B1</p>
<p>B2 outputclass="keep-with-previous"</p>
<p>B3</p>
<p>B4 outputclass="keep-with-next"</p>
<p>B5</p>
</entry>
</row>
</tgroup>
</table>
My css is as follows:
#media print
{ *[outputclass~="keep-with-next"] { page-break-after: avoid; }
*[outputclass~="keep-with-previous"] { page-break-before: avoid; }
*[outputclass~="top-of-page"] { page-break-before: always; } }
You might need these two templates:
<xsl:template match="row/entry[2]/p[2]">
<p outputclass="keep-with-previous" >
<xsl:apply-templates/>
</p>
</xsl:template>
<xsl:template match="row/entry[2]/p[4]">
<p outputclass="keep-with-next">
<xsl:apply-templates/>
</p>
</xsl:template>

Using sum() to sum up values with nested foreach()

I need help in properly specifying the input parameter for sum(). I encountered sum()'s output to concatenate the values instead of summing up.
My goal is: To sum values of Column6 when Column2 is "invoice"
In the example below, I have 2000 and 1000 for Column6 whose Column is "invoice".
I'm expecting it to display 3000 for <TotalAmount>
<Root>
<DataArea>
<Row>... </Row>
<Row>... </Row>
<!-- and so on -->
<Row>
<Column1>29/03/19</Column1>
<Column2>cr note</Column2>
<Column3>092-213280101</Column3>
<Column4>1,474.98 </Column4>
<Column5>103.25 </Column5>
<Column6>2000 </Column6>
</Row>
<Row>
<Column1>29/03/19</Column1>
<Column2>invoice</Column2>
<Column3>092-213280101</Column3>
<Column4>1,474.98 </Column4>
<Column5>103.25 </Column5>
<Column6>2000 </Column6>
</Row>
<Row>
<Column1>11/06/19</Column1>
<Column2>invoice</Column2>
<Column3>123-123456789</Column3>
<Column4>100.50 </Column4>
<Column5>100.50 </Column5>
<Column6>1000</Column6>
</Row>
</DataArea>
</Root>
<xsl:for-each
select="Root/DataArea/Row">
<xsl:if ... >
<TransactionSummary>
<DebitSummary>
<TotalAmount>
<xsl:attribute name="currencyID"></xsl:attribute>
<xsl:for-each select="../Row">
<xsl:if test="(Column2 = 'invoice') and (position() > 9) ">
<xsl:value-of select="sum(../Column6)" />
</xsl:if>
</xsl:for-each>
</TotalAmount>
</DebitSummary>
</TransactionSummary>
</xsl:if>
</xsl:for-each>
I tried sum(Column6), didn't work. Output: 20001000
I tried sum(../Column6), didn't work. Output: 00
I tried sum(../Row/Column6), didn't work. Output: Cannot convert string "" to a double
I tried sum(Root/DataArea/Row/Column6), didn't work. Output: 00
I'd appreciate any help.
Thank you.
You don't need the inner xsl:for-each here, you can do it with a single xsl:value-of with the conditions in the select
<xsl:value-of select="sum(../Row[Column2 = 'invoice' and position() > 9]/Column6)" />
Or, in the context of your snippet....
<xsl:for-each select="Root/DataArea/Row">
<TransactionSummary>
<DebitSummary>
<TotalAmount currencyID="">
<xsl:value-of select="sum(../Row[Column2 = 'invoice' and position() > 9]/Column6)" />
</TotalAmount>
</DebitSummary>
</TransactionSummary>
</xsl:for-each>
It seems you want something like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<TransactionSummary>
<DebitSummary>
<TotalAmount currencyID="someId">
<xsl:value-of select="sum(/*/*/Row[Column2 = 'invoice']/Column6)"/>
</TotalAmount>
</DebitSummary>
</TransactionSummary>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<Root>
<DataArea>
<Row>... </Row>
<Row>... </Row>
<!-- and so on -->
<Row>
<Column1>29/03/19</Column1>
<Column2>cr note</Column2>
<Column3>092-213280101</Column3>
<Column4>1,474.98 </Column4>
<Column5>103.25 </Column5>
<Column6>2000 </Column6>
</Row>
<Row>
<Column1>29/03/19</Column1>
<Column2>invoice</Column2>
<Column3>092-213280101</Column3>
<Column4>1,474.98 </Column4>
<Column5>103.25 </Column5>
<Column6>2000 </Column6>
</Row>
<Row>
<Column1>11/06/19</Column1>
<Column2>invoice</Column2>
<Column3>123-123456789</Column3>
<Column4>100.50 </Column4>
<Column5>100.50 </Column5>
<Column6>1000</Column6>
</Row>
</DataArea>
</Root>
the wanted result is produced:
<TransactionSummary>
<DebitSummary>
<TotalAmount currencyID="someId">3000</TotalAmount>
</DebitSummary>
</TransactionSummary>

XML value to XML node conversion using XSLT?

I am trying to write XSLT file for following input XML to output XML, is it possible XSLT to convert the value of input xml as node in output XML? how can I implement this?
Input XML
<?xml version="1.0" encoding="UTF-8"?>
<Rows>
<Row><xml_data_name/> <xml_data_value/> </Row>
<Row><xml_data_name>persons</xml_data_name> <xml_data_value/> </Row>
<Row><xml_data_name>person</xml_data_name> <xml_data_value/> </Row>
<Row><xml_data_name>username</xml_data_name> <xml_data_value>JS1</xml_data_value> </Row>
<Row><xml_data_name>name</xml_data_name> <xml_data_value/> </Row>
<Row><xml_data_name>name</xml_data_name> <xml_data_value>John</xml_data_value> </Row>
<Row><xml_data_name>name</xml_data_name> <xml_data_value/> </Row>
<Row><xml_data_name>family-name</xml_data_name> <xml_data_value/> </Row>
<Row><xml_data_name>family-name</xml_data_name> <xml_data_value>Smith</xml_data_value> </Row>
<Row><xml_data_name>family-name</xml_data_name> <xml_data_value/> </Row>
<Row><xml_data_name>person</xml_data_name> <xml_data_value/> </Row>
<Row><xml_data_name>person</xml_data_name> <xml_data_value/> </Row>
<Row><xml_data_name>username</xml_data_name> <xml_data_value>MI1</xml_data_value> </Row>
<Row><xml_data_name>name</xml_data_name> <xml_data_value/> </Row>
<Row><xml_data_name>name</xml_data_name> <xml_data_value>Morka</xml_data_value> </Row>
<Row><xml_data_name>name</xml_data_name> <xml_data_value/> </Row>
<Row><xml_data_name>family-name</xml_data_name> <xml_data_value/> </Row>
<Row><xml_data_name>family-name</xml_data_name> <xml_data_value>Ismincius</xml_data_value> </Row>
<Row><xml_data_name>family-name</xml_data_name> <xml_data_value/> </Row>
<Row><xml_data_name>person</xml_data_name> <xml_data_value/> </Row>
<Row><xml_data_name>persons</xml_data_name> <xml_data_value/> </Row>
<Row><xml_data_name/> <xml_data_value/> </Row>
</Rows>
Output XML
<?xml version="1.0" ?>
<persons>
<person username="JS1">
<name>John</name>
<family-name>Smith</family-name>
</person>
<person username="MI1">
<name>Morka</name>
<family-name>Ismincius</family-name>
</person>
</persons>
You could certainly use xsl:element like
<xsl:template match="Row">
<!-- Note {} brackets in name attribute -->
<xsl:element name="{xml_data_name}">
<xsl:value-of select="xml_data_value" />
</xsl:element>
</xsl:template>
What would be greater problem is a structure of output because it is not easy to decide which rows should be nested, which rows should transform into an attribute rather than element etc.
Well, that's one of the weirdest data formats I've ever seen! Are you sure you can't get whatever produced this to produce something more reasonable?
I think the solution has to be recursion: you want a function that takes a sequence of rows as input; it outputs an element whose name is the name of the first element in the sequence with no data value and whose content is obtained by a recursive call that passes all rows after that first row up to the next row with no data value and the same name, then calls itself to process all rows after that row. Not easy, and certainly takes more time than I allow myself for answering SO questions!

Confused in XSLT scripting

hi i want to create an HTML table that shows the numbers(Years) in one column and the data in the second column. below is my xslt. i'm really confused as they have the same tags.
<chapter>
<row>
<entry>
<para>1984</para>
</entry>
<entry>
<para>International Business Companies Act passed into
law.</para>
</entry>
</row>
<row>
<entry>
<para>2004</para>
</entry>
<entry>
<para>BVI Business Companies Act passed into law, coming into
force on 1 January 2005.</para>
</entry>
</row>
<row>
<entry>
<para>2005</para>
</entry>
<entry>
<para>All three corporate statutes exist in parallel and it is
possible to incorporate companies under any of them.</para>
</entry>
</row>
<row>
<entry>
<para>2006</para>
</entry>
<entry>
<para>Incorporation provisions in the International Business
Companies Act and the Companies Act are repealed on 31 December
2005; the Acts remain in force but new companies may only be
incorporated under the BVI Business Companies Act.</para>
</entry>
</row>
</chapter>
Thanks
I am assuming that the first two elements of the XML that you posted are wrapped by a <row> element and all the rows are grouped under a parent element called rows.
If some of those assumptions are wrong, tell me and I will correct the code.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output mode="html" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="text()" />
<!-- I am assuming that the parent element for the set of row elements is
named rows. You can change this to match your XML -->
<xsl:template match="chapter">
<table>
<tr>
<th>Year</th>
<th>Data</th>
</tr>
<xsl:apply-templates select="row" />
</table>
</xsl:template>
<xsl:template match="row">
<tr>
<xsl:apply-templates select="*" />
</tr>
</xsl:template>
<xsl:template match="para">
<td><xsl:value-of select="." /></td>
</xsl:template>
</xsl:stylesheet>
UPDATE : If you just want to match the first entry/para element for each row, then you should use a template like the following one:
<xsl:template match="entry[1]/para">
<!-- Put your code here -->
</xsl:template>
Here's an example of creating a HTML table using XSLT. The dataset used in the example is very similar to your XML, just substitute the examples <book> tags with your <row> tags and so on.

XSLT 1.0 - Transform XML Based on Relational Data in Other XML Files

I need to transform an XML file by adding a new element that will have a value based on data in the current file and two other XML files using XSLT 1.0. The files:
File1:
<Table>
<Row>
<ColA>AValue1</ColA>
<ColB>BValue1</ColB>
</Row>
<Row>
<ColA>AValue2</ColA>
<ColB>BValue2</ColB>
</Row>
</Table>
File2:
<Table>
<Row>
<ColA>AValue1</ColA>
<ColB>BValue1</ColB>
<ColC>CValue1</ColC>
</Row>
<Row>
<ColA>AValue1</ColA>
<ColB>BValue1</ColB>
<ColC>CValue1</ColC>
</Row>
<Row>
<ColA>AValue1</ColA>
<ColB>BValue1</ColB>
<ColC>CValue2</ColC>
</Row>
<Row>
<ColA>AValue1</ColA>
<ColB>BValue1</ColB>
<ColC>CValue3</ColC>
</Row>
<Row>
<ColA>AValue2</ColA>
<ColB>BValue2</ColB>
<ColC>CValue1</ColC>
</Row>
</Table>
File3:
<Table>
<Row>
<ColC>CValue1</ColC>
<ColD>ABC</ColD>
</Row>
<Row>
<ColC>CValue2</ColC>
<ColD>DEF</ColD>
</Row>
<Row>
<ColC>CValue3</ColC>
<ColD>DEF</ColD>
</Row>
</Table>
Rows in File1 have a one-to-many relationship with rows in File2 by ColA and ColB.
Rows in File2 have a many-to-one relationship with rows in File3 by ColC.
For each row in File1, I need to:
Look up distinct ColC values in File2 for rows matching on ColA and ColB
For each distinct ColC value, look up ColD value in File3 for rows matching on ColC
Count the number of occurences of ColD values looked up in step 2. ColD will have one of two values (say "ABC" or "DEF"). I need to know if there are more "ABC" than "DEF" and if so add <ColD>ABC</ColD> to that Row in File1. Otherwise add <ColD>DEF</ColD> to that Row in File1. At the end, each Row in File1 should have <ColD>ABC</ColD> or <ColD>DEF</ColD>.
Desired Result (File1 transformed):
<Table>
<Row>
<ColA>AValue1</ColA>
<ColB>BValue1</ColB>
<ColD>DEF</ColD>
</Row>
<Row>
<ColA>AValue2</ColA>
<ColB>BValue2</ColB>
<ColD>ABC</ColD>
</Row>
</Table>
<ColD>DEF</ColD> would be added to the first Row since there were two occurences of DEF compared to 1 (distinct) occurence of ABC. <ColD>ABC</ColD> would be added to Row 2 since there was one occurence of ABC and zero DEF.
Well, I'll try a suggestion but I'm not sure to have understand everything well, so I'll delete it if it makes no sense.
I understand that you got some related Xml files model. I mean you need to reach elements in files 2 related to elements in File1 and so on.
I got some similar case and fix it by applying a first transform to solve all the references in all files first (duplicating XML nodes everywhere) and applying my more specifics transform on this 'un-related model'. It's way easier but may have some performances issues (un-relate all files could be long if lots of relations), in my case it was for off-line generation so no good perf needed.
I came up with a solution that appears to work (haven't tested it very much). I'm retyping it here so beware of typos. The thing I had the hardest time with was figuring out how to select unique ColC values. Looking at the solution again I didn't need unique values. I had it in my mind that I would loop thru $unique_c_values to count ColD values, but it turned out differently.
<xsl:template match="node()|#">
<xsl:copy>
<xsl:apply-templates select="node()|#">
</xsl:copy>
</xsl:template>
<xsl:template match="Row">
<xsl:copy>
<xsl:apply-templates select="node()|#">
<xsl:variable name="unique_c_values" select=
"document('file2.xml')/Table/Row[current()/ColA = ColA and current()/ColB = ColB]/ColC[not(. = preceding-sibling::*/ColC[current()/ColA = ../ColA and current()/ColB = ../ColB])]"/>
<xsl:variable name="d_values" select=
"document('file3.xml')/Table/Row[ColC = $unique_c_values]/ColD"/>
<xsl:choose>
<xsl:when test="count($d_values[. = 'DEF']) > count($d_values[. = 'ABC'])">
<ColD>DEF</ColD>
</xsl:when>
<xsl:otherwise>
<ColD>ABC</ColD>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>