I am trying to use ObjectOutputStream to save all objects in an Arraylist to file. An attribute of the object is a LocalDate and whenever I try to write to file an error NotSerializableException: java.time.format.DateTimeFormatter returns despite having no DateTimeFormatter for any of the LocalDates
Full Error:
java.io.NotSerializableException: java.time.format.DateTimeFormatter
at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1185)
at java.base/java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1553)
at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1510)
at java.base/java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1433)
at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1179)
at java.base/java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:349)
at java.base/java.util.ArrayList.writeObject(ArrayList.java:791)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at java.base/java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:1130)
at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1497)
at java.base/java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1433)
at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1179)
at java.base/java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:349)
at BikeNow.saveRent(BikeNow.java:330)
at BikeNow.main(BikeNow.java:114)
The method using Object output Stream
public void saveRent() {
//Create file and object output stream
try {
FileOutputStream fos = new FileOutputStream("tmp.dat");
ObjectOutputStream oos = new ObjectOutputStream(fos);
//Write array to file
oos.writeObject(rents);
oos.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
An Example of object trying to be saved to file
rents.add(new Rent(0001, "John Smith", true, "Roubaix Sport", LocalDate.of(2019, 03, 06), LocalDate.of(2019, 04, 05), 30, true));
The Object Class
import java.io.Serializable;
import java.time.LocalDate;
public class Rent extends Customer implements Serializable {
private LocalDate startDate;
private LocalDate endDate;
private int duration;
private boolean overdue;
public Rent(int customerID, String customerName, boolean renting, String bikeRented, LocalDate startDate, LocalDate endDate, int duration, boolean overdue) {
super(customerID, customerName, renting, bikeRented);
this.startDate = startDate;
this.endDate = endDate;
this.duration = duration;
this.overdue = overdue;
}
There's not much to go on here, but clearly you're trying to write a DateTimeFormatter when you write your object. This leads me to believe there is one defined in Customer, but since DTF doesn't implement Serializable, it explodes.
The best solution is to edit your Customer class to properly serialize. The other option is to stuff it into your Rent class, but that might not be possible if the fields are private.
Related
I am implementing an application that gets the data from certin endponts in json formats and tries to deserialize tem to Java Objects but I have problems with the parsing of the date in the JSON.This is how the Date looks like in the JSON: "/Date(1633122000000+0300)/" and I cannot find information in Google how to successfully parse this format.
{
"Date": "/Date(1633122000000+0300)/",
"Filled": 0,
"Needed": 0,
"Paid": 0
}
This is the pojo I use to deserialize the data to using Jackson ObjectMapper:
import java.util.Date;
#Data
#AllArgsConstructor
#NoArgsConstructor
public class TimeByDateSheet {
#JsonProperty("Date")
#JsonFormat(timezone = "GMT+03:00")
#JsonDeserialize(using = DateDeserializer.class, as=Date.class)
private Date date;
#JsonProperty("Filled")
private Long filled;
#JsonProperty("Needed")
private Long needed;
#JsonProperty("Paid")
private Integer paid;
}
And here is my DateDeserializer:
#SuppressWarnings("serial")
public class DateDeserializer extends JsonDeserializer<Date> {
#Override
public Date deserialize(JsonParser jsonParser, DeserializationContext context)
throws IOException, JsonProcessingException {
SimpleDateFormat simpleDateFormat =
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss zzz", Locale.getDefault());
String dateStr = jsonParser.getText();
Date date;
try{
date = simpleDateFormat.parse(dateStr);
}catch(ParseException e){
throw new RuntimeException(e);
}
return date;
}
}
But it does not work correctly. I get the following exception:
Connected to the target VM, address: '127.0.0.1:52760', transport: 'socket'
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: java.text.ParseException: Unparseable date: "/Date(1633035600000+0300)/" (through reference chain: java.util.ArrayList[0]->com.dataart.forecasts.pojo.timebydate.TimeByDateSheet["Date"])
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:392)
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:351)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.wrapAndThrow(BeanDeserializerBase.java:1821)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:315)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:176)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer._deserializeFromArray(CollectionDeserializer.java:355)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:244)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:28)
at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:322)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4675)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3630)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3613)
at com.dataart.forecasts.DataProcessor.deserializeTimeByDateSheetsList(DataProcessor.java:198)
at com.dataart.forecasts.ForecastReportApplication.main(ForecastReportApplication.java:50)
Caused by: java.lang.RuntimeException: java.text.ParseException: Unparseable date: "/Date(1633035600000+0300)/"
at com.dataart.forecasts.DateDeserializer.deserialize(DateDeserializer.java:28)
at com.dataart.forecasts.DateDeserializer.deserialize(DateDeserializer.java:16)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:313)
... 10 more
Caused by: java.text.ParseException: Unparseable date: "/Date(1633035600000+0300)/"
at java.base/java.text.DateFormat.parse(DateFormat.java:395)
at com.dataart.forecasts.DateDeserializer.deserialize(DateDeserializer.java:26)
... 13 more
Could someone help me, please. I searched a lot in internet but could not find a solution.
Thank you in advance! :)
It looks like there is a problem generating the JSON. I really don't think you want to have the dates formatted like that. Right now, you have some odd text surrounding a unix timestamp in milliseconds followed by a zone offset. You are also using the old and rather frowned-upon Date and SimpleDateFormat classes rather than the newer java.time API. However, it is possible to deserialize your date format. Here is one way:
public class DateDeserializer extends JsonDeserializer<Date> {
#Override
public Date deserialize(JsonParser jsonParser, DeserializationContext context)
throws IOException, JsonProcessingException {
Pattern pattern = Pattern.compile("/Date\\((\\d+)([+-]\\d+)\\)/");
Matcher matcher = pattern.matcher(jsonParser.getText());
if (matcher.find()) {
String timestamp = matcher.group(1);
String offset = matcher.group(2);
Instant instant = Instant.ofEpochMilli(Long.parseLong(timestamp));
ZonedDateTime zdt = instant.atZone(ZoneId.of(offset));
instant = zdt.toInstant();
return Date.from(instant);
} else {
throw new RuntimeException("Invalid format: " + jsonParser.getText());
}
}
}
java.time
For this answer I am assuming:
The time in your JSON may come with or without the UTC offset.
You can go all-in on java.time, the modern Java date and time API, and declare your variable to be of type Instant or OffsetDateTime, for example (not Date).
For JSON that comes with an offset such as +0300 declare your variable an OffsetDateTime. Then use the following deserializer.
public class OdtDeserializer extends JsonDeserializer<OffsetDateTime> {
private static final DateTimeFormatter JSON_DATE_FORMATTER = new DateTimeFormatterBuilder()
.appendLiteral("/Date(")
.appendValue(ChronoField.INSTANT_SECONDS)
.appendValue(ChronoField.MILLI_OF_SECOND, 3)
.appendOffset("+HHMM", "Z")
.appendLiteral(")/")
.toFormatter();
#Override
public OffsetDateTime deserialize(JsonParser jsonParser, DeserializationContext context)
throws IOException {
String dateStr = jsonParser.getText();
return OffsetDateTime.parse(dateStr, JSON_DATE_FORMATTER);
}
}
For JSON that comes without offset like /Date(1636510000000)/ declare your variable Instant. Use a similar deserializer. Leave out the offset from the formatter. Parse into an Instant — the syntax is a bit different.
public class InstantDeserializerWIthoutOffset extends JsonDeserializer<Instant> {
private static final DateTimeFormatter JSON_DATE_FORMATTER = new DateTimeFormatterBuilder()
.appendLiteral("/Date(")
.appendValue(ChronoField.INSTANT_SECONDS)
.appendValue(ChronoField.MILLI_OF_SECOND, 3)
.appendLiteral(")/")
.toFormatter();
#Override
public Instant deserialize(JsonParser jsonParser, DeserializationContext context)
throws IOException {
String dateStr = jsonParser.getText();
return JSON_DATE_FORMATTER.parse(dateStr, Instant::from);
}
}
For JSON that may come with or without the offset still use Instant and just modify the formatter of the latter deserializer to include an optional offset:
private static final DateTimeFormatter JSON_DATE_FORMATTER = new DateTimeFormatterBuilder()
.appendLiteral("/Date(")
.appendValue(ChronoField.INSTANT_SECONDS)
.appendValue(ChronoField.MILLI_OF_SECOND, 3)
.optionalStart()
.appendOffset("+HHMM", "Z")
.optionalEnd()
.appendLiteral(")/")
.toFormatter();
If you cannot modify your POJO class and need to stay with Date, modify my Instant deserializer into a Date deserializer by changing the declaration and returning a Date like this:
String dateStr = jsonParser.getText();
Instant inst = JSON_DATE_FORMATTER.parse(dateStr, Instant::from);
return Date.from(inst);
Final solution catching both: /Date(1633035600000+0300)/ and /Date(-62135596800000)/ (the latter was also present at one place in oneof the JSONs). Thank you #DavidConrad
#Override
public Date deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException, JsonProcessingException {
String dateString = jsonParser.getText();
Pattern pattern = Pattern.compile("/Date\\((-)?(\\d+)([+-]\\d+)?\\)/");
Matcher matcher = pattern.matcher(dateString);
if (!matcher.find()) {
throw new RuntimeException("Invalid format: " + dateString);
}
String timestamp = matcher.group(2);
String offset = matcher.group(3);
Instant instant = Instant.ofEpochMilli(Long.parseLong(timestamp));
if (nonNull(offset)) {
ZonedDateTime zdt = instant.atZone(ZoneId.of(offset));
instant = zdt.toInstant();
}
return Date.from(instant);
}
#DavidConrad Thank you, I will try your solution.
By the way, for now I made a workaround that works for me for now:
#Override
public Date deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException, JsonProcessingException {
SimpleDateFormat dateFormattter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateStr = jsonParser.getText();
String timeZone = dateStr.substring(dateStr.indexOf("+") + 1, dateStr.indexOf(")"));
String timeZoneShift = String.format("%s:%s",
timeZone.substring(0, timeZone.length()/2),
timeZone.substring(timeZone.length()/2));
dateFormattter.setTimeZone(TimeZone.getTimeZone(String.format("GMT+%s", timeZoneShift)));
Long millis = 0L;
if (dateStr.contains("+") && !dateStr.contains("-")) {
millis = Long.parseLong(dateStr.substring(dateStr.indexOf("(") + 1, dateStr.indexOf("+")));
} else if (dateStr.contains("+") && !dateStr.contains("-")) {
millis = Long.parseLong(dateStr.substring(dateStr.indexOf("(") + 1, dateStr.indexOf(")")));
}
Date date = new Date(millis);
String stringDate= dateFormattter.format(date);
try {
date = dateFormattter.parse(stringDate);
} catch (ParseException e) {
throw new RuntimeException(e);
}
return date;
}
I am trying to use opencsv to parse a csv file like this:
name,purchase,date
TEST,TEST,2020-10-20T00:37:53.562000000Z
TEST,TEST,2020-10-20T00:37:53.562000000Z
I am trying to add the parsed data to a firebase database following this tutorial: https://attacomsian.com/blog/spring-boot-upload-parse-csv-file. This is my class for the data:
public class Records {
#CsvBindByName
private String name;
#CsvBindByName
private String purchase;
#CsvBindByName
private Timestamp date;
// get and setters left out for brevity pls comment if needed
}
When I parse the file I get this error:
Exception in thread "pool-6-thread-2" Exception in thread "pool-6-thread-1" Exception in thread "pool-6-thread-4" Exception in thread "pool-6-thread-3" java.lang.RuntimeException: com.opencsv.exceptions.CsvDataTypeMismatchException: Conversion of 2022-10-20T00:37:53.562000000Z to com.google.cloud.Timestamp failed.
at com.opencsv.bean.concurrent.ProcessCsvLine.run(ProcessCsvLine.java:99)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: com.opencsv.exceptions.CsvDataTypeMismatchException: Conversion of 2022-10-20T00:37:53.562000000Z to com.google.cloud.Timestamp failed.
at com.opencsv.bean.ConverterPrimitiveTypes.convertToRead(ConverterPrimitiveTypes.java:128)
at com.opencsv.bean.BeanFieldSingleValue.convert(BeanFieldSingleValue.java:98)
at com.opencsv.bean.AbstractBeanField.setFieldValue(AbstractBeanField.java:180)
at com.opencsv.bean.AbstractMappingStrategy.setFieldValue(AbstractMappingStrategy.java:581)
at com.opencsv.bean.AbstractMappingStrategy.populateNewBean(AbstractMappingStrategy.java:328)
at com.opencsv.bean.concurrent.ProcessCsvLine.processLine(ProcessCsvLine.java:128)
at com.opencsv.bean.concurrent.ProcessCsvLine.run(ProcessCsvLine.java:83)
... 3 more
Caused by: org.apache.commons.beanutils.ConversionException: Can't convert value '2022-10-20T00:37:53.562000000Z' to type class com.google.cloud.Timestamp
at org.apache.commons.beanutils.converters.AbstractConverter.conversionException(AbstractConverter.java:474)
at org.apache.commons.beanutils.converters.StringConverter.convertToType(StringConverter.java:96)
at org.apache.commons.beanutils.converters.AbstractConverter.convert(AbstractConverter.java:169)
at org.apache.commons.beanutils.converters.ConverterFacade.convert(ConverterFacade.java:61)
at org.apache.commons.beanutils.ConvertUtilsBean.convert(ConvertUtilsBean.java:491)
at com.opencsv.bean.ConverterPrimitiveTypes.convertToRead(ConverterPrimitiveTypes.java:118)
... 9 more
How can i fix this error? Do I need to change the format of the date column? I copied the date format from a record in the database so that format is how it should be stored in the database
I changed the csv format to:
name,purchase,date
TEST,TEST,2018-09-16T08:00:00
TEST,TEST,2018-09-16T08:00:00
I modified the class that binds to the csv to look like this:
public class CsvRecords {
#CsvBindByName
private String name;
#CsvBindByName
private String purchase;
#CsvBindByName
private String date;
// get and setters left out for brevity pls comment if needed
}
The POJO class for the data in db:
public class Records {
private String name;
private String purchase;
private Timestamp date;
// get and setters left out for brevity pls comment if needed
}
When uploading in the controller class I then convert the string to LocalDateTime and then again to Timestamp like this:
#PostMapping("/upload-csv-file")
public String uploadCSVFile(#RequestParam("file") MultipartFile file, Model model) {
// validate file
if (file.isEmpty()) {
model.addAttribute("message", "Please select a CSV file to upload.");
model.addAttribute("status", false);
} else {
// parse CSV file to create a list of `User` objects
try (Reader reader = new BufferedReader(new InputStreamReader(file.getInputStream()))) {
// create csv bean reader
CsvToBean<Records> csvToBean = new CsvToBeanBuilder(reader)
.withType(Records.class)
.withIgnoreLeadingWhiteSpace(true)
.build();
// convert `CsvToBean` object to list of records
List<Records> records = csvToBean.parse();
// save users in DB?
for(int i = 0; i<records.size(); i++){
LocalDateTime localDateTime = LocalDateTime.parse(records.get(i).getDate);
Timestamp timestamp = Timestamp.valueOf(localDateTime);
Records rec = new Records(records.get(i).getName(), records.get(i).getPurchase(), timestamp)
firebaseServices.saveDetails(rec);
}
} catch (Exception ex) {
model.addAttribute("message", "An error occurred while processing the CSV file.");
model.addAttribute("status", false);
}
}
return "file-upload-status";
}
For details on the implementation of the firebaseServices class (saveDetails method) I used this tutorial
Could you have a try in this case that replaces the Timestamp to Date?
like follow this:
#CsvBindByName private Date date;
The timestamp field object is an instance from com.google.cloud.Timestamp package.
As #Allen suggested, you might have to look into java.util.date or event better, the Java 8 time APIs java.time.LocalTime to get support from ConverterPrimitiveTypes to be converted automatically.
I use Spring Boot and I try to save some date in MongoDB. My input date is
"2017-08-14T12:59"
I get this error while saving:
Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Failed to parse Date value '2017-08-14T12:59': Can not parse date "2017-08-14T12:59.000Z": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSS'Z'', parsing fails (leniency? null); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Failed to parse Date value '2017-08-14T12:59': Can not parse date "2017-08-14T12:59.000Z": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSS'Z'', parsing fails (leniency? null) (through reference chain:
In my POJO i tried like this:
#JsonDeserialize(using= CustomDateDeserialize.class)
private Date inputDateTime;
and I've implemented Deserializer like this :
private SimpleDateFormat dateFormat = new SimpleDateFormat(
"yyyy-MM-dd HH:mm");
#Override
public Date deserialize(JsonParser paramJsonParser,
DeserializationContext paramDeserializationContext)
throws IOException, JsonProcessingException {
String str = paramJsonParser.getText().trim();
try {
return dateFormat.parse(str);
} catch (ParseException e) {
}
return paramDeserializationContext.parseDate(str);
}
What else I miss here? Any help appreciated.
you need to modify format in your deserializer.
SimpleDateFormat dateFormat = new SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm");
any way simpledatetimeformat is not thread safe. if you java8 use DateTimeFormat.
Why don't you try Instant library
#Field("your_db_id_name")
private Instant inputDateTime;
public void setInputDateTime(Instant inputDateTime) {
this.inputDateTime = inputDateTime;
}
public void getInputDateTime() {
return inputDateTime;
}
You can set Filed by using Instant.now()
I'm implementing an object that represents a graph to be stored in a MongoDb. Here's the class definition:
#Document(collection = "mygraph")
public class MyGraph {
#Id
#JsonSerialize(using = ToStringSerializer.class)
public String id;
public Map<Instance, Set<Instance>> graph;
public String timestamp;
/**
* Add the default constructor
*/
public MyGraph() {
this.id = new ObjectId().toHexString();
this.graph = new LinkedHashMap<>();
DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
this.timestamp = dateFormat.format(new Date());
}
/**
*
* #param instances
*/
public MyGraph(Set<Instance> instances) {
this.id = new ObjectId().toHexString();
this.graph = new LinkedHashMap<>();
DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
this.timestamp = dateFormat.format(new Date());
/* Initialize the graph */
for (Instance instance : instances) {
this.graph.put(instance, new HashSet<>());
}
}
// get and set method are below
}
However I'm getting the following error:
Time elapsed: 0.03 sec <<< FAILURE!
org.springframework.data.mapping.model.MappingException: Cannot use a complex object as a key value.
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeMapInternal(MappingMongoConverter.java:669)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.createMap(MappingMongoConverter.java:585)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writePropertyInternal(MappingMongoConverter.java:471)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$3.doWithPersistentProperty(MappingMongoConverter.java:430)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$3.doWithPersistentProperty(MappingMongoConverter.java:418)
at org.springframework.data.mapping.model.BasicPersistentEntity.doWithProperties(BasicPersistentEntity.java:312)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeInternal(MappingMongoConverter.java:418)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeInternal(MappingMongoConverter.java:392)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.write(MappingMongoConverter.java:356)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.write(MappingMongoConverter.java:79)
at org.springframework.data.mongodb.core.MongoTemplate.toDbObject(MongoTemplate.java:853)
at org.springframework.data.mongodb.core.MongoTemplate.doSave(MongoTemplate.java:1014)
at org.springframework.data.mongodb.core.MongoTemplate.save(MongoTemplate.java:963)
at org.springframework.data.mongodb.repository.support.SimpleMongoRepository.save(SimpleMongoRepository.java:80)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:503)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:488)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:460)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
at com.sun.proxy.$Proxy197.save(Unknown Source)
at models.CloudGraphTest.testSaveCloudGraph(CloudGraphTest.java:79)
How can I change my MappingMongoConverter to accomodate for that?
There's an open Improvement in Spring: Don't require Converters for Complex classes that are used as Ids.
Unfortunately, currently Spring team decided not to support it.
decided not to support it at all
EDIT
There's an open related issue Allow using complex types as Map keys that may include a fix later
Am unable to convert string to Joda LocalTime with DefaultFormattingConversionService.
If I pass time as "12:00" it says time is too short, but if I pass it as "12:00:00", it says it is malformed.
import org.joda.time.LocalTime;
import org.springframework.format.support.DefaultFormattingConversionService;
public class SpringLocalTimeFormatterTry {
public static void main(String[] args) {
DefaultFormattingConversionService service = new DefaultFormattingConversionService();
try {
System.out.println(service.convert("12:00", LocalTime.class));
}
catch (Exception e) {
System.out.println(e.getMessage());
}
try {
System.out.println(service.convert("12:00:00", LocalTime.class));
}
catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
How to use it correctly or fix?
The vanilla settings of DefaultFormattingConversionService use platform default locale, which, I assume from the error, are the same as mine, ie. English. That means, that for time you need to add the AM/PM indicator. This works for me:
System.out.println(service.convert("10:12 am", LocalTime.class));
>> 10:12:00.000
To handle your desired time format, you can add an extra converter:
service.addConverter(new Converter<String, LocalTime>() {
#Override
public LocalTime convert(String source) {
return LocalTime.parse(source);
}
});
Then, both examples pass:
System.out.println(service.convert("12:00", LocalTime.class));
>> 12:00:00.000
System.out.println(service.convert("12:00:00", LocalTime.class));
>> 12:00:00.000
You can skip registering the default converters by creating the service with
new DefaultFormattingConversionService(false);
Finally, I assume in the production code you are actually getting the ConversionService from the ConversionServiceFactoryBean, so you can configure that as follows:
#Bean
public ConversionServiceFactoryBean conversionService() {
ConversionServiceFactoryBean conversionServiceFactoryBean = new ConversionServiceFactoryBean();
Set<Converter<?, ?>> myConverters = new HashSet<>();
myConverters.add(new Converter<String, LocalTime>() {
#Override
public LocalTime convert(String source) {
return LocalTime.parse(source);
}
});
conversionServiceFactoryBean.setConverters(myConverters);
return conversionServiceFactoryBean;
}
Try this:
DateTimeFormatter dtf = DateTimeFormat.forPattern("HH:mm:ss");
LocalTime localTime = dtf.parseLocalTime("12:00:00");
System.out.println("Time"+localTime);