SpringBoot, Hibernate and REST - date format in JSON - json

I have a problem with date format in JSON response generated in REST project (SpringBoot+Hibernate).
When I call function I got JSON like this:
"rezerwacjaDataOd": 1535580000000,
"rezerwacjaDataDo": 1535839200000,
"rezerwacjaGodzOd": "14:00:00",
"rezerwacjaGodzDo": "12:00:00"
my entity:
private Date rezerwacjaDataOd;
private Date rezerwacjaDataDo;
private Time rezerwacjaGodzOd;
private Time rezerwacjaGodzDo;
It's Date from java.sql and Time from java.sql too
My controller:
#RestController
#CrossOrigin
#RequestMapping("api/rezerwacja")
#Api
public class RezerwacjaController {
...
#GetMapping(value = "/getRezerwacjaById")
public #ResponseBody
Rezerwacja getRezerwacjaById(Integer id) {
return rezDao.findOne(id);
}
...
Why Time is in "12:00:00" format, but Date in 1535580000000 format?
How to make Date to be in "yyyy-MM-dd" format?

You should do two things
add spring.jackson.serialization.write-dates-as-timestamps:false in your application.properties this will disable converting dates to timestamps and instead use a ISO-8601 compliant format
You can then customize the format by annotating the getter method of you dateOfBirth property with #JsonFormat(pattern="yyyy-MM-dd")

The differences in the way hibernate persists the date/time objects in the database have to do with the way these objects are used.
Per the documentation Time is a thin wrapper around Date that allows the underlying JPA provider to save the date object using the convention your noticed.
On the other hand, the Date object you pass in is converted directly to a timestamp and gets saved this way.
In both cases you can retrieve the value in question and serialize over to the desired format (with ISO-8601 being the best).
Another solution, other than the one mentioned above, is to create a custom serializer to do this.
A simple implementation would be:
public class Iso8601Serializer extends StdSerializer<Date> {
private static final String ISO_8601_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
public Iso8601Serializer() {
this(null);
}
public Iso8601Serializer(Class clazz) {
super(clazz);
}
#Override
public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
throws IOException {
if (date == null) {
jsonGenerator.writeNull();
} else {
jsonGenerator.writeString(DateFormatUtils.format(date, ISO_8601_FORMAT));
}
}
}
Also (and this is a personal thing), I would advise in using plain Date objects to store dates and futhermore, have the respective fields annotated as #Temporal.

Related

Jackson custom date formatter

I want to serialize a Json object with a date field, into a Pojo.
For example
{
date: <some arbitrary date format>
}
to
#JsonFormat(???)
LocalDateTime dateTime;
The problem is that the date can be in any number of formats. For example, maybe just a year, just a date, or date and time. And even those can be in multiple formats.
2021
2021-5-23
2021/05/23
2021/05/23 02:07
2021-05-23 02:07:53
2021/05/23 02:07:50.567
I've already written a parser that has a list of formats and will loop through them until it finds one that works. How can I write a custom Jackson serializer or formatter that uses that code and can take a string from a Json object and properly convert it to a Date or LocalDateTime object in the Pojo?
If it makes a difference, I'm also doing this in SpringBoot
I think, you are talking about DEserializing? If so, you can create your own JsonDeserializer that supports LocalDateTime and register it with JsonComponent. This would look like this:
(The "input" can be accessed via p.getText())
#JsonComponent
public class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
#Override
public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
String stringValue = p.getText();
// Use your parser instead of this
DateTimeFormatter formatter = new DateTimeFormatterBuilder().appendPattern("yyyy[-MM-dd HH:mm:ss]")
.parseDefaulting(ChronoField.MONTH_OF_YEAR, 1)
.parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0).toFormatter();
return LocalDateTime.parse(stringValue, formatter);
}
}

Serialize and Deserialize Date to Bean in Java

