confusing statement while version change - xslt

I've the below XSLT statement.
<xsl:value-of select="substring-after(
./title/content-style/text(),' ')" />
when i use the style sheet version 1 as
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ntw="Number2Word.uri"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="ntw">
But when i change it to version 2.0 as below.
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ntw="Number2Word.uri"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="ntw">
it is throwing the below error
XSLT 2.0 Debugging Error: Error:
file:///C:/Users/u0138039/Desktop/Proview/HK/HKWB2014/XSLT%20and%20CSS/new_bull.xsl:202:
Wrong occurrence to match required sequence type -
Details: - XPTY0004: The supplied sequence ('2' item(s)) has
the wrong occurrence to match the sequence type xs:string ('zero or
one')
please let me know what is going right in XSL1.0 is going wrong in XSL 2.0.
Thanks

In XSLT 1.0, when you pass a node set of more than one node to a function that expects a single string, it simply uses the string value of the first node in the set and ignores the rest of them. In XSLT 2.0 the substring-after function expects its arguments to be xs:string?, i.e. zero or one string, so if you pass it a sequence with more than one item it will cause a type mismatch error.
I presume that ./title/content-style/text() selects more than one node, i.e. there's more than one title, more than one content-style and/or more than one text node within the content-style element.
Consider whether you actually need to use text() here at all - do you really need to process each text node child of content-style individually, or do you just want the string value of the content-style element as a whole? If the former, you need something like title/content-style/text()/substring-before(., ' ') (which only works in 2.0), if the latter, try just saying substring-after(title/content-style, ' ').

I had the same issue, but for me, it turned out that while my schema, for the element in question, had the multiplicity optional setting, it did not have the unbounded setting, and the input data I was using had 2 elements where it was expecting 0...1

Related

Xpath Matching a node and getting the value of it

Below is the xml file:
file1.xml
<?xml version="1.0" encoding="UTF-8"?><W4N xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:functx="http://www.functx.com"><LUNGROUP><OBJECT lungroupID="0" lunIds="0,221,228"/></LUNGROUP><LUNGROUP><OBJECT lungroupID="1" lunIds="1,3,5/></LUNGROUP></W4N>
I want to match on lunIds. I have given the below xpath expression /W4N/LUNGROUP/OBJECT[tokenize(#lunIds,',')='228']
Its showing the result as Elements found: 1
Now my requirement is to get the lungroupID of the matched element.How can I do this using xpath? Any help is highly appreciated.
I don't see the XML you intended to post, but you should be able to add the attribute you want at the end of your xpath expression:
/W4N/LUNGROUP/OBJECT[tokenize(#lunIds,',')='228']/#lungroupID

How to avoid Open xml tag on its own line

I have an XML structure as below:
<?xml version="1.0" encoding="utf-8"?>
<cl:doc identifier="ISBN" xsi:schemaLocation="http://xml.cengage-learning.com/cendoc-core cendoc.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cl="http://xml.cengage-learning.com/cendoc-core" xmlns:m="http://www.w3.org/1998/Math/MathML">
<cl:chapter identifier="ch01">
<cl:opener identifier="ch06_opn">
<cl:introduction identifier="ch06_int">
<cl:list identifier="tu_1" list-style="Unformatted" item-length="long">
<cl:item identifier="tu_2"><cl:para identifier="ch01_dum_2">Solubility</cl:para></cl:item>
<cl:item identifier="tu_3"><cl:para identifier="ch01_dum_3">Polarity</cl:para></cl:item>
</cl:list></cl:introduction></cl:opener></cl:chapter></cl:doc>
When I transform this above xml using XSLT, I got the below output:
<?xml version="1.0" encoding="utf-8"?>
<cl:doc identifier="ISBN" xsi:schemaLocation="http://xml.cengage-learning.com/cendoc-core cendoc.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cl="http://xml.cengage-learning.com/cendoc-core" xmlns:m="http://www.w3.org/1998/Math/MathML"><cl:chapter identifier="ch01">
<cl:opener identifier="ch06_opn">
<cl:introduction identifier="ch06_int"><cl:list identifier="tu_1" list-style="Unformatted" item-length="long">
<cl:item identifier="tu_2"><cl:para identifier="ch01_dum_2">Solubility</cl:para></cl:item>
<cl:item identifier="tu_3"><cl:para identifier="ch01_dum_3">Polarity</cl:para></cl:item></cl:list></cl:introduction></cl:opener></cl:chapter></cl:doc>
Here, the opening tag <cl:opener identifier="ch06_opn"> alone comes on separate line. This result me to have the blank line after doing the conversion.
I need this <cl:opener identifier="ch06_opn"> tag must be run-on with either its previous line or to the next line.
Can anybody help me how this can be achieved through XSLT.
Thanks,
Gopal
Without seeing your XSLT it's difficult to be certain, but it sounds like your XSLT is copying over the whitespace in the source into the output.
The quickest way to prevent that is to put
<xsl:strip-space elements="*"/>
or alternatively
<xsl:template match="text()[not(normalize-space())]"/>
This removes all whitespace, but you can of course be more specific about the whitespace you're removing, such as
<xsl:template match="cl:opener/text()[1][not(normalize-space())]"/>
to remove just the whitespace after that opening element tag- this matches the first text node within cl:opener if it's whitespace only, and outputs nothing in it's place.

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 to get text with xpath from bad xml?

I have a "bad xml structure" file:
<cars>
<car>Toyota
<country>Japan</coutry>
....
</car>
</cars>
How to correctly get the right word (Toyota) using Xpath?
I tried:
<xsl:value-of select = "cars/car/text()"/>.
It works, but I think there are more appropriate methods.
Thanks.
Use:
/cars/car/text()[1]
or if you want to discard most of the white space in the text node selected above, use:
normalize-space(/cars/car/text()[1])
Do note that while in XSLT 1.0 <xsl:value-of> outputs the string valu only of the first node of the node-set selected by the expression in the select attribute, <xsl:copy-of> will output all the nodes in the node-set. In XSLT 2.0 even <xsl:value-of> outputs all the nodes in the node-set.
Therefore, for purposes of portability, upgradability and simply for avoiding errors, it is better to specify which exactlyy node from the nodeset is to be output -- even when using <xsl:value-of>

apostrophe text comparison in xsl

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.