In XSLT, we can use xsl:analyze-string to check input string with regex expression and get regex-group.
For example, I have following strings of shapes:
segment: 20m
triangle: 30m 30m 30m
rectangle: 10m 10m 30m 30m
...so on (but it is finite)
I want to use xml element to markup them with xsl:analyze-string.
Approach 1:
<xsl:for-each select="$lines">
<xsl:analyze-string select="." regex="segment: (\w*)">
<xsl:matching-substring>
<segment seg1="{regex-group(1)}"/>
</xsl:matching-substring>
</xsl:analyze-string>
<xsl:analyze-string select="." regex="triangle: (\w*), (\w*), (\w*)">
<xsl:matching-substring>
<triangle seg1="{regex-group(1)}" seg2="{regex-group(2)}" seg3="{regex-group(3)}"/>
</xsl:matching-substring>
</xsl:analyze-string>
<xsl:analyze-string select="." regex="rectangle: (\w*), (\w*), (\w*), (\w*)">
<xsl:matching-substring>
<rectangle seg1="{regex-group(1)}" seg2="{regex-group(2)}" seg3="{regex-group(3)}" seg4="{regex-group(4)}"/>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:for-each>
This approach has a disadvantage, that is: if we have some irregular data in line, it will ignore, but it should report error message.
Approach 2:
Nest xsl:analyze-string in xsl:non-matching-substring element, but there will be very ugly code if I have more than 8 kinds of Shape.
The best way to resolve this issue is integrating switch case or if else-if syntax in xsl:analyze-string.
So is there any way to switch case xsl:analyze-string in XSLT?
Use template matching.
In XSLT 3.0:
<xsl:template match=".[starts-with(., 'segment)]">
<xsl:analyze-string select="." regex="segment: (\w*)">
<xsl:matching-substring>
<segment seg1="{regex-group(1)}"/>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:template>
<xsl:template match=".[starts-with(., 'triangle)]">
<xsl:analyze-string select="." regex="triangle: (\w*), (\w*), (\w*)">
<xsl:matching-substring>
<triangle seg1="{regex-group(1)}" seg2="{regex-group(2)}" seg3="{regex-group(3)}"/>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:template>
etc.
and then
<xsl:apply-templates select="$lines"/>
Template rules that match atomic values are new in 3.0. In 2.0 you can use the same technique provided that the input ($lines) is a sequence of nodes, rather than a sequence of strings.
Related
I have one question regarding xslt. In my input I have strings like:
<dxrCardNumber2>[EBERG615] [104699] [104913]</dxrCardNumber2>
and I have to remove all brackets and values between the brackets, if they contains a letter.
The result should be:
=> <dxrCardNumber2>104699 104913</dxrCardNumber2>
The position of the data I have to remove is random. I tried it with tokenize and then I can filter the not relevant entries out. But at teh end I have the problem to combine all entries again in one string.
Assuming you can use XSLT 2.0 or higher, you could do:
<xsl:template match="dxrCardNumber2">
<xsl:copy>
<xsl:value-of select="tokenize(translate(., '[]', ''), ' ')[matches(., '^\d+$')]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="dxrCardNumber2">
<xsl:analyze-string select="." regex="\[([A-Z0-9]+)\]\s+\[([0-9]+)\]\s+\[([0-9]+)\]" flags="i">
<xsl:matching-substring>
<dxrCardNumber2><xsl:value-of select="concat(regex-group(2),' ')"/>
<xsl:value-of select="regex-group(3)"/></dxrCardNumber2>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
INPUT:
<title>This is <b>sample</b> ack <i>file</i>: good</title>
NEED OUTPUT:
<title>This is <b>sample</b> ack <i>file</i>: Good</title>
Just convert the letter 'g' lowercase to uppercase without any change using XSL.
Thanks in Advance.
In XSLT 2.0 you can do:
<xsl:template match="title/text()">
<xsl:analyze-string select="." regex=":\s*.">
<xsl:matching-substring>
<xsl:value-of select="upper-case(.)"/>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
Demo: https://xsltfiddle.liberty-development.net/gVAkJ5g/1
I'm trying to replace a single char with an element (containing more elements).
Using XSL 2.0.
Example:
<element1>
<element2>some text and the char - I want to replace </element2>
...
</element1>
The - (dash) should now be replaced with a new element:
<element1>
<element2>some text and the char <newElement/> I want to replace </element2>
...
</element1>
I tried already:
<xsl:template match="element1">
<xsl:analyze-string select="." regex="-">
<xsl:matching-substring>
<newElement/>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
But this removed all the other elements inbetween (because only strings are "returned").
And with the function replace() you only can insert strings (no < possible).
Any further ideas?
Your template matches an element(), but replaces text(). If you match text() and replace text() instead while copying the rest, it will work as expected:
<!-- modified identity template matching no text() nodes -->
<xsl:template match="element() | comment() | processing-instruction()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:analyze-string select="." regex="-">
<xsl:matching-substring>
<newElement/>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:copy-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
Two corrections are needed:
Your template should match element2, not element1.
At the beginning and end of your tempate you should add
opening / closing tag for element2 (something like in
the identity template).
So your template should look like this:
<xsl:template match="element2">
<element2>
<xsl:analyze-string select="." regex="-">
<xsl:matching-substring>
<newElement/>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</element2>
</xsl:template>
Of course, your script should include also the identity template.
I'm doing xml to xml transformation using XSLT and I have a XML code like follows.
<section>
<para>height 10cm, width 15cm</para>
<para>height 20cm, width 15cm</para>
<para>height 10cm, width 22cm</para>
</section>
here I need to double the height and width value in the output. So transformed xml would be,
<section>
<para>height 20cm, width 30cm</para>
<para>height 40cm, width 30cm</para>
<para>height 20cm, width 44cm</para>
</section>
I thought about use XSLT regex to solve this matter and wrote following template,
<xsl:template match="para/text()">
<xsl:variable name="elValue" select="."/>
<xsl:analyze-string select="$elValue" regex="(\d{{5}}(\-\d{{4}})?)\s*">
<xsl:matching-substring>
<xsl:value-of select="number(regex-group(1))*2"/>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:copy-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
but it does not work as expected.
can anyone suggest me a method how can I doubled that numbers exist within para elements?
I am far from being a regex whiz, but this seems to be working for me:
<xsl:template match="para/text()">
<xsl:analyze-string select="." regex="\d+">
<xsl:matching-substring>
<xsl:value-of select="2 * number(.)"/>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
I've the below XML.
<title>1. IN AGENTS IN GENERAL</title>
Here i'm trying to convert this into camel case, and if there are a set of words, if they occur they have to be changed into lower case. But the condition is, if these words come in starting, they have to be changed into camel case, else lower case.
And my XSL is
<xsl:template match="title">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="title/page"/>
<xsl:param name="Conjunction">^(of|to|and|the|for|on|or|an|as|by|of|it|between|with|in|into|on|onto|here|a)$</xsl:param>
<xsl:template match="title/text()">
<xsl:analyze-string select="." regex="(\w)(\w*)">
<xsl:matching-substring>
<xsl:value-of
select="if (matches(., $Conjunction, 'i'))
then lower-case(.)
else concat(upper-case(regex-group(1)), lower-case(regex-group(2)))"/>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
and as per the input the Expected output should be
1. In Agents in General
but what is get is
1. in Agents in General
here is the working demo. DEmo
please let me know how can i get this done.
Thanks
If you can assume a full-stop delimits each sentence, and you want the first word of each sentence to be capitalised regardless, you could tokenize the text by full-stop...
<xsl:for-each select="tokenize(., '\.')">
You then apply your regular expression on each part separately, but include a check on the position in the matching-substring so that it doesn't convert the first match to lower case.
Try this template
<xsl:template match="title/text()">
<xsl:for-each select="tokenize(., '\.')">
<xsl:if test="position() > 1">.</xsl:if>
<xsl:analyze-string select="." regex="(\w)(\w*)">
<xsl:matching-substring>
<xsl:value-of
select="if (matches(., $Conjunction, 'i') and position() > 2)
then lower-case(.)
else concat(upper-case(regex-group(1)), lower-case(regex-group(2)))"/>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:for-each>
</xsl:template>
In your current code, try changing:
<xsl:value-of select="if (matches(., $Conjunction, 'i'))
to
<xsl:value-of select="if (matches(., $Conjunction, 'i') and position() != 3)