Loop logic for creating a formatted particular xml format - java

How do I construct the for loop for this below logic in Java. I have a list of items that i need to end that with the KT1 tag for every KT2 item. Please let me know what is the for loop logic to apply
This is the format that i have as
Input
<KT1>
<KT2>
String1
</KT2>
<KT2>
String2
</KT2>
.
.
.
</KT1>
Expected output
<KT1>
<KT2>
String1
</KT2>
</KT1>
<KT1>
<KT2>
String2
</KT2>
</KT1>
.
.
.
</KT1>

OK, first up, to use XSLT with Java, see the answer to this question:
java xslt tutorial
The XSLT you would need is as follows (disclaimer: I'm learning XSLT myself so, if anyone has any better solutions, please post them and comment here!)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- Template 1 (see below) -->
<xsl:template match="/">
<xsl:element name="Root">
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<!-- Template 2 (see below) -->
<xsl:template match="KT2">
<xsl:element name="KT1">
<xsl:copy>
<xsl:apply-templates select="text()" />
</xsl:copy>
</xsl:element>
</xsl:template>
<!-- Template 3 (see below) -->
<xsl:template match="text()">
<xsl:copy />
</xsl:template>
</xsl:stylesheet>
The results of applying this XSLT to your input XML are as follows:
<?xml version="1.0" encoding="UTF-8"?>
<Root>
<KT1>
<KT2>String1</KT2>
</KT1>
<KT1>
<KT2>String2</KT2>
</KT1>
.
.
.
</Root>
My understanding of the way XSLT works is that it will apply templates in order of specificity. A dry run through the XLST above would be as follows:
1) The root of the XML document matches exactly to Template 1 - this tells it to output a root node and then carry on processing other elements
2) The first KT1 element doesn't match to any templates, so is not output
3) The first KT2 child of the first KT1 element matches to Template 2 - this tell it output a KT1 node, and then copy the found KT2 node and then to continue processing
4) The text of the KT2 node matches to Template 3, which just copies it out
5) The second KT2 child of the first KT1 element matches to Template 2 - and so on
The XSLT above is quite specific, it can easily be made more general - google "XSLT Identity Transforms" for a good start.

Related

XSLT check if String is inside a set

I am trying to check if a String is contained within a set. I have an Excel sheet that I convert to an xml file; example:
Excel sheet on left and converted sheet on right (RowData.xml):
So I have an xml file where those set of numbers may or may not be there. For example, the source xml may look like this:
Source.xml:
<Data>
<Number>5556781234</Number>
<Number>5556781235</Number>
<Number>5556781236</Number>
</Data>
As you see it can stop anywhere. The source xml file may have all the numbers listed in RowData.xml or it may have only 1 or more. So my question is, how would I check for that in my xslt file?
I want to do this:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- This is the Excel sheet converted to an XML file -->
<xsl:param name="sheet-uri" select="'RowData.xml'"/>
<xsl:param name="sheet-doc" select="document($sheet-uri)"/>
<xsl:template match="Data">
<xsl:for-each select="Data/Number">
<xsl:variable name="continue" select="$sheet-doc//Sheet/Row[Number = current()]/Continue"/>
<xsl:if test="">
<!-- Check the Source.xml against the RowData.xml and
see if the set contains any "No"'s in it. -->
<!-- If it does then don't do the following -->
<Data2>
<Number><xsl:value-of select="Number"/></Number>
<Timestamp>125222</Timestamp>
</Data2>
</xsl:if>
</xsl:for-each>
</xsl:template>
So basically, before making the <Data2> element, check the numbers in Source.xml and see if any of those numbers have a value of No for the column Continue in RowData.xml. I don't know how to make the if statement above. I know there's a contains() function in xslt; however, I don't know how I can use it here.
Is this possible? Please let me know if anything was confusing. Thanks in advance!
check the numbers in Source.xml and see if any of those numbers have a value of No for the column Continue in RowData.xml.
You can take advantage of XSLT's "existential equal" operator here:
test="doc('source.xml')/Data/Number =
$sheet-doc//Sheet/Row[Continue='No']/Number"
Essentially, if A and B are sets of values, then A = B returns true if some value in A is equal to some value in B.
I would suggest you use the key mechanism - esp. if you're using XSLT 2.0.
Define a key as:
<xsl:key name="row" match="Row" use="Number" />
then do:
<xsl:template match="/Data">
<xsl:for-each select="Number[not(key('row', ., $sheet-doc))]">
<Data2>
<xsl:copy-of select="."/>
<Timestamp>125222</Timestamp>
</Data2>
</xsl:for-each>
</xsl:template>
This selects only Numberelements that do not have a corresponding Row in the RowData.xml document.

