Jackson Custom Deserializer Not Working In Spring Boot - java

I created a custom deserializer for my entities but it keeps on throwing exception:
I have two classes: AppUser and AppUserAvatar
AppUser.java
#Entity
#Table(name = "user")
public class AppUser implements Serializable {
#Transient
private static final long serialVersionUID = -3536455219051825651L;
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
#Column(name = "password", nullable = false, length = 256)
private String password;
#JsonIgnore
#Column(name = "is_active", nullable = false)
private boolean active;
#JsonIgnore
#OneToMany(mappedBy = "appUser", targetEntity = AppUserAvatar.class, fetch = FetchType.LAZY)
private List<AppUserAvatar> appUserAvatars;
//// Getters and Setters and toString() ////
}
AppUserAvatar.java
#Entity
#Table(name = "user_avatar")
public class AppUserAvatar extends BaseEntityD implements Serializable {
#Transient
private static final long serialVersionUID = 8992425872747011681L;
#Column(name = "avatar", nullable = false)
#Digits(integer = 20, fraction = 0)
#NotEmpty
private Long avatar;
#JsonDeserialize(using = AppUserDeserializer.class)
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id", nullable = false)
private AppUser appUser;
//// Getters and Setters and toString() ////
}
AppUserDeserializer.java
package com.nk.accountservice.deserializer;
import com.edoctar.accountservice.config.exception.InputNotFoundException;
import com.edoctar.accountservice.domain.candidate.AppUser;
import com.edoctar.accountservice.service.candidate.AppUserService;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.IOException;
import java.io.Serializable;
public class AppUserDeserializer extends JsonDeserializer implements Serializable {
private static final long serialVersionUID = -9012464195937554378L;
private AppUserService appUserService;
#Autowired
public void setAppUserService(AppUserService appUserService) {
this.appUserService = appUserService;
}
#Override
public Object deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
JsonNode node = jsonParser.getCodec().readTree(jsonParser);
Long userId = node.asLong();
System.out.println(node);
System.out.println(node.asLong());
AppUser appUser = appUserService.findById(userId);
System.out.println("appuser: " + appUser);
if (appUser == null) try {
throw new InputNotFoundException("User not found!");
} catch (InputNotFoundException e) {
e.printStackTrace();
return null;
}
return appUser;
}
}
Sample xhr boy is:
{
"appUser": 1,
"avatar": 1
}
An exception is thrown each time I submit the request.
Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: (was java.lang.NullPointerException); nested exception is com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: com.edoctar.accountservice.domain.candidate.AppUserAvatar["appUser"])]
I discovered that the appUserService.findById() method is not being called. I am really confused. I don't know where I went wrong. Will be greatful for any solution. Thanks.

Updated answer:
You can't use autowired properties because you are not in the Spring context. You are passing the class AppUserDeserializer as a reference in the annotation
#JsonDeserialize(using = AppUserDeserializer.class)
In this situation is the FasterJackson library that creates the instance of AppUserDeserializer, so the Autowired annotation is not taken in consideration.
You can solve your problem with a little trick. Add a static reference to the instance created by spring in the AppUserService:
#Service
public AppUserService {
public static AppUserService instance;
public AppUserService() {
// Modify the constructor setting a static variable holding a
// reference to the instance created by spring
AppUserService.instance = this;
}
...
}
Use that reference in the AppUserDeserializer:
public class AppUserDeserializer extends JsonDeserializer implements Serializable {
private AppUserService appUserService;
public AppUserDeserializer() {
// Set appUserService to the instance created by spring
this.appUserService = AppUserService.instance;
}
...
}
Original answer: To have a correct initialization of an Autowired property you have to annotate your class AppUserDeserializer, otherwise appUserService is null if you don't explicitly init it using the set method.
Try to annotate AppUserDeserializer with #Component:
#Component // Add this annotation
public class AppUserDeserializer extends JsonDeserializer implements Serializable {
...
}

