how to interpret HTML in XSL - html

I have the following xml
<results>
<first-name>Carl<first-name>
<data><b> This is carl's data </b></data>
</results>
How do I include the bold tags which is present in the <data> tag to be a part of the output but rendered as an HTML
When I say <xsl:value-of select="results/data"/> The output is
<b> This is carl's data </b>
I want to achieve "This is carl's data" as the output in bold.

Well <xsl:copy-of select="results/data/node()"/> is a start but if the requirement is part of a larger problem then you are better off writing a template for data elements which uses apply-templates to push the child nodes through some template(s) for copying HTML elements through to the output.

I am sure someone will let me know if I am being naive:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="html" indent="yes"/>
<xsl:template match="/results">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="first-name">
<xsl:value-of select="." />
<xsl:text>: </xsl:text>
</xsl:template>
<xsl:template match="data">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="b">
<b>
<xsl:value-of select="." />
</b>
</xsl:template>
</xsl:stylesheet>

Related

Convert text to uppercase between tag keeping them

I'm trying to convert HTML content to uppercase using XSLT, but the requirement is to keep the tag hierarchy unaltered (i.e., just change the text).
For example: <p>some text <b>other text</b></p>
should result in
<p>SOME TEXT <b>OTHER TEXT</b></p>.
With the following XSLT I managed to convert the text to uppercase, but the result loses the tag hierarchy.
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output encoding="UTF-8" indent="no" method="xhtml" standalone="0" version="1.0"/>
<xsl:variable name="smallcase" select="'abcdefghijklmnopqrstuvwxyz'"/>
<xsl:variable name="uppercase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
<xsl:template match="/">
<xsl:value-of select="translate(/, $smallcase, $uppercase)"/>
</xsl:template>
</xsl:transform>
Is there any way to keep the tags unaltered?
Thanks in advance.
Try:
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:variable name="smallcase" select="'abcdefghijklmnopqrstuvwxyz'"/>
<xsl:variable name="uppercase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:value-of select="translate(., $smallcase, $uppercase)"/>
</xsl:template>
</xsl:stylesheet>
Note that xhtml is not a valid output method in XSLT 1.0.

Normalize space issue with html tags

Here's one for you XSLT gurus :-)
I have to deal with XML output from a Java program I cannot control.
In the docs outputted by this app the html tags remain as
<u><i><b><em>
etc, instead of
<u><i><b><em> and so on.
That's not a massive problem, I use XSLT to fix that, but using normalize-space to remove excess whitespace also removes spaces before these html tags.
Example
<Locator Precode="7">
<Text LanguageId="7">The next word is <b>bold</b> and is correctly spaced
around the html tag,
but the sentence has extra whitespace and
line breaks</Text>
</Locator>
If I run the XSLT script we use to remove extra white space, of which this is the relevant part
<xsl:template match="text(.)">
<xsl:value-of select="normalize-space()"/>
</xsl:template>
In the resulting output the xslt has correctly removed the extra whitespace and the line breaks, but it has also removed the space before the tag resulting in this output :-
The next word isboldand is correctly spaced around the html tag, but the sentence has extra whitespace and line breaks.
The spacing before and after the word "bold" has been stripped as well.
Anyone have any ideas how to prevent this from happening? Pretty well at my wits end so any help will be greatly appreciated!
:-)
Hi again,
Yes of course, here's the full stylesheet. We have to deal with the html tags and spacing in one pass
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="no" encoding="UTF-8"/>
<xsl:strip-space elements="*" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Text//*">
<xsl:value-of select="concat('<',name(),'>')" />
<xsl:apply-templates />
<xsl:value-of select="concat('</',name(),'>')" />
</xsl:template>
<xsl:template match="text()">
<xsl:value-of select="normalize-space(.)"/>
</xsl:template>
<xsl:template match="Instruction//*">
<xsl:value-of select="concat('<',name(),'>')" />
<xsl:apply-templates />
<xsl:value-of select="concat('</',name(),'>')" />
</xsl:template>
<xsl:template match="Title//*">
<xsl:value-of select="concat('<',name(),'>')" />
<xsl:apply-templates />
<xsl:value-of select="concat('</',name(),'>')" />
</xsl:template>
</xsl:stylesheet>
An XSLT 1.0 solution is an XPath expression to replace a sequence of several whitespace characters with a single one. The idea is not my own, it is taken from an answer by Dimitre Novatchev.
The advantage over the built-in normalize-space() function is that trailing whitespace (in your case, before and after the b element) is kept.
EDIT: As a response to you editing your question. Below is the said XPath expression incorporated into your stylesheet. Also:
Explicitly saying omit-xml-declaration="no" is redundant. It is the default action taken by the XSLT processor
Several of your templates have the same content. I summarized them using | to a single one.
Stylesheet
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" encoding="UTF-8"/>
<xsl:strip-space elements="*" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Text//*|Instruction//*|Title//*">
<xsl:value-of select="concat('<',name(),'>')" />
<xsl:apply-templates />
<xsl:value-of select="concat('</',name(),'>')" />
</xsl:template>
<xsl:template match="text()">
<xsl:value-of select=
"concat(substring(' ', 1 + not(substring(.,1,1)=' ')),
normalize-space(),
substring(' ', 1 + not(substring(., string-length(.)) = ' '))
)
"/>
</xsl:template>
</xsl:stylesheet>
XML Output
<?xml version="1.0" encoding="UTF-8"?>
<Locator Precode="7">
<Text LanguageId="7">The next word is <b>bold</b> and is correctly spaced around the html tag, but the sentence has extra whitespace and line breaks</Text>
</Locator>

