XSLT formatting of string - xslt

I am a novice when it comes to XSLT.
I run the below select
<xsl:value-of select="./#name"/>
I get the following result
TestSomething.Cancel(GIVEN WHEN THEN)
I want the output to say
GIVEN WHEN THEN
instead of TestSomething.Cancel(GIVEN WHEN THEN)
Would be thankful if someone could point me in the right direction.

Use ...
<xsl:value-of select="substring-before(substring-after(./#name,'('),')')" />

It would help if you could post the source XML and some information on the xslt processor you are using, but at a guess I'd say this.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<xsl:value-of select="substring-before(substring-after(./#name, '('), ')')"/>
</xsl:template>
</xsl:stylesheet>

Related

XSL modify output based on presence of element data in another file

This is all new to me and I'm trying to learn how it works by examining and modifying some pre-existing XSL on our system, so please excuse that I don't really have a test script to troubleshoot.
I have an exported list of rooms that exist on our system - and a survey of criteria within those rooms.
I want to lookup the surveyed room and alter the behaviour of the output depending on whether it exists on the export or not.
Some sample data below which gives an idea of what I want to achieve. I'm just not sure how to (a) link to the reference xml and (b) query it in this way.
Reference listing XML (name = spaces.xml):
<Listing>
<Space>
<Code>Room1</Code>
</Space>
<Space>
<Code>Room2</Code>
</Space>
<Space>
<Code>Room3</Code>
</Space>
<Space>
<Code>Room4</Code>
</Space>
</Listing>
Survey XML example
<Survey>
<Record>
<Ref>1</Ref>
<Space>Room1</Space>
<Data>123</Data>
</Record>
<Record>
<Ref>2</Ref>
<Space>Room2</Space>
<Data>456</Data>
</Record>
<Record>
<Ref>3</Ref>
<Space>Room5</Space>
<Data>789</Data>
</Record>
</Survey>
As room 5 is not in the listing, the output should behave differently
XSLT so far. This doesn't include the reference to the listing.xml file or the proper test condition
xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml"/>
<xsl:template match="Record">
<OutputData>
<SurveyRef>
<xsl:value-of select="Ref"/>
</SurveyRef>
<xsl:choose>
<xsl:when test="##SPACE IS PRESENT IN SPACES.XML##">
<SpaceRef>
<xsl:value-of select="Space"/>
</SpaceRef>
<xsl:otherwise>
<SpaceRef>CheckTheSpace</SpaceRef>
</xsl:otherwise>
</xsl:choose>
<SurveyResult>
<xsl:value-of select="Data"/>
</SurveyResult>
</OutputData>
</xsl:template>
</xsl:stylesheet>
Hoped-for output
<OutputData>
<SurveyRef>1</SurveyRef>
<SpaceRef>Room1</SpaceRef>
<SurveyResult>123</SurveyResult>
</OutputData>
<OutputData>
<SurveyRef>2</SurveyRef>
<SpaceRef>Room2</SpaceRef>
<SurveyResult>456</SurveyResult>
</OutputData>
<OutputData>
<SurveyRef>3</SurveyRef>
<SpaceRef>CheckTheSpace</SpaceRef>
<SurveyResult>789</SurveyResult>
</OutputData>
Any help with this much appreciated, as I'm sure it will help me get to use this tool more
effectively.
Thanks.
how to (a) link to the reference xml
It depends on where exactly the reference document will be. If you place it at the same location where your XSLT stylesheet is, then you can refer to it using just its name within the document() function. Otherwise you need to use either a relative or a full absolute path.
and (b) query it in this way.
The most convenient way to resolve a cross-reference is by using a key - for example:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:key name="spc" match="Space" use="Code" />
<xsl:template match="/Survey">
<root>
<xsl:for-each select="Record">
<OutputData>
<SurveyRef>
<xsl:value-of select="Ref"/>
</SurveyRef>
<SpaceRef>
<xsl:value-of select="if (key('spc', Space, document('space.xml'))) then Space else 'CheckTheSpace'"/>
</SpaceRef>
<SurveyResult>
<xsl:value-of select="Data"/>
</SurveyResult>
</OutputData>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>

XSL Generating CSV

