Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring boot jackson java.time.Duration

I have create a restful web service and i try to send post request the above class

public class A {
    Duration availTime;
}

public class MyRest {
    @RequestMapping(value = "/test", method = RequestMethod.POST)
    public Response<?> test(@RequestBody A ta) {
        return new ResponseEntity<>(HttpStatus.OK);`
    }
}

raw request body

{
  "availTime": {
    "seconds": 5400,
    "nano": 0,
    "units": [
      "SECONDS",
      "NANOS"
    ]
  }
}

expected json result

 {"availTime": "PT1H30M"}

and i got the above error : Failed to read HTTP message:

org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Unexpected token (START_OBJECT),
expected one of [VALUE_STRING, VALUE_NUMBER_INT, VALUE_NUMBER_FLOAT] for java.time.Duration value; nested exception is
com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (START_OBJECT), expected one of [VALUE_STRING, VALUE_NUMBER_INT, VALUE_NUMBER_FLOAT] for java.time.Duration value
like image 887
Chris Antonopoulos Avatar asked Dec 17 '22 23:12

Chris Antonopoulos


1 Answers

As exception says java.time.Duration can be created only from int, float or string but you are providing a Json object.

Define your Duration class as

public class Duration {

    private long seconds;
    private long nano;
    private List<String> units;

    // setters / getters
}

Now your request will map from class A

Edit 1
In case you still want to map to java.time.Duration, then you need to change your request body.
Case 1 (int)

  • Request Body

    { "availTime": 5400 }

  • Output

    {"availTime":"PT1H30M"}

Case 2 (float)

  • Request Body

    { "availTime": 5400.00 }

  • Output

    {"availTime":"PT1H30M"}

Case 3 (string)

  • Request Body

    { "availTime": "PT5400S" }

  • Output

    {"availTime":"PT1H30M"}

For all above three cases to work add dependency

<dependency>
  <groupId>com.fasterxml.jackson.datatype</groupId>
  <artifactId>jackson-datatype-jsr310</artifactId>
  <version>2.8.8</version>
</dependency>

Class A should be modified as

public class A {

    private Duration availTime;

    // for json output of duration as string 
    @JsonFormat(shape = Shape.STRING)
    public Duration getAvailTime() {
        return availTime;
    }

    public void setAvailTime(Duration availTime) {
        this.availTime = availTime;
    }
}

To Print request body as json

ObjectMapper mapper = new ObjectMapper();
mapper.findAndRegisterModules();
System.out.println(mapper.writeValueAsString(ta));

If you still want to map your existing request body, then you need to write your custom deserializer for Duration class.

Hope it helps.

like image 119
Pratapi Hemant Patel Avatar answered Jan 01 '23 09:01

Pratapi Hemant Patel