You could go on and try to inject correctly the AppUserService but according to me this is not the cleanest solution. Generally I dislike the idea to use #Entity as communication models or view models. In this way you are coupling the entity to the producer/consumer of the view model. You are basically short-circuiting the model part.
What I would do is to map the contents of the json to a different class in deserialization phase, and use this one later on to construct the corresponding entity.

Try changing this line of code:
private boolean active;
to
private Boolean active;
boolean primitive can't handle nulls and may result in NPE.

Related

Post request transform int field to string automatically

I'm doing a dummy app of a hostpital. The problem I'm having is that, I'm trying to verify that when a Patient is created, the fields passed are of the correct type, but whenever I POST an Int in a String field, it doesn't fail and just transform the Int to String. The field I'm trying to make fail is "surname", which by the definition of the Patient class, is a String.
If I do this (I pass a number to the "surname" field):
{
"name": "John",
"surname": 43,
"sickness": "headache"
}
It just transforms 43 into a String by the time its in the Controller method.
Here we have the Patient object:
#Data
#Entity
#NoArgsConstructor
#AllArgsConstructor
public class Patient implements Serializable {
private static final long serialVersionUID = 4518011202924886996L;
#Id
//TODO: posible cambiar luego la generationType
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "patient_id")
private Long id;
#Column(name = "patient_name")
#JsonProperty(required = true)
private String name;
#Column(name = "patient_surname")
#JsonProperty(required = true)
private String surname;
#Column(name = "patient_sickness")
#JsonProperty(required = true)
private String sickness;
}
And this is the controller class:
#Controller
#Path("/patient")
#Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
public class PatientController {
#POST
#Path("")
public ResponseEntity<Object> postPatient(final Patient patient) {
ResponseEntity<Object> createdPatient = patientBusiness.createPatient(patient);
return new ResponseEntity<Patient>(createdPatient.getBody(), createdPatient.getStatusCode());
}
EDIT 1:
Following the "clues" and closing the circle of attention, I tried modifying the ObjectMapper, but my configuration isn't applying. I'm still getting the error from above.
This is the config class:
#Configuration
public class JacksonConfig {
#Bean
#Primary
public ObjectMapper getModifiedObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(MapperFeature.ALLOW_COERCION_OF_SCALARS, false);
mapper.coercionConfigFor(LogicalType.Integer).setCoercion(CoercionInputShape.String, CoercionAction.Fail);
return mapper;
}
}
Even added the property to the application.yml, but still nothing:
spring:
jackson:
mapper:
allow-coercion-of-scalars: false
Any help is appreciated. Thx.
In the end I referred to this post to do a deserializer and a module to just have it along all the program, not just the field I want not to be transformed.
Disable conversion of scalars to strings when deserializing with Jackson

Spring Boot Jackson ResponseEntity No serializer found for class

