XSLT New Line in CSV but not in the CSV - csv

I hope this doesn't confuse you. I'm going to start from the perspective of an Excel file. If I add the word "Cat" to a cell and then use the key combination Alt+Enter I get a new line within the cell so then I can enter the word "Dog".
The outcome of this is that I have two lines within the one cell with Cat and Dog on the lines. I've written this in cell A2.
In A1 I wrote "Snake".
And in A3 I wrote "Hamster"
If I save this Excel file as CSV I get the following;
Snake
"CatDog"
Hamster
This is what I'm trying to achieve using XSLT.
But when I try this I do actually get a new line, like this;
Snake
"Cat
Dog"
Hamster
Which isn't what I'm after.
I'm receiving the data as a comma separated list from a different source (ie: not Excel).
Here is the XSLT code I'm trying to use at the moment;
<xsl:template name="parse-comma-separated">
<xsl:param name="text"/>
<xsl:param name="separator"/>
<xsl:choose>
<xsl:when test="not(contains($text, $separator))">
<xsl:value-of select="normalize-space($text)" />
</xsl:when>
<xsl:otherwise>
<xsl:text>
</xsl:text><xsl:value-of select="normalize-space(substring-before($text, $separator))" />
<xsl:call-template name="parse-comma-separated">
<xsl:with-param name="text" select="substring-after($text, $separator)"/>
<xsl:with-param name="separator" select="$separator"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
This appears to be putting a space between the Pets.
When I tried a straight up string-replace method (which works great if replacing commas with pipes) I actually get a new line which is kinda what I want but only in the cell, not a new line in the csv file.
Any and all help appreciated.

Related

Convert line break in XML to space with XSLT

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.

XSL:if test not working as expected

