【问题标题】:Using XSLT to change surrounding data based on an empty tag使用 XSLT 根据空标签更改周围数据
【发布时间】:2010-08-25 00:38:47
【问题描述】:

在尝试制作样式表以转换使用非常过时的 SGML DTD 进行格式化的书籍的旧 LoC 转录本的过程中,我遇到了以下情况:

在转换后的 XML 文件中,有如下几行文本:

<p> Text on left <hsep></hsep> Text on right </p>

hsep 本质上是推动剩余的文本右对齐。不幸的是,我不知道通过转换标签将其转换为 HTML 的任何方法,因为 HTML 没有像 hsep 这样的可疑 CSS hacks。我认为能够将其转换为以下内容会更有用:

<p> Text on left <span class="right">Text on right</span> </p>

但是,我不确定如何执行此操作,因为它需要在 <p> 元素中确定是否存在 <hsep>,然后根据剩余文本的存在创建一个围绕剩余文本的标签,同时还将模板应用于可能存在的任何元素。我不认为我有类似的情况

<p> Text a <em> Text b <hsep></hsep> Text c </em> </p>

很常见甚至存在,所以我认为这不会造成问题,但可能会出现以下情况:

<p> <em> Text a Text b <hsep></hsep> Text c </em> </p>

我能想到涉及正则表达式的复杂、可怕的方法,但我希望有一种不可怕的方法。

【问题讨论】:

  • 实际上,在更仔细地检查 DTD 时,事实证明 hsep 可以表示文本之间的任何间距(通常类似于制表符);在实践中,我通常在目录中看到它来分隔页码。
  • 好问题 (+1)。请参阅我的答案以获得完整而简单的解决方案。 :)

标签: html xml xslt


【解决方案1】:

创建一个围绕其余部分的标签 基于它存在的文本,而 还将模板应用于任何 可能存在的元素

我认为为了更好地进行转发处理,您可以使用此样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="node()|@*" name="identity">
        <xsl:copy>
            <xsl:apply-templates select="node()[1]|@*"/>
        </xsl:copy>
        <xsl:apply-templates select="following-sibling::node()[1]"/>
    </xsl:template>
    <xsl:template match="hsep">
        <span class="right">
            <xsl:apply-templates select="following-sibling::node()[1]"/>
        </span>
    </xsl:template>
</xsl:stylesheet>

根据 Dimitre 的意见:

<html>
  <p> Text a <em> Text b <hsep></hsep> Text c </em> </p>
  <p> <em> Text a Text b <hsep></hsep> Text c </em> </p>
</html>

输出:

<html>
<p> Text a <em> Text b <span class="right"> Text c </span></em></p>
<p><em> Text a Text b <span class="right"> Text c </span></em></p>
</html>

注意:在 out 模式下,您可以为 hsep 之前或之后的元素声明一次规则。

【讨论】:

  • 嗨,亚历杭德罗。您能解释一下您的方法是如何工作的,以及为什么它具有“更好的正向处理”特性吗?我对 Dimitre 的 XSLT 运行了您的 XSLT,而您的 XSLT 速度提高了 3 到 4 倍。如果网上已经有解释了,能指点一下吗?我不知道如何搜索这个模式......它有没有像“身份转换”这样的名字?谢谢。
  • @Zachary Young:这种模式被称为“最细粒度的横向”,基本的模式以及身份转换和按文档顺序逐节点导航树。 “更好的前向处理”意味着如果你有 hsep 的兄弟元素要转换(例如,ispan class="italic"),你可以只为那些不同模式的进程声明一个规则,这需要每个模式的规则来捕获没有模式的前面和有模式的后面。关于性能,我能想到的唯一原因是您有很多hsep 关注两次获取过程(每种模式一次)。
【解决方案2】:

这种转变

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <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="hsep">
  <span class="right">
   <xsl:apply-templates mode="copy"
        select="following-sibling::node()"/>
  </span>
 </xsl:template>

 <xsl:template match="node()[preceding-sibling::hsep]"/>

 <xsl:template mode="copy"
  match="node()[preceding-sibling::hsep]">

  <xsl:call-template name="identity"/>
 </xsl:template>
</xsl:stylesheet>

应用于此文档时

<html>
  <p> Text a <em> Text b <hsep></hsep> Text c </em> </p>
  <p> <em> Text a Text b <hsep></hsep> Text c </em> </p>
</html>

产生想要的正确结果

<html>
   <p> Text a <em> Text b <span class="right"> Text c </span></em></p>
   <p><em> Text a Text b <span class="right"> Text c </span></em></p>
</html>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-10
    • 2012-08-17
    • 2021-10-14
    • 2017-09-13
    • 2014-08-03
    相关资源
    最近更新 更多