Frage

A co-worker and I have been given a new requirement. Please review the following.

We have a requirement to transform an XML source document while enforcing a numerical limit on certain items in the result document. A source document, an XSLT stylesheet, and a result document are reproduced below. This result document, however, does not reflect the desired numerical limit, because this stylesheet transforms the source document without any restriction.

At the end, our question will be: How can the desired numerical limit be enforced in the stylesheet?

Source document:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<T:Message xmlns="http://www.noname.com/Platform" xmlns:T="http://www.noname.com/Transportation">
   <T:ShipmentListMessage>
       <Shipment>
           <ShipmentNumber>ABC</ShipmentNumber>
           <PickupCommodityCode>Hazmat</PickupCommodityCode>
           <DeliveryCommodityCode>Hazmat</DeliveryCommodityCode>
           <ShipmentLine>
               <ShipmentLineNumber>ABC-1</ShipmentLineNumber>
               <OrderLineItemNumber>1</OrderLineItemNumber>
           </ShipmentLine>
           <ShipmentLine>
               <ShipmentLineNumber>ABC-2</ShipmentLineNumber>
               <OrderLineItemNumber>2</OrderLineItemNumber>
           </ShipmentLine>
           <ShipmentLine>
               <ShipmentLineNumber>ABC-3</ShipmentLineNumber>
               <OrderLineItemNumber>3</OrderLineItemNumber>
           </ShipmentLine>
       </Shipment>
       <Shipment>
           <ShipmentNumber>DEF</ShipmentNumber>
           <PickupCommodityCode>Hazmat</PickupCommodityCode>
           <DeliveryCommodityCode>Hazmat</DeliveryCommodityCode>
           <ShipmentLine>
               <ShipmentLineNumber>DEF-1</ShipmentLineNumber>
               <OrderLineItemNumber>1</OrderLineItemNumber>
           </ShipmentLine>
           <ShipmentLine>
               <ShipmentLineNumber>DEF-2</ShipmentLineNumber>
               <OrderLineItemNumber>2</OrderLineItemNumber>
           </ShipmentLine>
       </Shipment>
       <Shipment>
           <ShipmentNumber>GHI</ShipmentNumber>
           <PickupCommodityCode>Hazmat</PickupCommodityCode>
           <DeliveryCommodityCode>Hazmat</DeliveryCommodityCode>
           <ShipmentLine>
               <ShipmentLineNumber>GHI-1</ShipmentLineNumber>
               <OrderLineItemNumber>1</OrderLineItemNumber>
           </ShipmentLine>
           <ShipmentLine>
               <ShipmentLineNumber>GHI-2</ShipmentLineNumber>
               <OrderLineItemNumber>2</OrderLineItemNumber>
           </ShipmentLine>
           <ShipmentLine>
               <ShipmentLineNumber>GHI-3</ShipmentLineNumber>
               <OrderLineItemNumber>3</OrderLineItemNumber>
           </ShipmentLine>
           <ShipmentLine>
               <ShipmentLineNumber>GHI-4</ShipmentLineNumber>
               <OrderLineItemNumber>4</OrderLineItemNumber>
           </ShipmentLine>
       </Shipment>
   </T:ShipmentListMessage>
   <T:MovementListMessage>
       <Movement>
           <MovementStop>
               <StopSequenceNumber>0</StopSequenceNumber>
               <MovementStopLine>
                   <OperationType>Pickup</OperationType>
                   <ShipmentNumber>ABC</ShipmentNumber>
               </MovementStopLine>
               <MovementStopLine>
                   <OperationType>Pickup</OperationType>
                   <ShipmentNumber>DEF</ShipmentNumber>
               </MovementStopLine>
               <MovementStopLine>
                   <OperationType>Pickup</OperationType>
                   <ShipmentNumber>GHI</ShipmentNumber>
               </MovementStopLine>
           </MovementStop>
           <MovementStop>
               <StopSequenceNumber>1</StopSequenceNumber>
               <MovementStopLine>
                   <OperationType>Deliver</OperationType>
                   <ShipmentNumber>ABC</ShipmentNumber>
               </MovementStopLine>
           </MovementStop>
           <MovementStop>
               <StopSequenceNumber>2</StopSequenceNumber>
               <MovementStopLine>
                   <OperationType>Deliver</OperationType>
                   <ShipmentNumber>DEF</ShipmentNumber>
               </MovementStopLine>
               <MovementStopLine>
                   <OperationType>Deliver</OperationType>
                   <ShipmentNumber>GHI</ShipmentNumber>
               </MovementStopLine>
           </MovementStop>
       </Movement>
   </T:MovementListMessage>
