Custom converter in ModelMapper not being fired - java

I am trying to conver a String to a Date but the converter doesn't seem to be fired and I'm getting a MappingException. This is my code:
#Bean
public ModelMapper modelMapper() {
ModelMapper modelMapper = new ModelMapper();
Converter<String, Date> dateConverter = context -> {
Date date;
try {
date = new SimpleDateFormat("dd/MM/yyyy").parse(context.getSource());
} catch (ParseException e) {
date = null;
}
System.out.println("working");
return date;
};
modelMapper.addConverter(dateConverter);
return modelMapper;
}
And then I just use it like this but I continue getting the same error:
Date date = this.modelMapper.map("20/12/2019", Date.class);
1) Converter org.modelmapper.internal.converter.DateConverter#27c7dc27 failed to convert java.lang.String to java.util.Date.
It's not even printing the working word.
What am I missing?
Thanks.

Okay, for some reason my dumb IDE (IntelliJ) adviced me to change the code above for how it is in the question. That's what's making it not work, having it as a lambda, it seems. With this code it works perfectly:
Converter<String, Date> dateConverter = new Converter<String, Date>()
{
public Date convert(MappingContext<String, Date> context)
{
Date date = null;
try {
date = new SimpleDateFormat("dd/MM/yyyy").parse(context.getSource());
} catch (ParseException e) {
System.out.println(e.toString());
}
return date;
}
};

Related

BeanUtils: populate String Date formatted map field to Date property

