I am writing text output files reading an XML file using XSL.
Here i am trying to check weather a particular content is available in the source XML and write that content to a file if available.
But if the content is not available ( not fulfilling "<XSL:if>" condition), then output file would be an empty file.
So I want to add an else condition and in that else condition to avoid XSL output file being created at runtime.
Any body having any clue?
<xsl:message terminate="yes"> wont help because it does generate the output but only terminating the further processing of XSL.
Can any body help or even suggest any other approach to be taken in java code even without deleting files after they have created. [By reading them and identifying empty files]
Currently I am using java to read the created empty files and delete them explicitly. Thanks in adavance.
I will give two examples how this can be done -- the second is what I recommend:
Suppose we have this XML document:
<nums>
<num>01</num>
<num>02</num>
<num>03</num>
<num>04</num>
<num>05</num>
<num>06</num>
<num>07</num>
<num>08</num>
<num>09</num>
<num>10</num>
</nums>
and we want to produce another one from it, in which the num elements with even numbers are "deleted".
One way of doing this is:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<nums>
<xsl:apply-templates/>
</nums>
</xsl:template>
<xsl:template match="num">
<xsl:choose>
<xsl:when test=". mod 2 = 1">
<num><xsl:value-of select="."/></num>
</xsl:when>
<!-- <xsl:otherwise/> -->
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
The wanted result is produced:
<nums>
<num>01</num>
<num>03</num>
<num>05</num>
<num>07</num>
<num>09</num>
</nums>
Do notice: For "not doing anything" you even don't need the <xsl:otherwise> and it is commented out.
A better solution:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="num[. mod 2 = 0]"/>
</xsl:stylesheet>
This produces the same correct result.
Here we are overriding the identity rule with a template matching num elements with even value and with empty body -- which does the "delete".
Do notice:
Here we don't use any "if-then-else" explicit instructions at all -- just Xtemplate pattern matching, which is the most distinguishing feature of XSLT.
Related
I'm trying to find an Xpath from exchange body and add a value to the tag.
<root>
<details1>
<Name>Ying</Name>
<status></status>
</details1>
<details2>
<Name>Ying</Name>
<status></status>
</details2>
</root>
I want to find xPath=root/details2/status From this exchange body and add value to the status. Since there are two occurances of status in the body String I will not be able to use String1.replace('<status></status>',<status>no</status>) Is there any way to use camel Xpath to find the correct tag and add the value?
With xpath you can find and read the correct tag. But how are you going to write it?
One solution could be to use the xpath inside an xlst tranformation.
So you can add the value you want to put in the element in a camel header "myHeader" and then use it in the xslt as an xsl:param .
Assuming the xml is in the inbox folder then
from("file:inbox?noop=true")
.setHeader("myHeader",constant("no"))
.to("xslt:mytransform.xslt")
.to("file:outbox/?fileName=out.xml");
Will put the value "no" in root/details2/status.
Where mytransform.xlst inside your /src/main/resources folder is like
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="myHeader"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/root/details2/status[. = '']">
<status><xsl:value-of select="$myHeader"/></status>
</xsl:template>
</xsl:stylesheet>
Xpath /root/details2/status[. = ''] matches empty status elements
I have two .xml files which need to be joined
The first file is Song.xml as the following:
<Songs>
<Song>
<SongID>1</SongID>
<SongName>We dont talk anymore</SongName>
<Author>M-TP</Author>
<UploadBy>admin</UploadBy>
<GerneCode>1</GerneCode>
</Song>
</Songs>
and the Gerne.xml which generate from a schema
<ns2:gernes xmlns="http://www.w3.org/2001/XMLSchema/playlist" xmlns:ns2="https://xml.netbeans.org/schema/genses">
<ns2:gerne>
<GerneCode>1</GerneCode>
<GerneName>Pop</GerneName>
<Image>img-pop.jpg</Image>
</ns2:gerne>
</ns2:gerne>
I want to join these .xml files inside a XSL, to which will add the GerneName for every Song that match the GerneName inside the Gerne.xml.
The result im trying to get should be like this:
<Songs>
<Song>
<SongID>1</SongID>
<SongName>We dont talk anymore</SongName>
<Author>M-TP</Author>
<UploadBy>admin</UploadBy>
<GerneName>Pop</GerneName>
<GerneCode>1</GerneCode>
</Song>
</Songs>
Can anyone help me with this? Any e.g or keywords what should I look up for this problem?
Assuming you are limited to XSLT 1.0, you can do:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:pl="http://www.w3.org/2001/XMLSchema/playlist"
xmlns:ns2="https://xml.netbeans.org/schema/genses"
exclude-result-prefixes="pl ns2">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="lookup-path" select="'Gerne.xml'" />
<xsl:key name="genre" match="ns2:gerne" use="pl:GerneCode" />
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="GerneCode">
<xsl:variable name="code" select="." />
<!-- switch context to the other file -->
<xsl:for-each select="document($lookup-path)">
<GerneCode>
<xsl:value-of select="key('genre', $code)/pl:GerneName" />
</GerneCode>
</xsl:for-each>
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
This is assuming you are telling your XSLT processor to process the Song.xml file, and passing the path to the Gerne.xml file as a parameter.
Note that the Gerne.xml document you have posted is not well-formed XML (no </ns2:gernes> closing tag).
BTW, the correct term is "genre" - not "gerne" or "gense".
In my xslt I'd like to look up an xml file. I need to pass the path to this file from java code.I have the followings:
...
Transformer transformer = TRANSFORMER_FACTORY.newTransformer();
transformer.setParameter("mypath", "/home/user/repository");
xslt:
<?xml version="1.0"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:param name="mypath"/>
...
<xsl:template match="connection[#id]">
<xsl:variable name="lookupStore" select="document('$mypath/myfile.xml')/connections"/>
<xsl:copy>
<xsl:apply-templates select="$lookupStore">
<xsl:with-param name="current" select="."/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
...
<xsl:transform>
The problem is that I want to pass an absolute "base" path to the xsl, which I want to combine with the actual xml file name (myfile.xml). It seems to me that document considers file parameters relative to the location of the xsl.
Furthermore I remarked that the parameter is not picked up from the java code. I use JABX with the default Xalan XSLT processor (1.0)
I tried many variations of passing the parameters based on the other SO posts, but no success.
You need to construct a string then with the complete file URL: document(concat('file://', $mypath, '/myfile.xml')).
I have a number of XSD schemas with too much documentation in them which makes them hard to read and use, how can I write a program to produce the equivalent XSD files with all the xs:annotation elements (including any xs:appinfo, xs:documentation or other elements they contain) removed whenever they may be found?
You could run each of your files through an XSLT to strip out the unwanted elements:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="xs:annotation" />
</xsl:stylesheet>
As noted by #IanRoberts You only really need to remove the xs:annotation elements and the other two types of elements will be removed along with them.
I have a requirement in my code and it is as follows:
The
Parameters in my Java program are passed to an XSLT(X1) for processing. The X1 returns only one String value.
Another set of Parameters are passed to another XSLT(X2) for processing.The X2 also returns only one String value.
After Transformations the XSLT results need to be passed to another XSLT(X3) which would use the value as Variables and generate result basis the results from X1 and X2.
As far as i have been studied the XSLT variables values cannot be modified once stored. and also that these values are received as parameters when calling <xsl:apply-template name="someTemplate" > and with parameter tags.
i can implement this functionality by retrieving the result into String in Java and again Passing them as parameters to the next template. but i wanted to know if the same can be done directly via XSLT.
Please help
Edit: Xalan removed as tag
i can implement this functionality by retrieving the result into
String in Java and again Passing them as parameters to the next
template. but i wanted to know if the same can be done directly via
XSLT.
Yes, here is an XSLT 2.0 example:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pP1" select="2"/>
<xsl:param name="pP2" select="3"/>
<xsl:param name="pP3" select="5"/>
<xsl:variable name="vPass1">
<xsl:apply-templates mode="pass1">
<xsl:with-param name="pP1" select="$pP1"/>
</xsl:apply-templates>
</xsl:variable>
<xsl:variable name="vPass2">
<xsl:apply-templates mode="pass2">
<xsl:with-param name="pP2" select="$pP2"/>
</xsl:apply-templates>
</xsl:variable>
<xsl:variable name="vPass3">
<xsl:apply-templates mode="pass3">
<xsl:with-param name="pP3" select="$pP3"/>
</xsl:apply-templates>
</xsl:variable>
<xsl:template match="/">
<xsl:value-of select="$vPass1 + $vPass2 + $vPass3"/>
</xsl:template>
<xsl:template match="/*" mode="pass1">
<xsl:param name="pP1" as="xs:integer"/>
<xsl:value-of select="sum(*[. mod $pP1 = 0])"/>
</xsl:template>
<xsl:template match="/*" mode="pass2">
<xsl:param name="pP2" as="xs:integer"/>
<xsl:value-of select="sum(*[. mod $pP2 = 0])"/>
</xsl:template>
<xsl:template match="/*" mode="pass3">
<xsl:param name="pP3" as="xs:integer"/>
<xsl:value-of select="sum(*[. mod $pP3 = 0])"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following XML document:
<nums>
<num>01</num>
<num>02</num>
<num>03</num>
<num>04</num>
<num>05</num>
<num>06</num>
<num>07</num>
<num>08</num>
<num>09</num>
<num>10</num>
</nums>
three passes are performed, each with its own parameters. Each pass computes the sum of num elements, whose value is multiple of the supplied parameter. Finally, the results of the three passes are summed and returned as the final result:
63
Exactly the same transformation can be run with an XSLT 1.0 processor, with the exception that any string "as='xs:integer'" must be removed from the code.