XSLT replace \n with </p><p>

1k views Asked by At

I have an XML that in one node has a bunch of text with several paragraphs. I need to replace the \n with </p><p> the text section in all will have a <p> at the start and a </p> at the end in the XSLT definition.

I have tried this but it gives me an error in Saxon: Variable find_all has not been declared (or its declaration is not in scope)

<xsl:template match="/csv/row/col[17]">
    <xsl:param name="find_all">
        <xsl:value-of select="."/>
    </xsl:param>
    <xsl:param name="find_return">
        <xsl:text>(.*)\n(.*)</xsl:text>
    </xsl:param>
    <xsl:param name="replace_return">
        <xsl:text>$1&lt;/p&gt;&lt;p&gt;$2</xsl:text>
    </xsl:param>
</xsl:template>

And later this:

<col name="Model descr.">
    <content>
        <xsl:text>&lt;p&gt;</xsl:text>
    </content>
    <content>
        <xsl:value-of select="replace($find_all, $find_return, $replace_return)"/>
    </content>
    <content>
        <xsl:text>&lt;/p&gt;</xsl:text>
    </content>
</col>

Whats's wrong here??

Thanks

Jan

2

There are 2 answers

2
Tim C On

You are taking the wrong approach here. For a start, from the snippets you have shown us, you have a template with parameters, but those parameters are local in scope to that template. If you are trying to use those parameters outside the template, they won't be accessible.

Additionally, the replace function works with strings, and you cannot use it to create new elements like <p>. What you will create in your output is the escaped value of &lt;p&gt;.

A different approach to take is use the tokenize function, to split the text based on the line-break, and iterate over each item, wrapping it in a new <p> tag.

Try this XSLT

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

    <xsl:template match="text()" priority="2">
        <xsl:for-each select="tokenize(., '\n')[normalize-space()]">
            <p>
                <xsl:value-of select="." />
            </p>            
        </xsl:for-each>
    </xsl:template>

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

OK looking some more in to this I will go a step further. My process involves an XSLT that splits the node <row> in to single XML files. It will be easier to run the tokenize function on this smaller XML file.

But I only want to tokenize the text in just on of the nodes of this XML, how do I select just that one. I found this:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/excel-row/col">
    <xsl:apply-templates/>
</xsl:template>
<xsl:template match="text()">
    <xsl:for-each select="tokenize(.,'\n')">
        <xsl:sequence select="."/>
        <xsl:if test="not(position() eq last())">
            <br />
        </xsl:if>
    </xsl:for-each>
</xsl:template>

The xml node looks like this:

<col name="Model descr.">Some text in first paragraph.
More text in second paragraph.
And then a third paragraph with more text.

Then we have the last paragraph.

So how do I select the node col name="Model descr.">

Thankful for any help, my XSLT experience is limited as you see.

Jan