18

I am transforming XML into HTML using XSLT.

I have the following XML structure:

<root>
    <element>
        <subelement>
            This is some html text which should be <span class="highlight">displayed highlighted</span>.
         </subelement>
    </element>
</root>

I use the following template for the transformation:

<xsl:template name="subelement">
  <xsl:value-of select="." />
</xsl:template>

Unfortunately, I lose the <span>-tags.

Is there a way to keep them so the HTML is displayed correctly (highlighted)?

Cary Millsap
  • 806
  • 6
  • 17
monty
  • 7,888
  • 16
  • 63
  • 100

3 Answers3

41

The correct way to get the all the contents of the current matching node (text nodes included) is:

    <xsl:template match="subelement">
       <xsl:copy-of select="node()"/>
    </xsl:template>

This will copy everything descendent.

Emiliano Poggi
  • 24,390
  • 8
  • 55
  • 67
  • 1
    +1 for correct answer. Note that this will not copy *attributes* of the context node... that would require `select="node() | @*"`. But I don't think @idefix needed those. – LarsH Jun 01 '11 at 11:25
  • 1
    @LarsH: this is not true in this case. It's going to be true in `xsl:apply-templates` or `match` but not in `xsl:copy`. – Emiliano Poggi Jun 01 '11 at 13:54
  • @empo - I stand corrected. (I assume you meant `xsl:copy-of` instead of `xsl:copy`.) I thought the `node()` expression had an implied `child::` axis, which would exclude attributes. But testing seems to show you are right. I will have to review those details. – LarsH Jun 01 '11 at 14:20
  • @LarsH: yes `xsl:copy-of`! For details about `node()` see [here](http://www.dpawson.co.uk/xsl/sect2/nodetest.html#d7653e19), even if still not really clear. Test is the best way anyway. – Emiliano Poggi Jun 01 '11 at 14:33
  • @empo: you're right, that explanation is not clear in terms of what I'm wondering about. Sure `node()` matches attributes. But the `child::` axis does not. So what I don't understand is what axis `node()` uses when it is preceded by nothing else in the XPath expression. Why should `select="node()"` imply a `child::` axis when used with `xsl:apply-templates` but not when used with `xsl:copy-of`? – LarsH Jun 01 '11 at 14:59
  • @empo I'm guessing there is not a difference between `xsl:copy-of` and `xsl:apply-templates` in terms of an implied `child::` axis; rather the difference comes from the fact that `xsl:apply-templates` is filtered by patterns, and the `"node()"` pattern does not match attributes. – LarsH Jun 01 '11 at 15:13
  • @LarsH: I think this is a potentially good question which we can ask to SO. – Emiliano Poggi Jun 01 '11 at 16:49
  • @empo: hm. I've changed my mind. I don't know what I was doing in my original test, but now as I test it, it's clear that `` will **not** copy attributes of the context node. (I'm using XSLT 1.0.) So I stand by that original statement. I could show you my test stylesheet but it's rather big for a comment. If you can show me an example where `` copies attributes of the context node, I would love to be better informed about this detail. – LarsH Jun 01 '11 at 16:59
  • @LarsH: this is going to be interesting. I tested the template provided in my answer with MSXSL (XSLT 1.0 processor) and Saxon-HE 9 (XSLT 2.0 processor) and both tests returned the `span` along with `@class` :). I will try this evening asap with Saxon 6 (XSLT 1.0). – Emiliano Poggi Jun 01 '11 at 17:42
  • @empo: I think we just have a miscommunication here. Outputting `span` along with `@class` is an example of copying attributes of the *child* elements, not attributes of the *context node*. (The context node is `` in your template.) – LarsH Jun 01 '11 at 17:48
  • 1
    @LarsH: that's true, sorry. I read comments with approximation. Yes, `node()` will not copy attributes of the _context_ node. – Emiliano Poggi Jun 01 '11 at 18:02
  • 1
    @LarsH, @empo: `child::` is the default axis in `select` for `` and `` and it applies to `node()` in the same way as to other node tests. The difference between these two `select` attributes is that in `` the expression must evaluate to node set. – jasso Jun 01 '11 at 22:53
  • I've got a related question I can't find an answer to. When using xsl:copy al textual content is unescaped. When using xsl:value-of I can provide a disable-output-escaping="yes" attribute that avoid escaping. When using xsl to turn an xml into another, even if I declare output method is XML, xsl:copy just unescapes texts. How can I obtain a proprerly unescaped XML starting from an XML if I need to copy some parts of the first into the second? – Mr.Gate Jun 08 '16 at 12:43
  • @Mr.Gate even if you think your question is really clear, it is not. I suggest you to ask a new question in SO with a good example of what you are trying to achieve with XSLT. – Emiliano Poggi Jun 10 '16 at 13:37
7

Try using <xsl:copy-of... instead of <xsl:value-of... for example:

<xsl:template name="subelement">
  <xsl:copy-of select="*" />
</xsl:template>

Note the * which will stop the <subelement></subelement> bits being output to the results, rather than using . which will include the <subelement></subelement> bits .

For example, the xsl stylesheet:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="2.0">

    <xsl:template match="root/element">
        <output>
            <xsl:apply-templates select="subelement"/>
        </output>
    </xsl:template>

    <xsl:template match="subelement">
        <xsl:copy-of select="*"/>
    </xsl:template>

</xsl:stylesheet>

when applied to your example xml file returns:

<?xml version="1.0" encoding="UTF-8"?>
<output>
    <span class="highlight">displayed highlighted</span>
</output>
Jon Egerton
  • 40,401
  • 11
  • 97
  • 129
  • 1
    Updating with fuller example after some testing – Jon Egerton Jun 01 '11 at 10:26
  • 1
    In the first specimen above `"./"` is not a valid XPath expression. In the second specimen, `"./*"` is valid, but it is equivalent to `"*"`. The comment "Note the '/'..." is plain wrong. – Michael Kay Jun 01 '11 at 11:11
  • 1
    The ./ is a type and should be * - you're right. My point with the comment is that if you just do you get the outerxml including the node itself, which isn't required. I was pointing it out to avoid frustration. I'll make it clearer. – Jon Egerton Jun 01 '11 at 11:31
0

The <xsl:value-of> declaration takes the concatenated contents of all text nodes within the element, in sequential order, and doesn't output any elements at all.

I'd recommend using <xsl:apply-templates> instead. Where it finds a text node, it will output the contents as-is, but you would need to define a template for handling span tags to convert them to html ones. If that span tag IS an html tag, then strictly speaking, you should have separate namespaces for your own document structure and html.

Flynn1179
  • 11,925
  • 6
  • 38
  • 74