views:

48

answers:

3

I'm outputting text with XSLT. I need to count the number of lines of each section and write it out to my output file. How this could be done?

My output is like this:

HDR+aaa:bbb'
AAA+78901234567890+String1:String2'
BBB+123+String'
CCC+321:1212'
DDD+112211'
DDD+121122'
XXX+number_of_records+78901234567890'
AAA+1234567890+String1:String2'
BBB+123+String'
CCC+321:1212'
DDD+1212:2121'
BBB+123+String'
BBB+122+String'
CCC+String'
XXX+number_of_records+1234567890'

The number_of_records should contain number of lines from AAA to XXX including both lines. In the first section the number of lines should be 6 and in the second message it should be 8. The first and the last line of each section will share the same unique ID-number.

The number of lines cannot be counted from source since there is so much processing inside XSLT.

+1  A: 

Just for fun, until you post your input sample and stylesheet building that text output, this stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
    <xsl:output method="text"/>
    <xsl:template match="result" name="result">
        <xsl:param name="pString" select="."/>
        <xsl:variable name="vAfter" select="substring-after($pString, 'AAA+')"/>
        <xsl:choose>
            <xsl:when test="$vAfter!=''">
                <xsl:variable name="vId" 
                        select="substring-before($vAfter, '+')"/>
                <xsl:variable name="vEnd" 
                  select='concat("XXX+number_of_records+",$vId,"&apos;&#xA;")'/>
                <xsl:variable name="vInto" 
                        select="substring-before($vAfter,$vEnd)"/>
                <xsl:value-of 
                        select='concat(substring-before($pString,"AAA+"),
                             "AAA+",
                             $vInto,
                             "XXX+",
                             string-length(translate($vInto,
                                                     translate($vInto,
                                                               "&#xA;",
                                                               ""),
                                                     "")) + 1,
                             "+",$vId,"&apos;&#xA;")'/>
                <xsl:call-template name="result">
                    <xsl:with-param name="pString" 
                        select="substring-after($vAfter,$vEnd)"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$pString"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

With this input:

<result>
HDR+aaa:bbb'
AAA+78901234567890+String1:String2'
BBB+123+String'
CCC+321:1212'
DDD+112211'
DDD+121122'
XXX+number_of_records+78901234567890'
AAA+1234567890+String1:String2'
BBB+123+String'
CCC+321:1212'
DDD+1212:2121'
BBB+123+String'
BBB+122+String'
CCC+String'
XXX+number_of_records+1234567890'
</result>

Output:

HDR+aaa:bbb'
AAA+78901234567890+String1:String2'
BBB+123+String'
CCC+321:1212'
DDD+112211'
DDD+121122'
XXX+6+78901234567890'
AAA+1234567890+String1:String2'
BBB+123+String'
CCC+321:1212'
DDD+1212:2121'
BBB+123+String'
BBB+122+String'
CCC+String'
XXX+8+1234567890'
Alejandro
+2  A: 

A conceptually simple way to do this would be to use a second-stage process. Take the output of your initial transformation (what you posted) and run it through a template (or stylesheet like @Alejandro's) that parses it into lines, and groups the lines starting with AAA... and ending with XXX. See Up-conversion using XSLT 2.0 for a very clear and practical tutorial on doing this, using tokenize(), xsl:analyze-string, and xsl:for-each-group. Then count the lines in each group, and re-output each line, plug the line count into the XXX record.

But that's inefficient, and somewhat error-prone, since you would be parsing the initial output. Why parse a serialization of information that the stylesheet already had internally? You could avoid the inefficiency by changing your initial output to XML, something like

<hdr>
  <section id="78901234567890">
    <!-- It sounds like AAA's ID actually applies to the section? -->
    <AAA String1="..." String2="..."/>
    <BBB .../>
    <!-- no need to include XXX at this stage AFAICT -->
  </section>
  <section id="1234567890">
    ...
  </section>
</hdr>

Then the second-stage template (or a separate stylesheet) could take this XML as input and very easily serialize it as you have done above, counting the lines as it goes. In XSLT 1.0, you would have to use a separate stylesheet to process the output XML, or else use the extension function node-set(). (But even with a separate stylesheet processor, you could still avoid the cost of re-parsing the intermediate XML, if you can pipeline the two stylesheet processors together using SAX.) In XSLT 2.0, you can process the XML output of one template with another template, without restriction.

LarsH
A: 

My solution: I created an extension function that increments number_of_records by one each time I call it. I use xsl:comment to suppress the output until I really need to output the number. I reset the number_of_records after each XXX+ -line.

Doing this in two steps vould have caused too much hassle.

Bobby