I'm using WSO2 Micro Integrator and I have an API that receives a request with a Timestamp. I want to convert the Timestamp to UTC format. How can I convert a timestamp to UTC in wso2? The timestamp is like 2020-10-06T08:08:35-04:00.
Option 1 : Script Mediator
The easiest way is to use a script mediator.
<property expression="//time1/text()" name="time" scope="default" type="STRING"/>
<script language="nashornJs">
<![CDATA[
if(mc.getProperty('time')) {
var date = new Date(mc.getProperty("time"));
mc.setProperty("convUTC", date.toISOString());
}
]]>
</script>
Option 2 : XSLT Mediator
You can also do the same with XSLT Mediator as well. Here is an XSLT that will work for you. First, add the XSLT as a Local Entry like below and then use it to transform the timestamp.
<?xml version="1.0" encoding="UTF-8"?>
<localEntry key="TimeToUTC" xmlns="http://ws.apache.org/ns/synapse">
<xsl:stylesheet version="1.0" xmlns:ns4="http://nmdp.org/esb/domain/v04" xmlns:ns7="http://nmdp.org/esb/domain/v02" xmlns:ns8="http://nmdp.org/esb/domain/v01" xmlns:sfdc="sfdc" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="UTF-8" indent="yes" method="xml" omit-xml-declaration="yes"/>
<xsl:param name="TimeStamp"/>
<xsl:template match="/">
<payload xmlns="">
<UTCTime>
<xsl:call-template name="dateTime-to-UTC">
<xsl:with-param name="dateTime" select="$TimeStamp"/>
</xsl:call-template>
</UTCTime>
</payload>
</xsl:template>
<xsl:template name="dateTime-to-UTC">
<xsl:param name="dateTime"/>
<xsl:choose>
<xsl:when test="contains($dateTime, 'Z')">
<xsl:value-of select="$dateTime"/>
</xsl:when>
<xsl:otherwise>
<!-- extract components -->
<xsl:variable name="date" select="substring-before($dateTime, 'T')"/>
<xsl:variable name="time" select="substring-after($dateTime, 'T')"/>
<!-- date components -->
<xsl:variable name="year" select="substring($date, 1, 4)"/>
<xsl:variable name="month" select="substring($date, 6, 2)"/>
<xsl:variable name="day" select="substring($date, 9, 2)"/>
<!-- time components -->
<xsl:variable name="local-time" select="substring($time, 1, string-length($time) - 6)"/>
<xsl:variable name="hour" select="substring($local-time, 1, 2)"/>
<xsl:variable name="minute" select="substring($local-time, 4, 2)"/>
<xsl:variable name="second" select="substring($local-time, 7)"/>
<!-- offset components -->
<xsl:variable name="offset" select="substring-after($time, $local-time)"/>
<xsl:variable name="offset-sign" select="1 - 2 * starts-with($offset, '-')"/>
<xsl:variable name="offset-hour" select="substring($offset, 2, 2) * $offset-sign"/>
<xsl:variable name="offset-minute" select="substring($offset, 5, 2) * $offset-sign"/>
<!-- convert to seconds -->
<xsl:variable name="a" select="floor((14 - $month) div 12)"/>
<xsl:variable name="y" select="$year + 4800 - $a"/>
<xsl:variable name="m" select="$month + 12*$a - 3"/>
<xsl:variable name="jd" select="$day + floor((153*$m + 2) div 5) + 365*$y + floor($y div 4) - floor($y div 100) + floor($y div 400) - 32045"/>
<xsl:variable name="total-seconds" select="86400*$jd + 3600*$hour + 60*$minute + $second - 3600*$offset-hour - 60*$offset-minute"/>
<!-- convert to date -->
<xsl:variable name="new-jd" select="floor($total-seconds div 86400)"/>
<xsl:variable name="new-hour" select="floor($total-seconds mod 86400 div 3600)"/>
<xsl:variable name="new-minute" select="floor($total-seconds mod 3600 div 60)"/>
<xsl:variable name="new-second" select="$total-seconds mod 60"/>
<xsl:variable name="f" select="$new-jd + 1401 + floor((floor((4 * $new-jd + 274277) div 146097) * 3) div 4) - 38"/>
<xsl:variable name="e" select="4*$f + 3"/>
<xsl:variable name="g" select="floor(($e mod 1461) div 4)"/>
<xsl:variable name="h" select="5*$g + 2"/>
<xsl:variable name="D" select="floor(($h mod 153) div 5 ) + 1"/>
<xsl:variable name="M" select="(floor($h div 153) + 2) mod 12 + 1"/>
<xsl:variable name="Y" select="floor($e div 1461) - 4716 + floor((14 - $M) div 12)"/>
<!-- output -->
<xsl:value-of select="$Y"/>
<xsl:value-of select="format-number($M, '-00')"/>
<xsl:value-of select="format-number($D, '-00')"/>
<xsl:text>T</xsl:text>
<xsl:value-of select="format-number($new-hour, '00')"/>
<xsl:value-of select="format-number($new-minute, ':00')"/>
<xsl:value-of select="format-number($new-second, ':00.###')"/>
<xsl:text>Z</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
</localEntry>
You can use the XSLT like below.
<property expression="//time1/text()" name="time" scope="default" type="STRING"/>
<xslt key="TimeToUTC">
<property expression="$ctx:time" name="TimeStamp"/>
</xslt>
<log>
<property expression="//UTCTime/text()" name="Converted Time with XSLT"/>
</log>
Full Combined Example
<?xml version="1.0" encoding="UTF-8"?>
<api context="/time" name="HelloWorld" xmlns="http://ws.apache.org/ns/synapse">
<resource methods="POST">
<inSequence>
<property expression="//time1/text()" name="time" scope="default" type="STRING"/>
<xslt key="TimeToUTC">
<property expression="$ctx:time" name="TimeStamp"/>
</xslt>
<log>
<property expression="//UTCTime/text()" name="Converted Time with XSLT"/>
</log>
<script language="nashornJs"><![CDATA[if(mc.getProperty('time')) {
var date = new Date(mc.getProperty("time"));
mc.setProperty("convUTC", date.toISOString());
}]]></script>
<log>
<property expression="$ctx:convUTC" name="Converted Time with Script"/>
</log>
<payloadFactory media-type="xml">
<format>
<LatestTime>$1</LatestTime>
</format>
<args>
<arg evaluator="xml" expression="$ctx:convUTC"/>
</args>
</payloadFactory>
<respond/>
</inSequence>
<outSequence/>
<faultSequence/>
</resource>
</api>
Request
<request>
<time1>2020-10-06T08:08:35-04:00</time1>
</request>
Response
<LatestTime>2020-10-06T12:08:35.000Z</LatestTime>
Related
Hello and have a nice day!
There are two steps that I'm trying to figure out:
(But not in priority) to take integer from xml file with tag "duration" in milliseconds
I am trying to convert "duration" milliseconds into timecode that will looks like hh:mm:ss:ff , where h - hours, m - minutes, s - seconds and f - frames (25frames=1second).
As I see the algorithm is:
z=milliseconds
h=z idiv (60*60*25)
m=(z-h*60*60*25) idiv (60*25)
s=(z-h*60*60*25-m*60*25) idiv 25
f=(z-h*60*60*25-m*60*25-s*25)
Have any idea how to make a calculation within XSLT properly?
Consider the following example:
XML
<input>
<milliseconds>45045500</milliseconds>
</input>
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="input">
<output>
<xsl:variable name="f" select="floor(milliseconds div 40) mod 25" />
<xsl:variable name="s" select="floor(milliseconds div 1000) mod 60" />
<xsl:variable name="m" select="floor(milliseconds div 60000) mod 60" />
<xsl:variable name="h" select="floor(milliseconds div 3600000)" />
<timecode>
<xsl:value-of select="format-number($h, '00')"/>
<xsl:value-of select="format-number($m, ':00')"/>
<xsl:value-of select="format-number($s, ':00')"/>
<xsl:value-of select="format-number($f, ':00')"/>
</timecode>
</output>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<output>
<timecode>12:30:45:12</timecode>
</output>
Note that this truncates fractional frames. If you want to round to the nearest frame, then change the variables to:
<xsl:variable name="totalFrames" select="round(milliseconds div 40)" />
<xsl:variable name="f" select="$totalFrames mod 25" />
<xsl:variable name="s" select="floor($totalFrames div 25) mod 60" />
<xsl:variable name="m" select="floor($totalFrames div 1500) mod 60" />
<xsl:variable name="h" select="floor($totalFrames div 90000)" />
Here the result will be:
<timecode>12:30:45:13</timecode>
In XSLT 2.0 or higher, you can shorten the expression floor($a div $b) to $a idiv $b.
I am trying to create an XSLT mapping to get the last(max) day of the previous month.
Eg- If I pass a value of 2019-10-17 to the mapping it should return
2019-09-30. The date format that I am using here is YYYY-MM-DD.
tried to get the month from the current data and subtract it with 1 so that it would return the previous month. But I am not able to get the max date of the last month.
xp20:month-from-dateTime (/ns0:ddSelecCorpoMasterOutputCollection/ns0:ddSelecCorpoMasterOutput/ns0:FROM_DATE_FILTER ) - 1
input- sysdate
o/p- maxdate of previous month
eg- i/p-2019-10-18
o/p- 2019-09-30
Thanks in advance.
Finding the last day of previous month in pure XSLT 1.0:
<xsl:template name="end-of-last-month">
<xsl:param name="date"/>
<!-- extract date components -->
<xsl:variable name="year" select="substring($date, 1, 4)"/>
<xsl:variable name="month" select="substring($date, 6, 2)"/>
<!-- go one month back -->
<xsl:variable name="y" select="$year - ($month = 1)"/>
<xsl:variable name="m" select="($month + 10) mod 12 + 1"/>
<!-- get month length -->
<xsl:variable name="cal" select="'312831303130313130313031'"/>
<xsl:variable name="leap" select="not($y mod 4) and $y mod 100 or not($y mod 400)"/>
<xsl:variable name="month-length" select="substring($cal, 2*($m - 1) + 1, 2) + ($m=2 and $leap)" />
<!-- output -->
<xsl:value-of select="$y" />
<xsl:value-of select="format-number($m, '-00')" />
<xsl:text>-</xsl:text>
<xsl:value-of select="$month-length" />
</xsl:template>
Example:
XML
<input>
<date>2019-01-15</date>
<date>2019-02-15</date>
<date>2019-03-15</date>
<date>2019-04-15</date>
<date>2019-05-15</date>
<date>2019-06-15</date>
<date>2019-07-15</date>
<date>2019-08-15</date>
<date>2019-09-15</date>
<date>2019-10-15</date>
<date>2019-11-15</date>
<date>2019-12-15</date>
<date>2020-01-15</date>
<date>2020-02-15</date>
<date>2020-03-15</date>
</input>
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/input">
<output>
<xsl:for-each select="date">
<end-of-last-month date="{.}">
<xsl:call-template name="end-of-last-month">
<xsl:with-param name="date" select="."/>
</xsl:call-template>
</end-of-last-month>
</xsl:for-each>
</output>
</xsl:template>
<xsl:template name="end-of-last-month">
<xsl:param name="date"/>
<!-- extract date components -->
<xsl:variable name="year" select="substring($date, 1, 4)"/>
<xsl:variable name="month" select="substring($date, 6, 2)"/>
<!-- go one month back -->
<xsl:variable name="y" select="$year - ($month = 1)"/>
<xsl:variable name="m" select="($month + 10) mod 12 + 1"/>
<!-- get month length -->
<xsl:variable name="cal" select="'312831303130313130313031'"/>
<xsl:variable name="leap" select="not($y mod 4) and $y mod 100 or not($y mod 400)"/>
<xsl:variable name="month-length" select="substring($cal, 2*($m - 1) + 1, 2) + ($m=2 and $leap)" />
<!-- output -->
<xsl:value-of select="$y" />
<xsl:value-of select="format-number($m, '-00')" />
<xsl:text>-</xsl:text>
<xsl:value-of select="$month-length" />
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<output>
<end-of-last-month date="2019-01-15">2018-12-31</end-of-last-month>
<end-of-last-month date="2019-02-15">2019-01-31</end-of-last-month>
<end-of-last-month date="2019-03-15">2019-02-28</end-of-last-month>
<end-of-last-month date="2019-04-15">2019-03-31</end-of-last-month>
<end-of-last-month date="2019-05-15">2019-04-30</end-of-last-month>
<end-of-last-month date="2019-06-15">2019-05-31</end-of-last-month>
<end-of-last-month date="2019-07-15">2019-06-30</end-of-last-month>
<end-of-last-month date="2019-08-15">2019-07-31</end-of-last-month>
<end-of-last-month date="2019-09-15">2019-08-31</end-of-last-month>
<end-of-last-month date="2019-10-15">2019-09-30</end-of-last-month>
<end-of-last-month date="2019-11-15">2019-10-31</end-of-last-month>
<end-of-last-month date="2019-12-15">2019-11-30</end-of-last-month>
<end-of-last-month date="2020-01-15">2019-12-31</end-of-last-month>
<end-of-last-month date="2020-02-15">2020-01-31</end-of-last-month>
<end-of-last-month date="2020-03-15">2020-02-29</end-of-last-month>
</output>
Demo: https://xsltfiddle.liberty-development.net/94AbWB5
If you have access to the XPath 2.0 date/time library,
(1) convert to an xs:date
<xsl:variable name="d" select="xs:date($in)"/>
(2) extract the day of the month:
<xsl:variable name="dom" select="day-from-date($d)"/>
(3) subtract this number of days from the date:
<xsl:variable name="result" select="$d - xs:dayTimeDuration('P1D') * $dom"/>
I'm trying to generate very simple documentation from the annotations in a Relax NG XML Schema. For example, given the following Relax NG:
<?xml version="1.0" encoding="UTF-8"?>
<grammar xmlns="http://relaxng.org/ns/structure/1.0" xmlns:a="http://relaxng.org/ns/compatibility/annotations/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
<start>
<element name="topNode">
<ref name="topNode-ref"/>
</element>
</start>
<define name="topNode-ref">
<a:documentation>This is the top of the doc.</a:documentation>
<oneOrMore>
<element name="level1">
<ref name="level1-ref"/>
</element>
</oneOrMore>
</define>
<define name="level1-ref">
<a:documentation>Here's some notes about level1.</a:documentation>
<attribute name="att1">
<a:documentation>Details about att1.</a:documentation>
</attribute>
<element name="subLevel2">
<ref name="subLevel2-ref"/>
</element>
</define>
<define name="subLevel2-ref">
<a:documentation>Notes on subLevel2.</a:documentation>
<attribute name="subAtt"/>
<zeroOrMore>
<element name="subLevel3">
<ref name="subLevel3-ref"/>
</element>
</zeroOrMore>
</define>
<define name="subLevel3-ref">
<a:documentation>And here is subLevel3.</a:documentation>
<attribute name="subSubAtt"/>
</define>
</grammar>
Which would be used to valid an XML file like:
<?xml version="1.0" encoding="UTF-8"?>
<topNode>
<level1 att1="some test">
<subLevel2 subAtt="more text"></subLevel2>
</level1>
<level1 att1="quick">
<subLevel2 subAtt="brown">
<subLevel3 subSubAtt="fox"></subLevel3>
</subLevel2>
</level1>
</topNode>
I'd like to be able to produce documentation that lists the basic XPath to each element/attribute and then display any corresponding documentation annotations. For example:
/topNode
This is the top of the doc.
/topNode/level1
Here's some notes about level1
/topNode/level1/#att1
Details about att1.
etc...
Eventually, I'll add in more documentation about "zeroOrMore", possible data types, etc... but I need to get this first step solved first.
I've found the Techquila RELAX-NG Documentation Tools. I've played around with the rng to docbook stylesheet, but it don't do what I'm looking for. It just lists elements individually with no details about the XPath as far as I can tell. I don't see how I can use it as a starting point to get the output I'm after.
Is it possible (and if so, how?) to produce this type of documentation output with XSLT given the RelaxNG example provided?
While XSLT would be ideal, it's not a requirement. I'm open for anything that gets the job done.
This will work for a very simple grammar like your example.
<?xml version='1.0'?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:r="http://relaxng.org/ns/structure/1.0"
xmlns:a="http://relaxng.org/ns/compatibility/annotations/1.0"
>
<xsl:output method="text" />
<xsl:template match="/">
<xsl:apply-templates select="//r:define[a:documentation] | //r:attribute[a:documentation]" />
</xsl:template>
<xsl:template match="r:define">
<xsl:variable name="doc" select="a:documentation" />
<xsl:call-template name="print-path">
<xsl:with-param name="elm" select="//r:element[r:ref/#name=current()/#name]" />
</xsl:call-template>
<xsl:value-of select="$doc" /><xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="r:attribute">
<xsl:variable name="doc" select="a:documentation" />
<xsl:call-template name="print-path">
<xsl:with-param name="elm" select="//r:element[r:ref/#name=current()/ancestor::r:define/#name]" />
<xsl:with-param name="path" select="concat('/#',#name)" />
</xsl:call-template>
<xsl:value-of select="$doc" /><xsl:text>
</xsl:text>
</xsl:template>
<xsl:template name="print-path">
<xsl:param name="elm" />
<xsl:param name="path" />
<xsl:variable name="parent" select="//r:ref[#name=$elm/ancestor::r:define/#name]/ancestor::r:element" />
<xsl:message><xsl:value-of select="$elm/#name" /></xsl:message>
<xsl:choose>
<xsl:when test="$parent">
<xsl:call-template name="print-path">
<xsl:with-param name="elm" select="$parent" />
<xsl:with-param name="path" select="concat('/',$elm/#name,$path)" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat('/',$elm/#name,$path)" /><xsl:text>
</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
I have a xml file like this
<netcdf xmlns="http://www.unidata.ucar.edu/namespaces/netcdf/ncml-2.2" location="file:/dev/null" iosp="lasp.tss.iosp.ValueGeneratorIOSP" start="0" increment="1">
<attribute name="title" value="Vector time series"/>
<dimension name="time" length="100"/>
<variable name="time" shape="time" type="double">
<attribute name="units" type="String" value="seconds since 1970-01-01T00:00"/>
</variable>
<group name="Vector" tsdsType="Structure" shape="time">
<variable name="x" shape="time" type="double"/>
<variable name="y" shape="time" type="double"/>
<variable name="z" shape="time" type="double"/>
</group>
</netcdf>
And I want to the value of the nodes whose name is either variable or group, so what's the right syntax to do things like?
<xsl:value-of select="/netcdf/variable or /netcdf/group"/>
Thanks in advance
Use (namespace declared with prefix x):
"/x:netcdf/*[self::x:variable or self::x:group]"
Do note that XSLT 1.0 xsl:value-of will return always the text value of the first element found. use better xsl:copy-of to show all returned elements.
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:d="http://www.unidata.ucar.edu/namespaces/netcdf/ncml-2.2">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:copy-of select="/*/*[self::d:variable or self::d:group]"/>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<netcdf xmlns="http://www.unidata.ucar.edu/namespaces/netcdf/ncml-2.2"
location="file:/dev/null" iosp="lasp.tss.iosp.ValueGeneratorIOSP"
start="0" increment="1">
<attribute name="title" value="Vector time series"/>
<dimension name="time" length="100"/>
<variable name="time" shape="time" type="double">
<attribute name="units" type="String"
value="seconds since 1970-01-01T00:00"/>
</variable>
<group name="Vector" tsdsType="Structure" shape="time">
<variable name="x" shape="time" type="double"/>
<variable name="y" shape="time" type="double"/>
<variable name="z" shape="time" type="double"/>
</group>
</netcdf>
produces (what I guess is) the wanted result:
<variable xmlns="http://www.unidata.ucar.edu/namespaces/netcdf/ncml-2.2" name="time" shape="time" type="double">
<attribute name="units" type="String" value="seconds since 1970-01-01T00:00"/>
</variable>
<group xmlns="http://www.unidata.ucar.edu/namespaces/netcdf/ncml-2.2" name="Vector" tsdsType="Structure" shape="time">
<variable name="x" shape="time" type="double"/>
<variable name="y" shape="time" type="double"/>
<variable name="z" shape="time" type="double"/>
</group>
Do note: <xsl:value-of> outputs the string value, while <xsl:copy-of> outputs the node(s). In your case the string value of either element is empty on white-space only, so you probably want the elements themselves.
This is really an XPath question and there are different possible solutions:
/*/*[self::d:variable or self::d:group]
(the above is used in the transformation above), or:
/*/d:variable | /*d:group
This one uses the XPath union operator /
Given the following XML:
<interface name="Serial1/0"/>
<interface name="Serial2/0.0"/>
<interface name="Serial2/0.1"/>
<interface name="Serial3/0:0"/>
<interface name="Serial3/0:1"/>
I am trying to produce the following output:
<interface name="Serial1/0">
<unit name="Serial1/0"/>
</interface>
<interface name="Serial2/0">
<unit name="Serial2/0.0"/>
<unit name="Serial2/0.1"/>
</interface>
<interface name="Serial3/0">
<unit name="Serial3/0:0"/>
<unit name="Serial3/0:1"/>
</interface>
I have created the following function for splitting the string:
<xsl:template name="getPhysicalInterfaceName">
<xsl:param name="interfaceName"/>
<xsl:choose>
<xsl:when test="contains($interfaceName, ':')">
<xsl:value-of select="substring-before($interfaceName, ':')"/>
</xsl:when>
<xsl:when test="contains($interfaceName, '.')">
<xsl:value-of select="substring-before($interfaceName, '.')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$interfaceName"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
I found references to using the xsl:key element, but I didn't see an obvious way to use it in the context. Any idea? (I am using xsltproc (XSLT1.0) to do the transformation.)
XSLT 1.0:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:key
name = "kInterfaceByName"
match = "interface"
use = "
substring-before(concat(
substring-before(concat(#name, ':'), ':'),
'.'), '.')
"
/>
<xsl:template match="i">
<xsl:copy>
<xsl:apply-templates mode="group" select="interface" />
</xsl:copy>
</xsl:template>
<xsl:template match="interface" mode="group">
<xsl:variable name="name" select="
substring-before(concat(
substring-before(concat(#name, ':'), ':'),
'.'), '.')
" />
<xsl:variable name="interfaces" select="key('kInterfaceByName', $name)" />
<!-- Muenchian step -->
<xsl:if test="generate-id()=generate-id($interfaces[1])">
<interface name="{$name}">
<xsl:apply-templates mode="unit" select="$interfaces" />
</interface>
</xsl:if>
</xsl:template>
<xsl:template match="interface" mode="unit">
<unit name="{#name}" />
</xsl:template>
</xsl:stylesheet>
when applied to
<i>
<interface name="Serial1/0"/>
<interface name="Serial2/0.0"/>
<interface name="Serial2/0.1"/>
<interface name="Serial3/0:0"/>
<interface name="Serial3/0:1"/>
</i>
results in
<i>
<interface name="Serial1/0">
<unit name="Serial1/0" />
</interface>
<interface name="Serial2/0">
<unit name="Serial2/0.0" />
<unit name="Serial2/0.1" />
</interface>
<interface name="Serial3/0">
<unit name="Serial3/0:0" />
<unit name="Serial3/0:1" />
</interface>
</i>
The XPath expression replaces your getPhysicalInterfaceName template.
It does, on the examples of 'Serial2/0.0' and 'Serial3/0:1':
append a ':' (=> 'Serial2/0.0:'; 'Serial3/0:1:')
take everything before the first ':' (=> 'Serial2/0.0'; 'Serial3/0')
append a '.' (=> 'Serial2/0.0.'; 'Serial3/0.')
take everything before the first '.' (=> 'Serial2/0'; 'Serial3/0')
EDIT: Simplified XPath expression. My first try worked but was more complex:
concat(
substring-before(#name, '/'),
'/',
substring-before(
concat(
translate(substring-after(#name, '/'), '.', ':'), ':'
),
':'
)
)
On the plus side, the above expression correctly handles colons and dots in the first part of the name, e.g. 'Some.Serial3/0:1'. The shorter one does not. If you expect dots in the name, use the longer expression. An explanation of it is in the revision history of this post.
You should to take a look into Muenchian method to group XML items.