ReturnTrue if node is found - xslt-1.0

I need to form new element if specific node is found, but if that specific node is present several times i need to form only one element, My code is forming the aifound element twice.
<root>
<ai>
<i></i>
</ai>
<ai>
<i></i>
</ai>
</root>
output xml
<root>
<ai>
<i></i>
</ai>
<ai>
<i></i>
</ai>
<aifound>True</aifound>
</root>
My xslt
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ai">
<aifound>True</aifound>
</xsl:template>
</xsl:stylesheet>

Not clear whether you want to add aifound if there are no ais at all. The following xslt adds aifound only if ais exist.
<?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="#*|node()">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="root">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
<xsl:if test="//ai">
<aifound>True</aifound>
</xsl:if>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

How about 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="/root">
<xsl:copy>
<xsl:copy-of select="*"/>
<aifound>
<xsl:value-of select="boolean(ai)"/>
</aifound>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Related

Remove Namespace on Element

I want to remove namespace at output structure. I prepared XSLT code
but it gives namespace on this element
My Input XML is this.
<?xml version='1.0' encoding='UTF-8'?>
<n0:Messages xmlns:n0="http://sap.com/xi/XI">
<n0:Message>
<ContactData>
<Data>
<information>
<Name>A</Name>
<Phone>123456</Phone>
</information>
</Data>
</ContactData>
</n0:Message>
</n0:Messages>
XSLT CODE implemented
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:n0="http://sap.com/xi/XI" exclude-result-prefixes="n0">
<!-- Output -->
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<xsl:copy-of select= "//ContactData"/>
</xsl:template>
<xsl:template match="//*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Present output:
<?xml version='1.0' encoding='UTF-8'?>
<ContactData xmlns:n0="http://sap.com/xi/XI">
<Data>
<information>
<Name>A</Name>
<Phone>123456</Phone>
</information>
</Data>
</ContactData>
Output expected
<?xml version='1.0' encoding='UTF-8'?>
<ContactData>
<Data>
<information>
<Name>A</Name>
<Phone>123456</Phone>
</information>
</Data>
</ContactData>
Please help on this code
Thank you very much.
If you're able to use XSLT 2.0, you can achieve the required output simply by:
<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:strip-space elements="*"/>
<xsl:template match="/">
<xsl:copy-of select="*/*/*" copy-namespaces="no"/>
</xsl:template>
</xsl:stylesheet>
Demo: https://xsltfiddle.liberty-development.net/3NSSEuK
In XSLT 1.0, it takes a bit more work:
<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="/">
<xsl:apply-templates select="*/*/*" />
</xsl:template>
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Demo: https://xsltfiddle.liberty-development.net/3NSSEuK/1
Try this by using template * and # :
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:math="http://www.w3.org/2005/xpath-functions/math"
xmlns:n0="http://sap.com/xi/XI"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs math n0" version="1.0">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="#*">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates select="/*:Messages/ContactData"/>
</xsl:template>
</xsl:stylesheet>

xsl 1.0 muenchian sum to sum quantity field per compound key

