Quarkus: RESTEASY008200: JSON Binding deserialization error: No default constructor

Created on 2 Dec 2019  路  16Comments  路  Source: quarkusio/quarkus

Describe the bug
I get the error described in https://github.com/quarkusio/quarkus/issues/3179.
The issue was closed as it should be resolved with quarkus 1.0 Final and its dependency yasson 1.0.5. But error is still there. I cannot add a no args constructor because the class is defined within an external dependency (library).

javax.ws.rs.ProcessingException: RESTEASY008200: 
JSON Binding deserialization error: javax.json.bind.JsonbException:  Can't create instance of a class: class com.domain.Task, No default constructor found.
        at org.jboss.resteasy.plugins.providers.jsonb.JsonBindingProvider.readFrom(JsonBindingProvider.java:78)
    at org.jboss.resteasy.core.interception.jaxrs.AbstractReaderInterceptorContext.readFrom(AbstractReaderInterceptorContext.java:102)
    at org.jboss.resteasy.core.interception.jaxrs.AbstractReaderInterceptorContext.proceed(AbstractReaderInterceptorContext.java:81)
    at org.jboss.resteasy.client.jaxrs.internal.ClientResponse.readFrom(ClientResponse.java:226)
    at org.jboss.resteasy.specimpl.BuiltResponse.readEntity(BuiltResponse.java:88)
    at org.jboss.resteasy.specimpl.AbstractBuiltResponse.readEntity(AbstractBuiltResponse.java:270)
...
Caused by: javax.json.bind.JsonbException: Can't create instance of a class: class com.domain.Task, No default constructor found.
    at org.eclipse.yasson.internal.serializer.ObjectDeserializer.getInstance(ObjectDeserializer.java:95)
    at org.eclipse.yasson.internal.serializer.AbstractContainerDeserializer.deserialize(AbstractContainerDeserializer.java:61)
    at org.eclipse.yasson.internal.serializer.CollectionDeserializer.deserializeNext(CollectionDeserializer.java:106)
    at org.eclipse.yasson.internal.serializer.AbstractContainerDeserializer.deserializeInternal(AbstractContainerDeserializer.java:84)
    at org.eclipse.yasson.internal.serializer.AbstractContainerDeserializer.deserialize(AbstractContainerDeserializer.java:60)
    at org.eclipse.yasson.internal.Unmarshaller.deserializeItem(Unmarshaller.java:68)
    at org.eclipse.yasson.internal.Unmarshaller.deserialize(Unmarshaller.java:54)
    at org.eclipse.yasson.internal.JsonBinding.deserialize(JsonBinding.java:53)
    at org.eclipse.yasson.internal.JsonBinding.fromJson(JsonBinding.java:93)
    at org.jboss.resteasy.plugins.providers.jsonb.JsonBindingProvider.readFrom(JsonBindingProvider.java:70)
    ... 48 more

There must be a bug somewhere in Yasson, RESTEasy or Quarkus. The method execution works in wildfly but not in quarkus. Maybe yasson or something else cannot add a default constructor in quarkus at runtime if one is missing (by reflection).

Environment (please complete the following information):

  • Output of java -version: 12
  • Quarkus version or git rev: 1.0 Final
kinbug triaginvalid

Most helpful comment

Can you please try using the resteasy-jackson dependency instead of resteasy-jsonb?

The json annotations that you are using seem to be Jackson annotations, not JSON-B annotations.

All 16 comments

Well, do you have a default constructor for your object?

I do not have a default constructor ( = no arg constructor) for that object because the class is defined by a 3rd party dependency which I cannot change. However, the same method execution works in wildfly even without a default constructor.

Can we see what com.domain.Task looks like?

Task has not a no-arg constructor defined and is included as a library in my application - I cannot add a no arg constructor.

The following class is NOT defined in my main application but in a library which I use. The library uses jackson but my main application uses json-b. By the way, the library creates instances of Task.class from json under the hood. But I do not generate these instances directly from my application code - it is created by the library.

