-
-
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
Can not deserialize json to enum value with Object-/Array-valued input, @JsonCreator
#3280
Comments
I have read a little source code, please let me note down here com.fasterxml.jackson.databind.deser.impl.PropertyValueBuffer public Object[] getParameters(SettableBeanProperty[] props)
throws JsonMappingException
{
// quick check to see if anything else is needed
if (_paramsNeeded > 0) {
if (_paramsSeenBig == null) {
int mask = _paramsSeen;
// not optimal, could use `Integer.trailingZeroes()`, but for now should not
// really matter for common cases
for (int ix = 0, len = _creatorParameters.length; ix < len; ++ix, mask >>= 1) {
if ((mask & 1) == 0) {
_creatorParameters[ix] = _findMissing(props[ix]);
}
}
} else {
final int len = _creatorParameters.length;
for (int ix = 0; (ix = _paramsSeenBig.nextClearBit(ix)) < len; ++ix) {
_creatorParameters[ix] = _findMissing(props[ix]);
}
}
}
if (_context.isEnabled(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES)) {
for (int ix = 0; ix < props.length; ++ix) {
if (_creatorParameters[ix] == null) {
SettableBeanProperty prop = props[ix];
_context.reportInputMismatch(prop,
"Null value for creator property '%s' (index %d); `DeserializationFeature.FAIL_ON_NULL_FOR_CREATOR_PARAMETERS` enabled",
prop.getName(), props[ix].getCreatorIndex());
}
}
}
return _creatorParameters;
}
public boolean assignParameter(SettableBeanProperty prop, Object value)
{
final int ix = prop.getCreatorIndex();
_creatorParameters[ix] = value;
if (_paramsSeenBig == null) {
int old = _paramsSeen;
int newValue = (old | (1 << ix));
if (old != newValue) {
_paramsSeen = newValue;
if (--_paramsNeeded <= 0) {
// 29-Nov-2016, tatu: But! May still require Object Id value
return (_objectIdReader == null) || (_idValue != null);
}
}
} else {
if (!_paramsSeenBig.get(ix)) {
_paramsSeenBig.set(ix);
if (--_paramsNeeded <= 0) {
// 29-Nov-2016, tatu: But! May still require Object Id value
}
}
}
return false;
} com.fasterxml.jackson.databind.deser.std.FactoryBasedEnumDeserializer // Method to deserialize the Enum using property based methodology
protected Object deserializeEnumUsingPropertyBased(final JsonParser p, final DeserializationContext ctxt,
final PropertyBasedCreator creator) throws IOException
{
PropertyValueBuffer buffer = creator.startBuilding(p, ctxt, null);
JsonToken t = p.currentToken();
for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) {
String propName = p.getCurrentName();
p.nextToken(); // to point to value
SettableBeanProperty creatorProp = creator.findCreatorProperty(propName);
if (creatorProp != null) {
buffer.assignParameter(creatorProp, _deserializeWithErrorWrapping(p, ctxt, creatorProp));
continue;
}
if (buffer.readIdProperty(propName)) {
continue;
}
}
return creator.build(ctxt, buffer);
} com.fasterxml.jackson.core.json.ReaderBasedJsonParser @Override
public final JsonToken nextToken() throws IOException
{
/* First: field names are special -- we will always tokenize
* (part of) value along with field name to simplify
* state handling. If so, can and need to use secondary token:
*/
if (_currToken == JsonToken.FIELD_NAME) {
return _nextAfterName();
}
// But if we didn't already have a name, and (partially?) decode number,
// need to ensure no numeric information is leaked
_numTypesValid = NR_UNKNOWN;
if (_tokenIncomplete) {
_skipString(); // only strings can be partial
}
int i = _skipWSOrEnd();
if (i < 0) { // end-of-input
// Should actually close/release things
// like input source, symbol table and recyclable buffers now.
close();
return (_currToken = null);
}
// clear any data retained so far
_binaryValue = null;
// Closing scope?
if (i == INT_RBRACKET || i == INT_RCURLY) {
_closeScope(i);
return _currToken;
}
// Nope: do we then expect a comma?
if (_parsingContext.expectComma()) {
i = _skipComma(i);
// Was that a trailing comma?
if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) {
if ((i == INT_RBRACKET) || (i == INT_RCURLY)) {
_closeScope(i);
return _currToken;
}
}
}
/* And should we now have a name? Always true for Object contexts, since
* the intermediate 'expect-value' state is never retained.
*/
boolean inObject = _parsingContext.inObject();
if (inObject) {
// First, field name itself:
_updateNameLocation();
String name = (i == INT_QUOTE) ? _parseName() : _handleOddName(i);
_parsingContext.setCurrentName(name);
_currToken = JsonToken.FIELD_NAME;
i = _skipColon();
}
_updateLocation();
// Ok: we must have a value... what is it?
JsonToken t;
switch (i) {
case '"':
_tokenIncomplete = true;
t = JsonToken.VALUE_STRING;
break;
case '[':
if (!inObject) {
_parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
}
t = JsonToken.START_ARRAY;
break;
case '{':
if (!inObject) {
_parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
}
t = JsonToken.START_OBJECT;
break;
case '}':
// Error: } is not valid at this point; valid closers have
// been handled earlier
_reportUnexpectedChar(i, "expected a value");
case 't':
_matchTrue();
t = JsonToken.VALUE_TRUE;
break;
case 'f':
_matchFalse();
t = JsonToken.VALUE_FALSE;
break;
case 'n':
_matchNull();
t = JsonToken.VALUE_NULL;
break;
case '-':
/* Should we have separate handling for plus? Although
* it is not allowed per se, it may be erroneously used,
* and could be indicate by a more specific error message.
*/
t = _parseNegNumber();
break;
case '.': // [core#61]]
t = _parseFloatThatStartsWithPeriod();
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
t = _parsePosNumber(i);
break;
default:
t = _handleOddValue(i);
break;
}
if (inObject) {
_nextToken = t;
return _currToken;
}
_currToken = t;
return t;
}
private final int _skipWSOrEnd() throws IOException
{
// Let's handle first character separately since it is likely that
// it is either non-whitespace; or we have longer run of white space
if (_inputPtr >= _inputEnd) {
if (!_loadMore()) {
return _eofAsNextChar();
}
}
int i = _inputBuffer[_inputPtr++];
if (i > INT_SPACE) {
if (i == INT_SLASH || i == INT_HASH) {
--_inputPtr;
return _skipWSOrEnd2();
}
return i;
}
if (i != INT_SPACE) {
if (i == INT_LF) {
++_currInputRow;
_currInputRowStart = _inputPtr;
} else if (i == INT_CR) {
_skipCR();
} else if (i != INT_TAB) {
_throwInvalidSpace(i);
}
}
while (_inputPtr < _inputEnd) {
i = (int) _inputBuffer[_inputPtr++];
if (i > INT_SPACE) {
if (i == INT_SLASH || i == INT_HASH) {
--_inputPtr;
return _skipWSOrEnd2();
}
return i;
}
if (i != INT_SPACE) {
if (i == INT_LF) {
++_currInputRow;
_currInputRowStart = _inputPtr;
} else if (i == INT_CR) {
_skipCR();
} else if (i != INT_TAB) {
_throwInvalidSpace(i);
}
}
}
return _skipWSOrEnd2();
}
private void _closeScope(int i) throws JsonParseException {
if (i == INT_RBRACKET) {
_updateLocation();
if (!_parsingContext.inArray()) {
_reportMismatchedEndMarker(i, '}');
}
_parsingContext = _parsingContext.clearAndGetParent();
_currToken = JsonToken.END_ARRAY;
}
if (i == INT_RCURLY) {
_updateLocation();
if (!_parsingContext.inObject()) {
_reportMismatchedEndMarker(i, ']');
}
_parsingContext = _parsingContext.clearAndGetParent();
_currToken = JsonToken.END_OBJECT;
}
} |
Hi @peteryuanpan! Thank you for reporting this, I will have a look. |
@JsonCreator
Ok, looks like what was missing is the skipping of content for structured values (Object, Array) -- they consist of more than one token. So needed to add:
within Fix will be in 2.13.0; I merged it in 2.12 but not sure if there will be new patch releases from there. |
Describe the bug
can not deserialize json to enum value in a particular case
Version information
Which Jackson version(s) was this for?
2.11.0
To Reproduce
If you have a way to reproduce this with:
The output is
can not deserialize json3 and json4, because there is "]" or "}" before the parameter "b"
Expected behavior
all output is false
Additional context
Add any other context about the problem here.
The text was updated successfully, but these errors were encountered: