You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Describe the bug
I've discovered what appears to be a regression between 2.11.2 and 2.11.3 involving a combination of generic type references, and the use of static builder methods annotated with `@JsonCreator'
Version information
2.11.3
To Reproduce
Plain unit test:
packageregression;
importjava.io.IOException;
importorg.junit.jupiter.api.Assertions;
importorg.junit.jupiter.api.Test;
importcom.fasterxml.jackson.annotation.JsonCreator;
importcom.fasterxml.jackson.annotation.JsonProperty;
importcom.fasterxml.jackson.core.type.TypeReference;
importcom.fasterxml.jackson.databind.ObjectMapper;
publicclassJackson2Dot11Dot3Test {
@TestpublicvoidtestPolymorphism() throwsIOException {
Holder<TypedChild> holder = Holder.of(newTypedChild("jackson"));
ObjectMapperom = newObjectMapper();
Assertions.assertEquals("{\"value\":{\"name\":\"jackson\"}}", om.writeValueAsString(holder));
Holder<?> readBack = om.readValue(om.writeValueAsString(holder), newHolderChildReference());
// In Jackson 2.11.2, this is true, but in 2.11.3, 'value' has been unmarshalled as a plain LinkedHashMap// instead ? Seems to be the fact the JsonCreator is a public 'of' method rather than constructorAssertions.assertTrue(readBack.getValue() instanceofTypedChild);
}
privatestaticclassHolderChildReferenceextendsTypeReference<Holder<TypedChild>> {}
publicstaticclassHolder<V> {
privateVvalue;
privateHolder(Vvalue) {
this.value = value;
}
// Use this as the constructor instead, and the test passes in both 2.11.2 and 2.11.3./*@JsonCreator private Holder(@JsonProperty("value") V value) { this.value = value; }*/publicVgetValue() {
returnvalue;
}
@JsonCreatorpublicstatic <V> Holder<V> of(@JsonProperty("value") Vvalue) {
returnnewHolder<>(value);
}
}
publicstaticclassTypedChild {
privateStringname;
@JsonCreatorpublicTypedChild(@JsonProperty("name") Stringname) {
this.name = name;
}
publicStringgetName() {
returnname;
}
}
}
Expected behavior
The unmarshalled JSON uses the passed type reference information when unmarshalling the child object, so that the unit test passes.
Having tried to fix a more complicated example in my actual codebase by moving the @jsoncreator over to the constructor instead, my more complicated implementation still fails with what also looks like another failure to resolve the generic types from the provided type reference, so perhaps it's more of a bug about the processing of type references than it is about the use of static builder methods.
I'll see if I can narrow down a test case demonstrating the 2nd failure as well which doesn't look like it requires the static builder method to trigger in my codebase.
EDIT: No, I realised that as I'm using the immutables framework (https://github.com/immutables/immutables/) in my actual codebase, that also uses static @JsonCreator methods for its objects, and so it's the same issue.
I've narrowed down the slightly more complicated case I referred to above, which does look like another symptom, but this time the test throws an exception rather than just unmarshalling as the wrong type. Again, the Parent class here uses a static of method as its @JsonCreator. Uncommenting the constructor method @JsonCreator annotation is enough to make it work again.
packageregression;
importjava.io.IOException;
importorg.junit.jupiter.api.Assertions;
importorg.junit.jupiter.api.Test;
importcom.fasterxml.jackson.annotation.JsonCreator;
importcom.fasterxml.jackson.annotation.JsonProperty;
importcom.fasterxml.jackson.core.type.TypeReference;
importcom.fasterxml.jackson.databind.ObjectMapper;
publicclassJackson2Dot11Dot3Test {
@TestpublicvoidtestPolymorphism() throwsIOException {
GrandParent<Parent<Child>, Child> holder = GrandParent.of(newParent<>(newChild("jackson")));
ObjectMapperom = newObjectMapper();
Assertions.assertEquals("{\"parent\":{\"child\":{\"name\":\"jackson\"}}}", om.writeValueAsString(holder));
// In Jackson 2.11.2, this all works and passes, but in 2.11.3, an exception is thrown as the// static creation of the Parent doesn't know how to deal with the type of the Child by the looks// of thingsGrandParent<?, ?> readBack = om.readValue(om.writeValueAsString(holder), newAncestryReference());
Assertions.assertTrue(readBack.getParent() instanceofParent);
Assertions.assertTrue(readBack.getParent().getChild() instanceofChild);
Assertions.assertEquals("jackson", readBack.getParent().getChild().getName());
}
privatestaticclassAncestryReferenceextendsTypeReference<GrandParent<Parent<Child>, Child>> {}
publicstaticclassGrandParent<PextendsIParent<C>, CextendsIChild> {
privatePvalue;
@JsonCreatorprivateGrandParent(@JsonProperty("parent") Pparent) {
this.value = parent;
}
publicPgetParent() {
returnvalue;
}
publicstatic <PextendsIParent<C>, CextendsIChild> GrandParent<P, C> of(@JsonProperty("parent") Pparent) {
returnnewGrandParent<>(parent);
}
}
publicinterfaceIParent<CextendsIChild> {
CgetChild();
}
publicstaticclassParent<CextendsIChild> implementsIParent<C> {
privateCchild;
// @JsonCreator // **** Uncomment this and the test passes in both 2.11.2 and 2.11.3privateParent(@JsonProperty("child") Cchild) {
this.child = child;
}
@OverridepublicCgetChild() {
returnchild;
}
@JsonCreatorpublicstatic <CextendsIChild> Parent<C> of(@JsonProperty("child") Cchild) {
returnnewParent<>(child);
}
}
publicinterfaceIChild {
StringgetName();
}
publicstaticclassChildimplementsIChild {
privateStringname;
@JsonCreatorpublicChild(@JsonProperty("name") Stringname) {
this.name = name;
}
@OverridepublicStringgetName() {
returnname;
}
// @JsonCreatorpublicstaticChildof(@JsonProperty("name") Stringname) {
returnnewChild(name);
}
}
}
@stevestorey This may be same as #2894, patch for which is included in 2.11 branch (for 2.11.4). I will try that when I get a chance (there are a few issues I need to resolve first), but if you can either build locally against 2.11 branch or use 2.11.4-SNAPSHOT from Sonatype OSS repo maybe you could see if that would resolve this issue.
@cowtowncoder yes, you're right - upgrading to 2.11.4-SNAPSHOT means that both test examples above work just fine, so I'll mark this closed as a duplicate of #2894
Activity
stevestorey commentedon Nov 2, 2020
Having tried to fix a more complicated example in my actual codebase by moving the @jsoncreator over to the constructor instead, my more complicated implementation still fails with what also looks like another failure to resolve the generic types from the provided type reference, so perhaps it's more of a bug about the processing of type references than it is about the use of static builder methods.
I'll see if I can narrow down a test case demonstrating the 2nd failure as well which doesn't look like it requires the static builder method to trigger in my codebase.
EDIT: No, I realised that as I'm using the immutables framework (https://github.com/immutables/immutables/) in my actual codebase, that also uses static
@JsonCreatormethods for its objects, and so it's the same issue.stevestorey commentedon Nov 2, 2020
I've narrowed down the slightly more complicated case I referred to above, which does look like another symptom, but this time the test throws an exception rather than just unmarshalling as the wrong type. Again, the Parent class here uses a static
ofmethod as its@JsonCreator. Uncommenting the constructor method@JsonCreatorannotation is enough to make it work again.cowtowncoder commentedon Nov 3, 2020
@stevestorey This may be same as #2894, patch for which is included in 2.11 branch (for 2.11.4). I will try that when I get a chance (there are a few issues I need to resolve first), but if you can either build locally against 2.11 branch or use 2.11.4-SNAPSHOT from Sonatype OSS repo maybe you could see if that would resolve this issue.
stevestorey commentedon Nov 3, 2020
@cowtowncoder yes, you're right - upgrading to 2.11.4-SNAPSHOT means that both test examples above work just fine, so I'll mark this closed as a duplicate of #2894
cowtowncoder commentedon Nov 3, 2020
@stevestorey Thank you for verifying this! (and also reporting: better to have multiple reports than none, just in case)