问题
So the title of this post may be a little misleading, but it's the best I can come up with. I'm working on a project that uses TEI for encoding texts. One of the requirements of my current work is to write XSL transformations to render the XML-encoded texts as HTML. For the most part, no problem. I'm kind of stuck on this issue, though:
<l>There is <delSpan spanTo="A1"/>deleted text spanning</l>
<l>multiple lines here.<anchor xml:id="A1"/> More text...</l>
Or, in other instances:
<delSpan spanTo="A2"/>
<l>Several deleted lines -- the delspan marker can appear </l>
<l>outside of an l element.... </l>
<anchor xml:id="A2"/>
(In case you're not familiar with TEI: l = a line of text; delSpan = span of deleted text that includes more than 1 line, page, or smaller unit.)
The goal is to display the text between the delSpan (A1) and its corresponding anchor (A1) -- "deleted text spanning / multiple lines here" -- with some formatting that indicates the deletion (e.g., text-decoration="line-through"). Right now, there's a template for "l" elements that handles most text formatting--or at least calls other templates to do it.
But these singleton tags are an anomaly; all other formatting/markup is accomplished with tags that actually contain the text they're meant to format. Am I right in assuming that I need to process the delSpan and anchor elements in the "l" template? What's the most elegant way to approach this problem and handle the pseudo-overlapping elements?
Sorry if this is a noob question or if I haven't given enough information. I'm primarily a C/C++ programmer with little XSLT experience, so I appreciate any suggestions.
回答1:
This stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="delSpan|anchor"/>
<xsl:template match="text()[preceding::delSpan[1]/@spanTo=following::anchor[1]/@xml:id]">
<span style="text-decoration:line-through;">
<xsl:value-of select="."/>
</span>
</xsl:template>
</xsl:stylesheet>
Output:
<doc>
<l>There is <span style="text-decoration:line-through;">deleted text spanning</span></l>
<l><span style="text-decoration:line-through;">multiple lines here.</span> More text...</l>
</doc>
Note: The use of preceding and following axis and their meaning in document order. We don't overrides any previus template matching l
elements. delSpan
and anchor
could also not been striped.
回答2:
Your main issue here is that the delSpan
elements are not parents and that its ending and closing "element" are an empty element (anchor
for closing). The link between opening and closing is done by referencing an xml:id
tag.
There're multiple solutions to this issue, here's one from the top of my head, assuming you use XSLT 1.0 (it's much easier with XSLT 2.0). The idea is simple: find all siblings and apply them, but only if they are themselves followed by a sibling anchor
with the correct xml:id
:
<xsl:template match="delSpan">
<xsl:variable select="@spanTo" name="spanTo" />
<xsl:apply-templates select="following-sibling::*[following-sibling::anchor[@xml:id = $spanTo]" mode="deleted" />
</xsl:template>
<!--
do this for all elements you need to treat inside delSpan
if they have children, remember to use apply-templates with mode deleted
-->
<xsl:template match="l" mode="deleted">
<strike><xsl:value-of select="." /></strike>
</xsl:template>
I'm not 100% sure this is entirely correct. It may go wrong if <delSpan>
can be nested. If you're lucky, Dimitri Novatchev walks by and has a look, too.
来源:https://stackoverflow.com/questions/3495447/xslt-with-overlapping-elements