XSLT Exclude Empty Elements

chenegar
chenegar used Ask the Experts™
on
I'm a rank amateur when it comes to XSLT. I have a stylesheet that works except that I need to exclude empty elements. It's a long stylesheet, so here is an abbreviated version.
<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:strip-space elements="*"/>
  <xsl:output method = "xml" indent = "yes" />

  <!--
  This is a transform to create an XML file from the ANX file output by the Debt Collection HotDocs intake.
  -->
  
  <xsl:template match="/AnswerSet"> 

	<xsl:element name="OnlineIntake">
.
.
.
                 <xsl:for-each select="//Answer[@name='Factor']/RptValue/MCValue">

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

			<xsl:element name="Factors">			
				<xsl:element name="Factor">					
						<xsl:value-of select="//Answer[@name='Factor']//MCValue[$getfactors]"/>		
				</xsl:element>			
				<xsl:element name="FactorAmt">
					<xsl:value-of select="//Answer[@name='FactorAmount']//NumValue[$getfactors]"/>
				</xsl:element>				
			</xsl:element>  <!-- Factors -->		
		</xsl:for-each> <!-- end of foreach for Factors -->

Open in new window


The input looks like this
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<AnswerSet title="" version="1.1">
	<Answer name="(ANSWER FILE HISTORY)">
		<TextValue>Debt Collection Intake : May 6, 2015, 15:15
Maintenance Interview : February 18, 2015, 15:34
</TextValue>
.
.
.
<Answer name="Factor">
		<RptValue>
			<MCValue>
				<SelValue>Child Care/Work-Related</SelValue>
			</MCValue>
			<MCValue>
				<SelValue>Current income taxes</SelValue>
			</MCValue>
			<MCValue>
				<SelValue>Mortgage payments</SelValue>
			</MCValue>
			<MCValue>
				<SelValue>Rent</SelValue>
			</MCValue>
			<MCValue>
				<SelValue>Transportation Expenses/Work-Related</SelValue>
			</MCValue>
			<MCValue>
				<SelValue>Tools &amp; Equipment/Work-Related</SelValue>
			</MCValue>
			<MCValue unans="true"/>
		</RptValue>
	</Answer>
<Answer name="Factor">
		<RptValue>
			<MCValue>
				<SelValue>Child Care/Work-Related</SelValue>
			</MCValue>
			<MCValue>
				<SelValue>Current income taxes</SelValue>
			</MCValue>
			<MCValue>
				<SelValue>Mortgage payments</SelValue>
			</MCValue>
			<MCValue>
				<SelValue>Rent</SelValue>
			</MCValue>
			<MCValue>
				<SelValue>Transportation Expenses/Work-Related</SelValue>
			</MCValue>
			<MCValue>
				<SelValue>Tools &amp; Equipment/Work-Related</SelValue>
			</MCValue>
			<MCValue unans="true"/>
		</RptValue>
</AnswerSet>

Open in new window

And the output looks like this
<Factors>
    <Factor>Child Care/Work-Related</Factor>
    <FactorAmt>100.0000000</FactorAmt>
  </Factors>
  <Factors>
    <Factor>Current income taxes</Factor>
    <FactorAmt>250.0000000</FactorAmt>
  </Factors>
  <Factors>
    <Factor>Mortgage payments</Factor>
    <FactorAmt>600.0000000</FactorAmt>
  </Factors>
  <Factors>
    <Factor>Rent</Factor>
    <FactorAmt>0.0000000</FactorAmt>
  </Factors>
  <Factors>
    <Factor>Transportation Expenses/Work-Related</Factor>
    <FactorAmt>100.0000000</FactorAmt>
  </Factors>
  <Factors>
    <Factor>Tools &amp; Equipment/Work-Related</Factor>
    <FactorAmt>75.0000000</FactorAmt>
  </Factors>
  <Factors>
    <Factor/>
    <FactorAmt/>
  </Factors>

Open in new window

The output should not have the last empty <Factors> elements. Everything I've tried so far either crashes the transform or skips the Factors output. I can't change the input file, so can one of you experts help?
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Information Architect
Top Expert 2006
Commented:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">
    
    <xsl:strip-space elements="*"/>
    <xsl:output indent="yes"/>
    
    <xsl:template match="/AnswerSet"> 
        <xsl:element name="OnlineIntake">
            <xsl:apply-templates select="//Answer[@name='Factor']/RptValue/MCValue"></xsl:apply-templates>
        </xsl:element>
    </xsl:template>
    
    <xsl:template match="Answer[@name='Factor']/RptValue/MCValue">
        <xsl:variable name="getfactors" select="position()"/>
        <xsl:variable name="amt" select="(//Answer[@name='FactorAmount'])[$getfactors]//NumValue"></xsl:variable>
        <xsl:if test="normalize-space(SelValue) or normalize-space($amt)">
            <xsl:element name="Factors">			
                <xsl:element name="Factor">					
                    <xsl:value-of select="SelValue"/>		
                </xsl:element>			
                <xsl:element name="FactorAmt">
                    <xsl:value-of select="$amt"/>
                </xsl:element>				
            </xsl:element> 
        </xsl:if>
    </xsl:template>
    
</xsl:stylesheet>

Open in new window

Gertone (Geert Bormans)Information Architect
Top Expert 2006

Commented:
The short answer is... add a simple xsl:if before you output the element to the result tree

But I saw some other ways of improvement
jumping up the tree and come back to basically the same element node is expensive
Your source XML had obvious errors because you cut away some stuff,
but basically I think you need to add brackets and positional predicates at the right place to address the correct node

And I restructured a bit... you should use apply-templates over for-each whenever possible (and that means almost always :-)

Author

Commented:
Thank you, Geert. I knew I needed to use apply-template but I couldn't figure out how to write it. And I love the simplicity of your answer! I inherited the transform and I plan to re-write all of it when I get a chance because I know it's kind of kludgy.

The code you gave me works for getting all the Factors but it's only picking up the first FactorAmt. I've been trying to understand the code you wrote but I can't figure out what I need to add to get all the FactorAmts.

Author

Commented:
Ah, I've got it! I made one change in your code and I got all of the factors and their amounts. Your code was
<xsl:variable name="amt" select="(//Answer[@name='FactorAmount'])[$getfactors]//NumValue"></xsl:variable>

Open in new window

And I changed it to
<xsl:variable name="amt" select="(//Answer[@name='FactorAmount'])//NumValue[$getfactors]"></xsl:variable>

Open in new window


Thanks so much for your help!
Gertone (Geert Bormans)Information Architect
Top Expert 2006

Commented:
welcome,
I just got back so I only saw your feedback now.
I would have asked a proper source to evaluate, because that XPath depends really on the structure of the source XML. Glad you found it yourself
cheers

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial