xslt - add elements to text but remove the in between element - xslt

I am not worked with XSLT lot. But Somehow, I am struggling to get the expected output for the below items.
Input 1:
<name>xxxx <xsample>dddd</xsample> zzzz</name>
Output for 1:
<p><t>xxxx dddd zzzz</t></p> // here I don't want to wrap the tag
Input 2
<name>xxxx <ysample>dddd</ysample> zzzz</name>
Output for 2:
<p><t>xxxx </t><t>dddd</t><t> zzzz</t></p>
I have tried with the below xslt code:
<xsl:template match="name">
<p>
<xsl:apply-templates select="*|#*|comment()|processing-instruction()|text()"/>
</p>
</xsl:template>
<xsl:template match="name/text()[not(parent::ysample)]">
<t><xsl:value-of select="."/></t>
</xsl:template>
<xsl:template match="name/ysample">
<t><xsl:value-of select="."/></t>
</xsl:template>
Anybody could you please help me with this?
Thanks,
Kumar

I think the problem is with this line
<xsl:template match="name//text()[not(parent::ysample)]">
There are two issues here
name/text() matches text nodes that are direct children of name, and so the condition not(parent::ysample), which applies to the text node, will never be true as the parent will always be name
This is maybe a typo, but you probably want to check for xsample here, to implement your logic, especially because you already have a template matching ysample
Try this line instead:
<xsl:template match="name//text()[not(parent::xsample)]">

You can also check this in XSLT 2.0 with grouping
<xsl:template match="name">
<p>
<xsl:for-each-group select="node()" group-adjacent="self::text() or self::xsample">
<t>
<xsl:value-of select="current-group()"/>
</t>
</xsl:for-each-group>
</p>
</xsl:template>

Related

Attempts to use following-sibling to convert

I try to convert my old html by xslt-script to my new xml stucture.
I have a Problem to converting the folowing source to my needed xml structure.
Source
<p>
<a class="DropDown">Example Text</a>
</p>
<div class="collapsed">
<table>..</table>
<p>..</p>
</div>
xml structure
<lq>
<p>Example Text</p>
<table>..</table>
<p>..</p>
</lp>
I tried the following xls, but the div class="collapsed" is not adopted into the lp tag.
<xsl:template match="p/a[#class='DropDown']">
<lp>
<p><xsl:apply-templates select="text()"/></p>
<xsl:if test="/p/a/following-sibling::*[1][self::div]">
<xsl:apply-templates select="*|text()"/>
</xsl:if>
</lp>
</xsl:template>
Can anyone tell me what I did wrong ore where the mistake is?
Thanks much
IMHO, you want to do:
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="p[a/#class='DropDown']">
<lp>
<p>
<xsl:value-of select="a"/>
</p>
<xsl:copy-of select="following-sibling::*[1][self::div]/node()"/>
</lp>
</xsl:template>
<xsl:template match="div[preceding-sibling::*[1][self::p/a/#class='DropDown']]"/>
As for your mistake:
You are testing the existence of some p that is the root element
and contains an a whose following sibling is div. None of these are true in the given example;
xsl:if does not change the context: your <xsl:apply-templates
select="*|text()"/> applies templates to the child nodes of the
current a;
Presumably you don't want the div to appear again in the original place -
so if you have another template to suppress it, you cannot use
<xsl:apply-templates> to insert it at the place you do want it -
at least not without using another mode.

How to clear element value in xslt if the value is a string

I just wanna clear a element value if element value is a specific string value
Input xml
<A>
<B>
<C>BOLD</C>
</B>
</A>
Desired Output
<A>
<B>
<C/>
</B>
</A>
my xslt looks like following which doesn't work it just clears everything
<xsl:template match="A/B/C/text()">
<xsl:if test="text()='BOLD'">
<xsl:text></xsl:text>
</xsl:if>
</xsl:template>
<!--Copy the rest of the document as it is-->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
Please help thanks
Given you've started with an identity template there's really no need to use an explicit xsl:if, just put the condition in the match expression of an empty template:
<xsl:template match="A/B/C/text()[. = 'BOLD']"/>
Text nodes that are not under an A/B/C or whose content is not BOLD will be handled by the identity template and copied as normal.
The caveat with using text() in your match expressions is that it means the template is looking at only one text node at a time, and only at text that is directly inside the C element. If the C can have other content you will get some odd edge cases:
<C>BOLD<br/></C> --> <C><br/></C>
<C><b>B</b>OLD</C> --> unchanged
<C><b>BOLD</b></C> --> unchanged
If you want to treat the full text under the C as one unit regardless of child elements then you need something more like
<xsl:template match="C[. = 'BOLD']">
<C/>
</xsl:template>
without mentioning text(). This would clear all three of my examples above.
The logic in your first template isn't outputting anything if the text isn't "BOLD", so if the text is bold, its spits out nothing, otherwise it still spits out nothing. also, the text() node won't have its own text.
So, replace the first template it with this, and it should work fine.
<xsl:template match="A/B/C/text()">
<xsl:if test="not(.='BOLD')">
<xsl:copy/>
</xsl:if>
</xsl:template>
And applied to this:
<A>
<B>
<C>Bold</C>
<C>BOLD</C>
<C>Italic</C>
</B>
</A>
Gives this:
<A>
<B>
<C>Bold</C>
<C/>
<C>Italic</C>
</B>
</A>
Another way to fix this issue is:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.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="*[text()='BOLD']">
<xsl:copy/>
</xsl:template>
</xsl:stylesheet>

Transform tag only if other kind of tag will follow