</T:Message>

XSLT Stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:t="http://www.noname.com/Transportation" xmlns:vc="http://www.noname.com/Platform"> 
 <xsl:output method="text" indent="no"/>
 <xsl:template match="t:Message">
   <xsl:variable name="shipmentPath" select="t:ShipmentListMessage/vc:Shipment"/>
   <xsl:variable name="movementPath" select="t:MovementListMessage/vc:Movement"/>
   <xsl:variable name="numDeliverLocations" select="count($movementPath/vc:MovementStop/vc:MovementStopLine[vc:OperationType = 'Deliver'])"/>
   <xsl:variable name="numOffers" select="count($shipmentPath)"/>
   <xsl:for-each select="$movementPath/vc:MovementStop">
       <xsl:for-each select="vc:MovementStopLine[vc:OperationType = 'Deliver']">
           <xsl:variable name="OfferNumber" select="vc:ShipmentNumber"/>
           <xsl:variable name="OfferCount" select="count(../vc:MovementStopLine[vc:OperationType = 'Deliver'])"/>
           <xsl:if test="position() = 1">
S5*<xsl:value-of select="../vc:StopSequenceNumber+1"/>*UL<xsl:text>~</xsl:text>
           </xsl:if>
       </xsl:for-each>
       <xsl:for-each select="vc:MovementStopLine[vc:OperationType = 'Deliver']">
           <xsl:variable name="OfferNumber" select="vc:ShipmentNumber"/>
           <xsl:variable name="OfferCount" select="count(../vc:MovementStopLine[vc:OperationType = 'Deliver'])"/>
           <xsl:variable name="OfferPosition" select="if (position() lt 10) then position() else 0"/>
           <xsl:for-each select="$shipmentPath[vc:ShipmentNumber = $OfferNumber and vc:DeliveryCommodityCode != '']">
               <xsl:choose>
                   <xsl:when test="vc:DeliveryCommodityCode = 'Hazmat'">
                       <xsl:if test="$numOffers gt 1 or $numDeliverLocations = 1">
                           <xsl:for-each select="vc:ShipmentLine">
                               <xsl:variable name="ShipmentLine" select="vc:ShipmentLineNumber"/>
                               <xsl:choose>
                                   <xsl:when test="$OfferCount eq 1">
L5*<xsl:value-of select="vc:OrderLineItemNumber"/><xsl:text>~</xsl:text>
                                   </xsl:when>
                                   <xsl:when test="position() lt 10">
L5*<xsl:value-of select="concat($OfferPosition, '0', vc:OrderLineItemNumber)"/><xsl:text>~</xsl:text>
                                   </xsl:when>
                                   <xsl:otherwise>
L5*<xsl:value-of select="concat($OfferPosition, vc:OrderLineItemNumber)"/><xsl:text>~</xsl:text>
                                   </xsl:otherwise>
                               </xsl:choose>
                           </xsl:for-each>
                       </xsl:if>
                   </xsl:when>
               </xsl:choose>
           </xsl:for-each>
       </xsl:for-each>
       <xsl:for-each select="vc:MovementStopLine[vc:OperationType = 'Pickup']">
           <xsl:variable name="OfferNumber" select="vc:ShipmentNumber"/>
           <xsl:variable name="OfferCount" select="count(../vc:MovementStopLine[vc:OperationType = 'Pickup'])"/>
           <xsl:if test="position() = 1">
S5*<xsl:value-of select="../vc:StopSequenceNumber+1"/>*LD<xsl:text>~</xsl:text>
           </xsl:if>
       </xsl:for-each>
       <xsl:for-each select="vc:MovementStopLine[vc:OperationType = 'Pickup']">
           <xsl:variable name="OfferNumber" select="vc:ShipmentNumber"/>
           <xsl:variable name="OfferCount" select="count(../vc:MovementStopLine[vc:OperationType = 'Pickup'])"/>
           <xsl:variable name="OfferPosition" select="if (position() lt 10) then position() else 0"/>
           <xsl:for-each select="$shipmentPath[vc:ShipmentNumber = $OfferNumber and vc:PickupCommodityCode != '']">
               <xsl:choose>
                   <xsl:when test="vc:PickupCommodityCode = 'Hazmat'">
                       <xsl:for-each select="vc:ShipmentLine">
                           <xsl:variable name="ShipmentLine" select="vc:ShipmentLineNumber"/>
                           <xsl:choose>
                               <xsl:when test="$OfferCount eq 1">
