0

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

EricBDev
  • 1,279
  • 13
  • 21
  • If you are using XSLT 2.0, you can declare a default namespace - see: https://stackoverflow.com/a/69432328/3016153 – michael.hor257k Oct 17 '22 at 16:16
  • If you want to use XSLT 1.0, it is indeed more complicated, though if you are adding full day/s, it can be a bit simpler than the link you added - see: https://stackoverflow.com/questions/23566734/date-operations-on-xsl-1-0/23569278#23569278. However, thee is no native way to get the current date in XLT 1.0. And you will still have have the same problem with the namespace. – michael.hor257k Oct 17 '22 at 16:56
  • thanks a lot for your answers. I will look at this alternative impl with xslt 1.0. The main drawback of xslt 2.0 is the lack of support by some processor. Using saxon with Java is quite an heavy solution. xsltproc does seems to be 1.0 only... – EricBDev Oct 17 '22 at 17:03
  • `xsltproc` (i.e. `libxslt`) is indeed XSLT 1.0 only. But it supports quite a few extension functions - so you can not only get the current date, but also add a duration to it quite easily. – michael.hor257k Oct 17 '22 at 17:17
  • I eventually found a much easier solution with xsltproc which avoid me to use jre and saxon: xsltproc --stringparam current-date $(date +%Y-%m-%dT%H:%MZ) --stringparam end-date $(date -d "+1 days" +%Y-%m-%dT%H:%MZ) -o ./output.xml ./transform.xslt ./mock-src.xml – EricBDev Oct 17 '22 at 18:11
  • Could you please remove the duplicate banner since I shifted my question a bit and offered a full solution? If the question get re-openned, I will move my solution to a proper answer. – EricBDev Oct 17 '22 at 18:22
  • Currently your question is confusing because it deals with two completely unrelated issues. If you edit it to deal **only** with the issue of adding a date to current date in XSLT 1.0, I will restore it. Do note that your solution has nothing to do with XSLT (any version). It is purely a matter of sending two parameters, calculated in the calling application, and passing them to the input. So in terms of XSLT, this is an entirely trivial task. And I don't think your stylesheet will work, because the parameters are not declared. – michael.hor257k Oct 17 '22 at 18:35
  • thanks to your tips, both sytlesheet (1.0 and 2.0 variants) are now working on my test system, Indeed. the 1.0 variant is not using much from xslt. – EricBDev Oct 17 '22 at 21:23

2 Answers2

1

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>
michael.hor257k
  • 113,275
  • 6
  • 33
  • 51
0

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
EricBDev
  • 1,279
  • 13
  • 21
  • I am surprised that this works as posted. With a conforming processor, you should get an error saying that the variable 'dateNow' has not been declared. Apparently this is another corner that the `libxslt` processor cuts. --- In any case, as stated in the comment to your question, this solution has nothing to do with XSLT. – michael.hor257k Oct 18 '22 at 01:08