Victor
Victor

Reputation: 333

XSLT condition to check if node exists

I have below sample XML in which I want to check if the node exist.

Sample XML is

<document>
<item>
    <ID>1000909090</ID>
    <flex>
        <attrGroupMany name="pageinfo">
            <row>
                <attr name="pagelength">10</attr>
                <attr name="pagewidth">20</attr>
                <attr name="pageheight">30</attr>
            </row>
            <row>
                <attr name="pagelength">10</attr>
                <attr name="pagewidth">20</attr>
            </row>
        </attrGroupMany>
    </flex>
</item>
</document>

I am using below XSLT but is not getting converted

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:template match="document">
    <CatalogItem>
        <RelationshipData>
            <xsl:for-each select="item">
                <Relationship>
                    <RelationType>PAGEDETAILSINFO</RelationType>
                    <RelatedItems>
                        <xsl:attribute name="count">
                            <xsl:value-of select="count(flex//attrGroupMany[@name = 'pageinfo']/row)"/>
                        </xsl:attribute>
                        <xsl:for-each select="flex//attrGroupMany[@name = 'pageinfo']/row">
                            <xsl:choose>
                                <xsl:when test="not(string-length(attr[@name = 'pageheight'] )) = 0">
                                    <xsl:variable name="height" select="attr[@name='pageheight']"/>
                                </xsl:when>                                                         
                                <xsl:otherwise>
                                    <xsl:variable name="height" select="'0'"/>
                                </xsl:otherwise>
                            </xsl:choose> 
                            <RelatedItem>
                                <xsl:attribute name="referenceKey">
                                    <xsl:value-of select="concat('Food_And_Bev_Allergen_MVL','-',ancestor::item/ID,'-',$height)"/>
                                </xsl:attribute>
                            </RelatedItem>
                        </xsl:for-each>
                    </RelatedItems>
                </Relationship>
            </xsl:for-each>
        </RelationshipData>
    </CatalogItem>
</xsl:template>
</xsl:stylesheet>

I think the choose condition is incorrect. can you please let me know what will be the correct condition.

Upvotes: 1

Views: 11732

Answers (2)

kjhughes
kjhughes

Reputation: 111541

You might want to adjust the default logic as Stuart suggests, but the main problem is that the height variable scope must be corrected:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="xml" indent="yes" />
  <xsl:template match="document">
    <CatalogItem>
      <RelationshipData>
        <xsl:for-each select="item">
          <Relationship>
            <RelationType>PAGEDETAILSINFO</RelationType>
            <RelatedItems>
              <xsl:attribute name="count">
                <xsl:value-of select="count(flex//attrGroupMany[@name = 'pageinfo']/row)"/>
              </xsl:attribute>
              <xsl:for-each select="flex//attrGroupMany[@name = 'pageinfo']/row">
                <xsl:variable name="height">
                  <xsl:choose>
                    <xsl:when test="not(attr[@name = 'pageheight'])">                                       
                      <xsl:value-of select="'0'"/>
                    </xsl:when>                                                         
                    <xsl:otherwise>                                     
                      <xsl:value-of select="attr[@name='pageheight']"/>
                    </xsl:otherwise>
                  </xsl:choose>
                </xsl:variable>
                <RelatedItem>
                  <xsl:attribute name="referenceKey">
                    <xsl:value-of select="concat('Food_And_Bev_Allergen_MVL','-',ancestor::item/ID,'-',$height)"/>
                  </xsl:attribute>
                </RelatedItem>
              </xsl:for-each>
            </RelatedItems>
          </Relationship>
        </xsl:for-each>
      </RelationshipData>
    </CatalogItem>
  </xsl:template>
</xsl:stylesheet>

After making the above changes, this input XML:

<?xml version="1.0" encoding="UTF-8"?>
<document>
  <item>
    <ID>1000909090</ID>
    <flex>
      <attrGroupMany name="pageinfo">
        <row>
          <attr name="pagelength">10</attr>
          <attr name="pagewidth">20</attr>
          <attr name="pageheight">30</attr>
        </row>
        <row>
          <attr name="pagelength">10</attr>
          <attr name="pagewidth">20</attr>
        </row>
      </attrGroupMany>
    </flex>
  </item>
</document>

Will transform to this output XML:

<?xml version="1.0" encoding="UTF-8"?>
<CatalogItem>
   <RelationshipData>
      <Relationship>
         <RelationType>PAGEDETAILSINFO</RelationType>
         <RelatedItems count="2">
            <RelatedItem referenceKey="Food_And_Bev_Allergen_MVL-1000909090-30"/>
            <RelatedItem referenceKey="Food_And_Bev_Allergen_MVL-1000909090-0"/>
         </RelatedItems>
      </Relationship>
   </RelationshipData>
</CatalogItem>

Edit

Note that the following is a more natural expression of the default logic:

            <xsl:variable name="height">
              <xsl:choose>
                <xsl:when test="attr[@name = 'pageheight']">
                  <xsl:value-of select="attr[@name='pageheight']"/>
                </xsl:when>
                <xsl:otherwise>0</xsl:otherwise>
              </xsl:choose>
            </xsl:variable>

Edit 2

Or, if you're using XSLT 2.0, you can use the sequence idiom for default values very concisely:

            <xsl:variable name="height" 
                          select="(attr[@name='pageheight'], 0)[1]"/>

Upvotes: 3

StuartLC
StuartLC

Reputation: 107247

To determine whether the attribute is present or not, simply use not() when the element is current.

I would reverse the logic, viz move the not present first:

<xsl:when test="not(attr[@name='pageheight'])">
  ... not present, default it 

Will detect if the row has no attr elements with a @name attribute.

Edit

You can't assign <xsl:variable> in a branch like that - it will go out of scope within the xsl:choose. Switch to:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="xml" indent="yes" />
  <xsl:template match="document">
    <CatalogItem>
      <RelationshipData>
        <xsl:for-each select="item">
          <Relationship>
            <RelationType>PAGEDETAILSINFO</RelationType>
            <RelatedItems>
              <xsl:attribute name="count">
                <xsl:value-of select="count(flex//attrGroupMany[@name = 'pageinfo']/row)"/>
              </xsl:attribute>
              <xsl:for-each select="flex//attrGroupMany[@name = 'pageinfo']/row">
                <xsl:variable name="height">
                  <xsl:choose>
                    <xsl:when test="not(attr[@name = 'pageheight'])">
                      <xsl:text>0</xsl:text>
                    </xsl:when>
                    <xsl:otherwise>
                      <xsl:value-of select="attr[@name='pageheight']"/>
                    </xsl:otherwise>
                  </xsl:choose>
                </xsl:variable>
                <RelatedItem>
                  <xsl:attribute name="referenceKey">
                    <xsl:value-of select="concat('Food_And_Bev_Allergen_MVL','-',ancestor::item/ID,'-',$height)"/>
                  </xsl:attribute>
                </RelatedItem>
              </xsl:for-each>
            </RelatedItems>
          </Relationship>
        </xsl:for-each>
      </RelationshipData>
    </CatalogItem>
  </xsl:template>
</xsl:stylesheet>

And the returned snippet includes:

  <RelatedItems count="2">
    <RelatedItem referenceKey="Food_And_Bev_Allergen_MVL-1000909090-30" />
    <RelatedItem referenceKey="Food_And_Bev_Allergen_MVL-1000909090-0" />
  </RelatedItems>

Upvotes: 3

Related Questions