Spring Joda Jackson: DateTimeParser vs LocalDateDeserializer - spring-mvc

I'm trying to make Spring parse strings like "2013-11-11" to LocalDate.
I do:
import org.codehaus.jackson.map.annotate.JsonDeserialize;
import org.codehaus.jackson.map.ext.JodaDeserializers.LocalDateDeserializer;
import org.joda.time.LocalDate;
#Controller
public class MyController {
public static class Params {
private LocalDate myDate;
#JsonDeserialize(using=LocalDateDeserializer.class)
public void setMyDate(#JsonDeserialize(using=LocalDateDeserializer.class) LocalDate myDate) {
this.myDate = myDate;
}
}
#RequestMapping(value="/foo", method=RequestMethod.GET)
public void foo(Params params) {
// do foo
}
}
But when I issue request like "GET /foo?myDate=2013-11-11 HTTP/1.1", it tries to parse date using DateTimeFormatter, not LocalDateDeserializer, and so expects different format (I believe it's DateFormat.SHORT). Exception is java.lang.IllegalArgumentException: Invalid format: "2013-12-11" is malformed at "13-12-11"
How to make it respect LocalDateDeserializer?
BTW, Spring registers the DateTimeFormatter in org.springframework.format.datetime.joda.JodaTimeFormatterRegistrar, but don't see how to configure that.
Spring 3.2.5, Joda 2.3, Jackson 1.9.4

There's no reason for Spring to try and convert your request parameter with a JSON deserializer.
Instead specify that your field should be converted with #DateTimeFormat with whatever pattern you need
public static class Params {
private LocalDate myDate;
#DateTimeFormat(pattern = "yyyy-MM-dd")
public void setMyDate(LocalDate myDate) {
this.myDate = myDate;
}
}
This annotation
Can be applied to java.util.Date, java.util.Calendar, java.long.Long,
Joda-Time value types; and as of Spring 4 and JDK 8, to JSR-310
java.time types too.

Related

Unsupported Media type 415 #RequestBody Not working But #RequestParam is working

I have a Model class and a controller. I am posting json type data in the body of post man. But each time i'm getting an unsupported media type 415 error.
Here is My Model class :
public class Model1 {
private String name;
private String password;
public Model1() {
System.out.println("In the Model");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
#Override
public String toString() {
return "Model1 [name=" + name + ", password=" + password + "]";
}
}
And My Controller is :
#Controller
#ResponseBody
public class EcomController {
#RequestMapping(value="/getLogin", method=RequestMethod.POST,
consumes=MediaType.APPLICATION_JSON_VALUE)
public String getLoginStatus(#RequestBody Model1 name){
return "successfully called Post"+name.toString();
}
}
I have used HttpServletRequest in the place of #RequestBody and it worked. But Why its not woking when I am using #RequestBody?
This is the postman snapshot.
Here is the image of request from postman
{
"name": "anshsh",
"password": "vbvfjh"
}
This is the Screen shot of headers used in the request
I found the mistake in my configuration file.
adding <mvc:annotation-driven /> solved the problem.
Explanation :
My program was running well but problem was on Mapping the String (Json) into java objects. So there was some issue in default Mapping classes. Although, jackson was present in class-path, It wasn't working.
<mvc:annotation-driven />
this tag would register the HandlerMapping and HandlerAdapter required to dispatch requests to your #Controllers. In addition, it also applies some defaults based on what is present in your classpath. Such as: Support for reading and writing JSON, if Jackson is on the classpath.
There are two areas to check for such issues.
Add Content-type as application/json in the request (which you
have already done)
Make sure you have jackson (core and data-bind)
libraries in the classpath
The point #2 is to make sure that the application is able to convert between JSON and Java types.
To narrow down the issue further, check the logs and try GET API and see if the Java object is converted to JSON string.
Refer this link for complete working code.
You don't need to put #ResponseBody there
also some example would be
#RestController
public class EcomController {
#PostMapping("/getLogin")
public String getLoginStatus(#RequestBody Model1 name){
return "successfully called Post"+name.toString();
}
}
If you keep getting error, I think the problem is the servlet didn't find your controller registered.. Can you give more information in detail like the log when you try to compile?

Spring MVC date format output using MappingJackson2HttpMessageConverter

I have a date DTO:
public class SampleDTO{
Date date;
//setter, getter, constructor
}
In Spring MVC, I make it in ModelAttribute and sent:
#ModelAttribute("sample")
public SampleDTO getSample() {
return new SampleDTO(new Date());
}
However, In web page, it shows in following date format:
Thu Aug 31 00:00:00 CEST 2017
Anyone know how to change the date format?
PS: No any change in front-end, no use JSTL, no use tag.
I only want to make some change in MappingJackson2HttpMessageConverter
You can config configure message converters in your configuration file:
#Configuration
#EnableWebMvc
public class WebConfiguration extends WebMvcConfigurerAdapter {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.indentOutput(true).dateFormat(new SimpleDateFormat("yyyy-MM-dd"));
converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
converters.add(new MappingJackson2XmlHttpMessageConverter(builder.createXmlMapper(true).build()));
}
}

