【问题标题】:Substituting values over many XML elements [XSLT 2.0]在许多 XML 元素上替换值 [XSLT 2.0]
【发布时间】:2011-04-06 22:12:08
【问题描述】:

这个问题是this one 的扩展。我有很多价值观,如下所示:

<myStuff>
    <elem1>asdf</elem1>
    <elem2>foo bar</elem2>
    <elem3>foo bar foo</elem3>
    <elem4>foofoo</elem4>
</myStuff>

我一直在对大多数元素进行copy-ofcopy-of 中的select 非常具体),然后对生成的 XML 进行查找和替换,但我想将两者结合起来。在我应用此功能的大多数情况下,我会将&lt;xsl:value-of select="whatever"&gt; 中的任何内容替换为&lt;xsl:apply-templates select="whatever"&gt; 并使用以下模板:

<xsl:template match="*">
    <xsl:variable name="rep1">
        <xsl:choose>
            <xsl:when test='matches(., ".*foo.*")'>
                <xsl:value-of select='replace(., "foo", "qwerty")' />
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select='./text()' />
            </xsl:otherwise>
        </xsl:choose>
    </xsl:variable>
    <xsl:variable name="rep2">
        <xsl:choose>
            <xsl:when test='matches(., ".*bar.*")'>
                <xsl:value-of select='replace($rep1, "bar", "myBar")' />
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select='$rep1' />
            </xsl:otherwise>
        </xsl:choose>
    </xsl:variable>
    <xsl:value-of select="$rep2" />
</xsl:template>

我想使用类似的模板来替换我的代码中的copy-of(因为我在我的其他文件中进行相同的替换,所以我可以使用相同的模板),但我不确定如何做到这一点。

输出如下所示:

<myOtherStuff>
    <elem1>asdf</elem1>
    <elem2>qwerty myBar</elem2>
    <elem3>qwerty myBar qwerty</elem3>
    <elem4>qwertyqwerty</elem4>
</myOtherStuff>

感谢所有帮助,并提前致谢!

【问题讨论】:

  • 好问题 (+1)。请参阅我的答案以获得完整的解决方案。

标签: xml xslt replace xslt-2.0


【解决方案1】:

使用另一种方法,这个样式表:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes"/>
    <xsl:variable name="vReps" as="element()*">
        <rep target="foo" newval="qwerty"/>
        <rep target="bar" newval="myBar"/>
    </xsl:variable>
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="text()">
        <xsl:analyze-string select="." 
         regex="{string-join($vReps/concat('(',@target,')'),'|')}">
            <xsl:matching-substring>
                <xsl:value-of select="$vReps[@target=current()]/@newval"/>
            </xsl:matching-substring>
            <xsl:non-matching-substring>
                <xsl:value-of select="."/>
            </xsl:non-matching-substring>
        </xsl:analyze-string>
    </xsl:template>
</xsl:stylesheet>

输出:

<myStuff>
    <elem1>asdf</elem1>
    <elem2>qwerty myBar</elem2>
    <elem3>qwerty myBar qwerty</elem3>
    <elem4>qwertyqwerty</elem4>
</myStuff>

另外,这个输入(来自 cmets):

<myStuff>
    <elem>asdfooasdf</elem>
</myStuff>

输出:

<myStuff>
    <elem>asdqwertyasdf</elem>
</myStuff>

注意:这个 RegExp 联合一次执行所有替换。这可能与按顺序调用fn:replace 不同:假设此替换针对此字符串“AB”上的“A”->“B”和“B”->“C”,联合替换应输出“BC”并按顺序调用输出“抄送”。

注意 2:关于匹配顺序,请注意 RegExp 联合遵循其自己的规则(参见specs,更具体的If two alternatives within the supplied $pattern both match at the same position in the $input string, then the match that is chosen is the first.)并且顺序地fn:replace 调用严格遵循用户定义顺序。

【讨论】:

  • 我认为这是可行的,但后来意识到以下情况失败了:&lt;elem&gt;asdfooasdf&lt;/elem&gt;。没有更换。你能重现这个吗?
  • @adam_0:您好!另外,不要害怕声明自己的函数。
  • 很好的解决方案,Alejandro (+1)。但是,您的解决方案和我的解决方案存在一个根本区别。您的解决方案按照输入中目标的出现顺序执行更改。我的按照 $vReps 中的规范顺序执行它们。这两种处理可能会产生不同的结果,所以我认为我的解决方案给了用户更多的控制权。
  • @Dimitre:你是对的!我应该说之前。添加注释。
  • @Alejandro: 一个更好的例子是:corelation,如果第一个规则是core,结果会有所不同--> kernel,第二个是relation--> @987654334 @ .如果你颠倒规则的顺序,你会得到不同的结果!
【解决方案2】:

这种转变

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 xmlns:my="my:my" >
 <xsl:output omit-xml-declaration="yes"/>

  <xsl:variable name="vReps" as="element()*">
   <rep target="foo" newval="qwerty"/>
   <rep target="bar" newval="myBar"/>
  </xsl:variable>

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

    <xsl:template match="text()">
      <xsl:sequence select="my:myMultiRep(., $vReps)"/>
    </xsl:template>

    <xsl:function name="my:myMultiRep" as="xs:string">
      <xsl:param name="pText" as="xs:string"/>
      <xsl:param name="pReps" as="element()*"/>

      <xsl:sequence select=
       "if(not($pReps))
         then $pText
         else my:myMultiRep(replace($pText, $pReps[1]/@target, $pReps[1]/@newval),
                            subsequence($pReps, 2)
                           )
       "/>
    </xsl:function>
</xsl:stylesheet>

应用于提供的 XML 文档时

<myStuff>
    <elem1>asdf</elem1>
    <elem2>foo bar</elem2>
    <elem3>foo bar foo</elem3>
    <elem4>foofoo</elem4>
</myStuff>

产生想要的正确结果

<myStuff>
    <elem1>asdf</elem1>
    <elem2>qwerty myBar</elem2>
    <elem3>qwerty myBar qwerty</elem3>
    <elem4>qwertyqwerty</elem4>
</myStuff>

【讨论】:

  • my:my 的全部内容是关于什么的?
  • @Dimitre:+1 好答案!我借了你的变量;)
  • @adam_0: 用户定义的函数必须在命名空间下。
  • @adam, @Alejandro:即使是标准函数也在命名空间下,但不需要通过前缀指定。没有&lt;xsl:function&gt; 名称可以在“无命名空间”中。
  • @Dimitre:你是对的。我认为这是更恰当的说法。
猜你喜欢
  • 2021-11-09
  • 2021-05-06
  • 2014-07-26
  • 1970-01-01
  • 1970-01-01
  • 2018-04-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多