Moving elements of one XML node to a child node using xslt - java

Basically I need to move the contents of OrderRelease and Order into the OrderLine level ussing xsl.
Example input XML:
<OrderRelease EnterpriseCode="BRD" ReleaseNo="1234ABC" DocumentType="0001" SellerOrganizationCode="BU1" ShipNode="US1">
<Order OrderDate="2019-06-13T09:27:36-04:00" Action="CANCEL" OrderNo="1234ABC">
<Extn ExtnWMSOrderNumber="123ADS"/>
</Order>
<OrderLines>
<OrderLine Action="CANCEL" PrimeLineNo="1" SubLineNo="1" OrderedQty="5">
<Item ItemID="A" UnitOfMeasure="STD" ProductClass="NEW"/>
</OrderLine>
<OrderLine Action="" PrimeLineNo="2" SubLineNo="1" OrderedQty="10">
<Item ItemID="B" UnitOfMeasure="STD" ProductClass="NEW"/>
</OrderLine>
<OrderLine Action="CANCEL" PrimeLineNo="3" SubLineNo="1" OrderedQty="0">
<Item ItemID="C" UnitOfMeasure="STD" ProductClass="NEW"/>
</OrderLine>
</OrderLines>
</OrderRelease>
Example of XML post translation:
<OrderLines>
<OrderLine Action="CANCEL" PrimeLineNo="1" SubLineNo="1" OrderedQty="5" OrderDate="2019-06-13T09:27:36-04:00" OrderNo="1234ABC" EnterpriseCode="BRD" ReleaseNo="1234ABC" DocumentType="0001" SellerOrganizationCode="BU1" ShipNode="US1" AggregatorOrderId=”12345 >
<Item ItemID="A" UnitOfMeasure="STD" ProductClass="NEW"/>
</OrderLine>
<OrderLine Action="" PrimeLineNo="1" SubLineNo="1" OrderedQty="10" OrderDate="2019-06-13T09:27:36-04:00" OrderNo="1234ABC" EnterpriseCode="BRD" ReleaseNo="1234ABC" DocumentType="0001" SellerOrganizationCode="BU1" ShipNode="US1" AggregatorOrderId=”12345>
<Item ItemID="B" UnitOfMeasure="STD" ProductClass="NEW"/>
</OrderLine>
<OrderLine Action="CANCEL" PrimeLineNo="1" SubLineNo="1" OrderedQty="0" OrderDate="2019-06-13T09:27:36-04:00" OrderNo="1234ABC" EnterpriseCode="BRD" ReleaseNo="1234ABC" DocumentType="0001" SellerOrganizationCode="BU1" ShipNode="US1 AggregatorOrderId=”12345">
<Item ItemID="C" UnitOfMeasure="STD" ProductClass="NEW"/>
</OrderLine>
</OrderLines>
I used the following XSL but it removes all of the original OrderLine elements and also doesn't seem to do it for each OrderLine
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="OrderRelease">
<OrderLine>
<xsl:copy-of select="#*|OrderLine/#*" />
<xsl:apply-templates /><!-- optional -->
</OrderLine>
</xsl:template>
<xsl:template match="Order">
<xsl:copy-of select="#*|OrderLine/#*" />
<xsl:apply-templates /><!-- optional -->
</xsl:template>
<xsl:template match="Extn">
<xsl:copy-of select="#*|OrderLine/#*" />
<xsl:apply-templates /><!-- optional -->
</xsl:template>
<xsl:template match="OrderLine">
<xsl:copy-of select="#*|OrderLine/#*" />
</xsl:template>
</xsl:stylesheet>
Output of this is
<OrderLine EnterpriseCode="BRD"
ReleaseNo="1234ABC"
DocumentType="0001"
SellerOrganizationCode="BU1"
ShipNode="US1"
OrderDate="2019-06-13T09:27:36-04:00"
Action="CANCEL"
OrderNo="1234ABC"
ExtnWMSOrderNumber="123ADS"
PrimeLineNo="3"
SubLineNo="1"
OrderedQty="0"/>

You could do it this way.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="OrderLines">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="OrderLine">
<xsl:copy>
<xsl:copy-of select="#*"/> <!-- OrderLine attributes -->
<xsl:copy-of select="../../#*"/> <!-- OrderRelease attributes -->
<xsl:copy-of select="../../Order/#*"/> <!-- Order attributes -->
<xsl:attribute name="AggregatorOrderId">12345</xsl:attribute>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="Item">
<xsl:copy>
<xsl:copy-of select="#*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
See it working here : https://xsltfiddle.liberty-development.net/93nwMog

Related