XSLT transformation for multiple font types

I am trying to make an XSLT transformation from XML, I want to transform font style tags into HTML tags, but my I am doing something wrong.
My XML file is like this one :
<root>
<p>
<span>
<i/>
italic
</span>
<span>
<i/>
<b/>
bold-italic
</span>
<span>
normal
</span>
</p>
</root>
What I want is HTML with the same tags but my XSLT transformation does not work:
HTML:
<p>
<i>italic</i>
<i><b>bold-italic</b></i>
normal
<p>
I was trying xsl:if condition but it does not work,i do not know what I am doing wrong:
XSLT:
<xsl:template match="p">
<p>
<xsl:for-each select="span">
<xsl:if test="i">
<i>
<xsl:value-of select="."/>
</i>
</xsl:if>
<xsl:if test="b">
<b>
<xsl:value-of select="."/>
</b>
</xsl:if>
</xsl:for-each>
</p>
</xsl:template>
Do you know how to repair my code ?
Can you have more than just b and i elements? It may be possible to do this with a generic solution, that creates a nested element for each child element of a span element.
This solution uses a recursive template, that matches span, but with a parameter contain the index number of the child element that needs to be output. When this index exceeds the number of child elements, the text is output.
Try this XSLT too:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="span">
<xsl:param name="num" select="1"/>
<xsl:variable name="childElement" select="*[$num]"/>
<xsl:choose>
<xsl:when test="$childElement">
<xsl:element name="{local-name($childElement)}">
<xsl:apply-templates select=".">
<xsl:with-param name="num" select="$num + 1"/>
</xsl:apply-templates>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
This does assume that all the span element only contain elements you want to nest, in addition to the text.
You can test the contents of span element using an XPath expression with a predicate which tests for its contents, and match different templates for each situation. Since you need b and i for bold-italic, you should use that expression in one of your predicates.
The stylesheet below does the transformation using only templates (without the need of a for-each). I'm assuming the contents of your <span> elements is text (not mixed content):
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:strip-space elements="*"/>
<xsl:template match="p">
<p><xsl:apply-templates/></p>
</xsl:template>
<xsl:template match="span[i]">
<i><xsl:value-of select="."/></i>
</xsl:template>
<xsl:template match="span[b]">
<b><xsl:value-of select="."/></b>
</xsl:template>
<xsl:template match="span[i and b]">
<i><b><xsl:value-of select="."/></b></i>
</xsl:template>
</xsl:stylesheet>

Retrieve XML Variable in XSL file and say YES if found

