Json mapper converts LocalDate to month, year, day of month etc - json

Json mapper converts LocalDate to month, year, day of month ... when converting java class to json like this,
"dob":{
"year": 1992,
"month": "MARCH",
"dayOfMonth": 19,
"dayOfWeek": "THURSDAY",
"era": "CE",
"dayOfYear": 79,
"leapYear": true,
"monthValue": 3,
"chronology": {
"calendarType": "iso8601",
"id": "ISO"
}
}
this is saved as a Date in mysql as 1992-03-19 how to return this date as it is like
"dob:1992-03-19"

Jackson and java.time types
The Jackson JavaTimeModule is used to handle java.time serialization and deserialization.
It provides a set of serializers and deserializers for the java.time types. If the SerializationFeature.WRITE_DATES_AS_TIMESTAMPS is disabled, java.time types will be serialized in standard ISO-8601 string representations.
Handling serialization in your particular format
However, once you have a very particular format, you can create a custom serializer:
public class DateOfBirthSerializer extends JsonSerializer<LocalDate> {
#Override
public void serialize(LocalDate value, JsonGenerator gen,
SerializerProvider serializers) throws IOException {
gen.writeString("dob:" + value.format(DateTimeFormatter.ISO_DATE));
}
}
Then you can use it as follows:
public class Foo {
#JsonSerialize(using = DateOfBirthSerializer.class)
private LocalDate dateOfBirth;
// Getters and setters
}
Alternatively you can use:
SimpleModule module = new SimpleModule();
module.addSerializer(LocalDate.class, new DateOfBirthSerializer());
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
It will be applied to all LocalDate instances serialized with that ObjectMapper.
Handling deserialization in your particular format
For deserialization, you can use something like:
public class DateOfBirthDeserializer extends JsonDeserializer<LocalDate> {
#Override
public LocalDate deserialize(JsonParser p,
DeserializationContext ctxt) throws IOException {
String value = p.getValueAsString();
if (value.startsWith("dob:")) {
value = value.substring(4);
} else {
throw ctxt.weirdStringException(value,
LocalDate.class, "Value doesn't start with \"dob:\"");
}
return LocalDate.parse(value, DateTimeFormatter.ISO_DATE);
}
}

Related

How to deserialize JSON containing LocalDate field generated by GSON library

I have JSON string which was generated by GSON library and it looks like :
{
"id": 10,
"articleNumber": 5009,
"processDate": {
"year": 2021,
"month": 1,
"day": 1
},
"price": 1.22
}
I want to use Jackson for deserialize the above JSON. But it fails at processDate field due to the format how processDate field is present in the JSON.
How to parse the above JSON string by using some custom deserializer?
It seems you unwillingly got Jackson's built-in
LocalDateDeserializer parsing your date.
This deserializer supports several JSON date formats
(string, integer array, epoch day count)
"2021-1-1"
[2021, 1, 1]
18627
but unfortunately not your object-like format
{ "year": 2021, "month" :1, "day": 1 }
Therefore you need to write your own deserializer for LocalDate.
This is not so hard.
public class LocalDateDeserializer extends JsonDeserializer<LocalDate> {
#Override
public LocalDate deserialize(JsonParser parser, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = parser.getCodec().readTree(parser);
try {
int year = node.get("year").intValue();
int month = node.get("month").intValue();
int day = node.get("day").intValue();
return LocalDate.of(year, month, day);
} catch (Exception e) {
throw JsonMappingException.from(parser, node.toString(), e);
}
}
}
Then, in your Java class you need to tell Jackson,
that you want its processDate property be deserialized
by your own LocalDateDeserializer.
public class Root {
private int id;
private int articleNumber;
#JsonDeserialize(using = LocalDateDeserializer.class)
private LocalDate processDate;
private double price;
// getters and setters (omitted here for brevity)
}
I dont know java that well, just make a custom type like this. below
just create a custom Struct like:
inline class processDate {
int year,
int month,
int day,
public Date getDate(){
DateFormat formatter = new SimpleDateFormat("dd-MMM-yy");
Date date = formatter.parse(this.day + "-" + this.month + "-" + this.year);
return date;
}
}

Java LocalDateTime being converted into an array of ints when being converted to JSON using Spring boot rest [duplicate]

