9

I am hoping to get some help debugging this problem. If I send the following JSON to my backend it works correctly:

{
    "approvalRequired": false,
    "location": {
        "locationName": "<+37.33233141,-122.03121860> +\/- 5.00m (speed 0.00 mps \/ course -1.00) @ 9\/16\/18, 9:24:59 PM Pacific Daylight Time",
        "longitude": -122.0312186,
        "latitude": 37.332331410000002
    }
}

However, if I now send the following:

{
    "approvalRequired": false,
    "scheduledStartTime": "2016-01-01T10:24:00+01:00",
    "location": {
        "locationName": "<+37.33233141,-122.03121860> +\/- 5.00m (speed 0.00 mps \/ course -1.00) @ 9\/16\/18, 9:24:59 PM Pacific Daylight Time",
        "longitude": -122.0312186,
        "latitude": 37.332331410000002
    }
}

I get the above error. In my backend code I have the following:

@DynamoDBTypeConverted(converter = ZonedDateTimeTypeConverter.class)
@DynamoDBAttribute(attributeName = "scheduledStartTime")
public ZonedDateTime scheduledStartTime;

And the API method signature looks like this:

@RequestMapping(method = RequestMethod.POST)
public ResponseEntity create(@RequestBody Event event) {...}

I believe the problem I am having is that the JSON cannot be parsed to ZonedDateTime. Does anyone have advice as to either, (1) what time of json string format ZonedDateTime automatically accepts or (2) how to make a DTO to parse zoned date time?

Thanks!

Matthew Read
  • 1,365
  • 1
  • 30
  • 50
AlexK
  • 336
  • 8
  • 21
  • 41
  • Please give more information about the different technologies used – fgamess Sep 17 '18 at 05:18
  • Sure. What specifically are you looking for? Assume Spring MVC. I am more interested in understanding how to debug this that just obtaining the answer.. – AlexK Sep 17 '18 at 22:50
  • It is not clear how your `ZonedDateTimeTypeConverter` is implemented (please show enough info), but generally `ZonedDateTime` does not accept something like `558851094.57158995`. Try to send a string like `"2007-12-03T10:15:30+01:00[Europe/Paris]"` as in the [doc of `ZonedDateTime`](https://docs.oracle.com/javase/8/docs/api/java/time/ZonedDateTime.html#parse-java.lang.CharSequence-). – OOPer Sep 18 '18 at 02:14
  • If I literally try this exact string, it does not work either: { "approvalRequired": false, "scheduledStartTime": "2007-12-03T10:15:30+01:00[Europe\/Paris]", "location": { "locationName": "<+37.33233141,-122.03121860> +\/- 5.00m (speed 0.00 mps \/ course -1.00) @ 9\/19\/18, 12:16:05 PM Pacific Daylight Time", "longitude": -122.0312186, "latitude": 37.332331410000002 } } – AlexK Sep 19 '18 at 19:17
  • 1
    @AlexKornhauser , which parser do you use, could you please provide _Spring_ configuration part around this? And if you don't mind, please let us know also version of the _Spring_ used on your end. – marme1ad Sep 23 '18 at 02:38
  • What is the "above error"? The content of ZonedDateTimeTypeConverter.java? – Mumrah81 Sep 24 '18 at 11:54
  • Try 2015-09-22T19:58:22.947Z – Amit Parashar Sep 24 '18 at 18:35
  • 1
    This is not a Swift question, this is a Java one. – Cristik Sep 26 '18 at 05:06

2 Answers2

3

Solution:

Assume you are using the most "default" configuration, which is based on FasterXML Jackson.

If so, then you just need to configure properly serializer and desirializer for ZonedDateTime in your application; and it might be either custom ones or the ones from jackson-datatype-jsr310 (recommended).


I've created a small/minimal example, which is based on the Spring 5.0.9 and Jackson 2.9.6 (latest versions currently).

Please find it here: spring5-rest-zoneddatetime >>, main parts are:

  1. Event DTO:

    public class Event {
    
        private long id;
        private String name;
        private ZonedDateTime time;
    
        // Constructors, public getters and setters
    
    }
    

    Field time might be a public one same to your sample, it is also fine, but if field is private - then you will need public getter and setter.

    NOTE: I'm ignoring here @DynamoDBTypeConverted and @DynamoDBAttribute annotations since they are related to persistence logic, not the REST layer.

  2. EventController contains only one method same to yours:

    @RestController
    public class EventController {
    
        @RequestMapping(value = "/event", method = RequestMethod.POST)
        public ResponseEntity post(@RequestBody Event event) {
            System.out.println("Event posted: " + event.toString());
            return ResponseEntity.ok(event);
        }
    
    }
    
  3. Dependencies in the pom.xml looks so:

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.0.9.RELEASE</version>
    </dependency>
    
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.9.6</version>
    </dependency>
    
    <dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-jsr310</artifactId>
        <version>2.9.6</version>
    </dependency>
    

    The important one here is JSR-310 datatype implementation, which also introduces com.fasterxml.jackson.datatype.jsr310.ser.ZonedDateTimeSerializer and com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer.


Additional information:

  1. In case of custom serializer/desirializer will be needed, please check this question >>

  2. Next date formats will be supported for the time field:

    • "2018-01-01T22:25:15+01:00[Europe/Paris]" - not fully an ISO 8601 btw
    • "2018-01-01T22:25:15+01:00"
    • "2018-01-01T22:25:15.000000001Z"
    • 1514768461.000000001 - float-pointing number, amount of seconds from the 1970-01-01, 00:00:00 [UTC]
  3. By default REST APi response will use float-pointing numbers for dates, e.g. in our case response will look so:

    {
        "id": 3,
        "name": "Test event",
        "time": 1514768460
    }
    

    To return string values instead, please check e.g. this question >>

  4. Also need to mention that if you will use Spring Boot instead (good starter) - all things discussed above will work out of the box.

marme1ad
  • 1,253
  • 8
  • 8
  • 1
    The answer for me was that I had to use both Jackson core and Datatypes as you had recommended, not just one. Thanks for your clear answer! – AlexK Sep 26 '18 at 20:38
0

Send in this format 2016-08-22T14:30+08:00[Asia/Kuala_Lumpur]

LocalDateTime ldt = LocalDateTime.of(2016, Month.AUGUST, 22, 14, 30);

ZonedDateTime klDateTime = ldt.atZone(ZoneId.of("Asia/Kuala_Lumpur"));
Jin Thakur
  • 2,711
  • 18
  • 15