Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

@JsonFormat for date parsing in JAX-RS service is ignored

I'm trying to pass a date to a JAX-RS service. Checking other questions like: Date format Mapping to JSON Jackson

The answers and the documentation show that there is a jackson annotation which should allow date formatting.

public class SolutionFilter {
    @MatrixParam("toDate")
    @JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd", timezone="CET")
    private Date toDate;

    public void setToDate(Date toDate) {
        this.toDate = toDate;
    }
}

Upon calling the Rest-Service I'm getting a ParseException:

Caused by: java.text.ParseException: Unparseable date: "2016-01-01"
  at java.text.DateFormat.parse(DateFormat.java:366)
  at org.glassfish.jersey.message.internal.HttpDateFormat.readDate(HttpDateFormat.java:137)
  at org.glassfish.jersey.server.internal.inject.ParamConverters$DateProvider$1.fromString(ParamConverters.java:259)

It seems like the annotation is ignored. Debugging the parse method the pattern is set to EEE, dd MMM yyyy HH:mm:ss zzz and EEE MMM d HH:mm:ss yyyy.

I'm using Spring 4.2.1, Jersey 2.22 which binds jackson 2.5.4.

How can I get the dates parsed with the correct pattern?

Update: Thinking further about it the JSON is only used for output parsing. But this is probably about JAX-RS parameter parsing.

like image 254
Udo Held Avatar asked Sep 30 '15 10:09

Udo Held


People also ask

What is@ JsonFormat annotation?

@JsonFormat is a Jackson annotation that we use to specify how to format fields and/or properties for JSON output. Specifically, this annotation allows us to specify how to format Date and Calendar values according to a SimpleDateFormat format.

How does Jackson deserialize dates from JSON?

How to deserialize Date from JSON using Jackson. In order to correct deserialize a Date field, you need to do two things: 1) Create a custom deserializer by extending StdDeserializer<T> class and override its deserialize(JsonParser jsonparser, DeserializationContext context) method.

How does JSON format handle dates?

JSON does not have a built-in type for date/time values. The general consensus is to store the date/time value as a string in ISO 8601 format.

How do I change the date format in ObjectMapper?

We can format a date using the setDateFormat() of ObjectMapper class. This method can be used for configuring the default DateFormat when serializing time values as Strings and deserializing from JSON Strings.


1 Answers

Param conversion is done with ParamConverters. If you follow the stacktrace, you will see the ParamConverters$DateProvider. The next call is the HttpDateFormat class which does the parsing.

If you look at the top of the class, you will see the date formats supported. These are standard HTTP data formats

/**
 * The date format pattern for RFC 1123.
 */
private static final String RFC1123_DATE_FORMAT_PATTERN = "EEE, dd MMM yyyy HH:mm:ss zzz";
/**
 * The date format pattern for RFC 1036.
 */
private static final String RFC1036_DATE_FORMAT_PATTERN = "EEEE, dd-MMM-yy HH:mm:ss zzz";
/**
 * The date format pattern for ANSI C asctime().
 */
private static final String ANSI_C_ASCTIME_DATE_FORMAT_PATTERN = "EEE MMM d HH:mm:ss yyyy";

As far as I know or can tell, there is no configuration available where we can add to this list. The only other option is to write your own converter. For example

@Provider
public class DateParamConverterProvider implements ParamConverterProvider {

    private final String format;

    public DateParamConverterProvider(String dateFormat) {
        this.format = dateFormat;
    }

    @Override
    public <T> ParamConverter<T> getConverter(Class<T> rawType, 
                                              Type genericType, 
                                              Annotation[] annotations) {

        if (rawType != Date.class) { return null; }

        return (ParamConverter<T>) new ParamConverter<Date>() {

            @Override
            public Date fromString(String value) {
                SimpleDateFormat formatter = new SimpleDateFormat(format);
                try {
                    return formatter.parse(value);
                } catch (Exception ex) {
                    throw new WebApplicationException("Bad formatted date", 400);
                }
            }

            @Override
            public String toString(Date date) { 
                return new SimpleDateFormat(format).format(date); 
            }
        };
    }
}

Here is a complete test case using Jersey Test Framework

public class DateParamTest extends JerseyTest {

    private static final String FORMAT = "MM-dd-yyyy";

    @Path("date")
    public static class DateResource {
        @GET
        public String get(@MatrixParam("since") Date date) {
            return new SimpleDateFormat(FORMAT).format(date);
        }
    }

    @Override
    public ResourceConfig configure() {
        return new ResourceConfig(DateResource.class)
                .register(new DateParamConverterProvider(FORMAT));
    }

    @Test
    public void should_return_same_date_and_format() {
        final String date = "09-30-2015";
        Response response = target("date").matrixParam("since", date)
                .request().get();
        assertEquals(200, response.getStatus());
        String returnDate = response.readEntity(String.class);
        assertEquals(date, returnDate);
        System.out.println(returnDate);
    }
}

Here is the dependency for the test framework

<dependency>
    <groupId>org.glassfish.jersey.test-framework.providers</groupId>
    <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
    <version>${jersey2.version}</version>
    <scope>test</scope>
</dependency>

See Also:

  • Read another parameter within jersey's ParamConverter for another example.
  • How to in-memory unit test Spring-Jersey for an example of using the test framework with a Spring-Jersey app.
like image 66
Paul Samsotha Avatar answered Oct 20 '22 22:10

Paul Samsotha