I have Match class and field Date start. My goal is get start as timestamp. I use Spring, AngularJs, and jackson as json converter.
Spring Controller:
#RequestMapping(value = "/web2/getMatch", method =RequestMethod.POST)
public #ResponseBody Match getPicksHistory() {
PickDAO pd = new PickDAO();
return pd.getMatch();
}
On AgularJS controler:
var res = $http.post(urlSer.url+"web2/getMatch");
res.success(function(data, status, headers, config) {
// now returns data.start = "Aug 8, 2015 7:00:00 PM"
// My goal is get as timestamp
});
I assume that by 'timestamp' you mean a numeric timestamp as opposed to a textual representation. You can use a custom ObjectMapper:
#Component
#Primary
public class CustomObjectMapper extends ObjectMapper {
public CustomObjectMapper() {
configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true);
}
}
I use jackson-databind:2.6.1 and JsonSerializer
#Component
public class JsonDateSerializer extends JsonSerializer<Date>{
#Override
public void serialize(Date date, JsonGenerator gen,
SerializerProvider serializers) throws IOException,
JsonProcessingException {
gen.writeNumber(date.getTime());
}
}
Related
I have a scenario which I am not sure how to google, so I'd open a question myself. If its a duplicate please link it here so I can give that question credit.
Anyway,
I have a Java Object with fields that I have the following annotations:
#JsonSerialize(using = CustomSerializer.class)
#JSonDeserialize(using = CustomDeserializer.class)
private Date systemDate;
The business dictates that certain systemDate values in different database have different time zones (not sure why they did not standardize to UTC).
Here is an example of my CustomSerializer.java:
#Override
public void serialize(Date, value, JsonGenerator gen, SerializerProvider arg2) throws IOException {
SimpleDateFormat formatter = new SimpleDateFormat("dd-MMM-yy");
formatter.setTimeZone(TimeZone.getTimeZone("CET"));
if (value == null) {
gen.writeNull();
} else {
gen.writeString(formatter.formate(value.getTime()));
}
}
Instead of creating a new serializer class per timezone, is there a way to pass the timezone argument(s) to this class (and also my Deserializer class)?
You need to create a custom annotation as below:
#Target({ ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
#JacksonAnnotation
public #interface DateSerialize {
String timeZone();
}
Now in the field systemDate add this new annotation by passing the timezone
#DateSerialize(timeZone = "UTC")
#JsonSerialize(using = CustomSerializer.class)
#JSonDeserialize(using = CustomDeserializer.class)
private Date systemDate;
Your serializer class should implement ContextualSerializer this would allow us to get the BeanProperty from where we could get the annotation details.
public class DateSerializer extends JsonSerializer<Date> implements ContextualSerializer {
String timeZone;
public DateSerializer() {
this("UTC");
}
public DateSerializer(String tz) {
timeZone = tz;
}
#Override
public void serialize(Date value, JsonGenerator gen, SerializerProvider provider) throws IOException {
SimpleDateFormat formatter = new SimpleDateFormat("dd-MMM-yy");
formatter.setTimeZone(TimeZone.getTimeZone(timeZone));
if (value == null) {
gen.writeNull();
} else {
gen.writeString(formatter.format(value.getTime()));
}
}
#Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
DateSerialize annotation = property.getAnnotation(DateSerialize.class);
String tz = (annotation == null) ? "UTC" : annotation.timeZone();
return new DateSerializer(tz);
}
}
Similarly you could create a deserializer by implementing ContextualDeserializer.
I am using SpringBoot 2.2. date format is "validFrom": "2013-12-31T18:30:00.000+0000"
But I want in number format (like 1411471800000).
In my entity I included the below code snippet which worked in Number format.
#JsonProperty("updDate")
**#JsonFormat(shape = JsonFormat.Shape.NUMBER)**
private Date updDate;
To achieve that, I will have to do in all my entities.Is there a way where I can make one change and it will apply for all date formats.
Please advise
You can use custom Serializer for Date type which will used to serialize Date type.
public class DateSerializer extends StdSerializer<Date> {
private static final long serialVersionUID = -7880057299936791237L;
public JacksonLocalDateSerializer() {
this(null);
}
public JacksonLocalDateSerializer(Class<Date> type) {
super(type);
}
#Override
public void serialize(Date value, JsonGenerator jsonGenerator,
SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
jsonGenerator.writeNumber(value.getTime());
}
}
and add it in object mapper so that Date type object always serialize using your custom serializer
#Configuration
public class JacksonConfig {
#Bean
#Primary
public ObjectMapper configureObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(Date.class, new DateSerializer());
objectMapper.registerModule(javaTimeModule);
return objectMapper;
}
}
I have a spring rest service that accepts and gives json output.
#PostMapping(path = "/path", consumes = {"application/json"}, produces = {"application/json"})
public ResponseEntity<RequestData> method(#RequestBody RequestData request){
return request;
}
RequestData contains several dates (XMLGregorianCalendar). I cannot change the type, since it is generated from xsd. To get dates with the original time zones, I used the parameter
spring.jackson.date-format: yyyy-MM-dd'T'HH:mm:ssZ
Request
{
"date1":"2020-02-28T09:26:59+09:00",
"date2":"2020-01-10T12:46:29+04:00",
"date3":"2020-03-15T11:32:43+08:00"
}
From this request, I got an XMLGregorianCalendar with different time zones.
But when sending a response message, the dates are converted to 0 time zone.
Response
{
"date1":"2020-02-28T00:26:59+0000",
"date2":"2020-01-10T08:46:29+0000",
"date3":"2020-03-15T03:32:43+0000"
}
What settings need to be done on jackson to get non-zero time zones in the response? It is necessary that the response time zones returned in the request.
Or maybe jackson does not know how to do this and always converts the date to a single time zone? In that case, which library to use?
Thanks!
Solution
You must create a serializer and deserializer. Then you need to override the existing ObjectMapper.
If only the serializer is overrided, then upon receipt of the data, the time zone will be normalized (reduced to +00:00), therefore it is also necessary to override the deserializer.
Serializer:
public class XMLGCSerializer extends JsonSerializer<XMLGregorianCalendar> {
#Override
public void serialize(XMLGregorianCalendar value,
JsonGenerator gen,
SerializerProvider serializers)
throws IOException {
gen.writeObject(value.toString());
}
}
Deserializer:
public class XMLGCDeserializer extends JsonDeserializer<XMLGregorianCalendar> {
#Override
public XMLGregorianCalendar deserialize(JsonParser parser, DeserializationContext context) throws IOException {
String stringDate = parser.getText();
try {
return DatatypeFactory.newInstance().newXMLGregorianCalendar(stringDate);
} catch (Exception e) {
throw new RuntimeException(e);
//or return null
}
}
}
Override ObjectMapper
#Component
public class JacksonConfig {
private final ObjectMapper objectMapper;
public JacksonConfig() {
objectMapper = new ObjectMapper();
SimpleModule s = new SimpleModule();
s.addSerializer(XMLGregorianCalendar.class, new XMLGCSerializer());
s.addDeserializer(XMLGregorianCalendar.class, new XMLGCDeserializer());
objectMapper.registerModule(s);
}
#Bean
public ObjectMapper getContext() {
return objectMapper;
}
}
You can create a seperate class to handle serialization by yourself. Here is an example:
class XMLGCSerializer extends JsonSerializer<XMLGregorianCalendar> {
#Override
public void serialize(XMLGregorianCalendar value,
JsonGenerator gen,
SerializerProvider serializers)
throws IOException {
gen.writeObject(value.toString());
}
}
Now you just need to annotate your fields in RequestData:
class RequestData{
#JsonSerialize(using = XMLGCSerializer.class)
XMLGregorianCalendar date1;
//...
}
I am able to serialise the string field using #JsonSerialize applied on POJO getmethod, and able to see the post serialize value changes in controller before binding to response, but once the controller method returns ResponseEntity.ok(postRes), the serialise value is not coming in the response.
The application is Spring Boot 2.1.1, JsonSerialize comes from Jackson.
Do I need to configure anything in Spring Boot for this?
Usage:
#JsonSerialize(using = JsonString.class)
public String getInvoices() {
return invoices;
}
Implementation:
public class JsonString extends JsonSerializer <String> {
#Override
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException,
JsonProcessingException {
String val = value.replace("/", "");
gen.writeString(val);
}
}
I'm implementing a Spring #RestController with a #PostMapping annotated method. I want to allow a HTTP POST using this body:
{"dateTimes":[
"2017-07-19T14:25+02:00",
"2017-08-19T14:25+02:00"
]
}
I have an object used as #RequestBody:
public class TransactionAllowedRequestBody {
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private List<ZonedDateTime> dateTimes;
public List<ZonedDateTime> getDateTimes() {
return dateTimes;
}
public void setDateTimes(List<ZonedDateTime> dateTimes) {
this.dateTimes = dateTimes;
}
}
This is my controller method:
#PostMapping("/transaction-allowed")
public void isTransactionAllowed(#AuthenticationPrincipal CustomUserDetails userDetails,
#RequestBody TransactionAllowedRequestBody requestBody) {
System.out.println("requestBody = " + requestBody);
}
However, when I try this, I get:
Could not read JSON document: Can not construct instance of java.time.ZonedDateTime:
no String-argument constructor/factory method to deserialize from String value ('2017-07-19T14:25+02:00')
If I replace ZonedDateTime with String, it works.
I am using Spring Boot 1.5.3.
Note: Using #DateTimeFormat on a GET request parameter works fine. I tried it with:
#GetMapping("/transaction-allowed")
public void isTransactionAllowed(#AuthenticationPrincipal CustomUserDetails userDetails,
#RequestParam("datetime") #DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) ZonedDateTime dateTime) {
System.out.println("userDetails = " + userDetails);
System.out.println("dateTime = " + dateTime);
}
Seems the issue was that I forget to include the jackson-datatype-jsr310 dependency as Spring Boot will not add it by default:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson.version}</version>
</dependency>
You can use #JsonDeserialize and a Deserializer of your own
Like the following one
public class ZonedDateTimeDeserializer extends JsonDeserializer<List<ZonedDateTime>> {
#Override
public List<ZonedDateTime> deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
ObjectCodec oc = jp.getCodec();
JsonNode array = oc.readTree(jp);
List<ZonedDateTime> dates = new ArrayList<>();
if(array.isArray()) {
for (JsonNode node: array) {
dates.add(ZonedDateTime.parse(node.asText()));
}
}
return dates;
}
}
To use the Deserializer annotation will be like
#JsonDeserialize(using = ZonedDateTimeDeserializer.class)
private List<ZonedDateTime> dateTimes;