For a SAP PO xsl 1.0 mapping I need to map an 'exotic' file coming from our warehouse where I need to group/sum per order (RECH) a QTY field on a combination of a line identifier (MASTER_PO_LINE_NO) and an Itemnumber (SKU). Each line (RECL) is in a header (RECH).
INPUT:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:Messages xmlns:ns0="http://sap.com/xi/XI/SplitAndMerge">
<ns0:Message1>
<MARCXML>
<RCIC>
<RECH>
<SALESID>24000</SALESID>
<RECL>
<LINE_NO>1</LINE_NO>
<QTY_EXPE>1592</QTY_EXPE>
<SKU>11207-210</SKU>
<MASTER_PO_LINE_NO>10</MASTER_PO_LINE_NO>
</RECL>
<RECL>
<LINE_NO>2</LINE_NO>
<QTY_EXPE>1000</QTY_EXPE>
<SKU>11207-210</SKU>
<MASTER_PO_LINE_NO>10</MASTER_PO_LINE_NO>
</RECL>
<RECL>
<LINE_NO>3</LINE_NO>
<QTY_EXPE>175</QTY_EXPE>
<SKU>11207-210</SKU>
<MASTER_PO_LINE_NO>20</MASTER_PO_LINE_NO>
</RECL>
</RECH>
<RECH>
<SALESID>25001</SALESID>
<RECL>
<LINE_NO>1</LINE_NO>
<QTY_EXPE>440</QTY_EXPE>
<SKU>20000-210</SKU>
<MASTER_PO_LINE_NO>10</MASTER_PO_LINE_NO>
</RECL>
</RECH>
</RCIC>
</MARCXML>
</ns0:Message1>
</ns0:Messages>
Required OUTPUT (Qty for 11207-210 = 1592 + 1000):
<?xml version="1.0" encoding="UTF-8"?>
<ns0:Messages xmlns:ns0="http://sap.com/xi/XI/SplitAndMerge">
<ns0:Message1>
<MARCXML>
<RCIC>
<RECH>
<SALESID>24000</SALESID>
<RECL>
<LINE_NO>1</LINE_NO>
<QTY_EXPE>2592</QTY_EXPE>
<SKU>11207-210</SKU>
<MASTER_PO_LINE_NO>10</MASTER_PO_LINE_NO>
</RECL>
<RECL>
<LINE_NO>3</LINE_NO>
<QTY_EXPE>175</QTY_EXPE>
<SKU>11207-210</SKU>
<MASTER_PO_LINE_NO>20</MASTER_PO_LINE_NO>
</RECL>
</RECH>
<RECH>
<SALESID>25001</SALESID>
<RECL>
<LINE_NO>1</LINE_NO>
<QTY_EXPE>440</QTY_EXPE>
<SKU>20000-210</SKU>
<MASTER_PO_LINE_NO>10</MASTER_PO_LINE_NO>
</RECL>
</RECH>
</RCIC>
</MARCXML>
</ns0:Message1>
</ns0:Messages>
There are plenty of examples about muenchian grouping but I didn't manage to solve my requirement yet. To be honest I got as far as:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:Messages xmlns:ns0="http://sap.com/xi/XI/SplitAndMerge">
<ns0:Message1>
<MARCXML>
<RCIC>
<RECH>5284</RECH>
<RECH>5284</RECH>
<RECH>5284</RECH>
</RCIC>
</MARCXML>
</ns0:Message1>
</ns0:Messages>
With the following xsl:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kMSTR_PO_LN_SKU" match="RECL"
use="concat(MASTER_PO_LINE_NO, '#', SKU)"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="RECH">
<xsl:copy>
<xsl:value-of select="sum(//MARCXML/RCIC/RECH/RECL/QTY_CHECKED_IN)" />
<xsl:for-each select="/RECL[generate-id()=generate-id(key('kMSTR_PO_LN_SKU',concat(MASTER_PO_LINE_NO,'-',SKU))[1])]">
<xsl:copy>
<SKU>
<xsl:value-of select="SKU"/>
</SKU>
<Quantity>
<xsl:value-of select="sum(key('kMSTR_PO_LN_SKU',concat(MASTER_PO_LINE_NO,'-',RECL))/QTY_EXPE)"/>
</Quantity>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Hope someone can help me on my way here
AFAICT, this produces the required output - but having only a single example with somewhat ambiguous rules it could be a coincidence. In any case, it should provide you with a starting point.
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:key name="k1" match="RECL" use="concat(MASTER_PO_LINE_NO, '|', SKU)"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="RECH">
<xsl:copy>
<xsl:copy-of select="SALESID"/>
<xsl:for-each select="RECL[generate-id()=generate-id(key('k1', concat(MASTER_PO_LINE_NO,'|',SKU))[1])]">
<xsl:copy>
<xsl:copy-of select="LINE_NO"/>
<QTY_EXPE>
<xsl:value-of select="sum(key('k1', concat(MASTER_PO_LINE_NO,'|',SKU))/QTY_EXPE)" />
</QTY_EXPE>
<xsl:copy-of select="SKU | MASTER_PO_LINE_NO"/>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Added:
In order to summarize every order (RECH) separately, you must add an identifier of the order to the key:
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:key name="k1" match="RECL" use="concat(MASTER_PO_LINE_NO, '|', SKU, '|', generate-id(..))"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="RECH">
<xsl:copy>
<xsl:copy-of select="SALESID"/>
<xsl:for-each select="RECL[generate-id()=generate-id(key('k1', concat(MASTER_PO_LINE_NO,'|',SKU, '|', generate-id(..)))[1])]">
<xsl:copy>
<xsl:copy-of select="LINE_NO"/>
<QTY_EXPE>
<xsl:value-of select="sum(key('k1', concat(MASTER_PO_LINE_NO,'|',SKU, '|', generate-id(..)))/QTY_EXPE)" />
</QTY_EXPE>
<xsl:copy-of select="SKU | MASTER_PO_LINE_NO"/>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

XSLT1.0 Remove repeat nodes

