I am having problems consistently serializing and deserializing a Joda DateTime from java to json and back again using Spring Boot and Jackson-databind 2.5.2. My pom.xml looks like this.
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-joda</artifactId>
<version>2.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.2.1.RELEASE</version>
</dependency>
When I serialize the DateTime object I get an integer representing the DateTime. Not what I expected actually, but fine. BUT when I go to save my object back I get the following error...
Failed to convert property value of type 'java.lang.String' to required type 'org.joda.time.DateTime' for property 'endTime';
nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.lang.String to type org.joda.time.DateTime for value '1428600998511'
For some reason it is serializing it to an integer but then deserializing it as if it's a string, which it is not. I also tried setting the endTime = new Date(intValue) before calling the rest service and that also failed trying to convert a string like 'Tue Apr 28 2015 00:00:00 GMT-0700 (PDT)' to a DateTime.
What am I doing wrong?
UPDATE:
Here is the JSON that I GET and that I try to immediately POST right back.
{
id: 4,
username: "",
name: "eau",
email: "aoue",
verbatimLocation: null,
latitude: null,
longitude: null,
startTime:null,
endTime: 1429034332312,
description: "ueoa",
media: [ ],
timeSubmitted: 1428600998000,
status: null,
submissionid: null
}
For a more re-usable mechanism, you can create a JsonSerializer:
/**
* When passing JSON around, it's good to use a standard text representation of
* the date, rather than the full details of a Joda DateTime object. Therefore,
* this will serialize the value to the ISO-8601 standard:
* <pre>yyyy-MM-dd'T'HH:mm:ss.SSSZ</pre>
* This can then be parsed by a JavaScript library such as moment.js.
*/
public class JsonJodaDateTimeSerializer extends JsonSerializer<DateTime> {
private static DateTimeFormatter formatter = ISODateTimeFormat.dateTime();
#Override
public void serialize(DateTime value, JsonGenerator gen, SerializerProvider arg2)
throws IOException, JsonProcessingException {
gen.writeString(formatter.print(value));
}
}
Then you can annotate your get methods with:
#JsonSerialize(using = JsonJodaDateTimeSerializer.class)
This gives you consistent formatting throughout your application, without repeating text patterns everywhere. It's also timezone aware.
In the end I was able to do as beerbajay said and use ...
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss.SSSZ")
... to serialize my date. I did, though, end up going back to using a Long instead of a DateTime because dealing with the date on the javascript side was too troublesome. Finding a pattern that worked for jquery datepicker, joda DateTime, and for postgresql proved to be too much work for the little time I had.
Related
I've got an exposed DAO-style setup with a datetime-column and want to access it after I received the row from the schema.
Table:
object Entries : IntIdTable() {
val date = datetime(name = "date").nullable()
}
Entity:
class Entry(id: EntityID<Int>) : IntEntity(id) {
companion object : EntityClass<Entry>(Entries)
var date by Entries.date
}
DAO:
object DB {
private val pool = BasicDataSource()
private val database: Database
init {
pool.url = "jdbc:mysql://127.0.0.1:3306/test"
pool.driverClassName = "com.mysql.cj.jdbc.Driver"
pool.username = "user"
pool.password = "secret"
pool.minIdle = 5
pool.maxIdle = 10
database = Database.connect(pool)
}
infix fun <T> query(block: DB.() -> T): T {
return transaction {
block.invoke(this#DB)
}
}
}
Main:
fun main() {
val entry = DB.query {
Entry.all().single() // There is only one row at this point
}
println(entry.date) //! Error is thrown on this line
}
It throws a java.lang.IllegalStateException: No transaction in context.. When I move the println into the transaction, it works as expected. If I change the type of the column to varchar and expect a string it works in both cases.
How can I get the date outside of the transaction? I think it worked a few versions back, but I'm unsure.
I solved the problem by updating the Exposed dependencies to the latest version, which has a new naming pattern.
Old dependencies:
<dependency>
<groupId>org.jetbrains.exposed</groupId>
<artifactId>exposed</artifactId>
<version>0.17.14</version>
</dependency>
New dependencies:
<dependency>
<groupId>org.jetbrains.exposed</groupId>
<artifactId>exposed-core</artifactId>
<version>0.36.1</version>
</dependency>
<dependency>
<groupId>org.jetbrains.exposed</groupId>
<artifactId>exposed-dao</artifactId>
<version>0.36.1</version>
</dependency>
<dependency>
<groupId>org.jetbrains.exposed</groupId>
<artifactId>exposed-jdbc</artifactId>
<version>0.36.1</version>
</dependency>
<dependency>
<groupId>org.jetbrains.exposed</groupId>
<artifactId>exposed-java-time</artifactId>
<version>0.36.1</version>
</dependency>
Looks a little more bloated, but I guess it's cleaner to not have every possible module of Exposed loaded every time.
Side note:
I needed to update some imports (IntIdTable, EntityId, etc. and datetime are now located in new packages) and replace Entries.date eq null by Entries.date.isNull() in queries.
My Jackson is configured with
disabled DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE (jsr310 is also in place)
Thus de/serializing ZonedDateTime is at least correctly preserving the offset part. But the serialization of a ZonedDateTime is removing the zone. Is there a configuration to preserve the zone info?
Or in other words how to get this test green?:
#Test
public void zonedDateTime() throws JsonProcessingException {
ZonedDateTime time = random.nextObject(ZonedDateTime.class);
String string = om.writeValueAsString(time);
ZonedDateTime timeReadBack = om.readValue(string, ZonedDateTime.class);
assertThat(timeReadBack, is(time));
}
If not possible only with configuration is there code for correct de/serilization?
I'm building a custom function for BW6 that should parse ISO 8601 formatted string to a dateTime object.
So far I have built this function:
#XPathFunction(helpText = "Parse a ISO 8601 datetime string",
parameters = {
#XPathFunctionParameter(name = "isoDateTimeString", optional = false)
},
returnType = "dateTime")
public Date parseIsoDateTime(String isoDateTimeString) throws ParseException {
StringBuilder dateFormatBuilder = new StringBuilder();
dateFormatBuilder.append("yyyy-MM-dd'T'HH:mm:ss");
if (isoDateTimeString.contains(".")) {
dateFormatBuilder.append(".SSS");
}
DateFormat dateFormat = new SimpleDateFormat(dateFormatBuilder.toString());
return dateFormat.parse(isoDateTimeString);
}
When I run this function an exception is thrown by BW
09:02:42.412 ERROR [bwEngThread:In-Memory Process Worker-1] com.tibco.bw.core - TIBCO-BW-CORE-500050: The BW process [demo.parse-datetime.module.Process] instance faulted, JobId [bw0a100], ProcessInstanceId [bw0a100], ParentProcessInstanceId [-], Module [demo.parse-datetime.module:1.0.0.qualifier], Application [demo.parse-datetime:1.0].
<CausedBy> TIBCO-BW-CORE-500058: Activity [SetDateTimeValue] XML related error.
<CausedBy> com.tibco.pvm.dataexch.xml.util.exceptions.PmxException: PVM-XML-106017: Expression Evaluation Error: 'bxcom:parseIsoDateTime($dateTimeAsString)'
<CausedBy> java.lang.ClassCastException: java.util.Date cannot be cast to com.tibco.xml.data.primitive.XmlAtomicValue
So how can I return an XML dateTime object from a custom xslt function in BW6.
You need to create a Plug-in Project to allow Custom XPath Function. (You will need the ActiveMatrix BusinessWorks™ Plug-in Development Kit)
Create Custom XPath Functions
If you want to use them Design time, you'll need to export the Plugin Project and include the jar as a dependency.
Using Custom XPath Function at Design Time and Run Time
Now that NHibernate 5 is out, I was wondering what's the recommended approach to load dates as local dates when they're persisted as datetime2 in a SQL Server 2016 database. Until now, dates were interpreted as local (so, the properties ended up with the correct values), but now, the behavior has changed and I'm unable to use the LocalDateTime.
Unfortunately, my donmain's behavior relies in getting those dates automatically loaded as local dates...
Any pointers on how to solve this?
Thanks,
Luis
Since I didn't found a way to disable this behavior, I've ended up creating a new type that will simply adjust the format of the data (considering always that it was saved in local format):
public class LocalDateTimeTypeNoThrow : LocalDateTimeType {
public LocalDateTimeTypeNoThrow() {
}
public LocalDateTimeTypeNoThrow(DateTimeSqlType sqlType) : base(sqlType) {
}
public override string Name => "LocalDateTimeNoThrow";
public override void Set(DbCommand st, object value, int index, ISessionImplementor session) {
var dateValue = (DateTime) value;
//removed throwing from here
st.Parameters[index].Value = AdjustDateTime(dateValue);
}
}
If there's a better way, please let me know.
Luis
I'm using SpringFox (Swagger) to document my REST APIs. One of the nice features in Swagger is to show example model and the data type format.
Now, I configured my APIs to produce and consume dates in milliseconds but when I'm looking at the sample JSON model the date format is following: "2015-09-21T00:51:32.617Z". See screenshot below. Is it possible to tell SpringFox (Swagger) how to format the date properly?
You could try:
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.apiInfo(getApiInfo())
.directModelSubstitute(YourDateTimeClass.class, Integer.class);
Basically you are telling Swagger to replace the date class with an Integer which could represent the milliseconds.
This is related to the underlying Jackson serialiser. You have to set the correct date format for it otherwise by default it's using timestamps.
Here is an example configuration
#Configuration
public class WebConfiguration extends WebMvcConfigurerAdapter {
#Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
for (HttpMessageConverter<?> converter : converters) {
if (converter instanceof MappingJackson2HttpMessageConverter) {
MappingJackson2HttpMessageConverter jsonMessageConverter = (MappingJackson2HttpMessageConverter) converter;
ObjectMapper objectMapper = jsonMessageConverter.getObjectMapper();
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
break;
}
}
}
}
This way it will use an ISO-8601 date-time format. Here is another blog post that explains how to set your own preferred date format: http://yysource.com/2016/06/change-default-date-for-jackson-in-spring-boot-application/
I got a similar problem as yours, and I resolved it by adding the following config in my Spring Boot's application.properties file:
spring.jackson.date-format=com.fasterxml.jackson.databind.util.ISO8601DateFormat