XSLT remove duplicate nodes based on element value - xslt

I am newbie to XSLT.can you help me for xslt to achieve below output. In my input xml have duplicate nodes and i have to remove based on element (CustAccId) value should not be re-peat.
Inputxml :
<Main>
<Request>
<TypeInd>I</TypeInd>
<CustAcctID>505665599</CustAcctID>
<ServiceOrderID>1452653</ServiceOrderID>
</Request>
<Request>
<TypeInd>O</TypeInd>
<CustAcctID>2011395</CustAcctID>
<ServiceOrderID>1452652</ServiceOrderID>
</Request>
<Request>
<TypeInd>I</TypeInd>
<CustAcctID>505665599</CustAcctID>
<ServiceOrderID>1452653</ServiceOrderID>
</Request>
</Main>
Output XML :
<Main>
<Request>
<TypeInd>I</TypeInd>
<CustAcctID>505665599</CustAcctID>
<ServiceOrderID>1452653</ServiceOrderID>
</Request>
<Request>
<TypeInd>O</TypeInd>
<CustAcctID>2011395</CustAcctID>
<ServiceOrderID>1452652</ServiceOrderID>
</Request>
Here is XSLt i tried but didn't work like it retrun duplicate request node
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:if test="not(following::Request[CustAcctID=current()])">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

You can remove the duplicate with following XSLT:
<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:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="Request[CustAcctID = following::Request/CustAcctID]"/>
</xsl:stylesheet>
Output XML:
<?xml version="1.0" encoding="UTF-8"?>
<Main>
<Request>
<TypeInd>O</TypeInd>
<CustAcctID>2011395</CustAcctID>
<ServiceOrderID>1452652</ServiceOrderID>
</Request>
<Request>
<TypeInd>I</TypeInd>
<CustAcctID>505665599</CustAcctID>
<ServiceOrderID>1452653</ServiceOrderID>
</Request>
</Main>
The template matching all Request nodes where the CustAcctID matches the CustAcctID of the following Request will not produce any output for the matching Request, so the duplicates will not be written.
Update for the advice in the comment by michael.hor257k: Another approach is to use Muenchian grouping:
<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:strip-space elements="*"/>
<xsl:key name="x" match="ServiceOrderID" use="." />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="Request">
<xsl:for-each select=".">
<xsl:if test="generate-id(ServiceOrderID) =
generate-id(key('x', ServiceOrderID)[1])">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
which produces the same output XML but can be more efficient as you'll find described in detail in the article by Jeni Tennison http://www.jenitennison.com/xslt/grouping/muenchian.xml that michael.hor257k already recommended.
As additional reference for XSLT grouping you can have a look at http://www.dpawson.co.uk/xsl/sect2/N4486.html

Related

XSLT: Copy an element with all attributes where an attribute matches a value

Given an XML structure like follows:
<Resources PossibleAttribute="SomethingHere">
<Book ID="1" OtherAttribute="abc" />
<Book ID="2" DifferentAttribute="def" />
</Resources>
I need to copy the structure and element where ID='1'. So I'm looking to end up with:
<Resources PossibleAttribute="SomethingHere">
<Book ID="1" OtherAttribute="abc" />
</Resources>
What I've come up with thus far is:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="Resources">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="Book[#ID='1']">
<xsl:copy>
<xsl:value-of select="#*|node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This, however, is not producing the intended end result, giving me this instead:
<Resources>
<Book>1</Book>
</Resources>
A key item here is that any of these elements may contain additional attributes that are not specified here or currently known. Hence, if additional attributes are present, they too should be copied.
Any tips you can offer would be most appreciated!
Why not simply:
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:strip-space elements="*"/>
<xsl:template match="/Resources">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:copy-of select="Book[#ID=1]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

How to remove a specific prefix using XSLT

