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>
Related
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>
I want to remove specific values from my XML but keep the tag names. I've seen examples that do the opposite (remove tags but keep values). Here is my XML:
<Result>
<Max>100</Max>
<Min>10</Min>
<Range>90</Range>
<ResultPoints>
<ResultP1>.</ResultP1>
<ResultP2>.</ResultP2>
<ResultP3>.</ResultP3>
<ResultP4>.</ResultP4>
<ResultP5>.</ResultP5>
</ResultPoints>
</Result>
I want to remove the '.' but keep the tag names so my XML will look like this:
<Result>
<Max>100</Max>
<Min>10</Min>
<Range>90</Range>
<ResultPoints>
<ResultP1/>
<ResultP2/>
<ResultP3/>
<ResultP4/>
<ResultP5/>
</ResultPoints>
</Result>
Here is my XLT. This completely removes the ResultPn tags.
<?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" version="1.0" encoding="UTF-8" indent="yes" omit-xml-declaration="no"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[. = '.']">
<xsl:value-of select="''"/>
</xsl:template>
</xsl:stylesheet>
Any Help will be appreciated!
You just need to do an xsl:copy in your template, to copy across the element you have matched. Note you don't really need to output an empty string here either.
Try this XSLT
<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" omit-xml-declaration="no"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[. = '.']">
<xsl:copy>
<xsl:apply-templates select="#*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Note, I added an xsl:apply-templates to copy across any existing attributes.
Alternatively, you could replace the second template with this one instead (which matches the text node directly, rather than the parent element)
<xsl:template match="text()[. = '.']" />
I am new to XSLT .Kindly help me with the below query :
My Source XML:
<?xml version="1.0" encoding="UTF-8"?>
<ns1:Header1 xmlns:ns1="urn:src:abc">
<Header2>
<Header3>
<field1>1.1.2017</field1>
<field2>12</field2>
<field3> </field3>
</Header3>
</Header2>
</ns1:Header1>
Target/Expected XML
<?xml version="1.0" encoding="UTF-8"?>
<ns2:Header1 xmlns:ns2="urn:tar:abc" xmlns:v1="def.v1">
<Header2>
<v1:Header3>
<field1>1.1.2017</field1>
<field2>12</field2>
<field3> </field3>
</v1:Header3>
</Header2>
</ns2:Header1>
And also i need to remove the space/blank between the tag filed3 ( Field3 value sometimes will be blank)
I am using the below code for Transformation
<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="*">
<ns2:Header1 xmlns:ns2="urn:tar:abc" xmlns:v1="def.v1">
<Header2>
<xsl:copy-of select="//Header3"/>
</Header2>
</ns2:Header1>
</xsl:template>
</xsl:stylesheet>
I am not able to achieve my Target XML. Kindly help
Many thanks in advance
Regards,
Pavi
Your current template matches * which will match any element, but really you only want it to match the root element.
Additionally, as you want to change Header3 from being in no namespace, to being in the "def.v1" namespace, you should be making use of xsl:apply-templates, not xsl:copy-of
<xsl:template match="/*">
<ns2:Header1 xmlns:ns2="urn:tar:abc" xmlns:v1="def.v1">
<xsl:apply-templates />
</ns2:Header1>
</xsl:template>
You would then need a template matching Header3 to change the namespace
<xsl:template match="Header3" xmlns:v1="def.v1">
<v1:Header3>
<xsl:apply-templates />
</v1:Header3>
</xsl:template>
You would then just need the identity template to copy all other nodes without changes.
Try this XSLT
<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" indent="yes" />
<xsl:template match="/*">
<ns2:Header1 xmlns:ns2="urn:tar:abc" xmlns:v1="def.v1">
<xsl:apply-templates />
</ns2:Header1>
</xsl:template>
<xsl:template match="Header3" xmlns:v1="def.v1">
<v1:Header3>
<xsl:apply-templates />
</v1:Header3>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy copy-namespaces="no">
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
See it in action at http://xsltransform.net/jz1PuQb
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 a requierement as below:
if i give input as :
<?xml version="1.0"?>
<new:NewAddressData xmlns:new="http://www.example.org/NewAddress">
<new:NewStreet></new:NewStreet>
<new:NewArea>Area_1</new:NewArea>
<new:NewState></new:NewState>
</new:NewAddressData>
Output should be:
<new:NewArea>Area_1</new:NewArea>
Actually Iam new bee to XSLT but I read some basics and tried below code :
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:choose>
<xsl:when test="#*|node() != ''">
<xsl:value-of select="." disable-output-escaping="yes" />
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="#*|node()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
for this I am getting output as :
<new:NewAddressData xmlns:new="http://www.example.org/NewAddress">Area_1</new:NewAddressData>
where expected value should be like :
<new:NewArea>Area_1</new:NewArea>
So how can I achieve this using XSLT 1.0
Thanks in advance
You could do something like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="*[text()]">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
Depending on input, like if there was more than one element that contained text, this might result in output that is not well formed.
It looks like you have read about the XSLT Identity Template, which is good!
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
On its own, this will copy across all nodes unchanged (such as your NewArea element), so you need to then write templates for the things you do want to change. In this case, it looks like you want to remove elements that don't have non-empty text nodes as children.
<xsl:template match="*[not(text()[normalize-space()])]">
Try this XSLT
<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="*[not(text()[normalize-space()])]">
<xsl:apply-templates />
</xsl:template>
</xsl:stylesheet>
This would output the following
<new:NewArea xmlns:new="http://www.example.org/NewAddress">Area_1</new:NewArea>
The namespace is necessary here. You can not output an element with a prefix without also declaring the namespace associated with it.