0

以下应该是简单或不可能做到的,但现在我不知道怎么做,所以我问。在我的 XSLT 中,我有生成元素的模板,然后应该再次对其进行转换。更精确的是,每当模板输出原始输入中将要进行转换的元素时,它应该再次进行转换。因此,无限循环是可能的,但要通过仔细设计模板来避免。举个例子:

输入.xml

<?xml version="1.0" encoding="utf-8" ?>
<example>
    <a />
    <b />
</example>

变换.xsl

<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="@*|node()"><xsl:copy>
        <xsl:apply-templates select="@*|node()" />
    </xsl:copy></xsl:template>

    <xsl:template match="a">a</xsl:template>

    <xsl:template match="b">
        <B>b <a /></B>
    </xsl:template>
</xsl:transform>

当前输出.xml

<?xml version="1.0"?>
<example>
    a
    <B>b <a/></B>
</example>

所需的输出.xml

<?xml version="1.0"?>
<example>
    a
    <B>b a</B>
</example>

如果有的话,通过一次转换来实现这一目标的最佳解决方案是什么?

4

3 回答 3

2

这是产生所需结果的两遍转换:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">

 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

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

 <xsl:template match="/">
  <xsl:variable name="vrtfPass1">
    <xsl:apply-templates/>
  </xsl:variable>

  <xsl:variable name="vPass1" select="ext:node-set($vrtfPass1)/*"/>

  <xsl:apply-templates select="$vPass1" mode="pass2"/>
 </xsl:template>

 <xsl:template match="content">
   <wrapper>
     <replace />
     <xsl:apply-templates select="@*|node()" />
   </wrapper>
 </xsl:template>

 <xsl:template match="replace">
  <xsl:text>&#xA;Hello world&#xA;</xsl:text>
 </xsl:template>

 <xsl:template match="@*|node()" mode="pass2">
  <xsl:call-template name="identity"/>
 </xsl:template>
</xsl:stylesheet>

应用于提供的 XML 文档时

<example>
    <content>Lorem ipsum</content>
    <content><replace /></content>
</example>

产生了想要的正确结果

<example>
   <wrapper>
Hello world
Lorem ipsum</wrapper>
   <wrapper>
Hello world

Hello world
</wrapper>
</example>

请注意

在 XSLT 1.0 中,应用模板的结果是臭名昭著的 RTF(结果树片段)类型,根据定义,它不能被进一步访问和处理,除非使用xsl:copy-of标准字符串函数。

这就是为什么几乎每个 XSLT 1.0 处理器都提供了一个特定于供应商的扩展函数xxx:node-set(),该函数接受一个 RTF 并将其转换为一个“常规”树,可以使用任何 XPath 表达式访问其后代。此处的xxx前缀必须绑定到供应商特定的命名空间 uri。

EXSLText:node-set()由大多数 XSLT 处理器实现——因此它的使用保证了不同 XSLT 处理器之间的可移植性。

有关其他多通道转换示例,请参见

https://stackoverflow.com/a/3200026/36305

http://www.dpawson.co.uk/xsl/sect1/N169.html#d860e392

于 2012-10-06T18:25:30.147 回答
1

它并非在所有情况下都有效,但是有一种非常简单的方法可以实现您的要求。你需要做的就是给你的replace模板一个名字,然后用xsl:call-template. 这只需要对现有样式表进行一些小的更改:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="@*|node()"><xsl:copy>
        <xsl:apply-templates select="@*|node()" />
    </xsl:copy></xsl:template>

    <xsl:template match="content">
        <wrapper>
            <xsl:call-template name="replace"/>
            <xsl:apply-templates select="@*|node()" />
        </wrapper>
    </xsl:template>

    <xsl:template match="replace" name="replace">
        Hello world
    </xsl:template>
</xsl:transform>
于 2012-10-07T19:09:59.243 回答
0

根据其他答案的输入,我想出了解决我的问题的方法。考虑作为更合适的示例输入:

<?xml version="1.0" encoding="utf-8" ?>
<example>
    <a />
    <b />
    <c />
</example>

元素<a />,<b /><c />的使用与<replace />我的原始示例一样,但如转换中所示,它们的每个模板都包含对它们的进一步引用:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
exclude-result-prefixes="ext">

    <xsl:template match="@*|node()">
        <xsl:variable name="this">
            <xsl:apply-templates select="." mode="normal" />
        </xsl:variable>
        <xsl:apply-templates select="ext:node-set($this)" mode="normal" />
    </xsl:template>

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

    <xsl:template match="a" mode="normal">a</xsl:template>

    <xsl:template match="b" mode="normal">
        <B>b <a /></B>
    </xsl:template>

    <xsl:template match="c" mode="normal">
        <C>c <b /></C>
    </xsl:template>
</xsl:transform>

当应用于示例输入时,此转换会产生我想要的结果:

<?xml version="1.0"?>
<example>
    a
    <B>b a</B>
    <C>c <B>b a</B></C>
</example>

显然所有递归引用都已解决。尽管它使用一些更复杂的示例进行了测试,但我不确定它是否适用于所有情况。

这背后的想法如下:前两个模板用于复制。一个mode="normal"是一个简单的身份转换,一个没有做递归。使用模式是为了防止递归模板中的无限循环。

请告诉我,您对此有何看法以及其中是否存在明显缺陷!

于 2012-10-07T22:12:11.317 回答