I have an odd error with a spring boot controller, not returning a recently created object.
I have a controller, with 2 methods (see below). One simply retrieves an Object of the class "OrderPay" and returns it as the payload of a response entity. This works fine, the object is therefore okay.
The other one creates and persists a new instance of "OrderPay" and is then supposed to return that newly created object. The creation of the new object and its persistence work fine. However, when I try to return it, I get the error message below.
Now I would understand that error message if it occured consistently. However, when returning this newly created object using the first function ("getPaymentByIdTest"), it returns it without problems, even though I retrieve it in the exact same way from the database and return it in the same way, with the same return type of the method.
Now I know that executing the code in a HTTP-GET method is not best practise, however it is quicker and more convenient for testing.
Can anyone see where I need to adjust the code?
2020-04-13 21:37:57.507 ERROR 26796 --- [nio-8081-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: com.brownbag_api.model.OrderPay["posSend"]->com.brownbag_api.model.Pos$HibernateProxy$7l7MDMEi["hibernateLazyInitializer"])] with root cause
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: com.brownbag_api.model.OrderPay["posSend"]->com.brownbag_api.model.Pos$HibernateProxy$7l7MDMEi["hibernateLazyInitializer"])
The Controller
#CrossOrigin(origins = "*", maxAge = 3600)
#RestController
#RequestMapping("/api/pay")
public class PaymentController {
#Autowired
private OrderPayRepo orderPayRepo;
#Autowired
private OrderPaySvc orderPaySvc;
#GetMapping("/{id}")
public ResponseEntity<?> getPaymentByIdTest(#PathVariable Long id) {
Optional<OrderPay> orderPay = orderPayRepo.findById(id);
return ResponseEntity.ok(orderPay);
}
#GetMapping("/exec/from/{from}/to/{to}/amount/{amount}")
public ResponseEntity<?> execPayment(#PathVariable Long from, #PathVariable Long to, #PathVariable double amount) {
Pos posFrom = posRepo.getOne(from);
Pos posTo = posRepo.getOne(to);
OrderPay pay = orderPaySvc.createPay(amount, posFrom, posTo);
pay = orderPaySvc.execPay(pay);
if (pay == null) {
return ResponseEntity.ok("Payment could not be executed. Please see log for more details!");
} else {
System.err.println("Payment executed: " + pay.getPosRcv().getParty().getName());
Long payId = pay.getId();
System.err.println("Payment executed: " + payId);
// payId returns the expected value here, the object is therefore saved in the database (verified).
Optional<OrderPay> orderPay = orderPayRepo.findById(payId);
return ResponseEntity.ok(pay);
}
}
}
Order.java
#Entity
#Table(name = "order_base")
#Inheritance(strategy = InheritanceType.JOINED)
public class Order implements Serializable {
private static final long serialVersionUID = -3458221490393509305L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private Long id;
#NotNull
#Column(name = "QTY")
private double qty;
public Order() {
}
public Order(#NotNull double qty) {
super();
this.qty = qty;
}
}
OrderPay
#Entity
#Table(name = "order_pay")
public class OrderPay extends Order implements Serializable {
private static final long serialVersionUID = 4643589803146964779L;
#NotNull
#OneToOne(targetEntity = Pos.class)
#JoinColumn(name = "POS_SEND_ID")
private Pos posSend;
#NotNull
#OneToOne(targetEntity = Pos.class)
#JoinColumn(name = "POS_RCV_ID")
private Pos posRcv;
public OrderPay() {
super();
}
public OrderPay(#NotNull double qty, #NotNull Pos posSend, #NotNull Pos posRcv) {
super(qty);
this.posSend = posSend;
this.posRcv = posRcv;
}
}
Pos.java
#Entity
#Table(name = "POS")
public class Pos implements Serializable {
private static final long serialVersionUID = 1530699992135610397L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private Long id;
#NotNull
#Column(name = "QTY")
private double qty;
#NotNull
#ManyToOne(targetEntity = Party.class)
#JoinColumn(name = "PARTY_ID")
#JsonBackReference
private Party party;
public Pos() {
}
public Pos(#NotNull double qty, #NotNull Party owner) {
super();
this.qty = qty;
this.party = owner;
}
}
JSON
{
"id":7,
"qty":33000.0,
"posSend":
{
"id":1,
"qty":-266010.0,
"hibernateLazyInitializer":{}
},
"posRcv":
{
"id":2,
"qty":66000.0,
"hibernateLazyInitializer":{}
}
}
If you are using Spring Boot, you can set the following property in application.properties file. That should solve the issue according to you stacktrace (see: "to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS")
spring.jackson.serialization.FAIL_ON_EMPTY_BEANS=false

Am I using OneToOne relationship right?

