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>
Related
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>
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.
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>
I have the following XML:
<myreport>
<media>
<assets>
<asset>
<type>image</type>
<H>224 mm</H>
<W>154 mm</W>
</asset>
<asset>
<type>video</type>
<H>480 px</H>
<W>600 px</W>
</asset>
</assets>
</myreport>
I need to restructure as follows:
<myreport>
<media>
<assets>
<image>
<H>224 mm</H>
<W>154 mm</W>
</image>
<video>
<H>480 px</H>
<W>600 px</W>
<video>
</assets>
</media>
</myreport>
How do I match type with height (H) width (W) to come out with the desire transformation. I used xsl:value-of select="node" for normal restructuring.
Start with the identity transformation, which copies nodes as they appear in the input XML:
<?xml version="1.0" encoding="UTF-8"?>
<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:stylesheet>
Add a special case for asset elements:
<xsl:template match="asset">
<xsl:element name="{type}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
Note that name={type} will name an outputed element per the value of the child type element.
Suppress type elements:
<xsl:template match="type"/>
Clarify output format:
<xsl:output method="xml" indent="yes"/>
Altogether:
<?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="asset">
<xsl:element name="{type}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="type"/>
</xsl:stylesheet>
This can be accomplished fairly easily with a modified identity transform. A template to match asset elements and instead of copying the asset element, use the value of it's type element as the name of the element to create, then apply-templates to the rest of it's children. An (empty) template that will suppress the type elements and any whitespace-only text() nodes.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!--instead of an asset element, create an element named after it's type-->
<xsl:template match="asset[type]">
<xsl:element name="{type}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<!--suppress the type element and whitespace-only text nodes -->
<xsl:template match="asset/type | text()[not(normalize-space())]"/>
</xsl:stylesheet>
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>