I've to projects: an API Rest named Usuario and a JEE Application named store. Store gets the information to work from the Usuario API.
All the information is sent and received using MediaType.APPLICATION_JSON_TYPE.
In the API there's the Evento entity, which has the next attribute: Date fecha.
In the JEE Application there's the Evento bean, which has the next attribute: Date fecha.
When the API sends any object of the Evento class, it sends the Date with the format yyyy-MM-dd HH:mm: {"Date":"2017-12-06 23:02"}.
The JEE Application uses Client and WebTarget to use de API and get the information.
The problem here is that fecha (of type Date) in the evento object is null. It's not well-deserialized.
I can not use Spring on the JEE Application.I'm using JAX-RS in the store application.
Could you help me, please?
Let's clarify some facts, first. (I'm not 100% sure, though.)
There is no standard rule for representing dates or instants in JSON.
Some uses millisecons while others some formatted strings such as ISOZ.
There is no standard rule for serializing/deserializing in Java EE.
It only works as underlying message provider for application/json.
Note that JSON-B may not available in the spec yet.
Let's say we have this entity.
// I really want to know what Evento/fatcha means.
// Are they Italian words?
#XmlRootElement
#Entity
public class Evento implements Serializable {
public Date getFetcha() {
return ofNullable(fetcha)
.map(v -> new Date(v.getTime()))
.orElse(null);
}
public void setFetcha(final Date fetcha) {
this.fetcha = ofNullable(fetcha)
.map(v -> new Date(v.getTime()))
.orElse(null);
}
#XmlElement
#Temporal(TIMESTAMP)
private Date fetcha;
}
Now we can't help but depends on the underlying MessageReader or MessageWriter for serializing/deserializing in application/json.
Jackson will work in some way while MOXy will work in its way.
The only way to push client is that, they should use the format exactly as the same format when they GET the resource.
One way that I use is using another property for unified formatting/parsing.
#XmlRootElement
#Entity
public class Evento implements Serializable {
// ...
#JsonProperty
#JsonbProperty
#XmlElement
public String getFetchaIsoz() {
return ofNullable(getFetcha())
.map(Date::toInstant)
.map(DateTimeFormatter.ISO_INSTANT:format)
.orElse(null);
}
public void setFetchaIsoz(final String fetchaIsoz) {
setFetcha(ofNullable(fetchaIsoz)
.map(DateTimeFormatter.ISO_INSTANT:parse)
.map(Instant::from)
.map(Date::from)
orElse(null));
}
#JsonIgnore // just in case, e.g. Spring?
#JsonbTransient
#XmlTransient
#Temporal(TIMESTAMP)
private Date fetcha;
}
Not we have an alternative property which guarantees to work in specific format.

asmx change DateTime format

Haven't found anything out there after doing some searches.
I'm wondering if
there's any way to specify how an asmx WebService serializes a DateTime to json?
Can this be setup as a webconfig setting? Or am I stuck with the /Date(millis)/ format?
I don't know any way to do this, but one solution is to use a double value (or possibly long if you're not interested in fractional milliseconds) which contains the total number of milliseconds since the UnixEpoch. You could use a helper class something like:
public static class DateTimeExtensions
{
public static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1);
public static double ToUnixTime(this DateTime dateTime)
{
return (dateTime - UnixEpoch).TotalMilliseconds;
}
...
}

Configure ServiceStack.Text to throw on invalid JSON