<xsl:for-each select="class/student">
ID: <xsl:value-of select="id"/><br/>
Name: <xsl:value-of select="lastName"/>,<xsl:value-of select="firstName"/><br/>
Date: <xsl:value-of select="date"/><br/>
Major: <xsl:if test="major[#Year > 2008]">
<xsl:value-of select="major"/> ,
declared in: <xsl:value-of select="major[#Year]"/>
</xsl:if><br/><br/>
</xsl:for-each>
XML code`:
<student>
<id>1000001</id>
<lastName>john</lastName>
<firstName>Doe</firstName>
<date format="d">08/25/2006</date>
<major Year="2006">CS:BS</major>
</student>
output:
ID: 1000001
Name: Doe,John
Date-enrolled: August 25, 2006
Major: CS:BS , declared in: CS:BS
the xml code above is just a sample of the actual xml code, there are more 'Year' values/elements.
Hi guys,
Im trying to get only majors whose Year is greater than 2008, for some reason im getting the wrong output.
thanks
You say "there are more 'Year' values/elements" than shown, and that may be the key to the problem. If your input contains two elements
<major Year="2006">CS:BS</major>
<major Year="2009">CS:BS</major>
then test="major/#Year > 2008" will return true, because there is one such element, and in XSLT 1.0, <xsl:value-of select="major"/> will output the first selected element (in 2.0 it will raise an error).
In future, please try to supply a complete sample stylesheet and source document that allow others to reproduce the problem. If you try to cut it down without testing that the cut-down version exhibits the problem, it's easy to eliminate the feature that is the actual source of the trouble.
The problem is that you are going about this backwards. You need to select the stuff that you want, and then use it. You are checking whether the stuff that you want exists, and then using something less specific.
This should fix your issue:
Major:
<xsl:for-each select="major[#Year > 2008]">
<br/>
<xsl:value-of select="."/>, declared in: <xsl:value-of select="#Year"/>
</xsl:for-each>
<br/>
Your use of
<xsl:value-of select="major[#Year]"/>
was also incorrect. The following would have actually output a year value:
<xsl:value-of select="major/#Year"/>
I strongly suggest using either <xsl:text> or <xsl:value-of> for your static text. Your current approach of sprinkling text throughout your XSLT is resulting in very messy-looking XSLT code:
<xsl:for-each select="class/student">
<xsl:value-of select="concat('ID: ', id)"/>
<br/>
<xsl:value-of select="concat('Name: ', lastName, ',', firstName)"/>
<br/>
<xsl:value-of select="concat('Date: ', date)"/>
<br/>
<xsl:text>Major:</xsl:text>
<xsl:for-each select="major[#Year > 2008]">
<br/>
<xsl:value-of select="concat(., ', declared in: ', #Year)"/>
</xsl:for-each>
<br/>
</xsl:for-each>

how to check if a string variable is comma delimited in XSL?

I need to test if a xsl variable contains just one word or multiple comma separated words in xslt.
since the variable is dynamic, its a parameter that maps to url querystring which can either be sth like this:
index.aspx?role=manager
or
index.aspx?role=manager,director
in XSLT, i need to find out if the $role parameter is just one word or multiple comma separated words. If multiple words, then i need to extract each word and do xslt filtering based on those words like
/rows/row[role=extractedWord[1] or role = extractedWord[2]
so that only those rows matching for manager or director roles are returned.
Is this possible?
UPDATE:
I have a filter like this:
<xsl:variable name="AllPerRole" select="/dsQueryResponse/Rows/Row[contains(#EmployeeRoles,$Role)]" />
If role has just one value like index.aspx?role=manager then above works fine.
But if role is multiple words like these,
index.aspx?role=manager,director
then i need to some extract that string 'manager,CEO' and be able to do the following:
<xsl:variable name="AllPerRole" select="/dsQueryResponse/Rows/Row[contains(#EmployeeRoles,$Role{firstRole}) or contains(#EmployeeRoles,$Role{2ndRole})]" />
You can set up a variable for the first role in $Role as follows:
<xsl:variable name="Role1">
<xsl:choose>
<xsl:when test="contains($Role, ',')">
<xsl:value-of select="substring-before($Role, ',')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$Role" />
</xsl:otherwise>
<xsl:choose>
</xsl:variable>
And then set up a variable Role2 for the second role (if any), using substring-after() instead of substring-before().
Then:
<xsl:variable name="AllPerRole"
select="/dsQueryResponse/Rows/Row[
contains(#EmployeeRoles,$Role1) or contains(#EmployeeRoles,$Role2)]" />

extracting information from a JSON file using XSLT version 1.0

I'm a noobie to stackoverflow and xslt so I hope I don't sound unintelligent!
So I am working with SDI for a GIS company and I have a task that requires me to convert points that are in one spacial reference system (SRS) coordinate plane, such as EPSG:4035, to the world SRS, aka EPSG:4326. This really isn't a problem for me since I have the accessibility of an online service that will just give me what I want. However, the format that it outputs is in either JSON or HTML. I have browsed for a while to find a way to extract information from a JSON file but most of the techniques I have seen use xslt:stylesheet version 2.0, and I have to use version 1.0. One method I thought about doing was using the document($urlWithJsonFormat) xslt function, however this only accepts xml files.
Here is an example of the JSON formatted file that I would retrieve after asking for the conversion:
{
"geometries" :
[{
"xmin" : -4,
"ymin" : -60,
"xmax" : 25,
"ymax" : -41
}
]
}
All I simply want are the xmin, ymin, xmax, and ymax values, that's all! It just seems so simple yet nothing works for me...
You could use an external entity to include the JSON data as part of an XML file that you then transform.
For instance, assuming the example JSON is saved as a file called "geometries.json" you could create an XML file like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE wrapper [
<!ENTITY otherFile SYSTEM "geometries.json">
]>
<wrapper>&otherFile;</wrapper>
And then transform it with the following XSLT 1.0 stylesheet:
<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="wrapper">
<geometries>
<xsl:call-template name="parse-json-member-value">
<xsl:with-param name="member" select="'xmin'"/>
</xsl:call-template>
<xsl:call-template name="parse-json-member-value">
<xsl:with-param name="member" select="'ymin'"/>
</xsl:call-template>
<xsl:call-template name="parse-json-member-value">
<xsl:with-param name="member" select="'xmax'"/>
</xsl:call-template>
<xsl:call-template name="parse-json-member-value">
<xsl:with-param name="member" select="'ymax'"/>
</xsl:call-template>
</geometries>
</xsl:template>
<xsl:template name="parse-json-member-value">
<xsl:param name="member"/>
<xsl:element name="{$member}">
<xsl:value-of select="normalize-space(
translate(
substring-before(
substring-after(
substring-after(.,
concat('"',
$member,
'"'))
, ':')
,'
')
, ',', '')
)"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
To produce the following output:
<geometries>
<xmin>-4</xmin>
<ymin>-60</ymin>
<xmax>25</xmax>
<ymax>-41</ymax>
</geometries>
The two main choices here seem to be:
write (or use) a JSON parser in XSLT 1.0, or
use some other language than XSLT.
Since XSLT 1 engines generally can't process JSON directly I'd recommend using some other language to convert to XML.
https://github.com/WelcomWeb/JXS may help you too, if this is XSLT in a Web browser.

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!