Can we use multiple elements with except operator in xslt 1 - xslt

I want to add multiple matches in except operator.
Example:
<root>
<div>
<span style="font-family:'Work Sans', sans-serif;">
</span>
<h3>hi</h3>
<ol><li>hello</li></ol>
<ul><li>hello</li></ul>
<p>name</p>
</div>
</root>
Expected result: Want to convert div tag to p tag and then move only h3,ol,ul,p tag out of it if present.
<root>
<p>
<span style="font-family:'Work Sans', sans-serif;">
</span>
</p>
<h3>hi</h3>
<ol><li>hello</li></ol>
<ul><li>hello</li></ul>
<p>name</p>
</root>
I tried this:
<xsl:template match="div">
<p>
<xsl:copy-of select="* except h3"/>
</p>
<xsl:copy-of select="h3"/>
</xsl:template>
Above xslt results to :
<root>
<p>
<span style="font-family:'Work Sans', sans-serif;">
</span>
<ol><li>hello</li></ol>
<ul><li>hello</li></ul>
<p>name</p>
</p>
<h3>hi</h3>
</root>
Is there any way to add mutiple element names in except operator like <xsl:copy-of select="* except h3 and ol"/>
Another approach i tried:
<xsl:template match="div[p | ol | ul | h3 | h2]">
<xsl:apply-templates/>
</xsl:template>
<!-- convert <div> to <p> if it's direct child is not one of p,ol,ul -->
<xsl:template match="div[not(p | ol | ul | h3 | h2)]">
<p>
<xsl:apply-templates/>
</p>
</xsl:template>
And this results to which is wrong as i still want span tag to be under p tag:
<root><span style="font-family:'Work Sans', sans-serif;">
</span>
<h3>hi</h3>
<ol><li>hello</li></ol>
<ul><li>hello</li></ul>
<p>name</p></root>

In stead of using xsl:copy you could just use the xsl:apply-templates approach like this:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:template match="node()|#*" name="copy">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="div">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="div/span">
<p>
<xsl:call-template name="copy"/>
</p>
</xsl:template>
</xsl:stylesheet>

The except operator was introduced in XPath 2.0 so it doesn't make any sense on how to use it with XSLT 1.0 which uses XPath 1.0.
As for its use where supported, you can of course do e.g. * except (h3, ol).
In XPath 1.0 you can try e.g. *[not(self::h3|self::ol)].

Related

Strip all the text from a specific node and remove all tags from xml using xslt1

I'm trying to strip all tags from a xml doc and i need to strip all text from a specific node only. For more clearity see the below example:
<root>
<p>My 1st Semester Visual</p>
<p>
<b>Self Reflection</b>
</p>
<p>The activity</p>
<content-block>
<div class="imageWrapper" />
</content-block>
<p id="5fce699db97470099ea6c7e6"> </p>
<content-block>
<div class="carousel">
<div class="carouselHeader" />
<div class="carouselNavbar">
<div class="carouselNavbarThumbnails" />
</div>
</div>
My Space Unit Flyer
</content-block>
<div>
<br />
</div>
</root>
Result:
<root><text>My 1st Semester VisualSelf ReflectionThe activity
My Space Unit Flyer
</text><contentBlocks>2</contentBlocks></root>
Expected result: I also need to remove text that is inside the <content-block>.
<root><text>My 1st Semester VisualSelf ReflectionThe activity
</text><contentBlocks>2</contentBlocks></root>
My xslt:
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="UTF-8" indent="no" omit-xml-declaration="yes"/>
<!-- Strip out white space -->
<xsl:strip-space elements="*"/>
<!-- Strip out all html tags, only leaving text contents -->
<xsl:template match="*">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="root">
<root>
<text>
<xsl:apply-templates/>
</text>
<contentBlocks>
<xsl:if test="//content-block">
<xsl:value-of select="count(//content-block)"/>
</xsl:if>
<xsl:if test="figure">
<xsl:value-of select="count(figure)"/>
</xsl:if>
</contentBlocks>
</root>
</xsl:template>
</xsl:transform>
Thanks in advance
<!-- Add this to your code. It suppresses content-block. -->
<xsl:template match="content-block"/>

How to Add Color To a Div in XSL when Color is in the XML document

I have an XSL 1.0 document with the following:
<div class="workgroup_title">
<xsl:value-of select="./#name"/>
</div>
I need to set the color for this element. The color is in the XML file
<abc.xyz.color>FF5733</abc.xyz.color>
To get it, I use this:
<xsl:value-of select="./abc.xyz.color"/>
What I would have liked to do is
<div class="workgroup_title" style="color:"#<xsl:value-of select="./abc.xyz.color"/>>
<xsl:value-of select="./#name"/>
</div>
That's not allowed, though.
Or:
<xsl:attribute style="color:">#<xsl:value-of select="./abc.xyz.color"/></xsl:attribute>
But color is not one of the attributes that can be set like that.
You can use attribute value templates to compute (parts of) the value of a literal result element: <div style="color: #{abc.xyz.color}">...</div>
The following templates should suffice your needs:
<xsl:template match="text()" /> <!-- Removes the text from the <abc.xyz.color>FF5733</abc.xyz.color> element -->
<xsl:template match="/*"> <!-- Copies the root element and its namespace -->
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="div[#class='workgroup_title']"> <!-- Applies the template to the <div> element -->
<xsl:copy>
<xsl:attribute name="style"><xsl:value-of select="concat('color: #',../abc.xyz.color,';')"/></xsl:attribute>
<xsl:copy-of select="node()|#*" />
</xsl:copy>
</xsl:template>
Its output is:
<root xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<div style="color: #FF5733;" class="workgroup_title">
<xsl:value-of select="./#name"/>
</div>
</root>

xslt fields grouped by their type

I have some data that i put in (image, title, text). I ahve 3 different elements in my xslt for each of them:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:udt="DotNetNuke/UserDefinedTable" exclude-result-prefixes="udt">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
<!--
This prefix is used to generate module specific query strings
Each querystring or form value that starts with udt_{ModuleId}_param
will be added as parameter starting with param
-->
<xsl:variable name="prefix_param">udt_<xsl:value-of select="//udt:Context/udt:ModuleId" />_param</xsl:variable>
<xsl:template match="udt:Data" mode="list">
<xsl:value-of select="udt:Image" disable-output-escaping="yes" />
<xsl:value-of select="udt:Title" disable-output-escaping="yes" />
<xsl:value-of select="udt:Text" disable-output-escaping="yes" />
</xsl:template>
<xsl:template match="/udt:UserDefinedTable">
<xsl:variable name="currentData" select="udt:Data" />
<xsl:if test="$currentData">
<xsl:apply-templates select="$currentData" mode="list">
</xsl:apply-templates>
</xsl:if>
</xsl:template>
<xsl:template name="EditLink">
<xsl:if test="udt:EditLink">
<a href="{udt:EditLink}">
<img border="0" alt="edit" src="{//udt:Context/udt:ApplicationPath}/images/edit.gif" />
</a>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
I want to fill these elements each into its own div group, so my end result would be something like this:
<div id="images">
<!--all images here-->
</div>
<div id="title">
<!--all titles here-->
</div>
<div id="text">
<!--all texts here-->
</div>
Can this be achieved by any kind of grouping or whats the right aproach?
The concept to use xsl:value-of for your 3 element types is wrong,
as this instruction copies only the content of these elements,
without XML markup.
Assuming that the only goal is to reorder (group) your elements,
and they are direct descendants of the current element (udt:Data),
the task can be done the following way:
<xsl:template match="udt:Data">
<xsl:copy>
<div id="images">
<xsl:copy-of select="udt:Image"/>
</div>
<div id="title">
<xsl:copy-of select="udt:Title"/>
</div>
<div id="text">
<xsl:copy-of select="udt:Text"/>
</div>
</xsl:copy>
</xsl:template>
Of course, this is only a template, not the whole script.
Note that e.g. if these elements were located also at "deeper" descendant levels,
all the above XPath expressions should be prececed with descendant::.
And remember about including in your script all namespaces, the script refers to.
They should be declared in stylesheet tag.