SpringWeb Jmustache and #DateTimeFormat

I have a spring boot application using server-side Mustache-Templates (JMustache).
A simple Bean with an #DateTimeFormat-Annotation:
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
public class GeneralInformation {
private Date serverTime = new Date();
#DateTimeFormat(pattern="dd.MM.yyyy")
public Date getServerTime() {
return serverTime;
}
public void setServerTime(Date serverTime) {
this.serverTime = serverTime;
}
}
A simple controller adding the bean to the model:
#Controller
#RequestMapping(value="/")
public class RootController {
// some Autowiring stuff here...
#RequestMapping(value="")
public String index(Model model){
model.addAttribute("generalInformation", new GeneralInformation());
return "hello";
}
}
And my Server-Side Mustache-template stored under templates/hello.html
<p>Servertime: {{generalInformation.serverTime}}</p>
When using JSP's the output of the date is formatted after the pattern used in the #DateTimeFormat-Annotation but not when using my Mustache-Template.
I could format the date in the #Controller-Annotated-Method and store it as a String in the Bean, but that doesn't seem to be a good way.
Does anybody know, if it is possible to make JMustache aware of the Validation-Tags?
How else could I achieve Formatting when using JMustache together with SpringMVC?
#DateTimeFormat only works with JSP

Spring MVC - PropertyEditor not called during ModelAttribute type conversion

Using Spring 3.2.3, I'm trying to implement a simple CRUD controller that handles REST-ful URLs. It relies on a PropertyEditor to convert a path variable to a BusinessService entity by loading it from an application service. Code is as follows:
#Controller
public class BusinessServiceController {
#Autowired
private BusinessServiceService businessSvcService;
public BusinessServiceController() {
}
#InitBinder
public void initBinder(final WebDataBinder binder) {
binder.registerCustomEditor(BusinessService.class, new BusinessServicePropertyEditor(businessSvcService));
}
#RequestMapping(value = "/ui/account/business-services/{businessSvc}", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public ModelAndView update(#ModelAttribute("businessSvc") #Valid final BusinessService businessSvc, final BindingResult result,
final RedirectAttributes redirectAttribs) throws UnknownBusinessServiceException {
ModelAndView mav;
if (result.hasErrors()) {
mav = new ModelAndView("/business-service/edit");
}
else {
businessSvcService.updateBusinessService(XSecurity.principal().getId(), businessSvc);
mav = new ModelAndView("redirect:/ui/account/business-services");
redirectAttribs.addFlashAttribute("message", Message.info("businessService.updated", businessSvc.getTitle()));
}
return mav;
}
}
public class BusinessServicePropertyEditor extends PropertyEditorSupport {
private final BusinessServiceService businessSvcService;
public BusinessServicePropertyEditor(final BusinessServiceService businessSvcService) {
this.businessSvcService = businessSvcService;
}
#Override
public String getAsText() {
final BusinessService svc = (BusinessService) getValue();
return Long.toString(svc.getId());
}
#Override
public void setAsText(final String text) {
final BusinessService svc = businessSvcService.getBusinessService(Long.parseLong(text));
setValue(svc);
}
}
According to SPR-7608, starting from Spring 3.2, #ModelAttribute method argument resolution checks if a path variable by the same name exists (it does here), in which case it tries to convert that path variable's value to the target parameter type through registered Converters and PropertyEditors. This is not what I'm experiencing. When I inspect what ServletModelAttributeMethodProcessor does, it clearly uses the request DataBinder's ConversionService to perform type conversion, which does not consider registered PropertyEditors, and hence BusinessServicePropertyEditor#setAsText is never called.
Is this a configuration problem or an actual bug?
Thanks for your help!
Spring's ConversionService and Converters are replacement for standard Java Beans PropertyEditors.
You need to implement Converter instead of PropertyEditor if this feature is based purely on conversion service.
To register your custom converters in WebDataBinder you might use ConfigurableWebBindingInitializer or #InitBinder method.

