<xsl:when test="person/id!=127 or 112" >
or condition is not working in the above example. Please help
You've got your syntax wrong, and it should be an and, not or. Try this:
<xsl:when test="person/id!=127 and person/id!=112" >
If you put an or there, your condition is going to be true no matter what the value of ID is, because no number can equal 127 and 112 at the same time.
Try
<xsl:when test="person/id!=127 and person/id!=112">
Use:
not(person/id= 127 or person/id = 112)
Try to always avoid the != XPath operator, due to its unintuitive (and rarely useful) semantics when one or both of its arguments are node-sets 9or sequences in XPath 2.0).
When you have a long list to compare against, this kind of expression may be more convenient and efficient:
not(contains('|101|105|108|112|123|127|', concat('|', person/id, '|'))
I am not an xslt developer and never used it before. However, It appears that test statement is missing. what should xslt parser do with 112? no condition is provided for it.
Related
I'm starting to learn XSLT/XPath, and I copied the following from a study guide, making some modifications:
<xsl:variable name="fname" select="'polist.xml'"/>
<xsl:variable name="thePath" select="'/collection/doc'"/>
...
<xsl:value-of select="count(doc($fname)/collection/doc)"/>
It reports the number of doc elements in the XML file. The doc() function accepts the file name variable 'fname'. But if I try to do the same with the 'thePath' variable in the count() function, using $thePath instead of the "/collection/doc" text, I get an error.
Suggestions on whether/how to use the 'thePath' variable in the count() function? Is it possible? Thanks!
Learning from examples leaves you very exposed to this kind of problem: it's easy to build a completely incorrect mental model of how the examples actually work. That's why I always advise people to start by reading a good book that explains the concepts first.
In your case you've made a common mistake, which is to assume that variables work like macros, that is, that they represent fragments of XPath text that can be substituted into an expression. That's not the case: variables represent values, the result of evaluating an expression, and you can only use a variable in places where a literal value (like a number or string) could appear.
(I suspect it's the use of the $ sign that leads to this false impression. $ is often used to represent variables in macro-like languages, for example shell scripts).
In XPath 1.0 there's no direct way of achieving what you are trying to do. In practice people either use vendor extensions for this, or they construct a pipeline in which phase 1 generates an XSLT stylesheet and phase 2 executes it (that's easier in XSLT than in most other languages, because XSLT is XML and can therefore be easily manipulated using XSLT).
In 3.0 you can evaluate XPath expressions supplied in the form of a string using the xsl:evaluate instruction. But very often, the requirement can be met better using functions. We don't know what the real underlying requirement is here so it's hard to know whether that's true in this case.
An example use of xsl:evaluate in XSLT 3 would be e.g.
<xsl:evaluate xpath="'count(' || $thePath || ')'" context-item="doc($fname)"/>
I want to validate an assumption that a set of 'or' statements as a predicate of a for-each operates analogous to the choose statement. Either things work as I expect or I just haven't hit the edge case that will cause an issue.
In a choose structure I know that it is evaluated in sequence and when the sequence finds a test that resolves to 'true' the processor drops out of the choose.
<xsl:choose>
<xsl:when test="number($A) and $A=1">
<!—do a thing -->
</xsl:when>
<xsl:when test="number($A) and $A=2">
<!—do a thing -->
</xsl:when>
<xsl:when test="number($A) and $A < 3">
<!—do a thing -->
</xsl:when>
</xsl:choose>
So if the first and third when is true (highly probable), the processor drops out of the choose when the first when resolves as true. This is great and expected. However, when this choose needs to be compressed into a for-each it seems a simple task, but a nagging thought in my mind is that I'm just getting lucky.
<xsl:for-each select="bunch of paths[(number($A) and $A=1) or (number($A) and $A=2) or (number($A) and $A < 3)]
In this example whether the first or third (or both) passes probably doesn't matter, but most of my work operates on financial dates so in that respect I want to be sure that if the first and third both pass that the predicate will evaluate the same as the choose statement.
So, I'm just seeking to ensure that my assumption that has been based on observation of predicate evaluation within the for-each is the same as the choose.
A significant difference between the two cases is that with the xsl:for-each example the evaluation context changes (both in the predicate, and in the body of the for-each instruction), while in the xsl:choose case, the context is the same throughout.
Another difference is that xsl:when always takes the effective boolean value of the test expression (so number 1 means true), whereas predicates have different semantics if the value is numeric.
<xsl:value-of select="$MyVar"/>
works but
<xsl:value-of select="MyDataPfath/$MyVar"/>
do not work.
What is wrong in my code?
From the look of it, what you are trying to achieve is 'dynamic evaluation'. XSLT does not support the dynamic evaluation of XPath by default, so you will need to make use of an extension function.
Depending on your XSLT processor, you might want to look at EXSLT extensions. In particular the dynamic module at http://www.exslt.org/dyn/index.html. This would allow to do something like this
<xsl:value-of select="dyn:evaluate('MyDataPfath/$MyVar')"/>
However, in your case, perhaps the $MyVar contains just a single element name. In which case you could change your command to the following, which would work without any extension functions
<xsl:value-of select="MyDataPfath/*[local-name() = $MyVar]"/>
Your code didn't fail, it did exactly what the specification says it should do. Which was different from what you were hoping/imagining that it might do.
Your hopes/imagination were based on a fundamental misunderstanding of the nature of variables in XPath. XPath variables are not macros. They don't work by textual substitution; they represent values. If the variable $E contains the string "X", then MyPath/$E means the same as MyPath/"X", which is illegal in XPath 1.0, and in XPath 2.0 returns as many instances of the string "X" as there are nodes in MyPath.
You probably intended MyPath/*[name()=$E]
it is not possible to get the value by using syntax 'MyDataPfath/$MyVar' in . it will not recognize the proper path.
suppose $MyVar has value 'Hi'. so it will be represented as 'MyDataPfath/"Hi"', this is not valid path, which you want to retrieve from the XML.
to remove this limitation, You can use name() or local-name() function, that can be used as follows:
or
I have the following XSLT code that almost does what I want:
<xsl:variable name="scoredItems"
select=
".//item/attributes/scored[#value='true'] |
self::section[attributes/variable_name/#value='SCORE']/item |
.//item//variables//variable_name"/>
I want to change this to a more complicated boolean expression:
<xsl:variable name="scoredItems"
select=
".//item/attributes/scored[#value='true'] or
(self::section[variable_name/#value='SCORE']/item and
(not (.//item/attributes/scored[#value='false']))) or
.//item//variables//variable_name"/>
However, when I run this, I get the following error:
javax.xml.transform.TransformerConfigurationException: Could not compile stylesheet
at org.apache.xalan.xsltc.trax.TransformerFactoryImpl.newTemplates(TransformerFactoryImpl.java:832)
at org.apache.xalan.xsltc.trax.TransformerFactoryImpl.newTransformer(TransformerFactoryImpl.java:618)
How do I fix this? (Note that I'm using XSLT 1.0.)
In my experience, the default exception thrown by XSLT in Java is not very helpful. You'll need to implement an instance of ErrorListener and use its methods to capture and report the true XSLT problem. You can attach this ErrorListener using the setErrorListener method of your TransformerFactory.
I would greatly discourage anyone to write complicated expressions -- in any language!
This is not an XSLT question at all. It is a general programming question and the answer is:
Never write too complicated expressions because they are challenging to write, read, test, verify, proof, change.
Split a complicated expression onto a number of simpler expressions and assign them to different variables. Then operate on these variables.
What conventions (if any) do you use for indenting XSL code?
how do you deal with really long, complicated XPaths
can you plug them into your XML editor of choice?
is there some open source code that does the job well?
For some background, I use nxml-mode in Emacs. For the most part its OK and you can configure the number of spaces that child elements should be indented. Its not very good though when it comes to complicated XPaths. If I have a long XPath in my code, I like to make it's structure as transparent as possible by making it look something like this...
<xsl:for-each select="/some
/very[#test = 'whatever']
/long[#another-test = perhaps
/another
/long
/xpath[#goes='here']]
/xpath"
However, I currently have to do that manually as nxml will just align it all up with the "/some.."
Sometimes a longer xpath can't be avoided, even if you use templates instead of for-eaches (like you should, if you can). This is especially true in XSLT/XPath 2.0:
<xsl:attribute name="tablevel"
select="if (following::*[self::topic | self::part])
then (following::*[self::topic | self::part])[1]/#tablevel
else #tablevel"/>
I tend not to break a "simple" path across lines, but will break the "greater" path at operators or conditionals.
For editing, I use Oxygen (which is cross-platform) and it handles this kind of spacing pretty well. Sometimes it doesn't predict what you want exactly, but it will maintain the space once it's there, even if you re-indent your code.
In my opinion, long xpaths are hard to read and should be avoided. There are 2 ways to do it:
Simplify the source xml.
Split big templates into smaller ones.
Don't use long xpaths. Ditch the for-each and use match templates. Break down the xpath into several templates. It's much easier to read a bunch of trivial match templates than one of these.
I tend to break down the XSL differently if I'm having difficulty reading the xpath statements (which isn't very often, but it happens occasionally)... it's actually rather similar to my methods of breaking up syntax for other languages... So your example in the question might become something more like this:
<xsl:for-each select="/some/very[#test = 'whatever']/long">
<xsl:if test="#another-test = perhaps/another/long/xpath[#goes='here']">
<xsl:for-each select="xpath">
... result xml ....
</xsl:for-each>
</xsl:if>
</xsl:for-each>