L5*<xsl:value-of select="vc:OrderLineItemNumber"/><xsl:text>~</xsl:text>
                               </xsl:when>
                               <xsl:when test="position() lt 10">
L5*<xsl:value-of select="concat($OfferPosition, '0', vc:OrderLineItemNumber)"/><xsl:text>~</xsl:text>
                               </xsl:when>
                               <xsl:otherwise>
L5*<xsl:value-of select="concat($OfferPosition, vc:OrderLineItemNumber)"/><xsl:text>~</xsl:text>
                               </xsl:otherwise>
                           </xsl:choose>
                       </xsl:for-each>
                   </xsl:when>
               </xsl:choose>
           </xsl:for-each>
       </xsl:for-each>
   </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

Result document:

S5*1*LD~
L5*101~
L5*102~
L5*103~
L5*201~
L5*202~
L5*301~
L5*302~
L5*303~
L5*304~
S5*2*UL~
L5*1~
L5*2~
L5*3~
S5*3*UL~
L5*101~
L5*102~
L5*201~
L5*202~
L5*203~
L5*204~

In order to satisfy our requirement, the numerical limit must somehow be built into the stylesheet. For the purpose of this discussion, the desired limit is 5 on the number of “L5” segments following an “S5” segment. In this example, the limit will reduce the number of “L5” segments to 5 each following “S5*1*LD” and “S5*3*UL”. The limit will not come into play in the case of “S5*2*UL”.

Desired result document with the “L5” limit applied:

S5*1*LD~
L5*101~
L5*102~
L5*103~
L5*201~
L5*202~
S5*2*UL~
L5*1~
L5*2~
L5*3~
S5*3*UL~
L5*101~
L5*102~
L5*201~
L5*202~
L5*203~

Again, our question is: How can the desired numerical limit be coded in the stylesheet?

War es hilfreich?

Lösung

I think you could store the first result in a variable and then group and count as needed, for easier processing I have chosen to wrap the intermediary result as XML:

