Skip to content

Commit 888c6b9

Browse files
committed
More work with DeserializationProblemHandler; now allow handling of "weird number value"
1 parent e8ae156 commit 888c6b9

File tree

6 files changed

+142
-60
lines changed

6 files changed

+142
-60
lines changed

src/main/java/com/fasterxml/jackson/databind/DeserializationContext.java

Lines changed: 62 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -840,10 +840,12 @@ public boolean handleUnknownProperty(JsonParser p, JsonDeserializer<?> deser,
840840
}
841841

842842
/**
843-
* Method that deserializers should call if they encounter an unrecognized
844-
* property (and once that is not explicitly designed as ignorable), to
845-
* inform possibly configured {@link DeserializationProblemHandler}s and
846-
* let it handle the problem.
843+
* Method that deserializers should call if they encounter a String value
844+
* that can not be converted to expected key of a {@link java.util.Map}
845+
* valued property.
846+
* Default implementation will try to call {@link DeserializationProblemHandler#handleWeirdNumberValue}
847+
* on configured handlers, if any, to allow for recovery; if recovery does not
848+
* succeed, will throw {@link InvalidFormatException} with given message.
847849
*
848850
* @param keyClass Expected type for key
849851
* @param keyValue String value from which to deserialize key
@@ -852,7 +854,7 @@ public boolean handleUnknownProperty(JsonParser p, JsonDeserializer<?> deser,
852854
*
853855
* @return Key value to use
854856
*
855-
* @throws IOException
857+
* @throws IOException To indicate unrecoverable problem, usually based on <code>msg</code>
856858
*
857859
* @since 2.8
858860
*/
@@ -876,6 +878,46 @@ public Object handleWeirdKey(Class<?> keyClass, String keyValue,
876878
throw weirdKeyException(keyClass, keyValue, msg);
877879
}
878880

881+
/**
882+
* Method that deserializers should call if they encounter a numeric value
883+
* that can not be converted to target property type, in cases where some
884+
* numeric values could be acceptable (either with different settings,
885+
* or different numeric value).
886+
* Default implementation will try to call {@link DeserializationProblemHandler#handleWeirdNumberValue}
887+
* on configured handlers, if any, to allow for recovery; if recovery does not
888+
* succeed, will throw {@link InvalidFormatException} with given message.
889+
*
890+
* @param targetClass Type of property into which incoming number should be converted
891+
* @param value Number value from which to deserialize property value
892+
* @param msg Error message template caller wants to use if exception is to be thrown
893+
* @param msgArgs Optional arguments to use for message, if any
894+
*
895+
* @return Property value to use
896+
*
897+
* @throws IOException To indicate unrecoverable problem, usually based on <code>msg</code>
898+
*
899+
* @since 2.8
900+
*/
901+
public Object handleWeirdNumberValue(Class<?> targetClass, Number value,
902+
String msg, Object... msgArgs)
903+
throws IOException
904+
{
905+
// but if not handled, just throw exception
906+
if (msgArgs.length > 0) {
907+
msg = String.format(msg, msgArgs);
908+
}
909+
LinkedNode<DeserializationProblemHandler> h = _config.getProblemHandlers();
910+
while (h != null) {
911+
// Can bail out if it's handled
912+
Object key = h.value().handleWeirdNumberValue(this, targetClass, value, msg);
913+
if (key != DeserializationProblemHandler.NOT_HANDLED) {
914+
return key;
915+
}
916+
h = h.next();
917+
}
918+
throw weirdNumberException(value, targetClass, msg);
919+
}
920+
879921
/**
880922
* @since 2.8
881923
*/
@@ -968,19 +1010,6 @@ public void reportWeirdStringException(String value, Class<?> instClass,
9681010
throw weirdStringException(value, instClass, msg);
9691011
}
9701012

