Trying to expand data using XSLT - xslt

I am an admitted novice with XSLT and am looking for some direction. I have the follow (simplified) XML input data. I want to take the underlying data and apply it to AccountExternalSystemId or flatten it out.
<?xml version="1.0" ?>
<ns:CustomObject3WS_CustomObject3QueryPage_Output xmlns:ns="urn:crmondemand/ws/customobject3/10/2004">
<ns:LastPage>true</ns:LastPage>
<ListOfCustomObject3 xmlns="urn:/crmondemand/xml/customObject3">
<CustomObject3>
<AccountExternalSystemId>A000008351</AccountExternalSystemId>
<ListOfAccount>
<Account>
<AccountId>AAXA-H72YN</AccountId>
<ExternalSystemId>100000000002795</ExternalSystemId>
<Name>CATERPILLAR INC [100000000002795]</Name>
</Account>
<Account>
<AccountId>ADOA-3BAK0F</AccountId>
<ExternalSystemId>A000008351</ExternalSystemId>
<Name>CATERPILLAR</Name>
</Account>
</ListOfAccount>
</CustomObject3>
<CustomObject3>
<AccountExternalSystemId>100000000001059</AccountExternalSystemId>
<ListOfAccount>
<Account>
<AccountId>AAXA-H0B7N</AccountId>
<ExternalSystemId>100000000001059</ExternalSystemId>
<Name>SERV SA [100000000001059]</Name>
</Account>
</ListOfAccount>
</CustomObject3>
</ListOfCustomObject3>
I am applying the following XSL to the data:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xmlns:bpws="http://schemas.xmlsoap.org/ws/2003/03/business-process/">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<xsl:apply-templates select="*:CustomObject3WS_CustomObject3QueryPage_Output"/>
</xsl:template>
<xsl:template match="*:CustomObject3WS_CustomObject3QueryPage_Output">
<xsl:copy>
<xsl:apply-templates select="*:LastPage"/>
<xsl:apply-templates select="*:ListOfCustomObject3"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*:ListOfCustomObject3">
<xsl:copy>
<xsl:apply-templates select="*:CustomObject3"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*:CustomObject3">
<xsl:variable select="*:AccountExternalSystemId" name="AccountExternalSystemId"/>
<xsl:copy>
<xsl:for-each select="*:ListOfAccount/*:Account">
<xsl:element name="AccountId" namespace="urn:/crmondemand/xml/customObject3"><xsl:value-of select="substring(*:AccountId,1,15)"/></xsl:element>
<xsl:element name="AccountName" namespace="urn:/crmondemand/xml/customObject3"><xsl:value-of select="substring(*:Name,1,255)"/></xsl:element>
<xsl:element name="AccountExternalSystemId" namespace="urn:/crmondemand/xml/customObject3"><xsl:value-of
select="substring($AccountExternalSystemId,1,64)"/></xsl:element>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:copy>
<xsl:value-of select="."/>
</xsl:copy>
</xsl:template>
and here is my result (you can see that CustomObject3 is not properly ended (as there should be 2) in the first example. Not sure if my approach is the best way to accomplish what I need to do:
<?xml version="1.0" encoding="UTF-8"?>
<ns:CustomObject3WS_CustomObject3QueryPage_Output>
<ns:LastPage>true</ns:LastPage>
<ListOfCustomObject3 xmlns="urn:/crmondemand/xml/customObject3">
<CustomObject3>
<AccountId>AAXA-H72YN</AccountId>
<AccountName>CATERPILLAR INC [100000000002795]</AccountName>
<AccountExternalSystemId>A000008351</AccountExternalSystemId>
<AccountId>ADOA-3BAK0F</AccountId>
<AccountName>CATERPILLAR</AccountName>
<AccountExternalSystemId>A000008351</AccountExternalSystemId>
</CustomObject3>
<CustomObject3>
<AccountId>AAXA-H0B7N</AccountId>
<AccountName>SERV SA [100000000001059]</AccountName>
<AccountExternalSystemId>100000000001059</AccountExternalSystemId>
</CustomObject3>
</ListOfCustomObject3>
The desired output would be:
<?xml version="1.0" encoding="UTF-8"?>
<ns:CustomObject3WS_CustomObject3QueryPage_Output>
<ns:LastPage>true</ns:LastPage>
<ListOfCustomObject3 xmlns="urn:/crmondemand/xml/customObject3">
<CustomObject3>
<AccountId>AAXA-H72YN</AccountId>
<AccountName>CATERPILLAR INC [100000000002795]</AccountName>
<AccountExternalSystemId>A000008351</AccountExternalSystemId>
</CustomObject3>
<CustomObject3>
<AccountId>ADOA-3BAK0F</AccountId>
<AccountName>CATERPILLAR</AccountName>
<AccountExternalSystemId>A000008351</AccountExternalSystemId>
</CustomObject3>
<CustomObject3>
<AccountId>AAXA-H0B7N</AccountId>
<AccountName>SERV SA [100000000001059]</AccountName>
<AccountExternalSystemId>100000000001059</AccountExternalSystemId>
</CustomObject3>

Remark note that the output in your question presents a prefix (ns:) not bound to any namespace declaration.
Look at this direction:
where identity.xsl is the well known Identity Transformation
default namespaces are handled in a simpler way
produced output has the correct namespace declarations
[XSLT 2.0]
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xmlns="urn:/crmondemand/xml/customObject3"
xpath-default-namespace="urn:/crmondemand/xml/customObject3">
<xsl:output indent="yes"/>
<xsl:include href="identity.xsl"/>
<xsl:template match="CustomObject3">
<xsl:apply-templates select="ListOfAccount/Account"/>
</xsl:template>
<xsl:template match="Account">
<CustomObject3>
<xsl:apply-templates select="AccountId|Name"/>
<xsl:copy-of select="../../AccountExternalSystemId"/>
</CustomObject3>
</xsl:template>
<xsl:template match="Name">
<xsl:element name="Account{name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
gives wanted output (with correct namespace declarations):
<ns:CustomObject3WS_CustomObject3QueryPage_Output xmlns:ns="urn:crmondemand/ws/customobject3/10/2004">
<ns:LastPage>true</ns:LastPage>
<ListOfCustomObject3 xmlns="urn:/crmondemand/xml/customObject3">
<CustomObject3>
<AccountId>AAXA-H72YN</AccountId>
<AccountName>CATERPILLAR INC [100000000002795]</AccountName>
<AccountExternalSystemId>A000008351</AccountExternalSystemId>
</CustomObject3>
<CustomObject3>
<AccountId>ADOA-3BAK0F</AccountId>
<AccountName>CATERPILLAR</AccountName>
<AccountExternalSystemId>A000008351</AccountExternalSystemId>
</CustomObject3>
<CustomObject3>
<AccountId>AAXA-H0B7N</AccountId>
<AccountName>SERV SA [100000000001059]</AccountName>
<AccountExternalSystemId>100000000001059</AccountExternalSystemId>
</CustomObject3>
</ListOfCustomObject3>
</ns:CustomObject3WS_CustomObject3QueryPage_Output>

Related

I tried to remove specific element if its output is empty in XSLT

I am trying to remove Error Description element if it contains empty value using xslt. i tried lot of options but it does not work.
For example if inside Acknowledgement all the element get null then output get empty acknowledgement so I want remove acknowledgement element empty tag.
below is xml and xslt
<?xml version="1.0" encoding="UTF-8" ?>
<updateDocumentStatusResponse xmlns="http://xmlns.be/CommgrService_Message/v001">
<Acknowledgement>
<Result>SUCCESS</Result>
<ErrorCode>ErrorCode1375</ErrorCode>
<ErrorDescription></ErrorDescription>
</Acknowledgement>
</updateDocumentStatusResponse>
XSLT :
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:ns1="http://xmlns.be/CSM/v001" xmlns:mhdr="http://www.oracle.com/XSL/Transform/java/oracle.tip.mediator.service.common.functions.MediatorExtnFunction" xmlns:oraext="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.ExtFunc" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xp20="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.Xpath20" xmlns:xref="http://www.oracle.com/XSL/Transform/java/oracle.tip.xref.xpath.XRefXPathFunctions" xmlns:socket="http://www.oracle.com/XSL/Transform/java/oracle.tip.adapter.socket.ProtocolTranslator" xmlns:oracle-xsl-mapper="http://www.oracle.com/xsl/mapper/schemas" xmlns:dvm="http://www.oracle.com/XSL/Transform/java/oracle.tip.dvm.LookupValue" xmlns:oraxsl="http://www.oracle.com/XSL/Transform/java" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns0="http://xmlns.be/CommgrService_Message/v001" exclude-result-prefixes=" xsd oracle-xsl-mapper xsi xsl ns1 ns0 mhdr oraext xp20 xref socket dvm oraxsl"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
<oracle-xsl-mapper:schema>
<oracle-xsl-mapper:mapSources>
<oracle-xsl-mapper:source type="WSDL">
<oracle-xsl-mapper:schema location="../WSDLs/CommgrService_v001.wsdl"/>
<oracle-xsl-mapper:rootElement name="updateDocumentStatusResponse" namespace="http://xmlns.be/CommgrService_Message/v001"/>
</oracle-xsl-mapper:source>
</oracle-xsl-mapper:mapSources>
</oracle-xsl-mapper:schema>
<!--User Editing allowed BELOW this line - DO NOT DELETE THIS LINE-->
<xsl:template match="/">
<ns1:Output>
<ns1:CommunicationResponse>
<ns1:Acknowledgement>
<ns1:Result>
<xsl:value-of select="/ns0:updateDocumentStatusResponse/ns0:Acknowledgement/ns0:Result"/>
</ns1:Result>
<ns1:ErrorCode>
<xsl:value-of select="/ns0:updateDocumentStatusResponse/ns0:Acknowledgement/ns0:ErrorCode"/>
</ns1:ErrorCode>
<ns1:ErrorDescription>
<xsl:value-of select="/ns0:updateDocumentStatusResponse/ns0:Acknowledgement/ns0:ErrorDescription"/>
</ns1:ErrorDescription>
</ns1:Acknowledgement>
</ns1:CommunicationResponse>
</ns1:Output>
</xsl:template>
</xsl:stylesheet>
how can i achieve this?
I think this can be done if you're not thinking something big:
<xsl:if test="normalize-space(/ns0:updateDocumentStatusResponse/ns0:Acknowledgement/ns0:ErrorDescription) != ''">
<ns1:ErrorDescription>
<xsl:value-of select="/ns0:updateDocumentStatusResponse/ns0:Acknowledgement/ns0:ErrorDescription"/>
</ns1:ErrorDescription>
</xsl:if>
Edit:
One way to achieve this using extension function like exsl:node-set or msxsl:node-set to be able to further process a result tree fragment created in another template:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:ns1="http://xmlns.be/CSM/v001" xmlns:mhdr="http://www.oracle.com/XSL/Transform/java/oracle.tip.mediator.service.common.functions.MediatorExtnFunction" xmlns:oraext="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.ExtFunc" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xp20="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.Xpath20" xmlns:xref="http://www.oracle.com/XSL/Transform/java/oracle.tip.xref.xpath.XRefXPathFunctions" xmlns:socket="http://www.oracle.com/XSL/Transform/java/oracle.tip.adapter.socket.ProtocolTranslator" xmlns:oracle-xsl-mapper="http://www.oracle.com/xsl/mapper/schemas" xmlns:dvm="http://www.oracle.com/XSL/Transform/java/oracle.tip.dvm.LookupValue" xmlns:oraxsl="http://www.oracle.com/XSL/Transform/java" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns0="http://xmlns.be/CommgrService_Message/v001" exclude-result-prefixes=" xsd oracle-xsl-mapper xsi xsl ns1 ns0 mhdr oraext xp20 xref socket dvm oraxsl"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
<oracle-xsl-mapper:schema>
<oracle-xsl-mapper:mapSources>
<oracle-xsl-mapper:source type="WSDL">
<oracle-xsl-mapper:schema location="../WSDLs/CommgrService_v001.wsdl" />
<oracle-xsl-mapper:rootElement name="updateDocumentStatusResponse" namespace="http://xmlns.be/CommgrService_Message/v001" />
</oracle-xsl-mapper:source>
</oracle-xsl-mapper:mapSources>
</oracle-xsl-mapper:schema>
<!--User Editing allowed BELOW this line - DO NOT DELETE THIS LINE -->
<xsl:template match="/">
<xsl:variable name="result">
<ns1:Output>
<ns1:CommunicationResponse>
<ns1:Acknowledgement>
<ns1:Result>
<xsl:value-of select="/ns0:updateDocumentStatusResponse/ns0:Acknowledgement/ns0:Result" />
</ns1:Result>
<ns1:ErrorCode>
<xsl:value-of select="/ns0:updateDocumentStatusResponse/ns0:Acknowledgement/ns0:ErrorCode" />
</ns1:ErrorCode>
<ns1:ErrorDescription>
<xsl:value-of select="/ns0:updateDocumentStatusResponse/ns0:Acknowledgement/ns0:ErrorDescription" />
</ns1:ErrorDescription>
</ns1:Acknowledgement>
</ns1:CommunicationResponse>
</ns1:Output>
</xsl:variable>
<xsl:apply-templates select="exsl:node-set($result)/*" mode="step2" />
</xsl:template>
<xsl:template match="*[not(normalize-space())]" mode="step2" />
<xsl:template match="#* | node()" mode="step2">
<xsl:copy>
<xsl:apply-templates select="#* | node()" mode="step2" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
http://xsltransform.net/93wkLHW
If you take a top-down aproach with literal result elements you are always going to get that element in the output. Your only choice then is to chain two transformations or use a two pass transformation.
The XPath expression to know whether the current node is empty (true) or not (false) is:
"not(node())"
For example, this input document
<?xml version="1.0" encoding="UTF-8" ?>
<updateDocumentStatusResponse xmlns="http://xmlns.be/CommgrService_Message/v001">
<Acknowledgement>
<Result>SUCCESS</Result>
<ErrorCode>ErrorCode1375</ErrorCode>
<ErrorDescription></ErrorDescription>
</Acknowledgement>
</updateDocumentStatusResponse>
With this transformation
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements ="*"/>
<xsl:output indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[not(node())]"/>
</xsl:stylesheet>
Output
<updateDocumentStatusResponse xmlns="http://xmlns.be/CommgrService_Message/v001">
<Acknowledgement>
<Result>SUCCESS</Result>
<ErrorCode>ErrorCode1375</ErrorCode>
</Acknowledgement>
</updateDocumentStatusResponse>
Edit
The previus stylesheet is general. If you want to target specific elements, you need to match those elements with the pattern in the template. Example:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements ="*"/>
<xsl:output indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ErrorDescription[not(node())]"/>
</xsl:stylesheet>

Need to remove the prefix namespace in the output using XSLT

Is there any way on how to avoid populating the namespace of common: prefix in the output? And, this namespace should replace as a default namespace. I have this sample file:
INPUT:
<IntraConsignment xmlns="http://www.minfin.fgov.be/IntraConsignment" xmlns:common="http://www.minfin.fgov.be/InputCommon" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.minfin.fgov.be/IntraConsignment NewICO-in_v0_7.xsd" IntraListingsNbr="1">
<Representative>
<common:RepresentativeID identificationType="NVAT" issuedBy="BE">9876941603</common:RepresentativeID>
</Representative>
<IntraListing SequenceNumber="1" ClientsNbr="1" AmountSum="1000.00">
<Declarant>
<common:VATNumber>9876941603</common:VATNumber>
</Declarant>
<Period>
<Month>07</Month>
</Period>
<IntraClient SequenceNumber="1">
<CompanyVATNumber issuedBy="DE">123456</CompanyVATNumber>
</IntraClient>
</IntraListing>
</IntraConsignment>
And, I need to remove the common: prefix in the XML and the elements that doesn't have a prefix should have a ns2: prefix.
I have this stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns2="http://www.minfin.fgov.be/IntraConsignment" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:common="http://www.minfin.fgov.be/InputCommon" xmlns="http://www.minfin.fgov.be/InputCommon" exclude-result-prefixes="xs xsi xsl common">
<xsl:output encoding="UTF-8" indent="yes" method="xml" version="1.0"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="common:*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="*">
<xsl:element name="ns2:{local-name()}">
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
My xslt is quite working but the namespace xmlns="http://www.minfin.fgov.be/InputCommon" did not appear in the output.
GENERATED OUTPUT:
<ns2:IntraConsignment xmlns:ns2="http://www.minfin.fgov.be/IntraConsignment" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.minfin.fgov.be/IntraConsignment NewICO-in_v0_7.xsd" IntraListingsNbr="1">
<ns2:Representative>
<RepresentativeID xmlns="http://www.minfin.fgov.be/InputCommon" identificationType="NVAT" issuedBy="BE">9876941603</RepresentativeID>
</ns2:Representative>
<ns2:IntraListing SequenceNumber="1" ClientsNbr="1" AmountSum="1000.00">
<ns2:Declarant>
<VATNumber xmlns="http://www.minfin.fgov.be/InputCommon">9876941603</VATNumber>
</ns2:Declarant>
<ns2:Period>
<ns2:Month>07</ns2:Month>
</ns2:Period>
<ns2:IntraClient SequenceNumber="1">
<ns2:CompanyVATNumber issuedBy="DE">123456</ns2:CompanyVATNumber>
<ns2:Amount>1000.00</ns2:Amount>
</ns2:IntraClient>
</ns2:IntraListing>
</ns2:IntraConsignment>
EXPECTED:
<ns2:IntraConsignment xmlns:ns2="http://www.minfin.fgov.be/InputCommon" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.minfin.fgov.be/IntraConsignment NewICO-in_v0_7.xsd" xmlns="http://www.minfin.fgov.be/InputCommon" IntraListingsNbr="1">
<ns2:Representative>
<RepresentativeID identificationType="NVAT" issuedBy="BE">9876941603</RepresentativeID>
</ns2:Representative>
<ns2:IntraListing SequenceNumber="1" ClientsNbr="1" AmountSum="1000.00">
<ns2:Declarant>
<VATNumber>9876941603</VATNumber>
</ns2:Declarant>
<ns2:Period>
<ns2:Month>07</ns2:Month>
</ns2:Period>
<ns2:IntraClient SequenceNumber="1">
<ns2:CompanyVATNumber issuedBy="DE">123456</ns2:CompanyVATNumber>
<ns2:Amount>1000.00</ns2:Amount>
</ns2:IntraClient>
</ns2:IntraListing>
</ns2:IntraConsignment>
With XSLT 1.0 the choice of namespace prefixes is left largely to the XSLT processor, whereas XSLT 2.0 is much more prescriptive. However, most XSLT 1.0 processors do the "right thing" even though they're not strictly required to.
You basically seem to have two rules:
(a) elements in namespace http://www.minfin.fgov.be/IntraConsignment should be copied using the prefix ns2:
<xsl:template match="x:*" xmlns:x="http://www.minfin.fgov.be/IntraConsignment">
<xsl:element name="ns2:{local-name()}" namespace="http://www.minfin.fgov.be/IntraConsignment">
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
(b) elements in namespace http://www.minfin.fgov.be/InputCommon should be output in the default namespace:
<xsl:template match="x:*" xmlns:x="http://www.minfin.fgov.be/InputCommon">
<xsl:element name="{local-name()}" namespace="http://www.minfin.fgov.be/InputCommon">
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>

XSLT - Copy all nodes and add extra node in copied nodes

I am looking for XSLT(1.0) code for below input and output XML.
In output XML, there can be any child node under C6 element. In below XML, i have put CN element but it could be any name.
Input XML -
<?xml version = "1.0" encoding = "UTF-8"?>
<root>
<input>
<c2>
<c3>
<c4>c4</c4>
</c3>
</c2>
</input>
<output>
<c5>
<c6>
<CN>
<T1></T1>
<T2></T2>
</CN>
</c6>
<c6>
<CN>
<T1></T1>
<T2></T2>
</CN>
</c6>
</c5>
</output>
</root>
Desired Output XML-
<root>
<output>
<c5>
<c6>
<!-- It could have any child node. Putting an example with CN child node name.-->
<CN>
<T1></T1>
<T2></T2>
<c3>
<c4>c4</c4>
<NewNode>current number of CN node which will be 1</NewNode>
<NewNode1>total number of C6 nodes which will be 2.</NewNode1>
</c3>
</CN>
</c6>
<c6>
<CN>
<T1></T1>
<T2></T2>
<c3>
<c4>c4</c4>
<NewNode>current number of CN node which will be 2</NewNode>
<NewNode1>total number of C6 nodes which will be 2.</NewNode1>
</c3>
</CN>
</c6>
</c5>
</output>
</root>
Thank you in advance.
Use the following stylesheet:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:template match="c6/*">
<xsl:copy>
<xsl:variable name="v1" select="count(../preceding-sibling::*)+1"/>
<xsl:variable name="v2" select="count(../../*)"/>
<xsl:apply-templates/>
<xsl:apply-templates select="../../../../input/c2/c3">
<xsl:with-param name="v1" select="$v1"/>
<xsl:with-param name="v2" select="$v2"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="c3">
<xsl:param name="v1"/>
<xsl:param name="v2"/>
<xsl:copy>
<xsl:apply-templates/>
<NewNode><xsl:value-of select="$v1"/></NewNode>
<NewNode1><xsl:value-of select="$v2"/></NewNode1>
</xsl:copy>
</xsl:template>
<xsl:template match="input"/>
<xsl:template match="#*|node()">
<xsl:copy><xsl:apply-templates select="#*|node()"/></xsl:copy>
</xsl:template>
</xsl:stylesheet>

XSLT2.0 giving empty output when Input XML is having namespcaes

The requirement is to find the duplicate element(BaseName) in XML and marked the parent element(Account) with isDuplicate attribute. The XSL is working correctly when the input XML RootElement has no namespaces. When the root element has namespace then I get empty object. I am not sure why the namespace is causing XSL to generate empty output. Any help to get the right output would be greatly appreciated.`
Input XML WITH NAMESPACE
<?xml version="1.0"?>
<objects xmlns="urn:s.sexmaple.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Account>
<Id>001A00F</Id>
<RecordTypeId>012A00</RecordTypeId>
<BaseName>EFGH</BaseName>
</Account>
<Account>
<Id>001A0</Id>
<RecordTypeId>012A0</RecordTypeId>
<BaseName>ABCD</BaseName>
</Account>
<Account>
<Id>001A</Id>
<RecordTypeId>012A</RecordTypeId>
<BaseName>ABCD</BaseName>
</Account>
</objects>
XSL
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml"
version="1.0"
encoding="UTF-8"
indent="yes"/>
<xsl:strip-space elements="*" />
<xsl:template match="node()|#*">
<xsl:copy copy-namespaces="no">
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:variable name="Accounts">
<objects>
<xsl:for-each select="//Account">
<xsl:sort select="BaseName" />
<xsl:apply-templates select="." />
</xsl:for-each>
</objects>
</xsl:variable>
<xsl:variable name="unqentity">
<objects>
<xsl:for-each select="$Accounts/objects/Account">
<xsl:choose>
<xsl:when test="not(following-sibling::Account/BaseName=./BaseName) and not(preceding-sibling::Account/BaseName=./BaseName) ">
<xsl:copy-of select="." />
</xsl:when>
<xsl:otherwise>
<Account>
<xsl:attribute name="isDuplicate">yes</xsl:attribute>
<xsl:for-each select="child::*">
<xsl:element name="{name()}">
<xsl:copy-of select="#*|node()" />
</xsl:element>
</xsl:for-each>
</Account>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</objects>
</xsl:variable>
<xsl:copy-of select="$unqentity" />
</xsl:template>
</xsl:stylesheet>
Output XML WHEN INPUT XML HAS NAMESPACE
<?xml version="1.0" encoding="UTF-8"?>
<objects/>
Output XML when Input has no Namespaces
<?xml version="1.0" encoding="UTF-8"?>
<objects>
<Account>
<Id>001A00F</Id>
<RecordTypeId>012A00</RecordTypeId>
<BaseName>EFGH</BaseName>
</Account>
<Account isDuplicate="yes">
<Id>001A0</Id>
<RecordTypeId>012A0</RecordTypeId>
<BaseName>ABCD</BaseName>
</Account>
<Account isDuplicate="yes">
<Id>001A</Id>
<RecordTypeId>012A</RecordTypeId>
<BaseName>ABCD</BaseName>
</Account>
</objects>
When you have a namespace, it means the element within the namespace is not the same as an element without a namespace (or indeed an element in a different name space).
This means when you do this in your XSLT...
<xsl:for-each select="//Account">
You are looking for an Account element with no namespace, and so it will not match the Account element in your source XML, which is in the amusingly titled "urn:s.sexmaple.com" (which I suspect is a misspelling)
As you are using XSLT2.0 though, there is a simple way to get around this, by specifying a default namespace for any xpath expressions, using the xpath-default-namespace. Normally, this may be enough, but you have slightly complicated matters by creating new elements within a variable, which you then later try to select.
<xsl:for-each select="$Accounts/objects/Account">
This means when you create the objects and Account elements in the $Accounts variable, they will need to be part of the namespace too.
To cut to the chase, this is what your xsl:stylesheet element needs to look like
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xmlns="urn:s.sexmaple.com"
xpath-default-namespace="urn:s.sexmaple.com">
So, the xpath-default-namespace="urn:s.sexmaple.com" is used to match elements in your source XML, whilst the xmlns="urn:s.sexmaple.com" is used to ensure the elements you create in the variable have this namespace and can be matched later on.
Having said all that, you have rather over-complicated your whole XSLT. Are you simply trying to add an IsDuplicate attribute to Account elements with the same BaseName? Well, create a key to look up duplicates, like so
<xsl:key name="account" match="Account" use="BaseName" />
Then you can look up duplicates like so:
<xsl:if test="key('account', BaseName)[2]">
<xsl:attribute name="isDuplicate">Yes</xsl:attribute>
</xsl:if>
Try this XSLT, which should give the same results
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xpath-default-namespace="urn:s.sexmaple.com">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="account" match="Account" use="BaseName" />
<xsl:template match="Account">
<xsl:copy>
<xsl:if test="key('account', BaseName)[2]">
<xsl:attribute name="isDuplicate">Yes</xsl:attribute>
</xsl:if>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Note how this only needs to use xpath-default-namespace as it is not creating whole new elements, just copying existing ones (which get their namespace copied across too).
The reason your input XML without a namespace works were the one with a namespace doesn't, isn't because of the input XML, but because of the XSLT stylesheet.
When your XML file has a default namespace, that namespace will need to be declared in the stylesheet itself.
For example, with the following XML:
<test xmlns="test.xml.schema">
<element>Content</element>
</test>
When I apply the following XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="/">
<out>
<namespace>None</namespace>
<xsl:apply-templates />
</out>
</xsl:template>
<xsl:template match="test">
<test out="True">hello</test>
</xsl:template>
<xsl:template match="*"/>
</xsl:stylesheet>
The output is just:
<out><namespace>None</namespace></out>
The <xsl:template match="test"> can't match on the test element in the input xml, as it is actually test.xml.schema:test while the match in the stylesheet, with no namespace is actually :test. Thus no match is possible.
However, when we just add a namespace for the input document adn modify the template, like so:
<xsl:stylesheet xmlns:t="test.xml.schema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="/">
<out>
<namespace>test.xml.schema</namespace>
<xsl:apply-templates />
</out>
</xsl:template>
<xsl:template match="t:test">
<test out="True">hello</test>
</xsl:template>
<xsl:template match="*"/>
</xsl:stylesheet>
The output becomes:
<out xmlns:t="test.xml.schema">
<namespace>test.xml.schema</namespace>
<test out="True">hello</test>
</out>
Its important to note that the namespace abbreviation in the input document and XSL don't need to be the same (eg. blank vs. "t"), but the namespaces themselfs do: (e.g both blank and "t" must be bound to test.xml.schema).
Also note, that using a default namespace in XSLT can be fraught with issues. So its best to use declared namespaces in XSLT.

XSLT: Add namespace to root element

I need to change namespaces in the root element as follows:
input document:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<foo xsi:schemaLocation="urn:isbn:1-931666-22-9 http://www.loc.gov/ead/ead.xsd"
xmlns:ns2="http://www.w3.org/1999/xlink" xmlns="urn:isbn:1-931666-22-9"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
desired output:
<foo audience="external" xsi:schemaLocation="urn:isbn:1-931666-22-9
http://www.loc.gov/ead/ead.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-
instance" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="urn:isbn:1-931666-22-9">
I was trying to do it as I copy over the whole document and before I give any other transformation instructions, but the following doesn't work:
<xsl:template match="* | processing-instruction() | comment()">
<xsl:copy copy-namespaces="no">
<xsl:for-each select=".">
<xsl:attribute name="audience" select="'external'"/>
<xsl:namespace name="xlink" select="'http://www.w3.org/1999/xlink'"/>
</xsl:for-each>
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
Thanks for any advice!
XSLT 2.0 isn't necessary to solve this problem.
Here is an XSLT 1.0 solution, which works equally well as XSLT 2.0 (just change the version attribute to 2.0):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xlink="http://www.w3.org/1999/xlink"
exclude-result-prefixes="xlink"
>
<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="/*">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:copy-of select=
"namespace::*
[not(name()='ns2')
and
not(name()='')
]"/>
<xsl:copy-of select=
"document('')/*/namespace::*[name()='xlink']"/>
<xsl:copy-of select="#*"/>
<xsl:attribute name="audience">external</xsl:attribute>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
When the above transformation is applied on this XML document:
<foo
xsi:schemaLocation="urn:isbn:1-931666-22-9 http://www.loc.gov/ead/ead.xsd"
xmlns:ns2="http://www.w3.org/1999/xlink"
xmlns="urn:isbn:1-931666-22-9"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
the wanted result is produced:
<foo xmlns="urn:isbn:1-931666-22-9"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xlink="http://www.w3.org/1999/xlink"
xsi:schemaLocation="urn:isbn:1-931666-22-9 http://www.loc.gov/ead/ead.xsd"
audience="external"/>
You should really be using the "identity template" for this, and you should always have it on hand. Create an XSLT with that template, call it "identity.xslt", then into the current XSLT. Assume the prefix "bad" for the namespace you want to replace, and "good" for the one you want to replace it with, then all you need is a template like this (I'm at work, so forgive the formatting; I'll get back to this when I'm at home): ... If that doesn't work in XSLT 1.0, use a match expression like "*[namespace-uri() = 'urn:bad-namespace'", and follow Dimitre's instructions for creating a new element programmatically. Within , you really need to just apply-template recursively...but really, read up on the identity template.