Newbie to this site and using xslt but running into a roadblock transforming a SSRS 2008v2 rendered xml file into another XSL raw format for a 3rd Party EDI transfer. I've been searching this site and others for a while now, but struggling putting it all together.I'm starting with the following raw xml data;
<Invoices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.spscommerce.com/RSX" xsi:schemaLocation="http://www.spscommerce.com/RSX http://tfl- sql01/ReportServer_SQL2012? %2FTesting%2FINTest&rs%3ACommand=Render&rs%3AFormat=XML&rs%3ASessionID= jn5ugdirg4m02nmodnm0hynq&rc%3ASchema=True" Name="INTest">
<Invoices1> ***need to remove***
<ivhID_Collection> ***need to remove***
<Invoices>...</Invoices>
<Invoices>...</Invoices>
<Invoices>
<Invoice>
<Header1>
<InvoiceHeader>...</InvoiceHeader>
<PaymentTerms>...</PaymentTerms>
<Dates>...</Dates>
<Address>...</Address>
<References>...</References>
<ChargesAllowances>...</ChargesAllowances>
<LineItem_Collection> ***need to remove and replace with </Header>***
<LineItem>
<InvoiceLine>...</InvoiceLine>
<ProductOrItemDescription>...</ProductOrItemDescription>
</LineItem>
<LineItem>
<InvoiceLine>...</InvoiceLine>
<ProductOrItemDescription>...</ProductOrItemDescription>
</LineItem>
</LineItem_Collection> ***need to remove***
<Summary>...</Summary>
</Header1> ***need to remove***
</Invoice>
</Invoices>
<Invoices>...</Invoices>
<Invoices>...</Invoices>
<Invoices>...</Invoices>
/ivhID_Collection> ***need to remove***
</Invoices1> ***need to remove***
</Invoices>
Trying to get it in this structure instead;
<Invoices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.spscommerce.com/RSX" xsi:schemaLocation="http://www.spscommerce.com/RSX http://tfl-sql01/ReportServer_SQL2012?%2FTesting%2FINDoItBest%20v7&rs%3ACommand=Render&rs%3AFormat=XML&rs%3ASessionID=jn5ugdirg4m02nmodnm0hynq&rc%3ASchema=True" Name="INDoItBest v7">
<Invoices>...</Invoices>
<Invoices>...</Invoices>
<Invoices>
<Invoice>
<Header>
<InvoiceHeader>...</InvoiceHeader>
<PaymentTerms>...</PaymentTerms>
<Dates>...</Dates>
<Address>...</Address>
<References>...</References>
<ChargesAllowances>...</ChargesAllowances>
</Header>
<LineItem>
<InvoiceLine>...</InvoiceLine>
<ProductOrItemDescription>...</ProductOrItemDescription>
</LineItem>
<LineItem>
<InvoiceLine>...</InvoiceLine>
<ProductOrItemDescription>...</ProductOrItemDescription>
</LineItem>
<Summary>...</Summary>
</Invoice>
</Invoices>
<Invoices>...</Invoices>
<Invoices>...</Invoices>
<Invoices>...</Invoices>
</Invoices>
I made some progress using this style sheet, but am stuck on the regrouping of the Header tag and the display of the element namespace.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:t="http://www.spscommerce.com/RSX"
exclude-result-prefixes="t">
<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>
<!--rule to suppress the undesired nodes-->
<xsl:template match="t:Invoices1|t:ivhID_Collection">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="t:LineItem_Collection">
<xsl:apply-templates/>
</xsl:template>
<!--<xsl:template match="t:Invoice/t:Header1">
<xsl:apply-templates/>
</xsl:template>-->
<!-- Identity Transform -->
<xsl:template match="t:Header1">
<xsl:copy>
<xsl:element name="Header">
<xsl:apply-templates select="#*|t:InvoiceHeader|t:PaymentTerms|t:Dates|t:Address|t:References|t:ChargesAllowances"/>
</xsl:element>
<xsl:apply-templates select="#*|t:LineItem_Collection|t:Summary"/>
</xsl:copy>
</xsl:template>
<!-- Had to comment out -->
<!--<xsl:template match="t:Invoice/t:Header1">
<xsl:apply-templates/>
</xsl:template>-->
The stylesheet produced most of what I needed, but failed when I tried to remove the Header1 tag (code commented out). Also, struggling to understand why "exclude-result-prefixes" isn't working to remove the namespace from the new xml file.
<Invoices xmlns="http://www.spscommerce.com/RSX" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.spscommerce.com/RSX http://tfl-sql01/ReportServer_SQL2012?%2FTesting%2FINDoItBest%20v7&rs%3ACommand=Render&rs%3AFormat=XML&rs%3ASessionID=jn5ugdirg4m02nmodnm0hynq&rc%3ASchema=True" Name="INDoItBest v7">
<Invoices>...</Invoices>
<Invoices>...</Invoices>
<Invoices>
<Invoice>
<Header1>
<Header xmlns="">
<InvoiceHeader xmlns="http://www.spscommerce.com/RSX">... </InvoiceHeader>
<PaymentTerms xmlns="http://www.spscommerce.com/RSX">... </PaymentTerms>
<Dates xmlns="http://www.spscommerce.com/RSX">...</Dates>
<Address xmlns="http://www.spscommerce.com/RSX">...</Address>
<References xmlns="http://www.spscommerce.com/RSX">...</References>
<ChargesAllowances xmlns="http://www.spscommerce.com/RSX">... </ChargesAllowances>
</Header>
<LineItem>
<InvoiceLine>...</InvoiceLine>
<ProductOrItemDescription>...</ProductOrItemDescription>
</LineItem>
<LineItem>
<InvoiceLine>...</InvoiceLine>
<ProductOrItemDescription>...</ProductOrItemDescription>
</LineItem>
<Summary>
<TotalAmount>756.8400</TotalAmount>
<TotalSalesAmount>727.1600</TotalSalesAmount>
<TotalLineItemNumber>2</TotalLineItemNumber>
</Summary>
</Header1>
</Invoice>
</Invoices>
<Invoices>...</Invoices>
<Invoices>...</Invoices>
<Invoices>...</Invoices>
</Invoices>
Any advice or other options would be greatly appreciated!
You've already got a template matching t:Header1 in your XSLT, so you shouldn't add another one matching it, as only one can apply. (In your case, if you did add a template matching t:Invoice\t:Header1 then because of the parent being specified, it would have a higher priority as the one just matching t:Header1 and be used instead).
What you will need to do, is put all the logic in the single template. In this case, all you need to do is remove the xsl:copy from that template to avoid the Header1 being copied to the output tree. Additionally, when you create Header, you are creating it in no namespace, not in the namespace bound to the prefix "t". Therefore, the child elements will be given new namespace declarations because they will still be in that namespace.
One way to do it is simply add a "namespace" attribute to the xsl:element, like so:
<xsl:element name="Header" namespace="http://www.spscommerce.com/RSX">
Alternatively, you can create the element by just doing <Header> but you will need to add a default namespace declaration to the XSLT too, to ensure it gets output in the correct namespace.
Try this XSLT:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:t="http://www.spscommerce.com/RSX"
xmlns="http://www.spscommerce.com/RSX"
exclude-result-prefixes="t">
<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>
<!--rule to suppress the undesired nodes-->
<xsl:template match="t:Invoices1|t:ivhID_Collection">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="t:LineItem_Collection">
<xsl:apply-templates/>
</xsl:template>
<!-- Identity Transform -->
<xsl:template match="t:Header1">
<xsl:apply-templates select="#*" />
<Header>
<xsl:apply-templates select="#*|t:InvoiceHeader|t:PaymentTerms|t:Dates|t:Address|t:References|t:ChargesAllowances"/>
</Header>
<xsl:apply-templates select="t:LineItem_Collection|t:Summary"/>
</xsl:template>
</xsl:stylesheet>
As a side note, in your XSLT you were also doing this immediately after creating the Header element
<xsl:apply-templates select="#*|t:LineItem_Collection|t:Summary"/>
This would fail if the Header1 had attributes you wanted to copy, as it is an error to try to add attributes to a parent element after you have created child elements. This is why in my XSLT I have split the statement into two.
Related
Using XSLT 2 how can I skip and not touch a record if a field contains text, in this case a date? I want to only process all the record that don't have a <SurveyDate> and don't touch record that already have a <SurveyDate>.
I tried using a choose statement with a test of "not(SurveyDate/text())" but this is not working. here is my complete XSL code:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xmlns:lookup="lookup" xmlns:exsl="http://exslt.org/common" exclude-result-prefixes="lookup exsl">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes" encoding="utf-8" media-type="xml/plain" />
<xsl:strip-space elements="*" />
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*" />
</xsl:copy>
</xsl:template>
<xsl:template match="Sub">
<!-- This is the final output -->
<xsl:choose>
<xsl:when test="not(SurveyDate/text())">
<xsl:if test= "count(Request/Phase/Status) = count(Request/Phase/Status[matches(. , 'Sup|Ser|Adm|Can')])">
<Request>
<xsl:copy-of select="Request/Code"/>
<SurveyDate>
<xsl:value-of select="format-dateTime(current-dateTime(), '[Y0001]-[M01]-[D01]T[H1]:[m01]:[s01]')"/>
</SurveyDate>
</Request>
</xsl:if>
</xsl:when>
<xsl:otherwise>
<!-- just for testing remove when done -->
<Test>Do nothing</Test>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
And this is my test XML data.
<?xml version='1.0' encoding='UTF-8'?>
<document>
<businessobjects>
<Sub>
<Code>1.02</Code>
<Status>UsrWorkOrderCancelled</Status>
<Request>
<Code>1.00</Code>
<Description>Test 1</Description>
<SurveyDate>2022-11-02T22:55:55</SurveyDate>
<Phase>
<Code>1.01</Code>
<Status>UsrWorkOrderSupervisorApproved</Status>
</Phase>
<Phase>
<Code>1.02</Code>
<Status>UsrWorkOrderCancelled</Status>
</Phase>
</Request>
</Sub>
<Sub>
<Code>2.01</Code>
<Status>UsrWorkOrderSupervisorApproved</Status>
<Request>
<Code>2.00</Code>
<Description>Test 2</Description>
<SurveyDate></SurveyDate>
<Phase>
<Code>2.01</Code>
<Status>UsrWorkOrderSupervisorApproved</Status>
</Phase>
<Phase>
<Code>2.02</Code>
<Status>UsrWorkOrderCancelled</Status>
</Phase>
</Request>
</Sub>
</businessobjects>
</document>
The result XML I need is this:
<document>
<businessobjects>
<Request>
<Code>2.00</Code>
<SurveyDate>2022-11-03T21:45:13</SurveyDate>
</Request>
</businessobjects>
</document>
My advice: forget using xsl:choose or xsl:if, and instead put the conditional logic into the template's match expression:
<xsl:template match="Sub[not(Request/SurveyDate/text())]">
<!-- handle Sub without SurveyDate -->
<!-- ... -->
</xsl:template>
Leave the case where a Sub does have a SurveyDate for the identity template to handle, if you want to copy it unchanged. If you want to remove it (it's not clear from your test code what you want to do with it), you could add another template to do so:
<xsl:template match="Sub"/>
Note that template would have a lower priority than the one above, because its match expression is simpler, so it would apply only to Sub elements which did have a SurveyDate descendant.
I am trying to use XSLT to transform an XML document into a very similar XML document, but with a couple of additions. I'm having trouble getting xsl:copy-of to work properly. When I try to transform the following sample XML document:
<?xml version="1.0" encoding="UTF-8"?>
<mods xmlns="http://www.loc.gov/mods/v3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:mods="http://www.loc.gov/mods/v3"
xsi:schemaLocation="http://www.loc.gov/mods/v3
http://www.loc.gov/standards/mods/v3/mods-3-4.xsd">
<titleInfo>
<title>Test title
</title>
</titleInfo>
<subject authority="naf">
<geographic>Geo subject</geographic>
</subject>
<location>
<physicalLocation>Location here</physicalLocation>
</location>
<originInfo>
<dateCreated keyDate="yes">1904-01-05</dateCreated><dateCreated/>
</originInfo>
<typeOfResource>text</typeOfResource>
<genre authority="aat" valueURI="300026880">correspondence</genre>
<physicalDescription>
<extent>3 pages.</extent>
<note type="physical description">All pages ripped down the
middle.
</note>
</physicalDescription>
<relatedItem type="host" displayLabel="Collection"
<titleInfo>
<title>Collection name</title>
</titleInfo>
</relatedItem>
<accessCondition type="use and reproduction" displayLabel="Use and
Reproduction">Access condition here</accessCondition>
<identifier type="local">IDID</identifier>
</mods>
Using the following XSLT, only the literal values in the XSLT (originInfo, accessCondition) are output in the result XML document. I can't figure out why this is. When I remove all the header info from the source XML, the transform DOES work. But all my XML files have that header, and I want to make the XSLT work with it in - my guess is that my namespace declarations are contradicting each other, but I can't figure out why.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xlink="https://www.w3.org/1999/xlink"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mods="http://www.loc.gov/mods/v3" version="2.0" exclude-
result-prefixes="mods">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<mods>
<xsl:copy-of select="mods/titleInfo"/>
<xsl:copy-of select="mods/typeOfResource"/>
<xsl:copy-of select="mods/location"/>
<xsl:copy-of select="mods/physicalDescription"/>
<xsl:copy-of select="mods/subject"/>
<xsl:copy-of select="mods/name"/>
<xsl:copy-of select="mods/identifier"/>
<xsl:copy-of select="mods/genre"/>
<xsl:copy-of select="mods/relatedItem"/>
<xsl:copy-of select="mods/accessCondition"/>
<xsl:copy-of select="mods/language"/>
<xsl:copy-of select="mods/abstract"/>
<xsl:copy-of select="mods/note"/>
<originInfo>
<dateCreated>
<xsl:value-of select="mods/originInfo/dateCreated"/>
</dateCreated>
<dateCreated encoding="w3cdtf" keyDate="yes"
point="start">
<xsl:value-of select="mods/originInfo/dateCreated"/>
</dateCreated>
</originInfo>
<accessCondition type="use and reproduction">
<xsl:text>Copyright statement here</xsl:text>
</accessCondition>
</mods>
</xsl:template>
My expected output is:
<?xml version="1.0" encoding="UTF-8"?>
<mods xmlns="http://www.loc.gov/mods/v3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:mods="http://www.loc.gov/mods/v3"
xsi:schemaLocation="http://www.loc.gov/mods/v3
http://www.loc.gov/standards/mods/v3/mods-3-4.xsd">
<titleInfo>
<title>Test title
</title>
</titleInfo>
<subject authority="naf">
<geographic>Geo subject</geographic>
</subject>
<location>
<physicalLocation>Location here</physicalLocation>
</location>
<typeOfResource>text</typeOfResource>
<genre authority="aat" valueURI="300026880">correspondence</genre>
<physicalDescription>
<extent>3 pages.</extent>
<note type="physical description">All pages ripped down the
middle.
</note>
</physicalDescription>
<relatedItem type="host" displayLabel="Collection"
<titleInfo>
<title>Collection name</title>
</titleInfo>
</relatedItem>
<accessCondition type="use and reproduction" displayLabel="Use and
Reproduction">Access condition here</accessCondition>
<identifier type="local">IDID</identifier>
<originInfo>
<dateCreated>1904-01-05 </dateCreated>
<dateCreated encoding="w3cdtf" keyDate="yes" point="start">1904-01-05 </dateCreated>
</originInfo>
<accessCondition type="use and reproduction">Copyright statement here</accessCondition>
</mods>
There are two problems with your XSLT:
It does not select anything in the XML input, because your XML input puts its nodes in a namespace. If you're using XSLT 2.0, you can solve this by including xpath-default-namespace="http://www.loc.gov/mods/v3" in your xsl:stylesheet opening tag.
It does not put the literal result elements in the target namespace. In order to do this, you must declare the target namespace as the default namespace in one of the higher-level nodes, e.g. by including xmlns="http://www.loc.gov/mods/v3" in your xsl:stylesheet opening tag.
In addition, in order to prevent the original namespace declarations being replicated to every element copied by your stylesheet, you would do well to replace the literal result element <mods> by a copy of the original one.
Here's a complete stylesheet incorporating these changes:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.loc.gov/mods/v3"
xpath-default-namespace="http://www.loc.gov/mods/v3">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/mods">
<xsl:copy>
<xsl:copy-of select="titleInfo"/>
<xsl:copy-of select="typeOfResource"/>
<xsl:copy-of select="location"/>
<xsl:copy-of select="physicalDescription"/>
<xsl:copy-of select="subject"/>
<xsl:copy-of select="name"/>
<xsl:copy-of select="identifier"/>
<xsl:copy-of select="genre"/>
<xsl:copy-of select="relatedItem"/>
<xsl:copy-of select="accessCondition"/>
<xsl:copy-of select="language"/>
<xsl:copy-of select="abstract"/>
<xsl:copy-of select="note"/>
<originInfo>
<dateCreated>
<xsl:value-of select="originInfo/dateCreated"/>
</dateCreated>
<dateCreated encoding="w3cdtf" keyDate="yes" point="start">
<xsl:value-of select="originInfo/dateCreated"/>
</dateCreated>
</originInfo>
<accessCondition type="use and reproduction">
<xsl:text>Copyright statement here</xsl:text>
</accessCondition>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Demo: http://xsltransform.hikmatu.com/gWmuiHV
I'm still pretty new to XSLT and I have the following XML which I need to remove the namespace. I also found the following XSLT which almost gets the job done with the exception that it will not retain the xmlns declaration.
XML:
<?xml version="1.0" encoding="UTF-8"?>
<etd_ms:thesis xmlns:etd_ms="http://www.ndltd.org/standards/metadata/etdms/1.0/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.ndltd.org/standards/metadata/etdms/1.0/ http://www.ndltd.org/standards/metadata/etdms/1.0/etdms.xsd">
<etd_ms:title>Aspects of negritude in the works of two Harlem renaissance authors : Claude McKay and Langston Hughes</etd_ms:title>
<etd_ms:creator>Charles, Asselin</etd_ms:creator>
<etd_ms:subject/>
<etd_ms:publisher>Concordia University</etd_ms:publisher>
<etd_ms:contributor role="advisor">Butovsky, M</etd_ms:contributor>
<etd_ms:date>1980</etd_ms:date>
<etd_ms:type>Electronic Thesis or Dissertation</etd_ms:type>
<etd_ms:identifier>TC-QMG-1</etd_ms:identifier>
<etd_ms:format>text</etd_ms:format>
<etd_ms:identifier>https://spectrum.library.concordia.ca/1/1/MK49585.pdf</etd_ms:identifier>
<etd_ms:language>en</etd_ms:language>
<etd_ms:degree>
<etd_ms:name>M.A.</etd_ms:name>
<etd_ms:level>masters</etd_ms:level>
<etd_ms:discipline>Dept. of English</etd_ms:discipline>
<etd_ms:grantor>Concordia University</etd_ms:grantor>
</etd_ms:degree>
</etd_ms:thesis>
and here's the XSLT:
<?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" method="xml" encoding="utf-8" omit-xml-declaration="no"/>
<!-- Stylesheet to remove all namespaces from a document -->
<!-- NOTE: this will lead to attribute name clash, if an element contains
two attributes with same local name but different namespace prefix -->
<!-- Nodes that cannot have a namespace are copied as such -->
<!-- template to copy elements -->
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
<!-- template to copy attributes -->
<xsl:template match="#*">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
<!-- template to copy the rest of the nodes -->
<xsl:template match="comment() | text() | processing-instruction()">
<xsl:copy/>
</xsl:template>
</xsl:stylesheet>
The results is the following:
<?xml version="1.0" encoding="UTF-8"?>
<thesis xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.ndltd.org/standards/metadata/etdms/1.0/ http://www.ndltd.org/standards/metadata/etdms/1.0/etdms.xsd">
<title>Aspects of negritude in the works of two Harlem renaissance authors : Claude McKay and Langston Hughes</title>
<creator>Charles, Asselin</creator>
<subject/>
<publisher>Concordia University</publisher>
<contributor role="advisor">Butovsky, M</contributor>
<date>1980</date>
<type>Electronic Thesis or Dissertation</type>
<identifier>TC-QMG-1</identifier>
<format>text</format>
<identifier>https://spectrum.library.concordia.ca/1/1/MK49585.pdf</identifier>
<language>en</language>
<degree>
<name>M.A.</name>
<level>masters</level>
<discipline>Dept. of English</discipline>
<grantor>Concordia University</grantor>
</degree>
</thesis>
It's almost there, with the exception that I need to keep the xmlns declaration, so ultimatly the root element should be something like:
<thesis xmlns="http://www.ndltd.org/standards/metadata/etdms/1.0/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.ndltd.org/standards/metadata/etdms/1.0/ http://www.ndltd.org/standards/metadata/etdms/1.0/etdms.xsd">
Can someone help me resolving this issue? Thanks.
Change
<xsl:element name="{local-name()}">
to
<xsl:element name="{local-name()}" namespace="http://www.ndltd.org/standards/metadata/etdms/1.0/">
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.
I've searched through similar questions, but couldn't make any of the suggestions to work. I have the following xml I need to modify it
<XDB>
<ROOT>
<KEY><ID>12345</ID><DATE>5/10/2011</DATE></KEY>
<PERSONAL><ID>1</ID><INFO><LASTNAME>Smith</LASTNAME>...</INFO></PERSONAL>
<CONTACT><ID>1</ID><EMAIL>asmith#yahoo.com</EMAIL>...</CONTACT>
</ROOT>
<ROOT>
<KEY><ID>98765</ID><DATE>5/10/2013</DATE></KEY>
<CONTACT><ID>2</ID><EMAIL>psmithton#yahoo.com</EMAIL>...</CONTACT>
</ROOT>
...
</XDB>
And it needs to look like this:
<XDB>
<ROOT>
<KEY><ID>12345</ID><DATE>5/10/2011</DATE>
<PERSONAL><ID>1</ID><INFO><LASTNAME>Smith</LASTNAME>...</INFO></PERSONAL>
<CONTACT><ID>1</ID><EMAIL>asmith#yahoo.com</EMAIL>...</CONTACT>
</KEY>
</ROOT>
<ROOT>
<KEY><ID>98765</ID><DATE>5/10/2013</DATE>
<CONTACT><ID>2</ID><EMAIL>psmithton#yahoo.com</EMAIL>...</CONTACT>
</KEY>
</ROOT>
...
</XDB>
I need to make 2...n siblings as children of the first 'key' sibling. Essentially, i need to remove the closing < /KEY> and put it before the closing < /ROOT>. I would appreciate your help.
Thanks.
Following xslt based on Identity transform could make this job
<?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" version="1.0" encoding="UTF-8" indent="yes"/>
<!-- Copy everything you find... -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<!-- ... but if you find first element inside ROOT ... -->
<xsl:template match="ROOT/node()[1]">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
<!-- ... copy its sibling into it ... -->
<xsl:copy-of select="following-sibling::*" />
</xsl:copy>
</xsl:template>
<!-- ignore other elements inside ROOT element since they are copied in template matching first element -->
<xsl:template match="ROOT/node()[position() > 1]" />
</xsl:stylesheet>