<root>
<xnode>
<Node1/>
<Node2/>
<Node3>
<CNode1>
<CCNode1>
<CCField1>
<CCField2>
<CCCNode1/>
</CCNode1>
<CCNode2>
<CCCNode3/>
</Node3>
<Node4/>
</xnode>
<xnode>
<Node1/>
<Node2/>
<Node3>
<CNode1>
<CCNode2>
<CCCNode3/>
</Node3>
<Node4/>
</xnode>
<xnode>
<Node1/>
<Node2/>
<Node3>
<CNode1>
<CCNode1>
<CCField1>
<CCField2>
<CCCNode1/>
</CCNode1>
<CCNode2>
<CCCNode3/>
</Node3>
<Node4/>
</xnode>
</root>
In the above xml, I need to copy all the nodes and values except for Node3 - CNode1 - CCNode1. i.e. if CCNode1 exists copy as its including the child elements, if not, create CCNode1 with the corresponding fields and child elements. For ex, here the first and third xnode has CCNode1 whereas its missing in the second xnode. So copy the first and 3rd node1 as it is and create the CCNode1 and its child elements in the 2nd xnode with some dummy values.
Please suggest how to achieve this with XSLT.
THanks
So, the best I can tell is that you need an identity template to copy everything. And, then you need a template like the following to select Node3/CNode1 nodes that do not have a CCNode1 node. There you can add your nodes.
<xsl:template match="Node3/CNode1[not(.//CCNode1)]">
<xsl:copy>
Add your ccNode1 and child nodes here.
<!-- Output other child nodes of CNode1 -->
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- Identity. -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
Related
Below, I have a subset of car nodes. I then want to process this further by filtering-in by a change in an attribute.
Because preceding-sibling works on the document, the second xpath below will not work if the preceding-sibling node is a non-yellow car. Is there a simple way to achieve this in xpath or with minimal xslt?
<xsl:template match="/">
<xsl:variable name="yellowCars" select="/cars/car[#color = 'yellow']"/>
<changedinspectiondate>
<xsl:apply-templates select="$yellowCars[not(#inspectiondate = preceding-sibling::car[1]/#inspectiondate)]"/>
</changedinspectiondate>
</xsl:template>
I am trying to apply a template using XSLT1 and I can't get it to work:
<xsl:apply-templates select="MeetingWorkBook/Meeting[1]/SpecialEvent/Event[/Date[#AllDayEvent='1']]" mode="FirstRow_Pray">
<xsl:with-param name="strClass">cellBold borderRight</xsl:with-param>
</xsl:apply-templates>
I am trying to apply this template:
<!--This is used to insert the Chairman, or Special Event-->
<xsl:template match="Chairman | Event" mode="FirstRow_Pray">
<xsl:param name="strClass"/>
<td class="{$strClass}">
<xsl:if test="self::Event">
<xsl:attribute name="rowspan">2</xsl:attribute>
</xsl:if>
<xsl:value-of select="."/>
</td>
</xsl:template>
But I only want to apply the template if the Event element exists and the sibling Date[#AllDayEvent] is true.
I know I can wrap my apply-templates call with if clause first and that would work(see below).
I know I can improve the logic of the Event template to test the value of the sibling elements attribute.
I have a lot of templates for using specific Event properties and would prefer the ability the select the item Event where its sibling element Date has a certain attribute value.
But my attempt does not work. I saw this answer and tried:
MeetingWorkBook/Meeting[1]/SpecialEvent/Event[Date[#AllDayEvent='1']]
But it still doesn't work.
This will work:
<xsl:if test="MeetingWorkBook/Meeting[1]/SpecialEvent/Date[#AllDayEvent='1']">
<xsl:apply-templates select="MeetingWorkBook/Meeting[1]/SpecialEvent/Event" mode="FirstRow_Pray">
<xsl:with-param name="strClass">cellBold borderRight</xsl:with-param>
</xsl:apply-templates>
</xsl:if>
But I want to avoid needing the if if possible.
I can now see what was wrong with my original attempt. Date is not a child of Event but a sibling.
MeetingWorkBook/Meeting[1]/SpecialEvent/Date
MeetingWorkBook/Meeting[1]/SpecialEvent/Event
So is it possible to apply a template on Event where the sibling Date has an attribute AllDayEvent with a value of 1. Or do I have no choice but to use the if approach?
Sample XML data:
<Meeting BookmarkId="2" PageBreak="0" NumberClasses="1" SpecialEvent="1" CircuitVisit="0">
<Date ThisWeek="W20200217" NextWeek="W20200224">February 17-23</Date>
<WeeklyBibleReading>GENESIS 18-19</WeeklyBibleReading>
<ReviewQuestion></ReviewQuestion>
<SpecialEvent>
<Event>Circuit Assembly - Love Jehovah With All Your Heart</Event>
<Location>Bristol Assembly Hall, Hortham Lane, Bristol, BS32 4JH</Location>
<Date Day="23" DayShort="Sun" DayFull="Sunday" Month="2" MonthShort="Feb" MonthFull="February" Year="2020" Memorial="0" AllDayEvent="1" MidweekEvent="1">23/02/2020</Date>
</SpecialEvent>
</Meeting>
You should be able to add the predicate to SpecialEvent instead of Event...
<xsl:apply-templates select="MeetingWorkBook/Meeting[1]/SpecialEvent[Date/#AllDayEvent='1']/Event" mode="FirstRow_Pray">
<xsl:with-param name="strClass">cellBold borderRight</xsl:with-param>
</xsl:apply-templates>
I want to dynamically call parameters in a template based on another paramaters value, something like:
Let's say I have a list of Values that are dermined in several ways:
<xsl:param name="One"/>
<xsl:param name="Two"/>
<xsl:param name="Three"/>
from which I'd like to create matching elements from such as:
<One>1111</One>
<Two>2222</Two>
<Three>3333</Three>
Instead of creating all of the elements individually I like to create a seperate template or function to do that.
I was attempting something like:
<xsl:template name="AddElement">
<xsl:param name="Name" select="/foo/bar/text()"/>
...
<xsl:variable name="Value" select="concat('$', $Name)"/>
<xsl:element name="$ElementName">
<xsl:value-of select="$Value"/>
</xsl:element>
...
</xsl:template>
I want to avoid doing that multiple times the same way:
<xsl:if test="string-length($One) != 0">
<One>
<xsl:value-of select="$One"/>
</One>
</xsl:when>
Ideally it could just be
<xsl:call-template name="AddElement">
<xsl:with-param name="ElementName">One</xsl:with-param>
</xsl:call-template>
I think Change following Code:-
<xsl:element name="{$ElementName}">
No, you can't construct variable references (or other XPath expressions) dynamically as strings and then evaluate them. XSLT isn't a macro language.
In XSLT 3.0 you could put the data in a map and select dynamically from the map. I tried to put together an example for you, but I really couldn't get to the bottom of what your hypothetical code was trying to achieve.
When I execute a call template, how do I access the attribute of the b node? There are a lot of possible attributes I have to be able to access, is the only way to pass it as a parameter?
XML:
<a>
<b id="anID"></b>
</a>
XSLT:
<xsl:template match="b">
<xsl:call-tempalte name="renderB"/>
</xsl:template>
<xsl:template name="renderB">
<!-- Based on id of b, do something -->
</xsl:template>
xsl:call-template does not change the context. If you called the template from the context of <xsl:template match="b">, then you are still in the context of b and the attributes of b are accessible simply as #id.
I just need to transform one XML file to another XML file with the wanted data.
The transformation remove every parent nodes if child nodes doesn't contain the correct category.
Input XML:
<SHOP>
<SHOPITEM>
<ITEM_ID>142</ITEM_ID>
<PRODUCT>Mora MV 1251</PRODUCT>
<DESCRIPTION>XXXXX</DESCRIPTION>
<URL>http://www.xxx.sk/o</URL>
<IMGURL>http://xxx.jpg</IMGURL>
<PRICE>6.74</PRICE>
<PRICE_VAT>8.10</PRICE_VAT>
<VAT>0.20</VAT>
<MANUFACTURER>Mora</MANUFACTURER>
<CATEGORYTEXT>Accessories / Mobile</CATEGORYTEXT>
<EAN>8590371028526</EAN>
<PRODUCTNO></PRODUCTNO>
<WARRANTY>24</WARRANTY>
<DELIVERY_DATE>24</DELIVERY_DATE>
</SHOPITEM>
<SHOPITEM>
<ITEM_ID>XXX</ITEM_ID>
<PRODUCT>Hyundai LLF 22924 DVDR</PRODUCT>
<DESCRIPTION>XXXXX</DESCRIPTION>
<URL>http://www.xxx.sk/t</URL>
<IMGURL>http://xxx.jpg</IMGURL>
<PRICE>173.35</PRICE>
<PRICE_VAT>208.00</PRICE_VAT>
<VAT>0.20</VAT>
<MANUFACTURER>Hyundai</MANUFACTURER>
<CATEGORYTEXT>Main category / TVs</CATEGORYTEXT>
<EAN>xxxxx</EAN>
<PRODUCTNO/>
<WARRANTY>24</WARRANTY>
<DELIVERY_DATE>99999</DELIVERY_DATE>
</SHOPITEM>
</SHOP>
I need to remove every SHOPITEM nodes where the CATEGORYTEXT is not Main category / TVs
I have the below 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:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="CATEGORYTEXT[not(text() = 'Main category / TVs')]"/>
</xsl:stylesheet>
But this only removes the CATEGORYTEXT node, not the full SHOPITEM node.
Can anyone help me please? :)
I'm new too XSLT, every help is appreciated.
Desired output is:
<SHOP>
<SHOPITEM>
<ITEM_ID>XXX</ITEM_ID>
<PRODUCT>Hyundai LLF 22924 DVDR</PRODUCT>
<DESCRIPTION>XXXXX</DESCRIPTION>
<URL>http://www.xxx.sk/t</URL>
<IMGURL>http://xxx.jpg</IMGURL>
<PRICE>173.35</PRICE>
<PRICE_VAT>208.00</PRICE_VAT>
<VAT>0.20</VAT>
<MANUFACTURER>Hyundai</MANUFACTURER>
<CATEGORYTEXT>Main category / TVs</CATEGORYTEXT>
<EAN>xxxxx</EAN>
<PRODUCTNO/>
<WARRANTY>24</WARRANTY>
<DELIVERY_DATE>99999</DELIVERY_DATE>
</SHOPITEM>
</SHOP>
Replace this
<xsl:template match="CATEGORYTEXT[not(text() = 'Main category / TVs')]"/>
with this:
<xsl:template match="SHOPITEM[not(CATEGORYTEXT = 'Main category / TVs')]"/>
Your current template filters CATEGORYTEXT elements based on the value of their text node children, whereas what you need is to filter SHOPITEM elements based on the value of their CATEGORYTEXT element children.
Note that it is only very rarely that you actually need to use text() in an XPath expression - text() gives you a set of all the individual text nodes that are direct children of the element you're dealing with, whereas usually what you care about is the complete string value of the element itself (which is, by definition, the concatenation of all its descendant text nodes)