Starts with number in xslt - xslt

Entries start with a number or the word "noon". Those will become elements.
An end-nested-style character () should be placed at the end of the show's title, which is marked by a space followed by one of the following codes: MVLSC, 16VLSC, MSC, 16VL, ML, MC, PGC, 16VL
Input :
<table>
<tr>
<td> 7.10 Between Worlds 16VLSC 2018 Thriller. Haunted by memories of his deceased family, a truck.</td>
</tr>
<tr>
<td>A look at the latest movie trailers, coming soon to cinemas.</td>
</tr>
</table>
Output :
<p type="Entry"><tab/>7.10<tab/>Between Worlds<char type="endNestedStyleHere"/> 16VLSC 2018 Thriller. Haunted by memories of his deceased family, a truck.</p>
Tried code :
<xsl:template match="td[starts-with(.,'1234567890')]">
<p>
<xsl:apply-templates/>
</p>
</xsl:template>
I am using XSLT 2.0. Thank you

You can use matches in XSLT 2.0
<xsl:template match="td[matches(., '^[0-9].*')]">
<p>
<xsl:apply-templates/>
</p>
</xsl:template>
Or maybe you need to check for spaces too too if there could be whitespace before the number...
<xsl:template match="td[matches(., '^\s*[0-9].*')]">
<p>
<xsl:apply-templates/>
</p>
</xsl:template>
Note, if you did want an XSLT 1.0 solution, you could do this...
<xsl:template match="td[contains('0123456789', substring(normalize-space(), 1, 1))]">

Related

Transforming XLIFF to HTML table with XLST 1.0: group elements with same ID from different parent elements, side by side?

I'm trying to get my head around putting XLIFF's segmented source/target language elements side by side in an HTML table, like
<trans-unit id="/html[1]/head[1]/title[1]" resname="Title">
<source xml:lang="de-DE">E-Mail: Vorlagen</source>
<seg-source>
<mrk mid="1" mtype="seg">E-Mail: Vorlagen</mrk>
<mrk mid="2" mtype="seg">Vorlagen</mrk>
</seg-source>
<target state="translated">
<mrk mid="1" mtype="seg">Email:</mrk>
<mrk mid="2" mtype="seg">Templates</mrk>
</target>
</trans-unit>
should become
<tr>
<td class="mid">1</td> <td class="src">E-Mail:</td> <td class="tgt">Email:<td/>
</tr>
<tr>
<td class="mid">2</td> <td class="src">Vorlagen</td> <td class="tgt">Templates<td/>
</tr>
XLIFF translation units can have any number of sub-segments, it thus must be a solution that, I don't know, counts how many MIDs there are in a TU and then jumps back and forth between the source-segment and target elements (with an incremental counter?) to put them side by side in the table?
I have seen XSLT 2.0 solutions working with "for-each-group" wizardry, but did not yet get an idea of how to do it in XSLT 1.0 (edit: with Xalan 1.11). Frankly, I'm a bit amazed that no one has whipped up a style sheet to show bilingual translations in XLIFF side by side as HTML table, so I thought I'd give it a try. Unfortunately, my XSLT hasn't been used in, like, years and I'm a bit rusty. Any pointers what keyword/topic/web source I should have a look at, if not a straight solution?
Thanks a lot,
Christopher
P.S.: If I can work out a solution, I'll, of course, put it freely online for my fellow translator colleagues (or anyone who wants to get a look at what actually is these XLIFF files they send out to us translators).
Somehow along the following lines:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:key name="tr" match="trans-unit/target/mrk" use="concat(../../#id, '|', #mid)"/>
<xsl:output method="html" indent="yes" version="5" doctype-system="about:legacy-doctype"/>
<xsl:template match="trans-unit[#id]">
<table>
<xsl:apply-templates select="source"/>
<thead>
<tr>
<th>mid</th>
<th>source</th>
<th>target</th>
</tr>
</thead>
<tbody>
<xsl:apply-templates select="seg-source/mrk"/>
</tbody>
</table>
</xsl:template>
<xsl:template match="trans-unit/source">
<caption>
<xsl:apply-templates/>
</caption>
</xsl:template>
<xsl:template match="seg-source/mrk">
<tr>
<td class="mid">
<xsl:value-of select="#mid"/>
</td>
<td class="src">
<xsl:apply-templates/>
</td>
<td class="tgt">
<xsl:apply-templates select="key('tr', concat(../../#id, '|', #mid))/text()"/>
</td>
</tr>
</xsl:template>
<xsl:template match="/">
<html>
<head>
<title>Example</title>
</head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

