Copying all XML elements and updating one of them in XSL - xslt

I have a requirement in my project some thing like this,
I have to copy all the elements from an xml and for few elements I have to update if present else I have to add it.
For example in the below xml I have an element Extrensic name "taxIncluded"> , in translated xml I want the value of it be updated.If it is missing I have to include it.
input xml 1
<?xml version="1.0" encoding="UTF-8"?>
<InvoiceHeader>
<Item1>
Item description
</Item1>
<Extrensic name="taxIncluded">
<percentage>
10%
</percentage>
</Extrensic>
</InvoiceHeader>
output
<?xml version="1.0" encoding="UTF-8"?>
<InvoiceHeader>
<Item1>
Item description
</Item1>
<Extrensic name="taxIncluded">
<percentage>
20%
</percentage>
</Extrensic>
</InvoiceHeader>
input xml 2
<?xml version="1.0" encoding="UTF-8"?>
<InvoiceHeader>
<Item1>
Item description
</Item1>
</InvoiceHeader>
output
<?xml version="1.0" encoding="UTF-8"?>
<InvoiceHeader>
<Item1>
Item description
</Item1>
<Extrensic name="taxIncluded">
<percentage>
20%
</percentage>
</Extrensic>
</InvoiceHeader>
I tried creating xsl but it is not working as expected, I thought of including it here but it is a very big xsl, in the above xml example I added only a part of it.
Could some one please help me how to do it?

Here are two ways to do it with XSLT 2.0 ...
Method 1:
<xsl:transform
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:param name="taxIncluded" select="20" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="InvoiceHeader[not( Extrensic[#name='taxIncluded'])]">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
<Extrensic name="taxIncluded">
<percentage>
<xsl:value-of select="$taxIncluded" />%
</percentage>
</Extrensic>
</xsl:copy>
</xsl:template>
<xsl:template match="Extrensic[#name='taxIncluded']/percentage">
<xsl:copy>
<xsl:value-of select="$taxIncluded" />%
</xsl:copy>
</xsl:template>
</xsl:transform>
Method 2:
<xsl:transform
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:param name="taxIncluded" select="20" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="InvoiceHeader">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
<xsl:variable name="Ex" as="element(Extrensic)?">
<xsl:if test="not( Extrensic[#name='taxIncluded'])">
<Extrensic name="taxIncluded" />
</xsl:if>
</xsl:variable>
<xsl:apply-templates select="$Ex" />
</xsl:copy>
</xsl:template>
<xsl:template match="Extrensic[#name='taxIncluded']">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<percentage>
<xsl:value-of select="$taxIncluded" />%
</percentage>
</xsl:copy>
</xsl:template>
</xsl:transform>

Related

XSLT create new tag if exists else update tag if exists

Is there a way for me to update the label element to Selma if "LastName" exists and if the label LastName doesn't exist then add the "LastName" and "label" elements to the XML?
<xml>
<udfs>
<udf>
<desc>FirstName</desc>
<label>Sam</label>
</udf>
<udf>
<desc>LastName</desc>
<label>Selman</label>
</udf>
</udfs>
</xml>
Here's what I have right now:
<xsl:stylesheet>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="udf[desc='LastName']/fieldValue">
<xsl:value-of select="'Selma'"/>
</xsl:template>
<xsl:template match="udf[not(desc='LastName')]">
<desc>LastName</desc>
<label>Selma</label>
</xsl:template>
</xsl:stylesheet>
I think you want to do:
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="udf[desc='LastName']/label">
<label>Selma</label>
</xsl:template>
<xsl:template match="udfs[not(udf/desc='LastName')]">
<xsl:copy>
<xsl:apply-templates/>
<udf>
<desc>LastName</desc>
<label>Selma</label>
</udf>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

xslt for converting one element to attribute among multiple

