XSLT for extracting elements from a non linear XML structure - xslt

I have a XML structure where the XML schema is irregular/not formatted. The structure looks like this-
<Host>
<element1>type0</element1>
<element2>Fruits</element2>
....
<elementn>Price0</elementn>
<Menu>
<NodeA>
<element1>type1</element1>
<element2>Fruits</element2>
....
<elementn>Price1</elementn>
<Menu>
<NodeB>
<element1>type2</element1>
<element2>Fruits</element2>
....
<elementn>Price2</elementn>
<Menu>
<NodeC>
<element1>type3</element1>
<element2>Fruits</element2>
....
<elementn>Price3</elementn>
<Menu>
<NodeD>
<Element1>type4</element1>
<Element2>Vegetables</Element2>
....
<Elementn>Price4</elementn>
</NodeD>
</Menu>
</NodeC>
</Menu>
</NodeB>
</Menu>
</NodeA>
<NodeE>
<element1>type5</element1>
<element2>Fruits</element2>
....
<elementn>Price5</elementn>
<Menu>
<NodeF>
<element1>type6</element1>
<element2>Vegetables</element2>
....
<elementn>Price6</elementn>
</NodeF>
</Menu>
</NodeE>
</Menu>
</Host>
Now my expected XML is as follows-
a) if <element2> == fruits in all the nodes, I need XML schema as follows. I may include or exclude the below n elements right under host -
`<element1>type0</element>
<element2>Fruits</element2>
....
<elementn>Price0</elementn>`
.Expected Result -
<Host>
<NodeA>
<element1>type1</element1>
<element2>Fruits</element2>
....
<elementn>Price1</elementn>
</NodeA>
<NodeB>
<element1>type2</element1>
<element2>Fruits</element2>
....
<elementn>Price2</elementn>
</NodeB>
<NodeC>
<element1>type3</element1>
<element2>Fruits</element2>
....
<elementn>Price3</elementn>
</NodeC>
<NodeE>
<element1>type5</element1>
<element2>Fruits</element2>
....
<elementn>Price5</elementn>
</NodeE>
</Host>
b) if <element2> == vegetables in all the nodes, I need XML schema as follows
Note: <element2> == Vegetables is always at the last node in the schema
<Host>
<NodeD>
<element1>type4</element1>
<element2>Vegetables</element2>
....
<elementn>Price4</elementn>
</NodeD>
<NodeF>
<element1>type6</element1>
<element2>Vegetables</element2>
....
<elementn>Price6</elementn>
</NodeF>
</Host>
Any help for getting the above XML formats through XSLT would be a great help.

