-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
@JacksonInject
will try to fetch deserializer for injected creator property if there is visible field
#2465
Comments
Looks like it only tries to find empty constructor. If you add empty constructor, everything will work fine. Empty constructor not called. This works fine: import com.fasterxml.jackson.annotation.JacksonInject;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.annotation.Nulls;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.InjectableValues;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
public class Test {
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.setDefaultSetterInfo(JsonSetter.Value.construct(Nulls.AS_EMPTY, Nulls.AS_EMPTY)); // not working?
mapper.setVisibility(mapper.getVisibilityChecker().withVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY));
TestCase o = mapper.reader(new InjectableValues.Std().addValue(Internal.class, new Internal("test")))
.forType(TestCase.class)
.readValue("{\"id\":3}");
System.out.println(o.str.val);
}
public static final class TestCase {
private final Internal str;
private final int id;
@JsonCreator
public TestCase(@JacksonInject Internal str, @JsonProperty("id") int id) {
this.str = str;
this.id = id;
}
}
public static final class Internal {
private final String val;
public Internal() {
if (true) throw new RuntimeException("Can't call this");
val = null;
}
public Internal(String val) {
this.val = val;
}
}
} |
Interesting. Thank you for providing all the information -- I hope to look into this in future, but right now there is a backlog of issues so it may take a while. But I may be able to add failing unit test quickly. |
Based on symptoms, I think that lack of setter/field (unless overridden by visibility to find it) somehow triggers the problem: addition of "set as 'empty' if 'null' passed" will trigger the exception but probably would just mask the real problem. |
Ok, some semi-good news. With fixing of #962, 2.11.0 will have a way to prevent the issue. First
and this should be used to avoid having to find out deserializer since value will never be read from input (content being bound). This is needed regardless. However, although this should be enough in and of itself, there is a problem with field @JsonIgnore
private final Internal2465 str; and that combination should avoid the problem, once 2.11.0 is released. |
@JacksonInject
will try to fetch deserializer for injected creator property if there is visible field
One sidenote: Jackson 3.0 does not seem to fail for the test, for whatever reason. |
I found this issue on my own (see the now-closed #3124), and here's a smaller test, if you want that: import com.fasterxml.jackson.annotation.JacksonInject;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.InjectableValues;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
public class CursedInjectionDeserializerTest {
@Test
void testInjectionBlackMagic() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
mapper.setInjectableValues(new InjectableValues.Std().addValue(Injected.class, new Injected()));
TestCase test = mapper.readValue("{\"id\": 3}", TestCase.class);
System.out.println(test.id);
}
public static final class TestCase {
private final Injected injected;
private final int id;
public TestCase(@JacksonInject Injected injected,
@JsonProperty("id") int id) {
this.injected = injected;
this.id = id;
}
}
public static final class Injected {
private void setAbcd(Void a) {}
private void setAbcd(Boolean a) {}
}
} I used some parts of what I found in #3124, and the existing test case you had. |
Everything works as expected if you remove
or
Otherwise error thrown:
Cannot create empty instance of [simple type, class dk.xakeps.jacksontest.Test$Internal], no default Creator
jackson-databind 2.9.9.3
Test class:
The text was updated successfully, but these errors were encountered: