XSLT field validation - xslt

I have a requirement to validate a field content which is within a 1..N structure, so the data come in pairs of RESULT_ID and RESULT_VALUE.
If "<" is found in Result.Min_Limit characteristic, its value must be put in the Max_Limit characteristic target field and Min_Limit must be cleared out in target structure.
Sample of source structure with data:
<?xml version="1.0" encoding="UTF-8"?>
<TEST>
<RESULTS>
<RESULT>
<D_RESULT>
<D_RESULT_ID>Result.Derived</D_RESULT_ID>
<D_RESULT_VALUE>59W</D_RESULT_VALUE>
</D_RESULT>
<D_RESULT>
<D_RESULT_ID>Result.Min_Limit</D_RESULT_ID>
<D_RESULT_VALUE><=600.0000</D_RESULT_VALUE>
</D_RESULT>
<D_RESULT>
<D_RESULT_ID>Result.Max_Limit</D_RESULT_ID>
<D_RESULT_VALUE/>
</D_RESULT>
<D_RESULT>
<D_RESULT_ID>Result.FailedCriticalLvl</D_RESULT_ID>
<D_RESULT_VALUE>false</D_RESULT_VALUE>
</D_RESULT>
</RESULT>
</RESULTS>
</TEST>
Expected target structure should be as follow:
<?xml version="1.0" encoding="UTF-8"?>
<TEST>
<RESULTS>
<RESULT>
<D_RESULT>
<D_RESULT_ID>Result.Derived</D_RESULT_ID>
<D_RESULT_VALUE>59W</D_RESULT_VALUE>
</D_RESULT>
<D_RESULT>
<D_RESULT_ID>Result.Min_Limit</D_RESULT_ID>
<D_RESULT_VALUE/>
</D_RESULT>
<D_RESULT>
<D_RESULT_ID>Result.Max_Limit</D_RESULT_ID>
<D_RESULT_VALUE><=600.0000</D_RESULT_VALUE>
</D_RESULT>
<D_RESULT>
<D_RESULT_ID>Result.FailedCriticalLvl</D_RESULT_ID>
<D_RESULT_VALUE>false</D_RESULT_VALUE>
</D_RESULT>
</RESULT>
</RESULTS>
</TEST>
I've created a variable to store Min_Limit value if it contains the "<" character, and it works fine when the pointer is still in Min_Limit characteristic.
When it flips to next characteristic (Max_Limit), the variable seems to loose its value and therefore I have no visibility of Min_Limit value anymore to assign the value to Max_Limit characteristic.
Currently, what I have is like the following:
......
<xsl:for-each select="a:D_RESULT">
<D_RESULT>
<D_RESULT_ID>
<xsl:value-of select="a:D_RESULT_ID"/>
</D_RESULT_ID>
<D_RESULT_VALUE>
<xsl:value-of select="a:D_RESULT_VALUE"/>
</D_RESULT_VALUE>
</D_RESULT>
</xsl:for-each>
</RESULT>
... other fields
I've found some interesting conversations here with some approaches I believe would help solving this, but I'm not sure I understood enough to apply to my scenario.
This is one of those XSLT: nested for-each and dynamic variable
Would anyone suggests something to solve this?
Many thanks in advance!
Rafael.

Here's one way you could look at it:
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="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="D_RESULT_VALUE[../D_RESULT_ID='Result.Min_Limit' and contains(., '<')] ">
<xsl:copy/>
</xsl:template>
<xsl:template match="D_RESULT_VALUE[../D_RESULT_ID='Result.Max_Limit' and contains(../../D_RESULT[D_RESULT_ID='Result.Min_Limit']/D_RESULT_VALUE, '<')] ">
<xsl:copy>
<xsl:value-of select="../../D_RESULT[D_RESULT_ID='Result.Min_Limit']/D_RESULT_VALUE"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Demo: http://xsltransform.net/3NzcBtA

Related

How to not touch a record if text exist in an XML field but process the record in no text exist using XSLT 2

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.

Not select a particular node child Elements in xslt using not

