0
votes

I need to output outbuilding nodes (only and only the ones that have a 'vacant'-child) with correct sequence number (see XML)

There are no attributes in XML below, just elements and values.

<stuff>
    <locations>
        <location>
            <properties>
                <property>
                    <belongings>
                        <house>
                            <houseWithAC/>
                        </house>
                    </belongings>
                </property>
                <property>
                    <belongings>
                        <outbuilding/>
                    </belongings>
                </property>
                <property>
                    <belongings>
                        <outbuilding>
                            <vacant/>
                        </outbuilding>
                    </belongings>
                </property>
                <property>              
                    <belongings>
                        <outbuilding>
                            <vacant/>
                        </outbuilding>
                    </belongings>
                </property>
                <property>
                    <belongings>
                        <vehicle/>
                    </belongings>
                </property>
            </properties>
        </location>
        <location>
            <properties>
                <property>
                    <belongings>
                        <vehicle/>
                    </belongings>
                </property>
                <property>
                   <belongings>
                       <outbuilding/>
                   </belongings>
                </property>
                <property>
                    <belongings>
                        <house/>
                    </belongings>
                </property>
                <property>
                     <belongings>
                        <outbuilding>
                            <vacant/>
                        </outbuilding>
                     </belongings>
                 </property>
                 <property>
                     <belongings>
                         <barn/>
                     </belongings>
                 </property>
             </properties>
          </location>
      </locations>
</stuff>
  1. sequencing: I need to count every belonging child node per location (for ex. house, outbuilding, vehicle) Note, house with houseWithAC child counts as two nodes !! the sequence format is Loc ### / Item ###

  2. I need to output every outbuilding node that has vacant child, with the correct sequence numbers (see above)

Note also: "collection" nodes like locations, properties have many children, while node belongings - only one.

I tried to create a recursive loop, but id does not work: if I have outbuilding node without "vacant"-child I still step into the if statement (looks like that condition is allways true)

Something like this: . . . . . . . . . . .

<xsl:for-each select="Locations/Location">
    <xsl:variable name="LOCID">
        <xsl:number level="single" count="*" format="001"/>
    </xsl:variable>
   <xsl:call-template name="WriteItems">
       <xsl:with-param name="propertiesNodes" select="properties/property"/>
       <xsl:with-param name="NumberOfProperties" select="count(properties/property)"/>
       <xsl:with-param name="LocCount" select="$LOCID"/>
   </xsl:call-template>
</xsl:for-each>     
. . . . . . . . . . . . .


