package com.fasterxml.jackson.databind; import java.io.Closeable; import java.io.IOException; import java.io.Serializable; import java.util.*; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.databind.util.ClassUtil; /** * Checked exception used to signal fatal problems with mapping of * content, distinct from low-level I/O problems (signaled using * simple {@link java.io.IOException}s) or data encoding/decoding * problems (signaled with {@link com.fasterxml.jackson.core.JsonParseException}, * {@link com.fasterxml.jackson.core.JsonGenerationException}). *
* One additional feature is the ability to denote relevant path * of references (during serialization/deserialization) to help in * troubleshooting. */ public class JsonMappingException extends JsonProcessingException { private static final long serialVersionUID = 1L; /** * Let's limit length of reference chain, to limit damage in cases * of infinite recursion. */ final static int MAX_REFS_TO_LIST = 1000; /* /********************************************************** /* Helper classes /********************************************************** */ /** * Simple bean class used to contain references. References * can be added to indicate execution/reference path that * lead to the problem that caused this exception to be * thrown. */ public static class Reference implements Serializable { private static final long serialVersionUID = 2L; // changes between 2.7 and 2.8 // transient since 2.8 protected transient Object _from; /** * Name of field (for beans) or key (for Maps) that is part * of the reference. May be null for Collection types (which * generally have {@link #_index} defined), or when resolving * Map classes without (yet) having an instance to operate on. */ protected String _fieldName; /** * Index within a {@link Collection} instance that contained * the reference; used if index is relevant and available. * If either not applicable, or not available, -1 is used to * denote "not known" (or not relevant). */ protected int _index = -1; /** * Lazily-constructed description of this instance; needed mostly to * allow JDK serialization to work in case where {@link #_from} is * non-serializable (and has to be dropped) but we still want to pass * actual description along. * * @since 2.8 */ protected String _desc; /** * Default constructor for deserialization/sub-classing purposes */ protected Reference() { } public Reference(Object from) { _from = from; } public Reference(Object from, String fieldName) { _from = from; if (fieldName == null) { throw new NullPointerException("Cannot pass null fieldName"); } _fieldName = fieldName; } public Reference(Object from, int index) { _from = from; _index = index; } // Setters to let Jackson deserialize instances, but not to be called from outside void setFieldName(String n) { _fieldName = n; } void setIndex(int ix) { _index = ix; } void setDescription(String d) { _desc = d; } /** * Object through which reference was resolved. Can be either * actual instance (usually the case for serialization), or * Class (usually the case for deserialization). *
* Note that this value must be `transient` to allow serializability (as
* often such Object is NOT serializable; or, in case of `Class`, may
* not available at the point of deserialization). As such will return
* `null` if instance has been passed using JDK serialization.
*/
@JsonIgnore
public Object getFrom() { return _from; }
public String getFieldName() { return _fieldName; }
public int getIndex() { return _index; }
public String getDescription() {
if (_desc == null) {
StringBuilder sb = new StringBuilder();
if (_from == null) { // can this ever occur?
sb.append("UNKNOWN");
} else {
Class> cls = (_from instanceof Class>) ? (Class>)_from : _from.getClass();
// Hmmh. Although Class.getName() is mostly ok, it does look
// butt-ugly for arrays.
// 06-Oct-2016, tatu: as per [databind#1403], `getSimpleName()` not so good
// as it drops enclosing class. So let's try bit different approach
int arrays = 0;
while (cls.isArray()) {
cls = cls.getComponentType();
++arrays;
}
sb.append(cls.getName());
while (--arrays >= 0) {
sb.append("[]");
}
/* was:
String pkgName = ClassUtil.getPackageName(cls);
if (pkgName != null) {
sb.append(pkgName);
sb.append('.');
}
*/
}
sb.append('[');
if (_fieldName != null) {
sb.append('"');
sb.append(_fieldName);
sb.append('"');
} else if (_index >= 0) {
sb.append(_index);
} else {
sb.append('?');
}
sb.append(']');
_desc = sb.toString();
}
return _desc;
}
@Override
public String toString() {
return getDescription();
}
/**
* May need some cleaning here, given that `from` may or may not be serializable.
*
* since 2.8
*/
Object writeReplace() {
// as per [databind#1195], need to ensure description is not null, since
// `_from` is transient
getDescription();
return this;
}
}
/*
/**********************************************************
/* State/configuration
/**********************************************************
*/
/**
* Path through which problem that triggering throwing of
* this exception was reached.
*/
protected LinkedList
* NOTE: typically not serializable hence
* NOTE: since 2.9 should usually NOT be used on input-side (deserialization)
* exceptions; instead use method(s) of transient
*
* @since 2.7
*/
protected transient Closeable _processor;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
/**
* @deprecated Since 2.7 Use variant that takes {@link JsonParser} instead
*/
@Deprecated // since 2.7
public JsonMappingException(String msg) { super(msg); }
/**
* @deprecated Since 2.7 Use variant that takes {@link JsonParser} instead
*/
@Deprecated // since 2.7
public JsonMappingException(String msg, Throwable rootCause) { super(msg, rootCause); }
/**
* @deprecated Since 2.7 Use variant that takes {@link JsonParser} instead
*/
@Deprecated // since 2.7
public JsonMappingException(String msg, JsonLocation loc) { super(msg, loc); }
/**
* @deprecated Since 2.7 Use variant that takes {@link JsonParser} instead
*/
@Deprecated // since 2.7
public JsonMappingException(String msg, JsonLocation loc, Throwable rootCause) { super(msg, loc, rootCause); }
/**
* @since 2.7
*/
public JsonMappingException(Closeable processor, String msg) {
super(msg);
_processor = processor;
if (processor instanceof JsonParser) {
// 17-Aug-2015, tatu: Use of token location makes some sense from databinding,
// since actual parsing (current) location is typically only needed for low-level
// parsing exceptions.
_location = ((JsonParser) processor).getTokenLocation();
}
}
/**
* @since 2.7
*/
public JsonMappingException(Closeable processor, String msg, Throwable problem) {
super(msg, problem);
_processor = processor;
// 31-Jan-2020: [databind#2482] Retain original location
if (problem instanceof JsonProcessingException) {
_location = ((JsonProcessingException) problem).getLocation();
} else if (processor instanceof JsonParser) {
_location = ((JsonParser) processor).getTokenLocation();
}
}
/**
* @since 2.7
*/
public JsonMappingException(Closeable processor, String msg, JsonLocation loc) {
super(msg, loc);
_processor = processor;
}
/**
* @since 2.7
*/
public static JsonMappingException from(JsonParser p, String msg) {
return new JsonMappingException(p, msg);
}
/**
* @since 2.7
*/
public static JsonMappingException from(JsonParser p, String msg, Throwable problem) {
return new JsonMappingException(p, msg, problem);
}
/**
* @since 2.7
*/
public static JsonMappingException from(JsonGenerator g, String msg) {
return new JsonMappingException(g, msg, (Throwable) null);
}
/**
* @since 2.7
*/
public static JsonMappingException from(JsonGenerator g, String msg, Throwable problem) {
return new JsonMappingException(g, msg, problem);
}
/**
* @since 2.7
*/
public static JsonMappingException from(DeserializationContext ctxt, String msg) {
return new JsonMappingException(ctxt.getParser(), msg);
}
/**
* @since 2.7
*/
public static JsonMappingException from(DeserializationContext ctxt, String msg, Throwable t) {
return new JsonMappingException(ctxt.getParser(), msg, t);
}
/**
* @since 2.7
*/
public static JsonMappingException from(SerializerProvider ctxt, String msg) {
return new JsonMappingException(ctxt.getGenerator(), msg);
}
/**
* @since 2.7
*/
public static JsonMappingException from(SerializerProvider ctxt, String msg, Throwable problem) {
/* 17-Aug-2015, tatu: As per [databind#903] this is bit problematic as
* SerializerProvider instance does not currently hold on to generator...
*/
return new JsonMappingException(ctxt.getGenerator(), msg, problem);
}
/**
* Factory method used when "upgrading" an {@link IOException} into
* {@link JsonMappingException}: usually only needed to comply with
* a signature.
*InputMismatchException
*
* @since 2.1
*/
public static JsonMappingException fromUnexpectedIOE(IOException src) {
return new JsonMappingException(null,
String.format("Unexpected IOException (of type %s): %s",
src.getClass().getName(),
ClassUtil.exceptionMessage(src)));
}
/**
* Method that can be called to either create a new JsonMappingException
* (if underlying exception is not a JsonMappingException), or augment
* given exception with given path/reference information.
*
* This version of method is called when the reference is through a
* non-indexed object, such as a Map or POJO/bean.
*/
public static JsonMappingException wrapWithPath(Throwable src, Object refFrom,
String refFieldName) {
return wrapWithPath(src, new Reference(refFrom, refFieldName));
}
/**
* Method that can be called to either create a new JsonMappingException
* (if underlying exception is not a JsonMappingException), or augment
* given exception with given path/reference information.
*
* This version of method is called when the reference is through an
* index, which happens with arrays and Collections.
*/
public static JsonMappingException wrapWithPath(Throwable src, Object refFrom, int index) {
return wrapWithPath(src, new Reference(refFrom, index));
}
/**
* Method that can be called to either create a new JsonMappingException
* (if underlying exception is not a JsonMappingException), or augment
* given exception with given path/reference information.
*/
@SuppressWarnings("resource")
public static JsonMappingException wrapWithPath(Throwable src, Reference ref)
{
JsonMappingException jme;
if (src instanceof JsonMappingException) {
jme = (JsonMappingException) src;
} else {
// [databind#2128]: try to avoid duplication
String msg = ClassUtil.exceptionMessage(src);
// Let's use a more meaningful placeholder if all we have is null
if (msg == null || msg.length() == 0) {
msg = "(was "+src.getClass().getName()+")";
}
// 17-Aug-2015, tatu: Let's also pass the processor (parser/generator) along
Closeable proc = null;
if (src instanceof JsonProcessingException) {
Object proc0 = ((JsonProcessingException) src).getProcessor();
if (proc0 instanceof Closeable) {
proc = (Closeable) proc0;
}
}
jme = new JsonMappingException(proc, msg, src);
}
jme.prependPath(ref);
return jme;
}
/*
/**********************************************************
/* Accessors/mutators
/**********************************************************
*/
/**
* Method for accessing full structural path within type hierarchy
* down to problematic property.
*/
public List