My XML is below. Is it possible to do this in same XSLT?
<response context="XXXX" type="abcd" errorCode="0" >
<output>
<Applicants>
<Applicant>
<IndividualEmployments/>
<Addresses/>
</Applicant>
</Applicants>
<Assets>
<Asset id="12345"></Asset>
</Assets>
<Liabilities>
<Liability id="8765"></Liability>
</Liabilities>
</output>
Desired output should be like below. I want two response nodes, one with Assets and the other with Liabilities.
<response context="XXXX" type="abcd" errorCode="0">
<output>
<Applicants>
<Applicant>
<IndividualEmployments/>
<Addresses/>
</Applicant>
</Applicants>
<Assets>
<Asset id="12345"></Asset>
</Assets>
</output>
<response context="XXXX" type="abcd" errorCode="0">
<output>
<Applicants>
<Applicant>
<IndividualEmployments/>
<Addresses/>
</Applicant>
</Applicants>
<Liabilities>
<Liability id="8765"></Liability>
</Liabilities>
</output>
You need to process the response element and output it twice, making sure the content is different, for instance by passing a parameter:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
expand-text="yes">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="response">
<xsl:next-match>
<xsl:with-param name="exclude" tunnel="yes" select="descendant::Liabilities"/>
</xsl:next-match>
<xsl:next-match>
<xsl:with-param name="exclude" tunnel="yes" select="descendant::Assets"/>
</xsl:next-match>
</xsl:template>
<xsl:template match="output">
<xsl:param name="exclude" tunnel="yes"/>
<xsl:copy>
<xsl:apply-templates select="#*, node() except $exclude"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
If needed or wanted you can of course wrap each xsl:next-match I have in an xsl:result-document.

Adding missing nodes to XML via XSLT

I'm using a tool to import XML files into Dynamics NAV, but some parties providing the XML files skip empty nodes. My tool (external) can not handle those situation so I want to include XSLT to add the missing nodes. The xslt works fine for 1 node, but adding multiple nodes does not work. So I must be doing something wrong.
I'm building an integration to Dynamics NAV to insert Sales Orders. The orders are delivered from multiple parties using a XML file. However some of the parties providing the XML do not list all nodes in their XML file, they skip the empty ones. I'm using a tool build within Dynamics NAV (Add-on from other vendor) to import those files. However some XML files go wrong because of the fact that some (empty) nodes are missing in the XML file. I know this is an issue within the add-on but I need a solution on short notice. So created an XSLT to add the missing nodes. It works fine with 1 missing node, but it is not able to add both missing nodes. I'm not that familiar with XSLT so most of the times it is trial & error. Perhaps someone can help me with this.
This is the XML file format that is provided, The nodes that are sometimes missing is the DeliveryParty node and the DeliveryAddress part.
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<Orders>
<Order>
<Partner>
<SenderEANCode>9999999999999</SenderEANCode>
<RecipientEANCode>9999999999999</RecipientEANCode>
</Partner>
<OrderHeader>
<OrderVersion>008</OrderVersion>
<OrderTypeCode>220</OrderTypeCode>
<Document>
<DocumentNumber>34034040</DocumentNumber>
<Date>2019-04-18</Date>
</Document>
<DeliveryDate>2019-04-24</DeliveryDate>
<CompleteDelivery>YES</CompleteDelivery>
<Supplier>9999999999999</Supplier>
<Buyer>9999999999999</Buyer>
<Invoicee>9999999999999</Invoicee>
<DeliveryParty>9999999999999</DeliveryParty>
<DeliveryAddress>
<DeliveryName>Private Customer</DeliveryName>
<DeliveryStreet>Teststraat</DeliveryStreet>
<DeliveryCity>TestCity</DeliveryCity>
<DeliveryCountry>NL</DeliveryCountry>
<DeliveryTelNo></DeliveryTelNo>
<DeliveryEmail>test#test.com</DeliveryEmail>
</DeliveryAddress>
</OrderHeader>
<OrderLine>
<LineItemNumber>1</LineItemNumber>
<GTIN>9999999999999</GTIN>
<OrderedQuantity>
<Quantity>260</Quantity>
</OrderedQuantity>
</OrderLine>
</Order>
</Orders>
Sometimes the DeliveryParty node is missing and other times the DeliveryAddress part including subnodes is missing. I created the following XSLT to add those nodes but as it is trail and error I need some help to fix this. I'm a novice to XSLT, I can so some small changes but I do not use it frequently so knowledge is fading away quickly.
<?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" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Orders/Order/OrderHeader[not(DeliveryParty)]">
<xsl:copy-of select="*"/>
<DeliveryParty/>
</xsl:template>
<xsl:template match="Orders/Order/OrderHeader[not(//DeliveryAddress)]">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
<DeliveryAddress>
<DeliveryName></DeliveryName>
<DeliveryStreet></DeliveryStreet>
<DeliveryPostalCode></DeliveryPostalCode>
<DeliveryCity></DeliveryCity>
<DeliveryCountry></DeliveryCountry>
<DeliveryTelNo></DeliveryTelNo>
<DeliveryEmail></DeliveryEmail>
</DeliveryAddress>
</xsl:copy>
</xsl:template>
With above mentioned XSLT the DeliveryAddress node with it's subnodes is added but the deliveryparty is not.
When the file is delivered like this:
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<Orders>
<Order>
<Partner>
<SenderEANCode>9999999999999</SenderEANCode>
<RecipientEANCode>9999999999999</RecipientEANCode>
</Partner>
<OrderHeader>
<OrderVersion>008</OrderVersion>
<OrderTypeCode>220</OrderTypeCode>
<Document>
<DocumentNumber>34034040</DocumentNumber>
<Date>2019-04-18</Date>
</Document>
<DeliveryDate>2019-04-24</DeliveryDate>
<CompleteDelivery>YES</CompleteDelivery>
<Supplier>9999999999999</Supplier>
<Buyer>9999999999999</Buyer>
<Invoicee>9999999999999</Invoicee>
</OrderHeader>
<OrderLine>
<LineItemNumber>1</LineItemNumber>
<GTIN>9999999999999</GTIN>
<OrderedQuantity>
<Quantity>260</Quantity>
</OrderedQuantity>
</OrderLine>
</Order>
</Orders>
The outcome should be this:
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<Orders>
<Order>
<Partner>
<SenderEANCode>9999999999999</SenderEANCode>
<RecipientEANCode>9999999999999</RecipientEANCode>
</Partner>
<OrderHeader>
<OrderVersion>008</OrderVersion>
<OrderTypeCode>220</OrderTypeCode>
<Document>
<DocumentNumber>34034040</DocumentNumber>
<Date>2019-04-18</Date>
</Document>
<DeliveryDate>2019-04-24</DeliveryDate>
<CompleteDelivery>YES</CompleteDelivery>
<Supplier>9999999999999</Supplier>
<Buyer>9999999999999</Buyer>
<Invoicee>9999999999999</Invoicee>
<DeliveryParty></DeliveryParty>
<DeliveryAddress>
<DeliveryName></DeliveryName>
<DeliveryStreet></DeliveryStreet>
<DeliveryCity></DeliveryCity>
<DeliveryCountry></DeliveryCountry>
<DeliveryTelNo></DeliveryTelNo>
<DeliveryEmail></DeliveryEmail>
</DeliveryAddress>
</OrderHeader>
<OrderLine>
<LineItemNumber>1</LineItemNumber>
<GTIN>9999999999999</GTIN>
<OrderedQuantity>
<Quantity>260</Quantity>
</OrderedQuantity>
</OrderLine>
</Order>
</Orders>
How about:
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="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="OrderHeader">
<xsl:copy>
<xsl:apply-templates/>
<xsl:if test="not(DeliveryParty)">
<DeliveryParty/>
</xsl:if>
<xsl:if test="not(DeliveryAddress)">
<DeliveryAddress>
<DeliveryName/>
<DeliveryStreet/>
<DeliveryPostalCode/>
<DeliveryCity/>
<DeliveryCountry/>
<DeliveryTelNo/>
<DeliveryEmail/>
</DeliveryAddress>
</xsl:if>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Is there a way to replace the for-each with apply-templates in an XSLT Transform?

