yasson icon indicating copy to clipboard operation
yasson copied to clipboard

Issue with @JsonbCreator constructor with Generic Type in Yasson 3.0.0 and higher

Open mskacelik opened this issue 2 years ago • 4 comments

There seems to be a bug in Yasson version 3.0.0 and higher that affects the @JsonbCreator constructor when it has a Generic Type parameter. This issue is not present in Yasson version 2.0.4.

Stack trace from the reproducer (version 3.0.0 and higher):

Exception in thread "main" jakarta.json.bind.JsonbException: Error resolving runtime type for type: T
	at org.eclipse.yasson.internal.ReflectionUtils.lambda$getRawType$0(ReflectionUtils.java:92)
	at java.base/java.util.Optional.orElseThrow(Optional.java:403)
	at org.eclipse.yasson.internal.ReflectionUtils.getRawType(ReflectionUtils.java:92)
	at org.eclipse.yasson.internal.deserializer.DeserializationModelCreator.createObjectDeserializer(DeserializationModelCreator.java:236)
	at org.eclipse.yasson.internal.deserializer.DeserializationModelCreator.deserializerChainInternal(DeserializationModelCreator.java:193)
	at org.eclipse.yasson.internal.deserializer.DeserializationModelCreator.deserializerChain(DeserializationModelCreator.java:135)
	at org.eclipse.yasson.internal.deserializer.DeserializationModelCreator.createNewChain(DeserializationModelCreator.java:488)
	at org.eclipse.yasson.internal.deserializer.DeserializationModelCreator.typeProcessor(DeserializationModelCreator.java:477)
	at org.eclipse.yasson.internal.deserializer.DeserializationModelCreator.typeProcessor(DeserializationModelCreator.java:430)
	at org.eclipse.yasson.internal.deserializer.DeserializationModelCreator.createCollectionDeserializer(DeserializationModelCreator.java:273)
	at org.eclipse.yasson.internal.deserializer.DeserializationModelCreator.deserializerChainInternal(DeserializationModelCreator.java:183)
	at org.eclipse.yasson.internal.deserializer.DeserializationModelCreator.deserializerChain(DeserializationModelCreator.java:135)
	at org.eclipse.yasson.internal.deserializer.DeserializationModelCreator.createNewChain(DeserializationModelCreator.java:488)
	at org.eclipse.yasson.internal.deserializer.DeserializationModelCreator.typeProcessor(DeserializationModelCreator.java:477)
	at org.eclipse.yasson.internal.deserializer.DeserializationModelCreator.typeProcessor(DeserializationModelCreator.java:430)
	at org.eclipse.yasson.internal.deserializer.DeserializationModelCreator.createObjectDeserializer(DeserializationModelCreator.java:227)
	at org.eclipse.yasson.internal.deserializer.DeserializationModelCreator.deserializerChainInternal(DeserializationModelCreator.java:193)
	at org.eclipse.yasson.internal.deserializer.DeserializationModelCreator.deserializerChain(DeserializationModelCreator.java:135)
	at org.eclipse.yasson.internal.deserializer.DeserializationModelCreator.deserializerChain(DeserializationModelCreator.java:123)
	at org.eclipse.yasson.internal.DeserializationContextImpl.deserializeItem(DeserializationContextImpl.java:137)
	at org.eclipse.yasson.internal.DeserializationContextImpl.deserialize(DeserializationContextImpl.java:127)
	at org.eclipse.yasson.internal.JsonBinding.deserialize(JsonBinding.java:55)
	at org.eclipse.yasson.internal.JsonBinding.fromJson(JsonBinding.java:62)
	at Reproducer.main(Reproducer.java:27)

Reproducer using JBang:

///usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS org.eclipse:yasson:3.0.3


import jakarta.json.bind.Jsonb;
import jakarta.json.bind.JsonbConfig;
import jakarta.json.bind.JsonbBuilder;
import jakarta.json.bind.annotation.JsonbCreator;
import java.util.List;


public class Reproducer {

    public static void main(String... args) {
        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()
                .withFormatting(true)
                .withNullValues(true));
        String jsonString = "{\n" +
                "    \"id\": 0,\n" +
                "    \"multiLangRecords\": [{\n" +
                "      \"lang\": \"\",\n" +
                "      \"record\": {\n" +
                "        \"description\": \"\"\n" +
                "      }\n" +
                "    }]  \n" +
                "  }";
        System.out.println(jsonb.fromJson(jsonString, InsertTechnicalLocationInputJ.class));
        // EXPECTED: InsertTechnicalLocationInputJ[id=0, multiLangRecords=[InsertLangEntryGQLJ[lang=, record=InsertTechnicalLocationRecordGQLJ[description=]]]]
    }

    public record InsertTechnicalLocationInputJ(String id,
                                                List<InsertLangEntryGQLJ<InsertTechnicalLocationRecordGQLJ>> multiLangRecords) {

        @JsonbCreator
        public InsertTechnicalLocationInputJ {
        }
    }

    public record InsertTechnicalLocationRecordGQLJ(String description) {

        @JsonbCreator
        public InsertTechnicalLocationRecordGQLJ {
        }
    }

    public record InsertLangEntryGQLJ<T>(String lang, T record) {
        @JsonbCreator
        public InsertLangEntryGQLJ {
        }
    }

}