<xsl:template name="WriteItems">
    <xsl:param name="propertiesNodes"/>
    <xsl:param name="NumberOfProperties"/>
    <xsl:param name="LocCount"/>
    <xsl:param name="Index">1</xsl:param>
    <xsl:param name="ItemCount">1</xsl:param>     
    <xsl:choose>
        <xsl:when test="$Index > $NumberOfProperties"/>
         <xsl:otherwise>
           <xsl:choose>
               <xsl:when test="$propertiesNodes[$Index]/belongings/house/houseWithAC">
                  <xsl:call-template name="WriteItems">
                      <xsl:with-param name="propertiesNodes" select="$propertiesNodes"/>
                      <xsl:with-param name="NumberOfProperties" select="$NumberOfProperties"/>
                      <xsl:with-param name="LocCount" select="$LocCount"/>
                      <xsl:with-param name="Index" select="$Index + 1"/>
                      <xsl:with-param name="ItemCount" select="$ItemCount + 2"/>
                  </xsl:call-template>
               </xsl:when>
               <xsl:when test="$propertiesNodes[$Index]/belongings/house">
                   <xsl:call-template name="WriteItems">
                       <xsl:with-param name="propertiesNodes" select="$propertiesNodes"/>
                       <xsl:with-param name="NumberOfProperties" select="$NumberOfProperties"/>
                       <xsl:with-param name="LocCount" select="$LocCount"/>
                       <xsl:with-param name="Index" select="$Index + 1"/>
                       <xsl:with-param name="ItemCount" select="$ItemCount + 1"/>
                   </xsl:call-template>
               </xsl:when>
               <xsl:when test="$propertiesNodes[$Index]/belongings/vehicle">
                   <xsl:call-template name="WriteItems">
                       <xsl:with-param name="propertiesNodes" select="$propertiesNodes"/>
                       <xsl:with-param name="NumberOfProperties" select="$NumberOfProperties"/>
                       <xsl:with-param name="LocCount" select="$LocCount"/>
                       <xsl:with-param name="Index" select="$Index + 1"/>
                       <xsl:with-param name="ItemCount" select="$ItemCount + 1"/>
                   </xsl:call-template>
               </xsl:when>
               <xsl:when test="$propertiesNodes[$Index]/belongings/barn">
                   <xsl:call-template name="WriteItems">
                       <xsl:with-param name="propertiesNodes" select="$propertiesNodes"/>
                       <xsl:with-param name="NumberOfProperties" select="$NumberOfProperties"/>
                       <xsl:with-param name="LocCount" select="$LocCount"/>
                       <xsl:with-param name="Index" select="$Index + 1"/>
                       <xsl:with-param name="ItemCount" select="$ItemCount + 1"/>
                   </xsl:call-template>
               </xsl:when>
               <xsl:when test="$propertiesNodes[$Index]/belongings/outbuilding">
                  <xsl:if test="$propertiesNodes[$Index]/belongings/outbuilding/vacant">
                        <xsl:variable name="ITEMID">
                             <xsl:value-of select="format-number($ItemCount,'000')"/>
                        </xsl:variable>  

                         .... output as  concat('-  ', $LocCount, '/', $ITEMID) ...... some description ....


                   </xsl:if>
                   <xsl:call-template name="WriteItems">
                       <xsl:with-param name="propertiesNodes" select="$propertiesNodes"/>
                       <xsl:with-param name="NumberOfProperties" select="$NumberOfProperties"/>
                       <xsl:with-param name="LocCount" select="$LocCount"/>
                       <xsl:with-param name="Index" select="$Index + 1"/>
                       <xsl:with-param name="ItemCount" select="$ItemCount + 1"/>
                   </xsl:call-template>
               </xsl:when>
           </xsl:choose>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>
1
This is very confusing and inexact. There isn't such terminology as "collection nodes" and it is impossible to guess what you mean by this. If you provide the exact wanted results, people may be better able to guess what is actually wanted.Dimitre Novatchev
Can you show (1) the desired output XML for your sample input; and (2) the actual output XML you're getting would be helpful.LarsH
I used double quotes: "collection" nodes. Those are the nodes that have multiple children of the same kind.user1540823
The desired output will not be in XML, but eventually in pdf format. I just want to understand better this logic. The output should have: 001 / 004 ... something... 001 / 005 ... something... 002 / 004 ... something else ...user1540823
What is the output if it's not XML? Text? "eventually in pdf format" doesn't really matter...what matters is current output of the stylesheet you are using and how that differs from your desired output.Daniel Haley

1 Answers

2
votes

I am not sure whether I understood your question correctly, but if this is what you want:

For each vacant "item", output:

  1. the position of the containing location within all locations, and
  2. the position of the "item" within all "items" in the containing location

Then you could achieve this by looping over the locations and "items" and using the position() function to tell the position when a match is found. Like this:

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>

  <xsl:template match="/">
    <xsl:for-each select="//location">
      <xsl:variable name="locposition" select="position()"/>
      <xsl:for-each select=".//belongings//*[not(self::vacant)]">
        <xsl:if test="vacant">
          <xsl:value-of select="format-number($locposition, '000')"/>
          <xsl:text> / </xsl:text>
          <xsl:value-of select="format-number(position(), '000')"/>
          <xsl:text>&#xa;</xsl:text>
        </xsl:if>
      </xsl:for-each>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

With your example input, this will output the following:

001 / 004
001 / 005
002 / 004

Edited to add: Here is an alternative solution using xsl:number which avoids explicit looping:

<xsl:stylesheet version="1.0"
                    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>

  <xsl:template match="*[vacant]">
    <xsl:number count="location" format="001"/> 
    <xsl:text> / </xsl:text>
    <xsl:number count="*[not(self::vacant)][ancestor::belongings]"
                level="any" from="location" format="001"/>
    <xsl:text>&#xa;</xsl:text>
  </xsl:template>

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

</xsl:stylesheet>