0

I have TEI (text encoding initiative) document containing

<div>
  <p>
     some text, and maybe nodes <note>A note</note><lb />
     and some more text<lb />
     final line without lb
  </p>
</div>

and I want to transform it to:

<div>
  <lg>
     <l>some text, and maybe nodes <note>A note</note></l>
     <l>and some more text</l>
     <l>final line without lb</l>
  </lg>
</div>

Transforming the p to lg is trivial by using

<xsl:template match="tei:div/tei:p">
   <lg>
     <xsl:apply-templates/>
   </lg>
</xsl:template>

But the rest can't i figure out how to do. Turning a sequence of nodes into children of a new parent.

If there is a solution for xslt 1.0 would it be great.

Tim C
  • 70,053
  • 14
  • 74
  • 93
Johan Dahl
  • 66
  • 1
  • 9

2 Answers2

1

Here's another way you could look at it. It uses a key to link every node to its nearest preceding lb separator. This enables you to fetch every group (except the very first one) by the unique id of the leading separator:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<!-- identity transform -->
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<xsl:key name="following-nodes" match="node()[not(self::lb)]" use="generate-id(preceding-sibling::lb[1])" />

<xsl:template match="p[lb]">
    <lg>
        <l>
            <xsl:apply-templates select="lb[1]/preceding-sibling::node()"/>
        </l>
        <xsl:for-each select="lb">
            <l>
                <xsl:apply-templates select="key('following-nodes', generate-id())"/>
            </l>
        </xsl:for-each>
    </lg>
</xsl:template>

</xsl:stylesheet>

This example uses no namespaces, because your question does not define them.

michael.hor257k
  • 113,275
  • 6
  • 33
  • 51
  • Thanks a lot @michael.hor257k :) I feel really like a newbee on xslt. And this was about the first thing I had to achive. – Johan Dahl Mar 17 '17 at 17:14
0

You could use a technique called Muenchian grouping here. In this case, you can group the child nodes of a p element by the number of lb elements that follow them

<xsl:key name="p-nodes" match="tei:p/node()" use="concat(generate-id(..), '|', count(following-sibling::tei:lb))" />

To get the first node in each group, which will represent each l you want outputted, you would select them like so...

<xsl:for-each
     select="node()[generate-id() = generate-id(key('p-nodes', concat($parentId, '|', count(following-sibling::tei:lb)))[1])]">

And to output the <l> tag itself and the contents of the group, use the key again...

<l><xsl:apply-templates select="key('p-nodes', concat($parentId, '|', count(following-sibling::tei:lb)))[not(self::tei:lb)]" /></l>

Try this XSLT (obviously changing the namespace for the tei prefix to match the real one in your XML)

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:tei="tei">
    <xsl:output method="xml" indent="yes" />

    <xsl:key name="p-nodes" match="tei:p/node()" use="concat(generate-id(..), '|', count(following-sibling::tei:lb))" />

    <xsl:template match="tei:div/tei:p">
       <lg>
            <xsl:variable name="parentId" select="generate-id()" />
            <xsl:for-each select="node()[generate-id() = generate-id(key('p-nodes', concat($parentId, '|', count(following-sibling::tei:lb)))[1])]">
                <l><xsl:apply-templates select="key('p-nodes', concat($parentId, '|', count(following-sibling::tei:lb)))[not(self::tei:lb)]" /></l>
            </xsl:for-each>
       </lg>
    </xsl:template>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

See it in action at http://xsltransform.net/gWEamMf

Tim C
  • 70,053
  • 14
  • 74
  • 93