XSLT, Extract sub-string from xml using regex

I am trying to apply XSLT on SVN log, I need to extract bug numbers from the commit messages. I am applying this regex on msg but get nothing back. What am I missing in XSLT?
Thank you in advance
Below is XML that I get from SVN:
<?xml version="1.0" encoding="UTF-8"?>
<log>
<logentry revision="265">
<author>dre</author>
<date>2015-04-13T02:35:25.246150Z</date>
<msg>modified code</msg>
</logentry>
<logentry revision="73283">
<author>john</author>
<date>2015-04-13T14:10:20.987159Z</date>
<msg>fixed bug DESK-1868</msg>
</logentry>
<logentry revision="73290">
<author>ron</author>
<date>2015-04-13T14:24:57.475711Z</date>
<msg>WEBAPP-1868 Fix for pallete list and settings dialog Selected Tab Index</msg>
</logentry>
</log>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<h2>SVN Issues</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th style="text-align:left">ver</th>
<th style="text-align:left">author</th>
<th style="text-align:left">date</th>
<th style="text-align:left">ticket</th>
</tr>
<xsl:for-each select="log/logentry">
<tr>
<td><xsl:value-of select="#revision"/></td>
<td><xsl:value-of select="author"/></td>
<td><xsl:value-of select="date"/></td>
<td>
<xsl:variable name="messageValue" select="msg"/>
<xsl:analyze-string select="$messageValue"
regex="(DESK|TRS|PEK|WEBAPP)-\d{4}$">
<xsl:matching-substring>
<bug><xsl:value-of select="regex-group(1)"/></bug>
</xsl:matching-substring>
</xsl:analyze-string>
</td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
http://www.w3.org/TR/xslt20/#analyze-string
Note: Because the regex attribute is an attribute value template, curly brackets within the regular expression must be doubled. For
example, to match a sequence of one to five characters, write
regex=".{{1,5}}". For regular expressions containing many curly
brackets it may be more convenient to use a notation such as
regex="{'[0-9]{1,5}[a-z]{3}[0-9]{1,2}'}", or to use a variable.
You do not want to anchor your expression to the end of the line using $ at the end of your expression. Otherwise the regex will only match when the message ends with an issue ID.
Use this regex expression to capture the entire bug number:
regex="((DESK|TRS|PEK|WEBAPP)-\d{{4}})"

XML to HTML table using XSL