How to get last 5 elements from an xml file using java

I have a XML file in the following format:
<CodeSnippet>
<Code tag="class eclipse">
<Snippet>sample</Snippet>
<TimeStamp>05/11/2014 13:50:36</TimeStamp>
</Code>
<Code tag="button java">
<Snippet>Sample code</Snippet>
<TimeStamp>05/11/2014 12:36:36</TimeStamp>
</Code>
.
.
.
</CodeSnippet>
I would like to know, how to retrieve the last 5 child nodes of the root node "CodeSnippet" and display the text inside the tag "Snippet".
Get a learning of using XPATH expressions in java here http://howtodoinjava.com/2012/11/07/how-to-work-with-xpaths-in-java-with-examples/
For your specific problem get all the snippet nodes using xpath expression //CodeSnippet/Snippet. Once done run a for loop from nodes.length to nodes.length-5.
Try something like:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:template match="/">
<root>
<xsl:for-each select="CodeSnippet/Code[position() > last() - 5]">
<xsl:copy-of select="Snippet"/>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
You didn't say what the exact format of output should be. The above will result in an XML document in the form of:
<?xml version="1.0" encoding="utf-8"?>
<root>
<Snippet>V</Snippet>
<Snippet>W</Snippet>
<Snippet>X</Snippet>
<Snippet>Y</Snippet>
<Snippet>Z</Snippet>
</root>
This is a good tutorial of XML Parsing for Java using three different technologies. In your case I would recommend you the DOM option, even if I see SAX the easiest one. Just try to make the changes you need for your case.
http://www.javacodegeeks.com/2013/05/parsing-xml-using-dom-sax-and-stax-parser-in-java.html
Have a nice code!
If you can use an XPath 2.0 processor, you could use the following XPath:
subsequence(//Snippet,count(//Snippet)-5)

XSLT: extract the last x digit of a sibling node with xpath expression

I am trying to extract the last 4 numbers of the "red" sibling with xpath.
The source xml looks like:
...
<node2>
<key><![CDATA[RED]]></key>
<value><![CDATA[98472978241908]]></value>
... more key value pairs here...
</node2>
...
And when I use the follwing xpath:
/nodelevelX/nodelevelY/node2/key[text()='RED']/following-sibling::value
I have the full number in output, then I tried to extract the digit with this xpath experssion:
/nodelevelX/nodelevelY/node2/key[text()='RED']/following-sibling::value/text()[substring(., string-length(.)-4)]
I still have the full number. The substring function does not seems to work.
my xsl header is:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
I think there is a small error, but I cannot see where. I followed many discussions on SO and others (w3schools) and tried to follow the advices whithout success.
UPDATE: The context:
I use the following identity which copy all the nodes from my source XML to the destination (xml)
and I apply specific rules for some node after inside a xsl:template:
<!-- This copy the whole source XML in destination -->
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*" />
</xsl:copy>
</xsl:template>
<!-- specific rules for some nodes -->
<xsl:template match="/nodeDetails">
<mynewnode>
<!-- here I take the whole value and it s working -->
<someVal><xsl:value-of select="/nodeDetails/nodeX/key[text()='ANOTHER_KEY']/following-sibling::value" /></someVal>
<!-- FIXME substring does not work now -->
<redVal><xsl:value-of select="/nodeDetails/nodeX/key[text()='RED']/following-sibling::value/text()[substring(.,string-length(.)-4)]" /></redVal>
</mynewnode>
</xsl:template>
And for the transformation I use the following code from a junit class in Java (JDK 6):
#Test
public void transformXml() throws TransformerException {
TransformerFactory factory = TransformerFactory.newInstance();
Source xslt = new StreamSource(getClass().getResourceAsStream("contract.xsl"));
Transformer transformer = factory.newTransformer(xslt);
Source input = new StreamSource(getClass().getResourceAsStream("source.xml"));
Writer output = new StringWriter();
transformer.transform(input, new StreamResult(output));
System.out.println("output=" + output.toString());
}
Your current XPath will evaluate to a nodeset, but what you need is a string. Please try something like this:
<xsl:variable name="value"
select="/nodelevelX/nodelevelY/node2/key[. = 'RED']
/following-sibling::value[1]" />
<xsl:value-of select="substring($value, string-length($value) - 3)" />
Though to be sure about an answer, I'd need to see the portion of your XSLT where you are trying to output this value.
Use this XPath 2.0 expression:
/nodelevelX/nodelevelY/node2/key[text()='RED']
/following-sibling::*[1][self::value]
/substring(., string-length() -3)
XSLT 2.0 - based verification:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:copy-of select=
"/nodelevelX/nodelevelY/node2/key[text()='RED']
/following-sibling::*[1][self::value]
/substring(., string-length() -3)"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following XML document:
<nodelevelX>
<nodelevelY>
<node2>
<key>GREEN</key>
<value>0123456789</value>
<key>RED</key>
<value>98472978241908</value>
<key>BLACK</key>
<value>987654321</value>
</node2>
</nodelevelY>
</nodelevelX>
the XPath expression is evaluated and the result of this evaluation is copied to the output:
1908

Removing elements from an XML document, XSLT and JAXB

This question is a follow up to my earlier question:
Creating a valid XSD that is open using <all> and <any> elements
Given that I have a Java String containing an XML document of the following form:
<TRADE>
<TIME>12:12</TIME>
<MJELLO>12345</MJELLO>
<OPTIONAL>12:12</OPTIONAL>
<DATE>25-10-2011</DATE>
<HELLO>hello should be ignored</HELLO>
</TRADE>
How can I use XSLT or similar (in Java by using JAXB) to remove all elements not contained in a set of elements.
In the above example I am only interested in (TIME, OPTIONAL, DATE), so I would like to transform it into:
<TRADE>
<TIME>12:12</TIME>
<OPTIONAL>12:12</OPTIONAL>
<DATE>25-10-2011</DATE>
</TRADE>
The order of the elements is not fixed.
This transformation:
<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="pNames" select="'|TIME|OPTIONAL|DATE|'"/>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*/*">
<xsl:if test="contains($pNames, concat('|', name(), '|'))">
<xsl:call-template name="identity"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<TRADE>
<TIME>12:12</TIME>
<MJELLO>12345</MJELLO>
<OPTIONAL>12:12</OPTIONAL>
<DATE>25-10-2011</DATE>
<HELLO>hello should be ignored</HELLO>
</TRADE>
produces the wanted, correct result:
<TRADE>
<TIME>12:12</TIME>
<OPTIONAL>12:12</OPTIONAL>
<DATE>25-10-2011</DATE>
</TRADE>
Explanation:
The identity rule (template) copies every node "as-is".
The identity rule is overridden by a template matching any element that is not the top element of the document. Inside the template a check is made if the name of the matched element is one of the names specified in the external parameter $pNames in a pipe-delimited string of wanted names.
See the documentation of your XSLT processor on how to pass a parameter to a transformation -- this is implementation-dependent and differs from processor to processor.
I haven't tried yet, but maybe the javax.xml.tranform package can help:
http://download.oracle.com/javase/6/docs/api/javax/xml/transform/package-summary.html
JAXB & XSLT
JAXB integrates very cleanly with XSLT for an example see:
How to get jaxb to Ignore certain data during unmarshalling
Your Other Question
Based on your previous question (see link below), the transform is really unnecessary as JAXB will just ignore attributes and elements that are not mapped to fields/properties in your domain object.
Creating a valid XSD that is open using <all> and <any> elements

Remove special characters from XML via XSLT only for specific tags

I am having a certian issue with special characters in my XML.
Bascially I am splitting up an xml into multiple xmls using Xalan Processor.
When splitting the documents up I am using their value of the name tag as the name of the file generated. The problem is that the name contains characters that arent recognized by the XML processor like ™ (TM) and ® (R). I want to remove those characters ONLY when naming the files.
<xsl:template match="products">
<redirect:write select="concat('..\\xml\\product\\en\\',translate(string(name),'</> ',''),'.xml')">
The above is the XSL code I have writter to split the XML into multlpe XMLs. As you can see I am using hte translate method to subtitute '/','<','>' with '' from the name. I was hoping I could do the same with ™ (TM) and ® (R) but it doesnt seem to work.
Please advice me how I would be able to do that.
Thanks for you help in advance.
I don't have Xalan, but with 8 other XSLT processors this thransformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="text()">
<xsl:value-of select="translate(., '</>™®', '')"/>
===================
<xsl:value-of select="translate(., '</>™®', '')"/>
</xsl:template>
</xsl:stylesheet>
when applied on this XML document:
<t>XXX™ My Trademark®</t>
produces the wanted result:
XXX My Trademark
===================
XXX My Trademark
I suggest that you try to use one of the two expressions above -- at least the second may work successfully.
Following Dimitre answer, I think that if you are not sure about wich special character could be in name, maybe you should keep what you consider legal document's name characters.
As example:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="text()">
<xsl:value-of select="translate(.,
translate(.,
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ',
''),
'')"/>
</xsl:template>
</xsl:stylesheet>
With input:
<t>XXX™ My > Trademark®</t>
Result:
XXX My Trademark

Categories