XSL Move one level Element into other while last one is appearing - xslt

Given the input XML data:
<Report_Entry>
<Time_Off_Type_Group>
<Time_Off_Type Descriptor="Sickness Full" />
</Time_Off_Type_Group>
<Time_Off_Type_Group>
<Time_Off_Type Descriptor="Sickness Part" />
</Time_Off_Type_Group>
<Time_Off_Entry_ID>2d90199913fa9fae8</Time_Off_Entry_ID>
<Request_or_Correction>Time Off Request</Request_or_Correction>
</Report_Entry>
As a result, I expect the output data via the condition: "for-each Time_Off_Type_Group move Time_Off_Entry_ID and Request_or_Correction into the Time_Off_Type_Group"
Output example:
<Report_Entry>
<Time_Off_Type_Group>
<Time_Off_Type Descriptor="Sickness Full" />
<Time_Off_Entry_ID>2d90199913fa9fae8</Time_Off_Entry_ID>
<Request_or_Correction>Time Off Request</Request_or_Correction>
</Time_Off_Type_Group>
<Time_Off_Type_Group>
<Time_Off_Type Descriptor="Sickness Part" />
<Time_Off_Entry_ID>2d90199913fa9fae8</Time_Off_Entry_ID>
<Request_or_Correction>Time Off Request</Request_or_Correction>
</Time_Off_Type_Group>
</Report_Entry>

