<block4>
<tag>
<name>50K</name>
<value>/001/002/300060000120135670
CREDIT AGRICOLE ASSET MANAGEMENT</value>
</tag>
</block4>
I need to get output that looks like:
/001/002,/300060000120135670,CREDIT AGRICOLE ASSET MANAGEMENT
I have done like this in XSL, but I didn't get the output I wanted. Can anyone please give me some idea how I could get that output?
<xsl:for-each select ="block4/tag[name = '50K']">
<xsl:value-of select="
concat(
substring(value,1,8),
(concat(substring(value,9,'
'),',')),
substring-after(value,'
')
)
" />,<xsl:text/>
</xsl:for-each>
concat takes any number of arguments, no need to nest those calls. Besides, substring takes a beginning and an optional length, not a terminating character. Try something like this instead:
<xsl:for-each select ="block4/tag[name = '50K']">
<xsl:value-of select="
concat(
substring(value, 1, 8), ',',
substring(substring-before(value,'
'),9), ',',
substring-after(value,'
')
)
" />,<xsl:text/>
</xsl:for-each>
I've kept the final comma in, which is one of the many things you did not really specify.
Why not use XSLT 2.0 tokenize() function?
See Here
Related
I am using XSLT (2.0) as a java code generator. At the moment I have a XML which describes a database table and I want to generate the entity class for it.
The column names of the table are always lowercase with _ between the words.
Example: bat_valid_from
I want to rename it in the Java class to camelcase with first letter lowercase
Example: batValidFrom
Because I need this quiet often in my codeGen I like to have a function for it.
But I only could achieve this with two sub functions.
<xsl:function name="local:VarName">
<xsl:param name="columnName"/>
<xsl:value-of select="lower-case(substring($columnName,1,1))"/>
<xsl:value-of select="substring(local:VarName_sub($columnName),2)"/>
</xsl:function>
<xsl:function name="local:VarName_sub">
<xsl:param name="columnName"/>
<xsl:value-of select="local:VarName_sub_sub($columnName)"/>
</xsl:function>
<xsl:function name="local:VarName_sub_sub">
<xsl:param name="columnName"/>
<xsl:for-each select="tokenize($columnName, '_')">
<xsl:value-of select="upper-case(substring(.,1,1))"/>
<xsl:value-of select="substring(.,2)"/>
</xsl:for-each>
</xsl:function>
Maybe someone has an idea to simplify this?
Without the sub functions I get the following error:
A sequence of more than one item is not allowed as the first argument of fn:substring()
PS: I haven't posted the whole code to shorten the question
XSLT/XPath 2.0 has supports for expressions. You could do this:
string-join(
for $part in tokenize($input, '_')
return concat(
upper-case(substring($part, 1, 1)),
substring($part, 2)
)
, '')
with $input set to 'bat_valid_from', this expression would produce 'BatValidFrom'.
I'm leaving lower-casing (or not upper-casing) the initial letter as an exercise.
With the hint from Tomalak i was able to make all in one function.
Maybe not light weighted but works like a charm.
<xsl:function name="local:VarName">
<xsl:param name="columnName"/>
<xsl:value-of select="
concat(
lower-case(substring($columnName, 1, 1)),
substring(string-join(for $word in tokenize($columnName, '_')
return concat(
upper-case(substring($word, 1, 1)),
substring($word, 2)), '')
, 2))" />
</xsl:function>
I have my string as below
<Text>Pack:NA Lead:20 Dimension:235</Text>
And need to map
NA to outputfield1
20 to outputfield2
235 to outputfield3
How to do this correctly in xslt mapping where the values 'NA,20,235' could be different each time?
I could only see substring component which takes length as second parameter.
That leads requires several steps to achieve this.
Any better solution to just take the value between Lead: and Dimension for outputfield2?
To extract the Pack value, you can use:
<xsl:value-of select="substring-before(substring-after(Text, 'Pack:'), ' ')" />
To extract the Lead value, use:
<xsl:value-of select="substring-before(substring-after(Text, 'Lead:'), ' ')" />
To extract the Dimension:
<xsl:value-of select="substring-after(Text, 'Dimension:')" />
I have a number such as: 457342137 but i want to display it as a 457 342 137.
I have something like this:
<xsl:template match="klient/#numer_telefonu">
<xsl:variable name="numer" select="." />
<xsl:value-of select="format-number($numer, '### ### ###')" />
</xsl:template>
but it does not work.
If you want to use a non-standard 'grouping' separator, you first need define the symbols you are going to use in your format command as follows:
<xsl:decimal-format name="spaces" grouping-separator=" " />
Then, you can reference this format in the command itself as follows:
<xsl:value-of select="format-number($numer, '# ###', 'spaces')" />
Further information about decimal-format can be found at http://www.w3.org/TR/xslt#format-number
A telephone number is a string, not a number and you shouldn't try formatting it as one. Technically, you could do:
<xsl:value-of select="translate(format-number(., '#,###'), ',', ' ' )" />
to achieve the desired result in your example. However, given a "number" such as "057342137" the result will be "57 342 137" (leading zeros stripped). You should be using string functions to manipulate a string.
I have the following xml:
<log>
<logentry revision="11956">
<author>avijendran</author>
<date>2013-05-20T10:25:19.678089Z</date>
<msg>
JIRA-1263 - did something
</msg>
</logentry>
<logentry revision="11956">
<author>avijendran</author>
<date>2013-05-20T10:25:19.678089Z</date>
<msg>
JIRA-1263 - did something 22 again
</msg>
</logentry>
</log>
I want to ignore any occurrence of the JIRA-1263 after the first one.
The xpath I am trying is (Which works if the duplicates nodes are following. But if you have duplicates else where(deep down), then it is ignored:
<xsl:variable name="uniqueList" select="//msg[not(normalize-space(substring-before(., '
')) = normalize-space(substring-before(following::msg, '
')))]" />
If you want to get each msg use //msg[starts-with(normalize-space(.), 'JIRA-1263')] to get output JIRA-1263 - did something and JIRA-1263 - did something 22 again.
And if you want to get any element with same codition use //*[starts-with(normalize-space(.), 'JIRA-1263')] which give same result as previous one.
At the end, if you want to get first msg with same condition use //logentry/msg[starts-with(normalize-space(.), 'JIRA-1263')][not(preceding::msg)] to get output JIRA-1263 - did something
You can define a key at the top level of your stylesheet that groups log entries by their first word:
<xsl:key name="logentryByCode" match="logentry"
use="substring-before(normalize-space(msg), ' ')" />
Now you need to select all logentry elements where either
the msg does not start JIRA-nnnn (where nnnn is a number) or
this entry is the first one whose msg starts with this word (i.e. the first occurrence of "JIRA-1234 - anything" for each ticket number)
(note that these two conditions need not be mutually exclusive):
<xsl:variable name="uniqueList" select="log/logentry[
(
not(
starts-with(normalize-space(msg), 'JIRA-') and
boolean(number(substring-before(substring(normalize-space(msg), 6), ' ')))
)
)
or
(
generate-id() = generate-id(key('logentryByCode',
substring-before(normalize-space(msg), ' '))[1])
)
]/msg" />
The boolean(number(...)) part checks whether a string of text can be parsed as a valid non-zero number (the text in this case being the part of the first word of the message that follows JIRA-), and the generate-id trick is a special case of the technique known as Muenchian grouping.
Equally, you could group the msg elements instead of the logentry elements, using match="msg" in the key definition and normalize-space(.) instead of normalize-space(msg).
And here another interpretation of what you try to do.
Find any first logentry which start with JIRA-XXXX.
If this it right try this:
log/logentry[
starts-with(normalize-space(msg), 'JIRA-') and
not
(
substring-before( normalize-space(msg), ' ')= substring-before( normalize-space(preceding::msg), ' ')
)]
This will find any logentry which starts with JIRA- but has not preceding one with the same substring before the first space (JIRA-XXXX) in your example.
How to use the choose condition in XSLT when the below requirement is needed
<xsl:choose>
<xsl:when test="contains(#name,'top %d holdings' ) ">
<!--code-->
</xsl:when>
</xsl:choose>
It should select all the data containing....
top 5 holdings
top 10 holdings
top 30 holdings
top 27 holdings
top * holdings
If you were using XSLT2.0 here, you could use the matches function which allows you to match text by means of a regular expression
<xsl:when test="matches(#name, '.*top \d+ holdings.*')">
On the other hand, if you were using XSLT 1.0, then the matches function is not available. One way you could do it in your very specific case is extract the text before "holdings" that occurs before "top" and check it is a number:
<xsl:when test="string(number(substring-before(substring-after(#name, 'top '), ' holdings' ) )) != 'NaN'">
You can use substring-before() and substring-after() to get the text between top and holdings, and then use the translate() function to remove numbers and the * character, and then verify that the result is an empty string.
<xsl:choose>
<xsl:when
test="translate(
substring-before(substring-after(#name, 'top '), ' holdings' ),
'0123456789*',
'') = '' ">
<!--code-->
</xsl:when>
</xsl:choose>