I have an xml with subelements and need to change one of the subelement to attribute using XSLT 1.0
<TransportationRequest>
<actionCode>01</actionCode>
<ContractConditionCode>DC</ContractConditionCode>
<ShippingTypeCode>17</ShippingTypeCode>
<MovementTypeCode>3</MovementTypeCode>
<DangerousGoodsIndicator>false</DangerousGoodsIndicator>
<DefaultCurrencyCode>SAR</DefaultCurrencyCode>
The Expected xml is as below using the XSLT code:
<TransportationRequest actionCode="01">
<ContractConditionCode>DC</ContractConditionCode>
<ShippingTypeCode>17</ShippingTypeCode>
<MovementTypeCode>3</MovementTypeCode>
<DangerousGoodsIndicator>false</DangerousGoodsIndicator>
<DefaultCurrencyCode>SAR</DefaultCurrencyCode>
First, close the root tag on your xml:
<TransportationRequest>
<actionCode>01</actionCode>
<ContractConditionCode>DC</ContractConditionCode>
<ShippingTypeCode>17</ShippingTypeCode>
<MovementTypeCode>3</MovementTypeCode>
<DangerousGoodsIndicator>false</DangerousGoodsIndicator>
<DefaultCurrencyCode>SAR</DefaultCurrencyCode>
</TransportationRequest>
then this xsl will do what you asked for:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" encoding="UTF-8" />
<xsl:template match="/">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#*|node()">
<!-- Copy every node as is -->
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="TransportationRequest">
<!-- transform the TransportationRequest node in a special way -->
<xsl:element name="TransportationRequest">
<xsl:attribute name="actionCode"><xsl:value-of select="actionCode" /></xsl:attribute>
<!-- don't transform the actionCode node (is in the attribute) -->
<xsl:apply-templates select="#*|node()[name()!='actionCode']"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
You could to it like this :
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="TransportationRequest">
<xsl:copy>
<xsl:attribute name="actionCode">
<xsl:value-of select="actionCode"/>
</xsl:attribute>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="actionCode"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
See it working here : https://xsltfiddle.liberty-development.net/naZYrpW/1

If attribute exists, remove it and put its value as element's value

