itsfoobar
itsfoobar

Reputation: 52

copy-of with search and replace relative paths

I want to insert an html snippet from an external file into my output document with copy-of like described here: https://stackoverflow.com/a/5976762/18427492 The html snipped is a navigation bar and also used by other (python) scripts to generate other html files. I need to replace the path in "href" to match a relative path that i have in a XSLT variable.

Full file content (Template file to be copied):

<ul class="nav">
    <li class="fineprint">MyNiceGame Developer Mode Documentation</li>
    <li class="switchlang"><a href="../en/content.html"><img src="/deco/dco_en_sml.gif" alt="English" border="0"></img></a></li>
    <li><a href="../../sdk/index.html">Introduction</a></li>
    <li><a href="../../content.html">Contents</a></li>
    <li><a href="../../search.php">Search</a></li>
    <li><a href="../../sdk/console.html">Engine</a></li>
    <li><a href="../../sdk/cmdline.html">Command Line</a></li>
    <li><a href="../../sdk/files.html">Game Data</a></li>
    <li><a href="../../sdk/script/index.html">Script</a></li>
</ul>

So how can i insert this snippet into my XSL document and replace ../../sdk/ (its possible to change this string to something like {replace-me}/sdk/...) with a relative path that i already have in a XSLT variable?

My XSLT document (i want to replace the <xsl:call-template name="nav"/> with the template file processing):

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="3.0" xpath-default-namespace="https://clonkspot.org" exclude-result-prefixes="xs">
    <xsl:output method="html" encoding="ISO-8859-1" doctype-public="-//W3C//DTD HTML 4.01//EN"
                doctype-system="http://www.w3.org/TR/html4/strict.dtd"/>

    <xsl:template match="/clonkDoc">
        <html>
            <body>
                <xsl:call-template name="nav"/>
                <xsl:apply-templates select="func"/>
                <!-- other possible nodes under /clonkDoc -->
                <xsl:call-template name="nav"/>
            </body>
        </html>
    </xsl:template>

<xsl:template name="nav">
        <xsl:param name="relpath" tunnel="yes"/>
            <ul class="nav">
                <li class="fineprint">
                    <xsl:when test='lang("en")'>>MyNiceGame Developer Mode Documentation</xsl:when>
                </li>
                <!-- Other li elements -->
    </xsl:template>

Example source file:

<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
<?xml-stylesheet type="text/xsl" href="../../../clonk.xsl"?>
<clonkDoc xmlns="https://clonkspot.org"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="https://clonkspot.org ../../../clonk.xsd" xml:lang="de">
    <func>
        <!-- other nodes -->
    </func>
</clonkDoc>

Desired target file:

<!DOCTYPE html
  PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
   <head>
   <!-- stuff -->
   </head>
   <body>
      <ul class="nav">
         <!-- The corrected li elements with modified a href link -->
      </ul>
    <!-- Other stuff from source file (<func>) -->
      <ul class="nav">
         <!-- The corrected li elements with modified a href link -->
      </ul>
   </body>
</html>

Martin Honnen's solution for my specific case with the xpath-default-namespace:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="3.0" xpath-default-namespace="https://clonkspot.org" exclude-result-prefixes="xs">
    <xsl:output method="html" encoding="ISO-8859-1" doctype-public="-//W3C//DTD HTML 4.01//EN"
                doctype-system="http://www.w3.org/TR/html4/strict.dtd"/>

    <xsl:template match="/clonkDoc">
        <html>
            <body>
                <xsl:apply-templates select="doc('file.html')//ul[@class = 'nav']" xpath-default-namespace="" mode="fix-links"/>
                <xsl:apply-templates select="func"/>
                <!-- other possible nodes under /clonkDoc -->
                <xsl:apply-templates select="doc('file.html')//ul[@class = 'nav']" xpath-default-namespace="" mode="fix-links"/>
            </body>
        </html>
    </xsl:template>

    <xsl:mode name="fix-links" on-no-match="shallow-copy"/>

    <xsl:template mode="fix-links" match="ul/li/a/@href" xpath-default-namespace="">
        <xsl:message>Value href: <xsl:value-of select="."></xsl:value-of></xsl:message>
        <xsl:attribute name="{name()}" select="replace(., '../../sdk', 'foobar')"/>
    </xsl:template>

Upvotes: 0

Views: 62

Answers (1)

Martin Honnen
Martin Honnen

Reputation: 167401

copy-of makes a a deep copy, if you want to transform input nodes (even only their attribute values) you write templates to do so e.g. <xsl:apply-templates select="doc('file.xml')//ul[@class = 'nav']" mode="fix-links"/>, or, perhaps, as the edit says the snippet with the ul is all in the file, use simply <xsl:apply-templates select="doc('file.xml')" mode="fix-links"/>, and

<xsl:mode name="fix-links" on-no-match="shallow-copy"/>

<xsl:template mode="fix-links" match="ul/li/a/@href">
  <xsl:attribute name="{name()}" select="replace(., '../../sdk', $varname)"/>
</xsl:template>

The xsl:mode declaration is XSLT 3 only, in earlier versions declare the identity transformation for that mode e.g.

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

in XSLT 1 or

<xsl:template mode="fix-links" match="@* | node()">
  <xsl:copy>
    <xsl:apply-templates select="@* | node()" mode="#current"/>
  </xsl:copy>
</xsl:template>

in XSLT 2.

XSLT 3 sample (slighly adapted for the demonstration to work with the primary input) outputs

<ul class="nav">
    <li class="fineprint">MyNiceGame Developer Mode Documentation</li>
    <li class="switchlang"><a href="../de/content.html"><img src="/deco/dco_de_sml.gif" alt="German" border="0"/></a></li>
    <li><a href="../sdk2/index.html">Introduction</a></li>
    <li><a href="../../content.html">Contents</a></li>
    <li><a href="../../search.php">Search</a></li>
    <li><a href="../sdk2/console.html">Engine</a></li>
    <li><a href="../sdk2/cmdline.html">Command Line</a></li>
    <li><a href="../sdk2/files.html">Game Data</a></li>
    <li><a href="../sdk2/script/index.html">Script</a></li>
</ul>

As for the information in the latest edit that the secondary input document you want to process has elements in no namespace but your primary one has elements in a certain namespace that your XSLT has used as the xpath-default-namespace, in that case you need to override that for any selections in the secondary input e.g.

<xsl:mode name="fix-links" on-no-match="shallow-copy"/>

<xsl:template mode="fix-links" match="ul/li/a/@href" xpath-default-namespace="">
  <xsl:attribute name="{name()}" select="replace(., '../../sdk', $varname)"/>
</xsl:template>

and if you continue to use the apply-templates with an element selector, there as well e.g. <xsl:apply-templates select="doc('file.xml')//ul[@class = 'nav']" xpath-default-namespace="" mode="fix-links"/>.

Upvotes: 1

Related Questions