Announcement Announcement Module
Collapse
No announcement yet.
@DateTimeFormat for PathVariable not converting? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • @DateTimeFormat for PathVariable not converting?

    I have a RESTful @Controller where I'm attempting to convert an incoming string into a Date object as follows:

    Code:
    @RequestMapping(value="/order/history/{userid}/{from}/", method = RequestMethod.GET)
    @ResponseBody
    public List<Order> orderHistory(@PathVariable("userid") Integer userId,
            @PathVariable("from") @DateTimeFormat(pattern="yyyyMMdd") Date from) {
    ...
    }
    When hitting this URL as /order/history/1/20100822/ a get a conversion exception of:
    Failed to convert value of type 'java.lang.String' to required type 'java.util.Date'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [java.util.Date]: no matching editors or conversion strategy found
    It doesn't seem as though @DateTimeFormat is ever resulting in a conversion attempt.

    I'm using Spring MVC with <mvc:annotation-driven /> in my applicationContext.xml. Additionally, I have joda time in my class path.

    This seems to be straight out of the documentation, but it's just not converting. Any help would be much appreciated.

  • #2
    Hi,

    be aware of the fact that Converters and Formatters are two very different things. In this case, you are trying to CONVERT a String (a piece of URL can be nothing else but a String) in a java.util.Date; you are NOT formatting a Date in a specific format or locale. If you don't want to do this conversion yourself using SimpleDateFormat in the code of your controller, you must at least properly register a string to date Converter in your context, so that Spring can use it to automatically do this for you every time you give a String as input and expect a Date. (A big number of converters are already implemented by Spring and contained in the core.convert.support package, but none involves a Date conversion)

    Doing this is pretty straightforward:

    Code:
    public class MyStringToDateConverter implements Converter<String, Date> {
        public Date convert(String source) {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
            return sdf.parse(source);
        }
    }
    and then you register the bean:

    Code:
    <bean class="com.mycompany.mypackage.MyStringToDateConverter"/>
    it's as simple as that, redeploy and your error will be gone...

    Comment


    • #3
      Ok

      That's essentially what I ended up doing to solve my problem, however I believe I'm now unclear what the purpose of the @DateTimeFormat annotation is as an annotation of a @PathVariable controller method parameter.

      A @PathVariable will always be an incoming string; so what would @DateTimeFormat ever actually accomplish in this context? It makes the documentation with this example unclear at best, and misleading (as in this case) at worst.

      Comment


      • #4
        From the documentation:

        Use @DateTimeFormat to format
        java.util.Date, java.util.Calendar, java.util.Long, or Joda Time fields.
        It most clearly says you can't use it to convert strings to dates.

        If you are referring to page 427 of the documentation, probably the use of converter behind the scenes is unemphasized because it is not the main subject of the paragraph (showing the usage of @RequestMapping). Yet, the example won't work without the converter.

        so what would @DateTimeFormat ever actually accomplish in this context?
        well the answer is simple...you have your piece of url converted to a java.util.Date with the converter, now you want that java.util.Date formatted in a certain way to be used the way you intend to...what do you do? You use the @DateTimeFormat formatter on the date, which is exactly its purpose as clearly stated in the documentation.

        Comment


        • #5
          some docs really do contradict this

          I'm seeing evidence in several places that @DateTimeFormat should be doing conversions as well.

          http://blog.springsource.com/2009/11...nd-validation/ -- see the "Parameter Annotations" section:

          Annotation-driven overrides can also be applied to method parameters. Consider the following Controller method that fetches upcoming appointments for a specific day, where the day is a URL path variable encoded in ISO date format:
          Code:
          @RequestMapping(value = "/appointments/{day}", method = RequestMethod.GET)
          public String getAppointmentsForDay(@PathVariable @DateTimeFormat(iso=ISO.DATE) Date day) {
              ...
          }
          Sending a GET request to /appointments/2009-11-17 fetches all Appointments on November 17th, 2009. Setting the @DateTimeFormat iso attribute to ISO.DATE instructs Spring to parse the incoming date string as an ISO Date (yyyy-mm-dd).
          This clearly states that it should handle this particular conversion.

          So, perhaps I did confuse formatting and conversion, but I definitely maintain all of these REST-ful examples of path variable strings having @DateTimeFormat is extremely misleading.

          Anyway, I do thank you for your response.

          Comment


          • #6
            Yep, you're right, I didn't read that particular article from Keith Donald, and was referring only on what you can find in the "official" documentation...
            While what you read in the documentation is in no way unclear or misleading, actually that article states exactly what you said and now I myself am not sure anymore what to think.

            Since the author of that article is one of the Spring team developers, his authority on the matter is indiscussed, so I would assume that after all you CAN use the @DateTimeFormat annotation as a converter instead of formatter, but maybe this requires additional configuration.

            Other option is, since the article was written before Spring 3 was out, that this feature was considered as an addition to the framework but was finally discarded for some reason (you don't see anything about it in the official documentation after all).

            Only Keith Donald himself or another Spring developer can shed some light on the point though...

            Comment


            • #7
              Do you have Joda Time in your classpath? Joda is required for @DateTimeFormat to work at all. Applying @DateTimeFormat to drive String->Date @PathVariable conversion should work fine then.

              Comment


              • #8
                I do and it doesn't

                Hi,

                I do have Joda Time on my classpath (joda-time version 1.6.2) and I don't get conversion from String to Date and the bean property is left at null, I see the binding result: no matching editors or conversion strategy found.

                Registering a CustomDateEditor makes it work but it would be more flexible to leave the conversion strategy to the bean.

                I don't have a setup that specifies mvc:annotation-driven. I forget why but there was a reason why that didn't work out for me. So, I'm wondering if I'm missing a crucial bean registration here

                Code:
                SimpleOfferVO
                	@DateTimeFormat(pattern="dd-MM-yyyy HH:ss")
                	private Date eventDateTo,eventDateFrom;
                [..]
                
                Controller
                	@RequestMapping(method = RequestMethod.POST, value = "/post.html")
                	public String post(
                			@ModelAttribute("vo") SimpleOfferVO vo,
                			BindingResult result,
                			ModelMap model,
                			HttpServletRequest request,
                			HttpServletResponse response) throws Exception {
                [..]

                Comment


                • #9
                  conversionService

                  For the config above I had the following conversion service registered:
                  PHP Code:
                  <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean" /> 
                  changing it to
                  PHP Code:
                  <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"/> 
                  didn't make a difference

                  Comment


                  • #10
                    Solved

                    Ok, the reason it didn't work is because the conversionService wasn't registered with the AnnotationMethodHandlerAdapter

                    PHP Code:
                        <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
                            <
                    property name="webBindingInitializer">
                                <
                    bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
                                    <
                    property name="validator" ref="validator" />
                                    <
                    property name="conversionService" ref="conversionService" />
                                </
                    bean>
                            </
                    property>
                            <
                    property name="alwaysUseFullPath" value="true"/>
                            <
                    property name="messageConverters">
                                <list>
                                    <
                    bean id="stringHttpMessageConverter" class="org.springframework.http.converter.StringHttpMessageConverter"/>
                                    <
                    bean id="formHttpMessageConverter" class="org.springframework.http.converter.FormHttpMessageConverter"/>
                                    <
                    bean id="byteArrayMessageConverter" class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
                                    <
                    bean id="bufferedImageHttpMessageConverter" class="org.springframework.http.converter.BufferedImageHttpMessageConverter"/>
                                    <
                    bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>
                                </list>
                            </
                    property>
                        </
                    bean
                    now it works

                    Comment

                    Working...
                    X