Subject: [xsl] Output fixed length records using mod operators to group results From: Chris <ryder.roundabout@xxxxxxxxx> Date: Thu, 10 May 2007 14:30:37 -0400 |
With thanks to replies to my earlier post by David and Abel, the magic of the mod operator was pointed out. That does the trick for selecting the positioning of my output, however, I'm wrestling with the second part of my problem (which I omitted in the first post for clarity).
Each record output is a fixed length and belongs in a fixed length line. So, when I'm on the last PO or container record and it happens to not be either a multiple of 2 or 4, i still need to fill fixed length fields with spaces before closing the record with a line feed.
I can get the PO lines to group correctly (up to 2 per line), but my containers (up to 4 per line) are showing up other than expected.
My current stylesheet is pretty crude -- I'm using <xsl:choose> to see my prior and following nodes and a fixed-length formatter template to output padded results. I'm also pretty sure I'm not the most efficient on the for-each or apply-templates approach. I'd welcome any input on making this approach more elegant.
I seem to get the same results using position() as well as the count of preceding-sibling or following-sibling nodes in the tests. I assume my XPath is incorrect and my containers are stopping because they are children of the context line_item element. (3,4,2,1)
Here is my actual output (the semi-colon should be in position 51 followed by a line break): SHIPMENT 2 ; PO P000001 P000002 ; PO P000001 P000004 ; CASE 1 2 3 ; CASE 5 6 7 8 ; CASE 10 11 ; CASE 12 ;
Each record starts with a 10 position description, and each value is 10 characters long.
My desired output would be this: SHIPMENT 2 ; PO P000001 P000002 ; PO P000001 P000004 ; CASE 1 2 3 5 ; CASE 6 7 8 10 ; CASE 11 12 ;
Here is the XML source I'm using: ?xml version="1.0" encoding="UTF-8" standalone="no"?> <asn> <header> <shipper date="08-May-2007" time="13:27:08" value="2"/> </header> <detail> <line_item part="100-001" po="P000001"> <quantity shipped="1500" unit_of_measure="EA"/> <master_container serial_no="4" type="PLT90"> <detail_container container_quantity="3" parts_per_container="500" type="BOX90"> <container serial_no="1"/> <container serial_no="2"/> <container serial_no="3"/> </detail_container> </master_container> </line_item> <line_item part="100-001" po="P000002"> <quantity shipped="2000" unit_of_measure="EA"/> <master_container serial_no="9" type="PLT90"> <detail_container container_quantity="4" parts_per_container="500" type="BOX90"> <container serial_no="5"/> <container serial_no="6"/> <container serial_no="7"/> <container serial_no="8"/> </detail_container> </master_container> </line_item> <line_item part="100-002" po="P000003"> <quantity shipped="1000" unit_of_measure="EA"/> <master_container serial_no="12" type="PLT90"> <detail_container container_quantity="2" parts_per_container="500" type="BOX90"> <container serial_no="10"/> <container serial_no="11"/> </detail_container> </master_container> </line_item> <line_item part="100-003" po="P000004"> <quantity shipped="500" unit_of_measure="EA"/> <master_container serial_no="13" type="PLT90"> <detail_container container_quantity="1" parts_per_container="500" type="BOX90"> <container serial_no="12"/> </detail_container> </master_container> </line_item> </detail> </asn>
And here is my current stylesheet (long!): <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="text" />
<xsl:template match="asn" > <xsl:variable name="lineItemNodes" select="//line_item" /> <xsl:variable name="containerNodes" select="//container" /> <xsl:call-template name="shipment" /> <xsl:call-template name="po"> <xsl:with-param name="lines" select="$lineItemNodes" /> </xsl:call-template> <xsl:call-template name="cas"> <xsl:with-param name="boxes" select="$containerNodes" /> </xsl:call-template> </xsl:template>
<xsl:template name="shipment"> <xsl:text>SHIPMENT </xsl:text> <xsl:call-template name="fixLenChar"> <xsl:with-param name="strValue" select="header/shipper/@value"/> </xsl:call-template> <xsl:text> ; </xsl:text> </xsl:template>
<xsl:template name="po"> <xsl:param name="lines" /> <xsl:for-each select="$lines"> <xsl:choose> <xsl:when test="(count(preceding-sibling::line_item) + 1) mod 2 = 0"> <!-- this is an even numbered node --> <xsl:text>PO </xsl:text> <xsl:call-template name="fixLenChar"> <xsl:with-param name="strValue" select="preceding-sibling::line_item/@po" /> </xsl:call-template> <xsl:call-template name="fixLenChar"> <xsl:with-param name="strValue" select="@po" /> </xsl:call-template> <xsl:text> ; </xsl:text> </xsl:when> <xsl:when test="(count(preceding-sibling::line_item) + 1) mod 2 = 0"> <!-- this is an even numbered node --> <xsl:text>PO </xsl:text> <xsl:call-template name="fixLenChar"> <xsl:with-param name="strValue" select="@po" /> </xsl:call-template> <xsl:call-template name="fixLenChar"> <xsl:with-param name="strValue" select="''" /> </xsl:call-template> <xsl:text> ; </xsl:text> </xsl:when> <xsl:otherwise /> </xsl:choose> </xsl:for-each> </xsl:template>
<xsl:template name="cas"> <xsl:param name="boxes" /> <xsl:for-each select="$boxes"> <xsl:choose> <xsl:when test="(1 + count(preceding-sibling::container)) mod 4 = 0" > <!-- 4th container --> <xsl:text>CASE </xsl:text> <xsl:call-template name="fixLenChar"> <xsl:with-param name="strValue" select="preceding-sibling::container[3]/@serial_no" /> </xsl:call-template> <xsl:call-template name="fixLenChar"> <xsl:with-param name="strValue" select="preceding-sibling::container[2]/@serial_no" /> </xsl:call-template> <xsl:call-template name="fixLenChar"> <xsl:with-param name="strValue" select="preceding-sibling::container[1]/@serial_no" /> </xsl:call-template> <xsl:call-template name="fixLenChar"> <xsl:with-param name="strValue" select="@serial_no" /> </xsl:call-template> <xsl:text>; </xsl:text> </xsl:when> <xsl:when test="((1 + count(preceding-sibling::container)) mod 4 = 3) and (count(following-sibling::container) = 0)" > <!-- 3rd and last container --> <xsl:text>CASE </xsl:text> <xsl:call-template name="fixLenChar"> <xsl:with-param name="strValue" select="preceding-sibling::container[2]/@serial_no" /> </xsl:call-template> <xsl:call-template name="fixLenChar"> <xsl:with-param name="strValue" select="preceding-sibling::container[1]/@serial_no" /> </xsl:call-template> <xsl:call-template name="fixLenChar"> <xsl:with-param name="strValue" select="@serial_no" /> </xsl:call-template> <xsl:call-template name="fixLenChar"> <xsl:with-param name="strValue" select="''" /> </xsl:call-template> <xsl:text>; </xsl:text> </xsl:when> <xsl:when test="((1 + count(preceding-sibling::container)) mod 4 = 2) and (count(following-sibling::container) = 0)" > <!-- 2nd and last container --> <xsl:text>CASE </xsl:text> <xsl:call-template name="fixLenChar"> <xsl:with-param name="strValue" select="preceding-sibling::container[1]/@serial_no" /> </xsl:call-template> <xsl:call-template name="fixLenChar"> <xsl:with-param name="strValue" select="@serial_no" /> </xsl:call-template> <xsl:call-template name="fixLenChar"> <xsl:with-param name="strValue" select="''" /> </xsl:call-template> <xsl:call-template name="fixLenChar"> <xsl:with-param name="strValue" select="''" /> </xsl:call-template> <xsl:text>; </xsl:text> </xsl:when> <xsl:when test="((1 + count(preceding-sibling::container)) mod 4 = 1) and (count(following-sibling::container) = 0)" > <!-- 1st and last container --> <xsl:text>CASE </xsl:text> <xsl:call-template name="fixLenChar"> <xsl:with-param name="strValue" select="@serial_no" /> </xsl:call-template> <xsl:call-template name="fixLenChar"> <xsl:with-param name="strValue" select="''" /> </xsl:call-template> <xsl:call-template name="fixLenChar"> <xsl:with-param name="strValue" select="''" /> </xsl:call-template> <xsl:call-template name="fixLenChar"> <xsl:with-param name="strValue" select="''" /> </xsl:call-template> <xsl:text>; </xsl:text> </xsl:when> <xsl:otherwise /> </xsl:choose> </xsl:for-each> </xsl:template>
<xsl:template name="fixLenChar"> <xsl:param name="strLength" select="'10'"/> <!-- set to 10, allow future pass as param --> <xsl:param name="strValue" /> <xsl:variable name="str10" select="' '"/> <xsl:variable name="curStrLength" select="string-length($strValue)" /> <xsl:variable name="strPadLength" select="$strLength - $curStrLength" /> <xsl:variable name="strPadValue" select="substring($str10, 1, $strPadLength)" /> <xsl:value-of select="concat($strValue, $strPadValue)" /> </xsl:template>
Sorry for the long post, but I wanted to get it all out there for review. Thanks in advance -- this list (and the archives on biglist) have helped me tremendously on previous projects!
Current Thread |
---|
|
<- Previous | Index | Next -> |
---|---|---|
Re: [xsl] using tokenize() and refe, Spencer Tickner | Thread | Re: [xsl] Output fixed length recor, Abel Braaksma |
RE: [xsl] using tokenize() and refe, Michael Kay | Date | Re: [xsl] Output fixed length recor, Abel Braaksma |
Month |