I'd like to amend an existing xsl file so that the tag <Empty> in the source document is only transformed if there will follow another sibling which is not this tag. A kind of truncate of these tags to the end.
Currently I have:
...
<xsl:template match="Expression">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="Empty">
<xsl:value-of select="."/>
</xsl:template>
Can this be achieved with xslt?
Sample input
<root>
<dummy1>Test1</dummy1>
<Empty>Empty1</Empty>
<Empty>Empty2</Empty>
<dummy2>Test2</dummy2>
<Empty>Empty3</Empty>
<Empty>Empty4</Empty>
<Empty>Empty5</Empty>
</root>
desired output:
Test1Empty1Empty2Test2
Please post a small input sample and the corresponding result you want to create with XSLT. There is a sibling axis in XPath so doing e.g.
<xsl:template match="Empty[following-sibling::*[1][not(self::Empty)]]">
<!-- now transform here as needed -->
</xsl:template>
matches any Empty elements followed by another element that is not an Empty element but I am not sure that is all you need and what you want to do inside of the template.
Assumed Input XML as:
<?xml version="1.0" encoding="utf-8"?>
<root>
<dummy1>test</dummy1>
<Empty>do-not-copy</Empty>
<Empty>copy-this1</Empty>
<dummy2>test</dummy2>
<Empty>do-not-copy-this2</Empty>
</root>
In the above XML .. 2nd element <Empty>do-not-copy</Empty> has immediate next sibling as <Empty>copy-this1</Empty> so it should not be selected. where as 3rd element is followed-by <dummy2/> so it should be copied ..
and 5th element is <Empty>copy-this2</Empty> it's not followed by any tag .. so it should be dropped as well :)
this is the XSL code for that:
<xsl:template match="Empty[following-sibling::*[1][name()!='Empty']]">
<xsl:value-of select="."/>
</xsl:template>
I am adding up a new answer ..
If this is your XML:
<?xml version="1.0" encoding="utf-8"?>
<root>
<dummy>test</dummy>
<Empty>copy-this</Empty>
<Empty>copy-this1</Empty>
<dummy>test</dummy>
<Empty>copy-this</Empty>
<Empty>copy-this1</Empty>
<dummy>test</dummy>
<Empty>donot-copy-this2</Empty>
<Empty>donot-copy-this2</Empty>
</root>
XSLT: sample code..
<xsl:template match="dummy">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="Empty[following-sibling::*[name()!='Empty']]">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="text()"/>
Output:
testcopy-thiscopy-this1testcopy-thiscopy-this1test
I modified the proposed solutions to the following:
<xsl:template match="Empty[following-sibling::*[not(self::Empty)]]">
<xsl:value-of select="."/>
</xsl:template>
which seems to work.

xsltproc generates more output than expected

I'm trying to write a simple .xslt to process .xml files. But I've been confused - why text in tags <tag>text</tag> has also been printed?
Please look at the example:
sample.xml
<source>
<employee>
<firstName>Joe</firstName>
<surname>Smith</surname>
</employee>
</source>
style.xsl
<xsl:stylesheet version = '1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<xsl:template match="surname">
<div>
<xsl:value-of select="name()"/>
</div>
</xsl:template>
</xsl:stylesheet>
Why after calling: xsltproc style.xslt sample.xml I'm getting
Joe
<div>surname</div>
instead of
<div>surname</div>
only?
This is because Joe is being handled by default. Text nodes are normally output by default. You need to override the default behavior.
<xsl:stylesheet version = '1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<!--Added to match all other nodes/attributes.-->
<xsl:template match="node()|#*">
<xsl:apply-templates select="node()|#*"/>
</xsl:template>
<xsl:template match="surname">
<div>
<xsl:value-of select="name()"/>
</div>
</xsl:template>
</xsl:stylesheet>
Processing starts with the document node and there are built-in templates that keep processing going until your templates match. You already got one suggestion to override the default templates, another approach in your case could be to explicitly select only the surname elements for processing with e.g.
<xsl:template match="/">
<xsl:apply-templates select="source/employee/surname"/>
</xsl:template>

What is the difference between <copy-of> and <apply-templates>?

When should I use <copy-of> instead of <apply-templates>?
What is their unique role? Most of the time replacing <apply-templates> with <copy-of> gives out weird output. Why is that?
xsl:copy-of is an exact copy of the matched input xml element. No xslt processing takes place and the output from that element will be exactly the same as the input.
xsl:apply-templates tells the xslt engine to process templates that match the selected elements. xsl:apply-templates is what gives xslt its overriding capability, since the templates you create with match on elements can have different priorities, and the template with the highest priority will be executed.
Input:
<a>
<b>asdf</b>
<b title="asdf">asdf</b>
</a>
Xslt 1:
<xsl:stylesheet ... >
<xsl:template match="a">
<xsl:copy-of select="b" />
</xsl:template>
</xsl:stylesheet>
Xml output 1:
<b>asdf</b>
<b title="asdf">asdf</b>
Xslt 2:
<xsl:stylesheet ... >
<xsl:template match="a">
<xsl:apply-templates select="b" />
</xsl:template>
<xsl:template match="b" priority="0">
<b><xsl:value-of select="." /></b>
<c><xsl:value-of select="." /></c>
</xsl:template>
<xsl:template match="b[#title='asdf']" priority="1">
<b title="{#title}"><xsl:value-of select="#title" /></b>
</xsl:template>
</xsl:stylesheet>
Xml output 2:
<b>asdf</b>
<c>asdf</c>
<b title="asdf">asdf</b>
copy-of
will simply return you a dump of the XML in the supplied node-set
apply-templates
on the other hand will apply any templates applicable to the node-set passed it.