Is it possible to make the ServiceStack.Text library throw when attempting to deserialize invalid JSON. By default it looks as if invalid JSON is just ignored, so that the result object contains null values.
When I attempt to deserialize this json (a " is missing after MongoConnectionString)
{
"MongoDb": {
"MongoConnectionString:"mongodb://localhost:27017/x",
"MongoDatabase":"x",
"MongoSafeModeEnabled":true,
"MongoSafeModeFSync":true,
"MongoSafeModeWriteReplicationCount":
"MongoSafeModeWriteTimeout":"00:00:00"
},
by doing this: JsonSerializer.DeserializeFromString(json);
where
public class Configuration {
public class MongoDbSettings
{
public string MongoConnectionString {get;set;}
public string MongoDatabase {get;set;}
public bool MongoSafeModeEnabled {get;set;}
public bool MongoSafeModeFSync {get;set;}
public int MongoSafeModeWriteReplicationCount {get;set;}
public TimeSpan MongoSafeModeWriteTimeout {get;set;}
}
}
I get a Configuration object where MongoDbSettings is null. I would prefer to get an exeception in this case. Is this possible?
At the moment the ServiceStack serializers are optimized for resilience, i.e. deserialize as much as possible without error.
I'd recommend adding some of your own validation checking post serialization to work out which fields weren't deserialized correctly.
You could also submit a pull-request to the ServiceStack.Text project that supports an opt-in flag (i.e. on JsConfig) to change the behavior to throw exceptions.

Set Jackson Timezone for Date deserialization

I'm using Jackson (via Spring MVC Annotations) to deserialize a field into a java.util.Date from JSON. The POST looks like - {"enrollDate":"2011-09-28T00:00:00.000Z"}, but when the Object is created by Spring & Jackson it sets the date as "2011-09-27 20:00:00".
How can I set the proper timezone in Jackson?
Or if that is not the problem, how do I send EST from the JSON message?
Javascript/jQuery:
var personDataView = { enrollDate : new Date($("#enrollDate").val()),
//...other members
};
$.postJSON('/some/path/', personDataView, function(data){
//... handle the response here
});
JSON Message:
{"enrollDate":"2011-09-28T00:00:00.000Z"}
Spring Controller:
#RequestMapping(value="/", method=RequestMethod.POST)
public #ResponseBody String saveProfile(#RequestBody personDataView persondataView, HttpServletRequest request)
{
//...dataView has a java.util.Date enrollDate field
//...other code
}
In Jackson 2+, you can also use the #JsonFormat annotation:
#JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone="America/Phoenix")
private Date date;
If it doesn't work this way then try wrapping Z with single quotes, i.e. pattern="yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
Have you tried this in your application.properties?
spring.jackson.time-zone= # Time zone used when formatting dates. For instance `America/Los_Angeles`
If you really want Jackson to return a date with another time zone than UTC (and I myself have several good arguments for that, especially when some clients just don't get the timezone part) then I usually do:
ObjectMapper mapper = new ObjectMapper();
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
dateFormat.setTimeZone(TimeZone.getTimeZone("CET"));
mapper.getSerializationConfig().setDateFormat(dateFormat);
// ... etc
It has no adverse effects on those that understand the timezone-p
I am using Jackson 1.9.7 and I found that doing the following does not solve my serialization/deserialization timezone issue:
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSSZ");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
objectMapper.setDateFormat(dateFormat);
Instead of "2014-02-13T20:09:09.859Z" I get "2014-02-13T08:09:09.859+0000" in the JSON message which is obviously incorrect. I don't have time to step through the Jackson library source code to figure out why this occurs, however I found that if I just specify the Jackson provided ISO8601DateFormat class to the ObjectMapper.setDateFormat method the date is correct.
Except this doesn't put the milliseconds in the format which is what I want so I sub-classed the ISO8601DateFormat class and overrode the format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition)
method.
/**
* Provides a ISO8601 date format implementation that includes milliseconds
*
*/
public class ISO8601DateFormatWithMillis extends ISO8601DateFormat {
/**
* For serialization
*/
private static final long serialVersionUID = 2672976499021731672L;
#Override
public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition)
{
String value = ISO8601Utils.format(date, true);
toAppendTo.append(value);
return toAppendTo;
}
}
Looks like older answers were fine for older Jackson versions, but since objectMapper has method setTimeZone(tz), setting time zone on a dateFormat is totally ignored.
How to properly setup timeZone to the ObjectMapper in Jackson version 2.11.0:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setTimeZone(TimeZone.getTimeZone("Europe/Warsaw"));
Full example
#Test
void test() throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.findAndRegisterModules();
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
JavaTimeModule module = new JavaTimeModule();
objectMapper.registerModule(module);
objectMapper.setTimeZone(TimeZone.getTimeZone("Europe/Warsaw"));
ZonedDateTime now = ZonedDateTime.now();
String converted = objectMapper.writeValueAsString(now);
ZonedDateTime restored = objectMapper.readValue(converted, ZonedDateTime.class);
System.out.println("serialized: " + now);
System.out.println("converted: " + converted);
System.out.println("restored: " + restored);
Assertions.assertThat(now).isEqualTo(restored);
}
`
Just came into this issue and finally realised that LocalDateTime doesn't have any timezone information. If you received a date string with timezone information, you need to use this as the type:
ZonedDateTime
Check this link
Your date object is probably ok, since you sent your date encoded in ISO format with GMT timezone and you are in EST when you print your date.
Note that Date objects perform timezone translation at the moment they are printed. You can check if your date object is correct with:
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
cal.setTime(date);
System.out.println (cal);
I had same problem with Calendar deserialization, solved extending CalendarDeserializer.
It forces UTC Timezone
I paste the code if someone need it:
#JacksonStdImpl
public class UtcCalendarDeserializer extends CalendarDeserializer {
TimeZone TZ_UTC = TimeZone.getTimeZone("UTC");
#Override
public Calendar deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonToken t = jp.getCurrentToken();
if (t == JsonToken.VALUE_NUMBER_INT) {
Calendar cal = Calendar.getInstance(TZ_UTC);
cal.clear();
cal.setTimeInMillis(jp.getLongValue());
return cal;
}
return super.deserialize(jp, ctxt);
}
}
in JSON model class just annotate the field with:
#JsonDeserialize(using = UtcCalendarDeserializer.class)
private Calendar myCalendar;
For anyone struggling with this problem in the now (Feb 2020), the following Medium post was crucial to overcoming it for us.
https://medium.com/#ttulka/spring-http-message-converters-customizing-770814eb2b55
In our case, the app uses #EnableWebMvc and would break if removed so, the section on 'The Life without Spring Boot' was critical. Here's what ended up solving this for us. It allows us to still consume and produce JSON and XML as well as format our datetime during serialization to suit the app's needs.
#Configuration
#ComponentScan("com.company.branch")
#EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(0, new MappingJackson2XmlHttpMessageConverter(
new Jackson2ObjectMapperBuilder()
.defaultUseWrapper(false)
.createXmlMapper(true)
.simpleDateFormat("yyyy-mm-dd'T'HH:mm:ss'Z'")
.build()
));
converters.add(1, new MappingJackson2HttpMessageConverter(
new Jackson2ObjectMapperBuilder()
.build()
));
}
}