-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
feat(#1381): Add a way to specify "inject-only" with @JacksonInject #5175
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
base: 2.x
Are you sure you want to change the base?
Changes from all commits
9a42cca
d714aeb
d6bee5f
bc094d8
a12a3d7
69c603e
191292c
5705aff
bc9d37e
272c499
5da46ac
deb1741
bf34e81
fac9b19
13715a3
03ba2cf
8423113
98abe58
402e3cb
bd2c4d7
0c2f3f3
202e5ec
e30140b
2da95a7
350f959
1e8578d
32e84cb
5c58665
5d32825
285989f
eaf80fd
be8aea6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -69,7 +69,15 @@ public void inject(DeserializationContext context, Object beanInstance) | |
throws IOException | ||
{ | ||
final Object value = findValue(context, beanInstance); | ||
if (!JacksonInject.Value.empty().equals(value)) { | ||
|
||
if (value == JacksonInject.Value.empty()) { | ||
if (Boolean.FALSE.equals(_optional)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is slightly wrong as it won't match
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, but I think it's more complicated than just flipping that condition, since now @DisplayName("input YES, injectable NO, useInput TRUE => input")
void test4() throws Exception {
assertEquals("input", plainMapper.readValue(input, InputTrue.class).getField()); In this case, I think that even if On the other side, there are a couple test that are currently passing but should not, such as this (the @DisplayName("FAIL_ON_UNKNOWN_INJECT_VALUE NO, input YES, injectable NO, useInput DEFAULT|FALSE => exception")
void test3() throws Exception {
assertEquals("input", plainMapper.readValue(input, InputDefault.class).getField()); Here we have:
So I think the user here should explicitly state that If we agree on the behavior I can start fixing the current implementation. |
||
throw context.missingInjectableValueException( | ||
String.format("No injectable value with id '%s' found (for property '%s')", | ||
_valueId, beanInstance), | ||
_valueId, null, beanInstance); | ||
} | ||
} else if (!Boolean.TRUE.equals(_useInput)) { | ||
_member.setValue(beanInstance, value); | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
package com.fasterxml.jackson.databind.deser.inject; | ||
|
||
import com.fasterxml.jackson.annotation.JacksonInject; | ||
import com.fasterxml.jackson.annotation.JsonCreator; | ||
import com.fasterxml.jackson.annotation.JsonProperty; | ||
import com.fasterxml.jackson.annotation.OptBoolean; | ||
import com.fasterxml.jackson.databind.DeserializationFeature; | ||
import com.fasterxml.jackson.databind.InjectableValues; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import com.fasterxml.jackson.databind.exc.ValueInstantiationException; | ||
import com.fasterxml.jackson.databind.testutil.DatabindTestUtil; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertThrows; | ||
|
||
class JacksonInject1381DeserializationFeatureDisabledTest extends DatabindTestUtil { | ||
static class InputDefault { | ||
@JacksonInject(value = "key") | ||
@JsonProperty("field") | ||
private final String _field; | ||
|
||
@JsonCreator | ||
public InputDefault(@JsonProperty("field") final String field) { | ||
_field = field; | ||
} | ||
|
||
public String getField() { | ||
return _field; | ||
} | ||
} | ||
|
||
static class InputDefaultConstructor { | ||
private final String _field; | ||
|
||
@JsonCreator | ||
public InputDefaultConstructor(@JacksonInject(value = "key") | ||
@JsonProperty("field") final String field) { | ||
_field = field; | ||
} | ||
|
||
public String getField() { | ||
return _field; | ||
} | ||
} | ||
|
||
static class InputTrue { | ||
@JacksonInject(value = "key", useInput = OptBoolean.TRUE) | ||
@JsonProperty("field") | ||
private final String _field; | ||
|
||
@JsonCreator | ||
public InputTrue(@JsonProperty("field") final String field) { | ||
_field = field; | ||
} | ||
|
||
public String getField() { | ||
return _field; | ||
} | ||
} | ||
|
||
static class InputTrueConstructor { | ||
private final String _field; | ||
|
||
@JsonCreator | ||
public InputTrueConstructor(@JacksonInject(value = "key", useInput = OptBoolean.TRUE) | ||
@JsonProperty("field") final String field) { | ||
_field = field; | ||
} | ||
|
||
public String getField() { | ||
return _field; | ||
} | ||
|
||
} | ||
|
||
static class InputFalse { | ||
@JacksonInject(value = "key", useInput = OptBoolean.FALSE) | ||
@JsonProperty("field") | ||
private final String _field; | ||
|
||
@JsonCreator | ||
public InputFalse(@JsonProperty("field") final String field) { | ||
_field = field; | ||
} | ||
|
||
public String getField() { | ||
return _field; | ||
} | ||
} | ||
|
||
static class InputFalseConstructor { | ||
private final String _field; | ||
|
||
@JsonCreator | ||
public InputFalseConstructor(@JacksonInject(value = "key", useInput = OptBoolean.FALSE) | ||
@JsonProperty("field") final String field) { | ||
_field = field; | ||
} | ||
|
||
public String getField() { | ||
return _field; | ||
} | ||
} | ||
|
||
private final String empty = "{}"; | ||
private final String input = "{\"field\": \"input\"}"; | ||
|
||
private final ObjectMapper plainMapper = jsonMapperBuilder() | ||
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_INJECT_VALUE) | ||
.build(); | ||
private final ObjectMapper injectedMapper = jsonMapperBuilder() | ||
.injectableValues(new InjectableValues.Std().addValue("key", "injected")) | ||
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_INJECT_VALUE) | ||
.build(); | ||
|
||
@Test | ||
@DisplayName("FAIL_ON_UNKNOWN_INJECT_VALUE NO, input NO, injectable NO, useInput DEFAULT|TRUE|FALSE => exception") | ||
void test1() { | ||
assertThrows(ValueInstantiationException.class, | ||
() -> plainMapper.readValue(empty, InputDefault.class)); | ||
assertThrows(ValueInstantiationException.class, | ||
() -> plainMapper.readValue(empty, InputDefaultConstructor.class)); | ||
|
||
assertThrows(ValueInstantiationException.class, | ||
() -> plainMapper.readValue(empty, InputTrue.class)); | ||
assertThrows(ValueInstantiationException.class, | ||
() -> plainMapper.readValue(empty, InputTrueConstructor.class)); | ||
|
||
assertThrows(ValueInstantiationException.class, | ||
() -> plainMapper.readValue(empty, InputFalse.class)); | ||
assertThrows(ValueInstantiationException.class, | ||
() -> plainMapper.readValue(empty, InputFalseConstructor.class)); | ||
} | ||
|
||
@Test | ||
@DisplayName("FAIL_ON_UNKNOWN_INJECT_VALUE NO, input NO, injectable YES, useInput DEFAULT|TRUE|FALSE => injected") | ||
void test2() throws Exception { | ||
assertEquals("injected", injectedMapper.readValue(empty, InputDefault.class).getField()); | ||
assertEquals("injected", injectedMapper.readValue(empty, InputDefaultConstructor.class).getField()); | ||
assertEquals("injected", injectedMapper.readValue(empty, InputTrue.class).getField()); | ||
assertEquals("injected", injectedMapper.readValue(empty, InputTrueConstructor.class).getField()); | ||
assertEquals("injected", injectedMapper.readValue(empty, InputFalse.class).getField()); | ||
assertEquals("injected", injectedMapper.readValue(empty, InputFalseConstructor.class).getField()); | ||
} | ||
|
||
@Test | ||
@DisplayName("FAIL_ON_UNKNOWN_INJECT_VALUE NO, input YES, injectable NO, useInput DEFAULT|FALSE => exception") | ||
void test3() throws Exception { | ||
assertEquals("input", plainMapper.readValue(input, InputDefault.class).getField()); | ||
assertEquals("input", plainMapper.readValue(input, InputDefaultConstructor.class).getField()); | ||
assertEquals("input", plainMapper.readValue(input, InputFalse.class).getField()); | ||
assertEquals("input", plainMapper.readValue(input, InputFalseConstructor.class).getField()); | ||
} | ||
|
||
@Test | ||
@DisplayName("FAIL_ON_UNKNOWN_INJECT_VALUE NO, input YES, injectable NO, useInput TRUE => input") | ||
void test4() throws Exception { | ||
assertEquals("input", plainMapper.readValue(input, InputTrue.class).getField()); | ||
assertEquals("input", plainMapper.readValue(input, InputTrueConstructor.class).getField()); | ||
} | ||
|
||
@Test | ||
@DisplayName("FAIL_ON_UNKNOWN_INJECT_VALUE NO, input YES, injectable YES, useInput DEFAULT|FALSE => injected") | ||
void test5() throws Exception { | ||
assertEquals("injected", injectedMapper.readValue(input, InputDefault.class).getField()); | ||
assertEquals("injected", injectedMapper.readValue(input, InputDefaultConstructor.class).getField()); | ||
assertEquals("injected", injectedMapper.readValue(input, InputFalse.class).getField()); | ||
assertEquals("injected", injectedMapper.readValue(input, InputFalseConstructor.class).getField()); | ||
} | ||
|
||
@Test | ||
@DisplayName("FAIL_ON_UNKNOWN_INJECT_VALUE NO, input YES, injectable YES, useInput TRUE => input") | ||
void test6() throws Exception { | ||
assertEquals("input", injectedMapper.readValue(input, InputTrue.class).getField()); | ||
assertEquals("input", injectedMapper.readValue(input, InputTrueConstructor.class).getField()); | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks odd; should second
||
actually be&&
? Or maybe it's just odd grouping of clauses with parenthesis.Should probably update comment to describe logic fully.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're returning
empty()
(we want to discard the injected value) in one of these cases:useInput
oroptional
istrue
useInput
oroptional
isnull
andFAIL_ON_UNKNOWN_INJECT_VALUE
is disabledYup, due to formatting, I think the precedence in the last two lines is not immediately clear
I'm updating the comment