Creating a JSON from xml with date and time logic

2019-08-26 11:16发布

问题:

I'm trying to create a JSON from a XML which has messages and each message has its date/time.

Below is the XML

  <message>
     <messageText heading="Temporary Maintenance Message 1">test message1</messageText>

     <displayScheduleContainer>
        <startDate>22/05/2019</startDate>
        <startTimeHrs>12</startTimeHrs>
        <startTimeMins>45</startTimeMins>
        <noEndDate>true</noEndDate>
     </displayScheduleContainer>
  </message>

   <message>
     <messageText heading="Temporary Maintenance Message 1">test message2</messageText>

     <displayScheduleContainer>
        <startDate>22/06/2019</startDate>
        <startTimeHrs>12</startTimeHrs>
        <startTimeMins>45</startTimeMins>
        <noEndDate>true</noEndDate>
     </displayScheduleContainer>
  </message>

The logic inside XSLT reads the date and time to activate the message

   <xsl:for-each select="xalan:nodeset($messageData)/activeMessage/message">

    <xsl:variable name="variableN">
        <xsl:call-template name="jsonMsg" />
    </xsl:variable>


    <xsl:choose>
        <xsl:when test="$variableN = 'true'">
            <xsl:copy-of select="messageText/text()" />
            <xsl:if test="position() &lt; last()">,</xsl:if>
        </xsl:when>

    </xsl:choose>

</xsl:for-each>

<xsl:template name="jsonMsg">
    <xsl:choose>
        <xsl:when test="displayScheduleContainer/noEndDate = 'true'">
            <xsl:variable name="messageInDateTime">
                <xsl:call-template name="noEndDateTemplate">
                    <xsl:with-param name="startDateTime"
                        select="concat(displayScheduleContainer/startDate, ' ', displayScheduleContainer/startTimeHrs, ':', displayScheduleContainer/startTimeMins)" />

                </xsl:call-template>
            </xsl:variable>
            <xsl:value-of select="$messageInDateTime" />
        </xsl:when>

    </xsl:choose>
</xsl:template>

<xsl:template name="noEndDateTemplate">
    <xsl:param name="startDateTime" />

    <xsl:variable name="sdf"
        select="java:text.SimpleDateFormat.new('dd/MM/yyyy hh:mm')" />
    <xsl:variable name="currentDateTime" select="java:util.Date.new()" />
    <xsl:choose>
        <xsl:when
            test="java:compareTo(java:parse($sdf, $startDateTime), $currentDateTime) &lt; 0">
            <xsl:text>true</xsl:text>
        </xsl:when>
        <xsl:otherwise>
            <xsl:text>false</xsl:text>
        </xsl:otherwise>
    </xsl:choose>


</xsl:template>

The problem i'm facing here is if the last value is false, I end up getting the comma at the end. As i'm checking for the last position and adding the comma. Due to this the whole JSON is broken. In this case it adds the comma because i'm displaying the text only if it is true.

 "message": ["test message1", ]  

I'm using XSLT 1.0

回答1:

Instead of xsl:choose, append a predicate to your select expression. Here's a simplified example:

XML

<messages>
    <message>
        <messageText>test message1</messageText>
        <displayScheduleContainer>
            <noEndDate>true</noEndDate>
        </displayScheduleContainer>
    </message>
    <message>
        <messageText>test message2</messageText>
        <displayScheduleContainer>
            <noEndDate>true</noEndDate>
        </displayScheduleContainer>
    </message>
    <message>
        <messageText>test message3</messageText>
        <displayScheduleContainer>
            <noEndDate>false</noEndDate>
        </displayScheduleContainer>
    </message>
</messages>

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8"/>

<xsl:template match="/messages">
    <xsl:for-each select="message[displayScheduleContainer/noEndDate = 'true']">
        <xsl:value-of select="messageText" />
        <xsl:if test="position() &lt; last()">,</xsl:if>
    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

Result

"test message1,test message2"

Added:

If the test is too complex to fit in a predicate, do the transformation in two passes. Here, again, a simplified example:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="text" encoding="UTF-8"/>

<xsl:template match="/messages">
    <!-- first pass -->
    <xsl:variable name="eligible-messages">
        <xsl:for-each select="message">
            <xsl:if test="displayScheduleContainer/noEndDate = 'true'">
                <xsl:copy-of select="."/>
            </xsl:if>
        </xsl:for-each>
    </xsl:variable>
    <!-- output -->
    <xsl:for-each select="exsl:node-set($eligible-messages)/message">
        <xsl:value-of select="messageText" />
        <xsl:if test="position() &lt; last()">,</xsl:if>
    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

Replace the test in:

<xsl:if test="displayScheduleContainer/noEndDate = 'true'">

with the test/s you want to perform.