我有以下 Html 文档。
<html>
<head><title>...</title></head>
<body>
<div class="figure-wrapper" id="figure1">...</div>
<p class="para">Lorem Ipsum (see Fig. 1). Lorem Ipsum (see Fig. 2).</p>
<div class="figure-wrapper" id="figure3">...</div>
<p class="para">Lorem Ipsum (see Fig. 3). Lorem Ipsum (see Fig. 1).</p>
<div class="figure-wrapper" id="figure2">...</div>
</body>
</html>
我想实现什么目标
- 将每个图形元素(被
<div class="figure-wrapper">
元素包裹的元素)放置在第一个引用它的段落之后。 - 如果第一段之后的元素本身就是图形元素,则相关图形元素应放在其后面。
示例和理想输出
<div class="figure-wrapper" id="figure1>
元素只能放置在第一段之后,因为它是引用该图的所有段落中的第一个。
<html>
<head><title>...</title></head>
<body>
<p class="para">Lorem Ipsum (see Fig. 1). Lorem Ipsum (see Fig. 2).</p>
<div class="figure-wrapper" id="figure1">...</div>
<div class="figure-wrapper" id="figure2">...</div>
<p class="para">Lorem Ipsum (see Fig. 3). Lorem Ipsum (see Fig. 1).</p>
<div class="figure-wrapper" id="figure3">...</div>
</body>
</html>
限制
输入文档中不存在对图形元素的显式引用(就 HTML 元素而言)。因此,我必须分析段落内容(例如,某些值的出现,如图 x 等),以推断段落中是否引用了该图。
到目前为止我制作的是以下解决方案。
我使用身份转换模式、 key 和多 channel 方法尝试了一种奇怪的混合,但是,我无法思考。
<xsl:stylesheet
xmlns:xsl ="http://www.w3.org/1999/XSL/Transform"
xmlns:xd ="http://www.oxygenxml.com/ns/doc/xsl"
xmlns:fn ="http://www.w3.org/2005/xpath-functions"
xmlns:functx="http://www.functx.com"
exclude-result-prefixes="xd"
version="2.0">
<!-- maximum number of figure references within one paragraph -->
<xsl:variable name="figThreshold" select="100" />
<!-- index of all figure elements -->
<xsl:key name="figure-index" match="node()[@class='figure-wrapper']" use="@id" />
<!-- transformation init -->
<xsl:template match="/">
<xsl:variable name="pass1">
<xsl:apply-templates mode="pass1" />
</xsl:variable>
<xsl:variable name="pass2">
<xsl:for-each select="$pass1">
<xsl:apply-templates mode="pass2" />
</xsl:for-each>
</xsl:variable>
<xsl:copy-of select="$pass2" />
</xsl:template>
<!-- pass 1 start -->
<xsl:template match="node() | @*" mode="pass1">
<xsl:copy>
<xsl:apply-templates select="node() | @*" mode="pass1" />
</xsl:copy>
</xsl:template>
<xsl:template match="node()[name()='p']" mode="pass1" priority="1">
<xsl:copy>
<xsl:apply-templates select="@* | node()" mode="pass1" />
</xsl:copy>
<xsl:call-template name="locate-and-move-figures" />
</xsl:template>
<!-- iterates x times (see value of figThreshold) over paragraph text and increment each time the figure number reference to look for -->
<xsl:template name="locate-and-move-figures">
<xsl:param name="figCount" select="1" />
<xsl:variable name="figureId" select="concat('figure',$figCount)" />
<xsl:variable name="searchStringText" select="concat('Fig. ',$figCount)) />
<!-- if figure reference is found within paragraph insert the appropriate after it -->
<xsl:if test="$searchStringText">
<xsl:copy-of select="key('figure-index',$figureId)" />
</xsl:if>
<!-- recursive call of template unless threshold value is reached -->
<xsl:if test="$figCount < $figThreshold">
<xsl:call-template name="locate-and-move-figures">
<xsl:with-param name="figCount" select="$figCount + 1" />
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template match="node()[@class='figure-wrapper']" mode="pass1" />
<!-- pass 1 end -->
<!-- pass 2 start - eliminations of all duplicates -->
<xsl:template match="node() | @*" mode="pass2">
<xsl:copy>
<xsl:apply-templates select="node() | @*" mode="pass2" />
</xsl:copy>
</xsl:template>
<!-- pass 2 end -->
</xsl:stylesheet>
我得到的输出是这样的:
<html>
<head><title>...</title></head>
<body>
<p class="para">Lorem Ipsum (see Fig. 1). Lorem Ipsum (see Fig. 2).</p>
<div class="figure-wrapper" id="figure1">...</div>
<div class="figure-wrapper" id="figure2">...</div>
<p class="para">Lorem Ipsum (see Fig. 3). Lorem Ipsum (see Fig. 1).</p>
<div class="figure-wrapper" id="figure1">...</div>
<div class="figure-wrapper" id="figure3">...</div>
</body>
</html>
问题
- 重复
<div class="figure-wrapper">
元素。我试图在第二遍中摆脱它们,但我无法结合身份转换模式来解决重复删除问题。 - 我对每个段落都会被增量搜索 x 次(在本例中为 100 次)以查找图形引用这一事实感到不太满意。我可以选择较低的阈值(例如 20 次),但我担心我可能会错过一些引用文献,因为一个段落中不存在自然最大的图形引用文献。
非常感谢您对这些问题的任何帮助。
最佳答案
这是您可以探索的不同方法。我在 XSLT 1.0 中做到了这一点,但差异对于该方法来说并不重要。
基本思想是将父 para 的 id 附加到 para 包含的每个引用。然后,使用 Muenchian 分组,我们只保留每个引用的第一次出现。由于每个都保留了原始父级的 id,我们知道它需要出现在最终输出中的位置。
请注意,假设没有独立的引用元素(即至少在一段中未引用的元素)。
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="tokens" match="token" use="." />
<xsl:key name="ref" match="div[@class='figure-wrapper']" use="@id" />
<xsl:variable name="root" select="/"/>
<!-- 1. collect all references, along with their parent id -->
<xsl:variable name="references">
<xsl:for-each select="//p[@class='para']">
<xsl:call-template name="cat_ref">
<xsl:with-param name="string" select="."/>
<xsl:with-param name="pid" select="generate-id()"/>
</xsl:call-template>
</xsl:for-each>
</xsl:variable>
<!-- 2. keep only unique references -->
<xsl:variable name="unique-ref" select="exsl:node-set($references)/token[count(. | key('tokens', .)[1]) = 1]"/>
<!-- 3. output -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="p[@class='para']">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
<!-- append my references -->
<xsl:for-each select="$unique-ref[@pid=generate-id(current())]">
<xsl:variable name="ref-key" select="."/>
<!-- switch back to document in order to use key -->
<xsl:for-each select="$root">
<xsl:copy-of select="key('ref', $ref-key)"/>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
<!-- suppress references -->
<xsl:template match="div [@class='figure-wrapper']"/>
<!-- proc template -->
<xsl:template name="cat_ref">
<xsl:param name="string"/>
<xsl:param name="pid"/>
<xsl:param name="prefix" select="'(see Fig. '" />
<xsl:param name="suffix" select="')'" />
<xsl:if test="contains($string, $prefix) and contains(substring-after($string, $prefix), $suffix)">
<token pid="{$pid}">
<xsl:text>figure</xsl:text>
<xsl:value-of select="substring-before(substring-after($string, $prefix), $suffix)" />
</token>
<!-- recursive call -->
<xsl:call-template name="cat_ref">
<xsl:with-param name="string" select="substring-after(substring-after($string, $prefix), $suffix)" />
<xsl:with-param name="pid" select="$pid" />
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
应用到您的输入,将获得以下结果:
<?xml version="1.0" encoding="UTF-8"?>
<html>
<head>
<title>...</title>
</head>
<body>
<p class="para">Lorem Ipsum (see Fig. 1). Lorem Ipsum (see Fig. 2).</p>
<div class="figure-wrapper" id="figure1">...</div>
<div class="figure-wrapper" id="figure2">...</div>
<p class="para">Lorem Ipsum (see Fig. 3). Lorem Ipsum (see Fig. 1).</p>
<div class="figure-wrapper" id="figure3">...</div>
</body>
</html>
关于xslt - 如何根据其他元素的文本节点内的值更改 HTML 文档的元素顺序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22161913/