(xslt 1.0) 如何用 xml 中所有文本值中的某个字符串替换空格?

发布于 2024-08-23 00:45:26 字数 3519 浏览 5 评论 0原文

编辑: [它从字符替换开始 我最终发现了字符串 借助 迪米特雷 诺瓦切夫罗兰·布曼

我认为示例代码足以解释需求..

这是示例 XML:

<root>
  <node1>text node</node1>
  <node2>space between the text</node2>
  <node3> has to be replaced with $</node3>
</root>

这是我期望的输出:

<root>
  <node1>text$node</node1>
  <node2>space$between$the$text</node2>
  <node3>$has$to$be$replaced$with$$</node3>
</root>

我尝试编写一个XSLT 代码未显示所需的输出..
这是代码:

    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
  <xsl:template match="text()[.!='']">
    <xsl:call-template name="rep_space">
      <xsl:with-param name="text" select="."/>
    </xsl:call-template>
  </xsl:template>
  <xsl:template name="rep_space">
    <xsl:param name="text"/>
    <xsl:variable name="temp" select="'&#x36;'"/> 
    <xsl:choose>
      <xsl:when test="contains(text,'&#x32;')">
        <xsl:call-template name="rep_space">
          <xsl:with-param name="text" select="concat((concat(substring-before(text,' '),temp)),substring-after(text,' '))"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="text"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

translate(., ' ', '$')函数可以工作..但没有达到令人满意的程度..我的问题是..如果它是一个字符串怎么办 而不是性格?我的意思是,假设 我打算将 ' ' 替换为 “%20”?还有一种情况,如果 输入 XML 不是“Pretty Print XML”, 那么 XML 中出现的所有空格 被替换为“$”..

Pretty print XML 是具有正确缩进的文件,(通常我的输入 XML 从来没有这个)例如:

多一个节点 这是@较低级别

您可以观察到,之前没有“空格字符” 节点,但它们实际上是正确缩进的,(使用 altova XMLSPY,我们可以在编辑菜单中给出一个简单的命令.. 使任何 XML 文件“漂亮地打印 XML”)..

如下所示示例 ..

<new>
  <test>one more node</test>
   <test2>
    <child>this is @ lower level</child>
   </test2>
</new>

所有开始标记之前都有空格字符 .. 标记之前比 节点有更多空格 ..

对于第二个示例xml ..所有空格字符都被“%20”替换..因此输出将是..

<new>
%20%20<test>one%20more%20node</test>
%20%20<test2>
%20%20%20%20<child>this%20is%20@%20lower%20level</child>
%20%20</test2>
</new>

当然这不是预期的..

发布的解决方案 Dimitre 诺瓦切夫Roland Bouman 还可以用另一个字符串替换一个字符串,方法是 修改传递给的参数 正在调用模板。

这是很棒的学习@Dimitre, @Roland,我真的很感激 谢谢你们..

问候,
婴儿专业人士。

EDIT: [it started with character replacement
and I ended up with discovering string
replacements with help of Dimitre
Novatchev
and Roland Bouman

I think the sample codes are sufficient to explain the requirements ..

This is the sample XML:

<root>
  <node1>text node</node1>
  <node2>space between the text</node2>
  <node3> has to be replaced with 
lt;/node3>
</root>

This is the Output I am expecting:

<root>
  <node1>text$node</node1>
  <node2>space$between$the$text</node2>
  <node3>$has$to$be$replaced$with$</node3>
</root>

I have tried writing an XSLT code which isn't showing the required output ..

This is the code:

    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
  <xsl:template match="text()[.!='']">
    <xsl:call-template name="rep_space">
      <xsl:with-param name="text" select="."/>
    </xsl:call-template>
  </xsl:template>
  <xsl:template name="rep_space">
    <xsl:param name="text"/>
    <xsl:variable name="temp" select="'6'"/> 
    <xsl:choose>
      <xsl:when test="contains(text,'2')">
        <xsl:call-template name="rep_space">
          <xsl:with-param name="text" select="concat((concat(substring-before(text,' '),temp)),substring-after(text,' '))"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="text"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

translate(., ' ', '$')function works .. but not to the satisfactory extent .. my questions are .. what if it is a string
instead of character? I mean, suppose
I am intended to replace ' ' with
"%20"? And one more case, What if the
input XML isn't "Pretty Print XML",
then all the space appearing in XML
are replaced with '$' ..

Pretty print XML is the file which has proper indent, (Usually my input XMLs never have this) for example:

one more node

this is @ lower level

You can observe, there are no "space characters" before <new> <test> nodes but they are actually properly indented, (With altova XMLSPY we can give a simple command in edit menu .. to make any XML files to "pretty print XML") ..

Where as in the below example ..

<new>
  <test>one more node</test>
   <test2>
    <child>this is @ lower level</child>
   </test2>
</new>

There are space chars before all the start tags .. <child> tag has more spaces before it than <test2> node ..

With the second sample xml .. all the space chars are replaced by "%20".. hence the output will be ..

<new>
%20%20<test>one%20more%20node</test>
%20%20<test2>
%20%20%20%20<child>this%20is%20@%20lower%20level</child>
%20%20</test2>
</new>

certainly it is not expected ..

The solutions posted by Dimitre
Novatchev
and Roland Bouman can also replace a string by another string, by
modifying the parameters passed to the
template being called.

That was great learning @Dimitre,
@Roland, I am really thankful and
grateful to you guys ..

regards,
infant pro.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

倾城月光淡如水﹏ 2024-08-30 00:45:26

根据Roland的愿望,这里是一个尾递归解决方案

 <xsl:template name="replace">
  <xsl:param name="ptext"/>
  <xsl:param name="ppattern"/>
  <xsl:param name="preplacement"/>

  <xsl:choose>
     <xsl:when test="not(contains($ptext, $ppattern))">
      <xsl:value-of select="$ptext"/>
     </xsl:when>
     <xsl:otherwise>
       <xsl:value-of select="substring-before($ptext, $ppattern)"/>
       <xsl:value-of select="$preplacement"/>
       <xsl:call-template name="replace">
         <xsl:with-param name="ptext"
           select="substring-after($ptext, $ppattern)"/>
         <xsl:with-param name="ppattern" select="$ppattern"/>
         <xsl:with-param name="preplacement" select="$preplacement"/>
       </xsl:call-template>
     </xsl:otherwise>
  </xsl:choose>
 </xsl:template>

请注意递归调用是模板中的最后一条指令——这就是它成为尾递归的原因。尾递归的属性允许智能 XSLT 处理器(例如 Saxon 或 .NET XslCompiledTransform)优化代码,用简单迭代代替递归。

即使调用的“嵌套”数百万个,这样的代码也不会以堆栈溢出异常结束,而非尾递归(和递归)代码通常会在大约 1000 个嵌套调用的深度处引发此堆栈溢出异常(这实际上取决于可用内存的大小)。

如果 XSLT 处理器不够“智能”怎么办?是否有另一种技术可以避免深层递归调用堆栈溢出,并且适用于每个 XSLT 处理器?

问我一个单独的问题,我可能会告诉你:)

As per the wish of Roland, here is a tail-recursive solution:

 <xsl:template name="replace">
  <xsl:param name="ptext"/>
  <xsl:param name="ppattern"/>
  <xsl:param name="preplacement"/>

  <xsl:choose>
     <xsl:when test="not(contains($ptext, $ppattern))">
      <xsl:value-of select="$ptext"/>
     </xsl:when>
     <xsl:otherwise>
       <xsl:value-of select="substring-before($ptext, $ppattern)"/>
       <xsl:value-of select="$preplacement"/>
       <xsl:call-template name="replace">
         <xsl:with-param name="ptext"
           select="substring-after($ptext, $ppattern)"/>
         <xsl:with-param name="ppattern" select="$ppattern"/>
         <xsl:with-param name="preplacement" select="$preplacement"/>
       </xsl:call-template>
     </xsl:otherwise>
  </xsl:choose>
 </xsl:template>

Note that the recursive call is the last instruction in the template -- this is what makes it tail-recursive. The property of being tail-recursive allows a smart XSLT processor (such as Saxon or .NET XslCompiledTransform) to optimize the code, replacing the recursion with simple iteration.

Such code will not end up with a stack-overflow exception even when the "nesting" of calls is millions, whereas non-tail-recursive (and recursive) code typically raises this stack-overflow at a depth of about 1000 nested calls (this really depends on the amount of the available memory).

What if the XSLT processor is not "smart enough"? Is there another technique to avoid deep-level recursive calls stack overflow, that works with every XSLT processor?

Ask me in a separate question and I might tell you :)