Hence, my application code (except the library which I use) does not make use of the jackson annotations in any way.

@JsonPropertyOrder({"id", "name", "date", "enabled"})
public class Task implements Serializable {

    private static final long serialVersionUID = -3670232159675112851L;

    private final String id;
    private final String name;
    private final LocalDate date;
    private final Boolean enabled;

    @JsonCreator
    public Task(
            @JsonProperty("id") final String id,
            @JsonProperty("name") final String name,
            @JsonProperty("date") final LocalDate date,
            @JsonProperty("enabled") final Boolean enabled) {
        this.id = id;
        this.name = name;
        this.date = date;
        this.enabled = enabled;
    }

    public String getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public LocalDate getDate() {
        return date;
    }

    public Boolean getEnabled() {
        return isEnabled;
    }
}

Can you please try using the resteasy-jackson dependency instead of resteasy-jsonb?

The json annotations that you are using seem to be Jackson annotations, not JSON-B annotations.

I cannot useresteasy-jackson as my application uses resteasy-jsonb, that means: my application has jsonb-annotated classes.

The json annotations that you are using seem to be Jackson annotations

I do NOT use Jackson annotations in my application: The Task-class is from a third party dependency which internally uses jackson. But my application does NOT use jackson. The same configuration works well in wildfly - wildlfly also uses jsonb as json-provider while the 3rd party dependency internally uses jackson at the same time running with no errors.

Why should I bother about the json provider of a 3rd party dependency? Imagine 2 different dependencies: one uses jackson internally and the other uses jsonb internally. My application uses jsonb. How does quarkus treats something like that?

The situation is quite simple (unless I have totally misunderstood it):
Your 3rd party library is using Jackson annotations, while your Quarkus application uses resteasy-jsonb instead of resteasy-jackson. That combination won't fly 馃槈 anyway we slice and dice things in Quarkus

Your 3rd party library is most probably using Jackson.

As I said, yes.

Your 3rd party library is using Jackson annotations, while your Quarkus application uses resteasy-jsonb instead of resteasy-jackson. That combination won't fly