This question already has answers here:
Jackson Serialize Instant to Nanosecond Issue
(2 answers)
Closed 2 years ago.
My code is as below
#Data
#Document(collection = "models")
public class Model {
#Field(value = "modelDt")
private LocalDateTime modelDate;
}
#Data
public class ModelDTO {
private LocalDateTime modelDate;
}
#RestController
#RequestMapping("/api/v1/model")
public class ModelController {
#Autowired
ModelService modelService;
#GetMapping
public List<ModelDTO> getModels() {
return modelService.getAllModels();
}
}
Used this almost everywhere where the JSON response is coming as a proper format like yyyy-mm-ddT00:00:00 , but in the above case I'm getting the date in the below format.
[
{
"modelDate": [
YYYY,
MM,
DD,
00,
00,
0000
]
}
]
I've crossed checked my code with the ones where the proper format is being returned.
use below Jackson annotation on date fields
#JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")
If you want LocalDateTime to always serialize in any format then follow the solution.
You can create a Serializer for LocalDateTime and add it ObjectMapper.Then always LocalDateTime serialize using JacksonLocalDateTimeSerializer.
public class JacksonLocalDateTimeSerializer extends StdSerializer<LocalDateTime> {
private static final long serialVersionUID = 1355852411036457107L;
private static final DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
public JacksonLocalDateTimeSerializer() {
this(null);
}
protected JacksonLocalDateTimeSerializer(Class<LocalDateTime> type) {
super(type);
}
#Override
public void serialize(LocalDateTime value, JsonGenerator jsonGenerator,
SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString(formatter.format(value));
}
}
And add this in ObjectMapper to auto-apply for LocalDateTime field for all responses.
#Configuration
public class JacksonConfig {
#Bean
#Primary
public ObjectMapper configureObjectMapper() {
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(LocalDateTime.class, new JacksonLocalDateTimeSerializer());
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(javaTimeModule);
return objectMapper;
}
}

How to change the data fomat of the JSON node while serializing and deserializing in jackson

I have following Incoming json, which I deserialize to Model.java and then copy that java object to ModelView.java. I wan't to convert date from String to milliseconds and send the Outgoing json as response.
How do I go for it ?
I've specific reason to copy the value from Model.java to ModelView.java using object mapper. So please don't suggest to modify that part. I'm looking to do this via annotation. I'm pretty sure that it can be done, but don't know how.
The json provided here is a simplistic one. I have a large json in actual scenario.
Incoming json
{
"date":"2016-03-31"
}
Outgoing Json
{
"date":236484625196
}
My Controller Class
#Controller
public class SomeController extends BaseController {
#Autowired
private SomeService someService;
#RequestMapping(method = RequestMethod.GET, produces = "application/json")
public #ResponseBody
ResponseEntity<RestResponse> getDetails(HttpServletRequest request, HttpServletResponse response) {
Model model = someService.getData();
ModelView modelView = ModelView.valueOf(model);
return getSuccessResponse(modelView);
}
}
Model.java
#JsonInclude(Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
public class Model implements Serializable {
private String date;
//normal getters and setters
}
ModelView.java
#JsonInclude(Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
public class ModelView implements Serializable {
private Long date;
//normal getters and setters
public static ModelView valueOf(Model model){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd");
ObjectMapper mapper = new ObjectMapper();
ModelView modelView = mapper.convertValue(model, ModelView.class);
try {
modelView.setDate(sdf.parse(model.getDate()).getTime());
} catch (ParseException e) {
IntLogger.error("logging error here");
}
return modelView;
}
}
I'm open to change the variable name from "date" to something else in ModelView.java but the outgoing json should remain same.
Jackson has some build in date formatting, for example, you can set the DateFormatter on the object mapper, but i believe this only works if the serialization and deserialization format is the same.
A simpler approach to date serialization and deserialization, if you want serialization and deserialization to be different format, is to use #JsonSerialize and #JsonDeserialize annotations on your Model.class directly (this could obsolete the need for ModelView if your only purpose was to convert the date).
You can create two classes for serialization and deserialization:
public class JsonDateDeserializer extends JsonDeserializer<Date> {
#Override
public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
String dateString = jsonParser.getText();
try {
return dateFormat.parse(dateString);
}
catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
Then for the serialization to your Outgoing json:
public class JsonDateSerializer extends JsonSerializer<Date> {
#Override
public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
jsonGenerator.writeString(Long.toString(date.getTime()));
}
}
Now, you an just annotate your Model.java:
#JsonInclude(Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
public class Model implements Serializable {
#JsonSerialize(using = JsonDateSerializer.class)
#JsonDeserialize(using = JsonDateDeserializer.class)
private String date;
//normal getters and setters
}

Serialize Date in a JSON REST web service as ISO-8601 string

