Skip to content

Commit

Permalink
More for #1207: now add `DeserializationContext.handleUnexpectedToken…
Browse files Browse the repository at this point in the history
…()` (and handler method in `DeserializationProblemHandler`), for the common failure mode of getting JSON token of unexpected type
  • Loading branch information
cowtowncoder committed May 22, 2016
1 parent 6824544 commit 851092c
Show file tree
Hide file tree
Showing 26 changed files with 303 additions and 304 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -770,7 +770,7 @@ public <T> T readValue(JsonParser p, Class<T> type) throws IOException {
public <T> T readValue(JsonParser p, JavaType type) throws IOException {
JsonDeserializer<Object> deser = findRootValueDeserializer(type);
if (deser == null) {
throw mappingException("Could not find JsonDeserializer for type %s", type);
throw mappingException("Could not find JsonDeserializer for type "+type);
}
return (T) deser.deserialize(p, this);
}
Expand All @@ -795,8 +795,9 @@ public <T> T readPropertyValue(JsonParser p, BeanProperty prop, JavaType type) t
JsonDeserializer<Object> deser = findContextualValueDeserializer(type, prop);
if (deser == null) {
String propName = (prop == null) ? "NULL" : ("'"+prop.getName()+"'");
throw mappingException("Could not find JsonDeserializer for type %s (via property %s)",
type, propName);
throw mappingException(String.format(
"Could not find JsonDeserializer for type %s (via property %s)",
type, propName));
}
return (T) deser.deserialize(p, this);
}
Expand Down Expand Up @@ -1059,6 +1060,71 @@ public Object handleInstantiationProblem(Class<?> instClass, Object argument,
throw instantiationException(instClass, t);
}

/**
* Method that deserializers should call if the first token of the value to
* deserialize is of unexpected type (that is, type of token that deserializer
* can not handle). This could occur, for example, if a Number deserializer
* encounter {@link JsonToken#START_ARRAY} instead of
* {@link JsonToken#VALUE_NUMBER_INT} or {@link JsonToken#VALUE_NUMBER_FLOAT}.
*
* @param instClass Type that was to be instantiated
* @param p Parser that points to the JSON value to decode
*
* @return Object that should be constructed, if any; has to be of type <code>instClass</code>
*
* @since 2.8
*/
public Object handleUnexpectedToken(Class<?> instClass, JsonParser p)
throws IOException
{
return handleUnexpectedToken(instClass, p.getCurrentToken(), p, null);
}

/**
* Method that deserializers should call if the first token of the value to
* deserialize is of unexpected type (that is, type of token that deserializer
* can not handle). This could occur, for example, if a Number deserializer
* encounter {@link JsonToken#START_ARRAY} instead of
* {@link JsonToken#VALUE_NUMBER_INT} or {@link JsonToken#VALUE_NUMBER_FLOAT}.
*
* @param instClass Type that was to be instantiated
* @param p Parser that points to the JSON value to decode
*
* @return Object that should be constructed, if any; has to be of type <code>instClass</code>
*
* @since 2.8
*/
public Object handleUnexpectedToken(Class<?> instClass, JsonToken t,
JsonParser p,
String msg, Object... msgArgs)
throws IOException
{
if (msgArgs.length > 0) {
msg = String.format(msg, msgArgs);
}
/*
LinkedNode<DeserializationProblemHandler> h = _config.getProblemHandlers();
while (h != null) {
Object instance = h.value().handleUnexpectedToken(this,
instClass, t, p, msg);
if (instance != DeserializationProblemHandler.NOT_HANDLED) {
if ((instance == null) || instClass.isInstance(instance)) {
return instance;
}
throw mappingException(String.format(
"DeserializationProblemHandler.handleUnexpectedToken() for type %s returned value of type %s",
instClass, instance.getClass()));
}
h = h.next();
}
*/
if (msg == null) {
msg = String.format("Can not deserialize instance of %s out of %s token",
_calcName(instClass), t);
}
throw mappingException(msg);
}

/**
* Method that deserializers should call if they encounter a type id
* (for polymorphic deserialization) that can not be resolved to an
Expand Down Expand Up @@ -1123,7 +1189,7 @@ public void reportWrongTokenException(JsonParser p,
JsonToken expToken, String msg, Object... msgArgs)
throws JsonMappingException
{
if (msgArgs.length > 0) {
if ((msg != null) && (msgArgs.length > 0)) {
msg = String.format(msg, msgArgs);
}
throw wrongTokenException(p, expToken, msg);
Expand Down Expand Up @@ -1166,50 +1232,18 @@ public void reportMappingException(String msg, Object... msgArgs)
throw mappingException(msg);
}

/**
* @since 2.8
*/
public void reportMappingException(Class<?> targetClass, JsonToken t) throws JsonMappingException {
throw mappingException(targetClass, t);
}

/**
* @since 2.8
*/
public void reportMappingException(Class<?> targetClass) throws JsonMappingException {
throw mappingException(targetClass);
}

/*
/**********************************************************
/* Methods for constructing exceptions, "untyped"
/**********************************************************
*/

/**
* Helper method for constructing generic mapping exception for specified type
*
* @deprecated Since 2.8 use {@link #reportMappingException(Class)} instead
*/
@Deprecated
public JsonMappingException mappingException(Class<?> targetClass) {
return mappingException(targetClass, _parser.getCurrentToken());
}

/**
* @deprecated Since 2.8 use {@link #reportMappingException(Class, JsonToken)} instead
*/
@Deprecated
public JsonMappingException mappingException(Class<?> targetClass, JsonToken token) {
return JsonMappingException.from(_parser,
String.format("Can not deserialize instance of %s out of %s token",
_calcName(targetClass), token));
}

/**
* @deprecated Since 2.8 use {@link #reportMappingException(String, Object...)} instead
* Helper method for constructing generic mapping exception with specified
* message and current location information
*
* @since 2.6
*/
@Deprecated
public JsonMappingException mappingException(String message) {
return JsonMappingException.from(getParser(), message);
}
Expand All @@ -1219,17 +1253,34 @@ public JsonMappingException mappingException(String message) {
* message and current location information
*
* @since 2.6
*
* @deprecated Since 2.8 use {@link #reportMappingException(String, Object...)} instead
*/
@Deprecated
public JsonMappingException mappingException(String msgTemplate, Object... args) {
if (args != null && args.length > 0) {
msgTemplate = String.format(msgTemplate, args);
}
return JsonMappingException.from(getParser(), msgTemplate);
}

/**
* Helper method for constructing generic mapping exception for specified type
*
* @deprecated Since 2.8 use {@link #handleUnexpectedToken(Class, JsonParser)} instead
*/
@Deprecated
public JsonMappingException mappingException(Class<?> targetClass) {
return mappingException(targetClass, _parser.getCurrentToken());
}

/**
* @deprecated Since 2.8 use {@link #handleUnexpectedToken(Class, JsonParser)} instead
*/
@Deprecated
public JsonMappingException mappingException(Class<?> targetClass, JsonToken token) {
return JsonMappingException.from(_parser,
String.format("Can not deserialize instance of %s out of %s token",
_calcName(targetClass), token));
}

/*
/**********************************************************
/* Methods for constructing semantic exceptions; usually not
Expand Down Expand Up @@ -1359,7 +1410,7 @@ public JsonMappingException unknownTypeIdException(JavaType baseType, String typ

/*
/**********************************************************
/* Methods for constructing semantic exceptions
/* Deprecated exception factory methods
/**********************************************************
*/

Expand Down Expand Up @@ -1393,7 +1444,7 @@ public JsonMappingException endOfInputException(Class<?> instClass) {

/*
/**********************************************************
/* Overridable internal methods
/* Other internal methods
/**********************************************************
*/

Expand All @@ -1415,12 +1466,6 @@ protected DateFormat getDateFormat()
protected String determineClassName(Object instance) {
return ClassUtil.getClassDescription(instance);
}

/*
/**********************************************************
/* Other internal methods
/**********************************************************
*/

protected String _calcName(Class<?> cls) {
if (cls.isArray()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,7 @@ protected final Object _deserializeOther(JsonParser p, DeserializationContext ct
return deserializeFromObject(p, ctxt);
default:
}
ctxt.reportMappingException(handledType());
return null;
return ctxt.handleUnexpectedToken(handledType(), p);
}

@Deprecated // since 2.8; remove unless getting used
Expand Down Expand Up @@ -514,8 +513,7 @@ protected Object deserializeFromNull(JsonParser p, DeserializationContext ctxt)
p2.close();
return ob;
}
ctxt.reportMappingException(handledType());
return null;
return ctxt.handleUnexpectedToken(handledType(), p);
}

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1353,11 +1353,10 @@ public Object deserializeFromArray(JsonParser p, DeserializationContext ctxt) th
if (t == JsonToken.END_ARRAY) {
return null;
}
ctxt.reportMappingException(handledType(), JsonToken.START_ARRAY);
return null;
return ctxt.handleUnexpectedToken(handledType(),
JsonToken.START_ARRAY, p, null);
}
ctxt.reportMappingException(handledType());
return null;
return ctxt.handleUnexpectedToken(handledType(), p);
}

public Object deserializeFromEmbedded(JsonParser p, DeserializationContext ctxt)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ protected final Object finishBuild(DeserializationContext ctxt, Object builder)
*/
@Override
public final Object deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException
throws IOException
{
JsonToken t = p.getCurrentToken();

Expand All @@ -157,28 +157,29 @@ public final Object deserialize(JsonParser p, DeserializationContext ctxt)
return finishBuild(ctxt, builder);
}
// and then others, generally requiring use of @JsonCreator
switch (t) {
case VALUE_STRING:
return finishBuild(ctxt, deserializeFromString(p, ctxt));
case VALUE_NUMBER_INT:
return finishBuild(ctxt, deserializeFromNumber(p, ctxt));
case VALUE_NUMBER_FLOAT:
return finishBuild(ctxt, deserializeFromDouble(p, ctxt));
case VALUE_EMBEDDED_OBJECT:
return p.getEmbeddedObject();
case VALUE_TRUE:
case VALUE_FALSE:
return finishBuild(ctxt, deserializeFromBoolean(p, ctxt));
case START_ARRAY:
// these only work if there's a (delegating) creator...
return finishBuild(ctxt, deserializeFromArray(p, ctxt));
case FIELD_NAME:
case END_OBJECT:
return finishBuild(ctxt, deserializeFromObject(p, ctxt));
default:
ctxt.reportMappingException(handledType());
return null;
if (t != null) {
switch (t) {
case VALUE_STRING:
return finishBuild(ctxt, deserializeFromString(p, ctxt));
case VALUE_NUMBER_INT:
return finishBuild(ctxt, deserializeFromNumber(p, ctxt));
case VALUE_NUMBER_FLOAT:
return finishBuild(ctxt, deserializeFromDouble(p, ctxt));
case VALUE_EMBEDDED_OBJECT:
return p.getEmbeddedObject();
case VALUE_TRUE:
case VALUE_FALSE:
return finishBuild(ctxt, deserializeFromBoolean(p, ctxt));
case START_ARRAY:
// these only work if there's a (delegating) creator...
return finishBuild(ctxt, deserializeFromArray(p, ctxt));
case FIELD_NAME:
case END_OBJECT:
return finishBuild(ctxt, deserializeFromObject(p, ctxt));
default:
}
}
return ctxt.handleUnexpectedToken(handledType(), p);
}

/**
Expand All @@ -189,7 +190,7 @@ public final Object deserialize(JsonParser p, DeserializationContext ctxt)
@Override
public Object deserialize(JsonParser p, DeserializationContext ctxt,
Object builder)
throws IOException, JsonProcessingException
throws IOException
{
/* Important: we call separate method which does NOT call
* 'finishBuild()', to avoid problems with recursion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.io.IOException;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JavaType;
Expand Down Expand Up @@ -172,6 +173,41 @@ public Object handleWeirdNumberValue(DeserializationContext ctxt,
return NOT_HANDLED;
}

/**
* Method that deserializers should call if the first token of the value to
* deserialize is of unexpected type (that is, type of token that deserializer
* can not handle). This could occur, for example, if a Number deserializer
* encounter {@link JsonToken#START_ARRAY} instead of
* {@link JsonToken#VALUE_NUMBER_INT} or {@link JsonToken#VALUE_NUMBER_FLOAT}.
*<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>Handle content to match (by consuming or skipping it), and return actual
* instantiated value (of type <code>targetType</code>) to use as replacement;
* value may be `null` as well as expected target type.
* </li>
* </ul>
*
* @param failureMsg Message that will be used by caller
* to indicate type of failure unless handler produces value 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 (possibly
* <code>null</code>
*
* @since 2.8
*/
public Object handleUnexpectedToken(DeserializationContext ctxt,
Class<?> targetType, JsonToken t, JsonParser p,
String failureMsg)
throws IOException
{
return NOT_HANDLED;
}

/**
* Method called when instance creation for a type fails due to an exception.
* Handler may choose to do one of following things:
Expand All @@ -194,7 +230,7 @@ public Object handleWeirdNumberValue(DeserializationContext ctxt,
* @param t Exception that caused instantiation failure
*
* @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
* what to do (and exception may be thrown), or value to use (possibly
* <code>null</code>
*
* @since 2.8
Expand Down Expand Up @@ -227,7 +263,7 @@ public Object handleInstantiationProblem(DeserializationContext ctxt,
* use it or skip it (latter with {@link JsonParser#skipChildren()}.
*
* @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
* what to do (and exception may be thrown), or value to use (possibly
* <code>null</code>
*
* @since 2.8
Expand Down
Loading

0 comments on commit 851092c

Please sign in to comment.