How to create template for display specific information using formulas. xml with xslt - xslt

I have come across related dates and am trying to find some algorithm to calculate the dependency between transfer date and exercise date. If our transfer date before exercise date it should convert to new element with value Spot. And if transfer date equal or later then exercise date , convert to new element with value Forward
<main>
<Exercise>
<transferDate>2000-08-30</transferDate>
<exerciseDate>2001-08-28</exerciseDate>
</Exercise>
<Exercise>
<transferDate>2001-08-30</transferDate>
<exerciseDate>2001-08-28</exerciseDate>
</Exercise>
<Exercise>
<transferDate>2001-08-28</transferDate>
<exerciseDate>2001-08-28</exerciseDate>
</Exercise>
</main>
I'm expecting next .xml using xslt
<?xml version="1.0" encoding="UTF-8"?>
<Type>Spot</Type>
<Type>Forward</Type>
<Type>Forward</Type>`

You could use something like this :
<?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"
exclude-result-prefixes="#all"
version="3.0">
<xsl:output method="xml" indent="yes"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="Exercise">
<xsl:copy>
<xsl:apply-templates/>
<xsl:variable name="tDate" select="xs:date(transferDate)"/>
<xsl:variable name="eDate" select="xs:date(exerciseDate)"/>
<Type>
<xsl:value-of select="if($tDate lt $eDate)
then 'Spot'
else if($tDate ge $eDate)
then 'Forward'
else 'Error'"/>
</Type>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Related

Split element within xslt file

I have the following input
UK/006/10
US/004/12
And wanted to get the following output.
Country: UK
Code:006
Line: 10
Country: US
Code:004
Line:12
I tried to use following, but I need something simple, like split function. Can someone help on this?
<xsl:value-of select="substring-before(substring-after($User_def_type_4, '/'), '/')" />
Using Invisible XML, you could define a grammar for your text data to map it to XML, then an extension function library like the CoffeeSacks library to Saxon Java can be used in XSLT to parse and post-process the text so that with e.g. the XML input being
<data>UK/006/10
US/004/12</data>
and the XSLT being
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:cs="http://nineml.com/ns/coffeesacks"
exclude-result-prefixes="#all"
expand-text="yes">
<xsl:template match="data">
<xsl:apply-templates select="cs:parse-string(cs:grammar-string($grammar), .)/node()"/>
</xsl:template>
<xsl:output method="xml" indent="yes"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="/" name="xsl:initial-template">
<xsl:next-match/>
<xsl:comment xmlns:saxon="http://saxon.sf.net/">Run with {system-property('xsl:product-name')} {system-property('xsl:product-version')} {system-property('Q{http://saxon.sf.net/}platform')}</xsl:comment>
</xsl:template>
<xsl:param name="grammar" as="xs:string" expand-text="no">Countries = Country*.
Country = Name, -'/', Code, -'/', Line, #A?.
Name = ['A'-'Z'],['A'-'Z'].
Code = ['0'-'9'],['0'-'9'],['0'-'9'].
Line = ['0'-'9'],['0'-'9'].</xsl:param>
</xsl:stylesheet>
you get e.g.
<?xml version="1.0" encoding="UTF-8"?>
<Countries>
<Country>
<Name>UK</Name>
<Code>006</Code>
<Line>10</Line>
</Country>
<Country>
<Name>US</Name>
<Code>004</Code>
<Line>12</Line>
</Country>
</Countries>
<!--Run with SAXON HE 11.3 -->
Online sample using Saxon HE 11 Java and the named CoffeeSacks library.

Streaming XSLT 3 with Nested Group by logic

I am working on transforming a huge set of data (~1000k records) using XSLT 3.0. I am getting Java memory heap errors in my ERP system (Workday) since the input XML message is very big. I tried streaming xslt only, but couldn't make it work. Can someone please assist me in transforming the data memory efficient way.
<?xml version="1.0" encoding="UTF-8"?>
<a:Report_Data xmlns:a="urn:com.workday.report/INT_Currency_Conversion_Rates_-_Monthly_Source">
<a:Report_Entry>
<a:Source_Currency>USD</a:Source_Currency>
<a:Target_Currency>INR</a:Target_Currency>
<a:Currency_Rate>76.33</a:Currency_Rate>
</a:Report_Entry>
<a:Report_Entry>
<a:Source_Currency>USD</a:Source_Currency>
<a:Target_Currency>CHN</a:Target_Currency>
<a:Currency_Rate>16.33</a:Currency_Rate>
</a:Report_Entry>
<a:Report_Entry>
<a:Source_Currency>CHN</a:Source_Currency>
<a:Target_Currency>INR</a:Target_Currency>
<a:Currency_Rate>26.33</a:Currency_Rate>
</a:Report_Entry>
</a:Report_Data>
XSLT code that I have tried:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:a="urn:com.workday.report/INT_Currency_Conversion_Rates_-_Monthly_Source"
xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
version="3.0">
<xsl:output method="xml" indent="no" omit-xml-declaration="yes" />
<xsl:mode streamable="yes" on-no-match="shallow-skip" />
<xsl:template match="a:Report_Data">
<RTMap>
<xsl:fork>
<xsl:for-each-group select="a:Report_Entry/copy-of()" group-by="a:Source_Currency">
<xsl:for-each-group select="current-group()" group-by="a:Target_Currency">
<Row>
<Map_Rate><xsl:value-of select="avg(current-group()/a:Currency_Rate)"/></Map_Rate>
<Map_From_Currency><xsl:value-of select="a:Source_Currency"/></Map_From_Currency>
<Map_Target_Currency><xsl:value-of select="a:Target_Currency"/></Map_Target_Currency>
</Row>
</xsl:for-each-group>
</xsl:for-each-group>
</xsl:fork>
</RTMap>
</xsl:template>
</xsl:stylesheet>
Thank you,
Jay
In XSLT 3 you can use a composite key and if you use copy-of() you don't need the xsl:fork:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xpath-default-namespace="urn:com.workday.report/INT_Currency_Conversion_Rates_-_Monthly_Source"
exclude-result-prefixes="#all"
expand-text="yes">
<xsl:template match="Report_Data">
<RTMap>
<xsl:for-each-group select="Report_Entry!copy-of()" composite="yes" group-by="Source_Currency, Target_Currency ">
<Row>
<Map_Rate>{avg(current-group()/Currency_Rate)}</Map_Rate>
<Map_From_Currency>{current-grouping-key()[1]}</Map_From_Currency>
<Map_Target_Currency>{current-grouping-key()[2]}</Map_Target_Currency>
</Row>
</xsl:for-each-group>
</RTMap>
</xsl:template>
<xsl:output method="xml" indent="yes"/>
<xsl:mode on-no-match="shallow-skip" streamable="yes"/>
</xsl:stylesheet>
But in the end any group-by needs to buffer groups as you don't know whether the last Report_Entry might belong into the first group so any grouping of that input based on those keys will consume memory. Streamed grouping with a low memory consumption works if you use group-starting-with or group-adjacent, if the input data and the requirements allow that, but group-by is always going to buffer groups.

I need to remove a node from an XML based on a Condition using Group by

I have the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<EMPLOYEE_ASSIGNMENT>
<REFRESH_DATE>2022-03-10 10:55:35.000</REFRESH_DATE>
<PERSON_ID>11189</PERSON_ID>
<EMPLOYEE_ID>032656300</EMPLOYEE_ID>
<EFFECTIVE_START_DATE>2020-08-19 00:00:00.000</EFFECTIVE_START_DATE>
<EFFECTIVE_END_DATE>4712-12-31 00:00:00.000</EFFECTIVE_END_DATE>
<BUSINESS_PROCESS>Absence Return for XXXXXXXXX last day of absence on 08/18/2020, first day back at work on 08/19/2020</BUSINESS_PROCESS>
<ACT_ASSIGNMENT_STATUS_TYPE_ID>1</ACT_ASSIGNMENT_STATUS_TYPE_ID>
<ACT_ORGANIZATION_ID>601</ACT_ORGANIZATION_ID>
<ACT_JOB_QUINTIQ_POSITION>Trainee</ACT_JOB_QUINTIQ_POSITION>
<ACT_HOURS_PER_WEEK>37.5</ACT_HOURS_PER_WEEK>
<ACT_HOURS_FREQUENCY>W</ACT_HOURS_FREQUENCY>
<ACT_BARGAINING_UNIT_CODE>C</ACT_BARGAINING_UNIT_CODE>
<ACT_PRIMARY_PROVINCE>BC</ACT_PRIMARY_PROVINCE>
</EMPLOYEE_ASSIGNMENT>
<EMPLOYEE_ASSIGNMENT>
<REFRESH_DATE>2022-03-10 10:55:35.000</REFRESH_DATE>
<PERSON_ID>11189</PERSON_ID>
<EMPLOYEE_ID>032656300</EMPLOYEE_ID>
<EFFECTIVE_START_DATE>2020-08-19 00:00:00.000</EFFECTIVE_START_DATE>
<EFFECTIVE_END_DATE>4712-12-31 00:00:00.000</EFFECTIVE_END_DATE>
<BUSINESS_PROCESS>Data Change: XXXXXXXXXXXX</BUSINESS_PROCESS>
<ACT_ASSIGNMENT_STATUS_TYPE_ID>1</ACT_ASSIGNMENT_STATUS_TYPE_ID>
<ACT_ORGANIZATION_ID>856</ACT_ORGANIZATION_ID>
<ACT_JOB_QUINTIQ_POSITION>Employee</ACT_JOB_QUINTIQ_POSITION>
<ACT_HOURS_PER_WEEK>37.5</ACT_HOURS_PER_WEEK>
<ACT_HOURS_FREQUENCY>W</ACT_HOURS_FREQUENCY>
<ACT_BARGAINING_UNIT_CODE>C</ACT_BARGAINING_UNIT_CODE>
<ACT_PRIMARY_PROVINCE>MB</ACT_PRIMARY_PROVINCE>
</EMPLOYEE_ASSIGNMENT>
</root>
The two nodes have the same EFFECTIVE_START_DATE but different BUSINESS_PROCESS for a single EMPLOYEE_ID. I need to transform that XML in a way that: when Two (or more) BUSINESS_PROCESS are present for and EMPLOYEE_ID on the same EFFECTIVE_START_DATE it shows only the one that is of value Data Change: XXXXXXXXX.
I need to transform it to:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<EMPLOYEE_ASSIGNMENT>
<REFRESH_DATE>2022-03-10 10:55:35.000</REFRESH_DATE>
<PERSON_ID>11189</PERSON_ID>
<EMPLOYEE_ID>032656300</EMPLOYEE_ID>
<EFFECTIVE_START_DATE>2020-08-19 00:00:00.000</EFFECTIVE_START_DATE>
<EFFECTIVE_END_DATE>4712-12-31 00:00:00.000</EFFECTIVE_END_DATE>
<BUSINESS_PROCESS>Data Change: XXXXXXXXXXXX</BUSINESS_PROCESS>
<ACT_ASSIGNMENT_STATUS_TYPE_ID>1</ACT_ASSIGNMENT_STATUS_TYPE_ID>
<ACT_ORGANIZATION_ID>856</ACT_ORGANIZATION_ID>
<ACT_JOB_QUINTIQ_POSITION>Employee</ACT_JOB_QUINTIQ_POSITION>
<ACT_HOURS_PER_WEEK>37.5</ACT_HOURS_PER_WEEK>
<ACT_HOURS_FREQUENCY>W</ACT_HOURS_FREQUENCY>
<ACT_BARGAINING_UNIT_CODE>C</ACT_BARGAINING_UNIT_CODE>
<ACT_PRIMARY_PROVINCE>MB</ACT_PRIMARY_PROVINCE>
</EMPLOYEE_ASSIGNMENT>
</root>
Thanks a lot
This is difficult to follow. If (!) I understand your description correctly, you want to do:
XSLT 2.0
<xsl:stylesheet version="2.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:copy>
<xsl:for-each-group select="EMPLOYEE_ASSIGNMENT" group-by="concat(EMPLOYEE_ID,'|', EFFECTIVE_START_DATE)">
<xsl:copy-of select="current-group()[starts-with(BUSINESS_PROCESS, 'Data Change:')]"/>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

XSLT output empty Debugging fails with no output

I read through forums and i am unable to understand why the output is empty. It might be a simple thing i am missing.
I tried debugging in VS 2017 and it does not give any output any help in this matter is appreciated. IF i input only the ns0:EFACT_D96A_ORDERS_EAN008 node as an input to XSLT the output comes with the "test" content
<?xml version="1.0" encoding="UTF-16"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:var="http://schemas.microsoft.com/BizTalk/2003/var" exclude-result-prefixes="msxsl var s0 userCSharp" version="1.0" xmlns:ns1="http://Microsoft.LobServices.Sap/2007/03/Types/Idoc/3/ORDERS05//740" xmlns:ns0="http://Microsoft.LobServices.Sap/2007/03/Idoc/3/ORDERS05//740/Send" xmlns:ns2="http://Microsoft.LobServices.Sap/2007/03/Types/Idoc/Common/" xmlns:s0="http://schemas.microsoft.com/BizTalk/EDI/EDIFACT/2006" xmlns:ns3="http://schemas.microsoft.com/2003/10/Serialization/" xmlns:userCSharp="http://schemas.microsoft.com/BizTalk/2003/userCSharp" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ins0="http://schemas.microsoft.com/BizTalk/EDI/EDIFACT/2006/InterchangeXML">
<xsl:output omit-xml-declaration="yes" method="xml" version="1.0" />
<xsl:template match="/">
<xsl:apply-templates select="/s0:EFACT_D96A_ORDERS_EAN008" />
</xsl:template>
<xsl:template match="/s0:EFACT_D96A_ORDERS_EAN008">
<ns0:Send>
<ns0:idocData>
Test
</ns0:idocData>
</ns0:Send>
</xsl:template>
</xsl:stylesheet>
Input file-
<ins0:EdifactInterchangeXml DelimiterSetSerializedData="39:-1:-1:43:58:63:-1:46:-1" xmlns:ins0="http://schemas.microsoft.com/BizTalk/EDI/EDIFACT/2006/InterchangeXML">
<ns0:UNA xmlns:ns0="http://schemas.microsoft.com/Edi/EdifactServiceSchema">
<UNA1>58</UNA1>
</ns0:UNA>
<ns0:UNB xmlns:ns0="http://schemas.microsoft.com/Edi/EdifactServiceSchema">
<UNB1>
<UNB1.1>ABCD</UNB1.1>
<UNB1.2>5</UNB1.2>
</UNB1>
</ns0:UNB>
<TransactionSetGroup>
<TransactionSet DocType="http://schemas.microsoft.com/BizTalk/EDI/EDIFACT/2006#EFACT_D96A_ORDERS_EAN008">
<ns0:EFACT_D96A_ORDERS_EAN008 xmlns:ns0="http://schemas.microsoft.com/BizTalk/EDI/EDIFACT/2006">
</ns0:EFACT_D96A_ORDERS_EAN008>
</TransactionSet>
</TransactionSetGroup>
<ns0:UNZ xmlns:ns0="http://schemas.microsoft.com/Edi/EdifactServiceSchema">
<UNZ1>1</UNZ1>
<UNZ2>86</UNZ2>
</ns0:UNZ>
</ins0:EdifactInterchangeXml>
A forward slash / at the start of the expression matches the document node, so doing select="/s0:EFACT_D96A_ORDERS_EAN008" will only select s0:EFACT_D96A_ORDERS_EAN008 if it is a child of the document node. i.e. if it is the root element, which it isn't.
To select s0:EFACT_D96A_ORDERS_EAN008 regardless of where it is in the document do this...
<xsl:apply-templates select="//s0:EFACT_D96A_ORDERS_EAN008" />
You also need to remove the single forward slash from the match expression too (You don't need the double-slashes in the match expression, as the match will work regardless of where the element is in the document)
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:var="http://schemas.microsoft.com/BizTalk/2003/var" exclude-result-prefixes="msxsl var s0 userCSharp" version="1.0" xmlns:ns1="http://Microsoft.LobServices.Sap/2007/03/Types/Idoc/3/ORDERS05//740" xmlns:ns0="http://Microsoft.LobServices.Sap/2007/03/Idoc/3/ORDERS05//740/Send" xmlns:ns2="http://Microsoft.LobServices.Sap/2007/03/Types/Idoc/Common/" xmlns:s0="http://schemas.microsoft.com/BizTalk/EDI/EDIFACT/2006" xmlns:ns3="http://schemas.microsoft.com/2003/10/Serialization/" xmlns:userCSharp="http://schemas.microsoft.com/BizTalk/2003/userCSharp" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ins0="http://schemas.microsoft.com/BizTalk/EDI/EDIFACT/2006/InterchangeXML">
<xsl:output omit-xml-declaration="yes" method="xml" version="1.0" />
<xsl:template match="/">
<xsl:apply-templates select="//s0:EFACT_D96A_ORDERS_EAN008" />
</xsl:template>
<xsl:template match="s0:EFACT_D96A_ORDERS_EAN008">
<ns0:Send>
<ns0:idocData>
Test
</ns0:idocData>
</ns0:Send>
</xsl:template>
</xsl:stylesheet>

XSLT transforming name value pairs to its corresponding XML

I am new to XSLT I am trying to transform a name value pair to its corresponding XML. This feature is primarily used in case of special extensions to a standard.
The file I want to transform is the following. There are no spaces expected in any of the extNames.
<?xml version="1.0" encoding="UTF-8"?>
<extensionItems xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ExtensionItems.xsd">
<extensionsItem>
<extName> callCode</extName>
<extValue>1</extValue>
<extType>integer</extType>
</extensionsItem>
<extensionsItem>
<extName>callbackType</extName>
<extValue>All</extValue>
<extType>string</extType>
</extensionsItem>
<extensionsItem>
<extName>callbackEmail</extName>
<extValue>me#mine.org</extValue>
<extType>string</extType>
</extensionsItem>
</extensionItems>
to the following:
<ODEventNotificationExtraField>
<callCode> 1</callCode>
<callbackType> All </callbackType>
<callbackEmail> me#mine.org </callbackEmail>
</ODEventNotificationExtraField>
The following stylesheet produces the desired result:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="extensionItems">
<ODEventNotificationExtraField>
<xsl:apply-templates/>
</ODEventNotificationExtraField>
</xsl:template>
<xsl:template match="extensionsItem">
<xsl:element name="{extName}">
<xsl:value-of select="extValue"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>