XSLT not matching element - namespace declarations - xslt

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.

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 for-each always takes only one element

I have XML which contains multiple elements:
<?xml version="1.0" encoding="UTF-8"?>
<data>
<element>
<ip>192.168.188.101</ip>
</element>
<element>
<ip>192.168.188.100</ip>
</element>
</data>
I want to make it to this structure:
<SYNCDW>
<CIDWSet>
<CI>
<CINUM>192.168.188.101</CINUM>
</CI>
<CI>
<CINUM>192.168.188.100</CINUM>
</CI>
</CIDWSet>
</SYNCDW>
But always one element is processed, the first one, although I have for-each.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<SyncCIDW xmlns="http://www.ibm.com/maximo">
<xsl:attribute name="xsi:schemaLocation" namespace="http://www.w3.org/2001/XMLSchema-instance">http://www.ibm.com/maximo</xsl:attribute>
<CIDWSet>
<xsl:for-each select="*[local-name()='data' and namespace-uri()='']/*[local-name()='element' and namespace-uri()='']">
<CI>
<CINUM>
<xsl:value-of select="string(*[local-name()='data' and namespace-uri()='']/*[local-name()='element' and namespace-uri()='']/*[local-name()='ip' and namespace-uri()=''])"/>
</CINUM>
</CI>
</xsl:for-each>
</CIDWSet>
</SyncCIDW>
</xsl:template>
</xsl:stylesheet>
Why I am not getting processed all other elements but only the first one?
Thank you in advance for help
A couple of things:
Inside of the xsl:for-each, the context switches to the element that you are iterating over (in this case, /data/element), so to select the ip element your XPath is relative from the /data/element that you are "standing on" and would simply be ip. The way you had it, it would be looking for /data/element/data/element/ip inside of the xsl:for-each and would not produce any values inside of the <CINUM>.
You can simplify your XPath expressions. If the elements you are addressing are not bound to a namespace, rather than a generic match on any element and a predicate matching the local-name() and namespace-uri()='', just use the simplified XPath data/element.
If you are creating a statically known attribute xsi:schemaLocation with a statically known value, just use the literal declaration inside of the SyncCIDW element literal.
If you are using xsl:value-of it will yield the string value of the selected node. There is no need for the string() function.
Changes applied to your stylesheet:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<SyncCIDW xmlns="http://www.ibm.com/maximo"
xsi:schemaLocation="http://www.ibm.com/maximo">
<CIDWSet>
<xsl:for-each select="data/element">
<CI>
<CINUM>
<xsl:value-of select="ip"/>
</CINUM>
</CI>
</xsl:for-each>
</CIDWSet>
</SyncCIDW>
</xsl:template>
</xsl:stylesheet>

XSL Transform, select namespace

I'm new with XSL, but it's ok, but it's the first time I need to do something with namespace, and I'm totally out, can someone explain how to do this :
I have an XHTML like this :
<?xml version="1.0" encoding="ISO-8859-1" ?>
<?xml-stylesheet type="text/xsl" href="5C.xslt"?>
<!DOCTYPE rdf:RDF SYSTEM "http://purl.org/dc/schemas/dcmes-xml20000714.
dtd">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:dcterms="http://purl.org/dc/terms/">
<rdf:Description rdf:about="MyJPeg.jpg">
<dc:title>Find Info</dc:title>
<dc:contributor>Myself</dc:contributor>
<dcterms:created>2013-12-11</dcterms:created>
<dcterms:issued>2013-12-23</dcterms:issued>
</rdf:Description>
</rdf:RDF>
I need to validate if the issued date if = to 2013-10-10 (answer no)
My XSLT is :
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
<xsl:output method="html" version="1.0" encoding="ISO-8859-1" indent="yes"/>
<xsl:template match="*">
<html><body><pre>
<xsl:value-of select="rdf/issued"/>
<xsl:if test="xxx = '2013-10-10' ">
</xsl:if>
</pre></body>
</html>
</xsl:template>
</xsl:stylesheet>
So I try to have ther value with this line :
<xsl:value-of select="rdf/issued"/>
(to see if I got it)
And to validate with this one :
<xsl:if test="xxx = '2013-10-10' ">
But I'm new with name space and I can't find out how to get my value,
Can someone help me ?
thanks
Question #2, the solution works, but :
If I want to validate if the date is HIGHER than instead of equal, how I can do that ? (I replace = by >), and I change my date to be higher and lower, and each time it doesn't work
<xsl:if test="rdf:Description/dcterms:issued > '2001-01-01' ">
Good job
</xsl:if>
What's wrong ?
thanks
In XML, an element with a namespace if different to an element with no namespace. For example, despite having the same "local" name of "RDF" the following two elements are different.
<RDF>Test</RDF>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">Test</RDF>
To access elements within a namespace in XSLT, you first have to declare the relevant namespaces in your XSTL
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xhtml="http://www.w3.org/1999/xhtml"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:dcterms="http://purl.org/dc/terms/">
Then, where you have an xpath expression that refers to elements, you need to add in the prefix
<xsl:value-of select="rdf:Description/dcterms:issued"/>
(I took it as a typo in your question, but "issued" is a child of "Description" in your XML sample!).
Try this XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xhtml="http://www.w3.org/1999/xhtml"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:dcterms="http://purl.org/dc/terms/">
<xsl:output method="html" version="1.0" encoding="ISO-8859-1" indent="yes"/>
<xsl:template match="rdf:RDF">
<html><body><pre>
<xsl:value-of select="rdf:Description/dcterms:issued"/>
<xsl:if test="rdf:Description/dcterms:issued = '2013-10-10' ">
</xsl:if>
</pre></body>
</html>
</xsl:template>
</xsl:stylesheet>
It is worth mentioning that the namespace prefix ("rdf:" in this case), does not have to be the same in the XML as it is in the XSLT. It is the namespace URI ("http://www.w3.org/1999/02/22-rdf-syntax-ns#") that has to match.

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.