Add attribute xsl:nil=true on empty elements using XSLT

What?
An article on how to declare an XML element as NULL using the attribute "xsi:nil". I'm going to use a very short example by providing a blank date of birth value:
copyraw
-- What I have:
<DATE_OF_BIRTH />
<DATE_OF_BIRTH_EUROPEANFORMAT>//</DATE_OF_BIRTH_EUROPEANFORMAT>

-- What I want:
<DATE_OF_BIRTH xsi:nil="true" />
<DATE_OF_BIRTH_EUROPEANFORMAT xsi:nil="true" />
  1.  -- What I have: 
  2.  <DATE_OF_BIRTH /> 
  3.  <DATE_OF_BIRTH_EUROPEANFORMAT>>//</DATE_OF_BIRTH_EUROPEANFORMAT> 
  4.   
  5.  -- What I want: 
  6.  <DATE_OF_BIRTH xsi:nil="true" /> 
  7.  <DATE_OF_BIRTH_EUROPEANFORMAT xsi:nil="true" /> 

Why?
Outputting from SITS:Vision to our staging environment, the application would only output blank values using single tags so we had to find a place to introduce it. On strings this has little worth, but on dates which could be NULL, this was necessary (unless we interpreted dates as strings which we don't want to do):
copyraw
-- SITS Output: Option 1
<stu_dob></stu_dob>

-- SITS Output: Option 2
<stu_dob />

-- but not
<stu_dob xsi:nil="true" />
  1.  -- SITS Output: Option 1 
  2.  <stu_dob></stu_dob> 
  3.   
  4.  -- SITS Output: Option 2 
  5.  <stu_dob /> 
  6.   
  7.  -- but not 
  8.  <stu_dob xsi:nil="true" /> 
This doesn't tell us if the element is just an empty string or null. This doesn't have much affect on point-to-point conversions and strings but what if the value is blank and I try to convert that date of birth from SQL format (yyyy-mm-dd) to European format (dd/mm/yyyy)? See the following:
copyraw
<xsl:stylesheet
	version="1.0"
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

	<xsl:output method="xml" indent="yes"/>
	<xsl:template match="exchange/stu">
		<STUDENT>
			<xsl:for-each select="stu.srs">
				<DATE_OF_BIRTH>
					<xsl:value-of select="stu_dob"/>
				</DATE_OF_BIRTH>

				<DATE_OF_BIRTH_EUROPEANFORMAT>
					<xsl:value-of select="concat(substring(stu_dob, 9, 2),'/',substring(stu_dob, 6, 2),'/',substring(stu_dob, 1, 4))"/>
				</DATE_OF_BIRTH_EUROPEANFORMAT>
			</xsl:for-each>
		</STUDENT>
	</xsl:template>
</xsl:stylesheet>


-- assuming value of DATE_OF_BIRTH is blank or null yields
<STUDENT xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
	<DATE_OF_BIRTH />
	<DATE_OF_BIRTH_EUROPEANFORMAT>//</DATE_OF_BIRTH_EUROPEANFORMAT>
</STUDENT>

-- errors
  1.  <xsl:stylesheet 
  2.      version="1.0" 
  3.      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
  4.   
  5.      <xsl:output method="xml" indent="yes"/> 
  6.      <xsl:template match="exchange/stu"> 
  7.          <STUDENT> 
  8.              <xsl:for-each select="stu.srs"> 
  9.                  <DATE_OF_BIRTH> 
  10.                      <xsl:value-of select="stu_dob"/> 
  11.                  </DATE_OF_BIRTH> 
  12.   
  13.                  <DATE_OF_BIRTH_EUROPEANFORMAT> 
  14.                      <xsl:value-of select="concat(substring(stu_dob, 9, 2),'/',substring(stu_dob, 6, 2),'/',substring(stu_dob, 1, 4))"/> 
  15.                  </DATE_OF_BIRTH_EUROPEANFORMAT> 
  16.              </xsl:for-each> 
  17.          </STUDENT> 
  18.      </xsl:template> 
  19.  </xsl:stylesheet> 
  20.   
  21.   
  22.  -- assuming value of DATE_OF_BIRTH is blank or null yields 
  23.  <STUDENT xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
  24.      <DATE_OF_BIRTH /> 
  25.      <DATE_OF_BIRTH_EUROPEANFORMAT>>//</DATE_OF_BIRTH_EUROPEANFORMAT> 
  26.  </STUDENT> 
  27.   
  28.  -- errors 

How?
I could correct this using SSIS but with all the data in XML, we might as well stick with the technology and we're going to keep our XSLT with some slight modifications. First let's look at the XML output from SITS (this is a cut down version for demonstration purposes):
copyraw
<exchange>
	<stu>
		<stu.srs>
			<stu_code>1234567</stu_code>
			<stu_fnm1>JOEL</stu_fnm1>
			<stu_surn>LIPMAN</stu_surn>
			<stu_dob />
		</stu.srs>
	</stu>
</exchange>
  1.  <exchange> 
  2.      <stu> 
  3.          <stu.srs> 
  4.              <stu_code>1234567</stu_code> 
  5.              <stu_fnm1>JOEL</stu_fnm1> 
  6.              <stu_surn>LIPMAN</stu_surn> 
  7.              <stu_dob /> 
  8.          </stu.srs> 
  9.      </stu> 
  10.  </exchange> 
Two things to do really:
  1. Add the xmlns:xsi namespace in the xsl:stylesheet tag so that "xsi:nil=true" is valid.
  2. Test for a string length of greater than 0 (not blank) and change the attribute of the element so that it's tag displays "xsi:nil=true".
If...then...else... will be emulated by the XSLT choose...when...test...otherwise:
copyraw
<xsl:stylesheet
	version="1.0"
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

	<xsl:output method="xml" indent="yes"/>
	<xsl:template match="exchange/stu">

		<!-- note the addition of the xmlns:xsi namespace //-->
		<STUDENT xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
			<xsl:for-each select="stu.srs">
				<DATE_OF_BIRTH>
					<!-- adding a bit of conditionality //-->
					<xsl:choose>
						<xsl:when test="string-length(normalize-space(stu_dob))>0">
							<xsl:value-of select="stu_dob"/>
						</xsl:when>
						<xsl:otherwise>
							<xsl:attribute name="xsi:nil">true</xsl:attribute>
							<xsl:value-of select="stu_dob"/>
						</xsl:otherwise>
					</xsl:choose>
				</DATE_OF_BIRTH>

				<DATE_OF_BIRTH_EUROPEANFORMAT>
					<xsl:choose>
						<xsl:when test="string-length(normalize-space(stu_dob))>0">
							<xsl:value-of select="concat(substring(stu_dob, 9, 2),'/',substring(stu_dob, 6, 2),'/',substring(stu_dob, 1, 4))"/>
						</xsl:when>
						<xsl:otherwise>
							<xsl:attribute name="xsi:nil">true</xsl:attribute>
							<xsl:value-of select="stu_dob"/>
						</xsl:otherwise>
					</xsl:choose>
				</DATE_OF_BIRTH_EUROPEANFORMAT>
			</xsl:for-each>
		</STUDENT>
	</xsl:template>
</xsl:stylesheet>
  1.  <xsl:stylesheet 
  2.      version="1.0" 
  3.      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
  4.   
  5.      <xsl:output method="xml" indent="yes"/> 
  6.      <xsl:template match="exchange/stu"> 
  7.   
  8.          <!-- note the addition of the xmlns:xsi namespace //--> 
  9.          <STUDENT xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
  10.              <xsl:for-each select="stu.srs"> 
  11.                  <DATE_OF_BIRTH> 
  12.                      <!-- adding a bit of conditionality //--> 
  13.                      <xsl:choose> 
  14.                          <xsl:when test="string-length(normalize-space(stu_dob))>0"> 
  15.                              <xsl:value-of select="stu_dob"/> 
  16.                          </xsl:when> 
  17.                          <xsl:otherwise> 
  18.                              <xsl:attribute name="xsi:nil">true</xsl:attribute> 
  19.                              <xsl:value-of select="stu_dob"/> 
  20.                          </xsl:otherwise> 
  21.                      </xsl:choose> 
  22.                  </DATE_OF_BIRTH> 
  23.   
  24.                  <DATE_OF_BIRTH_EUROPEANFORMAT> 
  25.                      <xsl:choose> 
  26.                          <xsl:when test="string-length(normalize-space(stu_dob))>0"> 
  27.                              <xsl:value-of select="concat(substring(stu_dob, 9, 2),'/',substring(stu_dob, 6, 2),'/',substring(stu_dob, 1, 4))"/> 
  28.                          </xsl:when> 
  29.                          <xsl:otherwise> 
  30.                              <xsl:attribute name="xsi:nil">true</xsl:attribute> 
  31.                              <xsl:value-of select="stu_dob"/> 
  32.                          </xsl:otherwise> 
  33.                      </xsl:choose> 
  34.                  </DATE_OF_BIRTH_EUROPEANFORMAT> 
  35.              </xsl:for-each> 
  36.          </STUDENT> 
  37.      </xsl:template> 
  38.  </xsl:stylesheet> 
copyraw
-- yields: assuming value of DATE_OF_BIRTH is blank or null yields
<STUDENT xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
	<DATE_OF_BIRTH xsi:nil="true" />
	<DATE_OF_BIRTH_EUROPEANFORMAT xsi:nil="true" />
</STUDENT>
  1.  -- yields: assuming value of DATE_OF_BIRTH is blank or null yields 
  2.  <STUDENT xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
  3.      <DATE_OF_BIRTH xsi:nil="true" /> 
  4.      <DATE_OF_BIRTH_EUROPEANFORMAT xsi:nil="true" /> 
  5.  </STUDENT> 

Done!

Category: XML Stylesheet Language Transformations :: Article: 578

Related Articles

Joes Revolver Map

Joes Word Cloud

Accreditation

Badge - Certified Zoho Creator Associate
Badge - Certified Zoho Creator Associate

Donate & Support

If you like my content, and would like to support this sharing site, feel free to donate using a method below:

Paypal:
Donate to Joel Lipman via PayPal

Bitcoin:
Donate to Joel Lipman with Bitcoin - Valid till 8 May 2022 3QnhmaBX7LQSRsC9hh6Je9rGQKEGNQNfPb
© 2021 Joel Lipman .com. All Rights Reserved.