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.
Related
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>
While answering this question, it occurred to me that I know how to use the XSLT 3.0 (XPath 3.0) serialize() function, but that I do not know how to avoid serialization of namespaces that are in scope. Here is a minimal example:
XML Input
<?xml version="1.0" encoding="UTF-8" ?>
<ci:cichlids xmlns:ci="http://www.cichlids.com">
<cichlid id="1">
<name>Zeus</name>
<color>gold</color>
<teeth>molariform</teeth>
<breeding-type>lekking</breeding-type>
</cichlid>
</ci:cichlids>
XSLT 3.0 Stylesheet
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:output="http://www.w3.org/2010/xslt-xquery-serialization"
xmlns:ci="http://www.cichlids.com">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/ci:cichlids/cichlid">
<xsl:variable name="serial-params">
<output:serialization-parameters>
<output:omit-xml-declaration value="yes"/>
</output:serialization-parameters>
</xsl:variable>
<xsl:value-of select="serialize(., $serial-params/*)"/>
</xsl:template>
</xsl:stylesheet>
Actual Output
<?xml version="1.0" encoding="UTF-8"?>
<ci:cichlids xmlns:ci="http://www.cichlids.com">
<cichlid xmlns:ci="http://www.cichlids.com" id="1">
<name>Zeus</name>
<color>gold</color>
<teeth>molariform</teeth>
<breeding-type>lekking</breeding-type>
</cichlid>
</ci:cichlids>
The serialization process included the namespace declaration that is in scope for the cichlid element, although it is not used on this element. I would like to remove this declaration and make the output look like
Expected Output
<?xml version="1.0" encoding="UTF-8"?>
<ci:cichlids xmlns:ci="http://www.cichlids.com">
<cichlid id="1">
<name>Zeus</name>
<color>gold</color>
<teeth>molariform</teeth>
<breeding-type>lekking</breeding-type>
</cichlid>
</ci:cichlids>
I know how to modify the cichlid element, removing the namespaces in scope, and serialize this modified element instead. But this seems a rather cumbersome solution. My question is:
What is a canonical way to serialize an XML element using the serialize() function without also serializing unused namespace declarations that are in scope?
Testing with Saxon-EE 9.6.0.7 from within Oxygen.
Serialization will always give you a faithful representation of the data model that you are serializing. If you want to modify the data model, that's called transformation. Run a transformation to remove the unwanted namespaces, then serialize the result.
Michael Kay already gave the correct answer and I have accepted it. This is just to flesh out his comments. By
Run a transformation to remove the unwanted namespaces, then serialize the result.
he means applying a transformation like the following before calling serialize():
XSLT Stylesheet
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:output="http://www.w3.org/2010/xslt-xquery-serialization"
xmlns:ci="http://www.cichlids.com">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:variable name="cichlid-without-namespace">
<xsl:copy-of copy-namespaces="no" select="/ci:cichlids/cichlid"/>
</xsl:variable>
<xsl:template match="/ci:cichlids/cichlid">
<xsl:variable name="serial-params">
<output:serialization-parameters>
<output:omit-xml-declaration value="yes"/>
</output:serialization-parameters>
</xsl:variable>
<xsl:value-of select="serialize($cichlid-without-namespace, $serial-params/*)"/>
</xsl:template>
</xsl:stylesheet>
XML Output
<?xml version="1.0" encoding="UTF-8"?>
<ci:cichlids xmlns:ci="http://www.cichlids.com">
<cichlid id="1">
<name>Zeus</name>
<color>gold</color>
<teeth>molariform</teeth>
<breeding-type>lekking</breeding-type>
</cichlid>
</ci:cichlids>
I am trying to make a simple selection just for a test, but it doesn't seem to work.
Here is the sml: http://pastebin.com/cwEcVEiL
And here is my xslt style:
<?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:tei="http://www.tei-c.org/ns/1.0"
exclude-result-prefixes="xs"
version="2.0">
<xsl:template match="/tei:TEI/tei:text/tei:body">
TEST
</xsl:template>
</xsl:stylesheet>
With this style it just selects the entire xml document
but if I type match="/", then I outputs TEST once, as expected.
What is the problem?
For elements which aren't matched by any template at all, XSLT will apply built in default templates.
Unless you want this default behaviour, you should override these, e.g. :
<xsl:template match="/">
<xsl:apply-templates select="/tei:TEI/tei:text/tei:body" />
</xsl:template>
<xsl:template match="tei:body">
TEST
</xsl:template>
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.
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>