Trying to convert this:
<list>
<entry>
<parentFeed>
<feedUrl>http://rss.nzherald.co.nz/rss/xml/nzhrsscid_000000001.xml</feedUrl>
<id>68</id>
</parentFeed>
<content>Schools will have to put up with problematic pay administered through Novopay for another eight weeks after the Government announced it would persist with the unstable system.Minister responsible for Novopay, Steven Joyce, delayed...</content>
<link>http://www.nzherald.co.nz/nz/news/article.cfm?c_id=1&objectid=10872300&ref=rss</link>
<title>Novopay: Govt sticks with unstable system</title>
<id>55776</id>
<published class="sql-timestamp">2013-03-19 03:38:55.0</published>
<timestamp>2013-03-19 07:31:16.358 UTC</timestamp>
</entry>
</list>
into this, using XSLT:
Title, Link, Date
Novopay: Govt sticks with unstable system, http://www.nzherald.co.nz/nz/news/article.cfm?c_id=1&objectid=10872300&ref=rss, 2013-03-19 03:38:55.0
But try as I might, I can't get rid of the blank line at the beginning of the document. My stylesheet follows:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:csv="csv:csv"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:template match="/list"> <xsl:for-each select="entry">
Title, Link, Date
<xsl:value-of select="title"/>, <xsl:value-of select="link"/>, <xsl:value-of select="published"/>
</xsl:for-each></xsl:template>
</xsl:stylesheet>
I've tried putting in <xsl:text>
</xsl:text> as suggested here which stripped the last linebreak, so I moved it to the top of the file, at which point it turned into a no-op. The solution here actually adds a blank line (which makes sense, as the hex code is for newline, according to the ascii manpage).
As a workaround, I've been using Java to generate the CSV output.
However, I do feel XSLT would be a lot faster as it is designed to transform XML to various other formats. A similar XSLT generates HTML, RSS, and ATOM feeds perfectly.
You have done it perfectly, your logic is spot on. However what you need to take in mind, when your outputting text all indents in your XSLT will affect the output so your XSLT should look like this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:csv="csv:csv"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:template match="/list"> <xsl:for-each select="entry">Title, Link, Date
<xsl:value-of select="title"/>, <xsl:value-of select="link"/>, <xsl:value-of select="published"/>
<xsl:text>
</xsl:text>
</xsl:for-each></xsl:template>
</xsl:stylesheet>
Run the above XSLT and it will work perfectly.

Problems with XSLT and apply templates

I'm going to be brief. I'm doing XSLT on the client. The output is a report/html with data. The report consists of several blocks, ie one block is one child element of root-node in the xml file.
There are n reports residing in n different xslt-files in my project and the reports can have the same block. That means if there is a problem with one block for one report and it is in n reports i have to update every n report (xslt file).
So i want to put all my blocks in templates (kind-of-a businesslayer) that i can reuse for my reports by xsl:include on the templates for those reports.
So the pseudo is something like this:
<?xml version="1.0".....?>
<xsl:stylesheet version="1.0"....>
<xsl:include href="../../Blocks/MyBlock.xslt"/>
<xsl:template match='/'>
<xsl:apply-templates />
</xsl:template>
</xsl:stylesheet>
MyBlock.xslt:
<?xml version="1.0"....?>
<xsl:stylesheet version="1.0".....>
<xsl:template match='/root/rating'>
HTML OUTPUT
</xsl:template>
</xsl:stylesheet>
I hope someone out there understands my question. I need pointers on how to go about this, if this is one way to do it. But it doesn't seem to work.
Below is my experience that how am dealing this.
This is example which I modified your code.
<?xml version="1.0"?>
<xsl:stylesheet version="1.0">
<xsl:include href="../../Blocks/MyBlock.xslt"/>
<xsl:template match="/">
<xsl:apply-templates select="node()" mode="callingNode1"/>
</xsl:template>
</xsl:stylesheet>
MyBlock.xslt:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0">
<xsl:template mode="callingNode1" match="*">
HTML OUTPUT
</xsl:template>
<xsl:template mode="callingNode2" match="/root/rating">
HTML OUTPUT
</xsl:template>
</xsl:stylesheet>
Here am calling the nodes based on the mode & match.

Namespace prefix is missing after XSLT transformation of XML using Apache Xalan