cdata-section-elements not working for dynamically created element

I am trying to define some dynamically created elements as cdata sections, but it's not working for some reason:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="no" indent="yes" method="xml"
cdata-section-elements="DESCRIPTION2"
/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="/RSS/ITEM/TEST">
<DESCRIPTION2>
<div class="container">
<xsl:if test="NAME != ''">
<div class="test">
<xsl:value-of select="NAME"/>
</div>
</xsl:if>
</div>
</DESCRIPTION2>
</xsl:template>
</xsl:stylesheet>
Test XML:
<?xml version="1.0" encoding="UTF-8"?>
<RSS>
<ITEM>
<CODE>41,000</CODE>
<TEST>
<NAME><p>HTML code</p></NAME>
</TEST>
</ITEM>
</RSS>
Live test.
Sure I can add manually (<xsl:text disable-output-escaping="yes"><![CDATA[</xsl:text>), but I would like to know why it's not working If I define it as cdata-section-elements.
CDATA serialization happens for text nodes inside of the nominated elements, if you put in elements there it doesn't happen. Note that, assuming an XSLT 3 processor with XPath 3.1 support, you can use the serialize function to serialize the content you build as html and then output it as a text node:
<xsl:template match="/RSS/ITEM/TEST">
<xsl:variable name="html">
<div class="container">
<xsl:if test="NAME != ''">
<div class="test">
<xsl:value-of select="NAME"/>
</div>
</xsl:if>
</div>
</xsl:variable>
<DESCRIPTION2>
<xsl:value-of select="serialize($html, map { 'method' : 'html' })"/>
</DESCRIPTION2>
</xsl:template>
http://xsltfiddle.liberty-development.net/948Fn5i/1 then gives the result as a CDATA section
<DESCRIPTION2><![CDATA[<div class="container">
<div class="test">Peter</div>
</div>]]></DESCRIPTION2>
Your content is well-formed XHTML, so it doesn't need to apply CDATA when serializing the content.
If you escaped the markup and constructed a string, it would serialize as CDATA:
<xsl:template match="/RSS/ITEM/TEST">
<DESCRIPTION2>
<div class="container">
<xsl:if test="NAME != ''">
<div class="test">
<xsl:value-of select="NAME"/>
</div>
</xsl:if>
</div>
</DESCRIPTION2>
</xsl:template>
Produces:
<DESCRIPTION2><![CDATA[
<div class="container">
<div class="test">
Peter
</div>
</div>
]]></DESCRIPTION2>
But why would you want to generate a string when you could have well-formed markup? It makes it a pain for everyone downstream.

How to get parents sibling value until a condition is reached using XSLT

I am fairly new to XSLT and this my xml
<q>
<w>
<p>
<b>b1</b>
</p>
<p>p1</p>
<p>p2</p>
<p>p3</p>
<p>
<b>b2</b>
</p>
<p>
<b>b3</b>
</p>
<p>p4</p>
<p>p5</p>
<p>
<b>b4</b>
</p>
<p>p6</p>
</w>
</q>
I need my output to look something like this
<position_1_b1>
<p>p1</p>
<p>p2</p>
<p>p3</p>
</position_1_b1>
<position_2_b3>
<p>p4</p>
<p>p5</p>
</position_2_b3>
<position_3_b4>
<p>p6</p>
</position_3_b4>
There is no b2 related tag in output because next 'P' element has an element 'B' in it!
<p><b>b2</b></p>
<p><b>b3</b></p>
Thanks in advance!!
You can use grouping in XSLT2.0
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output indent="yes"></xsl:output>
<xsl:template match="w">
<xsl:for-each-group select="*" group-starting-with="p[b][following-sibling::*[1][self::p][not(b)]]">
<xsl:element name="{concat('position_', position(), '_', normalize-space(.))}">
<xsl:apply-templates select="current-group() except self::p[b]"/>
</xsl:element>
</xsl:for-each-group>
</xsl:template>
<xsl:template match="p[not(b)]">
<p>
<xsl:apply-templates/>
</p>
</xsl:template>
<xsl:template match="p[b]"/>