My requirement is as follows
If any complex element contains only text then corresponding element attributes translate into element to parent level.
If any complex element contains children then corresponding element attributes translate into element to same element level.
This translation want to achieve using xslt logic.
Input XML
<root>
<food name="desert">butter scotch</food>
<special type="nonveg">
<name>chicken</name>
</special>
</root>
Output XML
<root>
<food>butter scotch</food>
<name>desert</name>
<special>
<type>nonveg</type>
<name>chicken</name>
</special>
</root>
Start off with the Identity Transform....
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
For the rule "If any complex element contains only text then corresponding element attributes translate into element to parent level", you could use the following template (I am ignoring comments and processing-instuctions here, and just checking the element has no child element)
<xsl:template match="*[not(*)][#*]">
<xsl:copy>
<xsl:apply-templates select="node()"/>
</xsl:copy>
<xsl:apply-templates select="#*" mode="toelement"/>
</xsl:template>
The template with mode "toelement" will turn the attribute to an element. (It will be re-used by the other rule).
<xsl:template match="#*" mode="toelement">
<xsl:element name="{local-name()}">
<xsl:value-of select="." />
</xsl:element>
</xsl:template>
For the rule "If any complex element contains children then corresponding element attributes translate into element to same element level." then you could actually match on the attribute directly:
<xsl:template match="*[*]/#*">
<xsl:apply-templates select="." mode="toelement"/>
</xsl:template>
Try this XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:template match="*[*]/#*">
<xsl:apply-templates select="." mode="toelement"/>
</xsl:template>
<xsl:template match="*[not(*)][#*]">
<xsl:copy>
<xsl:apply-templates select="node()"/>
</xsl:copy>
<xsl:apply-templates select="#*" mode="toelement"/>
</xsl:template>
<xsl:template match="#*" mode="toelement">
<xsl:element name="{local-name()}">
<xsl:value-of select="." />
</xsl:element>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Related
I am new to XSLT, cant get my head around it. Need to transform the below input file to below output file, can someone help me with xslt code. thanks in advance.
inputFile:
<input>
<inputOne>
<numberOne>1</numberOne>
</inputOne>
<inputTwo>
<numberTwo>2</numberTwo>
</inputTwo>
<inputThree>
<numberThree>3</numberThree>
</inputThree>
<inputFive>
<numberFive>5</numberFive>
</inputFive>
<inputFour>
<numberFour>4</numberFour>
</inputFour>
<inputThree>
<numberThree>32</numberThree>
</inputThree>
<inputFive>
<numberFive>52</numberFive>
</inputFive>
<inputFour>
<numberFour>42</numberFour>
</inputFour>
</input>
outputFile:
<input>
<inputOne>
<numberOne>1</numberOne>
<inputTwo>
<numberTwo>2</numberTwo>
<inputThree>
<numberThree>3</numberThree>
<inputFour>
<numberFour>4</numberFour>
<inputFive>
<numberFive>5</numberFive>
</inputFive>
</inputFour>
</inputThree>
<inputThree>
<numberThree>32</numberThree>
<inputFour>
<numberFour>42</numberFour>
<inputFive>
<numberFive>52</numberFive>
</inputFive>
</inputFour>
</inputThree>
</inputTwo>
</inputOne>
</input>
I am using Transformer object to transform streamsource into streamresult object. the transform object is created using xsl file.
my xsl file is not working.
xslcode:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8"/>
<xsl:template match="/input">
<xsl:copy>
<xsl:apply-templates select="inputOne"/>
</xsl:copy>
</xsl:template>
<xsl:template match="inputOne">
<xsl:apply-templates select="inputTwo"/>
</xsl:template>
<xsl:template match="inputOne[not(inputTwo)]">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="inputTwo">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
with above xsl i am trying to get inputOne tag and inside it inputTwo tag.
but just getting the below:
<input>
<inputOne>
<numberOne>1</numberOne>
</inputOne>
</input>
appreciate any help to fix the xsl code. Thanks.
FWIW, the following stylesheet will generate, with a lot of effort, the result shown in your question. Whether the logic implemented here will suit all your possible inputs is not clear to me.
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" oversion="1.0" encoding="utf-8" indent="yes"/>
<xsl:key name="in4" match="inputFour" use="generate-id(preceding-sibling::inputThree[1])" />
<xsl:key name="in5" match="inputFive" use="generate-id(following-sibling::inputFour[1])" />
<xsl:template match="/input">
<xsl:copy>
<xsl:apply-templates select="inputOne"/>
</xsl:copy>
</xsl:template>
<xsl:template match="inputOne">
<xsl:copy>
<xsl:copy-of select="numberOne"/>
<xsl:apply-templates select="../inputTwo"/>
</xsl:copy>
</xsl:template>
<xsl:template match="inputTwo">
<xsl:copy>
<xsl:copy-of select="numberTwo"/>
<xsl:apply-templates select="../inputThree"/>
</xsl:copy>
</xsl:template>
<xsl:template match="inputThree">
<xsl:copy>
<xsl:copy-of select="numberThree"/>
<xsl:apply-templates select="key('in4', generate-id())"/>
</xsl:copy>
</xsl:template>
<xsl:template match="inputFour">
<xsl:copy>
<xsl:copy-of select="numberFour"/>
<xsl:apply-templates select="key('in5', generate-id())"/>
</xsl:copy>
</xsl:template>
<xsl:template match="inputFive">
<xsl:copy>
<xsl:copy-of select="numberFive"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I am using xsltproc to process XSLT 1.0 transform on OS X Yosemite; input is HTML, and output is XML.
The idea is that the templates below matching h1[#class='page-header'] and div[#class='mixins'] actually work, but the problem is wrapping them in a custom parent XML element (here called dye).
I realize my template matching * is broken; it's there simply to illustrate the kind of structure I would like to output.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" method="xml"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|style|script"/>
<xsl:template match="*">
<xsl:element name="dye">
<xsl:apply-templates select="h1[#class='page-header']"/>
<xsl:apply-templates select="div[#class='mixins']"/>
</xsl:element>
</xsl:template>
<xsl:template match="h1[#class='page-header']">
<xsl:element name="color">
<xsl:value-of select="text()"/>
</xsl:element>
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="div[#class='mixins']">
<xsl:element name="tone">
<xsl:value-of select="p/a[#class='tone']/#href"/>
</xsl:element>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
Thanks for your interest!
I believe this should work:
<xsl:template match="*">
<dye>
<xsl:apply-templates select="h1[#class='page-header']"/>
<xsl:apply-templates select="div[#class='mixins']"/>
</dye>
</xsl:template>
I was trying to find a way to convert the XML tags to their respective unique address like xPath. Nd I found an XSLT, where XML is processed and the unique address is created only for the nodes which doesnt have child elements and with attributes. [link]:Generate/get xpath from XML node java
XSLT :
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="vApos">'</xsl:variable>
<xsl:template match="*[#* or not(*)] ">
<xsl:if test="not(*)">
<xsl:apply-templates select="ancestor-or-self::*" mode="path"/>
<xsl:value-of select="concat('=',$vApos,.,$vApos)"/>
<xsl:text>
</xsl:text>
</xsl:if>
<xsl:apply-templates select="#*|*"/>
</xsl:template>
<xsl:template match="*" mode="path">
<xsl:value-of select="concat('/',name())"/>
<xsl:variable name="vnumPrecSiblings" select=
"count(preceding-sibling::*[name()=name(current())])"/>
<xsl:if test="$vnumPrecSiblings">
<xsl:value-of select="concat('[', $vnumPrecSiblings +1, ']')"/>
</xsl:if>
</xsl:template>
<xsl:template match="#*">
<xsl:apply-templates select="../ancestor-or-self::*" mode="path"/>
<xsl:value-of select="concat('[#',name(), '=',$vApos,.,$vApos,']')"/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
when passed to this
<?xml version="1.0" encoding="UTF-8"?>
<root>
<main>
<tag1>001</tag1>
<tag2>002</tag2>
<tag3>
<tag4>004</tag4>
</tag3>
<tag2>002</tag2>
<tag5>005</tag5>
</main>
</root>
produces
/root/main/tag1='001' /root/main/tag2='002' /root/main/tag3/tag4='004' /root/main/tag2[2]='002' /root/main/tag5='005'
So I need the xslt to generate in the following way
/root
/root/main
/root/main/tag1
/root/main/tag2
/root/main/tag3
/root/main/tag3/tag4
/root/main/tag2[2]
/root/main/tag5
Also I dont need values. So please help me with this
Your result could be produced rather simply by:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:template match="*">
<xsl:for-each select="ancestor-or-self::*">
<xsl:value-of select="name()" />
<xsl:variable name="i" select="count(preceding-sibling::*[name()=name(current())])"/>
<xsl:if test="$i">
<xsl:value-of select="concat('[', $i + 1, ']')"/>
</xsl:if>
<xsl:if test="position()!=last()">
<xsl:text>/</xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
<xsl:apply-templates select="*"/>
</xsl:template>
</xsl:stylesheet>
Note that this does not process attributes (or any other type of nodes other than elements).
I'm having this xml as my input :
<unidad num="2.">
<tag></tag>
<tag2></tag2>
<unidad num="2.1">
<tag></tag>
<tag2></tag2>
<unidad num="2.1.1">
<tag></tag>
<tag2></tag2>
<unidad num="2.1.1.1">
<tag></tag>
<tag2></tag2>
</unidad>
</unidad>
</unidad>
</unidad>
My output should be :
<sub>
<tag></tag>
<tag2></tag2>
<sub2>
<tag></tag>
<tag2></tag2>
<sub3>
<tag></tag>
<tag2></tag2>
<sub4>
<tag></tag>
<tag2></tag2>
</sub4>
</sub3>
</sub2>
</sub>
I can't find the proper way to do this. I'm working with templates, i'm having this :
<xsl:for-each select="unidad">
<xsl:call-template name="unidades1"/>
</xsl:for-each>
<xsl:template name="unidades1">
<xsl:element name="sub1">
<xsl:text></xsl:text>
</xsl:element>
<xsl:if test="position() != last()">
<xsl:apply-templates select="child::*"/>
</xsl:if>
</xsl:template>
<xsl:template match="unidad">
<xsl:call-template name="unidades2"/>
</xsl:template>
<xsl:template name="unidades2">
<xsl:element name="sub2">
<xsl:text></xsl:text>
</xsl:element>
<xsl:if test="position() != last()">
<xsl:apply-templates select="child::*"/>
</xsl:if>
</xsl:template>
With this XSLT, every child of unidad matches the second condition, so it's written as sub2, and I don't know how to consider if it is a child of another unidad element. Any ideas how to reach this? Thanks!
This stylesheet produces the desired output. It uses a modified identity transform with a specialized template for the <unidad> elements.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<!--standard identity template-->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="unidad">
<!--count the number of ancestors that are unidad elements-->
<xsl:variable name="unidad-ancestor-count" select="count(ancestor::unidad)"/>
<!--If there are at least one unidad ancestors then
set the suffix to be the count()+1-->
<xsl:variable name="suffix">
<xsl:if test="$unidad-ancestor-count>0">
<xsl:value-of select="$unidad-ancestor-count+1"/>
</xsl:if>
</xsl:variable>
<!--create a new element using a base name of "sub" and the suffix value -->
<xsl:element name="sub{$suffix}">
<!--not pushing the #num attribute through the identity template,
just descendant nodes-->
<xsl:apply-templates />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Using http://xslt.online-toolz.com/tools/xslt-transformation.php
.xml
<?xml version="1.0"?>
<my:project xmlns:my="http://myns">
<my:properties>
<my:property>
<my:name>customerId</my:name>
<my:value>1</my:value>
</my:property>
<my:property>
<my:name>userId</my:name>
<my:value>20</my:value>
</my:property>
</my:properties>
</my:project>
I now want to look for the name customerId and want to replace the value.
It almost works, but it replaces ALL values in the document. What am I doing wrong to just replace the value where the name mached?
.xsl
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="http://myns" xmlns:saxon="http://saxon.sf.net">
<xsl:param name="name" select="'customerId'"/>
<xsl:param name="value" select="'0'"/>
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*/my:properties/my:property/my:value/text()" >
<xsl:choose>
<xsl:when test="/*/my:properties/my:property/my:name = $name">
<xsl:value-of select="$value"/>
</xsl:when>
<xsl:otherwise><xsl:copy-of select="saxon:parse(.)" /></xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
The test for /*/my:properties/my:property/my:name = $name always succeed because it uses an absolute path, and so the result is independent of the surrounding template context. A test with a relative xpath should work.
XSL:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="http://myns" xmlns:saxon="http://saxon.sf.net">
<xsl:param name="name" select="'customerId'"/>
<xsl:param name="value" select="'0'"/>
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*/my:properties/my:property/my:value/text()" >
<xsl:choose>
<xsl:when test="../../my:name = $name">
<xsl:value-of select="$value"/>
</xsl:when>
<xsl:otherwise>otherwise</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
XML:
<my:project xmlns:my="http://myns">
<my:properties>
<my:property>
<my:name>customerId</my:name>
<my:value>1</my:value>
</my:property>
<my:property>
<my:name>userId</my:name>
<my:value>20</my:value>
</my:property>
</my:properties>
</my:project>
Result of saxonb-xslt -s:test.xml -xsl:test.xsl
<my:project xmlns:my="http://myns">
<my:properties>
<my:property>
<my:name>customerId</my:name>
<my:value>0</my:value>
</my:property>
<my:property>
<my:name>userId</my:name>
<my:value>otherwise</my:value>
</my:property>
</my:properties>
</my:project>