I've got model and field like this:
#Element(name = "TIMESTAMP")
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private LocalDateTime date;
In response I received:
<TIMESTAMP>2016-05-04T13:13:42.000</TIMESTAMP>
but during parsing xml to model I have error:
"message": "org.simpleframework.xml.core.PersistenceException: Constructor not matched for class java.time.LocalDateTime",
I also tried with:
#Element(name = "TIMESTAMP")
#DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS")
private LocalDateTime date;
and this still doesn't work. Any Idea ? I am using springframework.xml lib.
The problem is by default simplexml lib doesn't know how to serialize/deserialize new Java8 date types.
In order to succeed you need to use custom converter.
Example entity (see the special #Convert annotation)
public class Entity {
#Element(name = "TIMESTAMP")
#Convert(LocalDateTimeConverter.class)
private LocalDateTime date;
// omitted
}
Special converter
public class LocalDateTimeConverter implements Converter<LocalDateTime> {
public LocalDateTime read(InputNode node) throws Exception {
String name = node.getValue();
return LocalDateTime.parse(name, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
}
public void write(OutputNode node, LocalDateTime input) {
String value = input.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
node.setValue(value);
}
}
Usage
Strategy strategy = new AnnotationStrategy();
Persister persister = new Persister(strategy);
Entity serializedEntity = persister.read(Entity.class, xmlInputStream);
Full source is available on GitHub
Related
I am new to java programming trying to do dto mapping,to implement data filtering
#Data
#FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
public class Request {
List<EventType> eventType;
String eventRef;
List<Status> status;
LocalDate DateFrom;
LocalDate DateTo;
}
public class FilterDto {
#JsonProperty("eventType")
#Valid
private List<EventTypeEnum> eventType = null;
#JsonProperty("eventRef")
private String eventRef;
#JsonProperty("status")
#Valid
private List<StatusEnum> status = null;
#JsonProperty("DateFrom")
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
private LocalDate DateFrom;
#JsonProperty("DateTo")
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
private LocalDate DateTo;
map struct func
Request map(List<FilterDto> filter);
I call this mapper in the controller
Request request = registryMapper.map(regPageReqDto.getFilter());
regPageReqDto generated class in swagger, which contains FilterDto
I have the following DTO which has 2 fields that must be converted to OffsetDateTime:
#Data
public class AppointmentDTO {
private String id;
#NotNull
private String startTime;
#NotNull
private String endTime;
#NotNull
private String timeZoneStart;
#NotNull
private String timeZoneEnd;
// other fields
}
to
#Data
#Document
#NoArgsConstructor
#AllArgsConstructor
public class Appointment {
#Id
private String id;
private String timeZoneStart;
private String timeZoneEnd;
private OffsetDateTime startTime;
private OffsetDateTime endTime;
private OffsetDateTime createdTime;
// other fields
}
In order to convert, I need the DTO's timeZone fields plus a DateTimeFormatter. So, my attempt is this:
#Component
#Mapper(componentModel = "spring")
public interface IAppointmentMapper {
#Mapping(target = "createdTime", ignore = true)
Appointment convertAppointmentDTOToAppointment(AppointmentDTO dto, #Context OffsetDateTimeMapper offsetDateTimeMapper);
}
public class OffsetDateTimeMapper {
private String startTime;
private String endTime;
private String timeZoneStart;
private String timeZoneEnd;
private final DateTimeFormatter dateTimeFormatter;
public OffsetDateTimeMapper(DateTimeFormatter dateTimeFormatter) {
this.dateTimeFormatter = dateTimeFormatter;
}
#BeforeMapping
public void beforeStartTimeMapping(AppointmentDTO dto) {
this.startTime = dto.getStartTime();
this.timeZoneStart = dto.getTimeZoneStart();
}
#BeforeMapping
public void beforeEndTimeMapping(AppointmentDTO dto) {
this.endTime = dto.getEndTime();
this.timeZoneEnd = dto.getTimeZoneEnd();
}
#AfterMapping
public void startTimeMap(#MappingTarget Appointment appointment) {
LocalDateTime localDateTime = LocalDateTime.parse(startTime, dateTimeFormatter);
ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.of(timeZoneStart));
appointment.setStartTime(zonedDateTime.toOffsetDateTime());
}
#AfterMapping
public void endTimeMap(#MappingTarget Appointment appointment) {
LocalDateTime localDateTime = LocalDateTime.parse(endTime, dateTimeFormatter);
ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.of(timeZoneEnd));
appointment.setEndTime(zonedDateTime.toOffsetDateTime());
}
}
When I build, however, I get the following error: Error:(22,17) java: Can't map property "java.lang.String startTime" to "java.time.OffsetDateTime startTime". Consider to declare/implement a mapping method: "java.time.OffsetDateTime map(java.lang.String value)". If I put a default map() method on the interface, I don't get the error, but I don't have the proper #Context required.
/*default OffsetDateTime map(String value) {
LocalDateTime localDateTime = LocalDateTime.parse(value);
ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneOffset.UTC);
return zonedDateTime.toOffsetDateTime();
}*/
Your approach is interesting. In order to work properly you would have to explicitly ignore the properties that you don't want MapStruct to automatically map.
In this case add:
#Mapping(target = "startTime", ignore = true)
#Mapping(target = "endTime", ignore = true)
However, in your case I would try to use Mapping method selection based on qualifiers and use the source parameters as source.
So something like:
#Component
#Mapper(componentModel = "spring")
public interface IAppointmentMapper {
#Mapping(target = "createdTime", ignore = true)
#Mapping(target = "startTime", source = "dto", qualifiedByName = "startTime")
#Mapping(target = "endTime", source = "dto", qualifiedByName = "endTime")
Appointment convertAppointmentDTOToAppointment(AppointmentDTO dto, #Context DateTimeFormatter dateTimeFormatter);
#Named("startTime")
default OffsetDateTime mapStartTime(AppointmentDTO dto, #Context DateTimeFormatter dateTimeFormatter) {
LocalDateTime localDateTime = LocalDateTime.parse(dto.getStartTime(), dateTimeFormatter);
ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.of(dto. getTimeZoneStart()));
return zonedDateTime.toOffsetDateTime()
}
#Named("endTime")
default OffsetDateTime mapEndTime(AppointmentDTO dto, #Context DateTimeFormatter dateTimeFormatter) {
LocalDateTime localDateTime = LocalDateTime.parse(dto.getEndTime(), dateTimeFormatter);
ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.of(dto. getTimeZoneEnd()));
return zonedDateTime.toOffsetDateTime()
}
}
Note: #Named is org.mapstruct.Named.
I have a simple rest service to store time range, however, Spring cannot parse datetime format with timezone correctly.
the Entity is
#Data
#Entity
public class TimeRange {
#Setter(AccessLevel.NONE)
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable = true)
private LocalDateTime startTime;
#Column(nullable = true)
private LocalDateTime endTime;
}
The controller is:
#PostMapping(path = "/time", consumes = "application/json", produces = "application/json")
public Boolean setTime(#RequestBody TimeRange timeRange) {
timeRangeRepository.save(timeRange);
return true;
}
and the actuall request is
url = f'http://localhost/api/time'
data = {
"startTime": "2019-12-03T19:58:29.047820+08:00",
"endTime": "2019-12-04T19:58:29.047820+08:00"}
resp = requests.post(url, json=data, timeout=10)
pprint(resp.json())
spring reported an error said:
esolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error:
Cannot deserialize value of type `java.time.LocalDateTime` from String "2019-12-
03T19:58:29.047820+08:00": Failed to deserialize java.time.LocalDateTime:
(java.time.format.DateTimeParseException) Text '2019-12-03T19:58:29.047820+08:00' could not be
parsed, unparsed text found at index 26; nested exception is
com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type
java.time.LocalDateTime from String "2019-12-03T19:58:29.047820+08:00": Failed to deserialize
java.time.LocalDateTime: (java.time.format.DateTimeParseException) Text '2019-12-
03T19:58:29.047820+08:00' could not be parsed, unparsed text found at index 26
at
You have a date with offset, if all your date comes in the same format you can create a custom deserializer like this
public class CustomLocalDateTimeDeserializer extends StdDeserializer<LocalDateTime> {
private static final long serialVersionUID = 1L;
public CustomLocalDateTimeDeserializer () {
this(null);
}
protected CustomLocalDateTimeDeserializer (Class<?> vc) {
super(vc);
}
#Override
public LocalDateTime deserialize(JsonParser arg0, DeserializationContext arg1)
throws IOException, JsonProcessingException {
return LocalDateTime.parse(arg0.getValueAsString(), DateTimeFormatter.ISO_OFFSET_DATE_TIME);
}
}
and the annotate your fields with #JsonDeserialize
#JsonDeserialize(using = CustomLocalDateTimeDeserializer.class)
private LocalDateTime startTime;
#JsonDeserialize(using = CustomLocalDateTimeDeserializer.class)
private LocalDateTime endTime;
And if you want to serialize your dates with the same format, you have to create a custom serializer
Annotate your LocalDateTime fields with:
#JsonSerialize(using = LocalDateTimeSerializer.class)
I have the following JSON string in REST response:
"09:41:50 CET"
For the corresponding POJO mapper class has a Date type for this field. So I've tried Jackson and GSON to map JSON to Java Object, but both failed with the following messages:
GSON: java.text.ParseException: Failed to parse date ["09:41:50 CET"]: Invalid number: 09:4
Jackson: InvalidFormatException: Cannot deserialize value of type `java.util.Date` from
String "09:41:50 CET": not a valid representation
Sadly I cannot modify in the POJO class the type to string or anything else, because I get those POJO classes from mvn dependency.
Try with this:
public static void main(String[] args) throws ParseException {
String jsonStr = "{ \"date\" : \"09:41:50 CET\" }";
Gson gson = new GsonBuilder().setDateFormat("HH:mm:ss").create();
JsonElement element = gson.fromJson (jsonStr, JsonElement.class);
OnlyDate date =gson.fromJson(element, new TypeToken<OnlyDate>(){}.getType());
System.out.println(date.getDate());
}
My example DTO is:
public class OnlyDate implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
#SerializedName("date")
private Date date ;
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
You have to specify the dateFormat of your gson Element
Not sure what kind of rest you have however if you are using spring rest you can do it by implementing custom Converter check the example at https://www.baeldung.com/spring-mvc-custom-data-binder.
Since Jackson v2.0, you can use #JsonFormat annotation directly on Object members;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm:ss", timezone="CET")
private Date date;
I am using Spring Boot + Spring data Redis example to save Date into the Redis Cache. Although I used #DateTimeFormat #JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd"), but still persistance happening is long value. Look like its a millisecond.
Can somebody guide if I need to set extra configurations to persist date like yyyy-MM-dd.
HGETALL users:1
1) "_class"
2) "com.XXX.entity.User"
3) "userId"
4) "1"
5) "name"
6) "John"
7) "createdDate"
8) "1542043247352"
Entity classes:
#Builder
#Data
#AllArgsConstructor
#NoArgsConstructor
#RedisHash("users")
public class User {
#Id
private Long userId;
private String name;
#DateTimeFormat
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private Date createdDate;
private List<Group> groups;
}
UPDATE-1:: As per suggestion I implemented, but still not working
CustomDateSerializer.java
#Component
public class CustomDateSerializer extends JsonSerializer<Date> {
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy");
#Override
public void serialize(Date date, JsonGenerator gen, SerializerProvider provider)
throws IOException, JsonProcessingException {
String formattedDate = dateFormat.format(date);
gen.writeString(formattedDate);
}
}
Custom Interface
#Retention(RetentionPolicy.RUNTIME)
public #interface MyJsonFormat {
String value();
}
Model class
#MyJsonFormat("dd.MM.yyyy")
#JsonSerialize(using = CustomDateSerializer.class)
private Date createdDate;
I'd advise using LocalDateTime (or LocalDate if you prefer) instead. You can then annotate your fields with
#JsonDeserialize(using = LocalDateTimeDeserializer.class)
#JsonSerialize(using = LocalDateTimeSerializer.class)
private LocalDateTime createdAt;
using jackson's jsr310 add on:
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
By Using Custom Serializer, this can be solved. Ref #https://kodejava.org/how-to-format-localdate-object-using-jackson/#comment-2027
public class LocalDateSerializer extends StdSerializer<LocalDate> {
private static final long serialVersionUID = 1L;
public LocalDateSerializer() {
super(LocalDate.class);
}
#Override
public void serialize(LocalDate value, JsonGenerator generator, SerializerProvider provider) throws IOException {
generator.writeString(value.format(DateTimeFormatter.ISO_LOCAL_DATE));
}
}
POJO:
#JsonDeserialize(using = LocalDateDeserializer.class)
#JsonSerialize(using = LocalDateSerializer.class)
private LocalDate createdDate;