Hello I am actually working on a REST server using Spring-boot, hibernate, psql and I am experiencing some difficulties after adding an OneToOne relationship between entities.
Here are the 2 entities:
Pays:
#Entity
#Table(name = "pays")
public class Pays implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#JsonProperty("codePays")
private String codePays;
#Column(name = "libelle_pays")
#JsonProperty("libellePays")
private String libellePays;
#OneToOne(mappedBy = "pays",cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional=false)
private Traduction traduction;
protected Pays() {
}
public Pays(String codePays,String libellePays) {
this.codePays = codePays;
this.libellePays = libellePays;
}
and Traduction:
#Entity
#Table(name = "traduction")
public class Traduction implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JsonProperty("codeTrad")
private long codeTrad;
#Column(name = "defaultLanguage")
#JsonProperty("defaultLanguage")
private boolean defaultLanguage;
#OneToOne(cascade = CascadeType.ALL,fetch = FetchType.LAZY)
#JoinColumn(name="fk_code_pays")
#JsonProperty("codePays")
private Pays pays;
public Traduction(){
}
public Traduction(String codePays,boolean defaultLanguage) {
this.defaultLanguage = defaultLanguage;
pays.setCodePays(codePays);
}
My problem happen when I try to populate my table traduction using a Post method:
#PostMapping("/traduction")
public Traduction createTraduction(#RequestBody Traduction trad) {
System.err.println(trad);
return repository.save(trad);
}
when I send JSON data to my server via PostMan like this:
{
"codeTrad":0,
"defaultLanguage":true,
"fk_code_pays":"FR"
}
or this way:
{
"codeTrad":0,
"defaultLanguage":true,
"pays":
{
"codePays":"FR",
"libellePays":"France"
}
}
My server doesn't seem to understand the mapping with the object Pays.
Here what the object Traduction look like after my request:
[codeTrad=0, null, defaultLanguage=true]
and the pretty error:
Resolved [org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: (was java.lang.NullPointerException); nested exception is com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: com.auchan.corp.ipon.iponportail.model.Traduction["codePays"])]
So I am wondering if the problem comes from my server conception or just my JSON. Do you have an idea?
Your issue comes from :
public Traduction(String codePays, boolean defaultLanguage) {
this.defaultLanguage = defaultLanguage;
pays.setCodePays(codePays)
The pays is null and that's why you get an exception : java.lang.NullPointerException, well try to add Pays pays to that constructor.
This json won't work:
{ "codeTrad":0, "defaultLanguage":true, "fk_code_pays":"FR" }
as there is no field name fk_code_pays in your Traduction class.
Below won't work either:
{ "codeTrad":0, "defaultLanguage":true, "pays": { "codePays":"FR", "libellePays":"France" } }
Because pays is annotated with #JsonProperty("codePays")
As per your DTO classes, your json should be:
{ "codeTrad":0, "defaultLanguage":true, "codePays": { "codePays":"FR", "libellePays":"France" } }
Also I would recommend you to use wrapper classes in place of primitives. Boolean in place of boolean and Long in place of long.

spring data auditing fields marked as updatable=false don't get set on the returned entity when updating

I'm using Spring Data's annotations to add auditing data to my entities when they are saved or updated. When I create the entity the createdBy, createdDate, lastModifiedBy and lastModifiedDate get set on the object returned by repository.save().
ResourceEntity(id=ebbe1f3d-3359-4295-8c83-63eab21c4753, createdDate=2018-09-07T21:11:25.797, lastModifiedDate=2018-09-07T21:11:25.797, createdBy=5855070b-866f-4bc4-a18f-26b54f896a4b, lastModifiedBy=5855070b-866f-4bc4-a18f-26b54f896a4b)
Unfortunately, when I call repository.save() to update an existing entity the object returned does not have the createdBy and createdDate set.
ResourceEntity(id=ebbe1f3d-3359-4295-8c83-63eab21c4753, createdDate=null, lastModifiedDate=2018-09-07T21:12:01.953, createdBy=null, lastModifiedBy=5855070b-866f-4bc4-a18f-26b54f896a4b)
All the fields are set correctly in the database and a call to repository.findOne() outside of my service class returns an object with all the fields set correctly.
ResourceEntity(id=ebbe1f3d-3359-4295-8c83-63eab21c4753, createdDate=2018-09-07T21:11:25.797, lastModifiedDate=2018-09-07T21:12:01.953, createdBy=5855070b-866f-4bc4-a18f-26b54f896a4b, lastModifiedBy=5855070b-866f-4bc4-a18f-26b54f896a4b)
But if I call repository.findOne() in the service right after calling repository.save() to update the entity I also get an object back with createdBy and createdDate set to null.
Here is my entity:
#Entity(name = "resource")
#EntityListeners(AuditingEntityListener.class)
#Table(name = "resource")
#Data
#EqualsAndHashCode(of = "id")
#Builder
#AllArgsConstructor
#NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ResourceEntity {
#Id
#org.hibernate.annotations.Type(type = "org.hibernate.type.PostgresUUIDType")
private UUID id;
#CreatedDate
#Column(nullable = false, updatable = false)
private LocalDateTime createdDate;
#LastModifiedDate
private LocalDateTime lastModifiedDate;
#CreatedBy
#Column(nullable = false, updatable = false)
#org.hibernate.annotations.Type(type = "org.hibernate.type.PostgresUUIDType")
private UUID createdBy;
#LastModifiedBy
#org.hibernate.annotations.Type(type = "org.hibernate.type.PostgresUUIDType")
private UUID lastModifiedBy;
}
Here is my service:
#Component
public class ResourceService {
#Autowired
private ResourceRepository resourceRepository;
public ResourceEntity createResource(ResourceEntity resourceEntity) {
return saveResource(resourceEntity);
}
public ResourceEntity updateResource(ResourceEntity resourceEntity) {
return saveResource(resourceEntity);
}
public ResourceEntity getResource(UUID resourceId) {
return resourceRepository.findOne(resourceId);
}
private ResourceEntity saveResource(ResourceEntity resourceEntity) {
ResourceEntity savedResourceEntity = resourceRepository.save(resourceEntity);
return savedResourceEntity;
}
}
Here is my test:
def "Test update"() {
given:
UUID id = aRandom.uuid()
Resource resource = aRandom.resource().id(id).build()
Resource savedResource = resourceClient.createResource(resource)
when:
Resource updatedResource = aRandom.resource().id(id).build()
updatedResource = resourceClient.updateResource(updatedResource)
then:
Resource result = resourceClient.getResource(id)
assert result.id == updatedResource.id
assert result.createdBy == updatedResource.createdBy
assert result.creationDate == updatedResource.creationDate
assert result.lastModifiedBy == updatedResource.lastModifiedBy
assert result.lastModifiedDate == updatedResource.lastModifiedDate
}
Years after the original question and it's still an issue.
While not a perfect solution, I ended up fetching (via findById()) the existing entity and manually setting the #CreatedBy and #CreatedDate fields on the new entity before performing the update. It's both surprising and frustrating that JPA's auditing framework doesn't automatically handle this or provide a better way to accomplish it.
I tried before like this when i need to add audit info.
I have a DataConfig class like this.
#Configuration
#EnableJpaAuditing
public class DataConfig {
#Bean
public AuditorAware<UUID> auditorProvider() {
return new YourSecurityAuditAware();
}
#Bean
public DateTimeProvider dateTimeProvider() {
return () -> Optional.of(Instant.from(ZonedDateTime.now()));
}
}
Now you need AuditorAware class to get audit info.
So this class will be like this ;
public class XPorterSecurityAuditAware implements AuditorAware<UUID> {
#Override
public Optional<UUID> getCurrentAuditor() {
//you can change UUID format as well
return Optional.of(UUID.randomUUID());
}
}
Hope this will help you.

