Convert line break in XML to space with XSLT - html

I have some poorly formatted XML files that may contains line breaks within element. For exemple:
<para> This is a
text with random
breaklines </para>
My issue is that when is use XSLT to convert this to HTML, the words separated by line breaks in the xml are actually jointed.
<p> This is atext with randombreaklines </p>
I wish to be able to insert a Space character where there is line breaks in the XML?
Is that possible?

The result you show can be produced using:
<xsl:template match="para">
<p>
<xsl:value-of select="translate(., '
', ' ')"/>
</p>
</xsl:template>
You may prefer to use:
<xsl:value-of select="normalize-space(.)"/>
in order to produce:
<p>This is a text with random breaklines</p>
That said, I don't see why you need to do anything at all. A simple:
<xsl:value-of select="."/>
will result in:
<p> This is a
text with random
breaklines </p>
which any browser will render as:
If you are seeing a different result, then there must be some additional factor not mentioned in your question.

Related

Value in CDATA tag not being displayed in XSL file?

I want to replace the & symbol inside of a piece of text that is generated dynamically with its encoded value %26 to prevent it from breaking the URL string. I am storing the text inside hrefvalue variable. My goal is to replace & with %26 to be output in the final HTML code in the browser.
For example:
"listening & comprehension" should become "listening %26 comprehension"
I am using <![CDATA[ to preserve %26 but this seems not to be working. I still end up with "listening & comprehension" in the browser. Why?
<xsl:variable name="hrefvalue" select="./node()" />
<xsl:choose>
<xsl:when test="contains($hrefvalue, '&')">
<xsl:variable name="string-before" select="substring-before($hrefvalue, '&')" />
<xsl:variable name="string-after" select="substring-after($hrefvalue, '&')" />
<xsl:variable name="ampersand"><![CDATA[%26]]></xsl:variable>
<xsl:value-of select="concat($string-before, $ampersand, $string-after)" disable-output-escaping="yes" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$hrefvalue" disable-output-escaping="no" />
</xsl:otherwise>
</xsl:choose>
I am working on a system that uses XSL 1.0
Firstly, the CDATA is completely unnecessary and irrelevant. The only purpose of CDATA is to allow & and < to be written without escaping, and if those special characters are not present, the CDATA tag makes no difference.
Also, if you're generating an attribute (which seems likely if it's a URL) then disable-output-escaping has no effect. But again, it's probably not needed.
Your code only deals with one ampersand in a string.
But your code looks fine. If there's a problem, it's in the part of the code that you haven't shown us. Try to construct a complete reproducible example: a complete stylesheet and source document that we can actually run to see if we can reproduce the problem.

XSL - Whitespace issue when setting dynamically placeholder

I have an xslt template, in which i am loading translation content from xml files.
I want to set dynamically the placeholder in an input field, but apparently I am keep getting whitespace (the placeholder is moved to the right).
Here is my code.
<xsl:attribute name="placeholder">
<xsl:value-of select="/paygate/language/computop.creditcard.number.message"/>
</xsl:attribute>
I tried removing the whitespace between the lines, also setting
<xsl:strip-space elements="*"/>
in the beginning of the file. Nothing worked :(
An XSLT processor ought to strip whitespace-only text nodes that are direct children of an <xsl:attribute> by default. If the transform you present is producing placeholder attributes with unwanted leading or trailing whitespace in their values, then, I conclude it is coming from the application of the <xsl:value-of> element; its result is not subject to whitespace stripping.
In that case, you could consider applying the standard normalize-space() XPath function to the attribute value:
<xsl:attribute name="placeholder">
<xsl:value-of select="normalize-space(string(/paygate/language/computop.creditcard.number.message))"/>
</xsl:attribute>
normalize-space() will delete both leading and trailing whitespace from its (string) argument, but will also replace each internal run of whitespace characters with a single space character.

How to store superscript in XML attribute and read using XSL?

I have a requirement where I need create an XML document dynamically. Some of the attributes of the nodes of this XML contain superscript Reg etc. My question is how should I store such superscript characters in XML and then read it using XSL to render as HTML. A sample XML is shown below:
<?xml version="1.0" encoding="utf-8"?>
<node name="Some text <sup>®</sup>"/>
I know this cannot be stored under sup tag inside attribute as it breaks XML. I tried using <sup> also in place of opening and closing tag. But then they are rendered as <sup> on HTML instead of actually making it superscript.
Please let me know the solution for this problem. I have control over generation of XML. I can write it the correct way, If I know what is the right way to store superscripts.
Since you're using XSL to transform the input into HTML, I would suggest using a different method to encode the fact that some things need to be superscripts. Make up your own simple markup, for example
<node name="Some text [[®]]"/>
The markup can be anything that you can uniquely identify later and doesn't occur naturally in your data. Then in your XSL process the attribute values that can contain this markup with a custom template that converts the special markup to <sup> and </sup>. This allows you to keep the document structure (i.e. not move these string values to text nodes) and still achieve your goal.
Please let me know the solution for this problem. I have control over
generation of XML. I can write it the correct way, If I know what is
the right way to store superscripts.
Because attributes can only contain values (no nodes), the solution is to store markup (nodes) inside elements:
<node>
<name>Some text <sup>®</sup></name>
</node>
If it's only single characters like ® that need to be made superscript, then you can leave the XML without crooks like <sup>, i.e. like
<node name="Some text ®"/>
and look for the to-be-superscripted characters during processing. A template like this might help:
<xsl:template match="node/#name">
<xsl:param name="nameString" select="string()"/>
<!-- We're stepping through the string character by character -->
<xsl:variable name="firstChar" select="substring($nameString,1,1)"/>
<xsl:choose>
<!-- '®' can be extended to be a longer string of single characters
that are meant to be turned into superscript -->
<xsl:when test="contains('®',$firstChar)">
<sup><xsl:value-of select="$firstChar"/></sup>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$firstChar"/>
</xsl:otherwise>
</xsl:choose>
<!-- If we we didn't yet step through the whole string,
chop off the first character and recurse. -->
<xsl:if test="$firstChar!=''">
<xsl:apply-templates select=".">
<xsl:with-param name="nameString" select="substring($nameString,2)"/>
</xsl:apply-templates>
</xsl:if>
</xsl:template>
This approach is however not very efficient, especially if you have lots of name attributes and/or very long name attributes. If your application is performance critical, then better do some testing whether the impact on processing times is justifiable.

Is there a way to detect numeric string in xslt?

I am now doing a html to xml xslt transformation, pretty straigh-forward. But I have one slight problem that is left unsolved.
For example, in my source html, a node looks like:
<p class="Arrow"><span class="char-style-override-12">4</span><span class="char-style-override-13"> </span>Sore, rash, growth, discharge, or swelling.</p>
As you can see, the first child node < span> has a value of 4, is it actually rendered as a arrow point in the browser (maybe some encoding issue, it is treated as a numeric value in my xml editor).
So my question is, I wrote a template to match the tag, then pass the text content of it to another template match :
<xsl:template match="text()">
<xsl:variable name="noNum">
<xsl:value-of select="normalize-space(translate,'4',''))"/>
</xsl:variable>
<xsl:copy-of select="$noNum"/>
</xsl:template>
As you can see, this is definitely not a good solution, it will replace all the numbers appearing in the string, not only the first character. So I wonder if there is a way to remove only the first character IF it is a number, maybe using regular expression? Or, I am actually going the wrong way, should there be a better way to think of solving this problem(e.g, changing the encoding)?
Any idea is welcomed! Thanks in advance!
Just use this :
<xsl:variable name="test">4y4145</xsl:variable>
<xsl:if test= "not(string(number(substring($test,1,1)))='NaN')">
<xsl:message terminate="no">
<xsl:value-of select="substring($test,2)"/>
</xsl:message>
</xsl:if>
This is a XSLT 1.0 solution. I think regex is an overkill for this.
Output :
[xslt] y4145
Use this single XPath expression:
concat(translate(substring(.,1,1), '0123456789', ''),
substring(.,2)
)

Is there an elegant way to add multiple HTML classes with XSLT?

Let's say I'm transforming a multiple-choice quiz from an arbitrary XML format to HTML. Each choice will be represented as an HTML <li> tag in the result document. For each choice, I want to add an HTML class of correct to the <li> if that choice was the correct answer. Additionally, if that choice was the one selected by the user, I want to add a class of submitted to the <li>. Consequently, if the choice is the correct one as well as the submitted one, the <li> should have a class of correct submitted.
As far as I know, white-space separated attribute values aren't a part of the XML data model and thus cannot directly be created via XSLT. However, I have a feeling there's a better way of doing this than littering the code with one conditional for every possible combination of classes (which would be acceptable in this example, but unwieldy in more complex scenarios).
How can I solve this in an elegant way?
Example of Desired Result:
<p>Who trained Obi-Wan Kenobi?</p>
<ul>
<li>Mace Windu</li>
<li class="correct submitted">Qui-Gon Jinn</li>
<li>Ki-Adi-Mundi</li>
<li>Yaddle</li>
</ul>
Firstly, there is nothing wrong with whitespace in attribute values in XML: roughly speaking, attribute value normalization converts whitespace characters to spaces and collapses adjacent spaces to a single space when a document is parsed, but whitespace is definitely allowed. EDIT: See below for more on this.
Matthew Wilson's approach fails to include whitespace between the possible values, as you mention in your comment thereto. However, his approach is fundamentally sound. The final piece of the jigsaw is your dislike of redundant spaces: these can be eliminated by use of the normalize-space XPath function.
The following stylesheet puts all the bits together - note that it doesn't do anything with its input document, so for testing purposes you can run it against any XML document, or even against itself, to verify that the output meets your requirements.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="foo0" select="false()"/>
<xsl:variable name="bar0" select="true()"/>
<xsl:variable name="foo1" select="true()"/>
<xsl:variable name="bar1" select="false()"/>
<xsl:variable name="foo2" select="true()"/>
<xsl:variable name="bar2" select="true()"/>
<xsl:template match="/">
<xsl:variable name="foobar0">
<xsl:if test="$foo0"> foo</xsl:if>
<xsl:if test="$bar0"> bar</xsl:if>
</xsl:variable>
<xsl:variable name="foobar1">
<xsl:if test="$foo1"> foo</xsl:if>
<xsl:if test="$bar1"> bar</xsl:if>
</xsl:variable>
<xsl:variable name="foobar2">
<xsl:if test="$foo2"> foo</xsl:if>
<xsl:if test="$bar2"> bar</xsl:if>
</xsl:variable>
<li>
<xsl:attribute name="class">
<xsl:value-of select="normalize-space($foobar0)"/>
</xsl:attribute>
</li>
<li>
<xsl:attribute name="class">
<xsl:value-of select="normalize-space($foobar1)"/>
</xsl:attribute>
</li>
<li>
<xsl:attribute name="class">
<xsl:value-of select="normalize-space($foobar2)"/>
</xsl:attribute>
</li>
</xsl:template>
</xsl:stylesheet>
EDIT: Further to the question of spaces separating discrete components within the value of an attribute: The XML Spec defines a number of possible valid constructs as attribute types, including IDREFS and NMTOKENS. The first case matches the Names production, and the second case matches the NMTokens production; both these productions are defined as containing multiple values of the appropriate type, delimited by spaces. So space-delimited lists of values as the value of a single attribute are an inherent component of the XML information set.
Off the top of my head, you can build up a space-separated list with something like:
<li>
<xsl:attribute name="class">
<xsl:if cond="...">correct</xsl:if>
<xsl:if cond="...">submitted</xsl:if>
</xsl:attribute>
</li>
As far as I know, white-space separated attribute values aren't a part of the XML data model and thus cannot directly be created via XSLT
Unless you are converting to an XML language (which HTML is not, XHTML is), you shouldn't worry about the XML validity of the XSLT ouput. This can be anything, and doesn't need to conform to XML!