is it possible to use Jackson with nanoseconds value
Asked Answered
M

1

6

I have had the worst imaginable time to work with nanoseconds while parsing from object to json and vice verse. I created the simplest possible use of Jackson and I can't get the nanoseconds. Below is my demonstration. There are three important statement in fasterxml FAQ that are related to my case. The first two give me the trick to make work and the third tells not to use sql.Date but sql.timestamp is "son" of sql.Date.

The issues are:

Firstly, mapper.configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS,true) and @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss.SSSSSS") take no effect at all. I can place false, true, even not use mapper.configure, use or not @JsonFormat and the result will be the same,

Secondly, if I try only Deserialize, I mean, enter the value 2015-01-07 11:37:52.390452 in theuser.json and only run mapper.readValue I will get the value 2015-01-07 11:44:22.452, so I miss the exact value because Jacskon round up.

from http://wiki.fasterxml.com/JacksonFAQDateHandling

1 - Feature.WRITE_DATES_AS_TIMESTAMPS, false); which disable use of timestamps (numbers), and instead use a [ISO-8601 ]-compliant notation, which gets output as something like: "1970-01-01T00:00:00.000+0000".

2 - You can configure formatting by passing a java.text.DateFormat

3 - Please do NOT use java.sql.Date, ever!

//the pojo

package com.jim.core;


import java.sql.Timestamp;
import com.fasterxml.jackson.annotation.JsonFormat;


public class User {

       @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss.SSSSSS")
       private Timestamp tsFirstTry;

       private Timestamp tsSecondTry;   

       @Override
       public String toString() {
              return "User [tsFirstTry=" + tsFirstTry + ", tsSecondTry=" + tsSecondTry + "]";
       }

       //getters and setters
}

//main class

package com.jim.core;


import java.io.File;
import java.io.IOException;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

public class Main {
    public static void main(String[] args) {

          User user = new User();
          user.setTsFirstTry(Timestamp.valueOf(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS").format(new Date())));

          user.setTsSecondTry(Timestamp.valueOf(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS").format(new Date())));


          System.out.println("firstTryValue = "+ Timestamp.valueOf(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS").format(new Date())));

          System.out.println("secondTryValue = "+ Timestamp.valueOf(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS").format(new Date())));

          ObjectMapper mapper = new ObjectMapper();

          try {
                 //mapper.setTimeZone(TimeZone.getTimeZone("UTC"));
                 //mapper.configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS,true);

                 //mapper.configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS,true);

                 mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS"));

                 //Serialization - saving the created objects in the sequence of bytes.
                 mapper.writeValue(new File("c:\\temp\\user.json"), user);
                 System.out.println("Serialized Outcome = " + mapper.writeValueAsString(user));

                 //Deserialization - Retrieving those saved bytes into the form of original object.
                 user = mapper.readValue(new File("c:\\temp\\user.json"), User.class);
                 System.out.println("Deserialized Outcome = " + user);


          } catch (IOException e) {
                 e.printStackTrace();
          }
    }
}

//pom (relevant part only)

<properties>
   <java-version>1.6</java-version>
   <jackson.databind-version>2.2.3</jackson.databind-version>
</properties>
<dependencies>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>${jackson.databind-version}</version>
    </dependency>

//Console

firstTryValue = 2015-01-08 11:31:53.000773
secondTryValue = 2015-01-08 11:31:53.000773

Serialized Outcome = {"tsFirstTry":"2015-01-08 17:31:53.000000","tsSecondTry":"2015-01-08 11:31:53.000000"}

Deserialized Outcome = User [tsFirstTry=2015-01-08 11:31:53.0, tsSecondTry=2015-01-08 11:31:53.0]
Muse answered 8/1, 2015 at 18:36 Comment(0)
T
7

Yes, it is possible to use Jackson with nanoseconds value; to preserve nanoseconds in Java 8 you can use java.util.Date or java.sql.Timestamp (assuming you haven't disabled Jackson configuration DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS and SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, which are enabled by default).

Before Java 8 you can use a java.math.BigDecimal to store the number of days since epoch plus fractional time of day. Or just keep the value in its String representation. Java 7 and prior versions don't keep nanoseconds in dates, only milliseconds. So if you convert the value to a java.util.Date or one of its subclasses like java.sql.Timestamp, you will have precision only to milliseconds. Therefore java.text.DateFormat is not useful here if you are using it to convert to any kind of java Date in a pre-Java 8 environment.

Here is a further discussion of nanoseconds in java Timestamps: java.sql.Timestamp way of storing NanoSeconds

Threecornered answered 8/1, 2015 at 19:16 Comment(5)
Let us say that I have a legacy application with 100 pojo classes in domain layer and I do need to use them to serialize and deserialize to Json/Object respectvely (please accept this scenario). All of them have at least one sql.timestamp attribute. What is the Jackson expert team recomendation? Should I change the code from Timestamp to String or BigDecimal? Is that the real solution proposed from Jackson specification when facing time with nanoseconds? Timestamp.valueOf(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS") prints nanoseconds. Must realy use String in pojo for that case?Muse
Addtionally, should I consider mapper.configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS,true) / mapper.configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS,true) as not feasible possibility. I read some examples taking advantage of this but I definitely couldn't make a simple hello word takes effect by this feature.Muse
Apologies, it appears Java 8 has added support for nanoseconds in its Date classes. I have updated my answer to reflect this. Hopefully your application is running on Java 8 so you can take advantage of this enhancement.Threecornered
I changed from Java 6 to Java 8 but the outcome is absolutely the same.Muse
hmm, I have read the documentation here docs.oracle.com/javase/9/docs/api/java/util/Date.html for java.util.Date. The docs says : The class Date represents a specific instant in time, with millisecond precision.. So this should not answer the question, it will not have nanoseconds value precissions.Clavicorn

© 2022 - 2024 — McMap. All rights reserved.