Class com.fasterxml.jackson.datatype.joda.deser.DateTimeDeserializer has no default (no arg) constructor

I am getting an error - 'Class com.fasterxml.jackson.datatype.joda.deser.DateTimeDeserializer has no default (no arg) constructor' while I am trying to call restangular for post request. When I call the method it goes in the error block.
Restangular.all('tests').post($scope.test).then(function (data) {
$scope.test.id = data.id;
$location.path($location.path() + data.id).replace();
}, function (error) {
$scope.exceptionDetails = validationMapper(error);
});
I am using jackson-datatype-joda - 2.6.5
The entity class used in this method as follows -
#Data
#JsonInclude(JsonInclude.Include.NON_NULL)
#Entity
#Table(name = "Test")
#EqualsAndHashCode(of = "id", callSuper = false)
#ToString(exclude = {"keywords", "relevantObjectIds"})
public class Test {
#Id
#Column(unique = true, length = 36)
private String id;
#NotBlank
#NotNull
private String name;
#Transient
private List<Testabc> Testabcs = new ArrayList<>();
}
The entity class used in above entity Testabc class as follows
#Data
#JsonInclude(JsonInclude.Include.NON_NULL)
#Slf4j
#Entity
#Table(name = "Test_abc")
#EqualsAndHashCode(of = "id", callSuper = false)
public class Testabc{
#Id
#Column(unique = true, length = 36)
#NotNull
private String id = UUID.randomUUID().toString();
#Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTime")
#JsonDeserialize(using = DateTimeDeserializer.class)
#JsonSerialize(using = DateTimeSerializer.class)
private DateTime createdOn;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "Id")
#NotNull
private t1 pid;
private long originalSize;
}
Finally resource class where I am requesting to create test data -
#ApiOperation(value = "Create new Test", notes = "Create a new Test and return with its unique id", response = Test.class)
#POST
#Timed
public Test create(Test newInstance) {
return super.create(newInstance);
}
I have tried to add this
#JsonIgnoreProperties(ignoreUnknown = true) annotation on entity class, but it doesn't work.
Can anyone help to resolve this problem?
Looking at the latest sources of DateTimeDeserializer you can easily see that it does not have a no-arg constructor, which seems to be required by the framework. This is also indicated in both the linked questions: joda.time.DateTime deserialization error & Jackson, Retrofit, JodaTime deserialization
Since you want to use only an annotation based solution, a possible workaround would be to create your own deserializer which extends the DateTimeDeserializer and provides a nor-arg constructor.
1) MyDateTimeSerializer
import com.fasterxml.jackson.datatype.joda.cfg.FormatConfig;
import com.fasterxml.jackson.datatype.joda.deser.DateTimeDeserializer;
import org.joda.time.DateTime;
public class MyDateTimeDeserializer extends DateTimeDeserializer {
public MyDateTimeDeserializer() {
// no arg constructor providing default values for super call
super(DateTime.class, FormatConfig.DEFAULT_DATETIME_PARSER);
}
}
2) AClass using the custom deserializer
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.joda.ser.DateTimeSerializer;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
public class AClass {
#JsonSerialize(using = DateTimeSerializer.class) // old serializer
#JsonDeserialize(using = MyDateTimeDeserializer.class) // new deserializer
private DateTime createdOn = DateTime.now(DateTimeZone.UTC); // some dummy data for the sake of brevity
public DateTime getCreatedOn() {
return createdOn;
}
public void setCreatedOn(DateTime createdOn) {
this.createdOn = createdOn;
}
}
3) Unit test
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
public class ATest {
#Test
public void testSomeMethod() throws Exception {
// Jackson object mapper to test serialization / deserialization
ObjectMapper objectMapper = new ObjectMapper();
// our object
AClass initialObject = new AClass();
// serialize it
String serializedObject = objectMapper.writeValueAsString(initialObject);
// deserialize it
AClass deserializedObject = objectMapper.readValue(serializedObject, AClass.class);
// check that the dates are equal (no equals implementation on the class itself...)
assertThat(deserializedObject.getCreatedOn(), is(equalTo(initialObject.getCreatedOn())));
}
}
This deserializer was never meant to be used by annotations; and as others have mentioned, cannot. In general you really should just add/register JodaModule and serializer/deserializer gets added as expected.
I am not sure why you would not want to go that route; it might be worth expanding on why this solution
(or registering deserializer you get by DateTimeDeserializer.forType(ReadableDateTime.class) through your custom module)
is not applicable.

Categories