Environment: XSLT 1.0
The transform will take each element in partOne section and lookup #field attribute in partTwo section using #find attribute and then output #value attribute.
I'm using a for-each loop and was wondering if apply-templates could work?
xml
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="file.xslt"?>
<xml>
<partOne>
<target field="hello"/>
<target field="world"/>
</partOne>
<partTwo>
<number input="2" find="hello" value="valone" />
<number input="2" find="world" value="valtwo" />
<number input="2" find="hello" value="valthree" />
<number input="2" find="world" value="valfour" />
</partTwo>
</xml>
xsl
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="/xml/partOne/target">
,<xsl:value-of select="#field"/>
<xsl:for-each select="/xml/partTwo/number[#find=current()/#field]">
,<xsl:value-of select="#value"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Output:
,hello
,valone
,valthree
,world
,valtwo
,valfour
Well, it seems straight-forward to change
<xsl:for-each select="/xml/partTwo/number[#find=current()/#field]">
,<xsl:value-of select="#value"/>
</xsl:for-each>
to
<xsl:apply-templates select="/xml/partTwo/number[#find=current()/#field]"/>
with a template
<xsl:template match="partTwo/number">
,<xsl:value-of select="#value"/>
</xsl:template>
As your root template so far processes all elements you need to change it to
<xsl:template match="/">
<xsl:apply-templates select="xml/partOne"/>
</xsl:template>
to avoid processing the partTwo element(s) twice.
For the cross-reference you might want to use a key in both versions:
<xsl:key name="ref" match="partTwo/number" use="#find"/>
and then select="key('ref', #field)" instead of select="/xml/partTwo/number[#find=current()/#field]" for the apply-templates or for-each.

Multiple transformations to an xml file using xslt 1

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.