十二 2024-08-30 00:45:26

查看 XPath 翻译功能:
http://www.w3.org/TR/xpath/#function-translate< /a>

<xsl:template match="text()">
    <xsl:value-of select="translate(., ' ', '

如果不是单个字符,而是需要替换的字符串,则需要花费更多精力,并且需要一个模板来递归替换字符串:

<xsl:template match="text()[not(../*)]">
    <xsl:call-template name="replace">
        <xsl:with-param name="text" select="."/>
        <xsl:with-param name="search" select="' '"/>
        <xsl:with-param name="replace" select="'%20'"/>
    </xsl:call-template>
</xsl:template>

<xsl:template name="replace">
    <xsl:param name="text"/>
    <xsl:param name="search"/>
    <xsl:param name="replace"/>
    <xsl:choose>
        <xsl:when test="contains($text, $search)">
            <xsl:variable name="replace-next">
                <xsl:call-template name="replace">
                    <xsl:with-param name="text" select="substring-after($text, $search)"/>
                    <xsl:with-param name="search" select="$search"/>
                    <xsl:with-param name="replace" select="$replace"/>
                </xsl:call-template>
            </xsl:variable>
            <xsl:value-of 
                select="
                    concat(
                        substring-before($text, $search)
                    ,   $replace
                    ,   $replace-next
                    )
                "
            />
        </xsl:when>
        <xsl:otherwise><xsl:value-of select="$text"/></xsl:otherwise>
    </xsl:choose>
