我正在使用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>
下面的结果。 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。
答案 0 :(得分:0)
如何在每对cb标签之间抓取所有内容
我没有看到你有对包含列内容的cb
个标签 - 只有顶部的cb
元素。< / p>
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
元素的兄弟节点。
答案 1 :(得分:0)
我提出了一个可行的解决方案。可能不是很优雅,但它符合我的目的。我将在此发布,以防将来对其他人有用。
<!-- 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看到了答案,我会用他的方法简化这段代码。