I have problems with the Xpath expression test="$roles/roles/role='HOBSCS1GB'" . Can anyone help in solving. Thanks
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:template match="/">
<xsl:variable name="roles">
<roles>
<role>HOBSCS1ROI</role>
<role>HOBSCS1GB</role>
<role>HOBSCS1FT</role>
</roles>
</xsl:variable>
<xsl:if test="$roles/roles/role='HOBSCS1GB'">
<xsl:value-of select="'YES'"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Assuming you want to find if a the roles element has one or more role elements with text = 'HOBSCS1GB': (Works in Saxon)
<xsl:if test="$roles/roles[role='HOBSCS1GB']">
<xsl:value-of select="'YES'"/>
</xsl:if>
Note that certain parsers like Microsoft may require you to tell the parser that $roles is a result tree fragment, by using node-set(), like so: (Works in msxsl)
<xsl:stylesheet ... xmlns:msxsl="urn:schemas-microsoft-com:xslt" ... />
<xsl:if test="msxsl:node-set($roles)/roles[role='HOBSCS1GB']">
<xsl:value-of select="'YES'"/>
</xsl:if>
Or in xsltproc:
<xsl:stylesheet ... xmlns:exsl="http://exslt.org/common" ... />
<xsl:if test="exsl:node-set($roles)/roles[role='HOBSCS1GB']">
<xsl:value-of select="'YES'"/>
</xsl:if>

Preserve certain html tags during XSLT

I have looked up solutions on stackflow, but none of them seem to work for me. Here is my question. Lets say I have the following text :
Source:
<greatgrandparent>
<grandparent>
<parent>
<sibling>
Hey, im the sibling .
</sibling>
<description>
$300$ <br/> $250 <br/> $200! <br/> <p> Yes, that is right! <br/> You can own a ps3 for only $200 </p>
</description>
</parent>
<parent>
... (SAME FORMAT)
</parent>
... (Several more parents)
</grandparent>
</greatgrandparent>
Output:
<newprice>
$300$ <br/> $250 <br/> $200! <br/> Yes, that is right! <br/> You can own a ps3 for only $200
</newprice>
I can't seem to find a way to do that.
Current XSL:
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="greatgrandparents">
<xsl:apply-templates />
</xsl:template>
<xsl:template match = "grandparent">
<xsl:for-each select = "parent" >
<newprice>
<xsl:apply-templates>
</newprice>
</xsl:for-each>
</xsl:template>
<xsl:template match="description">
<xsl:element name="newprice">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="p">
<xsl:apply-templates/>
</xsl:template>
Use templates to define behavior on specific elements
<!-- after standard identity template -->
<xsl:template match="description">
<xsl:element name="newprice">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="p">
<xsl:apply-templates/>
</xsl:template>
The first template says to swap description with newprice. The second one says to ignore the p element.
If you're unfamiliar with the identity template, take a look here for a few examples.
EDIT: Given the new example, we can see that you want to only extract the description element and its contents. Notice that the template action starts with the match="/" template. We can use this control where our stylesheet starts and thus skip much of the riffraff we want to filter out.
change the <xsl:template match="/"> to something more like:
<xsl:template match="/">
<xsl:apply-templates select="//description"/>
<!-- use a more specific XPath if you can -->
</xsl:template>
So altogether our solution looks like this:
<xsl:stylesheet
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
exclude-result-prefixes="xs">
<xsl:template match="/">
<xsl:apply-templates select="//description" />
</xsl:template>
<!-- this is the identity template -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="description">
<xsl:element name="newprice">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="p">
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
Shouldn't the contents of be inside a CDATA element? And then probably disable output encoding on xsl:value-of..
You should look into xsl:copy-of.
You would probably wind up with somthing like:
<xsl:template match="description">
<xsl:copy-of select="."/>
</xsl:template>
Probably the shortest solution is this one:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="description">
<newprice>
<xsl:copy-of select="node()"/>
</newprice>
</xsl:template>
<xsl:template match="text()[not(ancestor::description)]"/>
</xsl:stylesheet>
When this transformation is applied on the provided XML document, the wanted result is produced:
<newprice>
$300$ <br /> $250 <br /> $200! <br /> <p> Yes, that is right! <br /> You can own a ps3 for only $200 </p>
</newprice>
Do note:
The use of <xsl:copy-of select="node()"/> to copy all the subtree rooted in description, without the root itself.
How we override (with a specific, empty template) the XSLT built-in template, preventing any text nodes that are not descendents of a <description> element, to be output.