简体   繁体   中英

XSLT get all nodes with text

I'm trying to loop through all nodes with text in my document from a certain location.

<xsl:template name="interpret_text">
  <xsl:param name="location"/>
  <xsl:for-each select="$location//text()">
    <xsl:choose>
      <xsl:when test="name(.) = tag_im_looking_for">
        <!-- various code stuff and closing tags -->

This code is functional except you may notice my problem. When I enter the for-each loop, the text has forgotten its tag. the value of current() is the raw text, and no longer remembers its owner. I tried to adjust my algorithm to select nodes and then only parsing those with text. Like so:

<xsl:template name="interpret_text">
  <xsl:param name="location"/>
  <xsl:for-each select="$location//node()">
    <xsl:if test="not(text() = '')">
      <xsl:choose>
        <xsl:when test="name(.) = tag_im_looking_for">
          <!-- various code stuff and closing tags -->

However somehow this algorithm runs in a problem for me. Suppose the xml below does not have extra whitespace that needs to be normalized.

<a>
  <b>
  before
    <c>inner text</c>
  after 
  </b>
</a>

Top algorthim will run in this order.

before
inner text
after

The bottom will run in this order

b context
c context

But there is text before and after the c context, and I need to parse the "before", then the "inner text", then the "after". Note I need this algorithm to work for any depth and with or without the "before" or "after" text. Is there and easy way from the node solution or the text solution to get my desired result?

It's unclear to me what location is, but this sample:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes"/>
<xsl:template match="/">
    <sample>        <xsl:call-template name="interpret_text">
        <xsl:with-param name="location" select="."/>
    </xsl:call-template>
    </sample>

</xsl:template>
<xsl:template name="interpret_text">
    <xsl:param name="location"/>
    <xsl:for-each select="$location//text()">
        <xsl:if test="string-length(normalize-space(.))>0">
        <elem>
            Parent: <xsl:value-of select="name(parent::*)"/>, Text: <xsl:value-of select="normalize-space(.)"/>
        </elem>
        </xsl:if>
    </xsl:for-each>
</xsl:template>
</xsl:stylesheet>

Would yield this output with your input sample:

<sample>
  <elem>
            Parent: b, Text: before</elem>
  <elem>
            Parent: c, Text: inner text</elem>
  <elem>
            Parent: b, Text: after</elem>
</sample

So you can get the parent::* and operate on it's name to do what you wish.

I had to guess a little about your actual needs, but this should be pretty close.

Stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/root">
    <output>
      <xsl:apply-templates match="foo"/>
    </output>
  </xsl:template>
  <xsl:template name="interpret-text">
    <xsl:for-each select=".//interesting-tag[text()]">
      <xsl:value-of select="text()"/>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

Document:

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="style.xslt"?>
<root>
  <foo>
    <bar>
      <interesting-tag>
        Hello
      </interesting-tag>
      <interesting-tag>
        World
      </interesting-tag>
    </bar>
    <interesting-tag>
      o11c was here
    </interesting-tag>
  </foo>
</root>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM