Skip to content

Commit

Permalink
Further improvements related to #1711 to both catch problem AND to al…
Browse files Browse the repository at this point in the history
…low handling via `DeserializationProblemHandler`
  • Loading branch information
cowtowncoder committed Jul 26, 2017
1 parent 659408c commit 23a610d
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -944,7 +944,7 @@ public Object handleWeirdNumberValue(Class<?> targetClass, Number value,
if ((key == null) || targetClass.isInstance(key)) {
return key;
}
throw weirdNumberException(value, targetClass, String.format(
throw weirdNumberException(value, targetClass, _format(
"DeserializationProblemHandler.handleWeirdNumberValue() for type %s returned value of type %s",
targetClass, key.getClass()));
}
Expand All @@ -953,6 +953,28 @@ public Object handleWeirdNumberValue(Class<?> targetClass, Number value,
throw weirdNumberException(value, targetClass, msg);
}

public Object handleWeirdNativeValue(JavaType targetType, Object badValue,
JsonParser p)
throws IOException
{
LinkedNode<DeserializationProblemHandler> h = _config.getProblemHandlers();
final Class<?> raw = targetType.getRawClass();
for (; h != null; h = h.next()) {
// Can bail out if it's handled
Object goodValue = h.value().handleWeirdNativeValue(this, targetType, badValue, p);
if (goodValue != DeserializationProblemHandler.NOT_HANDLED) {
// Sanity check for broken handlers, otherwise nasty to debug:
if ((goodValue == null) || raw.isInstance(goodValue)) {
return goodValue;
}
throw JsonMappingException.from(p, _format(
"DeserializationProblemHandler.handleWeirdNativeValue() for type %s returned value of type %s",
targetType, goodValue.getClass()));
}
}
throw weirdNativeValueException(badValue, raw);
}

/**
* Method that deserializers should call if they fail to instantiate value
* due to lack of viable instantiator (usually creator, that is, constructor
Expand Down Expand Up @@ -1531,6 +1553,24 @@ public JsonMappingException weirdNumberException(Number value, Class<?> instClas
value, instClass);
}

/**
* Helper method for constructing exception to indicate that input JSON
* token of type "native value" (see {@link JsonToken#VALUE_EMBEDDED_OBJECT})
* is of incompatible type (and there is no delegating creator or such to use)
* and can not be used to construct value of specified type (usually POJO).
* Note that most of the time this method should NOT be called; instead,
* {@link #handleWeirdNativeValue} should be called which will call this method
*
* @since 2.9
*/
public JsonMappingException weirdNativeValueException(Object value, Class<?> instClass)
{
return InvalidFormatException.from(_parser, String.format(
"Cannot deserialize value of type %s from native value (`JsonToken.VALUE_EMBEDDED_OBJECT`) of type %s: incompatible types",
ClassUtil.nameOf(instClass), ClassUtil.classNameOf(value)),
value, instClass);
}

/**
* Helper method for constructing instantiation exception for specified type,
* to indicate problem with physically constructing instance of
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1459,10 +1459,19 @@ public Object deserializeFromEmbedded(JsonParser p, DeserializationContext ctxt)
return bean;
}
}

// TODO: maybe add support for ValueInstantiator, embedded?

return p.getEmbeddedObject();

// 26-Jul-2017, tatu: related to [databind#1711], let's actually verify assignment
// compatibility before returning. Bound to catch misconfigured cases and produce
// more meaningful exceptions.
Object value = p.getEmbeddedObject();
if (value != null) {
if (!_beanType.getClass().isInstance(value)) {
// allow this to be handled...
value = ctxt.handleWeirdNativeValue(_beanType, value, p);
}
}
return value;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,8 @@ public Object handleWeirdKey(DeserializationContext ctxt,
* to indicate type of failure unless handler produces key to use
*
* @return Either {@link #NOT_HANDLED} to indicate that handler does not know
* what to do (and exception may be thrown), or value to use as key (possibly
* <code>null</code>
* what to do (and exception may be thrown), or value to use as (possibly
* <code>null</code>)
*
* @since 2.8
*/
Expand Down Expand Up @@ -161,14 +161,41 @@ public Object handleWeirdStringValue(DeserializationContext ctxt,
* to indicate type of failure unless handler produces key to use
*
* @return Either {@link #NOT_HANDLED} to indicate that handler does not know
* what to do (and exception may be thrown), or value to use as key (possibly
* <code>null</code>
* what to do (and exception may be thrown), or value to use as (possibly
* <code>null</code>)
*
* @since 2.8
*/
public Object handleWeirdNumberValue(DeserializationContext ctxt,
Class<?> targetType, Number valueToConvert,
String failureMsg)
Class<?> targetType, Number valueToConvert, String failureMsg)
throws IOException
{
return NOT_HANDLED;
}

/**
* Method called when an embedded (native) value ({@link JsonToken#VALUE_EMBEDDED_OBJECT})
* cannot be converted directly into expected value type (usually POJO).
* Handler may choose to do one of 3 things:
*<ul>
* <li>Indicate it does not know what to do by returning {@link #NOT_HANDLED}
* </li>
* <li>Throw a {@link IOException} to indicate specific fail message (instead of
* standard exception caller would throw
* </li>
* <li>Return actual converted value (of type <code>targetType</code>) to use as
* replacement, and continue processing.
* </li>
* </ul>
*
* @return Either {@link #NOT_HANDLED} to indicate that handler does not know
* what to do (and exception may be thrown), or value to use (possibly
* <code>null</code>)
*
* @since 2.9
*/
public Object handleWeirdNativeValue(DeserializationContext ctxt,
JavaType targetType, Object valueToConvert, JsonParser p)
throws IOException
{
return NOT_HANDLED;
Expand Down

0 comments on commit 23a610d

Please sign in to comment.