yes, exactly. So I must change my json provider only because of a 3rd party lib? That`s an ugly limitation: imagine 2 other libs using two different json providers and I can only use one of the lib because quarkus cannot handle those cases.

Quarkus should not bother about the json provider of any 3rd party library. Quarkus must handle such common things.

Would it help if quarkus generates missing no args constructors for such cases? Maybe another solution would be to allow to integrate both extensions (actually, this is not possible). Quarkus could determine which one to use (jsonb or jackson) by the kind of annotation. However, maybe generating no args constructors for such cases would be sufficient to resolve this issue.

anyway we slice and dice things in Quarkus

good to know, but this does not solve our issue. I have to decide to switch to wildfly again only because quarkus cannot handle such a common case.

Now of course we could do various things to get this specific use case working, but anything we do that involves mixing two JSON providers will be hack.
You just can't honestly expect two different json libraries to play nicely together in all cases.
I would really much like to see your Wildfly application, but I am assuming that if it does work, its only by coincidence (since it also uses a lot of the same technologies as Quarkus).

I have two proper solutions in mind of which only the second is currently doable:
1) Have JSON-B support something like Jackson's Mixins (as far as I know this doesn't exist)
2) Use Mapstruct (or a similar library) to convert the Task to another class which contains the proper JSON-B annotations.

You just can't honestly expect two different json libraries to play nicely together in all cases.

I do not bother and have no influence which json provider a 3rd party library uses, such scenarios work in java as usual. I do only use one json-provider in my application. What other libs are using is not my money. The app runs in wildfly long ago without such troubles. You can try it yourself: create a simple project with the scenario above - you will see no errors. You see no errors, because in general: annotations of jacksons are not used by yasson and vice versa. Or maybe wildfly uses some tricks to handle such cases - but I dont think so.

  1. Use Mapstruct (or a similar library) to convert the Task to another class which contains the proper JSON-B annotations.

Would it help if quarkus generates missing no args constructors for such cases? Maybe another solution would be to allow to integrate both extensions (actually, this is not possible). Quarkus could determine which one to use (jsonb or jackson) by the kind of annotation. However, maybe generating no args constructors for such cases would be sufficient to resolve this issue.

We can (and do for other safe cases) generate a default constructor.
However this wouldn't help much in this case since the fields are all final and there are no setters.
I'm short if you expecting input that is going to be deserialized to class with Jackson annotations, you need to use a Jackson JAX-RS provider.

Another hacky solution would be for Quarkus to add the corresponding JSON-B annotations alongside the Jackson ones, but again that just seems plain hacky with no real value...

Perhaps I am misunderstanding this whole situation so I'll let @gsmet comment before I say anything else 馃槀

To sum up:

There are a lot of 3rd party libs using jackson for json processing under the hood. Actually, all these libs cannot be used in quarkus if they do not comply with the json provider used by the _host application_.

For example:

myApp (= host application) (makes use of json-b annotation)
->libs
-->quarkus-resteasy.jar
-->quarkus-resteasy-jsonb.jar (interferes in any way in tasks-webservice.jar)
-->tasks-webservice.jar (this lib makes use of jackson annotations)
-->transitive dependency to jackson-libs (because of tasks-webservice.jar)
-->...

Quarkus should encapsulate (isolate) the json provider of any 3rd party library from the _host application_.

@nimo23 that sounds like a worthwhile goal to explore for Quarkus.

However, this would require that all RESTEasy - JSON integrations are always on the classpath so Quarkus can choose the appropriate one. That is kind of antithetical with the goal of keeping things as lean as possible.

would require that all RESTEasy - JSON integrations are always on the classpath

_Not_ always, but only if the user decides to include both in its pom.xml. For example, if user knows that the 3rd party dependency makes use of jackson but another makes use of json-b, then the user includes both, otherwise not:

<!-- actually including both is not possible -->
<dependency>
        <!-- this is needed because 
        my application makes use of json-b -->
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-resteasy-jsonb</artifactId>
</dependency>
<dependency>
        <!-- this is needed because 
        my application includes a library which makes use of jackson -->
        <!-- even if the library already includes its jackson-dependencies, we have to declare this? 
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-resteasy-jackson</artifactId>
</dependency>

We don't plan on supporting modular class loaders in Quarkus for now so I think I'm gonna close this one.

The error message is easy to understand: just add a default constructor. It's needed by most serialization library anyway.

just add a default constructor

@gsmet With default-constructor you mean a "no-arg-constructor". I cannot add this - the class is within a library only included in my pom.xml. and secondly, such a class (with final fields) cannot have a no-arg-constructor.

The error:

Can't create instance of a class: class com.domain.Task, 
No default constructor found.
    at org.eclipse.yasson.internal.serializer.ObjectDeserializer.getInstance(ObjectDeserializer.java:95)
    at org.eclipse.yasson.internal.serializer.AbstractContainerDeserializer.deserialize(AbstractContainerDeserializer.java:61)

is misleading because quarkus uses falsely json-b instead of jackson and this is the reason why it is not working. The library which includes Task.class explicitly declares jackson and not json-b..however quarkus ignores this. Using @JsonCreator eliminates the need of an empty default constructor in jackson by definition. However, quarkus ignores this and wants a default constructor even if no such constructor is needed because the class uses @JsonCreator .

The error message is easy to understand

As I described above: the origin error which lead to this error is hard to trace and not shown in this error log because it is indirectly caused by the fact I described above.

So the only reason why this works in wildfly (even without adding a no-arg-constructor) is that it has a modular class loader (jboss modules). Am I right?

Was this page helpful?
0 / 5 - 0 ratings