I am trying to apply Muenchian grouping to eliminate duplicate nodes, but I seem not to be able to make it work.
Input:
<GetUsersByRoleRspMsg>
<UserList>
<User>
<UserId>PPAUSER1</UserId>
<Email>xyz#gmail.com</Email>
</User>
<User>
<UserId>PPAUSER1</UserId>
<Email>xyz#gmail.com</Email>
</User>
</UserList>
</GetUsersByRoleRspMsg>
Required:
<GetUsersByRoleRspMsg>
<UserList>
<User>
<UserId>PPAUSER1</UserId>
<Email>xyz#gmail.com</Email>
</User>
</UserList>
</GetUsersByRoleRspMsg>
The critical part of my code is
<xsl:key name="userIdEmailPair"
match="User"
use="concat(UserId,' ',Email)"/>
<xsl:copy>
<xsl:for-each select="User[
count(. | key('userIdEmailPair',
concat(UserId,' ',Email))[1])
= 1]">
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:copy>
I don't see any problem with your grouping code. If you try:
<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="userIdEmailPair" match="User" use="concat(UserId,' ',Email)"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="UserList">
<xsl:copy>
<xsl:for-each select="User[count(. | key('userIdEmailPair',concat(UserId,' ',Email))[1]) = 1]">
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
you will get the result you're looking for.
Of course, you could shorten the last template to:
<xsl:template match="UserList">
<xsl:copy>
<xsl:copy-of select="User[count(. | key('userIdEmailPair',concat(UserId,' ',Email))[1]) = 1]"/>
</xsl:copy>
</xsl:template>

Remove Duplicates from a list XSLT 1.0 based on element value

I need help with writing a function in xslt 1.0 that I can pass a list and it'll return the list with duplicates removed. It needs to be a template that I can easily clone or modify for other lists since there are several lists that I want to be able to run this.
Here is one example:
Input list:
<Diagnoses>
<Code>444.4</Code>
<Code>959.99</Code>
<Code>524</Code>
<Code>444.4</Code>
</Diagnoses>
Desired Output, after the duplicate code value 444.4 is removed:
<Diagnoses>
<Code>444.4</Code>
<Code>959.99</Code>
<Code>524</Code>
</Diagnoses>
Here is what I have so far, but it doesn't seem to be working:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:math="http://exslt.org/math"
xmlns:exsl="http://exslt.org/common"
xmlns:data="http://example.com/data" version="1.0"
extension-element-prefixes="math exsl"
exclude-result-prefixes="math exsl data">
<xsl:output omit-xml-declaration="yes" />
<xsl:variable name="DiagnosesList" select="Diagnoses"/>
<Diagnoses>
<xsl:call-template name="DedupeLists">
<xsl:with-param name="Input" select = "exsl:node-set($DiagnosesList)" />
</xsl:call-template>
</Diagnoses>
<xsl:template name="DedupeLists">
<xsl:param name = "Input" />
<xsl:for-each select="$Input/*">
<xsl:if test="Code[not(preceding::Code)]">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
#lingamurthy,
Just
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:strip-space elements="*"/>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Code[. = preceding-sibling::Code]"/>
</xsl:stylesheet>
This is one way:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:strip-space elements="*"/>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Code[not(. = preceding-sibling::Code)]">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Code"/>
</xsl:stylesheet>

XSLT xmlns on root only

Want to add an xmlns declaration to the root only and I use this xml:
<Message>
</Message>
and xslt:
<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:template match="/">
<xsl:element name="Element1" namespace="http://www.blablabla.com">
<xsl:element name="Element2">
<xsl:element name="Element3">Hmm</xsl:element>
</xsl:element>
<xsl:element name="Element4">
</xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
The result is:
<Element1 xmlns="http://www.blablabla.com">
<Element2 xmlns="">
<Element3>Hmm</Element3>
</Element2>
<Element4 xmlns=""/>
</Element1>
I want only Element1 to have xmlns not the Element2, 4 or others.
I can make this way but that means to write for each element the variable name:
<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:variable name="vNS"
select="'http://www.blablabla'"/>
<xsl:template match="/">
<xsl:element name="Element1" namespace="{$vNS}">
<xsl:element name="Element2" namespace="{$vNS}">
<xsl:element name="Element3" namespace="{$vNS}">Hmm</xsl:element>
</xsl:element>
<xsl:element name="Element4" namespace="{$vNS}">
</xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
How to do it?
Thanks.
Using xsl:element with namespaces requires you to indicate for all elements in which namespace they belong. It is easier to avoid using xsl:element when dealing with namespaces.
For example:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:bla="http://www.blablabla.com">
<xsl:output method="xml" version="1.0" encoding="UTF-8" omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<bla:Element1>
<Element2>
<Element3>Hmm</Element3>
</Element2>
<Element4 />
</bla:Element1>
</xsl:template>
</xsl:stylesheet>
This will create the required namespace on only the first element. You just have to declare the namespace into the stylesheet declaration and then you can use this namespace directly in the elements. Also your XSLT is more readable when avoiding xsl:element.