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>
Related
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>
I updated the provided xslt to accept a param "multiplexpaths" from my source and assignin enter code here`g this to nodes variable in xslt to below:
<?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:param name="multiplexpaths" as="xs:string" static="yes" />
<!-- xsl:param name="copy" as="xs:string" static="yes" select="'//other[. = 1345], //more[. = 2]'"/-->
<xsl:variable name="nodes" _select="{$multiplexpaths}"/>
<xsl:variable name="ancestors" select="$nodes/ancestor::*"/>
<xsl:mode on-no-match="shallow-skip"/>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="$nodes">
<xsl:sequence select="."/>
</xsl:template>
<xsl:template match="$ancestors">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
For the xsl:evaluate example, you would need to use Saxon 10 HE, 9.9 HE doesn't support xsl:evaluate.
As for setting a static parameter with Saxon 9.9 and s9api, a simple example is
String paths = "catalog/cd1,catalog/Test/value/a1";
Processor processor = new Processor(true);
XsltCompiler xsltCompiler = processor.newXsltCompiler();
xsltCompiler.setParameter(new QName("copy"), new XdmAtomicValue(paths));
XsltExecutable xsltExecutable = xsltCompiler.compile(new StreamSource("static-param-example2.xsl"));
Xslt30Transformer xslt30Transformer = xsltExecutable.load30();
xslt30Transformer.transform(new StreamSource("input-sample1.xml"), xslt30Transformer.newSerializer(System.out));
with the input-sample1.xml being
<catalog>
<cd1>
<name>abc</name>
<Stext>1234</Stext>
<Tag>uuuu</Tag>
</cd1>
<cd2>
<name>abc</name>
<Stext>1234</Stext>
<Tag>uuuu</Tag>
</cd2>
<Test>
<value>
<a1>123</a1>
<b1>77474</b1>
</value>
</Test>
</catalog>
and the XSLT
<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:param name="copy" as="xs:string" static="yes" select="'()'"/>
<xsl:variable name="nodes" _select="{$copy}"/>
<xsl:variable name="ancestors" select="$nodes/ancestor::*"/>
<xsl:mode on-no-match="shallow-skip"/>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="$nodes">
<xsl:sequence select="."/>
</xsl:template>
<xsl:template match="$ancestors">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
this outputs
<catalog>
<cd1>
<name>abc</name>
<Stext>1234</Stext>
<Tag>uuuu</Tag>
</cd1>
<Test>
<value>
<a1>123</a1>
</value>
</Test>
</catalog>
So the main point is to set the parameter on the XsltCompiler and not after compilation on the transformer.
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>
The XML I am expecting is supposed to be only one url/urn (xmlns:urn="urn:123:456") like below:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:urn="urn:123:456"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl" exclude-result-prefixes="urn">
When used with the below namespace it's OK:
<Document xmlns="123:456" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
But recently I am receiving a different document with the same structure as before, only difference is the namespace like below:
<Document xmlns="789:123" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
My question is, is there any way that I can support both in the same XSLT file
Below is a sample of my XSLT file:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:urn="urn:123:456"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl" exclude-result-prefixes="urn">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/urn:Document">
<Profiles>
<xsl:apply-templates select="*"/>
</Profiles>
</xsl:template>
<xsl:template match="urn:File">
<File>
<FileId>
<xsl:value-of select="urn:Id"/>
</FileId>
<FileDate>
<xsl:value-of select="urn:Created"/>
</FileDate>
</File>
</xsl:template>
<xsl:template match="urn:Employee">
<Information>
<EmpName>
<xsl:value-of select="urn:Personal/Name"/>
</EmpName>
<Age>
<xsl:value-of select="urn:Personal/Age"/>
</Age>
.
.
.
</Information>
</xsl:template>
</xsl:stylesheet>
You could declare both namespaces, e.g.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns1="urn:123:456"
xmlns:ns2="urn:789:123"
exclude-result-prefixes="ns1 ns2">
Then use a union expression for your matches and selections, for example:
<xsl:template match="/ns1:Document | /ns2:Document">
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.