Jackson deserialize JSON with timestamp field - java

I have such string:
{
"debug":"false",
"switchTime":"2017-04-12 17:04:42.896026"
}
I'm trying to get object in such approach:
new ObjectMapper().readValue(string, MyObject.class);
And MyObject class:
class MyObject {
private Boolean debug;
private Timestamp switchTime;
//...getters, setters, constructors
}
I have such exception:
com.fasterxml.jackson.databind.exc.InvalidFormatException:
Can not deserialize value of type java.sql.Timestamp from String
"2017-04-12 17:04:42.896026": not a valid representation (error:
Failed to parse Date value '2017-04-12 17:04:42.896026':
Can not parse date "2017-04-12 17:04:42.896026Z": while it seems
to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSS'Z'',
parsing fails (leniency? null)) at [Source:
{"debug":"false", "switchTime":"2017-04-12 17:04:42.896026"};
I don't understand why...If i use in debug mode Timestamp.valueOf() with "2017-04-12 17:04:42.896026" - i have success

I think you need to set the expected date/time format using #JsonFormat annotation as shown below.
class MyObject {
private Boolean debug;
#JsonFormat(pattern="yyyy-MM-dd HH:mm:ss.SSS")
private Timestamp switchTime;
//...getters, setters, constructors
}
You can also set timezone as #JsonFormat(pattern="yyyy-MM-dd HH:mm:ss.SSS",timezone="PST")

I faced the similar problem when I was using lombok in the POJO class which has #Builder and #Value annotations
I have also added the annotation AllArgsConstructor and then it is working fine with my custom deserializer code

The value that you see in debug mode is "toString()" version of actual value of timestamp, so don't rely on what you inspect in debug mode.
You can use #JsonFormat annotation that helps you to convert your timestamp with specified format. You need to take care of timezones also while converting!

Related

default Jackson naming strategy for fields with short names

I've used Jackson for years, and I am not sure I ever faced this issue.
Using Jackson 2.12.5 in a Spring Boot 2.5.5 project, I have an object that I need to serialize. I have no issue with other fields, but these 2 fields are causing me problems :
#Jacksonized
#Builder
#Getter
public class ComputationResult {
private final String pId;
private final String cId;
... other fields ignored
}
As you can see, I am also using Lombok annotations. When "delombokized", the getters are :
public String getPId() {
return this.pId;
}
public String getCId() {
return this.cId;
}
when serializing the POJO, I expect the field names to be "pId" and "cId", but they are not : I get "pid" and "cid", all lower-case. I don't have the problem with other fields, for which the case is respected.
It caused me an issue because I need to serialize then deserialize the POJO, and the deserialization failed because it could not map "cid" json field to "cId" java field.
There are various workarounds (I am using #JsonAlias("cid") on the field to allow the deserialization), but I am puzzled : is this an expected behavior by Jackson ? does it process String fields differently depending on their length ? or is it a java beans convention that I am not aware of ?
Is there a property to set in the objectMapper to "fix" the behavior, without implementing my own com.fasterxml.jackson.databind.PropertyNamingStrategy ?
The problem seems to be caused by how JavaBeans methods get generated when there's a single lowercase character at the beginning of the property name. You might be surprised by the fact that getpId and getcId are indeed correctly named, just as I was.
In short, pId correctly results in the getter getpId rather than the Lombok-generated getPId (the one JavaBeans should have kept, in my opinion).
Now, the interesting part is that Jackson makes cid and pid out of getCId and getPId, respectively, for some reason... while at the same time producing cId and pId for getcId and getpId.
So while getcId and getpId are a quirk of JavaBeans, it seems that Jackson is behaving correctly by default, when the getters are correctly named, i.e., getpId -> "pId" and getcId -> "cId".
Given that Lombok generates getPId and getCId, which lead to the all-lowercase keys in the resulting JSON, deserialization does not work.
If you don't like the getpId/getcId naming, then you may have to write your own getters and force a property name explicitly:
#Builder
#Jacksonized
class ComputationResult {
private final String pId;
private final String cId;
#JsonProperty("pId")
public String getPId() {
return pId;
}
#JsonProperty("cId")
public String getCId() {
return cId;
}
}
For the setters, you already have to rely on #Jacksonized because of final fields anyway, so it doesn't make a difference. Just note that #Getter has to be omitted because it'd result in duplicate properties (with the other kind of naming, that is).

Problems with sonar changing the names of an object in REST calls by Jackson JSON

I have an object that in its fields is mandatory that some names have '_' for example local_PC instead of localPC.
The problem I have is that I need it to be local_PC and when a call is made to my app they send that field and I can't change it, but sonar launches me error because it must be localPC
Is there any way I can control it by Jackson?
realizing #jsonproperty only allows me to change the names in the output but not in the input of the controller
#Data
#JsonInclude(JsonInclude.Include.NON_NULL)
#AllArgsConstructor
public class Example{
private String local_PC;
}
Use #JsonAlias
#JsonAlias is introduced in Jackson 2.9 release. #JsonAlias defines one or more alternative names for a property to be accepted during deserialization i.e. setting JSON data to Java object. But at the time of serialization i.e. while getting JSON from Java object, only actual logical property name is used and not alias. #JsonAlias
#JsonAlias({"local_PC", "localPC"})
private String local_PC;

How to fix 'com.fasterxml.jackson.databind.JsonMappingException: Problem deserializing property' error

I am converting date string in format yyyy-MM-dd to com.datastax.driver.core.LocalDate using object mapper and save that data into Cassandra in my java spring boot project.
The type of my column in Cassandra is date and i am using com.datastax.driver.core.LocalDate in my java class.
But while converting map to my java class it throws error "com.fasterxml.jackson.databind.JsonMappingException: Problem deserializing property 'employeedateofbirth' (expected type: [simple type, class com.datastax.driver.core.LocalDate]; actual type: java.time.LocalDate), problem: argument type mismatch".
I am not using java.time.LocalDate still it gives me argument mismatch error.
I even tried registering module by using the following code
ObjectMapper oMapper = new ObjectMapper().registerModule(new ParameterNamesModule()).registerModule(new Jdk8Module()).registerModule(new JavaTimeModule());
oMapper.findAndRegisterModules();
I have also tried some configuration for object mapper like
oMapper.configure(MapperFeature.USE_GETTERS_AS_SETTERS, false) ;
Even used annotation on my attribute:
#JsonDeserialize(using = LocalDateDeserializer.class)
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
#JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate employeedateofbirth;
But nothing seems to work for me. Any help would be appreciated.
Thanks in advance.
As the error message indicates, there is a type mismatch. Change this in your target class:
import com.datastax.driver.core.LocalDate;
into:
import java.time.LocalDate;

Deserialize millisecond timestamp to java.time.Instant

I'm attempting to read a JSON file using Jackson and store one of the fields that is stored as a epoch milliseconds as a Java Instant, however deserialization is not behaving as expected.
Here is what I am seeing when trying to read the timestamp:
1503115200000
Jackson is setting the Instant field as +49601-10-28T16:00:00Z.
This appears to be occurring because Jackson's default is to read the timestamp with Instant.ofEpochSecond(Long l) instead of Instant.ofEpochMilli(Long l).
Is there a way to set the Jackson ObjectMapper to use the ofEpochMilli method instead? This is what I currently have for my ObjectMapper:
ObjectMapper om = new ObjectMapper()
.registerModule(new JavaTimeModule())
.configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false)
.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.setSerializationInclusion(Include.NON_NULL);
Note
If I change the input JSON to ISO date such as 2017-08-19T04:00:00Z or to epoch seconds such as 1503115200 the Instant field is able to set properly.
Unfortunately the JSON input must be epoch milliseconds e.g. 1503115200000.
Solution was to add .configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS, false) to the ObjectMapper. Complete ObjectMapper looks like:
ObjectMapper om = new ObjectMapper()
.registerModule(new JavaTimeModule())
.configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false)
.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS, false)
.setSerializationInclusion(Include.NON_NULL);
For those who use Spring Boot and experience this issue during rest request deserialization - provide this in the application.properties:
spring.jackson.deserialization.read-date-timestamps-as-nanoseconds=false
#JB Nizet answer is correct, but since Spring Boot has it's own ObjectMapper inside - you need to configure this value in the properties.
From https://github.com/FasterXML/jackson-modules-java8/blob/master/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/JavaTimeModule.java:
The more ambiguous integer types are read as fractional seconds without a decimal point if {#code READ_DATE_TIMESTAMPS_AS_NANOSECONDS} is enabled (it is by default), and otherwise they are read as milliseconds.
So you need to disable READ_DATE_TIMESTAMPS_AS_NANOSECONDS.
A more flexible solution could be to write your own convertor class, if you don't want to edit or create an object mapper.
public class LongToInstantConverter extends StdConverter<Long, Instant> {
public Instant convert(final Long value) {
return Instant.ofEpochMilli(value);
}
}
You just need to add the annotation in your class
#JsonDeserialize(converter = LongToInstantConverter.class)
private Instant sentDate;

Unmarshalling multiple XML elements to one field using JAXB

I have an XML file in format described below. I'm currently using JAXB to unmarshall the XML elements (event) to Java objects (Event). All properties work fine, except one...
As you can see in the XML, the date element stores only the date and the time stores only the time of the event. I would like to combine these two XML elements into one LocalDataTime field named time, with appropriate getters and setters.
XML:
...
<event>
...
<date>2014-02-19</date>
<time>2000-01-01T14:17:00Z</time>
...
</event>
...
Desired Java Object:
public class Event {
...
// Returns a combination of the date and time stored in the XML
public LocalDateTime getDateTime() {
return dateTime;
}
...
}
My first thought was to use a XMLAdapter but it seems that this only allows me to map 1 XML element to 1 Java object.
After that I tried to implement this by overriding the setters setTime and setDate. These setters would each just change the time or date of the stored time.. But I wasn't able to get it working and it also seemed a quite ugly way to do this.
I already read the following JAXB convert multiple attributes to one type while unmarshalling. But for this project, I would rather not use an external library like MOXy.
Does anyone know how to do this using solely JAXB in a clean way?
You could define in your Event object lifecycle methods:
void afterUnmarshal(Unmarshaller unm, Object parent) {
localDateTime = ....
};
boolean beforeMarshal(Marshaller mar) {
date = localDateTime.toDate();
....
};
to construct the LocalDateTime property after unmarshalling (using the date and time values), and prepare date/time fields before marshalling using the current LocaLDateTime value.
You would still need the time/date fields to match the xml and the localDateTime field must be XmlTransient. So it is not so different from the set/getter approach but probably more "clean".

Categories