Write html from XSLT - xslt

Take the below xml
<?xml version="1.0"?>
<?xml-stylesheet href="desktop.xsl" type="text/xsl"?>
<desktop>
<tag name="h1" caption="hello"/>
</desktop>
I have an XSLT that will take the name attribute of the tag element and create the appropriate html element
Snippet from the xsl
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="UTF-8" omit-xml-declaration="yes"/>
<xsl:template match="tag">
<{#name}>{#caption}</{#name}>
</xsl:template>
</xsl:stylesheet>
which of course is not working, due to the < > characters (I suppose)
How can I come around it?
Thanks

You will need to use <xsl:element>. See here.
For example:
<xsl:element name="#name"><xsl:value-of select="#caption"></xsl:element>

Use <xsl:element> instead which will create a new node. For example, I've once used the following code to create automatically nested headings in HTML:
<xsl:variable name="extlevel" select="count(ancestor::External[not(#link)])"/>
<xsl:element name="h{$extlevel + 2}"><xsl:value-of select="#name"/></xsl:element>

Related

Extract text inside cdata tag using XSLT

I have the following XML with a cdata tag that I would like to extract the text from?
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<cus:TestData xmlns:cus="http://test.namespace.com/data">
<![CDATA[testValue]]></cus:TestData >
How can I achieve this in XSLT?
I was briefly trying with the following
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
"<xsl:value-of select="/*/Name"/>"
</xsl:template>
</xsl:stylesheet>
But it doesn't seem to be working
Also the XML doesn't also have the same prefix or namespace, it changes
This is not really an issue with CData. Your XSLT is currently looking for an element called Name, under the root element, which does not exist in your XML. If your XML source is the one you are actually using, you can just do this...
<xsl:value-of select="/*"/>
But supposing your XML looked like this...
<cus:TestData xmlns:cus="http://test.namespace.com/data">
<cus:Name><![CDATA[testValue]]></cus:Name>
</cus:TestData>
Then, you would need to account for the namespace in your XSLT, as Name is in a namespace in your XML, but your XSLT is currently looking for a Name element in no namespace.
Something like this would do:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:c="http://test.namespace.com/data">
<xsl:output method="text"/>
<xsl:template match="/">
"<xsl:value-of select="/*/c:Name"/>"
</xsl:template>
</xsl:stylesheet>
Note, the prefixes don't need to match, but the namespace URI does.
If the namespace URI could actually vary, you could do something like this instead...
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
"<xsl:value-of select="/*/*[local-name() = 'Name']"/>"
</xsl:template>
</xsl:stylesheet>

Xslt url condition

I want to know if there's any url logic if url contains /market/3. like:
<xsl:variable name="cultureRequest" select="concat('http://',MAIN_URL)" />
<xsl:if test="contains($cultureRequest, '/market/3')" >
</xsl:if>
well, there's any way to check if url contains /market/3/ for example?
As I said in the comments, yes, that's possible. As long as the URL is a string. But you have to be aware that this does not mean that XSLT treats this string as an URL. It still thinks of it as a string.
For example, your code snippet works with the following input:
<?xml version="1.0" encoding="UTF-8"?>
<MAIN_URL>www.main-url.com/market/3/xs.htm</MAIN_URL>
Then, you could apply a stylesheet like the one below. It outputs a yes element if the contains function returns true.
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:template match="/">
<xsl:variable name="cultureRequest" select="concat('http://',MAIN_URL)" />
<xsl:if test="contains($cultureRequest, '/market/3')" >
<yes>!</yes>
</xsl:if>
</xsl:template>
</xsl:transform>
XML Output
The URL contains /market/3, so the output is:
<?xml version="1.0" encoding="UTF-8"?>
<yes>!</yes>
Note: There are two things I did not understand about your question and that I tried to ask about in the comments:
I am not sure why you concatenate the URL to http;// before handing
it to contains.
Why do you refer to "url logic"?

Use XSLT to copy XML without the xml declaration

I have the following xml and want the output to not contain the xml declaration
i.e.
FROM
<?xml version="1.0" encoding="UTF-8"?>
<tns:MFTRNS xmlns:tns="MFTRNS" recordState="New" msgVersion="13.0">
<OSCONO>100</OSCONO>
<OSINOU>1</OSINOU>
<OSDLIX>155379</OSDLIX>
<OSPANR>AAG44780</OSPANR>
<OSWHLO>AAG</OSWHLO>
</tns:MFTRNS>
TO
<tns:MFTRNS xmlns:tns="MFTRNS" recordState="New" msgVersion="13.0">
<OSCONO>100</OSCONO>
<OSINOU>1</OSINOU>
<OSDLIX>155379</OSDLIX>
<OSPANR>AAG44780</OSPANR>
<OSWHLO>AAG</OSWHLO>
</tns:MFTRNS>
Can you get an xslt to do this and if so how?
The reason for doing this is that I want to wrap the xml in an envelope which cannot be done if the declaration is a part of the XML as it does not create a valid xml file
Thanks
If you only want to remove the declaration then a stylesheet as simple as this will do it:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" omit-xml-declaration="yes" />
<xsl:template match="/">
<xsl:copy-of select="node()" />
</xsl:template>
</xsl:stylesheet>
But if your ultimate aim is to "wrap the xml in an envelope" then you might be better doing that directly in your XSLT, for example:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" />
<xsl:template match="/">
<soap:Envelope xmlns:soap="http://www.w3.org/2001/12/soap-envelope">
<xsl:copy-of select="node()" />
</soap:Envelope>
</xsl:template>
</xsl:stylesheet>
which will be safer than trying to combine the two files using non-XML-aware textual operations. For example, if your envelope declares a default namespace xmlns="http://example.com" then simply inserting the text of another XML document inside the envelope would change the semantics as it would move the non-prefixed elements like OSCONO into the envelope's default namespace when they should really be in no namespace. XSLT will spot this case and add the necessary xmlns="" overrides.
It's simple: you have to set the omit-xml-declaration attribute of your xsl:output element to yes.

Processing a GPX file with xsl (probably a namespace issue)

This question looks like a duplicate of XPath query for GPX files with namespaces?, but I must be missing something because I can't seem to get a fairly simple style sheet to work. I have this input:
<?xml version="1.0" encoding="utf-8"?>
<gpx xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0" creator="Groundspeak Pocket Query" xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd http://www.groundspeak.com/cache/1/0 http://www.groundspeak.com/cache/1/0/cache.xsd" xmlns="http://www.topografix.com/GPX/1/0">
<name>Ottawa Pocket Query</name>
<wpt lat="45.348517" lon="-75.825933">
<name>GC3HXAZ</name>
<desc>Craft maker box by FishDetective, Traditional Cache (2/2.5)</desc>
<url>http://www.geocaching.com/seek/cache_details.aspx?guid=e86ce3f5-9e75-48a6-b47e-9415101fc658</url>
<groundspeak:cache id="2893138" available="True" archived="False" xmlns:groundspeak="http://www.groundspeak.com/cache/1/0">
<groundspeak:name>Craft maker box</groundspeak:name>
<groundspeak:difficulty>2</groundspeak:difficulty>
<groundspeak:terrain>2.5</groundspeak:terrain>
</groundspeak:cache>
</wpt>
</gpx>
And a stylesheet that looks like this:
<?xml version="1.0"?>
<!-- -->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:groundspeak="http://www.groundspeak.com/cache/1/0"
>
<xsl:output method="html"/>
<xsl:template match="/">
Cache names:
<xsl:apply-templates select="//wpt">
</xsl:apply-templates>
</xsl:template>
<xsl:template match="wpt">
<li><xsl:value-of select="groundspeak:cache/groundspeak:name"/></li>
</xsl:template>
</xsl:stylesheet>
And what I would expect is a list with one element on it, "Craft Maker Box", but what I get is an empty list.
What am I missing?
The default namespace is http://www.topografix.com/GPX/1/0. You should add that and use a prefix to match wpt.
Something like this: (untested)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:groundspeak="http://www.groundspeak.com/cache/1/0"
xmlns:gpx="http://www.topografix.com/GPX/1/0"
>
<xsl:output method="html"/>
<xsl:template match="/">
Cache names:
<xsl:apply-templates select="//gpx:wpt">
</xsl:apply-templates>
</xsl:template>
<xsl:template match="gpx:wpt">
<li><xsl:value-of select="groundspeak:cache/groundspeak:name"/></li>
</xsl:template>
</xsl:stylesheet>
It is indeed a namespace issue. You have
xmlns="http://www.topografix.com/GPX/1/0"
in the XML so unprefixed element names are in this namespace. You need to bind the same uri to a prefix in your stylesheet, e.g.
xmlns:g="http://www.topografix.com/GPX/1/0"
and then use g:wpt in the match and select expressions.

XSLT not matching element - namespace declarations

I'm sure this is a very simple fix, but I'm stumped. I've got input XML with the following root element, and repeating child elements:
<modsCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.loc.gov/mods/v3"
xsi:schemaLocation="
http://www.loc.gov/mods/v3
http://www.loc.gov/standards/mods/v3/mods-3-4.xsd">
<mods version="3.4">
...
I've got an XSLT sheet with the following to match each <mods> node, and kick it out as a separate file named by an <identifier type="local"> element.
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.loc.gov/mods/v3">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/modsCollection">
<xsl:for-each select="mods">
<xsl:variable name="filename"
select="concat(normalize-space(
identifier[#type='local']),
'.xml')" />
<xsl:result-document href="{$filename}">
<xsl:copy-of select="."></xsl:copy-of>
</xsl:result-document>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
This works if the XML input does not have the xmlns:xsi, xmlns, or xsi:schemaLoaction attributes in the root element. So, for example, it works on the following:
<modsCollection>
<mods version="3.4">
...
I know that some of our MODS files have had the prefix included but I'm unclear why this won't work without the prefix if our XSLT matching is not looking for the prefix. Any thoughts or advice would be greatly appreciated.
<xsl:template match="/modsCollection">
matches modsCollection in no namespace. You want
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.loc.gov/mods/v3"
xmlns:m="http://www.loc.gov/mods/v3">
then
<xsl:template match="/m:modsCollection">
To match modsCollection in the mods namespace, and similarly use the m: prefix in all xslt patterns and xpath expressions in the stylesheet.