XSLT change from elements to attribute format - xslt

What would be the XSLT to change this XML
<?xml version="1.0" encoding="utf-8"?>
<cas:ADDRESS_DETAILS PRIMARY_ADDRESS_INDICATOR="1" ADDRESS_ID="-289495914026885120" ADDRESS_TYPE="45001" ADDRESS_ACTIVE_FROM_DATE="2006-12-23" PERSON_ID="14512823342202880">
<cas:ADDRESS_ELEMENT VALUE="McMurchy Avenue" TYPE="ADD2" />
<cas:ADDRESS_ELEMENT VALUE="ON" TYPE="PROV" />
<cas:ADDRESS_ELEMENT VALUE="CA" TYPE="COUNTRY" />
<cas:ADDRESS_ELEMENT VALUE="Brampton" TYPE="CITY" />
<cas:ADDRESS_ELEMENT VALUE="440" TYPE="ADD1" />
</cas:ADDRESS_DETAILS>
In to this format
<?xml version="1.0" encoding="utf-8"?>
<cas:ADDRESS_DETAILS PRIMARY_ADDRESS_INDICATOR="1" ADDRESS_ID="-289495914026885120" ADDRESS_TYPE="45001" ADDRESS_ACTIVE_FROM_DATE="2006-12-23" PERSON_ID="14512823342202880" ADD2 ="McMurchy" PROV="ON" COUNTRY="CA" CITY="Brampton" ADD1="440">
</cas:ADDRESS_DETAILS>

