Useful XSLT transformations for XML
24. 11. 2022
In this blog we want to share useful transformation which are commonly needed and used to prepare xml or adapt xml for integration or sync purposes. We will firstly demonstrate some usefull xslt patterns. We assume to use xslt version 2. In the end of this blog there will be also example of java code used to run the transformation. For these examples you can always test the xslt transformation also in the online tester such as https://xslttest.appspot.com/.
Remove prefixes in elements and attributes
Very common requirement is to remove namespaces prefixes for the elements and attributes. Here is an example of xml with prefixes both in elements and attributes.
Let us have a XML which has namespace prefixes
<aetgt:SiebelMessage xmlns:aetgt="http://www.siebel.com/xml/MOBI/EAI/INS/PUL" xmlns:ns5="http://xml.mobi.ch/service/rdp/siebel/TransformationService/v1_0" xmlns:ns2="http://www.siebel.com/xml/MOBI/EAI/INS/PUL" xmlns:ns4="http://xml.mobi.ch/datatype/common/Commons/v3" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns3="http://xml.mobi.ch/datatype/pov/Contract/v3" EventName="SendAssetRel" ExtractionTimestamp="2022-11-10-18.23.11.070000" FlowId="mps_contract" FlowVersion="1.0" KeyType="1" KeyValue="16409144" MessageId="1557696355_2022-11-10-18.23.11.017000_101_l91r02d2.mobi.mobicorp.ch/MPSContractJMSAdapter_mps_contract">
<ns2:ListOfMobiEaiInsPul>
<ns2:MobiInsPolicyEai IntegrationId="101|16409144">
<ns2:MOBIPMDiscount>N</ns2:MOBIPMDiscount>
<ns2:MOBIIndividualDiscount>N</ns2:MOBIIndividualDiscount>
<ns2:Category>Contract</ns2:Category>
<ns2:ExpirationDate/>
<ns2:MOBIABDisbursement/>
<ns2:Channel>0</ns2:Channel>
<ns2:MOBIBusinessNumber>16409144</ns2:MOBIBusinessNumber>
<ns2:MOBICategory>2</ns2:MOBICategory>
<ns2:MOBIContractType>Non Life</ns2:MOBIContractType>
<ns2:MOBIIntegrationId>16409144</ns2:MOBIIntegrationId>
<ns2:MOBIReadOnlyFlag>Y</ns2:MOBIReadOnlyFlag>
<ns2:MOBISourceSystem>101</ns2:MOBISourceSystem>
<ns2:MOBIStatus>1</ns2:MOBIStatus>
<ns2:MOBIStatusTechnical>1</ns2:MOBIStatusTechnical>
<ns2:MOBIBusinessNumberDisplay>G-1640-9144</ns2:MOBIBusinessNumberDisplay>
<ns2:MOBIGBRAccount>519587</ns2:MOBIGBRAccount>
<ns2:MOBIIndustry/>
<ns2:MOBIMandantName>Mandant-Mobiliar</ns2:MOBIMandantName>
<ns2:MOBIClaimholder>100015</ns2:MOBIClaimholder>
<ns2:MOBISuspensionDate/>
<ns2:MOBICancelationDate/>
<ns2:ListOfMobiPolicyAccountEai>
<ns2:MobiPolicyAccountEai>
<ns2:MOBICustomerNumber>23367461</ns2:MOBICustomerNumber>
<ns2:MOBIRole>1331</ns2:MOBIRole>
<ns2:MOBIStartDateDB2>2021-11-11-00.00.00.000000</ns2:MOBIStartDateDB2>
<ns2:MOBIStartDate>11/11/2021</ns2:MOBIStartDate>
<ns2:MOBIEndDate>12/31/9999</ns2:MOBIEndDate>
</ns2:MobiPolicyAccountEai>
</ns2:ListOfMobiPolicyAccountEai>
</ns2:MobiInsPolicyEai>
</ns2:ListOfMobiEaiInsPul>
</aetgt:SiebelMessage>
These prefixes are often problem so with xslt we can strip them,
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema xmlns:ns2=http://www.siebel.com/xml/MOBI/EAI/INS/PUL" exclude-result-prefixes="xs">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> <xsl:template match="*">
<!-- remove element prefix -->
<xsl:element name="{local-name()}">
<!-- process attributes -->
<xsl:for-each select="@*">
<!-- remove attribute prefix -->
<xsl:attribute name="{local-name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:for-each>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
The result looks pretty good :
<?xml version="1.0" encoding="UTF-8"?>
<SiebelMessage EventName="SendAssetRel"
ExtractionTimestamp="2022-11-10-18.23.11.070000"
FlowId="mps_contract"
FlowVersion="1.0"
KeyType="1"
KeyValue="16409144"
MessageId="1557696355_2022-11-10-18.23.11.017000_101_l91r02d2.mobi.mobicorp.ch/MPSContractJMSAdapter_mps_contract">
<ListOfMobiEaiInsPul>
<MobiInsPolicyEai IntegrationId="101|16409144">
<MOBIPMDiscount>N</MOBIPMDiscount>
<MOBIIndividualDiscount>N</MOBIIndividualDiscount>
<Category>Contract</Category>
<ExpirationDate/>
<MOBIABDisbursement/>
<Channel>0</Channel>
<MOBIBusinessNumber>16409144</MOBIBusinessNumber>
<MOBICategory>2</MOBICategory>
<MOBIContractType>Non Life</MOBIContractType>
<MOBIIntegrationId>16409144</MOBIIntegrationId>
<MOBIReadOnlyFlag>Y</MOBIReadOnlyFlag>
<MOBISourceSystem>101</MOBISourceSystem>
<MOBIStatus>1</MOBIStatus>
<MOBIStatusTechnical>1</MOBIStatusTechnical>
<MOBIBusinessNumberDisplay>G-1640-9144</MOBIBusinessNumberDisplay>
<MOBIGBRAccount>519587</MOBIGBRAccount>
<MOBIIndustry/>
<MOBIMandantName>Mandant-Mobiliar</MOBIMandantName>
<MOBIClaimholder>100015</MOBIClaimholder>
<MOBISuspensionDate/>
<MOBICancelationDate/>
<ListOfMobiPolicyAccountEai>
<MobiPolicyAccountEai>
<MOBICustomerNumber>23367461</MOBICustomerNumber>
<MOBIRole>1331</MOBIRole>
<MOBIStartDateDB2>2021-11-11-00.00.00.000000</MOBIStartDateDB2>
<MOBIStartDate>11/11/2021</MOBIStartDate>
<MOBIEndDate>12/31/9999</MOBIEndDate>
</MobiPolicyAccountEai>
</ListOfMobiPolicyAccountEai>
</MobiInsPolicyEai>
</ListOfMobiEaiInsPul>
</SiebelMessage>
Replace element based on condition of the value
Let us have a requirement to replace Date Element which contains value 01/01/0001 what is not valid date for DB and replace it with 01/01/1970. The origin XML also contains prefixes so we want to strip those.
<aetgt:SiebelMessage xmlns:aetgt="http://www.siebel.com/xml/MOBI/EAI/INS/PUL" xmlns:ns5="http://xml.mobi.ch/service/rdp/siebel/TransformationService/v1_0" xmlns:ns2="http://www.siebel.com/xml/MOBI/EAI/INS/PUL" xmlns:ns4="http://xml.mobi.ch/datatype/common/Commons/v3" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns3="http://xml.mobi.ch/datatype/pov/Contract/v3" EventName="SendAssetRel" ExtractionTimestamp="2022-11-10-18.23.11.070000" FlowId="mps_contract" FlowVersion="1.0" KeyType="1" KeyValue="16409144" MessageId="1557696355_2022-11-10-18.23.11.017000_101_l91r02d2.mobi.mobicorp.ch/MPSContractJMSAdapter_mps_contract">
<ns2:ListOfMobiEaiInsPul>
<ns2:MobiInsPolicyEai IntegrationId="101|16409144">
<ns2:MOBIPMDiscount>N</ns2:MOBIPMDiscount>
<ns2:ListOfMobiPolicyAccountEai>
<ns2:MobiPolicyAccountEai>
<ns2:MOBICustomerNumber>23367461</ns2:MOBICustomerNumber>
<ns2:MOBIRole>1331</ns2:MOBIRole>
<ns2:MOBIStartDateDB2>2021-11-11-00.00.00.000000</ns2:MOBIStartDateDB2>
<ns2:MOBIStartDate>01/01/0001</ns2:MOBIStartDate>
<ns2:MOBIEndDate>12/31/9999</ns2:MOBIEndDate>
</ns2:MobiPolicyAccountEai>
</ns2:ListOfMobiPolicyAccountEai>
</ns2:MobiInsPolicyEai>
</ns2:ListOfMobiEaiInsPul>
</aetgt:SiebelMessage>
The start date will be replaced with compatible one using this xslt plus the prefixes will be removed
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema xmlns:ns2=http://www.siebel.com/xml/MOBI/EAI/INS/PUL"
exclude-result-prefixes="xs">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="*[local-name()='MOBIStartDate']">
<xsl:choose>
<xsl:when test="text()='01/01/0001'">
<MOBIStartDate>01/01/1970</MOBIStartDate>
</xsl:when>
<xsl:otherwise>
<MOBIStartDate><xsl:value-of select="."/></MOBIStartDate>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!--Add any of node which you want to skip can add more just by adding the line-->
<xsl:template match="/*">
<SiebelMessage>
<xsl:value-of select="title"/>
<xsl:apply-templates />
</SiebelMessage>
</xsl:template>
<xsl:template match="*">
<!-- remove element prefix -->
<xsl:element name="{local-name()}">
<!-- process attributes -->
<xsl:for-each select="@*">
<!-- remove attribute prefix -->
<xsl:attribute name="{local-name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:for-each>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
The result looks like this :
<?xml version="1.0" encoding="UTF-8"?>
<SiebelMessage>
<ListOfMobiEaiInsPul>
<MobiInsPolicyEai IntegrationId="101|16409144">
<MOBIPMDiscount>N</MOBIPMDiscount>
<ListOfMobiPolicyAccountEai>
<MobiPolicyAccountEai>
<MOBICustomerNumber>23367461</MOBICustomerNumber>
<MOBIRole>1331</MOBIRole>
<MOBIStartDateDB2>2021-11-11-00.00.00.000000</MOBIStartDateDB2>
<MOBIStartDate>01/01/1970</MOBIStartDate>
<MOBIEndDate>12/31/9999</MOBIEndDate>
</MobiPolicyAccountEai>
</ListOfMobiPolicyAccountEai>
</MobiInsPolicyEai>
</ListOfMobiEaiInsPul>
</SiebelMessage>
Skip or remove elements and nodes
We want remove elements from XML either based on xml tag or some value. In this scenario we want to remove element with XML Tag MOBIStartDate and we want to remove whole node MobiPolicyAccountEai which has a child node with End Date up to today. We also want to strip the prefixes.
Let us have a xml with 1 MobiPolicyAccountEai node which has EndDate 24.11.2022. Today is 25.11.2022. Second node MobiPolicyEai has EndDate in the future 01.01.2025. We expect that the first node will not be in the target XML.
<aetgt:SiebelMessage xmlns:aetgt="http://www.siebel.com/xml/MOBI/EAI/INS/PUL" xmlns:ns5="http://xml.mobi.ch/service/rdp/siebel/TransformationService/v1_0" xmlns:ns2="http://www.siebel.com/xml/MOBI/EAI/INS/PUL" xmlns:ns4="http://xml.mobi.ch/datatype/common/Commons/v3" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns3="http://xml.mobi.ch/datatype/pov/Contract/v3" EventName="SendAssetRel" ExtractionTimestamp="2022-11-10-18.23.11.070000" FlowId="mps_contract" FlowVersion="1.0" KeyType="1" KeyValue="16409144" MessageId="1557696355_2022-11-10-18.23.11.017000_101_l91r02d2.mobi.mobicorp.ch/MPSContractJMSAdapter_mps_contract">
<ns2:ListOfMobiEaiInsPul>
<ns2:MobiInsPolicyEai IntegrationId="101|16409144">
<ns2:MOBIPMDiscount>N</ns2:MOBIPMDiscount>
<ns2:MOBIBusinessNumber>16409144</ns2:MOBIBusinessNumber>
<ns2:MOBICategory>2</ns2:MOBICategory>
<ns2:MOBIContractType>Non Life</ns2:MOBIContractType>
<ns2:MOBIIntegrationId>16409144</ns2:MOBIIntegrationId>
<ns2:MOBIReadOnlyFlag>Y</ns2:MOBIReadOnlyFlag>
<ns2:MOBISourceSystem>101</ns2:MOBISourceSystem>
<ns2:MOBIStatus>1</ns2:MOBIStatus>
<ns2:MOBIStatusTechnical>1</ns2:MOBIStatusTechnical>
<ns2:MOBIBusinessNumberDisplay>G-1640-9144</ns2:MOBIBusinessNumberDisplay>
<ns2:MOBIGBRAccount>519587</ns2:MOBIGBRAccount>
<ns2:MOBIIndustry/>
<ns2:MOBIMandantName>Mandant-Mobiliar</ns2:MOBIMandantName>
<ns2:MOBIClaimholder>100015</ns2:MOBIClaimholder>
<ns2:MOBISuspensionDate/>
<ns2:MOBICancelationDate/>
<ns2:ListOfMobiPolicyAccountEai>
<ns2:MobiPolicyAccountEai>
<ns2:MOBICustomerNumber>23367461</ns2:MOBICustomerNumber>
<ns2:MOBIRole>1331</ns2:MOBIRole>
<ns2:MOBIStartDateDB2>2021-11-11-00.00.00.000000</ns2:MOBIStartDateDB2>
<ns2:MOBIStartDate>11/11/2021</ns2:MOBIStartDate>
<ns2:MOBIEndDate>11/24/2022</ns2:MOBIEndDate>
</ns2:MobiPolicyAccountEai>
<ns2:MobiPolicyAccountEai>
<ns2:MOBICustomerNumber>23367461</ns2:MOBICustomerNumber>
<ns2:MOBIRole>13331</ns2:MOBIRole>
<ns2:MOBIStartDateDB2>2021-11-11-00.00.00.000000</ns2:MOBIStartDateDB2>
<ns2:MOBIStartDate>11/11/2021</ns2:MOBIStartDate>
<ns2:MOBIEndDate>01/01/2025</ns2:MOBIEndDate>
</ns2:MobiPolicyAccountEai>
</ns2:ListOfMobiPolicyAccountEai>
</ns2:MobiInsPolicyEai>
</ns2:ListOfMobiEaiInsPul>
XSLT transformation is using XSLT 2 syntax <xsl:template match="ns2:MobiPolicyAccountEai[ns2:MOBIEndDate[xs:date(concat(substring(string(),7,4),'-',substring(string(),1,2),'-',substring(string(),4,2))) <= current-date()]]">
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema xmlns:ns2=http://www.siebel.com/xml/MOBI/EAI/INS/PUL"
exclude-result-prefixes="xs">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[local-name()='MOBISourceSystem']"/>
<xsl:template match="ns2:MobiPolicyContactEai[ns2:EndDate[xs:date(concat(substring(string(),7,4),'-',substring(string(),1,2),'-',substring(string(),4,2))) <= current-date()]]">
</xsl:template>
<xsl:template match="ns2:MobiPolicyAccountEai[ns2:MOBIEndDate[xs:date(concat(substring(string(),7,4),'-',substring(string(),1,2),'-',substring(string(),4,2))) <= current-date()]]">
</xsl:template>
<xsl:template match="*[local-name()='MOBIStartDate']">
<xsl:choose>
<xsl:when test="text()='01/01/0001'">
<MOBIStartDate>01/01/1970</MOBIStartDate>
</xsl:when>
<xsl:otherwise>
<MOBIStartDate><xsl:value-of select="."/></MOBIStartDate>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!--Add any of node which you want to skip can add more just by adding the line-->
<xsl:template match="/*">
<SiebelMessage>
<xsl:value-of select="title"/>
<xsl:apply-templates />
</SiebelMessage>
</xsl:template>
<xsl:template match="*">
<!-- remove element prefix -->
<xsl:element name="{local-name()}">
<!-- process attributes -->
<xsl:for-each select="@*">
<!-- remove attribute prefix -->
<xsl:attribute name="{local-name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:for-each>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Resulting XML looks :
<?xml version="1.0" encoding="UTF-8"?>
<SiebelMessage EventName="SendAssetRel"
ExtractionTimestamp="2022-11-10-18.23.11.070000"
FlowId="mps_contract"
FlowVersion="1.0"
KeyType="1"
KeyValue="16409144"
MessageId="1557696355_2022-11-10-18.23.11.017000_101_l91r02d2.mobi.mobicorp.ch/MPSContractJMSAdapter_mps_contract">
<ListOfMobiEaiInsPul>
<MobiInsPolicyEai IntegrationId="101|16409144">
<MOBIPMDiscount>N</MOBIPMDiscount>
<MOBIIndividualDiscount>N</MOBIIndividualDiscount>
<Category>Contract</Category>
<ExpirationDate/>
<MOBIABDisbursement/>
<Channel>0</Channel>
<MOBIBusinessNumber>16409144</MOBIBusinessNumber>
<MOBICategory>2</MOBICategory>
<MOBIContractType>Non Life</MOBIContractType>
<MOBIIntegrationId>16409144</MOBIIntegrationId>
<MOBIReadOnlyFlag>Y</MOBIReadOnlyFlag>
<MOBISourceSystem>101</MOBISourceSystem>
<MOBIStatus>1</MOBIStatus>
<MOBIStatusTechnical>1</MOBIStatusTechnical>
<MOBIBusinessNumberDisplay>G-1640-9144</MOBIBusinessNumberDisplay>
<MOBIGBRAccount>519587</MOBIGBRAccount>
<MOBIIndustry/>
<MOBIMandantName>Mandant-Mobiliar</MOBIMandantName>
<MOBIClaimholder>100015</MOBIClaimholder>
<MOBISuspensionDate/>
<MOBICancelationDate/>
<ListOfMobiPolicyAccountEai>
<MobiPolicyAccountEai>
<MOBICustomerNumber>23367461</MOBICustomerNumber>
<MOBIRole>1331</MOBIRole>
<MOBIStartDateDB2>2021-11-11-00.00.00.000000</MOBIStartDateDB2>
<MOBIStartDate>11/11/2021</MOBIStartDate>
<MOBIEndDate>01/01/2025</MOBIEndDate>
</MobiPolicyAccountEai>
</ListOfMobiPolicyAccountEai>
</MobiInsPolicyEai>
</ListOfMobiEaiInsPul>
</SiebelMessage>
JAVA code to execute the transformation with XSLT2 standard using SAXON library
package ch.mobi.transformation;
import ch.mobi.models.BetweenDatesResult;
import ch.mobi.models.CxFlowsDiff;
import ch.mobi.repositories.CxMobiLogRepository;
import io.quarkus.logging.Log;
import io.quarkus.narayana.jta.QuarkusTransaction;
import org.apache.commons.lang3.RandomStringUtils;
@ApplicationScoped
public class PayloadTransformation {
@Inject
CxMobiLogRepository cxMobiLogRepository;
public String executeXslt(String xmlString) throws TransformerException {
ClassLoader cl = this.getClass().getClassLoader();
Templates templates = new net.sf.saxon.BasicTransformerFactory().newTemplates(
new StreamSource(cl.getResourceAsStream("xslt/transformPayload.xsl")));
final Transformer transformer = templates.newTransformer();
final StringWriter writer = new StringWriter();
transformer.transform(new StreamSource(new StringReader(xmlString)),
new StreamResult(writer));
return writer.toString();
}
private void someProcess(xml) {
kafkaReadyXML = executeXslt(xmlKafka);
..
}
To load the library into project extend your pom.xml with
<dependency>
<groupId>net.sf.saxon</groupId>
<artifactId>Saxon-HE</artifactId>
<version>11.3</version>
</dependency>
Conclusion XSLT v2 is a powerful tool to process and transform XML Payload. It has many functions for reference visit https://saxon.sourceforge.net/saxon7.9.1/functions.html. CCW is a proven consultancy with hunderd of man years of developing, integrating and consulting experience. If you need expertise contact us.
Back to Blog