Switching yasson's version (line starting //DEPS) from 3.0.3 to 2.0.4 resolves the issue.

JBang documentation and how to download it: https://www.jbang.dev/

mskacelik avatar May 05 '23 10:05 mskacelik

The first commit where this error gets thrown is b74033d Not sure what caused this because the diff is huge

Degubi avatar Jul 10 '23 06:07 Degubi

It seems we are facing the same issue using Yasson 3.0.2 (which ships with WildFly 30):

jakarta.ws.rs.ProcessingException: RESTEASY008200: JSON Binding deserialization error: jakarta.json.bind.JsonbException: Error resolving runtime type for type: T
	at [email protected]//org.jboss.resteasy.plugins.providers.jsonb.JsonBindingProvider.readFrom(JsonBindingProvider.java:78
	
[..]
Caused by: jakarta.json.bind.JsonbException: Error resolving runtime type for type: T
	at org.eclipse.yasson//org.eclipse.yasson.internal.ReflectionUtils.lambda$getRawType$0(ReflectionUtils.java:92)
	at java.base/java.util.Optional.orElseThrow(Optional.java:403)
	at org.eclipse.yasson//org.eclipse.yasson.internal.ReflectionUtils.getRawType(ReflectionUtils.java:92)
	at org.eclipse.yasson//org.eclipse.yasson.internal.deserializer.DeserializationModelCreator.createObjectDeserializer(DeserializationModelCreator.java:236)
	at org.eclipse.yasson//org.eclipse.yasson.internal.deserializer.DeserializationModelCreator.deserializerChainInternal(DeserializationModelCreator.java:193)
	at org.eclipse.yasson//org.eclipse.yasson.internal.deserializer.DeserializationModelCreator.deserializerChain(DeserializationModelCreator.java:135)
	at org.eclipse.yasson//org.eclipse.yasson.internal.deserializer.DeserializationModelCreator.deserializerChain(DeserializationModelCreator.java:123)
	at org.eclipse.yasson//org.eclipse.yasson.internal.DeserializationContextImpl.deserializeItem(DeserializationContextImpl.java:137)
	at org.eclipse.yasson//org.eclipse.yasson.internal.DeserializationContextImpl.deserialize(DeserializationContextImpl.java:127)
	at org.eclipse.yasson//org.eclipse.yasson.internal.JsonBinding.deserialize(JsonBinding.java:55)
	at org.eclipse.yasson//org.eclipse.yasson.internal.JsonBinding.fromJson(JsonBinding.java:95)
	at [email protected]//org.jboss.resteasy.plugins.providers.jsonb.ManagedJsonb.fromJson(ManagedJsonb.java:73)
	at [email protected]//org.jboss.resteasy.plugins.providers.jsonb.JsonBindingProvider.readFrom(JsonBindingProvider.java:71)	
```	

In our case the Java data class in question which Yasson is trying to deserialize is:

public class RuleResponse<T> {

private final T result;

@JsonbCreator
public RuleResponse(@JsonbProperty("result") final T result) {
    this.result = result;
}

public T getResult() {
    return result;
}

}


Is there a workaround for this issue? We cannot just downgrade Yasson because Yasson 3 is used internally by WildFly a lot as well it seems.

edgarvonk avatar Nov 09 '23 13:11 edgarvonk

@edgarvonk Possible workaround was brought up here: https://github.com/smallrye/smallrye-graphql/issues/1819#issuecomment-1549588537

mskacelik avatar Nov 09 '23 14:11 mskacelik

Thanks @mskacelik ! It seems to do the trick indeed. A rather cumbersome workaround for us however since our code base is large with quite a huge number of Java value classes that do not implement an interface nor extend from an abstract class which are used as generic types in JSON deserialisation..

edgarvonk avatar Nov 09 '23 14:11 edgarvonk

This likely came in on #537. ~~I've locally got a fix for it, but I'm working to see if I can write a test for it.~~ The fix I've got locally fixes a slightly different issue I'm seeing which I'll file an issue and a fix for.

jamezp avatar Feb 03 '25 20:02 jamezp