I need to remove the ns0: prefix from the RecuperarCopiaResult node.
From <ns0:RecuperarCopiaResult> to <RecuperarCopiaResult>
Follows Input XML
<?xml version="1.0" encoding="UTF-8"?>
<ns0:RecuperarCopiaResponse xmlns:ns0="http://tempuri.org/">
<ns0:RecuperarCopiaResult><![CDATA[<Abastecimento_NF
ULTIMO_PONTEIRO="447050"><Abastecimento_NFRow><DT_PROCESS>6/2/2018
1:46:08</DT_PROCESS><CD_ABASTECIMENTO>123936138</CD_ABASTECIMENTO>
<CD_VEICULO>479077</CD_VEICULO><CD_TIPO_REGISTRO>1</CD_TIPO_REGISTRO>
<NR_BANCO>237</NR_BANCO><CD_REDE>801</CD_REDE><DC_REDE>801</DC_REDE>
<COD_POSTO>244</COD_POSTO><COD_FROTA>4941</COD_FROTA>
<COD_SUBFROTA>11264</COD_SUBFROTA><DC_SUBFROTA>R2C</DC_SUBFROTA>
<CD_COMBUSTIVEL>S</CD_COMBUSTIVEL><DC_COMBUSTIVEL>S</DC_COMBUSTIVEL>
<NR_UVE></NR_UVE><DC_PLACA>KWG8687</DC_PLACA><NM_MOTORISTA>
</NM_MOTORISTA><NR_KM_ATUAL>226076</NR_KM_ATUAL>
<NR_QTD_LITROS>139,55</NR_QTD_LITROS>
<NR_QTD_LITROS_TOTAL>139,55</NR_QTD_LITROS_TOTAL>
<CD_STATUS_ABASTECIMENTO>S</CD_STATUS_ABASTECIMENTO>
</Abastecimento_NFRow></Abastecimento_NF>]]></ns0:RecuperarCopiaResult>
</ns0:RecuperarCopiaResponse>
I am using the following xslt code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:SOAP-
ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://tempuri.org/">
<xsl:output encoding='UTF-8' method="xml" indent="yes" omit-xml-
declaration="yes" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="SOAP-ENV:*">
<xsl:apply-templates select="#* | node()"/>
</xsl:template>
<xsl:template match="ns0:*">
<xsl:element name="ns0:{local-name()}"
namespace="http://www.supergasbras.com.br/service/CtfAbastecimento">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="text()">
<xsl:value-of select="." disable-output-escaping="yes"/>
</xsl:template>
</xsl:stylesheet>
Following is the expected XML:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:RecuperarCopiaResponse
xmlns:ns0="http://www.supergasbras.com.br/service/CtfAbastecimento">
<RecuperarCopiaResult>
<Abastecimento_NF ULTIMO_PONTEIRO="447050">
<Abastecimento_NFRow>
<DT_PROCESS>6/2/2018 1:46:08</DT_PROCESS>
<CD_ABASTECIMENTO>123936138</CD_ABASTECIMENTO>
<CD_VEICULO>479077</CD_VEICULO>
<CD_TIPO_REGISTRO>1</CD_TIPO_REGISTRO>
<NR_BANCO>237</NR_BANCO>
<CD_REDE>801</CD_REDE>
<DC_REDE>801</DC_REDE>
<COD_POSTO>244</COD_POSTO>
<COD_FROTA>4941</COD_FROTA>
<COD_SUBFROTA>11264</COD_SUBFROTA>
<DC_SUBFROTA>R2C</DC_SUBFROTA>
<CD_COMBUSTIVEL>S</CD_COMBUSTIVEL>
<DC_COMBUSTIVEL>S</DC_COMBUSTIVEL>
<NR_UVE/>
<DC_PLACA>KWG8687</DC_PLACA>
<NM_MOTORISTA/>
<NR_KM_ATUAL>226076</NR_KM_ATUAL>
<NR_QTD_LITROS>139,55</NR_QTD_LITROS>
<VL_PRECO_UNITARIO>3,798</VL_PRECO_UNITARIO>
<VL_PRECO_AEP>3,798</VL_PRECO_AEP>
<VL_VALOR_TOTAL>530,01</VL_VALOR_TOTAL>
<DT_EVENTO>5/2/2018 14:37:00</DT_EVENTO>
<DT_DEBITO>26/2/2018 0:00:00</DT_DEBITO>
<DT_CREDITO>27/2/2018 0:00:00</DT_CREDITO>
<NOMEARQ>T2060218.ZZ001305.00244</NOMEARQ>
<NR_KM_PERCORRIDA>365</NR_KM_PERCORRIDA>
<NR_QTD_LITROS_TOTAL>139,55</NR_QTD_LITROS_TOTAL>
<CD_STATUS_ABASTECIMENTO>S</CD_STATUS_ABASTECIMENTO>
</Abastecimento_NFRow>
</Abastecimento_NF>
</RecuperarCopiaResult>
</ns0:RecuperarCopiaResponse>
Your question says one thing, your expected result shows something quite different. Try it this way:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:tmp="http://tempuri.org/"
exclude-result-prefixes="tmp">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/tmp:RecuperarCopiaResponse">
<ns0:RecuperarCopiaResponse xmlns:ns0="http://www.supergasbras.com.br/service/CtfAbastecimento">
<xsl:apply-templates/>
</ns0:RecuperarCopiaResponse>
</xsl:template>
<xsl:template match="tmp:RecuperarCopiaResult">
<RecuperarCopiaResult>
<xsl:value-of select="." disable-output-escaping="yes"/>
</RecuperarCopiaResult>
</xsl:template>
</xsl:stylesheet>

