Home > Net >  XSLT: group elements without deleting neighboring elements
XSLT: group elements without deleting neighboring elements

Time:03-30

I want to prepare the index entries in a Word file for an XSL-FO transformation.

The index entry always starts with

w:r/w:fldChar/@w:fldCharType="begin" and ends with w:r/w:fldChar/@w:fldCharType="end".

In between, one or more elements may contain the content.

How do I get the content as a contiguous string?

I have tried it with different variants of for-each-group. But the problem is that it always removes the other w:r that are not part of the index.

How can I do this?

Source-XML

<w:p>      
    <w:r>
        <w:t>Stahl und Beton</w:t>
    </w:r>
    <w:r>
        <w:t>lässt</w:t>
    </w:r>
    <w:r>
        <w:t> sich aus dem alten Namen des Materials ableiten.</w:t>
    </w:r>
    <w:r>
        <w:fldChar w:fldCharType="begin"/>
    <w:r>
        <w:instrText> XE "</w:instrText>
    </w:r>
    <w:r>
        <w:instrText>Moniereisen</w:instrText>
    </w:r>
    <w:r>
        <w:instrText>" </w:instrText>
    </w:r>
    <w:r>
        <w:fldChar w:fldCharType="end"/>
    </w:r>
</w:p>

One of the XSLT attempts

    <xsl:template match="w:p[w:r[w:fldChar[@w:fldCharType = 'begin']]]">
        <xsl:for-each-group select="*" group-starting-with="w:r[w:fldChar[@w:fldCharType = 'begin']]">
            <xsl:if test="self::w:r[w:fldChar/@w:fldCharType = 'begin']">
                <indexentry>
                        <xsl:for-each-group select="current-group()[position() gt 1]"
                            group-ending-with="w:r[w:fldChar[@w:fldCharType = 'end']]">
                            <xsl:if test="position() eq 1">
                                <xsl:apply-templates
                                    select="current-group()[not(self::w:r[w:fldChar[@w:fldCharType = 'end']])]"/>
                            </xsl:if>
                        </xsl:for-each-group>
                </indexentry>
            </xsl:if>
        </xsl:for-each-group>
    </xsl:template>

Actual output (simplified)

<indexentry>
    <w:r/>
    <w:r/>
    <w:r/>
</indexentry>

Desired output

<w:p>      
    <w:r>
        <w:t>Stahl und Beton</w:t>
    </w:r>
    <w:r>
        <w:t>lässt</w:t>
    </w:r>
    <w:r>
        <w:t> sich aus dem alten Namen des Materials ableiten.</w:t>
    </w:r>
    <indexentry> XE "Moniereisen" </indexentry>
</w:p>

CodePudding user response:

For the outer xsl:if you want an xsl:choose/xsl:when instead where the additional xsl:otherwise pushes the current-group() with xsl:apply-templates to your default templates (e.g. the identity transformation). I don't know whether you need the inner xsl:if and the xsl:apply-templates, I would think that doing <xsl:value-of select="current-group()[position() != last()]/w:r/w:instrText"/> suffices.

  • Related