7

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?

Geetanjali Jain
  • 402
  • 6
  • 19
  • Possible duplicate of [joda.time.DateTime deserialization error](http://stackoverflow.com/questions/22643367/joda-time-datetime-deserialization-error) – Morfic Jul 18 '16 at 10:29
  • No there is no duplicacy – Geetanjali Jain Jul 18 '16 at 11:10
  • 1
    Does this mean that the explanation from the answer for the linked question and the suggested solution of `mapper.registerModule(new JodaModule());` don't work for you? – Morfic Jul 18 '16 at 11:27
  • This link (http://stackoverflow.com/questions/36795151/jackson-retrofit-jodatime-deserialization) suggests to use either annotation or above mentioned help , and I want to do this work with annotation only. do I need to use mapper.registerModule(new JodaModule()); with annotation? – Geetanjali Jain Jul 18 '16 at 13:50
  • 1
    Both the solutions state that `DateTimeDeserializer` **does not have a no-arg constructor**, which is indeed your problem. If you want to use only annotations, you can simply extend the it yourself and add a no-arg constructor which calls `super` with the necessary parameters. – Morfic Jul 18 '16 at 15:36

2 Answers2

5

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())));
    }
}
Community
  • 1
  • 1
Morfic
  • 15,178
  • 3
  • 51
  • 61
  • Thanks, but Its doesn't work, Still getting same error – Geetanjali Jain Jul 19 '16 at 06:06
  • @Geetanjali While it's possible, it's very unlikely you'd get the same exception after you changed something, because it would mean your modification had no impact whatsoever. Can you please **clean** your workspace/output folder, **rebuild** your app, then retry and share the exception message and the stacktrace? – Morfic Jul 19 '16 at 09:07
  • Thanks for response, but I have tried your suggestion and make sure that I have build and clean app. One more thing is that I don't have any stack trace because the code breaks before even going to the java code, it just calls the error block of Restangular i.e 'Class com.fasterxml.jackson.datatype.joda.deser.DateTimeDeserializer has no default (no arg) constructor'. – Geetanjali Jain Jul 19 '16 at 11:06
  • @Geetanjali If you've replaced `DateTimeDeserializer` with `MyDateTimeDeserializer` in your entity and you're getting the same exception, then the problem must be somewhere else. Under the circumstances, could you share a [SSCCE](http://sscce.org) of the backend on github or similar? – Morfic Jul 19 '16 at 11:27
  • Sorry but I can't share my code as it is client code. – Geetanjali Jain Jul 19 '16 at 12:20
  • @Geetanjali of course, that is a normal and understandable. Nonetheless, this is not what a [SSCCE](http://sscce.org) implies. On the contrary, it means creating from scratch the simplest example possible to reproduce your issue while leaving out any sensitive information. This way we can debug and spot anything out of the ordinary which you may have missed. Otherwise we can only keep guessing what it it that may have gone wrong, making things very difficult. – Morfic Jul 19 '16 at 12:31
1

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.

StaxMan
  • 113,358
  • 34
  • 211
  • 239