notation Gson to formatter date in attribute DTO - java

I have the following code with Jackson:
public class Header implements Serializable {
#JsonProperty("numeroUnico")
private Integer numeroCliente;
#JsonProperty("oficina")
private Integer oficina;
#JsonProperty("fecha")
#JsonSerialize(using = CustomDateSerializer.class)
private Date fechaInscripcion;
}
this is my class "CustomDateSerializer.class"
public class CustomDateSerializer extends StdSerializer<Date> {
private SimpleDateFormat formatter
= new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
public CustomDateSerializer() {
this(null);
}
public CustomDateSerializer(Class t) {
super(t);
}
#Override
public void serialize (Date value, JsonGenerator gen, SerializerProvider arg2)
throws IOException, JsonProcessingException {
gen.writeString(formatter.format(value));
}
}
They asked me to migrate all the implementations of Jackson to Gson.
Taking into account that the notation in Jackson #JsonProperty has an equivalence in Gson that is #SerializedName.
But for the notation in Jackson of:
    
#JsonSerialize (using = CustomDateSerializer.class)
What is its equivalent for Gson? if not, as it should be the implementation for attributes of type Date in my DTO.

I think the closest and probably the only match is #TypeAdapter. However you need to code either JsonSerializer<T> or TypeAdapter<T> to be used with that annotation.
For example how to make something like your CustomDateSerializer see accepted answer for this question.
Or maybe you can wrap your existing CustomDateSerializer with Gson TypeAdapter<Date> and use that in the annotation.

Related

Serialize objects to a map with the object class as the key?

I'm writing a application using Spring boot and jackson for JSON parsing. I need to handle another service which produces JSON like this:
{
"task-id": 5081,
"task-created-on": {
"java.util.Date": 1631022026000
}
}
Notably, certain fields like the date field here are serialized into a map with a single key-value pair, where the key is a java classname and the value is the actual value of the field.
I've been going through the jackson documentation and haven't found anything about this format. Is there a way to configure jackson to produce and parse fields in this format?
At a minimum, I need to handle dates formatted this way. But I believe the service also uses this format for other objects, where the map key will be the name of some arbitrary java class and the value will be a map of its own. So I'd be interested in a solution that handles more than just dates if possible.
It can be easily done with custom serializer in Jackson by following steps.
First, create objects for serialization as follows:
class MyDateObject {
private Date date;
//general getter/setter
}
class Task {
#JsonProperty("task-id")
private int taskId;
#JsonProperty("task-created-on")
private MyDateObject taskCreatedOn;
//general getters/setters
}
Second, define your custom serializer: (Please note that I used myDateObject.getDate().getClass().getName() to get the class name of date field.)
class DateSerializer extends StdSerializer<MyDateObject> {
public DateSerializer() {
this(null);
}
protected DateSerializer(Class<MyDateObject> t) {
super(t);
}
#Override
public void serialize(MyDateObject myDateObject, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeStartObject();
jsonGenerator.writeNumberField(myDateObject.getDate().getClass().getName(), myDateObject.getDate().getTime());
jsonGenerator.writeEndObject();
}
}
Finally, register the serializer with ObjectMapper for the MyDateObject class and perform the serialization:
MyDateObject myDateObject = new MyDateObject();
myDateObject.setDate(new Date());
Task task = new Task();
task.setTaskId(5081);
task.setTaskCreatedOn(myDateObject);
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(MyDateObject.class, new DateSerializer());
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(simpleModule);
System.out.println(objectMapper.writeValueAsString(task));
The expected output is:
{"task-id":5081,"task-created-on":{"java.util.Date":1633402076254}}
Please refer to Jackson – Custom Serializer for more information.
It is possible to solve the issue with the use of a custom JsonSerializer and applying the JsonSerialize over the fields in the pojo you are interested like below :
public class Task {
#JsonProperty("task-id")
private int taskId;
#JsonProperty("task-created-on")
#JsonSerialize(using = ObjectSerializer.class)
Date taskCreatedOn;
}
The custom serializer will use the JsonGenerator.html#writeObjectField to serialize a generic object (Date or other java class) as propertyname : {"classname" : value} :
public class ObjectSerializer extends JsonSerializer<Object> {
#Override
public void serialize(Object t, JsonGenerator jg, SerializerProvider sp) throws IOException {
jg.writeStartObject();
jg.writeObjectField(t.getClass().getName(), t);
jg.writeEndObject();
}
}

Is there a common code to change the date format(timestamp) to Number format

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;
}
}

How to configure jackson for convert Enum to JSON?

