Domanda

I have this kind of html

<span>Hello this is user874774 writing. <strong>Here is some text in another tag that I can't substring.</strong></span>

Say I would like to extract the substring(Given an number of chars as input)

<span>Hello this is user874774 writing. <strong>Here</strong></span> 

Any ideas how I can accomplish this with XSLT2.0. The substring function will of course not work because of my nested tags.

È stato utile?

Soluzione

If you want to count characters from the start of the span, but maintain the element structure of the original XML, then I think you'll have to approach it using tail-recursive templates to pass the "number of remaining characters" from one sibling node to the next. At each point check whether you've yet output the required number of characters, and if not then continue processing the next sibling node (element or text node) with the updated count:

  <!-- text nodes are output, truncated if necessary -->
  <xsl:template match="text()" mode="substr" priority="10">
    <xsl:param name="chars" as="xs:integer" tunnel="yes" />
    <xsl:value-of select="substring(., 1, $chars)" />
    <xsl:next-match/><!-- call the common node() template -->
  </xsl:template>

  <!-- for element nodes we copy the start tag and then we continue processing
       with the first child node (which may be a text node or element) -->
  <xsl:template match="*" mode="substr" priority="10">
    <xsl:copy>
      <xsl:sequence select="@*" />
      <xsl:apply-templates select="node()[1]" mode="substr"/>
    </xsl:copy>
    <xsl:next-match/><!-- call the common node() template -->
  </xsl:template>

  <!-- Ignore comments and PIs, or you might want to copy them instead -->
  <xsl:template match="comment()|processing-instruction()"
                mode="substr" priority="10">
    <xsl:apply-templates select="following-sibling::node()[1]" mode="substr" />
  </xsl:template>

  <!-- check the continuation criteria and go to next sibling if appropriate -->
  <xsl:template match="node()" mode="substr" priority="5">
    <xsl:param name="chars" as="xs:integer" tunnel="yes"/>
    <xsl:if test="$chars gt string-length(.)">
      <xsl:apply-templates select="following-sibling::node()[1]" mode="substr">
        <xsl:with-param name="chars" select="$chars - string-length(.)"
                        tunnel="yes"/>
      </xsl:apply-templates>
    </xsl:if>
  </xsl:template>

To start the process you then apply the "substr" mode templates to the first child node of your span and pass the necessary character count as a parameter

  <xsl:template match="span">
    <xsl:copy>
      <!-- <xsl:sequence select="@*" /> if the span might have attributes -->
      <xsl:apply-templates select="node()[1]" mode="substr">
        <xsl:with-param name="chars" select="38" tunnel="yes" />
      </xsl:apply-templates>
    </xsl:copy>
  </xsl:template>

Given your example input, a chars value of 38 would produce

<span>Hello this is user874774 writing. <strong>Here</strong></span>

as there are 34 characters in the first text node (between <span> and <strong>) and four from the <strong> up to the length limit. A chars value of 33 would produce just

<span>Hello this is user874774 writing.</span>

truncating the first text node after 33 characters and not including the strong at all.

Altri suggerimenti

EDIT: Response to your comment:

It could be another tag inside my span in another position with another content.

Then write separate templates and use a parameter to store a number of chars that should be preserved after substring().

There is another parameter start where you can specify the starting point for the substring function. If you always take a substring from the beginning of a string, the start could of course be set to "0".

Stylesheet

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

    <xsl:output method="xml" version="1.0" indent="yes"/>

    <xsl:param name="start" select="0"/>
    <xsl:param name="len" select="5"/>

    <xsl:template match="/span|span/*">
        <xsl:copy>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="text()[parent::span]">
        <xsl:copy/>
    </xsl:template>

    <xsl:template match="text()">
        <xsl:value-of select="substring(.,$start,$len)"/>
    </xsl:template>

</xsl:stylesheet>

Output

<?xml version="1.0" encoding="UTF-8"?>
<span>Hello this is user874774 writing. <strong>Here</strong>
</span>
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top