0

I have an input XML:

<?xml version='1.0' encoding='UTF-8'?>
<MessageFormat name='TransportEvent' version='2.02'>
    <StructFormat name='TransportEventHeader' >
        <TagField type='String' value='H0 '/>
        <FieldFormat name='NotificationTriggeringOU' length='10'/>
        <FieldFormat name='NotificationTriggeringIT' length='8'/>
        <FieldFormat name='NotificationReference' length='1'/>
        <FieldFormat name='NotificationCode' length='3'/>
        <FieldFormat name='NotificationType' length='8'/>
        <FieldFormat name='NotificationStatus' length='1'/>
        <FieldFormat name='NotificationTripType' length='1'/>
        <FieldFormat name='ProducingRailwayUndertaking' length='4'/>
        <FieldFormat name='ExternalPartner' length='35'/>
        <FieldFormat name='ActualNumberOfWagons' length='3' />
        <FieldFormat name='ProcessingTime' length='26'/>
        <StructFormat name='NotificationFunctionalClassification'>
            <FieldFormat name='OrderRelevant' length='1'/>
            <FieldFormat name='TimetableRelevant' length='1'/>
            <FieldFormat name='CapacityRelevant' length='1'/>
            <FieldFormat name='IntermodalRelevant' length='1'/>
            <FieldFormat name='XrailRelevant' length='1'/>
            <FieldFormat name='NotificationLocationRelevant' length='1'/>
        </StructFormat>
        <FieldFormat name='Reserve' length='1'/>
    </StructFormat>
    <StructFormat name='NotificationLocation' >
        <TagField type='String' value='M1 '/>
        <StructFormat name='CurrentLocation'>
            <FieldFormat name='CurrentLocationRL100' length='5'/>
            <FieldFormat name='CurrentLocationLocationType' length='1'/>
            <FieldFormat name='CurrentUICRailAuthorityNumber' length='6'/>
            <FieldFormat name='CurrentNetworkLocationNumber' length='6'/>
            <FieldFormat name='CurrentLocationSatelliteNumber' length='2'/>
            <FieldFormat name='CurrentFreightCarLocationNumber' length='4'/>
        </StructFormat>
        <StructFormat name='NextLocation'>
            <FieldFormat name='NextLocationRL100' length='5'/>
            <FieldFormat name='NextLocationLocationType' length='1'/>
            <FieldFormat name='NextUICRailAuthorityNumber' length='6'/>
            <FieldFormat name='NextNetworkLocationNumber' length='6'/>
            <FieldFormat name='NextLocationSatelliteNumber' length='2'/>
            <FieldFormat name='NextFreightCarLocationNumber' length='4'/>
        </StructFormat>
    </StructFormat>
    <StructFormat name='NotificationTime' >
        <TagField type='String' value='M2 '/>
        <FieldFormat name='ActualTime' length='18'/>
    </StructFormat>
    <StructFormat name='Trip' >
        <TagField type='String' value='Z1 '/>
        <FieldFormat name='TripNumber' length='6'/>
        <FieldFormat name='RegionNetz' length='2'/>
        <FieldFormat name='NationalProductionDate' length='10'/>
        <FieldFormat name='TrainTypeMainNumber' length='2'/>
        <FieldFormat name='TrainTypeSubNumber' length='1'/>
        <FieldFormat name='DepartureStationRL100' length='5'/>
        <FieldFormat name='DepartureStationUICRailAuthority' length='6'/>
        <FieldFormat name='DepartureStationNetworkLocation' length='6'/>
        <FieldFormat name='TargetTime' length='18'/>
        <FieldFormat name='RelativeTime' length='5' />
    </StructFormat>
    <StructFormat name='HandoverTakeover' >
        <TagField type='String' value='U1 '/>
        <FieldFormat name='HandoverTakeoverFlag' length='1'/>
        <FieldFormat name='ConsigningRU' length='4'/>
        <FieldFormat name='AcceptingRU' length='4'/>
        <FieldFormat name='UICBorderCode' length='3'/>
    </StructFormat>
</MessageFormat>

And in my xslt template I want to count (FieldFormat/@length + string-length(TagField/@value) of each StructFormat direct under MessageFormat:

<?xml version="1.0" encoding="UTF-8"?>
<?altova_samplexml format.xml?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:math="http://www.w3.org/2005/xpath-functions/math" xmlns:array="http://www.w3.org/2005/xpath-functions/array" xmlns:map="http://www.w3.org/2005/xpath-functions/map" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:err="http://www.w3.org/2005/xqt-errors" xmlns:saxon="http://saxon.sf.net/" exclude-result-prefixes="array fn map math xhtml xs err" version="3.0">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:variable name="msg">H0 EVU_DBSRD PVG     W14 PVGERL  I                                        0142019-12-08-07.46.52.436704NNJNNJNM1           80270611 04401                        M2 08.12.201907:50:00Z1 BAU1    08.12.2019                    08.12.201907:50:00 0000R1 00131806940728280201912063946287201912061215058175346965                                   000NNJNNN                                  80270611 04401R1 00231806940476880201912063946287201912061215059494346965                                   000NNJNNN                                  80270611 04401R1 00331806948174180201912063946287201912061215050425346965                                   000NNJNNN                                  80270611 04401R1 00431806948098280201912063946287201912061215051864346965                                   000NNJNNN                                  80270611 04401</xsl:variable>
    <xsl:accumulator name="position-count" as="xs:double" initial-value="1" streamable="no">
        <xsl:accumulator-rule match="/MessageFormat/StructFormat" phase="end" select="$value +sum(.//FieldFormat/@length) + string-length(./TagField/@value)" />
    </xsl:accumulator>
    <xsl:template match="/">
        <output>
            <xsl:for-each select="/MessageFormat/StructFormat">
            <xsl:element name="{./@name}">
                <xsl:attribute name="start" select="fn:accumulator-before('position-count')"/>
                <xsl:attribute name="end" select="fn:accumulator-after('position-count')" />
                </xsl:element>
            </xsl:for-each>
        </output>
    </xsl:template>
</xsl:stylesheet>

When I run it with XML Spy Professional 2020 SP1, I get the output:

<?xml version="1.0" encoding="UTF-8"?>
<output xmlns:saxon="http://saxon.sf.net/">
    <TransportEventHeader start="1" end="111"/>
    <NotificationLocation start="111" end="162"/>
    <NotificationTime start="162" end="183"/>
    <Trip start="183" end="247"/>
    <HandoverTakeover start="247" end="262"/>
</output>

But when I run it with Saxon 9-9-1-5 Java java -cp /d/SaxonPE9-9-1-5J/saxon9pe.jar net.sf.saxon.Transform -s:format.xml -xsl:accumulator.xslt -o:output.xml I get error:

Error evaluating (fn:accumulator-before(...)) in xsl:attribute/@select on line 13 column 83 of accumulator.xslt: XTDE3362: Accumulator position-count is not applicable to the current document invoked by unknown caller (class net.sf.saxon.expr.instruct.ForEach) at file:/D:/xslt/accumulator.xslt#11 In template rule with match="/" on line 9 of accumulator.xslt Accumulator position-count is not applicable to the current document

What did I wrong with my accumulator? Why does it work with XML Spy, not with Saxon 9.9?

Actually I want to add a condition in my accumulator to check, if the value TagField/@value in variable $msg at a right position occures.

For example, for StructFormat[@name='TransportEventHeader'], the first 3 characters in $msg are "H0 ", so it matches StructFormat[@name='TransportEventHeader']/TagField/@value, in this case the length shoud be added to my accumulator, if not, then it should not be added. I don't know how to implement this in accumulator.

Michael Kay
  • 156,231
  • 11
  • 92
  • 164
dingjun
  • 94
  • 8
  • https://www.w3.org/TR/2014/WD-xslt-30-20141002/Overview-diff.html#err-XTDE3362 suggests you are in streamed mode with Saxon, but have not declared the accumulator to be streamable. – Richard Dec 10 '19 at 08:36
  • When I set streamable="yes", then I got besides the error as the same also a warning: Warning at xsl:accumulator on line 6 column 91 of accumulator.xslt: SXST0068: Request for streaming ignored: this Saxon configuration does not support streaming. Do I need set Saxon configuration explicitly to enable streaming? – dingjun Dec 10 '19 at 08:48
  • I checked this page, https://www.saxonica.com/html/documentation/sourcedocs/streaming/configuration-streaming.html. It says, streaming requires Saxon EE, since I'm using Saxon PE, so streaming is not supported? – dingjun Dec 10 '19 at 08:54
  • @Richard you have cited an early working draft of the XSLT 3.0 spec, the rules on applicability of accumulators were only introduced later. See https://www.w3.org/TR/xslt-30/#applicability-of-accumulators – Michael Kay Dec 10 '19 at 09:59

2 Answers2

2

The rules are here: https://www.w3.org/TR/xslt-30/#applicability-of-accumulators

Rule 5 says: For a document containing nodes supplied in the initial match selection, the accumulators that are applicable are those determined by the xsl:mode declaration of the initial mode. This means that in the absence of an xsl:mode declaration, no accumulators are applicable.

So you need to add

<xsl:mode use-accumulators="position-count"/>

The reason for these rules in the spec is that (a) with streaming, it is expensive to maintain an accumulator for a streamed document if it is not going to be used on that document, and (b) although the same overhead does not exist with non-streamed documents, because accumulators can be evaluated when first used, it was thought desirable to have the same rule whether the document is streamed or unstreamed.

Altova does not implement streaming so they probably thought (and with some justification, I would concur) that implementing the rules is a lot of effort that delivers little value to users.

Michael Kay
  • 156,231
  • 11
  • 92
  • 164
  • Thanks a lot! Without your hint, I could not get this by myself. I'm wondering, except those xslt engine implementors, how many pepole will read w3 xslt spec. – dingjun Dec 10 '19 at 11:33
  • People learn a programming language in different ways: by reading the spec, by reading books and tutorials, from training courses, by "sitting next to Nellie", from looking at examples, and even (judging from Stack Overflow) by trial and error. "Avoiding surprises" is one of the criteria that language designers use, but in the end, one has to assume that people using a language construct have access to information about how it works. – Michael Kay Dec 10 '19 at 20:01
0

Michael Kay has answered my question. And the question came originally from applying an accumulator. Now I get a working accumulator like below to calculate the group position:

        <xsl:accumulator-rule match="/MessageFormat/StructFormat" phase="end">
            <xsl:choose>
                <xsl:when test="substring($msg, $value, string-length(./TagField/@value)) = ./TagField/@value">
                    <xsl:value-of select="$value + xs:integer(sum(.//FieldFormat/@length)) + string-length(./TagField/@value)"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="$value"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:accumulator-rule>
    </xsl:accumulator>
dingjun
  • 94
  • 8
  • As a general hint on using `xsl:value-of` versus `xsl:sequence`, I would suggest to use `xsl:sequence` in all cases where you want a function body or an accumulator rule to return a value of a certain type like an `xs:integer` as `xsl:value-of` always creates a text node from the selected `select` expression value while `xsl:sequence` simply returns the selected value. – Martin Honnen Dec 10 '19 at 14:10
  • @MartinHonnen Thanks for your hint. I think, `xsl:sequence` is introduced in *XSLT 2.0*., and I'm still not accustomed to it, even *XSLT 3.0* is there. – dingjun Dec 11 '19 at 08:57