Unable to fetch the specific entire XML tag by using XSLT

I have the below sample XML file
Sample XML:
<?xml version="1.0" encoding="UTF-8"?>
<testng-results skipped="0" failed="0" total="10" passed="10">
<class name="com.transfermoney.Transfer">
<test-method status="PASS" name="setParameter" is-config="true" duration-ms="4"
started-at="2018-08-16T21:43:38Z" finished-at="2018-08-16T21:43:38Z">
<params>
<param index="0">
<value>
<![CDATA[org.testng.TestRunner#31c2affc]]>
</value>
</param>
</params>
<reporter-output>
</reporter-output>
</test-method> <!-- setParameter -->
</class>
<class name="com.transfermoney.Transfer">
<test-method status="FAIL" name="setSettlementFlag" is-config="true" duration-ms="5"
started-at="2018-08-16T21:44:55Z" finished-at="2018-08-16T21:44:55Z">
<reporter-output>
<line>
<![CDATA[runSettlement Value Set :false]]>
</line>
</reporter-output>
</test-method> setSettlementFlag
</class>
</testng-results>
I just want to take the below piece of tags from above XML file based on status PASS (I don't want to take <?XML version, <testng-results> and class tags those are should be ignored).
Expected Output:
<test-method status="PASS" name="setParameter" is-config="true" duration-ms="4"
started-at="2018-08-16T21:43:38Z" finished-at="2018-08-16T21:43:38Z">
<params>
<param index="0">
<value>
<![CDATA[org.testng.TestRunner#31c2affc]]>
</value>
</param>
</params>
<reporter-output>
</reporter-output>
</test-method>
I just used below XSLT to get the above output from sample XML file but It doesn't work It returned all the tags but I just want the above output not other than anything.
XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" method="xml"/>"
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="class"/>
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
<xsl:for-each select="test-method[#status='PASS']">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:stylesheet>
Also Using the below java code to run the XSLT and sample XML file
Code:
String XML = fetchDataFrmXML(".//Test//testng-results_2.xml");
Transformer t = TransformerFactory.newInstance().newTransformer(new StreamSource(new StringReader(XSL)));
t.transform(new StreamSource(new StringReader(XML)), new StreamResult(new File(".//Test//Sample1.xml")));
This is the sample payload. But the actual payload had multiple nodes with "PASS" and "Failed" status. I'm just only interested to fetch the PASS node in the above output format.
Any leads....
The result you show could be obtained quite simply by doing just:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/testng-results">
<xsl:copy-of select="class/test-method[#status='PASS']" />
</xsl:template>
</xsl:stylesheet>
However, in case of more than one test-method having a status of "PASS" this will result in an XML fragment with no single root element. So you'd probably be better off doing:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/testng-results">
<root>
<xsl:copy-of select="class/test-method[#status='PASS']" />
</root>
</xsl:template>
</xsl:stylesheet>

How to Read Java List Object in XSL

I am having a Java Object which is ArrayList and i have added it in response of service which is in XML format as below,
<root>
<SubRoot>
<type>A</type>
<mand>Y</mand>
<Section>B</Section>
</SubRoot>
<SubRoot>
<type>A</type>
<mand>Y</mand>
<Section>A</Section>
</SubRoot>
</root>
I am trying to use the some value from above xml for condition.On basis of true condition i will call some template for my purpose.
I am calling my xsl template as below,
<xsl:choose>
<xsl:when test="(/root/SubRoot[Section = 'A'])">
//Call some template
</xsl:when>
<xsl:otherwise>
//some template
</xsl:otherwise>
</xsl:choose>
<xsl:choose>
<xsl:when test="(/root/SubRoot[Section = 'B'])">
//call some template
</xsl:when>
<xsl:otherwise>
//some template
</xsl:otherwise>
</xsl:choose>
Each time my first when will get executed its not coming inside the second when condition.
Could you please help me how the both condition will get executed?is it right approach in terms of performance?
Any Suggestion approach must be appreciated.
Try this,
XSL
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns1="http://locomotive/bypass/docx">
<xsl:output method="xml" version="1.0" encoding="UTF-8"
indent="yes" />
<xsl:strip-space elements="*" />
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="SubRoot">
<xsl:choose>
<xsl:when test="Section = 'A'">
<!-- your logic for A -->
<A></A>
</xsl:when>
<xsl:otherwise>
<!-- your logic for B -->
<B></B>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
XML
<root>
<SubRoot>
<type>A</type>
<mand>Y</mand>
<Section>B</Section>
</SubRoot>
<SubRoot>
<type>A</type>
<mand>Y</mand>
<Section>A</Section>
</SubRoot>
Demo link : http://xsltransform.net/ejivdHb/15

XSLT Apply Template for Specific list of value

I am beginner in XSLT.
My Source XML is as below
<Options>
<Option>
<Data>Data1</Data>
<Type>A</Type>
</Option>
<Option>
<Data>Data2</Data>
<Type>B</Type>
</Option>
<Option>
<Data>Data3</Data>
<Type>C</Type>
</Option>
<Option>
<Data>Data4</Data>
<Type>D</Type>
</Option>
...
</Options>
I have parameter which is used to filter the result from above method and it is as below
<xsl:param name="filterType" select="'A,C'"/>
The output should be as below:
<Result>
<Data Type="A">Data1<Data>
<Data Type="C">Data3<Data>
</Result>
Below is the XSLT i have created:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:param name="filterType" select="'A,C'"/>
<xsl:template match="Options">
<xsl:element name="Result">
<xsl:apply-templates select="Option"/>
</xsl:element>
</xsl:template?
<xsl:template match="Option">
<xsl:element name="Data">
<xsl:attribute name="Type">
<xsl:value-of select="Type"/>
</xsl:attribute>
<xsl:value-of select="Data"/>
</xsl:element>
</xsl:template?
</xsl:stylesheet>
While applying template for 'Option' tag i need to use filterType.
How can i do that? Please Help.
I think you simply want
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:param name="filterType" select="'A,C'"/>
<xsl:variable name="filter" select="concat(',', $filterType, ',')"/>
<xsl:template match="Options">
<Result>
<xsl:apply-templates select="Option[contains($filter, concat(',', Type, ','))]"/>
</Result>
</xsl:template>
<xsl:template match="Option">
<Data Type="{Type}">
<xsl:value-of select="Data"/>
</Data>
</xsl:template>
</xsl:stylesheet>

XSLT skip duplicate element

I am a beginner to XSLT.
My Source XML is as below:
<Passengers>
<Passenger type="A" id="P1"/>
<Passenger type="A" id="P2"/>
<Passenger type="B" id="P3"/>
<Passenger type="C" id="P4"/>
</Passengers>
The out-put should be as below:
<Pax_Items>
<Item>
<Type>A</Type>
<Count>2</Count>
</Item>
<Item>
<Type>B</Type>
<Count>1</Count>
</Item>
<Item>
<Type>C</Type>
<Count>1</Count>
</Item>
</Pax_Items>
I have created XSLT as below
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0" exclude-result-prefixes="xmlns">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
<xsl:variable name="filter" select="'TK,AJ'"/>
<xsl:template match="Passengers">
<xsl:element name="Pax_Items">
<xsl:apply-templates select="Passenger"/>
</xsl:element>
</xsl:template>
<xsl:template match="Passenger">
<xsl:element name="Item">
<xsl:element name="Type">
<xsl:value-of select="#type"/>
</xsl:element>
<xsl:element name="Count">
<xsl:value-of select="count(//Passenger[#type=current()/#type])"/>
</xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
With above XSLT i got the below output:
<Pax_Items>
<Item>
<Type>A</Type>
<Count>2</Count>
</Item>
<Item>
<Type>A</Type>
<Count>2</Count>
</Item>
<Item>
<Type>B</Type>
<Count>1</Count>
</Item>
<Item>
<Type>C</Type>
<Count>1</Count>
</Item>
</Pax_Items>
How can i omit or skip the duplicate element? Please help.
This is actually a good example of a grouping problem. In XSLT1.0, the most efficient way to do grouping is with a technique called "Muenchian Grouping", so it might be worthwhile learning about this.
In this case, you want to group Passenger elements by their #type attribute, so you would define a key to do this
<xsl:key name="Passengers" match="Passenger" use="#type"/>
Then, you need to select the Passenger elements which happen to be the first occurence of that element in the group for their #type attribute. This is done as follows:
<xsl:apply-templates
select="Passenger[generate-id() = generate-id(key('Passengers', #type)[1])]"/>
Note the use of generate-id which generates a unique ID for a node, allowing two nodes to be compared.
Then, to count the number of occurences in the group, it is straight-forward
<xsl:value-of select="count(key('Passengers', #type))"/>
Here is the full XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:key name="Passengers" match="Passenger" use="#type"/>
<xsl:template match="Passengers">
<Pax_Items>
<xsl:apply-templates select="Passenger[generate-id() = generate-id(key('Passengers', #type)[1])]"/>
</Pax_Items>
</xsl:template>
<xsl:template match="Passenger">
<Item>
<Type>
<xsl:value-of select="#type"/>
</Type>
<Count>
<xsl:value-of select="count(key('Passengers', #type))"/>
</Count>
</Item>
</xsl:template>
</xsl:stylesheet>
When applied to your sample XML, the following is output
<Pax_Items>
<Item>
<Type>A</Type>
<Count>2</Count>
</Item>
<Item>
<Type>B</Type>
<Count>1</Count>
</Item>
<Item>
<Type>C</Type>
<Count>1</Count>
</Item>
</Pax_Items>
Also note there is no real reason to use xsl:element to output static elements. Just write out the element directly.
Update your passenger template as follows; I have added if condition to check duplicate nodes,
<xsl:template match="Passenger">
<xsl:if test="not(preceding-sibling::Passenger[#type = current()/#type])">
<xsl:element name="Item">
<xsl:element name="Type">
<xsl:value-of select="#type"/>
</xsl:element>
<xsl:element name="Count">
<xsl:value-of select="count(//Passenger[#type=current()/#type])"/>
</xsl:element>
</xsl:element>
</xsl:if>
</xsl:template>

Generic XML sorting via XSLT applied by Java?

Basically, I want an exact copy of the XML, except I'd like to sort certain nodes by their ID attribute. Not all elements with the ID attribute should be sorted.
I've kludged together a working stylesheet, but it requires me to hard-code the <xsl:apply-template>'s in for all sibling nodes to the nodes I'm sorting on. I've no formal schema, and I can't be certain that it won't change. I'd like to be able to create a more generic stylesheet which will output the non-sorted nodes without these explicit calls.
Is this possible?
My Java code to transform the XML:
String input = "C:\\presort.xml";
String output = "C:\\postsort.xml";
String xsl = "C:\\sort.xsl"
Document dom;
try
{
dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(input);
Element e = dom.getDocumentElement();
Transformer transform = TransformerFactory.newInstance().newTransformer(
new StreamSource( new File(xsl) ));
StreamResult result = new StreamResult(new File(output));
transform.transform(new DOMSource(dom), result);
}
catch (Exception e)
{
e.printStackTrace();
}
My XML:
<?xml version="1.0" encoding="utf-8"?>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Person id="1">
<Base>true</Base>
<Description>something</Description>
<Name>Vin Vinigar</Name>
<Privileges>
<Privilige id="340">
<Permission>2</Permission>
<Job id="dontsort" />
</Privilige>
<Privilige id="11">
<Permission>3</Permission>
<Job id="dontsort" />
</Privilige>
<Privilige id="1011">
<Permission>1</Permission>
<Job id="2342" />
</Privilige>
</Privileges>
</Person>
<Person id="f32">
<Base>true</Base>
<Description>Here be dragons</Description>
<Name>John Doe</Name>
<Privileges>
<Privilige id="23a">
<Permission>2</Permission>
<Job id="a2a" />
</Privilige>
</Privileges>
</Person>
<Person id="22">
<PossibleUnknownTagHere>something</PossibleUnknownTagHere>
<Name>Han Solo</Name>
<Privileges>
<Privilige id="23a">
<Permission>3</Permission>
<Job id="a2a" />
</Privilige>
</Privileges>
</Person>
</root>
My stylesheet:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="root|#*">
<xsl:copy >
<xsl:apply-templates select="#*" />
<xsl:apply-templates select="Person">
<xsl:sort select="#id" order="ascending" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="Person">
<xsl:copy >
<xsl:apply-templates select="#*" />
<!-- Works but requies a template for each sibling element -->
<xsl:apply-templates select="Base" />
<xsl:apply-templates select="Description" />
<xsl:apply-templates select="Name" />
<!-- I'd like to do something like this -->
<!--
<xsl:choose>
<xsl:when test="not(Privileges)">
<xsl:apply-templates select="." />
</xsl:when>
</xsl:choose>
-->
<xsl:apply-templates select="Privileges" />
</xsl:copy>
</xsl:template>
<!-- I'd like to remove the need for these 3 explicity copies -->
<xsl:template match="Base">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="Description">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="Name">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="Privileges">
<xsl:copy >
<xsl:apply-templates select="#*" />
<xsl:apply-templates select="Privilege">
<xsl:sort select="#id" order="ascending" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="Privilege">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates />
</xsl:copy>
<xsl:template>
<xsl:template match="root">
<xsl:copy>
<xsl:apply-templates select="#*" />
<xsl:apply-templates select="Person">
<xsl:sort select="#id" order="ascending" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="Privileges">
<xsl:copy>
<xsl:apply-templates select="#*" />
<xsl:apply-templates select="Privilege">
<xsl:sort select="#id" order="ascending" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>

Categories

Resources