Replace strings XSLT

i'm stucked. Please help me with a little problem.
I have to change just two specific lines in XML file like this:
<?xml version="1.0" encoding="UTF-8"?>
<max:PublishTP_WORKORDER xmlns:max="http://www.ibm.com/maximo" creationDateTime="2014-04-11T10:43:51+04:00" transLanguage="RU" baseLanguage="EN" messageID="1397198631936413520" maximoVersion="7 5 20130829-1209 V7510--1" event="1">
<TP_WORKORDERSet xmlns="http://www.ibm.com/maximo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<WORKORDER action="Replace">
<ACTCOST xsi:nil="true"/>
<ACTFINISH xsi:nil="true"/>
<ACTINTLABCOST>0.0</ACTINTLABCOST>
<ACTINTLABHRS>0.0</ACTINTLABHRS>
<ACTLABCOST>0.0</ACTLABCOST>
<ACTLABHRS>0.0</ACTLABHRS>
<ACTMATCOST>0.0</ACTMATCOST>
<ACTOUTLABCOST>0.0</ACTOUTLABCOST>
<ACTOUTLABHRS>0.0</ACTOUTLABHRS>
<ACTSERVCOST>0.0</ACTSERVCOST>
<ACTSTART>2013-11-08T12:03:26+04:00</ACTSTART>
<ACTTOOLCOST>0.0</ACTTOOLCOST>
<ADDRESS/>
<AMCREW/>
<AMS>0</AMS>
<AOS>0</AOS>
...........................
<WORKORDERID>10</WORKORDERID>
<WORKPACKMTLSTATUS/>
<WORKTYPE/>
<WOSEQUENCE xsi:nil="true"/>
</WORKORDER>
</TP_WORKORDERSet>
</max:PublishTP_WORKORDER>
I need to replace "PublishTP_WORKORDER" with "Create_WORKORDER", both open and close tags.
It works fine with:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:max="http://www.ibm.com/maximo" version="1.0">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/max:PublishTP_WORKORDER">
<xsl:element name="max:CreateTP_WORKORDER">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
But in XML file it could be "PublishTP_WORKORDER2" or "PublishTP_WORKORDER3" and so on.
It should be changed to "CreateTP_WORKORDER2", "CreateTP_WORKORDER3" etc
And this XSLT scheme stops working. It's just doesn't recognize strings with added numeric symbols. How could i turn it out? Thanks in advance.
It's always root element
Then how about:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:max="http://www.ibm.com/maximo">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:variable name="suffix" select="substring-after(local-name(), 'PublishTP_WORKORDER')" />
<xsl:element name="max:CreateTP_WORKORDER{$suffix}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>

XSLT/XSL recursive nested elements

I need create a recursive transformation in XSL,
input xml
<root><foo1 /><foo2 /><foo3 /></root>
output
<root>
<foo1>
<foo2>
<foo3>
</foo3>
</foo2>
<foo1>
</root>
Thanks a lot for any help...
Try something like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" method="xml"/>
<xsl:template match="/root">
<xsl:copy>
<xsl:apply-templates select="*[1]" />
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:copy>
<xsl:apply-templates select="following-sibling::*[1]" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Which will generate the following output:
<?xml version="1.0"?>
<root>
<foo1>
<foo2>
<foo3/>
</foo2>
</foo1>
</root>

XSLT: Adding a node!

How do I encapsulate nodes around my XML blocks using XSLT?
For example, I have the following XML file.
<?xml version="1.0" encoding="iso-8859-1"?>
<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" omit-xml-declaration="yes" />
<xsl:template match="/">
<Root>
<VOBaseCollection>
<xsl:apply-templates select="Root/Location" />
</VOBaseCollection>
</Root>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
My input XML file looks like this.
<Root>
<Location><Name>Pennsylvania</Name><Type>State</Type></Location>
</Root>
I wish the output to look like this.
<Root><Container>
<Location><Name>Pennsylvania</Name><Type>State</Type></Location>
</Container>
</Root>
I wish to make sure that a node called <CONTAINER> gets applied every time, it copies over information from Root/Location. What changes do I need to do to my XSLT file?
Summarizing all the answers in comments, this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="#*|node()" name="identity">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Location">
<Container>
<xsl:call-template name="identity"/>
</Container>
</xsl:template>
</xsl:stylesheet>
Result:
<Root>
<Container>
<Location>
<Name>Pennsylvania</Name>
<Type>State</Type>
</Location>
</Container>
</Root>
I am just guessing, and in guess mode it seems that you want this:
EDIT: helped by another guess by Mads Hansen...
Add this to the identity template you already have:
<xsl:template match="Location">
<CONTAINER><xsl:apply-templates/></CONTAINER>
</xsl:template>