I am trying to remove specific attributes and put their values as element values surrounded by #.
My knowledge of XSLT is, unfortunately, so elementary that I could not translate any similar question to something that I could use.
Whatever I put inside
<xsl:template match="#Attr">
</xsl:template>
just deletes the attribute.
In short, XML like:
<Parent>
<Elem1 Attr="Something" OtherAttr="Other">ExistingValue</Elem1>
<Elem2 Attr="SomethingElse" />
</Parent>
should become:
<Parent>
<Elem1 OtherAttr="Other">#Something#</Elem1>
<Elem2>#SomethingElse#</Elem2>
</Parent>
If an element already has a value it should be replaced. Attributes other than one named Attr, if they exist, should be left unchanged. Elements that don't have attribute Attr should be left unchanged.
If an element already has a value it should be replaced.
If you want to modify the element, you must operate on the element, not on the attribute.
Try it this way:
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="*[#Attr]">
<xsl:copy>
<xsl:apply-templates select="#*[not(name()='Attr')]"/>
<xsl:value-of select="concat('#', #Attr, '#')"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Use this XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:template match="*">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[#Attr]">
<xsl:copy>
<xsl:copy-of select="#* except #Attr"/>
<xsl:value-of select="#Attr"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Its been a while since I used XSLT but something like this should work:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" version="1.0">
<xsl:output encoding="UTF-8" indent="yes" method="xml" standalone="no" omit-xml-declaration="no"/>
<xsl:template match="/">
<xsl:apply-templates select="*"/>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{name()}">
<xsl:apply-templates select="#*"/>
<xsl:apply-templates select="*"/>
</xsl:element>
</xsl:template>
<xsl:template match="#*">
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>

XSLT - transform several sibling tags, but keep the rest intact

I'm trying to transform my input XML:
<?xml version="1.0" encoding="utf-8"?>
<request>
<customer_data>
<contact_info>
<name>john doe</name>
<dob_year>1984</dob_year>
<dob_month>09</dob_month>
<dob_date>14</dob_date>
<gender>m</gender>
</contact_info>
</customer_data>
</request>
so that it looks like this:
<?xml version="1.0" encoding="utf-8"?>
<request>
<customer_data>
<contact_info>
<name>john doe</name>
<dob>1984-09-14</dob>
<gender>m</gender>
</contact_info>
</customer_data>
</request>
Here is the XSLT, that I'm using:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="contact_info">
<xsl:copy>
<dob>
<xsl:value-of select="(dob_year, dob_month, dob_date)" separator="-"/>
</dob>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
And here is the result that I get:
<?xml version="1.0" encoding="utf-8"?><request>
<customer_data>
<contact_info><dob>1984-09-14</dob></contact_info>
</customer_data>
</request>
How do I transform the dob_* tags, keeping the rest of contact_info intact?
UPDATE
Based, on the answers below, my current solution is
<xsl:template match="contact_info">
<xsl:copy>
<xsl:apply-templates select="*[not(starts-with(name(), 'dob'))]"/>
<dob>
<xsl:value-of select="(dob_year, dob_month, dob_date)" separator="-"/>
</dob>
</xsl:copy>
</xsl:template>
This works for me, but isn't there more elegant way to express "apply transformation to given elements, and apply-templates to the rest"? Now, I'm kind of stuck with this expression *[not(starts-with(name(), 'dob'))] which is not that bad, but if the names of "DOB" attributes change, I'll have to fix this too.
Here's one way you could look at it:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="dob_year">
<dob>
<xsl:value-of select="(., ../dob_month, ../dob_date)" separator="-"/>
</dob>
</xsl:template>
<xsl:template match="dob_month|dob_date"/>
</xsl:stylesheet>
Here's another:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="contact_info">
<xsl:copy>
<xsl:copy-of select="name|gender"/>
<dob>
<xsl:value-of select="(dob_year, dob_month, dob_date)" separator="-"/>
</dob>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Edit:
In order to enumerate only the DOB elements and to do so only once, you could do:
<xsl:template match="contact_info">
<xsl:variable name="dob-fields" select="(dob_year, dob_month, dob_date)" />
<xsl:copy>
<xsl:apply-templates select="* except $dob-fields"/>
<dob>
<xsl:value-of select="$dob-fields" separator="-"/>
</dob>
</xsl:copy>
</xsl:template>
Change
<xsl:template match="contact_info">
<xsl:copy>
<dob>
<xsl:value-of select="(dob_year, dob_month, dob_date)" separator="-"/>
</dob>
</xsl:copy>
</xsl:template>
to
<xsl:template match="contact_info">
<xsl:copy>
<xsl:apply-templates select="dob_year/preceding-sibling::node()"/>
<dob>
<xsl:value-of select="(dob_year, dob_month, dob_date)" separator="-"/>
</dob>
<xsl:apply-templates select="dob_date/following-sibling::node()"/>
</xsl:copy>
</xsl:template>

How to remove the rootnodes using XSLT?

Input file:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:root xmlns:ns0="http://xyz.com/separate">
<ns0:root1>
<ns3:Detail xmlns:ns3="http://POProject/Details">
<DetailLines>
<ItemID>
<ItemDescription/>
</DetailLines>
</ns3:Detail>
</ns0:root1>
</ns0:root>
Output file:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:Detail xmlns:ns0="http://POProject/Details">
<DetailLines>
<ItemID>
<ItemDescription/>
</DetailLines>
</ns0:Detail>
Question: I have to remove the root1 and root nodes and need to do small
changes in Detail node. How to write a xslt code to achieve this?
This...
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://xyz.com/separate"
xmlns:ns3="http://POProject/Details">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates select="*/*/ns3:Detail" />
</xsl:template>
<xsl:template match="ns3:Detail">
<xsl:apply-templates select="." mode="copy-sans-namespace" />
</xsl:template>
<xsl:template match="*" mode="copy-sans-namespace">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates mode="copy-sans-namespace" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
...will yield this...
<?xml version="1.0" encoding="utf-8"?>
<ns3:Detail xmlns:ns3="http://POProject/Details">
<DetailLines>
<ItemID />
<ItemDescription />
</DetailLines>
</ns3:Detail>
I'm not sure it is possible to control the prefix. The XDM data model does not consider it to be significant information.
UDPATE
To get the prefix rename, I thought you would have to go to an XML 1.1 supporting XSLT processor (allowing prefix undefine), but I found a way to do it with XML 1.0 . Try this ...
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://xyz.com/separate">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/" xmlns:ns3="http://POProject/Details">
<xsl:apply-templates select="*/*/ns3:Detail" />
</xsl:template>
<xsl:template match="ns0:Detail" xmlns:ns0="http://POProject/Details">
<ns0:Detail xmlns:ns0="http://POProject/Details">
<xsl:apply-templates select="*" mode="copy-sans-namespace" />
</ns0:Detail>
</xsl:template>
<xsl:template match="*" mode="copy-sans-namespace">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates mode="copy-sans-namespace" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>