Assuming you want to merge all ADDRESS_ELEMENTs inside their parent you can use
<xsl:template match="ADDRESS_ELEMENT[1]">
<xsl:copy>
<xsl:apply-templates select="../ADDRESS_ELEMENT" mode="to-attribute"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ADDRESS_ELEMENT[position() > 1]"/>
<xsl:template match="ADDRESS_ELEMENT" mode="to-attribute">
<xsl:attribute name="{#TYPE}" select="#VALUE"/>
</xsl:template>
plus the identity transformation to handle the rest (i.e. <xsl:mode on-no-match="shallow-copy"/> in XSLT 3 (https://xsltfiddle.liberty-development.net/6qM2e2q) or the corresponding template in earlier versions)
If you want to transform the child elements into attributes of the parent, as your edit seems to indicate, you can simplify the code. Using a namespace requires some adaption however:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xpath-default-namespace="http://example.com/cas"
version="3.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="ADDRESS_DETAILS">
<xsl:copy>
<xsl:apply-templates select="#*, ADDRESS_ELEMENT"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ADDRESS_ELEMENT">
<xsl:attribute name="{#TYPE}" select="#VALUE"/>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/6qM2e2q/2

Related

XSLT Transformation that add namespace prefix doesn't copy attributes

I have a requirement to add a namespace prefix to all my xml elements.
I have a working xslt for this but unfortunately it doesn't copy the attributes. What do I need to change to also copy these attributes.
This my code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:ns0="http://ns.hr-xml.org/2007-04-15" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:template match="*">
<xsl:element name="ns0:{local-name()}">
<xsl:apply-templates select="*|node()" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
The input XML is this:
<HumanResource schemaLocation="http://ns.hr-xml.org/2007-04-15 ../HumanResource.xsd">
<HumanResourceId idOwner="StaffingCompany">
<IdValue>123COMP</IdValue>
</HumanResourceId>
<HumanResourceId idOwner="StaffingCustomer">
<IdValue>456CUST</IdValue>
<!--double hre-id, due fms wnr-code swap-->
</HumanResourceId>
<HumanResourceStatus status="x:Assigned"/>
<ReferenceInformation>
<StaffingSupplierId idOwner="StaffingCustomer">
<IdValue>105964</IdValue>
</StaffingSupplierId>
<StaffingCustomerId idOwner="StaffingCompany">
<IdValue>MIN0000612</IdValue>
</StaffingCustomerId>
<StaffingCustomerId idOwner="StaffingCustomer">
<IdValue>ELI</IdValue>
</StaffingCustomerId>
<StaffingSupplierOrgUnitId idOwner="StaffingCompany">
<IdValue>2606379</IdValue>
</StaffingSupplierOrgUnitId>
<StaffingCustomerOrgUnitId idOwner="StaffingCustomer">
<IdValue>ELI</IdValue>
</StaffingCustomerOrgUnitId>
</ReferenceInformation>
<ResourceInformation>
<PersonName>
<FormattedName>J</FormattedName>
<LegalName>C</LegalName>
<GivenName>J</GivenName>
<PreferredGivenName>J</PreferredGivenName>
<MiddleName>C.J.</MiddleName>
<FamilyName primary="true"></FamilyName>
</PersonName>
<EntityContactInfo>
<EntityName></EntityName>
<PersonName>
<FormattedName>Liesbeth Kramer</FormattedName>
</PersonName>
<ContactMethod>
<Telephone>
<FormattedNumber>070-3102150</FormattedNumber>
</Telephone>
</ContactMethod>
</EntityContactInfo>
<PostalAddress>
<CountryCode>NL</CountryCode>
<PostalCode></PostalCode>
<Municipality></Municipality>
<DeliveryAddress>
<AddressLine></AddressLine>
<StreetName></StreetName>
<BuildingNumber></BuildingNumber>
</DeliveryAddress>
</PostalAddress>
</ResourceInformation>
<Profile/>
<Preferences/>
<UserArea>
<ns2:HumanResourceAdditionalNL xmlns:ns2="http://ns.setu.nl/2008-01">
<ns2:BirthDate>1961-07-13</ns2:BirthDate>
<ns2:Sex>female</ns2:Sex>
</ns2:HumanResourceAdditionalNL>
</UserArea>
</HumanResource>
My result with my code looks like this:
<?xml version='1.0' ?>
<ns0:HumanResource xmlns:ns0="http://ns.hr-xml.org/2007-04-15">
<ns0:HumanResourceId>
<ns0:IdValue>123COMP</ns0:IdValue>
</ns0:HumanResourceId>
<ns0:HumanResourceId>
<ns0:IdValue>456CUST</ns0:IdValue>
</ns0:HumanResourceId>
<ns0:HumanResourceStatus/>
<ns0:ReferenceInformation>
As you can see, the ns0: is added ok but I lost the attributes.
You have lost the attributes because you have no code to select them. You currently do <xsl:apply-templates select="*|node()" />, but * select elements, and node() selects elements, text nodes, comments and processing instructions. You need to change it to this...
<xsl:apply-templates select="#*|node()" />
You would then need a separate template to match attributes and copy them.
<xsl:template match="#*">
<xsl:copy />
</xsl:template>
Try this XSLT
<xsl:stylesheet version="1.0" xmlns:ns0="http://ns.hr-xml.org/2007-04-15" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:template match="*">
<xsl:element name="ns0:{local-name()}">
<xsl:apply-templates select="#*|node()" />
</xsl:element>
</xsl:template>
<xsl:template match="#*">
<xsl:copy />
</xsl:template>
</xsl:stylesheet>
Alternatively, you could use xsl:copy-of to copy the attributes. Try this XSLT too
<xsl:stylesheet version="1.0" xmlns:ns0="http://ns.hr-xml.org/2007-04-15" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:template match="*">
<xsl:element name="ns0:{local-name()}">
<xsl:copy-of select="#*" />
<xsl:apply-templates />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Note that <xsl:apply-templates /> is short-hand for <xsl:apply-templates select="node()" />

XSLT add root node if not exists

I have some XML and having a difficult time transforming it.
Example XML:
<?xml version="1.0" encoding="utf-8"?>
<Cars xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Car> ... </Car>
</Cars>
I would like to change it to:
<?xml version="1.0" encoding="utf-8"?>
<Depot xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Cars>
<Car> ... </Car>
</Cars>
</Depot>
Sounds simple enough but the problem is some data is already in the expected format, in which case I don't want to apply the transform. How do I achieve this?
EDIT
Some starting XSLT:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" mlns:xsi="http://www.w3.org/2001/XMLSchema-
instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="Cars">
<Depot>
<Cars>
<xsl:apply-templates select="*"/>
</Cars>
</Depot>
</xsl:template>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I think you only want to match Cars if it is the root element, so instead of your template matching "Cars", change it to match "/Cars"
<xsl:template match="/Cars">
Try this XSLT (which I have slightly amended to get the first template to call the identity template)
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" >
<xsl:output method="xml" indent="yes" />
<xsl:template match="/Cars">
<Depot xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsl:call-template name="identity" />
</Depot>
</xsl:template>
<xsl:template match="#*|node()" name="identity">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I think that it is just necessary to use a choose in the root template to test if the node Depot exists, if not create it:
<xsl:template match="/">
<xsl:choose>
<xsl:when test="Depot">
<xsl:apply-templates/>
</xsl:when>
<xsl:otherwise>
<Depot>
<xsl:apply-templates/>
</Depot>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()" />
</xsl:copy>
</xsl:template>
This also gives same output.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" >
<xsl:template match="Cars">
<Depot>
<xsl:copy-of select="."/>
</Depot>
</xsl:template>
</xsl:stylesheet>

Copying all XML elements and updating one of them in XSL

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>

Generated wrong namespace

Is it possible that the namespace you declare will change if you generate an output? Is it because it gets the last version of this schema?
Below are the namespaces in my schema:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"xmlns:link="http://www.xbrl.org/2003/linkbase"xmlns="http://www.w3.org/2001/XMLSchema" xmlns:xbrli="http://www.xbrl.org/2003/instance" xmlns:gl-cor="http://www.xbrl.org/int/gl/cor/2006-10-25" targetNamespace="http://www.xbrl.org/int/gl/cor/2006-10-25" elementFormDefault="qualified" attributeFormDefault="unqualified">
and the output generated was like this:
<gl-cor:defter xmlns:gl-cor="http://www.xbrl.org/int/gl/cor/2010-04-12" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.xbrl.org/int/gl/cor/2010-04-12 gl-cor-content-2006-10-25.xsd">
if you notice that the namespace of xmlns:gl-cor in the output was changed to "http://www.xbrl.org/int/gl/cor/2010-04-12". I've checked all my files and there's no 2010-04-12 declared. Anyone knows the reason why it happened?
Here is my XSLT:
<?xml version="1.0" encoding="UTF-8"?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:gl-cor="http://www.xbrl.org/int/gl/cor/2010-04-12" xmlns:gl-bus="http://www.xbrl.org/int/gl/bus/2006-10-25" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:link="http://www.xbrl.org/2003/linkbase" xmlns:xbrli="http://www.xbrl.org/2003/instance">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="/gl-cor:defter">
<edefter:defter xmlns:edefter="http://www.edefter.gov.tr"
xmlns:xades="http://uri.etsi.org/01903/v1.3.2#"
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:apply-templates select="#*|node()"/>
</edefter:defter>
</xsl:template>
<xsl:template match="/gl-cor:defter/gl-cor:xbrl">
<xbrli:xbrl
xmlns:iso639="http://www.xbrl.org/2005/iso639"
xmlns:link="http://www.xbrl.org/2003/linkbase"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:gl-plt="http://www.xbrl.org/int/gl/plt/2006-10-25"
xmlns:iso4217="http://www.xbrl.org/2003/iso4217">
<xsl:apply-templates select="#*|node()"/>
</xbrli:xbrl>
</xsl:template>
<xsl:template match="*[ancestor::gl-cor:entityInformation]">
<xsl:element name="gl-bus:{local-name()}">
<xsl:copy-of select="#*" />
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<xsl:template match="*[self::gl-cor:creator]">
<xsl:element name="gl-bus:{local-name()}">
<xsl:copy-of select="#*" />
<xsl:apply-templates />
</xsl:element>
</xsl:template></xsl:stylesheet>
and my xml input file:
<gl-cor:defter xmlns:gl-cor="http://www.xbrl.org/int/gl/cor/2010-04-12" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.xbrl.org/int/gl/cor/2010-04-12 gl-cor-content-2006-10-25.xsd"><gl-cor:xbrl><gl-cor:accountingEntries><gl-cor:documentInfo><gl-cor:creator contextRef="ledger_context"></gl-cor:creator><gl-cor:entityInformation><gl-cor:entityPhoneNumber><gl-cor:phoneNumberDescription contextRef="ledger_context"></gl-cor:phoneNumberDescription>
Your XSLT just copies the input to the output (the first template is an identity transformation).
Since the namespace mappings for the prefix gl-cor are different in the XSLT and the XML input file, none of the other templates will match.

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>