<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:t="http://www.noname.com/Transportation" xmlns:vc="http://www.noname.com/Platform"
 exclude-result-prefixes="t vc"> 

 <xsl:output method="text"/>

 <xsl:template match="t:Message">
   <xsl:variable name="shipmentPath" select="t:ShipmentListMessage/vc:Shipment"/>
   <xsl:variable name="movementPath" select="t:MovementListMessage/vc:Movement"/>
   <xsl:variable name="numDeliverLocations" select="count($movementPath/vc:MovementStop/vc:MovementStopLine[vc:OperationType = 'Deliver'])"/>
   <xsl:variable name="numOffers" select="count($shipmentPath)"/>

   <xsl:variable name="items">
     <xsl:for-each select="$movementPath/vc:MovementStop">
         <xsl:for-each select="vc:MovementStopLine[vc:OperationType = 'Deliver']">
             <xsl:variable name="OfferNumber" select="vc:ShipmentNumber"/>
             <xsl:variable name="OfferCount" select="count(../vc:MovementStopLine[vc:OperationType = 'Deliver'])"/>
             <xsl:if test="position() = 1">
               <item>S5*<xsl:value-of select="../vc:StopSequenceNumber+1"/>*UL<xsl:text>~</xsl:text></item>
             </xsl:if>
         </xsl:for-each>
         <xsl:for-each select="vc:MovementStopLine[vc:OperationType = 'Deliver']">
             <xsl:variable name="OfferNumber" select="vc:ShipmentNumber"/>
             <xsl:variable name="OfferCount" select="count(../vc:MovementStopLine[vc:OperationType = 'Deliver'])"/>
             <xsl:variable name="OfferPosition" select="if (position() lt 10) then position() else 0"/>
             <xsl:for-each select="$shipmentPath[vc:ShipmentNumber = $OfferNumber and vc:DeliveryCommodityCode != '']">
                 <xsl:choose>
                     <xsl:when test="vc:DeliveryCommodityCode = 'Hazmat'">
                         <xsl:if test="$numOffers gt 1 or $numDeliverLocations = 1">
                             <xsl:for-each select="vc:ShipmentLine">
                                 <xsl:variable name="ShipmentLine" select="vc:ShipmentLineNumber"/>
                                 <xsl:choose>
                                     <xsl:when test="$OfferCount eq 1">
                                       <item>L5*<xsl:value-of select="vc:OrderLineItemNumber"/><xsl:text>~</xsl:text></item>
                                     </xsl:when>
                                     <xsl:when test="position() lt 10">
                                       <item>L5*<xsl:value-of select="concat($OfferPosition, '0', vc:OrderLineItemNumber)"/><xsl:text>~</xsl:text></item>
                                     </xsl:when>
                                     <xsl:otherwise>
                                       <item>L5*<xsl:value-of select="concat($OfferPosition, vc:OrderLineItemNumber)"/><xsl:text>~</xsl:text></item>
                                     </xsl:otherwise>
                                 </xsl:choose>
                             </xsl:for-each>
                         </xsl:if>
                     </xsl:when>
                 </xsl:choose>
             </xsl:for-each>
         </xsl:for-each>
         <xsl:for-each select="vc:MovementStopLine[vc:OperationType = 'Pickup']">
             <xsl:variable name="OfferNumber" select="vc:ShipmentNumber"/>
             <xsl:variable name="OfferCount" select="count(../vc:MovementStopLine[vc:OperationType = 'Pickup'])"/>
             <xsl:if test="position() = 1">
               <item>S5*<xsl:value-of select="../vc:StopSequenceNumber+1"/>*LD<xsl:text>~</xsl:text></item>
             </xsl:if>
         </xsl:for-each>
         <xsl:for-each select="vc:MovementStopLine[vc:OperationType = 'Pickup']">
             <xsl:variable name="OfferNumber" select="vc:ShipmentNumber"/>
             <xsl:variable name="OfferCount" select="count(../vc:MovementStopLine[vc:OperationType = 'Pickup'])"/>
             <xsl:variable name="OfferPosition" select="if (position() lt 10) then position() else 0"/>
             <xsl:for-each select="$shipmentPath[vc:ShipmentNumber = $OfferNumber and vc:PickupCommodityCode != '']">
                 <xsl:choose>
                     <xsl:when test="vc:PickupCommodityCode = 'Hazmat'">
                         <xsl:for-each select="vc:ShipmentLine">
                             <xsl:variable name="ShipmentLine" select="vc:ShipmentLineNumber"/>
                             <xsl:choose>
                                 <xsl:when test="$OfferCount eq 1">
                                   <item>L5*<xsl:value-of select="vc:OrderLineItemNumber"/><xsl:text>~</xsl:text></item>
                                 </xsl:when>
                                 <xsl:when test="position() lt 10">
                                   <item>L5*<xsl:value-of select="concat($OfferPosition, '0', vc:OrderLineItemNumber)"/><xsl:text>~</xsl:text></item>
                                 </xsl:when>
                                 <xsl:otherwise>
                                   <item>L5*<xsl:value-of select="concat($OfferPosition, vc:OrderLineItemNumber)"/><xsl:text>~</xsl:text></item>
                                 </xsl:otherwise>
                             </xsl:choose>
                         </xsl:for-each>
                     </xsl:when>
                 </xsl:choose>
             </xsl:for-each>
         </xsl:for-each>
     </xsl:for-each>
   </xsl:variable>

   <xsl:for-each-group select="$items/item" group-starting-with="item[starts-with(., 'S5*')]">
     <xsl:if test="position() gt 1"><xsl:text>&#10;</xsl:text></xsl:if>
     <xsl:value-of select="if (starts-with(., 'S5*1*LD') or starts-with(., 'S5*3*UL'))
                           then current-group()[position() le 6] else current-group()"
                   separator="&#10;"/>
    </xsl:for-each-group>
 </xsl:template>
</xsl:stylesheet>

With that stylesheet and your input Saxon 9.5 outputs

S5*1*LD~
L5*101~
L5*102~
L5*103~
L5*201~
L5*202~
S5*2*UL~
L5*1~
L5*2~
L5*3~
S5*3*UL~
L5*101~
L5*102~
L5*201~
L5*202~
L5*203~
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top