I am trying to transform one xml by changing 2 date fields.
Here is my source XML:
<?xml version="1.0" encoding="UTF-8"?>
<Publication_MarketDocument xmlns="urn:iec62325.351:tc57wg16:451-3:publicationdocument:7:0">
<period.timeInterval>
<start>2022-09-27T22:00Z</start>
<end>2022-09-29T22:00Z</end>
</period.timeInterval>
<TimeSeries>
<Period>
<timeInterval>
<start>2022-09-27T22:00Z</start>
<end>2022-09-28T22:00Z</end>
</timeInterval>
<resolution>PT60M</resolution>
<Point>
<position>1</position>
<price.amount>323.70</price.amount>
</Point>
<Point>
<position>2</position>
<price.amount>309.91</price.amount>
</Point>
</Period>
</TimeSeries>
</Publication_MarketDocument>
I want all the start node to use the current dateTime and the end one to be one day later. So I wrote the following XSLT:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:xs="http://www.w3.org/2001/XMLSchema"
xpath-default-namespace="urn:iec62325.351:tc57wg16:451-3:publicationdocument:7:0">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:variable name="dateNow" select="fn:format-dateTime(fn:current-dateTime(), '[Y]-[M]-[D]T[H]:[m]Z')"/>
<xsl:variable name="dateTomorrow" select="fn:format-dateTime(fn:current-dateTime() xs:dayTimeDuration('P1D'), '[Y]-[M]-[D]T[H]:[m]Z')"/>
<xsl:template match="start/text()">
<xsl:value-of select="$dateNow"/>
</xsl:template>
<xsl:template match="end/text()">
<xsl:value-of select="$dateTomorrow"/>
</xsl:template>
</xsl:stylesheet>
(after edit) I first got confused with namespaces, but defining the default namespace as the comment suggested allowed this transformation to run.
However, it is a xslt 2.0 and thus not supported by xslt 1.0 processor. For instance, Visual Studio 2022 offer xml debugging but still do NOT support xslt 2.0. Since my target environment is Linux based, I found out the most standard solution is to use Saxon: java net.sf.saxon.Transform -o:/mnt/d/tmp/output.xml -s:mock.xml -xsl:transform.xslt
this runs silently and finally get correct results. but using jre is quite heavy for a deployment on automated test systems. It is possible to do so with only XSLT 1.0? It seems to be quite complicated with time shift as seen here
CodePudding user response:
With xsltproc under Linux, following this question there is a much easier alternative by using the string parameters. Instead of doing the dateTime processing by xsl (which is not out of the box in XSLT 1.0), do it with the date tool under the shell! So I ended up with the following xslt
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:entsoe="urn:iec62325.351:tc57wg16:451-3:publicationdocument:7:0" exclude-result-prefixes="entsoe">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="entsoe:start/text()">
<xsl:value-of select="$dateNow"/>
</xsl:template>
<xsl:template match="entsoe:end/text()">
<xsl:value-of select="$dateTomorrow"/>
</xsl:template>
</xsl:stylesheet>
used with the following call from a sh script under Linux:
xsltproc --stringparam dateNow $(date -u %Y-%m-%dT%H:%MZ) --stringparam dateTomorrow $(date -u -d " 1 days" %Y-%m-%dT%H:%MZ) -o /mnt/d/tmp/output.xml ./transform.xslt ./mock-src.xml
CodePudding user response:
Since this question is focused specifically on xsltproc
, it is worth pointing out that the libxslt
processor used by xsltproc
supports some extension functions that can solve the problem without requiring the calling application to do all the work:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:entsoe="urn:iec62325.351:tc57wg16:451-3:publicationdocument:7:0"
xmlns:date="http://exslt.org/dates-and-times"
exclude-result-prefixes="entsoe date">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="today" select="date:date-time()" />
<xsl:variable name="tomorrow" select="date:add($today, 'P1D')" />
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="entsoe:start/text()">
<xsl:value-of select="$today" />
</xsl:template>
<xsl:template match="entsoe:end/text()">
<xsl:value-of select="$tomorrow" />
</xsl:template>
</xsl:stylesheet>