I'm using the Jackson ObjectMapper class like so:
objectMapper.treeToValue(jsonNode, MyClass.class)
where jsonNode
is an instance of JsonNode.
When I call treeToValue()
, I get a MismatchedInputException
with the message
Cannot deserialize instance of com.example.MyField` out of START_OBJECT token
because MyField
is defined as a String inside of MyClass
, but it is a JSON object inside of the jsonNode
variable. I am perfectly OK with jsonNode
having a non-matching type for one of its fields, but I'd rather ObjectMapper
just not try and serialize this field and ignore it instead of throwing the MismatchedInputException
.
I've tried using
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
but this just ignores missing fields, it does not do anything to prevent the MismatchedInputException
for an existing field.
You can recursively remove offending fields and try again. Here is what I came up with (can be simplified based on your logging and exception handling needs).
public class YourClass {
private static final Logger LOGGER = Logger
.getLogger(YourClass.class.getName());
public final static ObjectMapper mapper = new ObjectMapper().configure(
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
public static <T> T toTypedObject(Class<T> type, JsonNode tree) {
return toTypedObject(type, tree, true);
}
private static <T> T toTypedObject(Class<T> type, JsonNode tree,
boolean topLevel) {
T object;
try {
object = mapper.treeToValue(tree, type);
} catch (MismatchedInputException e) {
String originalTree = tree.toString();
object = toTypedObject(type, tree, originalTree, e);
if (topLevel) {
LOGGER.log(Level.WARNING, "Failed to convert node tree to a "
+ type.getSimpleName()
+ " object without modifications: " + originalTree, e);
LOGGER.log(Level.INFO,
"Modified node tree was successfully converted to a "
+ type.getSimpleName() + " object: " + tree);
}
} catch (JsonProcessingException e) {
throw new YourException("Failed to convert node tree to a "
+ type.getSimpleName() + " object: " + tree, e);
}
return object;
}
private static <T> T toTypedObject(Class<T> type, JsonNode tree,
String originalTree,
MismatchedInputException mismatchedInputException) {
T object;
List<Reference> path = mismatchedInputException.getPath();
if (path != null && !path.isEmpty()) {
try {
ObjectNode subNode = (ObjectNode) tree;
for (int i = 0; i < path.size(); i++) {
String fieldName = path.get(i).getFieldName();
if (i + 1 < path.size()) {
subNode = (ObjectNode) tree.get(fieldName);
} else {
subNode.remove(fieldName);
}
}
object = toTypedObject(type, tree, false);
} catch (Exception e) {
throw new YourException("Failed to convert node tree to a "
+ type.getSimpleName() + " object: " + originalTree,
mismatchedInputException);
}
} else {
throw new YourException(
"Failed to convert node tree to a " + type.getSimpleName()
+ " object: " + originalTree,
mismatchedInputException);
}
return object;
}
}
Call with:
YourObject yourObject = YourClass.toTypedObject(YourObject.class, tree);