Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check if the node set contains any value in XSLT

Tags:

xml

xslt

I need some help with XSLT syntax. Here is my scenario, I have an XML file that needs to be transformed in to a different look and feel of XML file, I have several sections where if particular node set don't contain any value the whole section shouldn't be processed.

Here is an example of XML:

<Dates>
    <Date>
        <VALUE1></VALUE1>
        <VALUE2></VALUE2>
        <VALUE3></VALUE3>
        <VALUE4></VALUE4>
        <VALUE5>3333</VALUE5>
    </Date>
    <Date>
        <VALUE1>AAAA</VALUE1>
        <VALUE2></VALUE2>
        <VALUE3>JJJJ</VALUE3>
        <VALUE4></VALUE4>
        <VALUE5>12345</VALUE5>
    </Date>
</Dates>

screenshot of xml

Here is my XSLT with the if statement that don't work right

<xsl:for-each select="Level1/Level2/Level3">
    <xsl:if test="@VALUE1!=''">                    
    <MyDates>               
            <value_1>
                <xsl:value-of select="VALUE1"/> 
            </value_1>
            <value_2>
                <xsl:value-of select="VALUE2"/> 
            </value_2>
            <value_3>
                <xsl:value-of select="VALUE3"/> 
            </value_3>
            <value_4>
                <xsl:value-of select="VALUE4"/> 
            </value_4>       
    </MyDates>
    </xsl:if>   
</xsl:for-each>

So as you can see I basically want all nodes (VALUE1, VALUE2, VALUE3, etc) to have values or else don't process and move on to the next section

(If you cannot see the XML come thought, I also made a screen shot)

like image 407
Alex Avatar asked Dec 08 '08 19:12

Alex


2 Answers

You are trying to match xml elements with names "Level1", "Level2", etc... that don't exist in the document. Then, you are looking for a VALUE1 attribute on the last element.

I think you want something like this:

<xsl:for-each select="Dates">
    <MyDates>
        <xsl:for-each select="Date">
        <xsl:if test="not(*[.=''])">
            <MyDate>
                <value_1>
                    <xsl:value-of select="VALUE1"/> 
                </value_1>
                <value_2>
                    <xsl:value-of select="VALUE2"/> 
                </value_2>
                <value_3>
                    <xsl:value-of select="VALUE3"/> 
                </value_3>
                <value_4>
                    <xsl:value-of select="VALUE4"/> 
                </value_4>               
            </MyDate>
        </xsl:if>
        </xsl:for-each>
    </MyDates>
</xsl:for-each>

This will add a new MyDate element as long as every value in the corresponding Date element is not empty.

What it does is

  1. Create a new MyDates element for each Dates element.
  2. Check each Date element. The * matches all children. [.=''] means "is empty". Finally, it wraps all that in a not. This means the if only passes if there is not any child that is empty.
  3. If it has no children that are empty, it creates a new MyDate element with a copy of each value.

You may also want to check the W3Schools XSL and XPath tutorials.

like image 177
Chris Marasti-Georg Avatar answered Oct 17 '22 10:10

Chris Marasti-Georg


You have not defined well what does it mean for "a node to have value".

Most probably, you will consider an element, that has a white space-only text child, not to have value. In this case, below is one solution:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:template match="Date[not(*[not(normalize-space())])]">
       <xsl:copy-of select="."/>
     </xsl:template>

     <xsl:template match="text()"/>
</xsl:stylesheet>

This templete for a "Date" element will not be matched if the "Date" contained even one child element, that has no content or only white space.

When this transformation is applied on the following XML document:

<Dates>
    <Date>
        <VALUE1></VALUE1>
        <VALUE2>  </VALUE2>
        <VALUE3></VALUE3>
        <VALUE4></VALUE4>
        <VALUE5>3333</VALUE5>
    </Date>
    <Date>
        <VALUE1>AAAA</VALUE1>
        <VALUE2>1</VALUE2>
        <VALUE3>JJJJ</VALUE3>
        <VALUE4>1</VALUE4>
        <VALUE5>12345</VALUE5>
    </Date>
</Dates> 

The correct result is produced:

<Date>
    <VALUE1>AAAA</VALUE1>
    <VALUE2>1</VALUE2>
    <VALUE3>JJJJ</VALUE3>
    <VALUE4>1</VALUE4>
    <VALUE5>12345</VALUE5>
</Date>
like image 22
Dimitre Novatchev Avatar answered Oct 17 '22 09:10

Dimitre Novatchev