XSLT Exclude Empty Elements

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?
chenegarAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Gertone (Geert Bormans)Information ArchitectCommented:
<?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

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Gertone (Geert Bormans)Information ArchitectCommented:
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 :-)
chenegarAuthor 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.
chenegarAuthor 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 ArchitectCommented:
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
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
XML

From novice to tech pro — start learning today.