I face the following issue:
i have the source XML file:
<TABLE NAME="TEST">
<DATA RECORDS="78">
<catalog>
<book id="bk109">
<description>An anthology of horror stories about roaches, centipedes, scorpions and other insects.</description>
</book>
<book id="bk110">
<description>Microsoft's .NET initiative is explored in detail in this deep programmer's reference.</description>
</book>
<book id="bk111">
<description>An anthology of HORROR stories about roaches, centipedes, scorpions and other insects.</description>
</book>
<book id="bk112">
<description>An anthology of horror stories about roaches, centipedes, scorpions and other insects.</description>
</book>
<book id="bk113">
<description>An anthology of horror stories about roaches, centipedes, scorpions and other insects.</description>
</book>
<book id="bk114">
<description>Microsoft's .NET initiative is explored in detail in this deep PROGRAMMER's reference.</description>
</book>
<book id="bk115">
<description>An anthology of HORROR stories about roaches, centipedes, scorpions and other insects.</description>
</book>
<book id="bk116">
<description>An anthology of horror stories about roaches, centipedes, scorpions and other insects. Beware, this must not be matched.</description>
</book>
<book id="bk114">
<description>Microsoft's .NET initiative is explored in detail in this deep PROGRAMMER's reference. Beware, this must not be matched.</description>
</book>
</catalog>
</DATA>
</TABLE>
Given the following xsl:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:functx="http://www.functx.com"
exclude-result-prefixes="xs functx">
<xsl:param name="search-text" as="xs:string">An anthology of horror stories about roaches, centipedes, scorpions and other insects.
Microsoft's .NET initiative is explored in detail in this deep programmer's reference.</xsl:param>
<xsl:param name="replacement-text" as="xs:string">Value we need to store in the (description) element.
Another value we need to store in the (description) element.</xsl:param>
<xsl:param name="search-terms" as="xs:string*" select="tokenize($search-text, '\r?\n')"/>
<xsl:param name="search-terms-is" as="xs:string*" select="for $term in $search-terms return concat('^', lower-case(functx:escape-for-regex($term)), '$')"/>
<xsl:param name="replace-terms" as="xs:string*" select="tokenize($replacement-text, '\r?\n')"/>
<xsl:include href="http://www.xsltfunctions.com/xsl/functx-1.0-nodoc-2007-01.xsl"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="description[some $search-term in $search-terms-is satisfies matches(., $search-term, 'i')]">
<xsl:copy>
<xsl:variable name="matched-term" as="xs:string" select="$search-terms-is[matches(current(), ., 'i')]"/>
<xsl:variable name="replacement" as="xs:string" select="$replace-terms[index-of($search-terms-is, $matched-term)]"/>
<xsl:value-of
select="$replacement"/>
</xsl:copy>
</xsl:template>
</xsl:transform>
result is ok:
<?xml version="1.0" encoding="UTF-8"?><TABLE NAME="TEST">
<DATA RECORDS="78">
<catalog>
<book id="bk109">
<description>Value we need to store in the (description) element.</description>
</book>
<book id="bk110">
<description>Another value we need to store in the (description) element.</description>
</book>
<book id="bk111">
<description>Value we need to store in the (description) element.</description>
</book>
<book id="bk112">
<description>Value we need to store in the (description) element.</description>
</book>
<book id="bk113">
<description>Value we need to store in the (description) element.</description>
</book>
<book id="bk114">
<description>Another value we need to store in the (description) element.</description>
</book>
<book id="bk115">
<description>Value we need to store in the (description) element.</description>
</book>
<book id="bk116">
<description>An anthology of horror stories about roaches, centipedes, scorpions and other insects. Beware, this must not be matched.</description>
</book>
<book id="bk114">
<description>Microsoft's .NET initiative is explored in detail in this deep PROGRAMMER's reference. Beware, this must not be matched.</description>
</book>
</catalog>
</DATA>
</TABLE>
but if in the search terms, i have twice the same terms, in different case, i get error
xsl:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:functx="http://www.functx.com"
exclude-result-prefixes="xs functx">
<xsl:param name="search-text" as="xs:string">An anthology of horror stories about roaches, centipedes, scorpions and other insects.
An anthology of HORROR stories about roaches, centipedes, scorpions and other insects.</xsl:param>
<xsl:param name="replacement-text" as="xs:string">Value we need to store in the (description) element.
Another value we need to store in the (description) element.</xsl:param>
<xsl:param name="search-terms" as="xs:string*" select="tokenize($search-text, '\r?\n')"/>
<xsl:param name="search-terms-is" as="xs:string*" select="for $term in $search-terms return concat('^', lower-case(functx:escape-for-regex($term)), '$')"/>
<xsl:param name="replace-terms" as="xs:string*" select="tokenize($replacement-text, '\r?\n')"/>
<xsl:include href="http://www.xsltfunctions.com/xsl/functx-1.0-nodoc-2007-01.xsl"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="description[some $search-term in $search-terms-is satisfies matches(., $search-term, 'i')]">
<xsl:copy>
<xsl:variable name="matched-term" as="xs:string" select="$search-terms-is[matches(current(), ., 'i')]"/>
<xsl:variable name="replacement" as="xs:string" select="$replace-terms[index-of($search-terms-is, $matched-term)]"/>
<xsl:value-of
select="$replacement"/>
</xsl:copy>
</xsl:template>
</xsl:transform>
error:
<?xml version="1.0" encoding="UTF-8"?><TABLE NAME="TEST">
<DATA RECORDS="78">
<catalog>
<book id="bk109">
Error on line 30
XTTE0570: A sequence of more than one item is not allowed as the value of variable
$matched-term ("^an anthology of horror storie...", "^an anthology of horror storie...")
at xsl:apply-templates (#23)
processing /TABLE/DATA[1]/catalog[1]/book[1]/description[1]
at xsl:apply-templates (#23)
processing /TABLE/DATA[1]/catalog[1]/book[1]
at xsl:apply-templates (#23)
processing /TABLE/DATA[1]/catalog[1]
at xsl:apply-templates (#23)
processing /TABLE/DATA[1]
in built-in template rule
how could i alter stylesheet, so as not to have this error occur?
The example was uploaded online by Martin Honnen, who kindly answered my previous question.
online at http://xsltransform.net/gVhD8RA
Related
I've been having issues while doing arithmetic operations on following XML
Source XML
<?xml version="1.0" encoding="UTF-8"?>
<Compensation>
<Salary>
<BasePay>$18600.12</BasePay>
<Bonus>$3500.99</Bonus>
<Gym>$670</Gym>
<Tax>$30,000</Tax>
</Salary>
<Salary>
<BasePay>$28600.12</BasePay>
<Bonus>$1500.99</Bonus>
<Gym/>
<Tax>$50,000</Tax>
</Salary>
</Compensation>
Current XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:this="urn:this-stylesheet"
exclude-result-prefixes="xs this"
version="2.0">
<xsl:output method="xml" indent="yes"/>
<xsl:function name = "this:translateCurrency">
<xsl:param name="stringValue"/>
<xsl:value-of select="format-number(xs:decimal(translate(xs:string($stringValue), '$,','')), '#.##')"/>
</xsl:function>
<xsl:template match="Compensation">
<Worker>
<xsl:for-each select="Salary">
<Comp>
<Amount>
<xsl:value-of select="this:translateCurrency(BasePay) - this:translateCurrency(Tax) "/>
</Amount>
<NoBonus>
<xsl:value-of select="this:translateCurrency(BasePay) + this:translateCurrency(Gym) "/>
</NoBonus>
</Comp>
</xsl:for-each>
</Worker>
</xsl:template>
</xsl:stylesheet>
Currency symbol and commas will always be present in amount related XML elements such as <BasePay> <Bonus> <Gym> <Tax> which i am translating and converting to decimal before adding or substracting.
There are two issues
1. Since my source XML have many Amount related fields, I have declared a function for translating and converting to decimal. However, I'm unable to get my function rounding to two decimal points. I was expecting following line of code in my function will be able to round to two decimal points.
<xsl:value-of select="format-number(xs:decimal(translate(xs:string($stringValue), '$,','')), '#.##')"/>
2. It's possible that some of the amount fields may be null for e.g. <Gym/> is null in my Source XML and current version of XSLT returns Cannot convert to string "" to xs:decimal no digits in value.
I tried $stringValue!='' in xsl:function statement and Gym!='' but to no avail.
Can anyone help me figure out what i should be doing to get my function round to two decimal points and get past no digits in value error?
<NoBonus>
<xsl:value-of select="this:translateCurrency(BasePay) + this:translateCurrency(Gym!='') "/>
</NoBonus>
Expected Result
<?xml version="1.0" encoding="UTF-8"?>
<Worker>
<Comp>
<Amount>-11399.88</Amount>
<NoBonus>19270.12</NoBonus>
</Comp>
<Comp>
<Amount>-21399.88</Amount>
<NoBonus>28600.12</NoBonus>
</Comp>
</Worker>
If you want to convert a string to a decimal value then don't use format-number on it. So for your input values to be converted into xs:decimals you need e.g.
<xsl:function name="this:translateCurrency" as="xs:decimal">
<xsl:param name="input" as="xs:string"/>
<xsl:sequence
select="if ($input = '')
then 0
else xs:decimal(translate($input, '$,', ''))"/>
</xsl:function>
Then use those xs:decimal values in any arithmetic computations, only where you need to output the final result of an arithmetic computation in a certain format use format-number on that result to ensure e.g. you get two decimals.
Building on this topic:
How do I select an XML node with the longest child #text node value with XPath?
I try to find the longest cell in column 1 of a table. Unfortunately I don't know how many ancestors the table has and there are sometimes several within one text- element which should be treated different.
XML
<text><table cols="3" rows="2">
<row >
<cell >first cell first row</cell>
<cell >second cell first row
</cell>
<cell >third cell first row
</cell>
</row>
<row >
<cell >first cell second row</cell>
<cell >this is an incredible long text</cell>
<cell />
</row>
</table>
</text>
XSLT:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="table">
<xsl:variable name="longest1">
<xsl:sequence select=
"/*/table/row/cell[1][not(string-length(.) < /*/table/row/cell[1]/string-length(.))]"/>
</xsl:variable>
<xsl:value-of select="longest1">
</xsl:template>
</xsl:stylesheet>
The output of course should be "first cell second row", as the second column is not processed.
I'm quite sure all I have to do is to fix the /* of this line:
<xsl:sequence select=
"/*/table/row/cell[1][not(string-length(.) < /*/table/row/cell[1]/string-length(.))]"/>
But I can't manage to see the solution.
As you are writing a template for the table you can simply use
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:template match="table">
<xsl:sequence select="row/cell[1][not(string-length() < current()/row/cell[1]/string-length())]"/>
</xsl:template>
</xsl:stylesheet>
Of course another approach would simply be to sort row/cell[1] by its string-length and taking the last, done in XSLT 3.0 with XPath 3.1 using sort(row/cell[1], function($c) { string-length($c)})[last()] or in XSLT 2.0 using <xsl:variable name="sorted-cells" as="element(cell)*"><xsl:perform-sort select="row/cell[1]"><xsl:sort select="string-length()"/></xsl:perform-sort></xsl:variable><xsl:copy-of select="$sorted-cells[last()]"/>.
I have an xml like this (see below).
Using xslt2.0, I need to find whether any one contain a mix of positive and negative numbers ?
Weights: concantenated string of positive/negative numbers.. (separator = ;).
<PriceInfo>
<price>
<date>20160124</date>
<weights>1;2;5;4;</weights>
</price>
<price>
<date>20160125</date>
<weights>1;2;3;4;</weights>
</price>
<price>
<date>20160126</date>
<weights>1;-2;3;4;</weights>
</price>
</PriceInfo>
Thanks
Well, using tokenize you can extract the tokens between ;, you can then check whether they are integers, if so, convert them, and then you can check whether there are any greater than and any smaller than zero:
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="weights">
<xsl:copy>
<xsl:variable name="integers" select="for $token in tokenize(., ';')[. castable as xs:integer] return xs:integer($token)"/>
<xsl:sequence select="(some $i in $integers satisfies $i gt 0) and (some $j in $integers satisfies $j lt 0)"/>
</xsl:copy>
</xsl:template>
</xsl:transform>
I modified the answer from Martin to suit my needs.
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:template match="/">
<xsl:variable name="allWeights" select="string-join(//weights/text(), '')" />
<xsl:variable name="weightTokens" select="for $token in tokenize($allWeights, ';')[. castable as xs:string] return xs:string($token)"/>
<xsl:variable name="isMixOfPositiveNegativeWeights" select="(some $posToken in $weightTokens satisfies matches($posToken, '[0-9].*')) and (some $negToken in $weightTokens satisfies matches($negToken, '-.*'))"/>
<xsl:value-of select="$isMixOfPositiveNegativeWeights" />
</xsl:template>
</xsl:transform>
##I need to iterate over commas to break the characters between comma in xslt. There can be at the max 15 words separated by comma##
For example
`Input
<root>
<child>A,B,C,D</child>
</root>
Output
<root>
<List>A</List>
<List>B</List>
<List>C</List>
<List>D</List> `
You may use fn:tokenize to accomplish this. It seperates a string by a delimiter and returns the individual letters without the delimiter.
fn:tokenize("abracadabra", "(ab)|(a)") returns ("", "r", "c", "d", "r", "")
For further reference: http://www.w3.org/TR/xpath-functions/#func-tokenize
Are you sure that you're using xslt 2.0, because fn:tokenize should work:
(Don't use the 'fn' namespace, this is the default namespace in xpath build in functions)
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="/root">
<output>
<xsl:for-each select="tokenize(child, ',')">
<child><xsl:value-of select="."/></child>
</xsl:for-each>
</output>
</xsl:template>
</xsl:transform>
Example:
http://xsltransform.net/eiQZDbf
I have a XML file like below:
<NOP>A focus on individual exhibitors is essential, though, in order to
understand how these figures influenced American (and global) culture and
how audiences were attracted to movies and movie theaters. Charles Musser
writes in his examination of Lyman Howe’s career that “a focus on
exhibition lends itself to industrial history precisely because it must
address the economic basis of the motion picture industry—the showman’s
ability to bring patrons through the front door.â€<ENREF>1</ENREF> In order
to understand the artistic and managerial influences of showmen like Samuel
Lionel Rothafel (“Roxyâ€) and Sidney Patrick Grauman, one must analyze
their construction of stardom, their ethnic heritage and cultural background,
their facility with music, theater, film, and other performing arts, and the
ways in which they motivated patrons to enter their “front door.â€
</NOP>
and I'm using the below XSLT with XML spy but it's not working for 'ENREF'. Any help
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:template match="NOP">
<div class="no-indent"><span><xsl:value-of select="."/></span></div>
</xsl:template>
<xsl:template match="ENREF">
<small>
<sup>
<xsl:element name="a">
<xsl:attribute name="id">enref-<xsl:value-of select="."/></xsl:attribute>
<xsl:attribute name="href">#fn<xsl:value-of select="."/></xsl:attribute>
<xsl:value-of select="."/>
</xsl:element>
</sup>
</small>
</xsl:template>
</xsl:stylesheet>
It does not suffice to write a template matching a certain element, you also need to ensure the element is processed. You have not shown us the result you want so I have to guess what you want to achieve but for a start try changing
<xsl:template match="NOP">
<div class="no-indent"><span><xsl:value-of select="."/></span></div>
</xsl:template>
to
<xsl:template match="NOP">
<div class="no-indent"><span><xsl:apply-templates/></span></div>
</xsl:template>
That way the child nodes of the NOP element are processed, either by the built-in templates (which ensure with <xsl:apply-templates/> that the processing is kept up for grandchildren and further descendants) or by your templates (like the one for the ENREF element).