I've this Map<String, Object>:
Map<String, Object> map = new HashMap<String, Object>();
map.put("date", "02/11/2018#11:29:03.463+0000");
My bean is:
public class MyBean {
private Date date;
// setters & getters
}
I'm trying to populate my map to bean:
MyBean bean = new MyBean();
BeanUtilsBean.getInstance().populate(bean, map);
I'm getting this error message:
ConversionException: DateConverter does not support default String to 'Date' conversion.
IMPORTANT: I can't change string format.
How could I solve it?
You can create an instance of DateTimeConverter and pass in your custom patterns. Here is an example:
DateTimeConverter dateConverter = new DateConverter(null);
dateConverter.setPatterns(new String[] {"dd/MM/yyyy#HH:mm:ss.SSSZ"});
ConvertUtils.register(dateConverter, Date.class);
You have to register converter:
class MyDateConverter implements Converter {
private final DateFormat format = new SimpleDateFormat("dd/MM/yyyy#HH:mm:ss.SSSZ");
public Object convert(Class type, Object value) {
if(value == null) {
return null;
} else { // parse your date format with date formatter
try {
return format.parse((String) value);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
}
And use it like:
ConvertUtilsBean convertUtilsBean = new ConvertUtilsBean();
convertUtilsBean.register(new MyDateConverter(), Date.class);
BeanUtilsBean beanUtilsBean =
new BeanUtilsBean(convertUtilsBean, new PropertyUtilsBean());
MyBean bean = new MyBean();
beanUtilsBean.populate(bean, map);

Overriding XmlAdapter for 3rd party library class

I am generating xml using jaxbMarshaller for a third party library class. Since library XmlAdapter which converts Calendar object to string is not using TimeZone field, so marshaller is generating wrong xml for every Calendar field of pojo class.
3rd Party library XmlAdapter is using below class for Calendar to string conversion:
public class DateConversion {
public static String printDate(Calendar value) {
if(value != null) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
return format.format(value.getTime());
}
return null;
}
}
So I want to override the behavior of XmlAdapter for Calendar field and tried below example but seems it is not working:
my custom XmlAdapter is using below class for conversion:
public class DateConversion {
public static String printDate(Calendar value, TimeZone timeZone) {
if(value != null) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
format.setTimeZone(timeZone);
return format.format(value.getTime());
}
return null;
}
}
and then I have done registry like:
public #Nullable
String toPdxmlString(final #NotNull Deals input) {
try {
final Marshaller marshaller = jaxbContext.createMarshaller();
final DateFormatterAdapter dateFormatterAdapter = new DateFormatterAdapter(PdxmlDateTimeUtil.FXONLINE_DEFAULT_DEAL_TIMEZONE);
marshaller.setAdapter(dateFormatterAdapter);
StringWriter writer = new StringWriter();
marshaller.marshal(input, writer);
return writer.toString();
} catch (JAXBException exception) {
LOGGER.error("Unable to marshall the given input Deals: {}, into String using JAXB Context: {}, ... ", input, jaxbContext, exception);
}
return null;
}
Can anyone help me to know if this is doable or not, if yes where I am going wrong?
So I found my solution. I extended XmlAdapter of 3rd party library and pluged in TimeZone field in DateConversion like:
public class DateFormatterAdapter extends Adapter2 {
private final TimeZone timeZone;
public DateFormatterAdapter(final TimeZone timeZone) {
this.timeZone = timeZone;
}
#Override
public Calendar unmarshal(String value) {
return javax.xml.bind.DatatypeConverter.parseDate(value);
}
#Override
public String marshal(Calendar calendar) {
return DateConversion.printDate(calendar, timeZone);
}
}
At last registered the extended XmlAdapter as:
public #Nullable
String toPdxmlString(final #NotNull Deals input) {
try {
final Marshaller marshaller = jaxbContext.createMarshaller();
final DateFormatterAdapter dateFormatterAdapter = new DateFormatterAdapter(PdxmlDateTimeUtil.FXONLINE_DEFAULT_DEAL_TIMEZONE);
marshaller.setAdapter(Adapter2.class, dateFormatterAdapter);
StringWriter writer = new StringWriter();
marshaller.marshal(input, writer);
return writer.toString();
} catch (JAXBException exception) {
LOGGER.error("Unable to marshall the given input Deals: {}, into String using JAXB Context: {}, ... ", input, jaxbContext, exception);
}
return null;
}

Formatting and validating a date in JSF

So I want to format my dateinputfield as "dd-MM-yyyy" and then validate that the date is not before tomorrow.
This is the relevant code in my view:
<h:inputText id="dueDate" required="true" value="#{submitRepairBean.dueDate}">
<f:convertDateTime pattern="dd-MM-yyyy"/>
<f:validator validatorId="be.kdg.repaircafe.validators.DueDateValidator"/>
</h:inputText>
This is my custom validator:
#FacesValidator("be.kdg.repaircafe.validators.DueDateValidator")
public class DueDateValidator implements Validator {
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
System.out.println(value.toString()); //For some reason this prints Wed Jul 23 02:00:00 CEST 2014 when inputting 23-07-2014
DateTime date = new DateTime(value.toString());
long dueDateMillis = date.getMillis();
long oneDayMillis = 86400000;
Calendar tomorrowMidnight = new GregorianCalendar();
// reset hour, minutes, seconds and millis
tomorrowMidnight.set(Calendar.HOUR_OF_DAY, 0);
tomorrowMidnight.set(Calendar.MINUTE, 0);
tomorrowMidnight.set(Calendar.SECOND, 0);
tomorrowMidnight.set(Calendar.MILLISECOND, 0);
tomorrowMidnight.add(Calendar.DAY_OF_MONTH, 1);
if (dueDateMillis + oneDayMillis < tomorrowMidnight.getTimeInMillis()) {
throw new ValidatorException(new FacesMessage("You can not have something repaired before tomorrow!"));
}
}
Now the thing I don't understand is why it doesn't print in the converted format (dd-MM-yyyy), even then I don't care so much as long as I get the correct amount of milliseconds.
However, the DateTime constructor then throws an exception that the date is in an invalid format.
I've tried using SimpleDateFormat as well, with no luck.
The converter it will show you the date in this format on the page (in the jsp/html page). What it does, is converting the date in a string in the format dd-mm-yyyy. When you pass the calue in the validate function, it is not converted in the string in the format dd-MM-yyyy. it is a date, dueDate is a date, so by printing value.toString() is just converts the date value to a string. So the object is a date and just by casting to Date is should work. if you want in the code to print it in the format dd-MM-yyyy try this
Date date = (Date) value;
SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy");
String strDate = sdf.format(date);
System.out.println(strDate );
#FacesValidator("validator.dateValidator")
public class DateValidator implements Validator, ClientValidator {
final static String DATE_FORMAT = "dd-MM-yyyy";
public DateValidator() {
}
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
if (value == null || StringUtils.isEmpty((String) value.toString())) {
return;
}
SimpleDateFormat objDf = new SimpleDateFormat(DATE_FORMAT);
objDf.setLenient(false);
try {
try {
Date data = new Date();
data.setDate(data.getDate() + 1);
if(objDf.parse(value.toString()).before(data)){
((UIInput) component).setValid(false);
throw new ValidatorException(new FacesMessage("You can not have something repaired before tomorrow!"));
}
} catch (ParseException e) {
((UIInput) component).setValid(false);
throw new ValidatorException(new FacesMessage(MessageUtils.ALERTA, "Data informada não Válida!", ""));
}
} catch (ParseException e) {
throw new ValidatorException(new FacesMessage("Invalid Date!"));
}
}
public Map<String, Object> getMetadata() {
return null;
}
public String getValidatorId() {
return "validator.dateValidator";
}
}

why this happen in type conversion?

