XSLT - changes number exist within text() node - regex

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>

Related

XSL - Removing text blocks from string

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>

How to convert lowercase to uppercase without any change inner elements using XSL

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

XSLT replace char with an element

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.

How to switch case xsl:analyze-string instead of nesting?

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.

Extracting a number from a string in a XSLT file

Is there a way to extract a number from a string after a set of characters.
Our software uses XSLT files to convert emails into XML files. In the Subject line of an email there can be a reference to an already opened Incident/Service Request/Task.
For example - the Subject of an email could be:
RE: SR#51417: D_SATTER-NOV60LKA-I_G-A0201244
I want to extract the Service Request Number 51417 from the Subject.
The number will always be after the String "SR#". "SR#" could be written as "sr#", "Sr#" or "sR#".
I was trying to use the RegEx functions in XSLT but can't get it to work.
Do you have any suggestions on how to do this?
Thanks in advance.
Update
I am trying the solution provided by cyclexx. This is the Code that I have put in my XSLT File:
<xsl:when test="contains($subject, 'SR#')">
<xsl:element name="Field">
<xsl:attribute name="Name">
<xsl:text>a_eco_parentObjectType</xsl:text>
</xsl:attribute>
<xsl:value-of select="'ServiceReq'"></xsl:value-of>
</xsl:element>
<xsl:element name="Field">
<xsl:attribute name="Name">
<xsl:text>a_eco_parentObjectID</xsl:text>
</xsl:attribute>
<xsl:analyze-string select ="$subject" regex="\s*[Ss][Rr]#([0-9]+)\s*">
<xsl:matching-substring>
<SR>
<xsl:value-of select="regex-group(1)"></xsl:value-of>
</SR>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:element>
The variable $subject contains the Subject line of the Email file that is being processed. The output file just contains:
ServiceReq
and I have an error message: :Error in loading Hierarchical Object XSLT file(s)
Here a solution :)
<emails>
<email>
<subject>RE: SR#51417: D_SATTER-NOV60LKA-I_G-A0201244</subject>
</email>
<email>
<subject>RE: Sr#565465: D_SATTER-NOV60LKA-I_G-A0201244</subject>
</email>
</emails>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />
<xsl:template match="subject">
<xsl:variable name="v" select="." />
<xsl:analyze-string select="$v" regex="\s*[Ss][Rr]#([0-9]+)\s*">
<xsl:matching-substring>
<SR>
<xsl:value-of select="regex-group(1)" />
</SR>
</xsl:matching-substring>
<xsl:non-matching-substring>
<subject>
<xsl:value-of select="$v"/>
</subject>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
</xsl:stylesheet>