I am having the following XML:
<?xml version="1.0"?>
<abc:Element1 xmlns:abc="http://..../resources/abc/v2/"
...>
<abc:Element2>
<abc:Element3s>
<abc:Element4 name="name1"
resourceRef="name2"/>
</abc:Element3s>
</abc:Element2>
<abc:Resources>
<abc:Resource xsi:type="abc:Something"
name="name2"/>
</abc:Resources>
</abc:Element1>
... and this XSLT stylesheet:
<?xml version="1.0" encoding="UTF-8"?><xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:abc="http://.../resources/abc/v2/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:template match="/">
<checker name="something">
<xsl:for-each select="abc:Element1/abc:Element2/abc:Element3s/abc:Element4">
<xsl:variable name="resource" select="#resourceRef"/>
<xsl:variable name="xsiType"><xsl:value-of select="//abc:Resource[#name=$resource]/#xsi:type"/></xsl:variable>
<xsl:choose>
<xsl:when test="$xsiType='abc:Something'">
...
</xsl:when>
<xsl:otherwise>
...
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</checker>
</xsl:template>
</xsl:stylesheet>
I am using XALAN 2.7.1 with org.apache.xalan.xsltc.trax.TransformerFactoryImpl (also tried with org.apache.xalan.processor.TransformerFactoryImpl -> same result) to transform the XML.
I expect the following line to store abc:Something in variable xsiType.
<xsl:variable name="xsiType"><xsl:value-of select="//abc:Resource[#name=$resource]/#xsi:type"/></xsl:variable>
but unfortunatley only Something (without namespace as prefix) is stored in xsiType. I verified this because
<xsl:when test="$xsiType='abc:Something'">
is not true.
I also transformed the XMl using xsltproc and the resulting XML looks as expected. Therefore, I expect the input XML/XSLT stylesheet to be correct. I assume something is wrong with Xalan and its configuration.
Can anyone help?
Your sample data is not well-formed, so it's difficult to tell. This is likely to be a namespace issues. Here's a sanitized version of your input and stylesheet that extracts the data you want:
<?xml version="1.0"?>
<abc:Element1
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:abc="http://resources/abc/v2">
<abc:Element2>
<abc:Element3s>
<abc:Element4 name="name1" resourceRef="name2"/>
</abc:Element3s>
</abc:Element2>
<abc:Resources>
<abc:Resource xsi:type="abc:Something" name="name2"/>
</abc:Resources>
</abc:Element1>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:abc="http://resources/abc/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
exclude-result-prefixes="abc xsi"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:variable name="resource" select="'name2'"/>
<xsl:variable name="type"
select="//abc:Resource[#name=$resource]/#xsi:type"/>
<checker name="{ $type }"/>
</xsl:template>
</xsl:stylesheet>
This will produce:
<?xml version="1.0"?>
<checker name="abc:Something"/>
It looks to me like a problem specific to Xalan. It could however be a problem with the underlying XML parser: the default parser in th Sun JDK has some weird bugs including some that corrupt attribute values. Always use the Apache versions of Xalan and Xerces rather than the versions that come with the JDK. And of course, if you're using Xalan then it's almost zero cost to switch to Saxon, which gives you all the benefits of XSLT 2.0.

Problem with XSLT getting values from tags with namespace prefixes

I have a specific problem getting values for width and height out of some XML that has namespace prefixes defined. I can get other values such as SomeText from RelatedMaterial quite easily using normal xpath with namespace "n:" but unable to get values for width and height.
Sample XML:
<Description>
<Information>
<GroupInformation xml:lang="en">
<BasicDescription>
<RelatedMaterial>
<SomeText>Hello</SomeText>
<t:ContentProperties>
<t:ContentAttributes>
<t:Width>555</t:Width>
<t:Height>444</t:Height>
</t:ContentAttributes>
</t:ContentProperties>
</RelatedMaterial>
</BasicDescription>
</GroupInformation>
</Information>
</Description>
Here is an extract from the XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:n="urn:t:myfoo:2010" xmlns:tva2="urn:t:myfoo:extended:2008"
<xsl:apply-templates select="n:Description/n:Information/n:GroupInformation"/>
<xsl:template match="n:GroupInformation">
<width>
<xsl:value-of select="n:BasicDescription/n:RelatedMaterial/t:ContentProperties/t:ContentAttributes/t:Width"/>
</width>
</xsl:template>
The above XSLT does not work for getting the width. Any ideas?
I'm not sure you have realised that both your input and XSLT is invalid, it's always better to provide working examples.
Anyway, if we look at the XPath expression n:BasicDescription/n:RelatedMaterial/t:ContentProperties/t:ContentAttributes/t:Width you're using a prefix n which is mapped to urn:t:myfoo:2010 but when the data infact is in the default namespace. The same goes for the t prefix which isn't defined at all in neither the input data nor XSLT.
You need to define the namespaces on "both sides", in the XML data and the XSLT transformation and they need to be the same, not the prefixes, but the URI.
Somebody else could probably explain this better than me.
I've corrected your example and added a few things to make this work.
Input:
<?xml version="1.0" encoding="UTF-8"?>
<Description
xmlns="urn:t:myfoo:2010"
xmlns:t="something...">
<Information>
<GroupInformation xml:lang="en">
<BasicDescription>
<RelatedMaterial>
<SomeText>Hello</SomeText>
<t:ContentProperties>
<t:ContentAttributes>
<t:Width>555</t:Width>
<t:Height>444</t:Height>
</t:ContentAttributes>
</t:ContentProperties>
</RelatedMaterial>
</BasicDescription>
</GroupInformation>
</Information>
</Description>
XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xmlns:n="urn:t:myfoo:2010"
xmlns:t="something...">
<xsl:template match="/">
<xsl:apply-templates select="n:Description/n:Information/n:GroupInformation"/>
</xsl:template>
<xsl:template match="n:GroupInformation">
<xsl:element name="width">
<xsl:value-of select="n:BasicDescription/n:RelatedMaterial/t:ContentProperties/t:ContentAttributes/t:Width"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Output:
<?xml version="1.0" encoding="UTF-8"?>
<width>555</width>