i am new to springMVC ,today i write a DateConverter
like this
public class DateConverter implements Converter<String,Date>{
private String formatStr = "";
public DateConverter(String fomatStr) {
this.formatStr = formatStr;
}
public Date convert(String source) {
SimpleDateFormat sdf = null;
Date date = null;
try {
sdf = new SimpleDateFormat(formatStr);
date = sdf.parse(source);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
}
then i write a controller like this
#RequestMapping(value="/converterTest")
public void testConverter(Date date){
System.out.println(date);
}
congfigure it to applicationContext,i am sure the DateConverter has been initialized correctly,when i test my converter with
http://localhost:8080/petStore/converterTest?date=2011-02-22
the browser says:
HTTP Status 400 -
type Status report
message
description The request sent by the client was syntactically incorrect ().
somebody could help me with it? thanks in advance
You have a typo in your converter. You misspelled the constructor param, so the assignment has no effect. Instead of:
public DateConverter(String fomatStr) {
this.formatStr = formatStr;
}
try:
public DateConverter(String formatStr) {
this.formatStr = formatStr;
}
There may be other issues, but at a minimum you'll want to fix that. I'm assuming you are using yyyy-MM-dd for your date format?

How to handle different date formats in Spring MVC controller?

Is it possible to handle different date format in a Spring MVC controller?
I know that setting something like this
#InitBinder
protected void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
binder.registerCustomEditor(Date.class, new CustomDateEditor(
dateFormat, false));
}
I can handle dd/MM/yyyy format, but what if i want to parse also dates in yyyyMMddhhmmss format? Should I add multiple CustomDateEditors in my controller?
If you need it only at puntual cases, you can register the custom editor attached to a field in the form:
DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy", this.getLocale(context));
DateFormat dateTimeFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss SSS", this.getLocale(context));
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateTimeFormat, true));
binder.registerCustomEditor(Date.class, "name.of.input", new CustomDateEditor(dateTimeFormat, true));
Inspired by Skipy
public class LenientDateParser extends PropertyEditorSupport {
private static final List<String> formats = new ArrayList<String>();
private String outputFormat;
static{
formats.add("dd-MM-yyyy HH:ss");
formats.add("dd/MM/yyyy HH:ss");
formats.add("dd-MM-yyyy");
formats.add("dd/MM/yyyy");
formats.add("dd MMM yyyy");
formats.add("MMM-yyyy HH:ss");
formats.add("MMM-yyyy");
formats.add("MMM yyyy");
}
public LenientDateParser(String outputFormat){
this.outputFormat = outputFormat;
}
#Override
public void setAsText(String text) throws IllegalArgumentException {
if(StringUtils.isEmpty(text))
return;
DateTime dt = null;
for(String format : formats){
try{
dt = DateTime.parse(text, DateTimeFormat.forPattern(format));
break;
}catch(Exception e){
if(log.isDebugEnabled())
log.debug(e,e);
}
}
if(dt != null)
setValue(dt.toDate());
}
#Override
public String getAsText() {
Date date = (Date) getValue();
if(date == null)
return "";
DateTimeFormatter f = DateTimeFormat.forPattern(outputFormat);
return f.print(date.getTime());
}
}
How about this. the above can go out of whack pretty soon.
public class MostLenientDateParser {
private final List<String> supportedFormats;
public MostLenientDateParser(List<String> supportedFormats) {
this.supportedFormats = supportedFormats;
}
public Date parse(String dateValue) {
for(String candidateFormat: supportedFormats) {
Date date = lenientParse(dateValue, candidateFormat);
if (date != null) {
return date;
}
}
throw new RuntimeException("tried so many formats, non matched");
}
private Date lenientParse(String dateCandidate, String dateFormat) {
try {
return new SimpleDateFormat(dateFormat).parse(dateCandidate);
} catch (Exception e) {
return null;
}
}
}
This could also be referenced through Spring Converters via a CustomDateEditor implementation for form-data binding.
For others having the same question, if you are using spring 3 You can use the awesome #DateTimeFormat(pattern="dd-MM-yyyy") in the field of your model.
Just make sure to register a conversionService with your org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
You can have as much as you want of #DateTimeFormat in the same bean.
If at a time you receive only one format of date, then you could simply create one instance of DateFormat based on format
for example
Decide the format based on the input
DateFormat df = null;
if(recievedDate.indexOf("//")!=-1){
df = new SimpleDateFormat("dd/MM/yyyy")
}else{
df = new SimpleDateFormat("yyyyMMddhhmmss")
}
Not a great idea to have lenient date formatters when dealing with multiple locales. A date like 10/11/2013 will get parsed correctly with both dd/MM/YYYY and MM/dd/YYYY

Categories