我正在研究一个XSLT来将XML(tei)文档转换为HTML。目标是创建可以设置样式以显示为固定列的div。
在文档中,列开头由 2 个空元素(里程碑和 cb)表示。"里程碑"表示文本流中的列数现在等于 n 属性。"cb"标记列的开头,其 n 属性指示其在从左到右序列中的顺序。"cb"标签并不总是同级。
示例 XML:
<p>
<milestone unit="column" n="2"/>
<cb n="1"/>
M. Dudley
<lb/>
H. E. Ernshimer
<lb/>
M. M. Cash
<lb/>
John Wheatly
<lb/>
Jno W. Cash
<lb/>
<cb n="2"/>
R. L. Wilson
<lb/>
R. B. Ratliff L.C.C.
<lb/>
G. D Watkins Clk
<lb/>
A. C. Mayes
<lb/>
<pb/>
</p>
<p>
<note place="left margin">Jury 1863 Nov.</note>
<lb/>
<cb n="1"/>
D C Mitchenssson
<lb/>
A. W. Forde, Tm P
<lb/>
L S Thomson
<lb/>
Louis Martin
<hi rend="sup">c</hi>
Casslin
<lb/>
E. M. Stevens
<lb />
<cb n="2"/>
O Ross Baker Clk Caldwell County Court
<lb/>
N. Jones
<lb/>
S. W. M
<milestone unit="column" n="1"/>
<pb/>
<lb/>
John Garrett
</p>
期望的结果如下。 类等于其前一个里程碑的 n 属性的div:
<div class="column 2">
M. Dudley<br />
H. E. Ernshimer<br />
M. M. Cash<br />
John Wheatly<br />
Jno W. Cash<br />
...
</div>
<div class="column 2">
R. L. Wilson<br />
R. B. Ratliff L.C.C.<br />
G. D Watkins Clk<br />
A. C. Mayes<br />
Jas Crenshaw<br />
</div>
如何抓取每对 cb 标签之间的所有内容,并将内容包装在包含的div 中?我尝试过的所有内容都会导致一系列嵌套的div。
如何抓取每对 cb 标签之间的所有内容
我没有看到您有一对cb
标签将列的内容括起来 - 只有顶部的前导cb
元素。
IIUC,你想做这样的事情:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="txt-by-col" match="text()" use="generate-id(preceding-sibling::cb[1])" />
<xsl:template match="/">
<root>
<xsl:for-each select="//cb">
<div class="column {preceding::milestone[1]/@n}">
<xsl:for-each select="key('txt-by-col', generate-id())">
<xsl:value-of select="." />
<br/>
</xsl:for-each>
</div>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
这并不是说这假定列的所有文本节点都是前导cb
元素的同级。
我想出了一个可行的解决方案。可能不优雅,但它符合我的目的。我会在这里发布,以防将来对其他人有用。
<!-- add a white space in empty milestone so it doesn't wrap around other elements -->
<xsl:template match="tei:milestone">
<xsl:variable name="milenum" select="@n" />
<milestone>
<xsl:attribute name="n">
<xsl:value-of select="$milenum" />
</xsl:attribute>
<xsl:text> </xsl:text>
</milestone>
</xsl:template>
<!-- add a white space in empty cb so it doesn't wrap around other elements -->
<xsl:template match="tei:cb">
<xsl:variable name="num" select="@n" />
<cb>
<xsl:attribute name="n">
<xsl:value-of select="$num" />
</xsl:attribute>
<xsl:text> </xsl:text>
</cb>
</xsl:template>
<!-- wrap content following cb elements in a div, with a class indicating the number of columns in the preceding milestone n attribute (if milestone n=2, then div class=column1of2 or div class=column2of2) -->
<xsl:template match="tei:p[tei:cb]">
<!-- to print text before the first milestone -->
<xsl:apply-templates select="node()[not(preceding::tei:milestone)]" />
<xsl:for-each select="tei:cb">
<xsl:variable name="count" select="position()" />
<div>
<xsl:variable name="numberofcolumns" select="preceding::tei:milestone[1]/@n" />
<xsl:variable name="n" select="@n" />
<xsl:attribute name="class">
<xsl:text>column</xsl:text>
<xsl:value-of select="$n" />
<xsl:text>of</xsl:text>
<xsl:value-of select="$numberofcolumns" />
</xsl:attribute>
<xsl:apply-templates select="following-sibling::node()[preceding-sibling::tei:cb[1][@n=$n] and count(preceding-sibling::tei:cb)=$count and preceding::tei:milestone[1][@n>1] and not(self::tei:milestone)]" />
</div>
</xsl:for-each>
</xsl:template>
这输出:
<milestone n="2"> </milestone>
<div class="column1of2">
</div>
<div class="column2of2">
</div>
<div class="column1of2">
</div>
<div class="column2of2">
</div>
现在我看到了 @michael.hor257k 的答案,我将用他的方法简化这段代码。