Adding namespaces to elements only and changing to uppercase - namespaces

I have an xml coming as below
<Envelope>
<content>
<feild1>1</feild1>
....
<feild10>10<feild10>
</content>
</Envelope>
but want the xml to be (add the namespace prefix as xs1 and content lower case c to Upper case "C"
<Envelope>
<Content> <!-- Note the lower case c chnaged to Upper Case --->
<xs1:feild1>1</xs1:feild1> <!-- and for the feild, xs1 is added as prefix -->
....
<xs1:feild10>10</xs1:feild10>
</Content>
</Envelope>
Please help looking for XSLT 1.0
Here is my XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs1="https://temp">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="*">
<xsl:element name="{name()}" namespace="https://temp">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="#*">
<xsl:attribute name="{name}"><xsl:value-of select="."/></xsl:attribute>
</xsl:template>
</xsl:stylesheet>

Hope this helps
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs1="https://temp">
<xsl:template match="/">
<xsl:variable name="smallcase" select="'abcdefghijklmnopqrstuvwxyz'" />
<xsl:variable name="uppercase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />
<Envelope>
<xsl:for-each select="Envelope/*">
<xsl:variable name="root" select="local-name(.)"/>
<xsl:variable name="temp" select="translate($root,$smallcase,$uppercase)"/>
<xsl:variable name="temp2" select="concat(substring($temp,1,1),translate(substring($temp,2,string-length($temp)),$uppercase,$smallcase))"/>
<xsl:element name="{$temp2}">
<xsl:for-each select="child::*">
<xsl:element name="xs1:{local-name(.)}" namespace="https://temp">
<xsl:value-of select="node()"/>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:for-each>
</Envelope>
</xsl:template>
</xsl:stylesheet>

Related

Handle excluded element in a foreach

In first template, I am intentionally excluding an element ('milk') because the parsed data map is relatively flat and I would like to use XSLT to categorize and structure the data. The aim is to process the excluded element ('milk') in the second template. The both templates works running them one at a time. Running the templates together will not show the result of the excluded element ('milk') which should set another attribute name and attribute value.
JSON:
<data>
{
"storage": {
"pencils": 12,
"milk": 8,
"rulers": 4
}
}
</data>
XSL:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform
version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:storage="http://www.exammple.com/1"
xmlns:office="http://www.exammple.com/2"
xmlns:item="http://www.exammple.com/3"
expand-text="yes">
<xsl:output method="xml" indent="yes"/>
<xsl:mode on-no-match="shallow-skip"/>
<!-- Parse JSON to XML -->
<xsl:template match="data">
<storage:one>
<xsl:apply-templates select="json-to-xml(.)"/>
</storage:one>
</xsl:template>
<!-- Print map -->
<!-- <xsl:template match="*[#key = 'storage']"> <xsl:copy-of select=".."/> </xsl:template> -->
<xsl:template match="*[#key='storage']">
<xsl:for-each select="*[not(#key='milk')]">
<xsl:element name="item:{#key}">
<xsl:attribute name="office">plant-1</xsl:attribute>
<xsl:value-of select="text()"/>
</xsl:element>
</xsl:for-each>
</xsl:template>
<xsl:template match="*[#key='milk']">
<xsl:for-each select=".">
<xsl:element name="item:{#key}">
<xsl:attribute name="beverage">plant-2</xsl:attribute>
<xsl:value-of select="text()"/>
</xsl:element>
</xsl:for-each>
</xsl:template>
</xsl:transform>
Result:
<?xml version="1.0" encoding="UTF-8"?>
<storage:one xmlns:item="http://www.exammple.com/3"
xmlns:office="http://www.exammple.com/2"
xmlns:storage="http://www.exammple.com/1">
<item:pencils office="plant-1">12</item:pencils>
<item:rulers office="plant-1">4</item:rulers>
</storage:one>
Wanted result:
<?xml version="1.0" encoding="UTF-8"?>
<storage:one xmlns:item="http://www.exammple.com/3"
xmlns:office="http://www.exammple.com/2"
xmlns:storage="http://www.exammple.com/1">
<item:pencils office="plant-1">12</item:pencils>
<item:rulers office="plant-1">4</item:rulers>
<item:milk beverage="plant-2">8</item:milk>
</storage:one>
Your second template is never matched, because it is never reached. All elements are processed by <xsl:template match="*[#key='storage']"> - which doesn't have an <xsl:apply-templates ...> to reach further templates.
Your first template does not recurse into its children. So add an <xsl:apply-templates select="*" /> to the end of the first template:
<xsl:template match="*[#key='storage']">
<xsl:for-each select="*[not(#key='milk')]">
<xsl:element name="item:{#key}">
<xsl:attribute name="office">plant-1</xsl:attribute>
<xsl:value-of select="text()"/>
</xsl:element>
</xsl:for-each>
<xsl:apply-templates select="*" />
</xsl:template>
This will try to apply further templates at the level of "storage" and therefore match the second template.
I would write templates for each different output type and if the order of the output is different from the order of the input throw in an xsl:sort or an XPath 3.1 sort call to change the order:
<xsl:template match="data">
<storage:one>
<xsl:apply-templates select="json-to-xml(.)"/>
</storage:one>
</xsl:template>
<xsl:template match="*[#key = 'storage']">
<xsl:apply-templates select="sort(*, (), function($el) { $el/#key = 'milk' })"/>
</xsl:template>
<xsl:template match="*[#key='storage']/*[not(#key='milk')]">
<xsl:element name="item:{#key}">
<xsl:attribute name="office">plant-1</xsl:attribute>
<xsl:value-of select="text()"/>
</xsl:element>
</xsl:template>
<xsl:template match="*[#key='storage']/*[#key='milk']">
<xsl:element name="item:{#key}">
<xsl:attribute name="beverage">plant-2</xsl:attribute>
<xsl:value-of select="text()"/>
</xsl:element>
</xsl:template>

structure, under certain conditions

I'm trying my hand at html but I don't know how to do it. That's my but doesnt work:(:
<xsl:template match="*[contains(local-name(), '.')]">
<xsl:element name="{translate(local-name(), '.', '_')}" namespace="{namespace-uri()}">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
Does anyone have an idea how to deal with it?
It seems a recursive grouping problem though the single example doesn't really spell out when to wrap and/or nest items as lists; nevertheless with XSLT 2 or 3 it could be tackled with a recursive function using for-each-group:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="#all"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="html" indent="yes" html-version="5"/>
<xsl:function name="mf:wrap" as="element()*">
<xsl:param name="elements" as="element()*"/>
<xsl:param name="level" as="xs:integer"/>
<xsl:for-each-group select="$elements" group-adjacent="boolean(self::*[matches(local-name(), '^li[' || $level || '-9]+$')])">
<xsl:choose>
<xsl:when test="current-grouping-key()">
<ul class="li{$level}">
<xsl:sequence select="mf:wrap(current-group(), $level + 1)"/>
</ul>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:function>
<xsl:template match="*[matches(local-name(), '^li[0-9]+$')]">
<li>
<xsl:apply-templates/>
</li>
</xsl:template>
<xsl:template match="*[*[matches(local-name(), '^li[0-9]+$')]]">
<div>
<xsl:apply-templates select="mf:wrap(*, 1)"/>
</div>
</xsl:template>
<xsl:template match="uz">
<h5>
<xsl:apply-templates/>
</h5>
</xsl:template>
<xsl:template match="/">
<html>
<head>
<title>.NET XSLT Fiddle Example</title>
</head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/bnnZWK
Note that the list structure the above creates is a bit different, all liX items of the same X level are wrapped into a single ul class="liX" wrapper while your wanted sample at some places seems to wrap several items and at other places wrap only single items.

Converting an RSS pubDate to a mySQL time stamp format using XSLT

I've got a little tool which strips out and re-arranges an iTunes formatted RSS feed and converts it to a nice simple XML file.
I then import the cleansed XML into mySQL to do things with later.
I need to be able to convert the pubDate in the feed to a mySQL timestamp so I can import this properly into a TIMESTAMP field in my table.
I'm having some issues with it.
My current XSL file does a tidy up on the date, but I don't need this at all.
I just want the <pubDate> node to have the correct mySQL friendly timestamp inside instead.
I've not yet managed to find anything which does what I need. Any pointers?
Here is my XSLT file...
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:cc="http://web.resource.org/cc/"
xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
xmlns:libsyn="http://libsyn.com/rss-extension"
xmlns:media="http://search.yahoo.com/mrss/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
exclude-result-prefixes="atom cc itunes libsyn media rdf">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<data>
<entries>
<xsl:apply-templates select="rss/channel/item"></xsl:apply-templates>
</entries>
</data>
</xsl:template>
<xsl:template match="item">
<entry>
<title><xsl:value-of select="title"/></title>
<link><xsl:value-of select="link"/></link>
<description><xsl:value-of select="description" disable-output-escaping="yes"/></description>
<subtitle><xsl:value-of select="itunes:subtitle"/></subtitle>
<pubDate><xsl:value-of select="pubDate"/></pubDate>
<xsl:apply-templates select="pubDate"/>
<explicit><xsl:value-of select="itunes:explicit"/></explicit>
<podcastImage><xsl:value-of select="itunes:image/#href"/></podcastImage>
<podcastURL><xsl:value-of select="enclosure/#url"/></podcastURL>
<podcastLength><xsl:value-of select="enclosure/#length"/></podcastLength>
<podcastDuration><xsl:value-of select="itunes:duration"/></podcastDuration>
</entry>
</xsl:template>
<xsl:template match="pubDate">
<date>
<xsl:attribute name="time"><xsl:value-of select="substring(text(),18,5)"/></xsl:attribute>
<xsl:call-template name="format-from-rfc-to-iso">
<xsl:with-param name="rfc-date" select="text()"/>
</xsl:call-template>
</date>
</xsl:template>
<xsl:template name="format-from-rfc-to-iso">
<xsl:param name="rfc-date"/>
<xsl:param name="day-with-zero" select="format-number(substring(substring($rfc-date,6,11),1,2),'00')"/>
<xsl:param name="month-with-zero">
<xsl:if test="contains($rfc-date,'Jan')">01</xsl:if>
<xsl:if test="contains($rfc-date,'Feb')">02</xsl:if>
<xsl:if test="contains($rfc-date,'Mar')">03</xsl:if>
<xsl:if test="contains($rfc-date,'Apr')">04</xsl:if>
<xsl:if test="contains($rfc-date,'May')">05</xsl:if>
<xsl:if test="contains($rfc-date,'Jun')">06</xsl:if>
<xsl:if test="contains($rfc-date,'Jul')">07</xsl:if>
<xsl:if test="contains($rfc-date,'Aug')">08</xsl:if>
<xsl:if test="contains($rfc-date,'Sep')">09</xsl:if>
<xsl:if test="contains($rfc-date,'Oct')">10</xsl:if>
<xsl:if test="contains($rfc-date,'Nov')">11</xsl:if>
<xsl:if test="contains($rfc-date,'Dec')">12</xsl:if>
</xsl:param>
<xsl:param name="year-full" select="format-number(substring(substring($rfc-date,6,11),7,5),'####')"/>
<xsl:param name="rfc-date-to-iso" select="concat($year-full,'-',$month-with-zero,'-',$day-with-zero)"/>
<xsl:value-of select="$rfc-date-to-iso"/>
</xsl:template>
</xsl:stylesheet>
The current date/time looks like this from the rss feed:
<pubDate>Sun, 07 Feb 2016 00:00:56 -0500</pubDate>
I'd like it to by displayed like this so it can be inserted into mySQL:
<pubDate>2016-02-07 00:00:56</pubDate>
I use PHP to process this.
$xml = new DOMDocument;
$xml->load('podbean.xml');
$xsl = new DOMDocument;
$xsl->load('podbean.xsl');
// Configure the transformer
$proc = new XSLTProcessor;
$proc->importStyleSheet($xsl); // attach the xsl rules
$proc->transformToXML($xml);
$proc->transformToURI($xml,'itunes.xml');
Simon
Using a processor that supports the EXSLT str:tokenize() function (as libxslt does), you can do something like this:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:str="http://exslt.org/strings"
extension-element-prefixes="str">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- ... -->
<xsl:template match="item">
<entry>
<!-- ... -->
<pubDate>
<xsl:variable name="date-tokens" select="str:tokenize(pubDate, ' ' )" />
<!-- year -->
<xsl:value-of select="$date-tokens[4]" />
<xsl:text>-</xsl:text>
<!-- month -->
<xsl:variable name="mmm" select="$date-tokens[3]" />
<xsl:variable name="m" select="string-length(substring-before('JanFebMarAprMayJunJulAugSepOctNovDec', $mmm)) div 3 + 1" />
<xsl:value-of select="format-number($m, '00')" />
<xsl:text>-</xsl:text>
<!-- day -->
<xsl:value-of select="format-number($date-tokens[2], '00')" />
<xsl:text> </xsl:text>
<!-- time -->
<xsl:value-of select="$date-tokens[5]" />
</pubDate>
<!-- ... -->
</entry>
</xsl:template>
</xsl:stylesheet>

How to call template with name as a variable in XSLT?

I have the following XML document which needs to be parsed with an XSLT to HTML.
<root>
<c>
<c1>
<id>1</id>
<text>US</text>
</c1>
<c1>
<id>2</id>
<text>UK</text>
</c1>
</c>
</root>
The XSLT for converting this to HTML is given below.
<?xml version="1.0" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes" />
<xsl:template match="root">
<html>
<xsl:for-each select="c/c1">
**<xsl:variable name="vTemplate" select="text"/>
<xsl:apply-templates select="$vTemplate[#name='text'"/>**
</xsl:for-each>
</html>
</xsl:template>
<xsl:template match="xsl:template[#name='text']" name="text">
<select>
<xsl:attribute name="id">
<xsl:value-of select="id"/>
</xsl:attribute>
</select>
</xsl:template>
</xsl:stylesheet>
I need to call a template depends up on the text field. So for the value US, one template will be executed and for UK, another will executed.
How to achieve this with a variable as a template name while calling the template? I just made a try but it gives error. Can someone help me to figure out where i made wrong?
I think it is not possible to choose name of template to be called dynamically. What could be done is xsl:choose utilization (perhaps with combination with mode attribute), like this
<?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" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/root">
<html>
<xsl:for-each select="c/c1">
<xsl:choose>
<xsl:when test="text = 'US'">
<xsl:apply-templates select="text" mode="US"/>
</xsl:when>
<xsl:when test="text = 'UK'">
<xsl:apply-templates select="text" mode="UK"/>
</xsl:when>
<xsl:otherwise>
<xsl:comment>Something's wrong</xsl:comment>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</html>
</xsl:template>
<xsl:template match="text" mode="US">
<xsl:comment>US mode</xsl:comment>
<select>
<xsl:attribute name="id">
<xsl:value-of select="preceding-sibling::id"/>
</xsl:attribute>
</select>
</xsl:template>
<xsl:template match="text" mode="UK">
<xsl:comment>UK mode</xsl:comment>
<select>
<xsl:attribute name="id">
<xsl:value-of select="preceding-sibling::id"/>
</xsl:attribute>
</select>
</xsl:template>
</xsl:stylesheet>
Or you can use match with appropriate predicate and avoid for-each like this
<?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" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/root">
<html>
<xsl:apply-templates select="//c1" />
</html>
</xsl:template>
<xsl:template match="c1[text = 'US']">
<xsl:comment>US mode</xsl:comment>
<select id="{id}" />
</xsl:template>
<xsl:template match="c1[text = 'UK']">
<xsl:comment>UK mode</xsl:comment>
<select id="{id}" />
</xsl:template>
</xsl:stylesheet>
The id attribute of select can be also filled by "Attribute value templates" (xpath in curly brackets) as shown in previous sample.

How to sanitize Mysql queries with XSL template?

I have a XSL associate with an XML file. This XSL aim to create Mysql queries but in my XML I had some special characters like apostroph ' which break my queries. Do you know how I can sanitize my XSL template in order to have safe queries?
Example of my XML file
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="fnac.xsl"?>
<products xmlns="http://zanox.com/productdata/exportservice/v1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://zanox.com/productdata/exportservice/v1 http://productdata.zanox.com/exportservice/schema/export-1.1.xsd">
<product>
<name>jack o'connor</name>
<program>3467</program>
</product>
</products>
And my XSL file :
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:v1="http://zanox.com/productdata/exportservice/v1">
<xsl:output method="text" omit-xml-declaration="yes"/>
<xsl:template match="/">
<xsl:apply-templates select="//v1:product"/>
</xsl:template>
<xsl:template match="v1:product">
<xsl:text>insert into fnac (name, program) values(</xsl:text>
<xsl:value-of select="./v1:name"/>
<xsl:text>,'</xsl:text>
<xsl:value-of select="./v1:program"/>
<xsl:text>'); </xsl:text>
</xsl:template>
</xsl:stylesheet>
Thanks for your inputs!
If you want to remove apostrope from your XML file, you should use this xslt:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:v1="http://zanox.com/productdata/exportservice/v1">
<xsl:output method="text" omit-xml-declaration="yes"/>
<xsl:template match="/">
<xsl:apply-templates select="//v1:product"/>
</xsl:template>
<xsl:template match="v1:product">
<xsl:text>insert into fnac (name, program) values(</xsl:text>
<xsl:call-template name="replace-string">
<xsl:with-param name="text" select="./v1:name"/>
<xsl:with-param name="from">'</xsl:with-param>
<xsl:with-param name="to" select="' '"/>
</xsl:call-template>
<xsl:text>,'</xsl:text>
<xsl:value-of select="./v1:program"/>
<xsl:text>'); </xsl:text>
</xsl:template>
<xsl:template name="replace-string">
<xsl:param name="text"/>
<xsl:param name="from"/>
<xsl:param name="to"/>
<xsl:choose>
<xsl:when test="contains($text, $from)">
<xsl:variable name="before" select="substring-before($text, $from)"/>
<xsl:variable name="after" select="substring-after($text, $from)"/>
<xsl:copy-of select="$before"/>
<xsl:value-of select="$to" disable-output-escaping="yes"/>
<xsl:call-template name="replace-string">
<xsl:with-param name="text" select="$after"/>
<xsl:with-param name="from" select="$from"/>
<xsl:with-param name="to" select="$to"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
which would generate output:
insert into fnac (name, program) values(jack o connor,'3467');
If you only like/need to replace the apostrope , this could be done lot easier with translate().
Try this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:v1="http://zanox.com/productdata/exportservice/v1">
<xsl:output method="text" omit-xml-declaration="yes"/>
<xsl:template match="/">
<xsl:apply-templates select="//v1:product"/>
</xsl:template>
<xsl:template match="v1:product">
<xsl:text>insert into fnac (name, program) values(</xsl:text>
<xsl:value-of select='translate(./v1:name,"&apos;", " ")'/>
<xsl:text>,'</xsl:text>
<xsl:value-of select="./v1:program"/>
<xsl:text>'); </xsl:text>
</xsl:template>
</xsl:stylesheet>