I have a JAX-RS application using JBoss AS 7.1, and I POST/GET JSON and XML objects which include Dates (java.util.Date):
#XmlRootElement
#XmlAccessorType(XmlAccessField.FIELD)
public class MyObject implements Serializable
{
#XmlSchemaType(name = "dateTime")
private Date date;
...
}
When I use #Produce("application/xml") on the get method, the objets are serialized as XML and the dates are converted into ISO-8601 strings (e.g. "2012-12-10T14:50:12.123+02:00").
However, if I use #Produce("application/json") on the get method, the dates in the JSON objects are timestamps (e.g. "1355147452530") instead of ISO-8601 strings.
How can I do to configure the JAX-RS implementation (RESTEasy) to serialize dates in JSON format as ISO-8601 strings instead of timestamps ?
Thank you for your answers.
Note: I also tried to use a custom JAX-RS provider to do the JSON serialization for Dates
#Provider
#Produces(MediaType.APPLICATION_JSON)
public class CustomJsonDateProvider implements MessageBodyWriter<Date>
{
...
}
This provider seems to be registered by RESTeasy on JBoss startup:
[org.jboss.jaxrs] Adding JAX-RS provider classes: package.CustomJsonDateProvider
...
[org.jboss.resteasy.cdi.CdiInjectorFactory] No CDI beans found for class package.CustomJsonDateProvider. Using default ConstructorInjector.
but it is never used !
I assume your json parser is Jackson, try:
#JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd,HH:00", timezone="CET")
public Date date;
(since Jackson 2.0)
The default JBoss parser is Jettison, but I wasn't able to change the date format. So I switched to Jackson and added the following class to my project to configure it:
#Provider
#Produces(MediaType.APPLICATION_JSON)
public class JacksonConfig implements ContextResolver<ObjectMapper>
{
private final ObjectMapper objectMapper;
public JacksonConfig()
{
objectMapper = new ObjectMapper();
objectMapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESPAMPS, false);
}
#Override
public ObjectMapper getContext(Class<?> objectType)
{
return objectMapper;
}
}
Sorry people for yelling out loud - I found the answers here
http://wiki.fasterxml.com/JacksonFAQDateHandling,
here
http://wiki.fasterxml.com/JacksonFAQ#Serializing_Dates,
here
http://wiki.fasterxml.com/JacksonHowToCustomSerializers
here
http://jackson.codehaus.org/1.1.2/javadoc/org/codehaus/jackson/map/util/StdDateFormat.html
Using the #JsonSerialize(using= ... ) way:
public class JsonStdDateSerializer
extends JsonSerializer<Date> {
private static final DateFormat iso8601Format =
StdDateFormat.getBlueprintISO8601Format();
#Override
public void serialize(
Date date, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException {
// clone because DateFormat is not thread-safe
DateFormat myformat = (DateFormat) iso8601Format.clone();
String formattedDate = myformat.format(date);
jgen.writeString(formattedDate);
}
}
Declare the same Serializer used by Soap/XML:
#XmlElement(name = "prealert_date")
#XmlSchemaType(name = "dateTime")
#JsonSerialize(using = XMLGregorianCalendarSerializer.class)
protected XMLGregorianCalendar prealertDate;

Deserialize JSON object/map as generic Collection with Jackson

I have JSON maps like this:
"things": {"foo": {"name": "foo", ...}, "bar": {"name": "bar", ...}}
I want to deserialize them as if they were arrays:
"things": [{"name": "foo", ...}, {"name": "bar", ...}]
(to match XML/JAXB deserialization behavior):
<things><thing name="foo">...</thing><thing name="bar">...</thing></things>
into a collection such as this:
#XmlElementWrapper
#XmlElement(name = "thing")
#JsonDeserialize(using = MapToCollectionDeserializer.class)
Collection<Thing> things;
Note that I have collections with various element types -- not just Thing -- so I need a generic mechanism.
However, when writing a custom deserializer, what's the right way to access the type information of the context?
public class MapToCollectionDeserializer extends StdDeserializer<Object>
{
#Override
public Object deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
Preconditions.checkState(jp.getCurrentToken() == JsonToken.START_OBJECT);
final LinkedList<Object> result = new LinkedList<>();
JsonToken tok;
while ((tok = jp.nextToken()) != JsonToken.END_OBJECT)
{
Preconditions.checkState(tok == JsonToken.FIELD_NAME);
// How to get the collection element type for deserialization?
result.add(...);
}
return result;
}
}
My approach so far is using ContextualDeserializer, which can provide a BeanProperty (which contains type information) to the deserializer. However, a JsonDeserializer must still have a no-arg constructor, so I end up constructing a broken object at first:
public class MapToCollectionDeserializer extends StdDeserializer<Object>
implements ContextualDeserializer<Object>
{
private final BeanProperty property;
public MapToCollectionDeserializer()
{
super(Collection.class);
property = null; // YUCK: BROKEN!!!
}
private MapToCollectionDeserializer(BeanProperty property)
{
super(property.getType());
this.property = property;
}
#Override
public JsonDeserializer<Object> createContextual(DeserializationConfig config,
BeanProperty property) throws JsonMappingException
{
return new MapToCollectionDeserializer(property);
}
#Override
public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException,
JsonProcessingException
{
Preconditions.checkState(jp.getCurrentToken() == JsonToken.START_OBJECT);
final JavaType elementType = property.getType().containedType(0);
final LinkedList<Object> result = new LinkedList<>();
JsonToken tok;
while ((tok = jp.nextToken()) != JsonToken.END_OBJECT)
{
Preconditions.checkState(tok == JsonToken.FIELD_NAME);
jp.nextToken();
final JsonDeserializer<Object> valueDeser = ctxt.getDeserializerProvider()
.findValueDeserializer(ctxt.getConfig(), elementType, property);
result.add(valueDeser.deserialize(jp, ctxt));
}
return result;
}
}
Is there a better/simpler way to do this?
It looks like you stopped using Jackson, but for anyone with a similar problem, you can turn on DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY. With that setting enabled, when Jackson finds an object in the JSON but is supposed to deserialize to a collection, it will create a collection and put the object into the collection which seems like what you want here.