Xslt copy element to existing or create new if not exists - xslt

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>

Related

Transform elements with some properties using group by attribute value

I have a requirement to transform below XML
<XML>
<Obj1 attr1="value1" attr2="value2" attr="10"/>
<Test1 tatt1="tvalue1" tatt2="tvalue2" attr="10"/>
<Obj1 attr1="value11" attr2="value21" attr="101"/>
<Test1 tatt1="tvalue11" tatt2="tvalue21" attr="101"/>
<Obj1 attr1="value12" attr2="value22" attr="102"/>
<Test1 tatt1="tvalue12" tatt2="tvalue22" attr="102"/>
</XML>
I want transformed XML like
<XML>
<Obj1 attr1="value1" attr2="value2" attr="10" tatt1="tvalue1"/>
<Obj1 attr1="value11" attr2="value21" attr="101" tatt1="tvalue11"/>
<Obj1 attr1="value12" attr2="value22" attr="102" tatt1="tvalue12"/>
</XML>
I have achieved it through normal pattern matching and finding the matching attribute value in all other elements. I doubt about the performance. So wanted to check if it can be done using group-by attribute name and combining attributes from all such elements into one.
I want to merge contents (all attributes of Obj1 and selected attributes from matching elements) of all matching elements having attr= into transformed XML.
Try this XSLT-1.0 stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output method="xml" indent="yes"/>
<xsl:key name="tests" match="Test1" use="#attr" />
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()"> <!-- Identity template: copies all nodes to the output - unless a more specific template matches -->
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Obj1"> <!-- Modifies all 'Obj1' elements -->
<xsl:copy>
<xsl:copy-of select="#*|key('tests',#attr)/#tatt1" />
</xsl:copy>
</xsl:template>
<xsl:template match="Test1" /> <!-- Removes all 'Test1' elements from the output -->
</xsl:stylesheet>
The output should be as desired. And the use of the xsl:key will improve the performance.
AFAICT, you could so simply:
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="/XML">
<xsl:copy>
<xsl:for-each select="Obj1">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:copy-of select="following-sibling::Test1[1]/#tatt1"/>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
If you want do it by matching the value of the attr attribute instead of by position, then it would become:
<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:key name="test" match="Test1" use="#attr"/>
<xsl:template match="/XML">
<xsl:copy>
<xsl:for-each select="Obj1">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:copy-of select="key('test', #attr)/#tatt1"/>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

XSLT 1.0 - Move certain elements into a new parent

Thank you for taking the time to look at my question
I have a source xml that looks like so
<root>
<a>asdf</a>
<b>asdfdsaf</b>
<c>adfasd</c>
<d>asddf</d>
<e></e>
<others>
<foo>sdfd</foo>
<bar>hghg</bar>
</others>
</root>
I need to wrap certain elements Eg:a,b,c into a new parent and remove empty elements.
so output like so
<root>
<newparent>
<a>asdf</a>
<b>asdfdsaf</b>
<c>adfasd</c>
</newparent>
<d>asddf</d>
<others>
<foo>sdfd</foo>
<bar>hghg</bar>
</others>
</root>
I managed to remove the empty elements using this XSL -
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[not(#*) and not(*) and (not(text()) or .=-1)]"/>
<xsl:template match="name">
</xsl:template>
</xsl:stylesheet>
Getting stuck at adding the newparent node. Any pointers/help is much appreciated.
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="/root">
<xsl:copy>
<newparent>
<xsl:apply-templates select="a|b|c"/>
</newparent>
<xsl:apply-templates select="*[not(self::a or self::b or self::c)]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[not(#* or node())]"/>
</xsl:stylesheet>
<xsl:template match="root">
<xsl:copy>
<newparent>
<xsl:copy-of select="a|b|c"/>
</newparent>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[.= '' or name() ='a' or name()='b' or name()='c']"/>
check it if it is fullfill your requirement.

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>

Using xslt to change an attribute value using arithmetic operations

I'm trying to bound the value of an xml attribute using xslt/xpath 1.0. In this example, it would be the id attribute on the m_m element.
<blart>
<m_data>
<m_m name="arggg" id="99999999" subs="asas"/>
</m_data>
<m_data>
<m_m name="arggg" id="99" subs="asas"/>
</m_data>
</blart>
If the id is greater then 20000 it gets set to 20000. I have the following xslt. I know it selects the correct node and attribute. It obviously is just outputing 20000. I realize I should have some sort of xpath logic in there but I'm having a hard time developing it. I have some big holes in my knowledge of xpath and xslt. If you can point me in the right direction in helping me understand on what I should be doing I would really appreciate it.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match ="m_data/m_m/#id[.> 20000]">20000 </xsl:template>
</xsl:stylesheet>
The expected output would be
<blart>
<m_data>
<m_m name="arggg" id="20000" subs="asas"/>
</m_data>
<m_data>
<m_m name="arggg" id="99" subs="asas"/>
</m_data>
</blart>
You can use the following XSLT that gives flexibility to the attribute you want to change, and keeps everything else as it is:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match ="m_data/m_m/#id[. > 20000]">
<xsl:attribute name="id">20000</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
Why don't you try:
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 ="m_m/#id[. > 20000]">
<xsl:attribute name="id">20000</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
NOTE: Since I posted this, much better answers were contributed (see here and here). SO won't let me delete this one because it was accepted, but in all fairness and for the sake of quality, I should encourage you to upvote the two aforementioned answers, so that they stand out over this one.
How about this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="m_m">
<m_m>
<xsl:copy-of select="#*" />
<xsl:if test="#id > 20000">
<xsl:attribute name="id">20000</xsl:attribute>
</xsl:if>
</m_m>
</xsl:template>
<xsl:template match="m_data">
<m_data>
<xsl:apply-templates select="m_m" />
</m_data>
</xsl:template>
<xsl:template match="/blart">
<blart>
<xsl:apply-templates select="m_data" />
</blart>
</xsl:template>
</xsl:stylesheet>

Adding a parent node to one or more child nodes of an XML using xslt

I am new to XSLT and i am trying to accomplish the follow case
I have an xml in the following format
<A>
<B>..</B>
<C>..</C>
..
<Z>..</Z>
</A>
I am trying to add a new node soon after so that the final xml will get transformed to
<A>
<aa>
<B>..</B>
<C>..</C>
..
<X>...</X>
</aa>
</A>
In order to achieve this i wrote the following xslt code
<?xml version="1.0" encoding="UTF-8"?>
<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="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="B">
<aa>
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</aa>
</xsl:template>
By using this i get the following output
<A>
<aa>
<B>..</B>
</aa>
<C>..</C>
..
<X>..</X>
</A>
I am not sure what kind of changes i need to make to the xslt to achieve the desired output
If you want to wrap all the children of A in a single aa then you need to do that in a template matching A, not B.
<?xml version="1.0" encoding="UTF-8"?>
<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="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="A">
<xsl:copy><!-- copy the A -->
<xsl:apply-templates select="#*" /><!-- attributes, if any -->
<aa><!-- insert the extra aa -->
<xsl:apply-templates /><!-- process children -->
</aa>
</xsl:copy>
</xsl:template>
<!-- this may be a typo in the question, but for reference, here's how
to rename Z to X. If you don't need to do this, just leave this template
out and let the identity template at the top handle it. -->
<xsl:template match="Z">
<X>
<xsl:apply-templates select="#*|node()"/>
</X>
</xsl:template>
</xsl:stylesheet>