I hope someone can give me a hand. This issue has been stumping me for several days now. The root of my issue is that I want to add markup to all nodes in document order between 2 elements.
I have a document that has XML similar to this:
<Employees>
<Employee>
<Title>Mr.</Title>
<FirstName>John</FirstName>
<LastName>Doe</LastName>
</Employee>
<Employee>
<Title>Mr.</Title>
<FirstName>Tom</FirstName>
<LastName>Doe</LastName>
</Employee>
</Employees>
When I use Oracle's 'markup' function which marks up search hits, and I search for the string 'John Doe', I get an XML result like this:
<Employees>
<Employee>
<Title>Mr.</Title>
<FirstName><hitStart/>John</FirstName>
<LastName>Doe<hitEnd/></LastName>
</Employee>
<Employee>
<Title>Mr.</Title>
<FirstName>Tom</FirstName>
<LastName>Doe</LastName>
</Employee>
</Employees>
I want to transform this into XHTML that highlights the hit. For example, the following XHTML would be a useful result:
<TABLE>
<TR>
<TD>Mr. <b style="color:red">John Doe</b></TD>
<TR>
<TR>
<TD>Tom Doe</TD>
</TR>
</TABLE>
I've tried writing stylesheets that use apply-templates or named templates to navigate through the document, but I can't get them to work. Using apply-templates is tricky because I can't pass a parameter that states whether or not the nodes are within the hitStart and hitEnd elements. Using named templates is tricky because I need to handle text and element nodes differently, which I'm not able to do in XSLT 1.0. Help would be appreciated.
Thanks, Brian
Thanks to everyone who helped!!!! You guys are great!
Here's what I settled on:
<xsl:template match="/*|node()">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates select="node()[1]"/>
</xsl:copy>
<xsl:apply-templates select="following-sibling::node()[1]"/>
</xsl:template>
<xsl:template match="text()[preceding::*[self::hitStart or self::hitEnd][1][self::hitStart]
and following::*[self::hitStart or self::hitEnd][1][self::hitEnd]]">
<span style="color:red;font-style:italic;font-weight:bold"><xsl:value-of select="."/></span>
<xsl:apply-templates select="following-sibling::node()[1]"/>
</xsl:template>
<xsl:template match="hitStart|hitEnd">
<xsl:apply-templates select="following-sibling::node()[1]"/>
</xsl:template>
This transformation:
<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()|@*">
<xsl:copy>
<xsl:apply-templates select="@*|node()[1]"/>
</xsl:copy>
<xsl:apply-templates select="following-sibling::node()[1]"/>
</xsl:template>
<xsl:template match=
"text()
[preceding::*[self::hitStart or self::hitEnd][1][self::hitStart]
and
following::*[self::hitStart or self::hitEnd][1][self::hitEnd]
]">
<xsl:value-of select="concat('*** ', ., ' ***')"/>
</xsl:template>
<xsl:template match="hitStart|hitEnd">
<xsl:apply-templates select="following-sibling::node()[1]"/>
</xsl:template>
</xsl:stylesheet>
when applied against the provided XML document:
<Employees>
<Employee>
<Title>Mr.</Title>
<FirstName><hitStart/>John</FirstName>
<LastName>Doe<hitEnd/></LastName>
</Employee>
<Employee>
<Title>Mr.</Title>
<FirstName>Tom</FirstName>
<LastName>Doe</LastName>
</Employee>
</Employees>
high-lights every text node in-between the hitStart and hitEnd elements -- by surrounding it with three asterisks and producing the wanted, correct result:
<Employees>
<Employee>
<Title>Mr.</Title>
<FirstName>*** John ***</FirstName>
<LastName>*** Doe ***</LastName>
</Employee>
<Employee>
<Title>Mr.</Title>
<FirstName>Tom</FirstName>
<LastName>Doe</LastName>
</Employee>
</Employees>
Explanation:
Using and overriding the "fine-grained" identity rule.
It's not ideal, but you could do something quick and dirty like...
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="utf-8" indent="no"/>
<xsl:template match="/">
<table>
<xsl:apply-templates />
</table>
</xsl:template>
<xsl:template match="FirstName[hitStart]">
<span class="alert"><xsl:value-of select="."/> </span>
</xsl:template>
<xsl:template match="FirstName">
<xsl:value-of select="."/> 
</xsl:template>
<xsl:template match="LastName[hitEnd]">
<span class="alert"><xsl:value-of select="."/></span>
</xsl:template>
<xsl:template match="LastName">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="Employees/Employee">
<tr>
<td>
<xsl:apply-templates select="Title"/>
</td>
<td>
<xsl:apply-templates select="FirstName | LastName"/>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With