Spring MVC jackson auto serialize?

I would like to serialize an object with jackson in spring MVC.
I have a controller which returns an ObjectTest1 which has a property ObjectTest2.
public class ObjectTest1{
private ObjectTest2;
// setters getters...
}
public class ObjectTest2{
private String value;
// setters getters...
}
public #ResponseBody ObjectTest1 test() throws IOException ...
I have a mapper and I have a serializer for ObjectTest2 and I've annotated the ObjectTest1.getObjectTest2 method with #JsonSerialize(using = ObjectTest2.class).
It works correctly!
But I want to use this serializer in a lot of Object, not just in ObjectTest1.
What should I do to avoid put annotation every getter method? Can use spring this serializer automatically for all properites which is ObjectTest2?
UPDATED:
I've already use this in my code:
<mvc:annotation-driven>
In ajax response Objects generated correctly as json.
Maybe I should try to explain another way.
So.
I have these objects:
public class DTO{
private InnerThing innerThing;
#JsonSerialize(using=ThingSerializer.class)
public InnerThing getThing(){...}
}
public class InnerThing{
private String value;
}
Generated json looks like:
{"innerThing":{"value":"something"}}
Afther when I've written a serializer, json is:
{"innerThing":"something"}
It is OK, but to get the second version of json I must annotate the getInnerThing method in DTO class with #JsonSerialize...
I don't want to annotate all methods where I use InnerThing as a property.
So my question is, can spring auto serialize every property which type is InnerThing?
By default, Spring will handle serialization and de-serialization of JSON automatically if you add Jackson to the classpath and you use either <mvc:annotation-driven> or #EnableWebMvc.
Links to the Spring Reference Docs:
Spring 3.0: <mvc:annotation-driven>
Spring 3.1: <mvc:annotation-driven> and #EnableWebMvc
You want Jackson to always use your custom JsonSerializer or JsonDeserializer to serialize/deserialize a specific type?
I ended up writing a custom Jackson module to let Jackson find serializers and deserializers that are Spring beans.
I am using Spring 3.1.2 and Jackson 2.0.6
Simplified version:
public class MyObjectMapper extends ObjectMapper {
#Autowired
public MyObjectMapper(ApplicationContext applicationContext) {
SpringComponentModule sm = new SpringComponentModule(applicationContext);
registerModule(sm);
}
}
Module:
public class SpringComponentModule extends Module {
private ApplicationContext applicationContext;
public SpringComponentModule(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
#Override public String getModuleName() {
return "jackson-spring-component";
}
#Override public Version version() {
return SpringComponentModuleVersion.instance.version();
}
#Override
public void setupModule(SetupContext context) {
context.addSerializers(new SpringComponentSerializers(this.applicationContext));
context.addDeserializers(new SpringComponentDeserializers(this.applicationContext));
}
}
ComponentSerializer class:
public class SpringComponentSerializers extends Serializers.Base {
private ApplicationContext applicationContext;
public SpringComponentSerializers(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
#Override
public JsonSerializer<?> findSerializer(SerializationConfig config, JavaType type, BeanDescription beanDesc) {
Class<?> raw = type.getRawClass();
Map<String,JsonSerializer> beanSet = applicationContext.getBeansOfType(JsonSerializer.class);
for(String beanName : beanSet.keySet()) {
JsonSerializer<?> serializer = beanSet.get(beanName);
if(serializer.handledType().isAssignableFrom(raw)) {
return serializer;
}
}
return null;
}
}

Resources