XSLT to Select Desired Elements When Nested In Not

2019-08-26 10:33发布

What XSLT would I use to extract some nodes to output, ignoring others, when the nodes to be be extracted are some times nested nodes to be ignored?

Consider:

<alpha_top>This prints.
  <beta>This doesn't.
    <alpha_bottom>This too prints.</alpha_bottom>
  </beta>
</alpha_top>

I want a transform that produces:

<alpha_top>This prints.
    <alpha_bottom>This too prints.</alpha_bottom>
</alpha_top>

This answer shows how to select nodes based on the presence of a string in the element tag name.

标签: xslt
4条回答
爷、活的狠高调
2楼-- · 2019-08-26 10:48

The following stylesheet works on your particular case, but I suspect you are looking for something a bit more generic. I'm also sure there is a simpler way.

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
    <xsl:apply-templates select="alpha_top"></xsl:apply-templates>
</xsl:template>

<xsl:template match="alpha_top">
    <xsl:copy>
    <xsl:apply-templates select="beta/alpha_bottom|text()"></xsl:apply-templates>
    </xsl:copy>
</xsl:template>

<xsl:template match="*|text()">
    <xsl:copy>
        <xsl:apply-templates select="*|text()"></xsl:apply-templates>
    </xsl:copy>
</xsl:template>
</xsl:stylesheet>
查看更多
甜甜的少女心
3楼-- · 2019-08-26 10:55

I think, that once you have a reasonable understand of how XSLT traversal works (hopefully I answered that in your other question) this becomes quite simple.

You have several choices on how to do this. Darrell Miller's answer shows you have to process a whole document and strip out the elements you're not interested in. That's one approach.

Before I go further, I get the impression that you might not entirely 'get' the concept of context in XSLT. This is important and will make your life simpler. At any time in XSLT there is one and only context node. This is the node (element, attribute, comment, etc) currently being 'processed'. Inside a template called via xsl:select the node that has been selected is the context node. So, given your xml:

<alpha_top>This prints.
  <beta>This doesn't.
    <alpha_bottom>This too prints.</alpha_bottom>
  </beta>
</alpha_top>

and the following:

<xsl:apply-templates select='beta'/>

and

<xsl:template match='beta'>...</xsl:template>

the beta node will be the context node inside the template. There's a bit more to it than that but not much.

So, when you start your stylesheet with something like:

<xsl:template match='/'>
    <xsl:apply-templates select='alpha_top'/>
</xsl:apply-templates>

you are selecting the children of the document node (the only child element is the alpha_top element). Your xpath statement inside there is relative to the context node.

Now, in that top level template you might decide that you only want to process your alpha_bottom nodes. Then you could put in a statement like:

<xsl:template match='/>
    <xsl:apply-templates select='//alpha_top'/>
</xsl:template>

This would walk down the tree and select all alpha_top elements and nothing else.

Alternatively you could process all your elements and simply ignore the content of the beta node:

<xsl:template match='beta'>
    <xsl:apply-templates/>
</xsl:template>

(as I mentioned in my other reply to you xsl:apply-templates with no select attribute is the same as using select=''*).

This will ignore the content of the beta node but process all of it's children (assuming you have templates).

So, ignoring elements in your output is basically a matter of using the correct xpath statements in your select attributes. Of course, you might want a good xpath tutorial :)

查看更多
看我几分像从前
4楼-- · 2019-08-26 11:01

Ok, here is a better way

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

<xsl:template match="beta">
    <xsl:apply-templates select="*"></xsl:apply-templates>
</xsl:template>

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

</xsl:stylesheet>

This basically does an identity transform, but for the element you don't want to include I removed the xsl:copy and only applied templates on the child elements.

查看更多
小情绪 Triste *
5楼-- · 2019-08-26 11:13

The probably simplest solution to your problem is this:

<xsl:template match="alpha_top|alpha_bottom">
  <xsl:copy>
    <xsl:value-of select="text()" />
    <xsl:apply-templates />
  </xsl:copy>
</xs:template>

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

This does not exhibit the same white-space behavior you have in your example, but this is probably irrelevant.

查看更多
登录 后发表回答