Phil
Phil

Reputation: 129

XSLT: select all elements without subtree

I try to transform Doxygen XML into a text output with XSLT. Doxygen returns this XML structure:

<detaileddescription>
    <para>a long text with <formula id="6">$ \LaTeX $</formula> code</para>
    <para>
        <simplesect kind="see">
            <para>
                <ulink url="url">url description</ulink>
            </para>
        </simplesect>
    </para>    
</detaileddescription>

I try to select only that para node that does not contain the simplesect node or any other "complex tree structure".
I try this XPath:

detaileddescription/para/node()

but this will also return the simplesect/para node.
So, how can I select only the para node that does neither contain a text() node nor a formula node as a sibling?

Upvotes: 1

Views: 444

Answers (3)

Valdi_Bo
Valdi_Bo

Reputation: 30971

You wrote para node that does not contain ... any ... complex tree structure.

Then do as you wrote:

  • Write a template matching para.
  • Read (direct) child text nodes, but only containing "real" text (not empty ones).
  • Then check whether you actually got something.
  • If yes (your para contains some not empty text (forgetting about "deeper" located text nodes)), execute an identity transform.
  • Otherwise your para will not be rendered.

Below you have a complete solution. I added strip-space to avoid empty output rows.

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

  <xsl:template match="para">
    <xsl:variable name="txt" select="text()[normalize-space()]"/>
    <xsl:if test="$txt">
      <xsl:copy><xsl:apply-templates select="@*|node()"/></xsl:copy>
    </xsl:if>
  </xsl:template>

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

Upvotes: 0

Michael Kay
Michael Kay

Reputation: 163302

The expression detaileddescription/para/node() does not select the simplesect/para node. It selects the simplesect element, but does not select its children. I think your confusion is about what happens after you select a node. You haven't shown us what you do with the selected nodes, but if for example you apply the xsl:copy-of instruction to the selected nodes, that will copy not only the selected nodes but all their children and descendants as well.

If you want a node to appear in the output without its children also appearing, then it's not enough to select the node, you need to transform it (specifically, to create a copy of the node that excludes its children).

Upvotes: 1

zx485
zx485

Reputation: 29022

An XPath to get only the ulink node would be

//para[count(descendant::*) &lt; 2 and  count(text()) = 0]

A full XSLT-1.0 to achieve this is

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

  <xsl:template match="text()" />

  <xsl:template match="//para[count(descendant::*) &lt; 2 and  count(text()) = 0]">
    <xsl:copy-of select="." />
  </xsl:template>

</xsl:stylesheet>

which output is

<?xml version="1.0"?>
<para>
    <ulink url="url">url description</ulink>
</para>

Upvotes: 0

Related Questions