Think templates not for-each, so write a template for the Time_Off_Type_Group elements copying the siblings as children and make sure the default idendity copying does not apply to those siblings:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output indent="yes"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Time_Off_Type_Group">
<xsl:copy>
<xsl:copy-of select="*, ../(* except Time_Off_Type_Group)"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Report_Entry/*[not(self::Time_Off_Type_Group)]"/>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/bEzkTcn

Or shortly:
<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="/Report_Entry">
<xsl:variable name="common" select="Time_Off_Entry_ID | Request_or_Correction" />
<xsl:copy>
<xsl:for-each select="Time_Off_Type_Group">
<xsl:copy>
<xsl:copy-of select="* | $common"/>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Related

XSLT - Removing root node and first child node and placing grand child as root

Need help in following logic.
Source XML --
<?xml version="1.0" encoding="UTF-8"?>
<multimap:Messages xmlns:multimap="http://somenamespace.com/xi/XI/SplitAndMerge">
<multimap:Message1>
<ns0:ToBeRoot xmlns:ns0="http://mynamespace.com">
<ns0:children_level1 xmlns:ns0="http://mynamespace.com">
<Somedata/>
</ns0:children_level1>
</ns0:ToBeRoot>
<ns0:ToBeRoot xmlns:ns0="http://mynamespace.com">
<ns0:children_level2 xmlns:ns0="http://mynamespace.com">
<Somedata/>
</ns0:children_level2>
</ns0:ToBeRoot>
</multimap:Message1>
</multimap:Messages>
Target XML (required)
<?xml version="1.0" encoding="UTF-8"?>
<ns0:ToBeRoot xmlns:ns0="http://mynamespace.com">
<ns0:children_level1 xmlns:ns0="http://mynamespace.com">
<Somedata/>
</ns0:children_level1>
<ns0:children_level2 xmlns:ns0="http://mynamespace.com">
<Somedata/>
</ns0:children_level2>
</ns0:ToBeRoot>
XSLT I tried,
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns0="http://somenamespace.com/xi/XI/SplitAndMerge">
<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>
<!-- template for the document element -->
<xsl:template match="/*">
<xsl:apply-templates select="node()/node()/node()"/>
</xsl:template>
<xsl:template match="ToBeRoot">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I think something needs to be added to the template
<xsl:template match="ToBeRoot">
but struggling, please help.
The output using the above XSLT
<?xml version="1.0" encoding="UTF-8"?>
<ns0:children_level1 xmlns:ns0="http://mynamespace.com" xmlns:multimap="http://somenamespace.com/xi/XI/SplitAndMerge">
<Somedata/>
</ns0:children_level1>
<ns0:children_level2 xmlns:ns0="http://mynamespace.com" xmlns:multimap="http://somenamespace.com/xi/XI/SplitAndMerge">
<Somedata/>
</ns0:children_level2>
The simplest way would be:
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:template match="/*">
<ns0:ToBeRoot xmlns:ns0="http://mynamespace.com">
<xsl:copy-of select="*/*/*"/>
</ns0:ToBeRoot>
</xsl:template>
</xsl:stylesheet>
This leaves a redundant namespace declaration on the copied elements, which should be harmless. If you want to remove it, then you must recreate the elements instead of copying them:
<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="/*">
<ns0:ToBeRoot xmlns:ns0="http://mynamespace.com">
<xsl:apply-templates select="*/*/*"/>
</ns0:ToBeRoot>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Or, if your processor supports it:
XSLT 2.0
<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:template match="/*">
<ns0:ToBeRoot xmlns:ns0="http://mynamespace.com">
<xsl:copy-of select="*/*/*" copy-namespaces="no"/>
</ns0:ToBeRoot>
</xsl:template>
</xsl:stylesheet>
Added:
is it feasible to get the name ns0:ToBeRoot dynamically from the payload
Yes. Instead of:
<ns0:ToBeRoot xmlns:ns0="http://mynamespace.com">
<!-- ... -->
</ns0:ToBeRoot>
use:
<xsl:element name="{name(*/*[1])}" namespace="{namespace-uri(*/*[1])}">
<!-- ... -->
</xsl:element>
It sounds as if you want
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://mynamespace.com"
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="/">
<xsl:apply-templates select="*/*/ns0:ToBeRoot[1]"/>
</xsl:template>
<xsl:template match="ns0:ToBeRoot">
<xsl:copy>
<xsl:apply-templates select="node() | following-sibling::ns0:ToBeRoot/node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
or perhaps better XSLT 2 or 3:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://mynamespace.com"
version="2.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#* | node()">
<xsl:copy copy-namespaces="no">
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates select="*/*/ns0:ToBeRoot[1]"/>
</xsl:template>
<xsl:template match="ns0:ToBeRoot">
<xsl:copy copy-namespaces="no">
<xsl:apply-templates select="node() | following-sibling::ns0:ToBeRoot/node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
You had some mixup in your namespace declaration: xmlns:ns0 was "http://mynamespace.com" in your XML, but "http://somenamespace.com/xi/XI/SplitAndMerge" in your XSLT.
Synchronizing them would give you the following XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns0="http://mynamespace.com" xmlns:multimap="http://somenamespace.com/xi/XI/SplitAndMerge">
<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>
<!-- template for the document element -->
<xsl:template match="/multimap:Messages">
<xsl:element name="ns0:ToBeRoot" namespace="http://mynamespace.com">
<xsl:apply-templates select="multimap:Message1/ns0:ToBeRoot/ns0:*" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
It will give you the desired output:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:ToBeRoot xmlns:ns0="http://mynamespace.com">
<ns0:children_level1>
<Somedata/>
</ns0:children_level1>
<ns0:children_level2>
<Somedata/>
</ns0:children_level2>
</ns0:ToBeRoot>

Xslt copy element to existing or create new if not exists

Pretty new to xml transformations and i'm stuck at (might be for you) pretty easy task.
Let's suggest we have source:
<root>
<someValue>123</someValue>
</root>
It should be transformed into:
<root>
<additional>
<someValue>123</someValue>
</additional>
</root>
But if we have this as a source:
<root>
<additional>
<b>something</b>
</additional>
<someValue>123</someValue>
</root>
we should move someValue to existing additional, i.e.:
<root>
<additional>
<b>something</b>
<someValue>123</someValue>
</additional>
</root>
Keep in mind that there can be other elements at a level with same behavior (moved under additional).
Well, working example is much appreciated, but if it is accompanied by small description of how it works that would be fantastic (i prefer be tought to fish, rather than just being fed with it).
One possible approach would be to add a additional wrapper as a child of root, and remove the existing additional wrapper - so its children move up to become children of root (or rather children of the added additional wrapper):
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="/root">
<xsl:copy>
<additional>
<xsl:apply-templates/>
</additional>
</xsl:copy>
</xsl:template>
<xsl:template match="additional">
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
Keep in mind that there can be other elements at a level with same
behavior (moved under additional).
This stylesheet:
<xsl:stylesheet version="1.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="root[additional|someValue]">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<additional>
<xsl:apply-templates select="additional/*|someValue"/>
</additional>
<xsl:apply-templates select="node()[not(self::additional|self::someValue)]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
With this input:
<root>
<additional>
<b>something</b>
</additional>
<someValue>123</someValue>
<anotherValue>keep</anotherValue>
</root>
Output:
<root>
<additional>
<b>something</b>
<someValue>123</someValue>
</additional>
<anotherValue>keep</anotherValue>
</root>
Do note: just one rule to override the identity transformation. Only process root meeting the conditions (someValue or additional childs). Copying root, applying templates to attributes (to further process), wrapping with an additional element the result of applying templates to additional's childs and root's someValue childs. Finally, applying templates to root's childs, which are not additional nor someValue.
I ended up with following:
<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="/root/additional"/>
<xsl:template match="/root">
<xsl:copy>
<additional>
<xsl:copy-of select="someValue"/>
<xsl:copy-of select="additional/*"/>
</additional>
</xsl:copy>
</xsl:template>
Here we strip original additional and then create it from scratch copying there needed someValue and original content from source (additional/*)
Use for version xslt 2.0
<?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:output method="xml" omit-xml-declaration="no" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node() except someValue"/>
</xsl:copy>
</xsl:template>
<xsl:template match="additional">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
<xsl:copy-of select="following-sibling::someValue"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

XSLT copy without segment name

I have the following XML:
<segment>
<personal_information>
<birth_name>xxx</birth_name>
<created_by>yyy</created_by>
<created_on_timestamp>2018-08-06T06:41:07.000Z</created_on_timestamp>
</personal_information>
<segment>
I want to copy the entire personal_information segment with all elements and sub-segments while adding a new field. I tried this with:
<segment>
<personal_information>
<action>DELETE</action>
<xsl:copy>
<xsl:apply-templates select="child::node()"/>
</xsl:copy>
</personal_information>
</segment>
But this results in the following:
<segment>
<personal_information>
<action>DELETE</action>
<personal_information>
<birth_name>xxx</birth_name>
<created_by>yyy</created_by>
<created_on_timestamp>2018-08-06T06:41:07.000Z</created_on_timestamp>
</personal_information>
</personal_information>
</segment>
Would would be the XSLT code to achieve this as a result:
<segment>
<personal_information>
<action>DELETE</action>
<birth_name>xxx</birth_name>
<created_by>yyy</created_by>
<created_on_timestamp>2018-08-06T06:41:07.000Z</created_on_timestamp>
</personal_information>
</segment>
I do not want to copy all fields one by one.
Instead of using <xsl:copy>, use <xsl:copy-of>, which accepts an XPath expression for the nodes to be included in the copy; in this case only the inner childnodes.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="segment">
<segment>
<personal_information>
<action>DELETE</action>
<xsl:copy-of select="personal_information/*" />
</personal_information>
</segment>
</xsl:template>
</xsl:stylesheet>
You need a identity template and one personal_information with adding one action element:
<?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:output indent="yes"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="personal_information">
<xsl:copy>
<action>DELETE</action>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Updated as per new requirement :
<?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:output indent="yes"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="personal_information">
<action>DELETE</action>
<xsl:apply-templates select="node()"/>
</xsl:template>
</xsl:stylesheet>

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>

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>