I'm very new to XSLT and trying to format some text for pdf's and I need to match and hide a few elements.
I am currently using:
<xsl:template match="*[#outputclass='LC ACaseName']">
to match:
<p outputclass="LC ACaseName">
and it works just fine.
What I now need to do is match 4 or 5 more
<p outputclass="<somestring>">
and apply the same style to them. I could easily just duplicate the above line substituting the different outputclass names each time but this is lazy and I know there must be a correct way of doing this which I should learn.
I hope I have provided enough info here. If I have missed anything please say.
thanks,
Hedley Phillips
You can specify multiple conditions in the predicate:
<xsl:template match="*[#outputclass='test' or #outputclass='blah']">
I couldn't find the duplicate...
In XSLT/XPath 1.0:
<xsl:template match="*[contains(
'|LC ACaseName|other class|',
concat('|',#outputclass,'|')
)
]">
<!-- Content Template -->
<xsl:template>
In XSLT/XPath 2.0:
<xsl:template match="*[#outputclass = ('LC ACaseName','other class')]">
<!-- Content Template -->
<xsl:template>
Note: For XSLT/XPath 1.0 solution you need a separator not being part of any item content.
Related
I've the below XML.
<?xml version="1.0" encoding="UTF-8"?>
<para align="center">
<content-style font-style="bold">A.1 This is the first text</content-style> (This is second text)
</para>
Below are my 2 Questions.
here i've declared a regex to match the content-style, But when i run this the second one is caught where as it should be div class="para", but in the output i get <div class="para align-center">. please let me know where am i going wrong.
Is there a way i can apply-templates with in the match. when i tried it throws me an error. I want it like below.
if (para)
xsl:apply-templates select child::node()[not(self::text)]
else
xsl:apply-templates
Working Example
Thanks
If you want to use apply-templates inside the analyze-string then you need to store the context node outside of analyze-string in a variable <xsl:variable name="context-node" select="."/>, then you can use <xsl:apply-templates select="$context-node/node()"/> for instance to process the child nodes.
Whether you need that approach I am not sure, I wonder whether you can not simply use the matches functions in a pattern e.g. <xsl:template match="para[content-style[matches(., '(\w+)\.(\w+)')]]">...</xsl:template>.
I'm using XSLT to transform RSS files in XHTML.
In order to create a link I use this block of code:
<!-- language: lang-xml -->
<xsl:for-each select="channel/item">
<h3><xsl:value-of select="title"/></h3>
<xsl:value-of select="description"/>
</xsl:for-each>
But the result comes with some unwanted characters:
<!-- language: lang-html -->
<h3><a href="%0A http://site.com/page.htm%0A ">
What am I doing wrong? Thanks in advance for your help.
It looks like the source has URLEncoded line feeds and some whitespace in it. Leading and trailing whitespace can be stripping using the normalize-space() function. The other stuff may be trickier, depending on how regular it is, and which version of XSLT you're using. If the URLs always end in "%0A ", you could do something like:
substring-before(substring-after(link, 'http'), "%")
This will only work all the time if your URLs are never going to have URLEncoded data in them (which might not be a safe assumption). If you're using XSLT 2.0, something like:
normalize-space(replace(link, '%0A', ''))
might work better.
We are using Xalan XSLT 1.0 in Java and we want to pass a variable to a template match to avoid hard-coding element names in the XSL file. The style sheet compiles, but the date returned is wrong. Are we using the correct syntax?
Possible XML inputs...
<books>
<book/>
<book/>
</books>
<dvds>
<dvd/>
<dvd/>
</dvds>
<xsl:variable name="matchElement" select="'book'"/>
<!-- OR -->
<xsl:variable name="matchElement" select="'dvd'"/>
<xsl:template match="/*[local-name() = $matchElement]">
This xsl:template:
<xsl:template match="/*[local-name() = $matchElement]">
is matching from root.
Either remove the / from /* or change it to //* (depending on how the rest of your stylesheet is designed).
Also, if you use xsl:param instead of xsl:variable, you can set the value from the command line.
Your variable syntax is correct, but note that it is technically illegal to use variable or parameter references in XSLT 1.0 match patterns. It is possible, however, that Xalan has implemented this behavior outside of the standard. (#DevNull's comment about your expression also applies.)
My xsl has a parameter
<xsl:param name="halfPath" select="'halfPath'"/>
I want to use it inside match
<xsl:template match="Element[#at1='value1' and not(#at2='{$halfPath}/another/half/of/the/path')]"/>
But this doesn't work. I guess a can not use parameters inside ''. How to fix/workaround that?
The XSLT 1.0 W3C Specification forbids referencing variables/parameters inside a match pattern.:
"It is an error for the value of the
match attribute to contain a
VariableReference"
There is no such limitation in XSLT 2.0, so use XSLT 2.0.
If due to unsurmountable reasons using XSLT2.0 isn't possible, put the complete body of the <xsl:template> instruction inside an <xsl:if> where the test in conjunction with the match pattern is equivalent to the XSLT 2.0 match pattern that contains the variable/parameter reference(s).
In a more complicated case where you have more than one template matching the same kind of node but with different predicates that reference variables/parameters, then a wrapping <xsl:choose> will need to be used instead of a wrapping <xsl:if>.
Well, you could use a conditional instruction inside the template:
<xsl:template match="Element[#at1='value1']">
<xsl:if test="not(#at2=concat($halfPath,'/another/half/of/the/path'))">
.. do something
</xsl:if>
</xsl:template>
You just need to be aware that this template will handle all elements that satisfy the first condition. If you have a different template that handles elements that match the first, but not the second, then use an <xsl:choose>, and put the other template's body in the <xsl:otherwise> block.
Or, XSLT2 can handle it as is if you can switch to an XSLT2 processor.
This topic had the answer to my question, but the proposed solution by Flynn1179 was not quite correct for me (YMMV). So try it the way it is suggested by people more expert than me, but if it doesn't work for you, consider how I solved it. I am using xsltproc that only handles XSL version 1.0.
I needed to match <leadTime hour="0024">, but use a param: <xsl:param name="hour">0024</xsl:param>. I found that:
<xsl:if test="#hour='{$hour}'"> did not work, despite statements here and elsewhere that this is the required syntax for XSL v.1.0.
Instead, the simpler <xsl:if test="#hour=$hour"> did the job.
One other point: it is suggested above by Dimitre that you put template inside if statement. xsltproc complained about this: instead I put the if statement inside the template:
<xsl:template match="leadTime">
<xsl:if test="#hour=$leadhour">
<xsl:copy>
<xsl:apply-templates select="node() | #*"/>
</xsl:copy>
</xsl:if>
</xsl:template>
In XSLT 2.0 you can refer to global variables within a match pattern, but the syntax is simpler than your guess:
<xsl:template match="Element[#at1='value1' and
not(#at2=$halfPath/another/half/of/the/path)]"/>
rather than
<xsl:template match="Element[#at1='value1' and
not(#at2='{$halfPath}/another/half/of/the/path')]"/>
Also, the semantics are not what you appear to be expecting: a variable referenced on the lhs of "/" must contain a node-set, not a fragment of an XPath expression.
I was wondering if someone remembers how to write a shorter OR statements in XSLT. I'm sure there was a way but I can't remember.
So instead of
test="$var = 'text1' or $var = 'text2'"
I'd like to use a shorter version like test="$var =['text1','text2']" However, I can't remember or find the right shorthand syntax for such cases.
Would really appreciate if someone could help with that!
Many thanks
With XSLT 2.0 (but not with XSLT 1.0) you can do
<xsl:if test="$var = ('text1','text2')">
Maybe that is the syntax you are looking for.
For string values as you appear to be using you can use a concat trick:-
test="contains('__text1____text2__', concat('__', $var, '__'))"
Not shorter for just two items but given 5 or more it starts to look better.
Having said that you probably can multi-line when using or's so it may be better just to use a series of or's:-
test = "
$var = 'text1'
or $var = 'text2'
or $var = 'text3'
or $var = 'text3'"
More text but clearer solution.
If you find that you do many comparisons against a fixed set of values, you can also do this:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:cfg="http://tempuri.org/config"
exclude-result-prefixes="cfg"
>
<xsl:output method="text" />
<!-- prepare a fixed list of possible values; note the namespace -->
<config xmlns="http://tempuri.org/config">
<val>text1</val>
<val>text2</val>
<!-- ... -->
</config>
<!-- document('') lets you access the stylesheet itself -->
<xsl:variable name="cfg" select="document('')/*/cfg:config/cfg:val" />
<xsl:template match="/">
<xsl:variable name="var" select="'text2'" />
<!-- check against all possible values in one step -->
<xsl:if test="$cfg[.=$var]">
<xsl:text>Match!</xsl:text>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
The above would print
Match!
The [] operator only works on a nodeset. Maybe you're thinking of when you say something like [a|b] to select nodes from your nodeset that have a child element a or a child element b. But for string comparison I don't know of any way other than using "or".
There is no 'contains' function for sequences, but you could use index-of or intersect:
fn:exists(('test1', 'test2') intersect $var))
or
fn:exists(fn:index-of(('test1', 'test2'), $var))
With only two strings, your original solution is shorter though.