xslt flattening and unflattening DocBook para element

148 views Asked by At

Many of you help me on this previous question: xslt flattening out child elements in a DocBook para element

There was a additional twist to this problem that i wanted to see if anyone had done this before. Recalling the sample text before. The previous question was answered that addressed taking text between block elements and wrapping them with para element, as our schema does NOT like block elements like tabe, figure, etc in a para element.

<para>some text.....
   <literallayout>
   </literallayout>
 more text....
 <table>
   ...
 </table>
 even more text
<table>...</table>
<literallayout>text also look here</literlayout>
 more text <link xlink:href="http://someurl.com">
</para>

The wrinkle in this, is that for inline elements, like link, those elements NEED to be in a para element. So the last two lines of text about ideally should look like this:

<para>more text <link xlink:hred="http://someurl.com">someurl</link></para>

Using a for-each construct, i can go through the nodes within the para element, but if any of the nodes were a non-block element, i will have to wrap them in a para element. If there is a text node prior to this non- block element, and/or afterward, then it would be desireable to wrap all of that in a para element.

I can think of how to do this, albeit in a hacky way, in any other programming language, but need help from the XSLT gurus to enlighten me as to an elegant way to do this!

There was a request for more info. In the example above, i need to be able to output the following:

<para>some text..... </para>
<literallayout>
</literallayout>
<para> more text.... </para>
<table> ... </table>
<para> even more text </para>
<table>...</table>
<literallayout>
<para>more text <link xlink:hred="http://someurl.com">someurl</link></para>

Hope this makes things more clear.

Thanks to everyone for all your help.

Russ

1

There are 1 answers

1
shapiy On BEST ANSWER

This is a classic problem. As far as I am concerned, flattening is usually solved with XSLT grouping. Of course, it's not easy to achieve with XSLT 1, when you have to use recursion-based algorithms to implement it. But let's consider a simpler solution (XSLT 2):

<xsl:stylesheet version="2.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns:f="http://stackoverflow.com/xslt-functions"
                xmlns:doc="http://docbook.org/ns/docbook"
                xmlns="http://docbook.org/ns/docbook"
                exclude-result-prefixes="#all">


    <xsl:variable name="block-elements" select="QName('http://docbook.org/ns/docbook', 'table'),
                                                QName('http://docbook.org/ns/docbook', 'figure')"/>


    <xsl:template match="doc:para">
        <xsl:for-each-group select="node()" group-adjacent="f:is-block-level-element(.)">
            <xsl:choose>
                <!-- If current node is a block element process the node as is -->
                <xsl:when test="current-grouping-key()">
                    <xsl:apply-templates select="current-group()"/>
                </xsl:when>
                <!-- Otherwise, wrap it in para element -->
                <xsl:otherwise>
                    <para>
                        <xsl:apply-templates select="current-group()"/>
                    </para>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:for-each-group>
    </xsl:template>


    <xsl:function name="f:is-block-level-element" as="xs:boolean">
        <xsl:param name="node" as="node()"/>

        <xsl:sequence select="$node instance of element() and
                              node-name($node) = $block-elements"/>
    </xsl:function>

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

</xsl:stylesheet>

The destination content will be:

<section xmlns="http://docbook.org/ns/docbook">
    <para>some text.....
        <literallayout>
        </literallayout>
        more text....
    </para>
    <table>
        ...
    </table>
    <para>
        even more text
    </para>
    <table>...</table>
    <para>
        <literallayout>text also look here</literallayout>
        more text
        <link/>
    </para>
</section>