多字符串替换

时间:2014-03-26 02:46:32

标签: xml xslt xslt-2.0

我试图使用XSLT 2.0来替换任意数量的子串。让我们说我的XML看起来像这样:

<scheme_template id="job"
    use_when_ref="action">
    <![CDATA[
    <p>
        @job.fluff@  Haul is based on 
        <b>@job.stat@:</b>
    </p>
    <table class="scheme_job_table">
        <tr>
            <td>2 or less</td>
            <td>@job.low@</td>
        </tr>
        <tr>
            <td>3-5</td>
            <td>@job.middle@</td>
        </tr>
        <tr>
            <td>6 or more</td>
            <td>@job.high@</td>
        </tr>
    </table>
    ]]>
</scheme_template>

<scheme name="JOB! CHICKEN SOUP FOR THE SOULLESS"
    copies="1">
    <use_scheme_template id_ref="job">
        <property id="job.fluff">
            Chose 1 of your monsters to make and sell 
            heart-warming books of life-affirming awwwww.
        </property>
        <property id="job.stat">Smart</property>
        <property id="job.low">$4</property>
        <property id="job.middle">$6</property>
        <property id="job.high">$8</property>
    </use_scheme_template>
</scheme>

我想使用XSL转换来放置所有&#34;属性&#34;值进入方案模板。我的(有缺陷的)XSL看起来像这样:

<xsl:template match="use_scheme_template" mode="expand_template">
    <xsl:param name="template_id" select="@id_ref"/>
    <xsl:param name="base_text" select="//scheme_template[@id=$template_id]"/>
    <xsl:variable name="expanded_text">
        <xsl:apply-templates select="property" mode="replace_props">
            <xsl:with-param name="base_text" select="$base_text"/>
        </xsl:apply-templates>
    </xsl:variable>
    <xsl:value-of select="$expanded_text" disable-output-escaping="yes" />
</xsl:template>

<xsl:template match="property" mode="replace_props">
    <xsl:param name="base_text"/>
    <xsl:value-of select="replace($base_text, concat('@', @id, '@'), text())"/>
</xsl:template>

但这只取代了第一个属性。

在同一个字符串上运行replace任意次数需要做什么?

2 个答案:

答案 0 :(得分:2)

我会考虑使用xsl:analyze-string代替replace来解决这个问题。以下内容将在文本中搜索@anything@子字符串,并用匹配的属性值替换它们(如果存在这样的属性),保留文本的其余部分不变:

<xsl:template match="use_scheme_template" mode="expand_template">
    <xsl:param name="template_id" select="@id_ref"/>
    <xsl:param name="base_text" select="//scheme_template[@id=$template_id]"/>

    <xsl:variable name="properties" select="property" />
    <xsl:variable name="expanded_text">
      <xsl:analyze-string select="$base_text" regex="@(.*?)@">
        <xsl:matching-substring>
          <!-- substitute the matching property value, if there is one,
               or leave untouched if not -->
          <xsl:value-of select="($properties[@id = regex-group(1)], .)[1]" />
        </xsl:matching-substring>
        <xsl:non-matching-substring>
          <!-- leave non-matching parts unchanged -->
          <xsl:value-of select="." />
        <xsl:non-matching-substring>
      </xsl:analyze-string>
    </xsl:variable>
    <xsl:value-of select="$expanded_text" disable-output-escaping="yes" />
</xsl:template>

答案 1 :(得分:1)

不要将 replace_props 模板初始应用于所有属性,而应考虑将其应用于第一个属性:

   <xsl:apply-templates select="property[1]" mode="replace_props">
        <xsl:with-param name="base_text" select="$base_text"/>
    </xsl:apply-templates>

然后,递归实际的 replace_props 模板。然后逻辑是创建一个带有当前替换文本的变量。然后,您将检查是否有以下属性元素可用。如果是这样,使用新替换的文本递归调用模板,否则输出文本:

试试这个......

<xsl:template match="property" mode="replace_props">
    <xsl:param name="base_text"/>
    <xsl:variable name="expanded_text" select="replace($base_text, concat('@', @id, '@'), text())"/>
    <xsl:choose>
        <xsl:when test="following-sibling::property">
            <xsl:apply-templates select="following-sibling::property[1]" mode="replace_props">
                <xsl:with-param name="base_text" select="$expanded_text"/>
            </xsl:apply-templates>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$expanded_text"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

...但是当你运行它时,你可能会发现“job.low”,“job.middle”和“job.high”值都是空的。这是因为$符号在替换函数中具有特殊含义(替换的第二个参数可以使用正则表达式,第二个参数中的$可以用于输出匹配模式的值)

这意味着您需要做一些额外的工作来逃避$符号。请尝试使用此模板

<xsl:template match="property" mode="replace_props">
    <xsl:param name="base_text"/>
    <xsl:variable name="text" select="replace(text(), '\$', '\\\$')" />
    <xsl:variable name="expanded_text" select="replace($base_text, concat('@', @id, '@'), $text)"/>
    <xsl:choose>
        <xsl:when test="following-sibling::property">
            <xsl:apply-templates select="following-sibling::property[1]" mode="replace_props">
                <xsl:with-param name="base_text" select="$expanded_text"/>
            </xsl:apply-templates>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$expanded_text"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>