I have an XML file that I export from my DB which is structured as below,
'
<output>
<row>
<Month>October</Month>
<Location>kansas</Location>
<bus_name>bus1</bus_name>
<bus_type>volvo</bus_type>
<bus_colour>red</bus_colour>
<bus_count>10</bus_count>
</row>
<row>
<Month>October</Month>
<Location>kansas</Location>
<bus_name>bus1</bus_name>
<bus_type>Volvo</bus_type>
<bus_colour>green</bus_colour>
<bus_count>11</bus_count>
</row>
<Month>October</Month>
<Location>kansas</Location>
<bus_name>bus1</bus_name>
<bus_type>Merc</bus_type>
<bus_colour>blue</bus_colour>
<bus_count>5</bus_count>
</row>
So on...
</output>
I need the table to look like the image attached below. The XSL and XML file will be refreshed periodically.The cells will have similar color's based on bus type.
I'm new to XSL thus having a really hard time coming up with a solution. Any help would be appreciated.
Just as a different approach and also handling the colours for the same bus_types.
Demo
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html"/>
<xsl:template match="/">
<table>
<tr>
<td colspan="9">ACME BUS SERVICE</td>
</tr>
<tr>
<td colspan="9">
Month: <xsl:value-of select="//Month"/>
</td>
</tr>
<tr>
<td>Season</td>
<td>Location</td>
<td>Bus Name</td>
<td colspan="2">RED</td>
<td colspan="2">GREEN</td>
<td colspan="2">BLUE</td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td>Bus Type</td>
<td>Bus Count</td>
<td>Bus Type</td>
<td>Bus Count</td>
<td>Bus Type</td>
<td>Bus Count</td>
</tr>
<xsl:for-each select="//row[Location[not(preceding::Location/. = .)]]" >
<xsl:variable name="currentLocation" select="./Location"/>
<tr>
<xsl:attribute name="class">
<xsl:value-of select="$currentLocation"/>
</xsl:attribute>
<td>
<xsl:if test="position()=1">Winter</xsl:if>
</td>
<td>
<xsl:value-of select="$currentLocation"/>
</td>
<td>
<xsl:value-of select="./bus_name"/>
</td>
<td>
<xsl:if test="count(//row[Location= $currentLocation]
[bus_type = //row[Location= $currentLocation]
[bus_colour = 'red']/bus_type]) > 1">
<xsl:attribute name="class">color</xsl:attribute>
</xsl:if>
<xsl:value-of select="//row[Location= $currentLocation]
[bus_colour = 'red']/bus_type"/>
</td>
<td>
<xsl:value-of select="//row[Location= $currentLocation]
[bus_colour = 'red']/bus_count"/>
</td>
<td>
<xsl:if test="count(//row[Location= $currentLocation]
[bus_type = //row[Location=$currentLocation]
[bus_colour = 'green']/bus_type]) > 1">
<xsl:attribute name="class">color</xsl:attribute>
</xsl:if>
<xsl:value-of select="//row[Location= $currentLocation]
[bus_colour = 'green']/bus_type"/>
</td>
<td>
<xsl:value-of select="//row[Location= $currentLocation]
[bus_colour = 'green']/bus_count"/>
</td>
<td>
<xsl:if test="count(//row[Location= $currentLocation]
[bus_type = //row[Location=$currentLocation]
[bus_colour = 'blue']/bus_type]) > 1">
<xsl:attribute name="class">color</xsl:attribute>
</xsl:if>
<xsl:value-of select="//row[Location= $currentLocation]
[bus_colour = 'blue']/bus_type"/>
</td>
<td>
<xsl:value-of select="//row[Location= $currentLocation]
[bus_colour = 'blue']/bus_count"/>
</td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
For every location, the location is set as classname to the <tr>, e.g. <tr class="kansas">. Every td with a bus type that is used more than once at a location gets the class="color". So to display the table with different colors, you can just add CSS like e.g. .kansas .color { background-color: blue; }. In case you want to display the same color based on the bus_type, just adjust the classname "color" in the xslt to the current bus_type.
Note: In the linked example I've only added one row for Texas to show that the XSLT displays multiple locations, only sets the season for the first one, and will also work in case not all colors are provided for a location. And the output is not valid HTML (no html-, head-, body-tags etc provided). As you mentioned you'd like to get HTML ouput, you probably already have an XSLT generating valid HTML where you can adjust/include the part you need for the table.
Update for the question in the comments: To set the class name for a <tr> to the name of the bus_type (in case a bus_type is used more than once at a location) instead of the location:
Change this in above XSLT:
<tr>
<xsl:attribute name="class">
<xsl:value-of select="$currentLocation"/>
</xsl:attribute>
into
<tr>
<xsl:if test="count(//row[Location=$currentLocation]) >
count(//row[Location=$currentLocation]/
bus_type[not(. = preceding::bus_type)])">
<xsl:attribute name="class">
<xsl:value-of select="//row[Location=$currentLocation]/
bus_type[ . = preceding::bus_type]"/>
</xsl:attribute>
</xsl:if>
Updated Demo 2 for this.
Additional notes and questions: One adjustment to the OP XML was to change the lowercase "volvo" to "Volvo". In case the original export from DB really mixes upper- and lowercase names, this can be handled in the XSLT to lowercase all bus_names (to get the unique values) and uppercase the first letter for the value in the <td>. Also it would be good to know if you use XSLT 2.0 or XSLT 1.0 as XSLT 2.0 provides functionality to simplify some tasks - e.g. 2.0 provides a lower-case() function where in 1.0 the same can be achieved using translate() - as reference for this as you mentioned you're new to XSL: How can I convert a string to upper- or lower-case with XSLT?
Further question is - as the XML example is only a part of the DB export - if there will be only one row for each location or if it is possible that there are various rows, e.g. kansas bus1, kansas bus2 etc.
Update 2 for the second question in the comments: I can add an (almost) line by line explanation and will drop a comment when done. I assume it's not necessary to cover the HTML part but only the XSLT. In the meantime, as you mentioned you're new to XSLT, maybe the following can be of use:
for <xsl:template match="/"> - https://stackoverflow.com/questions/3127108/xsl-xsltemplate-match
for XPath axes - http://www.xmlplease.com/axis
for some basics, e.g. - In what order do templates in an XSLT document execute, and do they match on the source XML or the buffered output?
Note that it should be avoided at SO to have extended comments - when there are too many comments below a post, an automated message will be displayed that suggests to move to chat. Because you need a reputation of 20 to chat (https://stackoverflow.com/help/privileges), this won't be possible at the moment.
The first three node has to be mapped with the first row
Now that is a specific question. Assuming your input is arranged so that each group of 3 consecutive <row> elements maps to a single table row (with internal group positions matching the column positions), try it this way:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:template match="/output">
<table border="1">
<tr>
<!-- build your header here -->
</tr>
<xsl:for-each select="row[position() mod 3 = 1]" >
<tr>
<td><xsl:value-of select="Location"/></td>
<td><xsl:value-of select="bus_name"/></td>
<xsl:for-each select=". | following-sibling::row[position() < 3]">
<td><xsl:value-of select="bus_type"/></td>
<td><xsl:value-of select="bus_colour"/></td>
<td><xsl:value-of select="bus_count"/></td>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
I suggest you ask a separate question regarding the coloring. Make sure we understand exactly what is known beforehand (e.g. a list of known bus types?) and what is the required output (post it as code).

XSLT: multiple tables

I come across this small problem while creating a xslt file... I have this generic xml file:
<data>
<folder>
<file>
<name>file1</name>
<date>2000</date>
<index1>1</index1>
<index2>1</index2>
</file>
<file>
<name>file2</name>
<date>2001</date>
<index1>1</index1>
<index2>1</index2>
</file>
<file>
<name>file3</name>
<date>2004</date>
<index1>2</index1>
<index2>1</index2>
</file>
</folder>
</data>
Given this abstract example, I have to transform it into something like:
<table>
<tr>
<td>Name</td>
<td>Date</td>
</tr>
<tr>
<td>file1</td>
<td>2000</td>
</tr>
<tr>
<td>file2</td>
<td>2001</td>
</tr>
</table>
<table>
<tr>
<td>Name</td>
<td>Date</td>
</tr>
<tr>
<td>file3</td>
<td>2004</td>
</tr>
</table>
I have to group the file elements per table based on their index1 and index2 (like an ID pair). I am able to create a table for every separated file, but I can't come with a solution to create a table for every file sharing index1 and index2. Any idea or suggestion?
Since you are using XSLT 2.0 you can use the xsl:for-each-group statement. You have two choices here, depending on whether you wish to keep groups together and respect the sequence or whether you just want to group regardless of sequence.
That is, given aabaab would you want groups of (aaaa, bb) or (aa, b, aa, b)?
This first groups all file elements with the same index1 and index2 regardless of order in the document (I've put in the body element just to make it well-formed)
<xsl:template match="folder">
<body>
<xsl:for-each-group select="file" group-by="concat(index1, '-', index2)">
<!-- xsl:for-each-group sets the first element in the group as the context node -->
<xsl:apply-templates select="."/>
</xsl:for-each-group>
</body>
</xsl:template>
<xsl:template match="file">
<table>
<tr>
<td>Name</td>
<td>Date</td>
</tr>
<xsl:apply-templates select="current-group()" mode="to-row"/>
</table>
</xsl:template>
<xsl:template match="file" mode="to-row">
<tr>
<xsl:apply-templates select="name|date"/>
</tr>
</xsl:template>
<xsl:template match="name|date">
<td><xsl:apply-templates/></td>
</xsl:template>
The second version would only need the first template changed to:
<xsl:template match="folder">
<body>
<xsl:for-each-group select="file" group-adjacent="concat(index1, '-', index2)">
<xsl:apply-templates select="."/>
</xsl:for-each-group>
</body>
</xsl:template>

How to repeat XSLT child nodes of the same name?

I have an xml dump of a transaction that I want to create a report from.
The (simplified) xml structure looks like:
Subject
id
name
license
event id=key
result
eventtype
date
action
charge id=key
result
code
date
...etc
There is only one id, name, and license for every subject; but there could be many events, and many charges for each subject--each one having a different result.
The report output is a table type, with each subject having a header of name, id, license, and multiple blocks of data transactions listed below the header. Each data block has different values and structure based on the type of transaction.
It works perfectly for the header, and one each of the data transaction types, but only the first event transaction and the first charge transaction will print out.
The (simplified) xsl looks like:
<xsl:for-each select="file/subject">
<xsl:if test="not (#subject = preceding-sibling::subject[1])">
<table width="754" border="2" cellpadding="0">
<tr>
<td width="172">ctn: <strong><xsl:value-of select="id"/></strong></td>
<td width="364">defendant: <strong><xsl:value-of select="name"/></strong></td>
<td width="200">sid: <strong><xsl:value-of select="license"/></strong></td>
</tr>
</table>
</xsl:if>
<blockquote>
<xsl:if test="event/eventType != ' '">
<table width="695" border="1">
<tr>
<td>Event<xsl:value-of select="event/result"/></td>
<td>Eventtype<xsl:value-of select="event/eventtype"/></td>
<td>Date<xsl:value-of select="event/date"/></td>
</tr>
</table>
</xsl:if>
<xsl:if test="charge/code != ' '">
<table width="695" border="1">
<tr>
<td>Charge<xsl:value-of select="charge/result"/></td>
<td>Code<xsl:value-of select="charge/code"/></td>
<td>Date<xsl:value-of select="charge/date"/></td>
</tr>
</table>
</xsl:if>
</blockquote>
</xsl:for-each>
I assume that I need an additional for-each to scan the child nodes, but adding the additional loop just gives me the headers.
Any thoughts?
(and thanks for taking the time to read this far. :o)
Where possible in XSLT, avoid for-each and apply your nodes to transformation templates instead.
I came up with this, which you can run at this playground session - not sure if it's broadly what you're after.
<!-- root and static content -->
<xsl:template match="/">
<xsl:apply-templates select='root/Subject' />
</xsl:template>
<!-- iteration content: subject -->
<xsl:template match='Subject'>
<table>
<tr>
<td>ctn: <strong><xsl:value-of select="id"/></strong></td>
<td>defendant: <strong><xsl:value-of select="name"/></strong></td>
<td>sid: <strong><xsl:value-of select="license"/></strong></td>
</tr>
</table>
<xsl:apply-templates select='event' />
<xsl:apply-templates select='charge' />
</xsl:template>
<!-- iteration content: event -->
<xsl:template match='event'>
<blockquote>
<table>
<tr>
<td>Event: <xsl:value-of select="result"/></td>
<td>Event type: <xsl:value-of select="eventtype"/></td>
<td>Date: <xsl:value-of select="date"/></td>
</tr>
</table>
</blockquote>
</xsl:template>
<!-- iteration content: charge -->
<xsl:template match='charge'>
<blockquote>
<table>
<tr>
<td>Charge: <xsl:value-of select="result"/></td>
<td>Code: <xsl:value-of select="code"/></td>
<td>Date: <xsl:value-of select="date"/></td>
</tr>
</table>
</blockquote>
</xsl:template>