Search code examples
javaxmlxsltxslt-1.0xslt-2.0

how to set certian attribute value in array node while maintaining of original structure of xml file


i have xml with multiply blocks of worker. i want to change the date field format (from string to date) only if the original value not equal to null. In addition, it is important to maintain the structure and values ​​of the file

i have xml file like that

<header>
<serianl_num>123</serianl_num>
<type>11</type>
<body>
   <direction>in</direction>
   <worker>
     <salary>1234</salary>
     <name>aaa</name>
      <gender>male</gender>
      <date>20140101</date>
   </worker>
 <worker>
     <salary>1234</salary>
     <name>bbb</name>
      <gender>female</gender>
      <date>nil:true</date>
   </worker>
</body>
</header>

and this the expected output result i want to get

<header>
<serianl_num>123</serianl_num>
<type>11</type>
<body>
   <direction>in</direction>
   <worker>
     <salary>1234</salary>
     <name>aaa</name>
      <gender>male</gender>
      <date>2014-01-01</date>
   </worker>
 <worker>
     <salary>1234</salary>
     <name>bbb</name>
      <gender>female</gender>
      <date>nil:true</date>
   </worker>
</body>
</header>

this is the xsl i tried to write:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl=http://www.w3.org/1999/XSL/Transform xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance>
            <xsl:output method="xml" indent="yes" xalan:indent-amount="2"
                        xmlns:xalan=http://xml.apache.org/xalan/>
            <xsl:strip-space elements="*"/>
            <xsl:template match="node()|@*">
                        <xsl:copy>
                             <xsl:apply-templates select="node()|@*" />
                        </xsl:copy>
            </xsl:template>
   <xsl:template match = "header/body">        
         <xsl:for-each select = "worker">
                        <xsl:choose>
                          <xsl:param name="myDate" select ="string-length(date)"/>
                             <xsl:when test = "string-length($myDate)=8">           
                       <xsl:copy>
<xsl:value-of select="concat(substring($myDate,1,4),'-',substring($myDate,5,2),'-',substring($myDate,7,2))"/>
                          </xsl:copy>
                         </xsl:when>
                            <xsl:otherwise>
                                <xsl:copy>
                              <xsl:attribute name="xsi:nil">true</xsl:attribute>
                               <xsl:value-of select="."/>
                                 </xsl:copy>
                          </xsl:otherwise>
                 </xsl:choose>                   
            </xsl:for-each>
            </xsl:template>
            </xsl:stylesheet>

but it dosent work. im Would appreciate for any help

i tried to find information in internet, I tried several attempts to achieve the desired result, but without success. I would greatly appreciate any help or advice to solve the problem.


Solution

  • If you want to change the value of the date element (not attribute) then match only the date element (or even only the text node inside the date element) and copy everything else as is.

    The other problem is that your test of string-length(date)=8 is also true for <date>nil:true</date>. So you need to find another test or make the test more selective. Try for example:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes"/>
    <xsl:strip-space elements="*"/>
    
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="date[string-length() = 8 and . != 'nil:true']/text()">
        <xsl:value-of select="concat(substring(., 1, 4), '-', substring(., 5, 2), '-',substring(., 7, 2))"/>
    </xsl:template>
    
    </xsl:stylesheet>