971-
/**
972-
* @since 2.8
973-
*/
974-
public void reportWeirdNumberException(Number value, Class<?> instClass,
975-
String msg, Object... msgArgs)
976-
throws JsonMappingException
977-
{
978-
if (msgArgs.length > 0) {
979-
msg = String.format(msg, msgArgs);
980-
}
981-
throw weirdNumberException(value, instClass, msg);
982-
}
983-
9841013
/**
9851014
* @since 2.8
9861015
*/
@@ -1081,6 +1110,9 @@ public JsonMappingException mappingException(String msgTemplate, Object... args)
10811110
* Helper method for constructing exception to indicate that given JSON
10821111
* Object field name was not in format to be able to deserialize specified
10831112
* key type.
1113+
* Note that most of the time this method should NOT be called; instead,
1114+
* {@link #handleWeirdKey} should be called which will call this method
1115+
* if necessary, but may also use overrides.
10841116
*/
10851117
public JsonMappingException weirdKeyException(Class<?> keyClass, String keyValue,
10861118
String msg) {
@@ -1090,6 +1122,18 @@ public JsonMappingException weirdKeyException(Class<?> keyClass, String keyValue
10901122
keyValue, keyClass);
10911123
}
10921124

1125+
/**
1126+
* Helper method for constructing exception to indicate that input JSON
1127+
* Number was not suitable for deserializing into given target type.
1128+
*/
1129+
public JsonMappingException weirdNumberException(Number value, Class<?> instClass,
1130+
String msg) {
1131+
return InvalidFormatException.from(_parser,
1132+
String.format("Can not deserialize value of type %s from number %s: %s",
1133+
instClass.getName(), String.valueOf(value), msg),
1134+
value, instClass);
1135+
}
1136+
10931137
/**
10941138
* Helper method for constructing exception to indicate that given JSON
10951139
* Object field name was not in format to be able to deserialize specified
@@ -1157,21 +1201,6 @@ public JsonMappingException weirdStringException(String value, Class<?> instClas
11571201
value, instClass);
11581202
}
11591203

1160-
/**
1161-
* Helper method for constructing exception to indicate that input JSON
1162-
* Number was not suitable for deserializing into given target type.
1163-
*
1164-
* @deprecated Since 2.8 use {@link #reportWeirdNumberException} instead
1165-
*/
1166-
@Deprecated
1167-
public JsonMappingException weirdNumberException(Number value, Class<?> instClass,
1168-
String msg) {
1169-
return InvalidFormatException.from(_parser,
1170-
String.format("Can not deserialize value of type %s from number %s: %s",
1171-
instClass.getName(), String.valueOf(value), msg),
1172-
value, instClass);
1173-
}
1174-
11751204
/**
11761205
* Helper method for indicating that the current token was expected to be another
11771206
* token.

src/main/java/com/fasterxml/jackson/databind/deser/DeserializationProblemHandler.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,40 @@ public Object handleWeirdKey(DeserializationContext ctxt,
104104
return NOT_HANDLED;
105105
}
106106

107+
/**
108+
* Method called when a number value (integral or floating-point from input
109+
* can not be converted to a non-numeric value type due to specific problem
110+
* (as opposed to numeric values never being usable).
111+
* Handler may choose to do one of 3 things:
112+
*<ul>
113+
* <li>Indicate it does not know what to do by returning {@link #NOT_HANDLED}
114+
* </li>
115+
* <li>Throw a {@link IOException} to indicate specific fail message (instead of
116+
* standard exception caller would throw
117+
* </li>
118+
* <li>Return actual converted value (of type <code>targetType</code> to use as
119+
* replacement, and continue processing.
120+
* </li>
121+
* </ul>
122+
*
123+
* @param failureMsg Message that will be used by caller (by calling
124+
* {@link DeserializationContext#weirdNumberException})
125+
* to indicate type of failure unless handler produces key to use
126+
*
127+
* @return Either {@link #NOT_HANDLED} to indicate that handler does not know
128+
* what to do (and exception may be thrown), or value to use as key (possibly
129+
* <code>null</code>
130+
*
131+
* @since 2.8
132+
*/
133+
public Object handleWeirdNumberValue(DeserializationContext ctxt,
134+
Class<?> targetType, Number valueToConvert,
135+
String failureMsg)
136+
throws IOException
137+
{
138+
return NOT_HANDLED;
139+
}
140+
107141
/**
108142
* Handler method called if resolution of type id from given String failed
109143
* to produce a subtype; usually because logical id is not mapped to actual

src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,9 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOEx
132132
// ... unless told not to do that
133133
int index = p.getIntValue();
134134
if (ctxt.isEnabled(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)) {
135-
_failOnNumber(ctxt, p, index);
136-
return null;
135+
return ctxt.handleWeirdNumberValue(_enumClass(), index,
136+
"not allowed to deserialize Enum value out of number: disable DeserializationConfig.DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS to allow"
137+
);
137138
}
138139
if (index >= 0 && index <= _enumsByIndex.length) {
139140
return _enumsByIndex[index];
@@ -143,10 +144,9 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOEx
143144
return _enumDefaultValue;
144145
}
145146
if (!ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
146-
ctxt.reportWeirdNumberException(index, _enumClass(),
147+
return ctxt.handleWeirdNumberValue(_enumClass(), index,
147148
"index value outside legal index range [0..%s]",
148149
_enumsByIndex.length-1);
149-
// fall through, if nothing thrown immediately
150150
}
151151
return null;
152152
}
@@ -172,13 +172,14 @@ private final Object _deserializeAltString(JsonParser p, DeserializationContext
172172
char c = name.charAt(0);
173173
if (c >= '0' && c <= '9') {
174174
try {
175-
int ix = Integer.parseInt(name);
175+
int index = Integer.parseInt(name);
176176
if (ctxt.isEnabled(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)) {
177-
_failOnNumber(ctxt, p, ix);
178-
return null;
177+
return ctxt.handleWeirdNumberValue(_enumClass(), index,
178+
"not allowed to deserialize Enum value out of number: disable DeserializationConfig.DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS to allow"
179+
);
179180
}
180-
if (ix >= 0 && ix <= _enumsByIndex.length) {
181-
return _enumsByIndex[ix];
181+
if (index >= 0 && index <= _enumsByIndex.length) {
182+
return _enumsByIndex[index];
182183
}
183184
} catch (NumberFormatException e) {
184185
// fine, ignore, was not an integer
@@ -215,14 +216,6 @@ protected Object _deserializeOther(JsonParser p, DeserializationContext ctxt) th
215216
return null;
216217
}
217218

218-
protected void _failOnNumber(DeserializationContext ctxt, JsonParser p, int index)
219-
throws IOException
220-
{
221-
ctxt.reportWeirdNumberException(index, _enumClass(),
222-
"not allowed to deserialize Enum value out of number: disable DeserializationConfig.DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS to allow"
223-
);
224-
}
225-
226219
protected Class<?> _enumClass() {
227220
return handledType();
228221
}

src/test/java/com/fasterxml/jackson/databind/BaseTest.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -290,22 +290,21 @@ protected JsonParser createParserUsingReader(String input)
290290
}
291291

292292
protected JsonParser createParserUsingReader(JsonFactory f, String input)
293-
throws IOException, JsonParseException
293+
throws IOException
294294
{
295295
return f.createParser(new StringReader(input));
296296
}
297297

298298
protected JsonParser createParserUsingStream(String input, String encoding)
299-
throws IOException, JsonParseException
299+
throws IOException
300300
{
301301
return createParserUsingStream(new JsonFactory(), input, encoding);
302302
}
303303

304304
protected JsonParser createParserUsingStream(JsonFactory f,
305-
String input, String encoding)
306-
throws IOException, JsonParseException
305+
String input, String encoding)
306+
throws IOException
307307
{
308-
309308
/* 23-Apr-2008, tatus: UTF-32 is not supported by JDK, have to
310309
* use our own codec too (which is not optimal since there's
311310
* a chance both encoder and decoder might have bugs, but ones

src/test/java/com/fasterxml/jackson/databind/TestFormatSchema.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,6 @@ public ObjectCodec getCodec() {
122122
@Override
123123
public void setCodec(ObjectCodec c) { }
124124

125-
@Override
126-
protected boolean loadMore() throws IOException {
127-
return false;
128-
}
129-
130125
@Override
131126
protected void _closeInput() throws IOException {
132127
}

src/test/java/com/fasterxml/jackson/databind/filter/ProblemHandlerTest.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,25 @@ public Object handleWeirdKey(DeserializationContext ctxt,
3434
}
3535
}
3636

37+
static class WeirdNumberHandler
38+
extends DeserializationProblemHandler
39+
{
40+
protected final Object value;
41+
42+
public WeirdNumberHandler(Object v0) {
43+
value = v0;
44+
}
45+
46+
@Override
47+
public Object handleWeirdNumberValue(DeserializationContext ctxt,
48+
Class<?> targetType, Number n,
49+
String failureMsg)
50+
throws IOException
51+
{
52+
return value;
53+
}
54+
}
55+
3756
static class IntKeyMapWrapper {
3857
public Map<Integer,String> stuff;
3958
}
@@ -61,6 +80,10 @@ static class BaseWrapper {
6180
public Base value;
6281
}
6382

83+
enum SingleValuedEnum {
84+
A;
85+
}
86+
6487
/*
6588
/**********************************************************
6689
/* Test methods
@@ -81,6 +104,15 @@ public void testWeirdKeyHandling() throws Exception
81104
assertEquals(Integer.valueOf(7), map.keySet().iterator().next());
82105
}
83106

107+
public void testWeirdNumberHandling() throws Exception
108+
{
109+
ObjectMapper mapper = new ObjectMapper()
110+
.addHandler(new WeirdNumberHandler(SingleValuedEnum.A))
111+
;
112+
SingleValuedEnum result = mapper.readValue("3", SingleValuedEnum.class);
113+
assertEquals(SingleValuedEnum.A, result);
114+
}
115+
84116
public void testInvalidTypeId() throws Exception
85117
{
86118
ObjectMapper mapper = new ObjectMapper()

0 commit comments

Comments
 (0)