If you want 2 separate document, you don't actually need 2 XSLTs. You can use one XSLT but with a parameter
<xsl:param name="element2" select="'Fruits'" />
(Here 'Fruits' is just the default value, should the parameter not be specified by the calling application).
You would start off by selecting the nodes which have element2 equal to the parameter (Do note XML and XSLT is case-sensitive, so element2 is not the same as Element2 in your XML, but I assumed that was a typo in your XML).
<xsl:apply-templates select="//*[element2=$element2]"/>
You would also need a template to ensure when a node is matched, it does not copy the child nodes...
<xsl:template match="*[element2]">
<xsl:copy>
<xsl:apply-templates select="*[not(*)]" />
</xsl:copy>
</xsl:template>
The other nodes would be handled by the identity template.
Try this XSLT...
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:param name="element2" select="'Fruits'" />
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="//*[element2=$element2]" mode="copy"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[element2]" mode="copy">
<xsl:copy>
<xsl:apply-templates select="*[not(*)]" mode="copy"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#*|node()" mode="copy">
<xsl:copy>
<xsl:apply-templates select="#*|node()" mode="copy"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Note that if you were using XSLT 2.0, you could create multiple documents in one call, using xsl:for-each-group to get the distinct groups, and xsl:result-document to create a file for each.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/*">
<xsl:for-each-group select="//*[element2]" group-by="element2">
<xsl:result-document href="{current-grouping-key()}.xml" method="xml">
<Host>
<xsl:apply-templates select="current-group()" mode="copy" />
</Host>
</xsl:result-document>
</xsl:for-each-group>
</xsl:template>
<xsl:template match="*[element2]" mode="copy">
<xsl:copy>
<xsl:apply-templates select="*[not(*)]" mode="copy"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#*|node()" mode="copy">
<xsl:copy>
<xsl:apply-templates select="#*|node()" mode="copy" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Related

Copying parent node value to child nodes

I want to copy the value of parentnode element into child node, as shown below. Can someone point me in the right direction on how to achieve this?
Thank you!
Here is the xml file.
<mainpart id="295928" num="1-MS15" quantity="1">
<explicitQuantity>1</explicitQuantity>
<proxy id="1E2B4D" ACADID="1E2B4E" basepart="58A67">
<singlepart id="24558D" num="1-m81" ncFile="1-m81.nc1" quantity="1">
<explicitQuantity>1</explicitQuantity>
<part id="58A67" name="C4X6.25" class="Beam" ACADID="59582" dstvName="C4X6.25">
<explicitQuantity>1</explicitQuantity>
<role key="Frame" name="FRAME"/>
<length>1727.194</length>
<paintArea>618840.532</paintArea>
<numHoles>8</numHoles>
<ObjectTopLevel>0.739</ObjectTopLevel>
<ObjectBottomLevel>-101.741</ObjectBottomLevel>
<material key="ASTM-A36" name="A36"/>
<coating key="G" name="G"/>
<commodity>FRAME</commodity>
<weight>16064.68</weight>
<exactWeight>15945.55</exactWeight>
<weightPerMeter>9301.02</weightPerMeter>
<density>7850.00</density>
<section key="AISC 14.1 C Channel##§##ChannelsC4X6.25" name="C4X6.25"/>
<sysLength>1727.194</sysLength>
<sawLength>1727.194</sawLength>
<angleX1>0.0000</angleX1>
<angleY1>0.0000</angleY1>
<angleX2>0.0000</angleX2>
<angleY2>0.0000</angleY2>
<SawCutInfo>0</SawCutInfo>
<ElementID>7400</ElementID>
</part>
</singlepart>
<singlepart id="295ADA" num="1-p3" quantity="4">
<explicitQuantity>4</explicitQuantity>
<part id="295C18" name="PL 1/4"x1 3/8"" class="Plate" ACADID="295C14" dstvName="PL 1/4"">
<explicitQuantity>1</explicitQuantity>
<role key="Plate" name="PLATE"/>
<length>85.725</length>
<paintArea>7215.343</paintArea>
<ObjectTopLevel>-7.199</ObjectTopLevel>
<ObjectBottomLevel>-93.396</ObjectBottomLevel>
<material key="ASTM-A36" name="A36"/>
<coating key="G" name="G"/>
<weight>143.81</weight>
<exactWeight>143.81</exactWeight>
<density>7850.00</density>
<thickness>6.350</thickness>
<width>35.535</width>
<area1>2884.914</area1>
<area2>2884.914</area2>
<contourLength>227.640</contourLength>
<ElementID>52306</ElementID>
<ElementID>52304</ElementID>
<ElementID>52303</ElementID>
<ElementID>52305</ElementID>
</part>
</singlepart>
</proxy>
</mainpart>
Here is my xsl, I know it is wrong, but I can't figure out how to fix it.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="mainpart" >
<xsl:variable name="mainpartmark" select="#name" />
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="part">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
<UserAttribute10>"$mainpartmark"</UserAttribute10>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Here is the result I wanted, I want to add the node UserAttr10 on each end of Part node with value of num from mainpart node.
<mainpart id="295928" num="1-MS15" quantity="1">
<explicitQuantity>1</explicitQuantity>
<proxy id="1E2B4D" ACADID="1E2B4E" basepart="58A67">
<singlepart id="24558D" num="1-m81" ncFile="1-m81.nc1" quantity="1">
<explicitQuantity>1</explicitQuantity>
<part id="58A67" name="C4X6.25" class="Beam" ACADID="59582" dstvName="C4X6.25">
<explicitQuantity>1</explicitQuantity>
<role key="Frame" name="FRAME"/>
<length>1727.194</length>
<paintArea>618840.532</paintArea>
<numHoles>8</numHoles>
<ObjectTopLevel>0.739</ObjectTopLevel>
<ObjectBottomLevel>-101.741</ObjectBottomLevel>
<material key="ASTM-A36" name="A36"/>
<coating key="G" name="G"/>
<commodity>FRAME</commodity>
<weight>16064.68</weight>
<exactWeight>15945.55</exactWeight>
<weightPerMeter>9301.02</weightPerMeter>
<density>7850.00</density>
<section key="AISC 14.1 C Channel##§##ChannelsC4X6.25" name="C4X6.25"/>
<sysLength>1727.194</sysLength>
<sawLength>1727.194</sawLength>
<angleX1>0.0000</angleX1>
<angleY1>0.0000</angleY1>
<angleX2>0.0000</angleX2>
<angleY2>0.0000</angleY2>
<SawCutInfo>0</SawCutInfo>
<ElementID>7400</ElementID>
<UserAttr10>1-MS15</UserAttr10> <!--Add this line-->
</part>
</singlepart>
<singlepart id="295ADA" num="1-p3" quantity="4">
<explicitQuantity>4</explicitQuantity>
<part id="295C18" name="PL 1/4"x1 3/8"" class="Plate" ACADID="295C14" dstvName="PL 1/4"">
<explicitQuantity>1</explicitQuantity>
<role key="Plate" name="PLATE"/>
<length>85.725</length>
<paintArea>7215.343</paintArea>
<ObjectTopLevel>-7.199</ObjectTopLevel>
<ObjectBottomLevel>-93.396</ObjectBottomLevel>
<material key="ASTM-A36" name="A36"/>
<coating key="G" name="G"/>
<weight>143.81</weight>
<exactWeight>143.81</exactWeight>
<density>7850.00</density>
<thickness>6.350</thickness>
<width>35.535</width>
<area1>2884.914</area1>
<area2>2884.914</area2>
<contourLength>227.640</contourLength>
<ElementID>52306</ElementID>
<ElementID>52304</ElementID>
<ElementID>52303</ElementID>
<ElementID>52305</ElementID>
<UserAttr10>1-MS15</UserAttr10> <!--Add this line-->
</part>
</singlepart>
</proxy>
</mainpart>
As there is only one mainpart node (the root element), simply declare your variable as a global variable (a child of xsl:stylesheet)
<xsl:variable name="mainpartmark" select="/mainpart/#num" />
(You wouldn't need the template matching mainpart in this case).
Then, to use it, just do this...
<UserAttribute10>
<xsl:value-of select="/mainpart/#num" />
</UserAttribute10>
Try this XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
exclude-result-prefixes="xsi">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:variable name="mainpartmark" select="/mainpart/#num" />
<xsl:template match="part">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
<UserAttribute10>
<xsl:value-of select="$mainpartmark" />
</UserAttribute10>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Of course, you could also do it without the variable at all, and just do <xsl:value-of select="/mainpart/#num" /> instead.
And, if there were multiple mainpart nodes (under a single root element), you could also do <xsl:value-of select="ancestor::mainpart/#num" />

Remove parent based on child value?

I want to display only those orders which dont have OrderLineSource = YTR. All other should be displayed....
My Sample XML :
<Orders>
<Order>
<OrderID>34209649</OrderID>
<OrderStatus>checkout_complete</OrderStatus>
<Amount>32.93</Amount>
<OrderCreation>2014-02-08T00:00:03.00</OrderCreation>
<OrderCompletion>2014-02-08T00:00:03.00</OrderCompletion>
<CustomerGUID>303965683</CustomerGUID>
<CSMPurchaserGUID>0</CSMPurchaserGUID>
<Brand>TRFE</Brand>
<SourceECommerceSystem>Framework</SourceECommerceSystem>
<Currency>GBP</Currency>
<OrderChannel>Online</OrderChannel>
<TransactionSummary>
<TransactionID>2407065</TransactionID>
<MerchantReference>TEdV-5648-34209649</MerchantReference>
<CardCategory>Personal</CardCategory>
<CardScheme>VISA Debit</CardScheme>
<CardCountry>gbr</CardCountry>
<CardIssuer>sdfsdf sdf Bank asdf</CardIssuer>
<CardStartDate>0/0</CardStartDate>
<CardExpiryDate>2016/08</CardExpiryDate>
<Amount>32.93</Amount>
<Currency>GBP</Currency>
<CardPAN>************4585</CardPAN>
<Created>2014-02-07T23:56:48</Created>
<Updated>2014-02-08T00:00:03</Updated>
<ResponseStatusCode>1</ResponseStatusCode>
<ResponseStatusReason>FULFILLED OK</ResponseStatusReason>
<HostedPageIdentifier>dsfasdf-ee85-4afa-bb6a-0afc6dc99896</HostedPageIdentifier>
<HostedPageURL>https://hps.datacash.com/hps/</HostedPageURL>
<PaymentStatus>Paid</PaymentStatus>
<PaymentType>Debit Card</PaymentType>
<NameOnCard>Miss L J adsf</NameOnCard>
<DataCashRef>56456456454</DataCashRef>
<MerchantID>545646</MerchantID>
<ThreeDCard>1</ThreeDCard>
<ThreeDRequested>1</ThreeDRequested>
<IPAddress>127.89.560.1</IPAddress>
</TransactionSummary>
<OrderLine>
<OrderLineID>84598837</OrderLineID>
<OrderID>34209649</OrderID>
<OrderLineLabel>GAREGSBV</OrderLineLabel>
<OrderLineSource>GHR</OrderLineSource>
<Quantity>1</Quantity>
<UnitPrice>32.93</UnitPrice>
<Total>32.93</Total>
<SKUCode>P0032</SKUCode>
<Title>Miss.</Title>
<FirstName>ertwer</FirstName>
<FamilyName>sdaf</FamilyName>
<DateOfBirth>1984-05-30</DateOfBirth>
<Email>sdfasdfa#hotmail.com</Email>
<Mobile>645646454</Mobile>
<PostChannel>0</PostChannel>
<TelephoneChannel>0</TelephoneChannel>
<EmailChannel>0</EmailChannel>
<TextAndOtherChannel>0</TextAndOtherChannel>
<BuildingNumber>27</BuildingNumber>
<AddressLine1>27</AddressLine1>
<AddressLine2>dsfasdf Road</AddressLine2>
<Town>London</Town>
<Country>sdfasdf er</Country>
<Postcode>KL7 2NS</Postcode>
<AddressValidated>1</AddressValidated>
<HKPolicy>
<PolicyNum>PP01754397</PolicyNum>
<ProductDescription>sadfsadfasdfgasdg</ProductDescription>
<CoverTypeDesc>Individual</CoverTypeDesc>
<SingleParentFamilyFlag>0</SingleParentFamilyFlag>
<PolicyTypeRefID>S</PolicyTypeRefID>
<PolicyTypeDesc>Sinasdfnce</PolicyTypeDesc>
<TierDesc>Classic</TierDesc>
<DestinationDesc>Worldwide including USA, Canada, Caribbean</DestinationDesc>
<TotalTravellers>1</TotalTravellers>
<NumOfAdults>1</NumOfAdults>
<NumOfUnder18>0</NumOfUnder18>
<PolicyStartDate>2014-02-08</PolicyStartDate>
<PolicyEndDate>2014-02-12</PolicyEndDate>
<BaseCost>32.93</BaseCost>
<Commission>11.18</Commission>
<UpsoldInd>0</UpsoldInd>
<TierRefID>C</TierRefID>
<DestinationRefID>W2</DestinationRefID>
<CoverTypeRefID>I</CoverTypeRefID>
<AONToPostPolicy>yes</AONToPostPolicy>
<SalesChannel>0011002</SalesChannel>
<WhereYouHeardOfUs>Press advertising</WhereYouHeardOfUs>
<TIPOLTraveller>
<TravellerUUID>1864-1</TravellerUUID>
<PolicyNum>PI0e31754397</PolicyNum>
<Title>Miss</Title>
<FirstName>sdfsf</FirstName>
<FamilyName>sdfsdf</FamilyName>
<DateOfBirth>1984-05-30</DateOfBirth>
<AgeBand>1864</AgeBand>
<DependentFlag>0</DependentFlag>
</TIPOLTraveller>
</TIPOLPolicy>
</OrderLine>
<OrderCustomerDetails>
<Title nil="true" />
<FirstName nil="true" />
<SecondName nil="true" />
<FamilyName nil="true" />
<DateOfBirth nil="true" />
<Email nil="true" />
<Telephone nil="true" />
<Mobile nil="true" />
<Gender nil="true" />
<PostChannel nil="true" />
<TelephoneChannel nil="true" />
<EmailChannel nil="true" />
<TextAndOtherChannel nil="true" />
<BuildingNumber>27</BuildingNumber>
<AddressLine1>27</AddressLine1>
<AddressLine2>asdfa Road</AddressLine2>
<Town>asdfasdf</Town>
<Country>United dsf</Country>
<Postcode>KH9 2NS</Postcode>
<AddressValidated>1</AddressValidated>
</OrderCustomerDetails>
</Order>
<Order>
<OrderID>34209674</OrderID>
<OrderStatus>checkout_complete</OrderStatus>
<Amount>11.13</Amount>
<OrderCreation>2014-02-08T00:08:40.00</OrderCreation>
<OrderCompletion>2014-02-08T00:08:40.00</OrderCompletion>
<CustomerGUID>303965688</CustomerGUID>
<CSMPurchaserGUID>0</CSMPurchaserGUID>
<Brand>TRFDS</Brand>
<SourceECommerceSystem>Framework</SourceECommerceSystem>
<Currency>GBP</Currency>
<OrderChannel>Online</OrderChannel>
<TransactionSummary>
<TransactionID>8115032</TransactionID>
<MerchantReference>JHF-0800-34209674</MerchantReference>
<CardCategory>Personal</CardCategory>
<CardScheme>VISA Debit</CardScheme>
<CardCountry>gbr</CardCountry>
<CardIssuer>Unknown</CardIssuer>
<CardStartDate>0/0</CardStartDate>
<CardExpiryDate>2016/09</CardExpiryDate>
<Amount>11.13</Amount>
<Currency>GBP</Currency>
<CardPAN>************4849</CardPAN>
<Created>2014-02-08T00:08:00</Created>
<Updated>2014-02-08T00:08:40</Updated>
<ResponseStatusCode>1</ResponseStatusCode>
<ResponseStatusReason>FULFILLED OK</ResponseStatusReason>
<HostedPageIdentifier>f3306487-d6ea-4200-9eea-99b1d6832a2e</HostedPageIdentifier>
<HostedPageURL>https://hps.dat.com/hps/</HostedPageURL>
<PaymentStatus>Paid</PaymentStatus>
<PaymentType>Debit Card</PaymentType>
<NameOnCard>Miss Jor </NameOnCard>
<DataCashRef>380010093738013</DataCashRef>
<MerchantID>21877049</MerchantID>
<ThreeDCard>1</ThreeDCard>
<ThreeDRequested>1</ThreeDRequested>
<IPAddress>86..25640.99</IPAddress>
</TransactionSummary>
<OrderLine>
<OrderLineID>84598874</OrderLineID>
<OrderID>34209674</OrderID>
<OrderLineLabel>3-1008617753325</OrderLineLabel>
<OrderLineSource>YTR</OrderLineSource>
<Quantity>1</Quantity>
<UnitPrice>11.13</UnitPrice>
<Total>11.13</Total>
<Title>Miss.</Title>
<FirstName>Jordan</FirstName>
<SecondName>oirut</SecondName>
<FamilyName>dfgsdfgs</FamilyName>
<Email>dfgsdfg#hotmail.com</Email>
<Mobile>654756464</Mobile>
<PostChannel>0</PostChannel>
<TelephoneChannel>0</TelephoneChannel>
<EmailChannel>0</EmailChannel>
<TextAndOtherChannel>0</TextAndOtherChannel>
<BuildingNumber>12</BuildingNumber>
<AddressLine1>12</AddressLine1>
<AddressLine2>sfgsdfg End Gardens</AddressLine2>
<Town>HEMEL sfgaefa</Town>
<Country>adf dgfsdfg</Country>
<Postcode>HP1 1SN</Postcode>
<OrderLineDetail>
<NameValuePair>
<Name>dfgsdfg</Name>
<Value>628</Value>
</NameValuePair>
<NameValuePair>
<Name>NameOnCard</Name>
<Value>adsfgasdgf Piper</Value>
</NameValuePair>
<NameValuePair>
<Name>DateOnCard</Name>
<Value>2014-02-05</Value>
</NameValuePair>
<NameValuePair>
<Name>CustomsOrSurcharge</Name>
<Value>CUSTOMS CHARGE TO PAY</Value>
</NameValuePair>
</OrderLineDetail>
</OrderLine>
<OrderCustomerDetails>
<Title>Miss.</Title>
<FirstName>Jordan</FirstName>
<SecondName>asdgfasdgf</SecondName>
<FamilyName nil="true" />
<DateOfBirth />
<Email>adfadf#hotmail.com</Email>
<Telephone />
<Mobile>adfasdf</Mobile>
<Gender nil="true" />
<PostChannel nil="true" />
<TelephoneChannel nil="true" />
<EmailChannel nil="true" />
<TextAndOtherChannel nil="true" />
<BuildingNumber>12</BuildingNumber>
<AddressLine1>12</AddressLine1>
<AddressLine2>adfasdf End Gardens</AddressLine2>
<Town>adsfasdf HEMPSTEAD</Town>
<Country>United asdfasdf</Country>
<Postcode>asd 1SN</Postcode>
</OrderCustomerDetails>
</Order>
</Orders>
I tried using XSLT :
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<!-- Orders -->
<xsl:template match="/*">
<xsl:element name="Orders">
<xsl:apply-templates select="./Order" />
</xsl:element>
</xsl:template>
<!-- Orders > Order -->
<xsl:template match="/Order">
<xsl:variable name="IsValid">
<xsl:call-template name="HasOrIsValidPOLine" />
</xsl:variable>
<xsl:if test="$IsValid='VALID'"> <!-- only display the order if there's a valid line under it-->
<xsl:element name="Order">
<xsl:apply-templates select=".//VORNR" />
</xsl:element>
</xsl:if>
</xsl:template>
<!-- Part Order List > Part Order > Operational BO Number -->
<xsl:template match="//VORNR">
<xsl:element name="./Order">
<xsl:apply-templates select="node()|#*"/>
<xsl:value-of select="text()"/>
</xsl:element>
</xsl:template>
<xsl:template name="HasOrIsValidPOLine">
<xsl:choose>
<xsl:when test="./OrderLineSource/text() != 'YTR'">VALID</xsl:when>
<xsl:otherwise>INVALID</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Can you provide me the solution or let me know where I am going wrong
First, your sample XML is not well-formed: It contains a closing </TIPOLPolicy> tag that doesn't match the starting <HKPolicy> tag. Change that to </HKPolicy> first.
After that, the following XSLT 1.0 does what you want:
<?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"/>
<!-- Identity transform -->
<!-- Default priority 0 for root node and -0.5 for the rest -->
<xsl:template match="/ | node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*"/>
</xsl:copy>
</xsl:template>
<!-- Do nothing for Order elements whose OrderLine/OrderLineSource equals 'YTR' -->
<!-- Default priority 0.5 -->
<xsl:template match="Order[OrderLine/OrderLineSource = 'YTR']"/>
</xsl:stylesheet>
It makes use of the identity transform and different default priorities: The identity transform with a lower default priority copies the input to the output unless another template with a higher priority exists for a given input match. This is the case for Order elements whose OrderLine/OrderLineSource descendant contains the text value 'YTR'. Due to its higher default priority, the more specific template takes precedence over the identity transform. Since the template doesn't produce any output, any matching Order elements are removed from the output.
<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>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Order[OrderLine/OrderLineSource[string() = 'YTR']]"/>
</xsl:stylesheet>

XSLT - create new XML

I have one xml A and want to create a new xml B. The outer elements of B are different from A, but some of the child nodes are the same. I have written an XSL file but am struggling to get it right. Can anyone tell me where I am going wrong? How do I copy individual elements from A to B? The issue is, when the transformation happens, the text in A is copied to B. Looking at previous stack overflow questions, this is because there is an error in my xsl but I cant figure out what.
A:
<Request>
<Source>
<RequestorID Client="1" EMailAddress="test#test.com" Password="pwd"/>
<RequestorPreferences Country="JP" Currency="jpy" Language="en">
<RequestMode>SYNCHRONOUS</RequestMode>
</RequestorPreferences>
</Source>
<RequestDetails>
<SearchHotelPriceRequest>
<ItemDestination DestinationCode="LON" DestinationType="city"/>
<ItemCode>98i</ItemCode>
<PeriodOfStay>
<CheckInDate>2015-05-20</CheckInDate>
<CheckOutDate>2015-05-21</CheckOutDate>
</PeriodOfStay>
<IncludePriceBreakdown/>
<IncludeChargeConditions/>
<Rooms>
<Room Code="tb" NumberOfCots="0" NumberOfRooms="1">
<ExtraBeds/>
</Room>
</Rooms>
</SearchHotelPriceRequest>
</RequestDetails>
</Request>
B:
<WebRequest>
<RequestDetails>
<WebSearchHotelPriceRequest
CallCentreClientUI="2577"
Client="1"
Country="JP"
Currency="jpy"
Language="en"
LoginID="">
<GcPriceOptions ShowDeduped="true"/>
<ItemDestination DestinationCode="LON" DestinationType="city"/>
<ItemName></ItemName>
<ItemCode>98i</ItemCode>
<EffectiveDate>2014-08-15</EffectiveDate>
<StarRatingRange>
<Min>0</Min>
<Max>0</Max>
</StarRatingRange>
<PeriodOfStay>
<CheckInDate>2015-05-20</CheckInDate>
<CheckOutDate>2015-05-21</CheckOutDate>
</PeriodOfStay>
<IncludeChargeConditions/>
<Rooms>
<Room Code="tb" NumberOfRooms="1"></Room>
</Rooms>
<SiteId>008</SiteId>
</WebSearchHotelPriceRequest>
</RequestDetails>
</WebRequest>
XSL:
<?xml version="1.0"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="Request">
<xsl:element name="WebRequest">
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<xsl:template match="RequestDetails">
<xsl:element name="RequestDetails">
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<xsl:template match="SearchHotelPriceRequest">
<xsl:element name="WebSearchHotelPriceRequest">
<xsl:attribute name="CallCentreClientUI">1</xsl:attribute>
<xsl:attribute name="Client">1</xsl:attribute>
<xsl:attribute name="Country">UK</xsl:attribute>
<xsl:attribute name="Currency">GBP</xsl:attribute>
<xsl:attribute name="Language">EN</xsl:attribute>
<xsl:attribute name="LoginID">100</xsl:attribute>
<SiteId>001</SiteId>
<EffectiveDate>01-01-99</EffectiveDate>
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<xsl:template match="ItemDestination">
<xsl:copy>
<xsl:copy-of select="#*" />
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<xsl:template match="ItemCode">
<xsl:copy>
<xsl:copy-of select="#*" />
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<xsl:template match="ItemName">
<xsl:copy>
<xsl:copy-of select="#*" />
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<xsl:template match="PeriodOfStay">
<xsl:copy>
<xsl:copy-of select="node()" />
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<xsl:template match="IncludeChargeConditions">
<xsl:copy>
<xsl:copy-of select="node()" />
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<xsl:template match="Rooms">
<xsl:copy>
<xsl:copy-of select="node()" />
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Incorrect Output:
<?xml version="1.0" encoding="UTF-8"?>
<WebRequest>SYNCHRONOUS<RequestDetails>
<WebSearchHotelPriceRequest CallCentreClientUI="1" Client="1" Country="UK" Currency="GBP" Language="EN" LoginID="100">
<SiteId>001</SiteId>
<EffectiveDate>01-01-99</EffectiveDate>
<ItemDestination DestinationCode="LON" DestinationType="city"/>
<ItemCode>98i</ItemCode>
<PeriodOfStay>
<CheckInDate>2015-05-20</CheckInDate>
<CheckOutDate>2015-05-21</CheckOutDate>2015-05-202015-05-21</PeriodOfStay>
<IncludeChargeConditions/>
<Rooms>
<Room Code="tb" NumberOfCots="0" NumberOfRooms="1">
<ExtraBeds/>
</Room>
</Rooms>
</WebSearchHotelPriceRequest>
</RequestDetails>
</WebRequest>
I think you are making this much more complicated than it needs to be. The simplest way to copy elements (along with all they contain, i.e. "deep copy") is to use xsl:copy-of. Also, you don't need to use xsl:element if you know the name of the element; just output the element literally. Similarly, for xsl:attribute you can write it directly and - if necessary - use attribute value template to insert the value from the input.
Have a look at the following stylesheet as an example:
<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:template match="/">
<WebRequest>
<xsl:for-each select="Request/RequestDetails/SearchHotelPriceRequest">
<RequestDetails>
<WebSearchHotelPriceRequest
CallCentreClientUI="2577"
Client="2577"
Country="J"
Currency="USD"
Language="E"
LoginID="">
<GcPriceOptions ShowDeduped="true"/>
<xsl:copy-of select="ItemDestination"/>
<ItemName></ItemName>
<xsl:copy-of select="ItemCode"/>
<EffectiveDate>2014-08-15</EffectiveDate>
<StarRatingRange>
<Min>0</Min>
<Max>0</Max>
</StarRatingRange>
<xsl:copy-of select="PeriodOfStay | IncludeChargeConditions | Rooms"/>
<SiteId>008</SiteId>
</WebSearchHotelPriceRequest>
</RequestDetails>
</xsl:for-each>
</WebRequest>
</xsl:template>
</xsl:stylesheet>
This is of course not exactly what you need, but I don't know which values go where.
If someone could tell me why the extra text (e.g. SYNCHRONOUS) is
being erroneously copied?
Because you are using xsl:apply templates indiscriminately - and text nodes are copied by default using the built-in templates.

I need to define a xslt to combine couple of elements with separators for custom defined xsd types

I have couple of custom types which have 2, 3 or 4 childs. So wherever I get these childs I need to combine them into a single element which is the parent tag itself in the output XML. I tried but could not do due to lack of experience with xslt. Can any one help.
My input XML.
<PERSON>
<ID>194</ID>
<NAME>IKHAJA</NAME>
<DETAILS>
<NUMBER>100</NUMBER>
<Description />
<NUMBER01 />
<NUMBER02>Test</NUMBER02>
</DETAILS>
<STATUS>
<NUMBER>ACTIVE</NUMBER>
<Description>ACTIVE</Description>
<NUMBER01 />
<NUMBER02>ACTIVE</NUMBER02>
</STATUS>
<employer>
<ID>123456</ID>
<FNAME>EMPLOYER F NAME</FNAME>
<LNAME>EMPLOYER L NAME</LNAME>
</employer>
<PERSON_OFF>
<TYPE>
<NUMBER>41</NUMBER>
<Description>AMPLIFIERS</Description>
<NUMBER01>77</NUMBER01>
<NUMBER02 />
</TYPE>
<REPORT>
<NUMBER />
<Description />
<NUMBER01 />
<NUMBER02 />
</REPORT>
<SERIAL>111</SERIAL>
<ADDITIONAL_DESC>TEST</ADDITIONAL_DESC>
<KEY>5</KEY>
<CREATED_BY>Test Guy</CREATED_BY>
<CREATED_ON>2013-03-13T10:03:00</CREATED_ON>
<PERSON_OFF_ONE>
<BULK>
<NUMBER>98078</NUMBER>
<Description>BULK</Description>
<NUMBER01 />
<NUMBER02>8563</NUMBER02>
</BULK>
<CHECKED>Y</CHECKED>
</PERSON_OFF_ONE>
</PERSON_OFF>
</PERSON>
And my output XML should be like this:
<PERSON>
<ID>194</ID>
<NAME>IKHAJA</NAME>
<DETAILS>100;;;Test</DETAILS>
<STATUS>ACTIVE;ACTIVE;;ACTIVE</STATUS>
<employer>123456:EMPLOYER F NAME,EMPLOYER L NAME</employer>
<PERSON_OFF>
<TYPE>41;AMPLIFIERS;77;</TYPE>
<REPORT>;;;</REPORT>
<SERIAL>111</SERIAL>
<ADDITIONAL_DESC>TEST</ADDITIONAL_DESC>
<KEY>5</KEY>
<CREATED_BY>Test Guy</CREATED_BY>
<CREATED_ON>2013-03-13T10:03:00</CREATED_ON>
<PERSON_OFF_ONE>
<BULK>98078;BULK;;8563</BULK>
<CHECKED>Y</CHECKED>
</PERSON_OFF_ONE>
</PERSON_OFF>
</PERSON>
If you observe here details, status, bulk etc. are my custom types which have child nodes NUMBER, Description, NUMBER01, NUMBRER02. and I need to combine them with a separator ";" if they are empty or null just I will have ";;;" in my destination column as shown in REPORT field.
Also I have some fields of employer type like employer with childs ID, FNAME and LNAME and I should combine them as ID: FNAME, LNAME as shown in employer field.
I think if I know handling one custom type, I can handle the other types easily.
Can you please help? I already spent whole day on this and I need to do this very badly ASAP.
OP's comment to the answer by JLRishe:
It works if I list all of my custom types but, here I have so many
fields that are of my custom types. Instead of listing out all those
like "DETAILS | STATUS | TYPE | REPORT | BULK", is there any way to
merge fields of these.
This can be done easily, using the fact that all these possibly hundreds of parent elements have one of four children.
A minor adjustment to JLRishe's solution works with unlimited number of parent names:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[NUMBER|Description|NUMBER01|NUMBER02]">
<xsl:copy>
<xsl:apply-templates select="*" mode="delimit" />
</xsl:copy>
</xsl:template>
<xsl:template match="*[position() > 1]" mode="delimit">
<xsl:value-of select="concat(';', .)"/>
</xsl:template>
<xsl:template match="employer">
<xsl:copy>
<xsl:apply-templates select="*" mode="idlist" />
</xsl:copy>
</xsl:template>
<xsl:template match="ID" mode="idlist">
<xsl:value-of select="concat(., ':')" />
</xsl:template>
<xsl:template match="*[not(self::ID)][position() > 1]" mode="idlist">
<xsl:value-of select="concat(',', .)" />
</xsl:template>
</xsl:stylesheet>
As you see, this transformation doesn't mention at all any parent names such as DETAILS, STATUS, TYPE, REPORT, BULK, ..., etc.
and when applied on the provided XML document, produces the wanted, correct result:
<PERSON>
<ID>194</ID>
<NAME>IKHAJA</NAME>
<DETAILS>100;;;Test</DETAILS>
<STATUS>ACTIVE;ACTIVE;;ACTIVE</STATUS>
<employer>123456:EMPLOYER F NAME,EMPLOYER L NAME</employer>
<PERSON_OFF>
<TYPE>41;AMPLIFIERS;77;</TYPE>
<REPORT>;;;</REPORT>
<SERIAL>111</SERIAL>
<ADDITIONAL_DESC>TEST</ADDITIONAL_DESC>
<KEY>5</KEY>
<CREATED_BY>Test Guy</CREATED_BY>
<CREATED_ON>2013-03-13T10:03:00</CREATED_ON>
<PERSON_OFF_ONE>
<BULK>98078;BULK;;8563</BULK>
<CHECKED>Y</CHECKED>
</PERSON_OFF_ONE>
</PERSON_OFF>
</PERSON>
Please give this a try:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="DETAILS | STATUS | TYPE | REPORT | BULK">
<xsl:copy>
<xsl:apply-templates select="*" mode="delimit" />
</xsl:copy>
</xsl:template>
<xsl:template match="*[position() > 1]" mode="delimit">
<xsl:value-of select="concat(';', .)"/>
</xsl:template>
<xsl:template match="employer">
<xsl:copy>
<xsl:apply-templates select="*" mode="idlist" />
</xsl:copy>
</xsl:template>
<xsl:template match="ID" mode="idlist">
<xsl:value-of select="concat(., ':')" />
</xsl:template>
<xsl:template match="*[not(self::ID)][position() > 1]" mode="idlist">
<xsl:value-of select="concat(',', .)" />
</xsl:template>
</xsl:stylesheet>
When run on your sample input, the result is:
<PERSON>
<ID>194</ID>
<NAME>IKHAJA</NAME>
<DETAILS>100;;;Test</DETAILS>
<STATUS>ACTIVE;ACTIVE;;ACTIVE</STATUS>
<employer>123456:EMPLOYER F NAME,EMPLOYER L NAME</employer>
<PERSON_OFF>
<TYPE>41;AMPLIFIERS;77;</TYPE>
<REPORT>;;;</REPORT>
<SERIAL>111</SERIAL>
<ADDITIONAL_DESC>TEST</ADDITIONAL_DESC>
<KEY>5</KEY>
<CREATED_BY>Test Guy</CREATED_BY>
<CREATED_ON>2013-03-13T10:03:00</CREATED_ON>
<PERSON_OFF_ONE>
<BULK>98078;BULK;;8563</BULK>
<CHECKED>Y</CHECKED>
</PERSON_OFF_ONE>
</PERSON_OFF>
</PERSON>

Replace all instances of a string in XML with ****

I have a XSL that needs to filter out specific data found in the XML.
Somewhere in my XML there will be a node like:
<id root="2.16.840.1.113883.3.51.1.1.6.1" extension="9494949494949" />
The XSL I have below deletes the extension node and adds a nullFlavor="MSK" to the node.
What I need to do now, is take the value from the extension node, and search the entire XML document for that value, and replace it with **.
But I'm not sure how to take the extension attribute, and find all instances of that value in the XML (they could be burried in text and inside attributes) and turn them into ** (4 *).
The example below is just an example. I cannot hard code the XSL to look at specific nodes, it needs to look through all text / attribute text in the xml (reason for this is there are 5+ different versions of XML that this will be applied to).
I need to find the Extension in the node, then replace (delete really) that value from the rest of the XML. I'm looking for a 1 solution fits all messages, so a global search->wipe of the Extension value.
Example:
<identifiedPerson classCode="IDENT">
<id root="2.16.840.1.113883.3.51.1.1.6.1" extension="9494949494949" displayable="true" />
<addr use="PHYS">
<city>KAMLOOPS</city>
<country>CA</country>
<postalCode>V1B3C1</postalCode>
<state>BC</state>
<streetAddressLine>1A</streetAddressLine>
<streetAddressLine>2A</streetAddressLine>
<streetAddressLine>9494949494949</streetAddressLine>
<streetAddressLine>4A</streetAddressLine>
</addr>
<note text="9494949494949 should be stars"/>
Should be (The below XSLT already masks the extension in the node with the matching OID).
<identifiedPerson classCode="IDENT">
<id root="2.16.840.1.113883.3.51.1.1.6.1" nullFlavor="MSK" displayable="true" />
<addr use="PHYS">
<city>KAMLOOPS</city>
<country>CA</country>
<postalCode>V1B3C1</postalCode>
<state>BC</state>
<streetAddressLine>1A</streetAddressLine>
<streetAddressLine>2A</streetAddressLine>
<streetAddressLine>****</streetAddressLine>
<streetAddressLine>4A</streetAddressLine>
</addr>
<note text="**** should be stars"/>
Any help would be appreciated.
I am able to use XSL 2.0
I have the current XSL.IT works fine. It matches any tag where the root is '2.16.840.1.113883.3.51.1.1.6.1', kills all attributes and adds a nullFlavor="MSK". However, this will not search the entire XML for that same #.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:param name="attrToKeep" select="'root'" />
<xsl:template match="* | node()">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="#*">
<xsl:choose>
<xsl:when test="../#root = '2.16.840.1.113883.3.51.1.1.6.1'">
<xsl:copy-of select=".[contains($attrToKeep, name())]" />
<xsl:attribute name="nullFlavor">MSK</xsl:attribute>
<!-- Need some way to use the value found in this node and hide the extension -->
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="." />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Any help would be appreciated.
Thanks,
Try using a variable to hold the value of the text to be replaced. Like this:
<xsl:variable
name="rootVar"
select="//*[#root = '2.16.840.1.113883.3.51.1.1.6.1']/#extension" />
And then you should just be able to use the replace function to replace them.
<xsl:template match="'//#*' | text()">
<xsl:sequence select="replace(., $rootVar, '****')"/>
</xsl:template>
The XSLT 2.0 stylesheet
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:param name="replacement" select="'****'"/>
<xsl:param name="new" select="'MKS'"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="identifiedPerson">
<xsl:copy>
<xsl:apply-templates select="#* , node()">
<xsl:with-param name="to-be-replaced" select="id/#extension" tunnel="yes"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="identifiedPerson//text()">
<xsl:param name="to-be-replaced" tunnel="yes"/>
<xsl:sequence select="replace(., $to-be-replaced, $replacement)"/>
</xsl:template>
<xsl:template match="identifiedPerson//#*">
<xsl:param name="to-be-replaced" tunnel="yes"/>
<xsl:attribute name="{name()}" namespace="{namespace-uri()}" select="replace(., $to-be-replaced, $replacement)"/>
</xsl:template>
<xsl:template match="identifiedPerson/id">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="nullFlavor" select="$new"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="identifiedPerson/id/#extension"/>
</xsl:stylesheet>
transforms
<identifiedPerson classCode="IDENT">
<id root="2.16.840.1.113883.3.51.1.1.6.1" extension="9494949494949" displayable="true" />
<addr use="PHYS">
<city>KAMLOOPS</city>
<country>CA</country>
<postalCode>V1B3C1</postalCode>
<state>BC</state>
<streetAddressLine>1A</streetAddressLine>
<streetAddressLine>2A</streetAddressLine>
<streetAddressLine>9494949494949</streetAddressLine>
<streetAddressLine>4A</streetAddressLine>
</addr>
<note text="9494949494949 should be stars"/>
</identifiedPerson>
with Saxon 9.4 into
<?xml version="1.0" encoding="UTF-8"?><identifiedPerson classCode="IDENT">
<id root="2.16.840.1.113883.3.51.1.1.6.1" displayable="true" nullFlavor="MKS"/>
<addr use="PHYS">
<city>KAMLOOPS</city>
<country>CA</country>
<postalCode>V1B3C1</postalCode>
<state>BC</state>
<streetAddressLine>1A</streetAddressLine>
<streetAddressLine>2A</streetAddressLine>
<streetAddressLine>****</streetAddressLine>
<streetAddressLine>4A</streetAddressLine>
</addr>
<note text="**** should be stars"/>
</identifiedPerson>
So for the sample it solves that problem I think. I am not sure whether there can be more context around that sample and whether you want to change values outside of the identifiedPerson element as well or don't want to change them (which above stylesheet does). If other elements also need to be changed consider to post longer input and wanted result samples to illustrate and also explain what determines the node where the value to be replaced is found.
[edit]
Based on your comment I adapted the stylesheet, it now has a parameter to pass in a id (e.g. 2.16.840.1.113883.3.51.1.1.6.1), then it looks for an element of any name with a root attribute having that passed in id value and replaces the extension attribute value found in all attributes and all text nodes found in the document. Furthermore a nullFlavor attribute is added to the element with the id and its extension attribute is removed.
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:param name="root-id" select="'2.16.840.1.113883.3.51.1.1.6.1'"/>
<xsl:variable name="to-be-replaced" select="//*[#root = $root-id]/#extension"/>
<xsl:param name="replacement" select="'****'"/>
<xsl:param name="new" select="'MKS'"/>
<xsl:template match="comment() | processing-instruction()">
<xsl:copy/>
</xsl:template>
<xsl:template match="*">
<xsl:copy>
<xsl:apply-templates select="#* , node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:sequence select="replace(., $to-be-replaced, $replacement)"/>
</xsl:template>
<xsl:template match="#*">
<xsl:attribute name="{name()}" namespace="{namespace-uri()}" select="replace(., $to-be-replaced, $replacement)"/>
</xsl:template>
<xsl:template match="*[#root = $root-id]">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="nullFlavor" select="$new"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[#root = $root-id]/#extension"/>
</xsl:stylesheet>