I have a problem with the serialization of an object to JSON using the org.json library.
In my code I have:
String resultStr = new JSONObject(result).toString();
and in result object two fields of type LocalDateTime:
private LocalDateTime startDate;
private LocalDateTime stopDate;
In variable resultStr I got date in following format:
2020-01-23T14:13:30.121205
I want this ISO format:
2016-07-14T07:58:08.158Z
I know that in Jackson there is an annotation #JsonFormat, but I didn't find anything like that in org.json. How to define a format of LocalDateTime in JSON string with org.json?
In JSON in Java, it seems that there are not much support for Date/Time formatting.
To customize the formatting of LocalDateTime field, we can make use of
1. #JSONPropertyIgnore to ignore the original getter to be serialized
2. #JSONPropertyName to annotate a new getter with ignored field name, which return the desired formatted date string, as following:
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import org.json.JSONObject;
import org.json.JSONPropertyIgnore;
import org.json.JSONPropertyName;
public class CustomizeLocalDateTimeFormatInOrgJson {
public static void main(String[] args) {
Result result = new Result(LocalDateTime.now(), LocalDateTime.now());
String resultStr = new JSONObject(result).toString();
System.out.println(resultStr);
}
public static class Result {
DateTimeFormatter customDateTimeFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssS'Z'");
private LocalDateTime startDate;
#JSONPropertyIgnore
public LocalDateTime getStartDate() {
return startDate;
}
#JSONPropertyName("startDate")
public String getStartDateString() {
return customDateTimeFormat.format(startDate);
}
private LocalDateTime stopDate;
#JSONPropertyIgnore
public LocalDateTime getStopDate() {
return stopDate;
}
#JSONPropertyName("stopDate")
public String getStopDateString() {
return customDateTimeFormat.format(stopDate);
}
public void setStopDate(LocalDateTime stopDate) {
this.stopDate = stopDate;
}
public void setStartDate(LocalDateTime startDate) {
this.startDate = startDate;
}
public Result(LocalDateTime startDate, LocalDateTime stopDate) {
super();
this.startDate = startDate;
this.stopDate = stopDate;
}
}
}
Related
I would like to know how to format the date time correctly? The result is Localdatetime yyyy-MM-ddTHH:mm.
Could you advise how to solve?
I'm using Java 11, and does it because #JsonFormat not support #RequestParam?
Controller:
#PostMapping("/checkFollowupDate")
public LocalDateTime updateCaseFollowup(#RequestParam("followupDate") #DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS") LocalDateTime followupDate) {
return followupDate;
}
Entity:
#Entity
#Table(name = "caseFollowup")
public class CaseFollowup {
#JsonFormat(pattern="yyyy-MM-dd HH:mm:ss.SSS")
private LocalDateTime followupDate;
Since you are using Spring-boot , I'm also assuming you are using java8 . In any case try using java8 time api for date like :
#JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")
private LocalDateTime followupDate;
and if you are on JPA 2.1 which was released before java8 then in your entity class you could have a converter to convert it for sql timestamp like :
#Converter(autoApply = true)
public class LocalDateTimeAttributeConverter implements AttributeConverter<LocalDateTime, Timestamp> {
#Override
public Timestamp convertToDatabaseColumn(LocalDateTime locDateTime) {
return locDateTime == null ? null : Timestamp.valueOf(locDateTime);
}
#Override
public LocalDateTime convertToEntityAttribute(Timestamp sqlTimestamp) {
return sqlTimestamp == null ? null : sqlTimestamp.toLocalDateTime();
}
}
Remember that in newer version of Hibernate(Hibernate 5) and JPA the above conversion will be performed automatically and doesn't require you to provide the above method.
If your requirement is just to persist the Date read from the #RequestParam through the entity class in a particular format, you could always convert it manually into any format that you may choose before setting the value into your entity class like :
#PostMapping("/caseFollowup")
public Integer updateCaseFollowup(#RequestParam("followupDate")
LocalDateTime followupDate) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formatDateTime = followupDate.format(formatter);
}
use this code in you model class :
#JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssZ", shape = JsonFormat.Shape.STRING)
private OffsetDateTime lastModifiedDate;
and create this class mapper :
import java.sql.Timestamp;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
#Component
public class DateMapper {
public OffsetDateTime asOffsetDateTime(Timestamp ts){
if (ts != null){
return OffsetDateTime.of(ts.toLocalDateTime().getYear(), ts.toLocalDateTime().getMonthValue(),
ts.toLocalDateTime().getDayOfMonth(), ts.toLocalDateTime().getHour(), ts.toLocalDateTime().getMinute(),
ts.toLocalDateTime().getSecond(), ts.toLocalDateTime().getNano(), ZoneOffset.UTC);
} else {
return null;
}
}
public Timestamp asTimestamp(OffsetDateTime offsetDateTime){
if(offsetDateTime != null) {
return Timestamp.valueOf(offsetDateTime.atZoneSameInstant(ZoneOffset.UTC).toLocalDateTime());
} else {
return null;
}
}
}
I need to deserialize the Json to Java Objects in Junit. I have Json file like
{
"studentId":57,
"JoinedDate":"31-12-2019",
"DOB":"08-06-1998"
}
I have class for the same to map
public class Student{
private long studentId ;
private LocalDate JoinedDate;
private LocalDate DOB ;
public long getStudentId() {
return studentId;
}
public void setStudentId(long studentId) {
this.studentId = studentId;
}
public LocalDate getJoinedDate() {
return JoinedDate;
}
public void setJoinedDate(LocalDate joinedDate) {
JoinedDate = joinedDate;
}
public LocalDate getDOB() {
return DOB;
}
public void setDOB(LocalDate dOB) {
DOB = dOB;
}
I need to write centralized builder for Unit testing project similar like this
builder.deserializers(new LocalDateDeserializer(DateTimeFormatter.ofPattern(dateFormat)));
builder.serializers(new LocalDateSerializer(DateTimeFormatter.ofPattern(dateFormat)));
Main Class
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Main.class)
#WebAppConfiguration
public class Main{
#Test
public void contextLoads() {
assertTrue(true);
}
}
Unit testing Project looks like
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Main.class)
#WebAppConfiguration
public class StudentTest{
private ObjectMapper jsonObjectMapper;
#Before
public void setUp() throws IOException {
jsonObjectMapper = new ObjectMapper();
studentJson = IOUtils.toString(getClass().getResourceAsStream(CommonTestConstants.StudentPath+ "/Student.json"));
}
I'm getting a error while mapping the objects -
com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type java.time.LocalDate from String "31-12-2019": Failed to deserialize java.time.LocalDate:
Another Error - Sometimes.
com.fasterxml.jackson.databind.JsonMappingException: Text '31-12-2019'
could not be parsed at index 0
I assume LocalDate format mismatch is the issue. Any suggestion to make it centralized way instead of specifying the format above the fields. Any one please advise?
Reference - Spring Boot JacksonTester custom serializer not registered
You just need to specify the date format by default jackson allows format of yyyy-MM-dd
public class Student{
private long studentId ;
#JsonProperty("JoinedDate") #JsonFormat(pattern = "dd/MM/yyyy")
private LocalDate JoinedDate;
#JsonProperty("DOB") #JsonFormat(pattern = "dd/MM/yyyy")
private LocalDate DOB ;
public long getStudentId() {
return studentId;
}
public void setStudentId(long studentId) {
this.studentId = studentId;
}
public LocalDate getJoinedDate() {
return JoinedDate;
}
public void setJoinedDate(LocalDate joinedDate) {
this.JoinedDate = joinedDate;
}
public LocalDate getDOB() {
return DOB;
}
public void setDOB(LocalDate dOB) {
this.DOB = dOB;
}
I hope it helps you
Springboot 1.4.x or above has this interface Jackson2ObjectMapperBuilderCustomizer which allows you to initialize objectMapper.
What we need to do, is override customize method and register deserializers and
serializers.
#SpringBootApplication
public class TestApplication implements Jackson2ObjectMapperBuilderCustomizer {
#Override
public void customize(Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder) {
// pattern could be anything whatever is required
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/dd/MM");
LocalDateSerializer localDateDeserializer = new LocalDateSerializer(formatter);
jackson2ObjectMapperBuilder
.failOnEmptyBeans(false)
.deserializersByType(new HashMap<Class<?>, JsonDeserializer<?>>(){{
put(LocalTime.class, localTimeSerializer);
}});
}
}
We can also add seriliazers similar way.
jackson2ObjectMapperBuilder
.failOnEmptyBeans(false)
.serializersByType(new HashMap<Class<?>, JsonSerializer<?>>(){{
put(LocalTime.class, localTimeSerializer);
}});
you can check more details here. Spring Jackson builder
I am returning a JSON as a response from the controller. I want to format the date fields in this response.
Controller-
#RequestMapping(value = "/call", method = RequestMethod.GET)
public SampleDTO get()
{
......
return sampleDTO;
}
SampleDTO-
{
"date" : "2020-03-10T08:57:58+0000",
"text" : "abc"
}
I want to format the date field to dd-MM-yyyy
To do this I add the #JsonFormat annotation to the bean class of SampleDTO.
SampleDTO.java -
import java.util.Date;
public class SampleDTO
{
#JsonFormat(pattern = "dd-MM-yyyy")
private Date date;
private String text;
#JsonFormat(pattern = "dd-MM-yyyy")
public void setDate(final Date date)
{
this.date = date;
}
#JsonFormat(pattern = "dd-MM-yyyy")
public Date getDate()
{
return date;
}
public void setText(final String text)
{
this.text = text;
}
public String getText()
{
return text;
}
}
Still, I am getting this format in the response on my browser.
"date" : "2020-03-10T08:57:58+0000"
EDIT 1:
Instead of returning the sampleDTO, converting it to String directly in the code works perfectly fine.
This works like a charm:
SampleDTO sampleDTO = new SampleDTO();
sampleDTO.setCreated(new Date());
ObjectMapper om = new ObjectMapper();
return om.writeValueAsString(sampleDTO);
Please, check that your Date is from java.util and not from java.sql package. Plus try the following:
#JsonSerialize(as = Date.class)
#JsonFormat(shape=JsonFormat.Shape.STRING, pattern="dd-MM-yyyy")
Could you try this on the field level and remove from getDate() method in your DTO.
Something like this,
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy")
private Date date;
This should work with your current version of jackson-databind:2.9.8.jar.
Here is the small example for you:
public class ExampleMain {
public static void main(String[] args) throws IOException {
Employee employee = new Employee();
employee.setDateOfBirth(Date.from(ZonedDateTime.now().minusYears(30).toInstant()));
System.out.println("-- before serialization --");
System.out.println(employee);
System.out.println("-- after serialization --");
ObjectMapper om = new ObjectMapper();
String jsonString = om.writeValueAsString(employee);
System.out.println(jsonString);
System.out.println("-- after deserialization --");
System.out.println(om.readValue(jsonString, Employee.class));
}
}
public class Employee {
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy")
private Date dateOfBirth;
public Date getDateOfBirth() {
return dateOfBirth;
}
public void setDateOfBirth(Date dateOfBirth) {
this.dateOfBirth = dateOfBirth;
}
#Override
public String toString() {
return "Employee{" +
", dateOfBirth=" + dateOfBirth +
'}';
}
}
There are three levels of how you can solve this date format issue with Spring.
1) Using #JsonFormat on your date fields
In this case, you need to use the same annotation in front of all your private date members.
public class MyBean{
#JsonFormat(pattern="yyyy-MM-dd")
private Date birthday;
#JsonFormat(pattern="yyyy-MM-dd")
private LocalDate birthday;
// getters and setters here
}
2) Setting the Default format
If you want to configure the default date format for all dates in your application, add the following line to the application.properties or application.yml config file:
spring.jackson.date-format=yyyy-MM-dd
Unfortunately, this solution doesn't work with the Java 8 date types, like LocalDate and LocalDateTime.
3) Customizing your Jackson ObjectMapper
This solution works like a charm with Java 8 date types as well.
#Configuration
public class ContactAppConfig {
private static final String DATE_FORMAT = "yyyy-MM-dd";
private static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
#Bean
public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
return builder -> {
builder.simpleDateFormat(DATE_TIME_FORMAT);
builder.serializers(new LocalDateSerializer(DateTimeFormatter.ofPattern(DATE_FORMAT)));
builder.serializers(new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT)));
};
}
}
I suggest you use the 3rd option.
you can use jstl format to format the date :)
<%# taglib prefix = "fmt" uri = "http://java.sun.com/jsp/jstl/fmt" %>
<fmt:formatDate pattern = "yyyy-MM-dd" value = "${date}" />
I'm switching from MongoDB to DynamoDB on a project. Now I'm trying to store this Post object in the db. I'm using the DynamoDBTypeConverter to convert the ZonedDateTime to a String, as DynamoDB doesn't support ZonedDateTime.
That works fine, but when I'm adding a ZonedDateTime field in the Comment object and try to convert it too it doesn't work. I've tried adding a converter to the Comment class, and tried using the converter in the Post class for Comment, but nothing seems to work. Is there a way to convert a field in a nested object for DynamoDB?
com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException: Cannot marshall type class java.time.ZonedDateTime without a custom marshaler or #DynamoDBDocument annotation.
#DynamoDBTable(tableName = "Post")
public class Post {
#DynamoDBHashKey
private String postNumber;
private ZonedDateTime date;
private List<Comment> comments;
#DynamoDBTypeConverted(converter = ZonedDateTimeConverter.class)
#DynamoDBAttribute
public ZonedDateTime getDate() {
return date;
}
#DynamoDBAttribute(attributeName = "comments")
public List<Comment> getComments() {
return comments;
}
static public class ZonedDateTimeConverter implements DynamoDBTypeConverter<String, ZonedDateTime> {
#Override
public String convert(final ZonedDateTime time) {
return time.toString();
}
#Override
public ZonedDateTime unconvert(final String stringValue) {
return ZonedDateTime.parse(stringValue);
}
}
#DynamoDBDocument
public class Comment {
private String commentNumber;
#NotNull
private User user;
private ZonedDateTime date;
#DynamoDBTypeConverted(converter = ZonedDateTimeConverter.class)
#DynamoDBAttribute
public ZonedDateTime getDate(){
return this.date;
}
static public class ZonedDateTimeConverter implements DynamoDBTypeConverter<String, ZonedDateTime> {
#Override
public String convert(final ZonedDateTime time) {
return time.toString();
}
#Override
public ZonedDateTime unconvert(final String stringValue) {
return ZonedDateTime.parse(stringValue);
}
}
I am trying to serialize a class that has two Date fields defined:
import com.google.gson.annotations.Expose;
import java.util.Date;
public class DateRange {
private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
#Expose
public Date startDate;
#Expose
public Date endDate;
public DateRange(Date startDate, Date endDate) {
this.startDate = startDate;
this.endDate = endDate;
}
public DateRange(String startDate, String endDate ) throws ParseException{
this.startDate = dateFormat.parse(startDate);
this.endDate = dateFormat.parse(endDate);
}
}
but using gson.toJson an exception is thrown where multiple
import com.google.gson.Gson
Gson gson = new Gson()
gson.toJson(new DateRange("2011-11-11", "2012-11-11"))
results in
java.lang.IllegalArgumentException: class java.text.DecimalFormat declares multiple JSON fields named maximumIntegerDigits
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:166)
The problem is exacerbated where I have a class ManyDates which has a field with DateRange as well as another field with an array of DateRange. I have attempted adding the field as a private transient but no luck (also tried with the field as a String type)
private transient java.text.DecimalFormat maximumIntegerDigits;
but the field is still causing issues with the serialization. I'm not sure where that field is even coming from but I suspect a simple solution to this must be just out of reach that I'm just not seeing.
In this case gson tries to serialize dateFormat field.
just annotate it with #Transient