apostrophe text comparison in xsl - xslt

I have a problem with text with apostrophe symbol
example i try to test this xml having the symbol is then how can i compare ?
<xsl:for each select="country[nation='India's]">
this is statement showing error
Regards
Nanda.A

One way to do it would be:
<xsl:variable name="apos" select='"&apos;"'/>
<!-- ... later ... -->
<xsl:for-each select="country[nation=concat('India', $apos, 's')]">
The problem here is twofold:
XSLT defines no way of character escaping in strings. So 'India\'s' is not an option.
You must get through two distinct layers of evaluation.
These are:
XML well-formedness: The XML document your XSLT program consists of must be well-formed. You cannot violate XML rules.
XSLT expression parsing: The resulting attribute value string (after XML DOM parsing is done) must be make sense to the XSLT engine.
Constructs like:
<xsl:for-each select="country[nation='India's']">
<xsl:for-each select="country[nation='India&apos;s']">
pass the XML layer but violate the XSLT layer, because in both cases the effective attribute value (as stored in the DOM) is country[nation='India's'], which clearly is an XPath syntax error.
Constructs like:
<xsl:for-each select="country[nation=concat('India', "'", 's')]">
<xsl:for-each select="country[nation=concat("India", "&apos;", "s")]">
clearly violate the XML layer. But they would not violate the XSLT layer (!), since their actual value (if the XSLT document could be parsed in the first place) would come out as country[nation=concat('India', "'", 's')], which is perfectly legal as an XPath expression.
So you must find a way to pass through both layer 1 and layer 2. One way is the variable way as shown above. Another way is:
<xsl:for-each select="country[nation=concat('India', "'", 's')]">
which would appear to XSLT as country[nation=concat('India', "'", 's')].
Personally, I find the "variable way" easier to work with.

Related

XSLT 2 analyze-string finding block names

I am trying to detect strings in other languages in my XML.
I thought I could use something like :
<xsl:analyze-string select="$mystring" regex="(\p{InGreek})" >
but I am unable to make this work.
Do you think this is possible in XSLT ? How would you do this ?
Thanks a lot.
Maria
(XSLT 2, Saxon-HE 9.8.0.8)
I think the right category name would be IsGreek so the regular expression would be \p{IsGreek}, however as the regex attribute of xsl:analyze-string allows attribute value templates you either need to put the expression into a string variable <xsl:param name="pattern" as="xs:string">\p{IsGreek}</xsl:param>you reference as regex="{$pattern}" or you need to duplicate the curly braces, as in regex="\p{{IsGreek}}".

Using for-each-group for high performance XSLT

I have an XSLT (1.0) style sheet. It works with no problem. I want to make it to 2.0. I want to use xsl:for-each-group (and make it have high performance). It is possible? How? Please explain.
I have many places like
<xsl:if test="test condition">
<xsl:for-each select="wo:tent">
<width aidwidth='{/wo:document/styles [#wo:name=current()/#wo:style-name]/#wo:width}'
</xsl:for-each>
</xsl:if>
ADDED
<xsl:template match="wo:country">
<xsl:for-each select="#*">
<xsl:copy/>
</xsl:for-each>
<xsl:variable name="states" select="wo:pages[#xil:style = "topstates" or #xil:style = "toppage-title"]"/>
<xsl:variable name="provinces" select="wo:pages[#xil:style = "topprovinces"]"/>
<xsl:choose>
<xsl:when test="$states">
<xsl:apply-templates select="$states[2]/preceding-sibling::*"/>
<xsl:apply-templates select="$states[2]" mode="states">
<xsl:with-param name="states" select="$states[position() != 0]"/>
</xsl:apply-templates>
</xsl:when>
<xsl:when test="$provinces">
<xsl:apply-templates select="$provinces[2]/preceding-sibling::*"/>
<xsl:apply-templates select="$provinces[2]" mode="provinces">
<xsl:with-param name="provinces" select="$provinces[position() != 2]"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
THE SOURCE
<?xml version="1.0" encoding="UTF-8"?>
<wo:country>
some stuff
</wo:country>
I have assumed that you want an in-depth description of xsl:for-each-group and how to use it. If this is not what you are asking for, then please let me know.
The instruction, new in XSLT 2.0, takes a set of items and groups them. The set of items is called "the population", and the groups are just called groups. The instruction processes each group in turn.
Possible attributes of the xsl:for-each-group instruction include:
select
group-by
group-adjacent
group-starting-with
group-ending-with
collation
#select is mandatory. The others are optional. It can take any number of xsl:sort children (but they must come first), followed by a sequence constructor. A "sequence constructor" is the term for all the sequence emitting type instructions that go inside templates and the like.
#select
The select attribute specifies an XPATH expression which evaluates to the population to be grouped.
#group-by
The group-by attribute specifies an XPATH expression, which you use when the type of grouping is by common value. Every item in the population that evaluates to the same group-by value as another is in the same group as that other.
XSLT 1.0 Muenchian grouping is not too difficult when the type of grouping is group by common value. There are two more common forms of grouping: group adjacent items by similar value; and group an adjacent group of items whose group is either demarcated at the end or the at the beginning by some test. While both these forms of grouping are still possible with Muenchian, it becomes relatively complex. Muenchian on these types will also be less efficient at scale, because of the use of sibling axises (however you spell that!).
Another advantage of XSLT 2.0 that comes to mind is that Muenchian only works on node sets, whereas xsl:for-each-group is broader in application because it works on a sequence of items, not just nodes.
The result of the #group-by expression will be a sequence of items. This sequence is atomized and de-duped. The population item being tested will be a member of one group per value. It's a strange consequence, that with #group-by, and item may be a member of more than one group, or perhaps even none. Although I suspect that any thing that you can do in XSLT 2.0, you can, by some tortuous path, do in XSLT 1.0, the ability to put an item into two groups is something that would be quiet fiddly to do in XSLT 1.0 Muenchian.
#group-adjacent
The attributes group-by, group-adjacent, group-starting-with and group-ending-with are mutually exclusive because they specify different kinds of grouping. Items with commons values and adjacent in the population are grouped together. Unlike #group-by, #group-adjacent must evaluate to, after atomization, a single atomic value.
group-starting-with
Unlike select, group-adjacent and group-by, this attribute does not specify an XPATH select expression, but rather a pattern, in the same way the xsl:template/#match specifies a pattern, not a selection. If an item in the population passes the pattern test or is the first item in the population then it starts a new group. Otherwise the item continues the group from the previous item.
Martin mentioned the spec examples (w3.org/TR/xslt20/#grouping-example). From that reference, I am going to copy the example entitled "Identifying a Group by its Initial Element", but alter it slightly to emphasis the point about the initial item of the population.
So this is our input document (copied from w3 spec. The inclusion of the orphaned line is mine) ...
<body>
<p>This is an orphaned paragraph.</p>
<h2>Introduction</h2>
<p>XSLT is used to write stylesheets.</p>
<p>XQuery is used to query XML databases.</p>
<h2>What is a stylesheet?</h2>
<p>A stylesheet is an XML document used to define a transformation.</p>
<p>Stylesheets may be written in XSLT.</p>
<p>XSLT 2.0 introduces new grouping constructs.</p>
</body>
... what we want to do is define groups as nodes starting with h2 and include all the following p up until the next h2. The example solution given by w3 is to use #group-starting-with ...
<xsl:template match="body">
<chapter>
<xsl:for-each-group select="*" group-starting-with="h2" >
<section title="{self::h2}">
<xsl:for-each select="current-group()[self::p]">
<para><xsl:value-of select="."/></para>
</xsl:for-each>
</section>
</xsl:for-each-group>
</chapter>
</xsl:template>
In the spec example, when the input does not contain an orphan line, this produces the desired result ...
<chapter>
<section title="Introduction">
<para>XSLT is used to write stylesheets.</para>
<para>XQuery is used to query XML databases.</para>
</section>
<section title="What is a stylesheet?">
<para>A stylesheet is an XML document used to define a transformation.</para>
<para>Stylesheets may be written in XSLT.</para>
<para>XSLT 2.0 introduces new grouping constructs.</para>
</section>
</chapter>
Although in our particular case we get instead ...
<chapter>
<section title="">
<para>This is an orphaned paragraph.</para>
</section>
<section title="Introduction">
<para>XSLT is used to write stylesheets.</para>
<para>XQuery is used to query XML databases.</para>
</section>
<section title="What is a stylesheet?">
<para>A stylesheet is an XML document used to define a transformation.</para>
<para>Stylesheets may be written in XSLT.</para>
<para>XSLT 2.0 introduces new grouping constructs.</para>
</section>
</chapter>
If the initial section for the orphaned lines is undesired, there are easy solutions. I won't go into them now. My point is just to high-light the fact that the first group resulting from #group-starting-with can be an 'orphan' group. By 'orphan', I mean a group whose head node does not fit the specified pattern.
#collation
The collation attribute specifies a collation URI and identifies a collation used to compare strings for equality.
current-group()
Within the xsl:for-each-group the current-group() function returns the current group being processed as a sequence of items.
current-grouping-key()
Within the xsl:for-each-group the current-group() function returns the current group key. I am not sure, but I believe that this can only be an atomic type. Also not sure, but I believe that this function is only applicable to #group-by and #group-adjacent type of grouping.
#group-by versus #group-adjacent
In some scenarios you will have a choice between these two sort types with the same functional result. When this is the case #group-adjacent is to be preferred over #group-by, because it will likely be more efficient to process.
Pattern versus Select
Some XSLT 2.0 instruction attributes contain select expressions. Michael Kay calls these "XPath expressions". Personally, when juxtaposing against patterns, I feel a better description would be "select expression". Other attributes contain patterns or "match expressions". While these two both contain the same syntax, they are very different beasts. The similarity between the two often makes XSLT beginners think of xsl:template/#match not as a pattern, but as a select expression. The consequence has been a lot of confusion from beginners about the value of the position() function within template's sequence constructors. As stated earlier, in xsl:for-each-group, #select, #group-by and #group-adjacent are select expressions, but #group-starting-with and #group-ending-with are patterns. So here is the difference:
Select expressions are a like a function. The input is a context document, context sequence, context item, context position and of course the actual expression. The output is a sequence of items. Depending where this is actually used, this could become the next context sequence. The default axis is child:: .
Unlike select expression, the default axis for a pattern is self:: . The pattern is also like a function. Its inputs are as before, and its output is not a sequence, but a boolean. Some item is being tested to see if it matches the pattern or not. The item being tested is made the context item. The match expression is temporarily evaluated as it were a select expression. Then the returned sequence is tested to see if the context item is a member or not. The returned sequence is then discarded. The result is true or 'match' if it was a member, and false otherwise.
Sean has provided a wonderful overview of xsl:for-each-group, which was very generous, but it doesn't really seem to be an answer to your question.
You've shown a fragment of XSLT code, and you've said you want faster performance. But the fragment you showed is not doing grouping, it is doing a join. There are two ways you can speed up a join. Either use an XSLT processor such as Saxon-EE that does automatic join optimization, or optimize it by hand using keys. For example, given this expression:
/wo:document/styles [#wo:name=current()/#wo:style-name]/#wo:width
you could define a key
<xsl:key name="style-name-key" match="styles" use="#wo:name"/>
and then replace the expression by
key('style-name-key', #wo:style-name)/#wo:width

In Xalan XSLT 1.0, how to pass a variable to a template match?

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.)

How can I translate ' into an apostrophe in xslt

The relevant parts of the code:
<xsl:variable name="apos">'</xsl:variable>
<xsl:variable name="and" select='"'"' />
<xsl:value-of select="translate(products_name/node(),$and,$apos)"/>
I'm thinking this should be a simple thing and that the above code should work but it doesn't effect the output at all.
(I used variables because names cannot begin within an ampersand and using just an apostrophe brings up a compile error.)
I've tested the code to make sure the translate is working using strings and there are no errors there.
Any help would be greatly appreciated.
You are on the right track, but not yet there: Your problem is, that XSL is a language that itself is written using XML. For all XML languages, the parser automatically decodes XML entities. The XSLT engine only comes afterwards.
As a result, the XSLT engine neither does nor can distinguish whether you wrote ' or ' - it's the same. For your problem, this has two effects:
You have to use a variable containing the apostrope - this is because the apostrophe itself is reserved for string literals in expressions that may contain functions. Even for <xsl:value-of select="translate(products_name/node(),$and,''')"/>, the XML parser transforms the entity into an apostrophe, i.e. <xsl:value-of select="translate(products_name/node(),$and,''')"/>
You have to escape the ampersand used in the string you search for: for the XSL engine, the variable "and" contains the value ', i.e. you are replacing an apostrophe with an apostrophe.
Working solution:
<xsl:variable name="apos">'</xsl:variable>
<xsl:value-of select='translate(text(), "&#039;", $apos)'/>
Technically, there's no difference in any XML between &apos;, ' and ', they're different ways of representing exactly the same thing. Therefore, that translate call shouldn't do anything.
It depends on how you're transforming it, where that output is (attribute value or element?), and how the output is serialized to text, but your problem isn't with your XSLT.

Concatenate with escaped chars in xslt?

I'm writing xslt code which concatenates some string:
<xsl:attribute name='src'>
<xsl:value-of select="concat('url(&apos;', $imgSrc, '&apos;)')" />
</xsl:attribute>
For some reason I can't use it, I keep getting this error:
Unknown function - Name and number of arguments do not match any function signature in the static context - 'http://www.w3.org/2005/xpath-functions:concat'
while evaluating the expression:
select="concat('url(&apos;', $imgSrc, '&apos;)')"
Any idea?
thx
====================
EDIT
I'm trying to get:
url('some_path')
Was having trouble with the apostrophes, but now it just doesn't work.
The &apos; references are resolved by the XML parser that parses your XSLT. Your XSLT processor never sees them. What your XSLT processor sees is:
concat('url('', $imgSrc, '')')
Which is not valid because the commas don't end up in the right place to separate the arguments. However, this might work for you, depending on the serializer your XSLT processor uses:
concat("url('", $imgSrc, "')")
This surrounds the arguments in double-quotes, so that your single-quotes do not conflict. The XSLT processor should see this:
concat("url('", $imgSrc, "')")
Another option is to define a variable:
<xsl:variable name="apos" select='"&apos;"'/>
Which can be used like this:
concat('url(', $apos, $imgSrc, $apos, ')')
More here:
When you apply an XSLT stylesheet to a
document, if entities are declared and
referenced in that document, your XSLT
processor won't even know about them.
An XSLT processor leaves the job of
parsing the input document (reading it
and figuring out what's what) to an
XML parser; that's why the
installation of some XSLT processors
requires you to identify the XML
parser you want them to use. (Others
include an XML parser as part of their
installation.) An important part of an
XML parser's job is to resolve all
entity references, so that if the
input document's DTD declares a cpdate
entity as having the value "2001" and
the document has the line "copyright
&cpdate; all rights reserved", the XML
parser will pass along the text node
"copyright 2001 all rights reserved"
to put on the XSLT source tree.
From http://www.w3.org/TR/xpath/#NT-Literal
[29] Literal ::= '"' [^"]* '"' | "'" [^']* "'"
Meaning that an XPath literal string value can't have the delimiter as also part of the content.
For this you should use the host language. In XSLT:
<xsl:variable name="$vPrefix">url('</xsl:variable>
<xsl:variable name="$vSufix">')</xsl:variable>
<xsl:attribute name="src">
<xsl:value-of select="concat($vPrefix, $imgSrc, $vSufix)" />
</xsl:attribute>
Or more proper:
<xsl:attribute name="src">
<xsl:text>url('</xsl:text>
<xsl:value-of select="$imgSrc"/>
<xsl:text>')</xsl:text>
</xsl:attribute>