</xsl:template>

编辑:将 match="text()" 更改为
match="text()[not(../*)]",这样
输入 xml 不必是一种
“漂亮打印 XML”..(以便删除
不需要的空间替换
此类 xml 文件中的“%20”字符串)

)"/> </xsl:template>

如果不是单个字符,而是需要替换的字符串,则需要花费更多精力,并且需要一个模板来递归替换字符串:

编辑:将 match="text()" 更改为
match="text()[not(../*)]",这样
输入 xml 不必是一种
“漂亮打印 XML”..(以便删除
不需要的空间替换
此类 xml 文件中的“%20”字符串)

Check out the XPath translate function:
http://www.w3.org/TR/xpath/#function-translate

<xsl:template match="text()">
    <xsl:value-of select="translate(., ' ', '

If it's not a single character, but a string you have to replace, it takes considerably more effort, and you need a template to recursively replace the string:

<xsl:template match="text()[not(../*)]">
    <xsl:call-template name="replace">
        <xsl:with-param name="text" select="."/>
        <xsl:with-param name="search" select="' '"/>
        <xsl:with-param name="replace" select="'%20'"/>
    </xsl:call-template>
</xsl:template>

<xsl:template name="replace">
    <xsl:param name="text"/>
    <xsl:param name="search"/>
    <xsl:param name="replace"/>
    <xsl:choose>
        <xsl:when test="contains($text, $search)">
            <xsl:variable name="replace-next">
                <xsl:call-template name="replace">
                    <xsl:with-param name="text" select="substring-after($text, $search)"/>
                    <xsl:with-param name="search" select="$search"/>
                    <xsl:with-param name="replace" select="$replace"/>
                </xsl:call-template>
            </xsl:variable>
            <xsl:value-of 
                select="
                    concat(
                        substring-before($text, $search)
                    ,   $replace
                    ,   $replace-next
                    )
                "
            />
        </xsl:when>
        <xsl:otherwise><xsl:value-of select="$text"/></xsl:otherwise>
    </xsl:choose>
</xsl:template>

Edit:changed match="text()" to
match="text()[not(../*)]", so that the
input xml need not be a kind of
"pretty print XML" .. (so as to remove
unwanted replacements of space with
"%20" string in such xml file)

)"/> </xsl:template>

If it's not a single character, but a string you have to replace, it takes considerably more effort, and you need a template to recursively replace the string:

Edit:changed match="text()" to
match="text()[not(../*)]", so that the
input xml need not be a kind of
"pretty print XML" .. (so as to remove
unwanted replacements of space with
"%20" string in such xml file)

蓝戈者 2024-08-30 00:45:26

“预打印 xml”的解决方案并不是真正的解决方案。

想象一下有一个这样的文档:

<a>
 <b>
  <c>O M G</c>
  <d>D I Y</d>
 </b>
</a>

当前接受的解决方案的输出(将其包装在 并添加标识规则是:

<a>
%20<b>
%20%20<c>O$M$G</c>
%20%20<d>D$I$Y</d>
%20</b>
</a>

现在,为什么建议的解决方法不保存这种情况?正如我们从上面的示例中看到的,一个元素可以有多个具有文本节点的子元素...

真正的解决方案是什么

XSLT 的创建者已经考虑过使用正确的术语,我们希望 XSLT 处理器忽略所有无关紧要的纯空白文本节点。根本不是文档树的一部分。这是通过 < 实现的。 strong>指令

只需在全局级别添加(作为 < 的子级)。 ;xsl:stylesheet> 并且为了可读性,位于任何模板之前):

 <xsl:strip-space elements="*"/>

现在您确实有了一个可行的解决方案。

The solution to the "prety-printed xml" is not really a solution.

Imagine having a document like this:

<a>
 <b>
  <c>O M G</c>
  <d>D I Y</d>
 </b>
</a>

The output from the currently accepted solution (after wrapping it in an <xsl:stylesheet> and adding the identity rule is:

<a>
%20<b>
%20%20<c>O$M$G</c>
%20%20<d>D$I$Y</d>
%20</b>
</a>

Now, why doesn't the proposed workaround save the situation? As we see from the above example, an element can have more than one child element that has text nodes...

What is the real solution?

The creators of XSLT have thought about this problem. Using the right terminology, we want all insignificant white-space-only text nodes to be ignored by the XSLT processor, as if they were not part of the document tree at all. This is achieved by the <xsl:strip-space> instruction.

Just add this at a global level (as a child of <xsl:stylesheet> and, for readability, before any templates):

 <xsl:strip-space elements="*"/>

and now you really have a working solution.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文