#AllArgsConstructor
#Getter
public enum MemberType {
INTERN("name_intern", 1),
EMPLOYEE("name_employee", 10);
private String name;
private int workingMonth;
}
Here is my enum. I want to convert Enum class to JSON string with some constraint.
I want to MemberType has no dependency with Jackson
I want to convert MemberType.INTERN to {id:INTERN, name:"name_intern", workingMonth:10}.
I have lots of Enums want to convert like above. And Their number of property is different each other.
I want resolve this problem through just one global configuration.
I don't want to use explicit java reflection.
Is there a solution that meets the above constraints?
You can use #JsonFormat annotation like this:
#JsonFormat(shape=JsonFormat.Shape.OBJECT)
public enum MemberType { ... }
or you can use #JsonValue annotation like this:
public enum MemberType {
[...]
#JsonValue
public String getName() {
return name;
}
}
or maybe a CustomSerializer for Enum, you can find more details here.
If you implement JsonSerializer,you can custom serialization.
An example is shown below.
#JsonComponent
public final class MediaTypeJsonComponent {
public static class Serializer extends JsonSerializer<MemberType> {
#Override
public void serialize(MemberType value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeStartObject();
gen.writeStringField("id", value.name());
gen.writeNumberField("workingMonth", value.getWorkingMonth());
gen.writeStringField("name", value.getName());
gen.writeEndObject();
}
}
//
// If you need,write code.
//public static class Deserializer extends JsonDeserializer<Customer> {
//}
}
Another way is to implement JsonSerialize.
If you want more information, you should refer to:
https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/jackson/JsonComponent.html
https://www.baeldung.com/jackson-custom-serialization
How do I use a custom Serializer with Jackson?
https://www.baeldung.com/jackson-serialize-enums

#JsonDeserializer in Mixin

Consider the following example:
I have a json string = {"timestamp":1504111920} which needs to be converted to CodeTimestamp class. The timestamp present in above json string is in epoch second.
CodeTimestamp class:
#Getter
#Setter
#NoArgsConstructor
class CodeTimestamp {
private Date timestamp;
}
By directly using fasterxml jackson mapper, I'll not be able to get the correct date since it assumes timestamp to be in epoch millisecond. So, I would need to write a custom deserializer.
However, I cannot edit/modify CodeTimestamp class. Is there any way to write JsonDeserializer in mixin?
I'm facing issues while deserializing. Following is the code:
public abstract class StreamRecordMixIn {
#JsonDeserialize(using = UnixTimestampDeserializer.class)
private Date approximateCreationDateTime;
}
public class UnixTimestampDeserializer extends JsonDeserializer<Date> {
#Override
public Date deserialize(JsonParser parser, DeserializationContext context)
throws IOException, JsonProcessingException {
String unixTimestamp = parser.getText().trim();
return new Date(TimeUnit.SECONDS.toMillis(Long.valueOf(unixTimestamp)));
}
}
Code to initialize and use object mapper:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.addMixIn(CodeTimestamp.class, StreamRecordMixIn.class);
CodeTimestamp codeTimeStamp = objectMapper.readValue(payload, CodeTimestamp.class);
Error:
Caused by: java.lang.IllegalArgumentException: Class com.test.TestConverter$UnixTimestampDeserializer has no default (no arg) constructor
at com.fasterxml.jackson.databind.util.ClassUtil.createInstance(ClassUtil.java:378)
at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.deserializerInstance(DefaultDeserializationContext.java:218)
at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory.findDeserializerFromAnnotation(BasicDeserializerFactory.java:1735)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.constructSettableProperty(BeanDeserializerFactory.java:730)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.addBeanProps(BeanDeserializerFactory.java:507)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.buildBeanDeserializer(BeanDeserializerFactory.java:229)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.createBeanDeserializer(BeanDeserializerFactory.java:142)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer2(DeserializerCache.java:403)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:352)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:264)
... 23 more
The mistake here is custom deserializer not declared as static. So if I used it as mentioned below, it works.
public static class UnixTimestampDeserializer extends JsonDeserializer<Date> {
#Override
public Date deserialize(JsonParser parser, DeserializationContext context)
throws IOException, JsonProcessingException {
String unixTimestamp = parser.getText().trim();
return new Date(TimeUnit.SECONDS.toMillis(Long.valueOf(unixTimestamp)));
}
}

custom field serialization using jacson

I want to serialize few fields of my class in custom way using jackson. So i wrote a custom serializer for this.But my problem is i am not able to get the name of the field in custom serializer. My POJO class is
public static class Foo {
public String foo = "a";
#JsonSerialize(using = CustomSerializer.class)
public String bar = "b";
#JsonSerialize(using = CustomSerializer.class)
public String foobar = "c";
}
And my custom serializer class is
public class CustomSerializer extends JsonSerializer<String>
{
#Override
public void serialize(String t, JsonGenerator jg, SerializerProvider sp) throws IOException, JsonProcessingException
{
if(field.name.equals("a"))
//do this
else if(filed.name.equals("b"))
//do that
}
}
Here i want get the name of field which is being serialized.
How can i get the name of fields "a" and "b" in custom serializer ?
Thanks
I think, this is not possible now. But you can create two separate serializers for each property. I know, this a little workaround, but it should work.

Categories

Resources