简体   繁体   中英

XSLT to text - Child nodes with same name- Check for last child of each parent

I need to convert an XML to Text using XSLT. The output I got has all the data in a block without the delimiter or new line feed, I feel the position part in the XSLT isn't even getting executed.

Question : I want to test the position of the last child of every parent and include a new line if its the last child (columnvalue) of the parent(currentRow) or a delimiter if it's not.

I have an XML which looks like below:

XML File:

<?xml version='1.0'?>
<webRowSet xmlns='http://java.sun.com/xml/ns/jdbc'>
<data>
<currentRow>
<columnValue><![CDATA[]]></columnValue>
<columnValue><![CDATA[26068384]]></columnValue>
<columnValue><![CDATA[070-0010055-4842MAR18]]></columnValue>
<columnValue>2018-04-25</columnValue>
<columnValue>170.310</columnValue>
<columnValue><![CDATA[UI-004058]]></columnValue>
</currentRow>
<currentRow><columnValue><![CDATA[]]></columnValue>
<columnValue><![CDATA[26068385]]></columnValue>
<columnValue><![CDATA[070-0010058-5739MAR18]]></columnValue>
<columnValue>2018-04-25</columnValue>
<columnValue>209.900</columnValue>
<columnValue><![CDATA[UI-004057]]></columnValue>
</currentRow>
</data></webRowSet>

XSLT:

<?xml version="1.0"?>
<xsl:stylesheet version ="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" omit-xml-declaration="yes" indent="no"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="Delimiter"><xsl:text>|</xsl:text></xsl:variable>
<xsl:variable name="NewLine"><xsl:text>&#10;</xsl:text></xsl:variable>
<xsl:template match="/">
<xsl:text>EXPR5_5|VOUCHER_ID|INVOICE_ID|ENTERED_DT|GROSS_AMT|UnifierRecordNo</xsl:text>
<xsl:text>&#10;</xsl:text>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="webRowSet/data">
<xsl:for-each select="currentRow">
<xsl:for-each select="columnValue">
<xsl:value-of select="."/>
<xsl:variable name="Rec_position" select="count(../preceding-sibling::columnValue)+1"/>
<xsl:if test="$Rec_position=6"><xsl:value-of select="$NewLine"/></xsl:if>
<xsl:if test="$Rec_position!=6"><xsl:value-of select="$Delimiter"/></xsl:if>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

Output in TEXT should be like:

EXPR5_5|VOUCHER_ID|INVOICE_ID|ENTERED_DT|GROSS_AMT|UnifierRecordNo
   |26068384|070-0010055-4842MAR18|2018-04-25|170.310|UI-004058
   |26068385|070-0010058-5739MAR18|2018-04-25|209.900|UI-004057

You have two problems in your XSLT.

Firstly, you have not accounted for the fact the elements in your XML are in a default namespace, as defined by xmlns='http://java.sun.com/xml/ns/jdbc on your root element.

If you are indeed only using XSLT 1.0, you have to associate that namespace with a prefix in your XSLT, like so...

xmlns:j="http://java.sun.com/xml/ns/jdbc"

And then use that prefix before all the elements in your select expressions. For example...

 <xsl:template match="j:webRowSet/j:data">

Secondly, in your count expesssion, the .. represents the parent node, when you really want to count the preceding siblings of the current node, so you should write this...

 <xsl:variable name="Rec_position" select="count(preceding-sibling::j:columnValue)+1"/>

Although the position() function will do just the same thing here.

<xsl:variable name="Rec_position" select="position()"/>

But do you really want to hard-code the number of columns as 6 though? Try this XSLT instead:

<xsl:stylesheet version ="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:j="http://java.sun.com/xml/ns/jdbc">
<xsl:output method="text" omit-xml-declaration="yes" indent="no"/>

<xsl:strip-space elements="*"/>

<xsl:variable name="Delimiter"><xsl:text>|</xsl:text></xsl:variable>
<xsl:variable name="NewLine"><xsl:text>&#10;</xsl:text></xsl:variable>

<xsl:template match="/">
  <xsl:text>EXPR5_5|VOUCHER_ID|INVOICE_ID|ENTERED_DT|GROSS_AMT|UnifierRecordNo</xsl:text>
  <xsl:value-of select="$NewLine"/>
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="j:webRowSet/j:data">
  <xsl:for-each select="j:currentRow">
    <xsl:for-each select="j:columnValue">
      <xsl:if test="position() != 1"><xsl:value-of select="$Delimiter" /></xsl:if>
      <xsl:value-of select="."/>
    </xsl:for-each>
    <xsl:value-of select="$NewLine"/>
  </xsl:for-each>
</xsl:template>
</xsl:stylesheet>

Note, if you could use XSLT 2.0, you could write it like this:

<xsl:stylesheet version ="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
                xpath-default-namespace="http://java.sun.com/xml/ns/jdbc">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>

<xsl:variable name="NewLine"><xsl:text>&#10;</xsl:text></xsl:variable>

<xsl:template match="/">
  <xsl:text>EXPR5_5|VOUCHER_ID|INVOICE_ID|ENTERED_DT|GROSS_AMT|UnifierRecordNo</xsl:text>
  <xsl:value-of select="$NewLine"/>
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="webRowSet/data">
  <xsl:for-each select="currentRow">
    <xsl:value-of select="columnValue" separator="|"/>
    <xsl:value-of select="$NewLine"/>
  </xsl:for-each>
</xsl:template>
</xsl:stylesheet>

Note the use of xpath-default-namespace that makes handling default namespaces easier. Also, the use of separator on the xsl:value-of which applies when selecting multiple values. (In XSLT 1.0, xsl:value-of would only display the first value).

You forgot to take the namespace into account. So add xmlns:j="http://java.sun.com/xml/ns/jdbc" to the xsl:stylesheet element of your XSLT file. This way you can access all elements with the correct namespace.

With respect to the namespace change your last template to:

<xsl:template match="j:webRowSet/j:data">
    <xsl:for-each select="j:currentRow">
      <xsl:for-each select="j:columnValue">
        <xsl:value-of select="."/>
        <xsl:variable name="Rec_position" select="position()"/>
        <xsl:if test="$Rec_position=6"><xsl:value-of select="$NewLine"/></xsl:if>
        <xsl:if test="$Rec_position &lt; 6"><xsl:value-of select="$Delimiter"/></xsl:if>
      </xsl:for-each>
    </xsl:for-each>
</xsl:template>

The output is:

EXPR5_5|VOUCHER_ID|INVOICE_ID|ENTERED_DT|GROSS_AMT|UnifierRecordNo
|26068384|070-0010055-4842MAR18|2018-04-25|170.310|UI-004058
|26068385|070-0010058-5739MAR18|2018-04-25|209.900|UI-004057

This is a very simple solution with a minimum of modification.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM