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 IOExceptions) or data encoding/decoding problems (signaled with JsonParseException, JsonGenerationException).

One additional feature is the ability to denote relevant path of references (during serialization/deserialization) to help in troubleshooting.

/** * 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}). *<p> * 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.
/** * 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.
/** * 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 _index defined), or when resolving Map classes without (yet) having an instance to operate on.
/** * 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 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).
/** * 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 _from is non-serializable (and has to be dropped) but we still want to pass actual description along.
Since:2.8
/** * 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
/** * 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.

/** * Object through which reference was resolved. Can be either * actual instance (usually the case for serialization), or * Class (usually the case for deserialization). *<p> * 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
/** * 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.
/** * Path through which problem that triggering throwing of * this exception was reached. */
protected LinkedList<Reference> _path;
Underlying processor (JsonParser or JsonGenerator), if known.

NOTE: typically not serializable hence transient

Since:2.7
/** * Underlying processor ({@link JsonParser} or {@link JsonGenerator}), * if known. *<p> * NOTE: typically not serializable hence <code>transient</code> * * @since 2.7 */
protected transient Closeable _processor; /* /********************************************************** /* Life-cycle /********************************************************** */
Deprecated:Since 2.7 Use variant that takes JsonParser instead
/** * @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 JsonParser instead
/** * @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 JsonParser instead
/** * @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 JsonParser instead
/** * @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
/** * @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
/** * @since 2.7 */
public JsonMappingException(Closeable processor, String msg, Throwable problem) { super(msg, problem); _processor = processor; if (processor instanceof JsonParser) { _location = ((JsonParser) processor).getTokenLocation(); } }
Since:2.7
/** * @since 2.7 */
public JsonMappingException(Closeable processor, String msg, JsonLocation loc) { super(msg, loc); _processor = processor; }
Since:2.7
/** * @since 2.7 */
public static JsonMappingException from(JsonParser p, String msg) { return new JsonMappingException(p, msg); }
Since:2.7
/** * @since 2.7 */
public static JsonMappingException from(JsonParser p, String msg, Throwable problem) { return new JsonMappingException(p, msg, problem); }
Since:2.7
/** * @since 2.7 */
public static JsonMappingException from(JsonGenerator g, String msg) { return new JsonMappingException(g, msg, (Throwable) null); }
Since:2.7
/** * @since 2.7 */
public static JsonMappingException from(JsonGenerator g, String msg, Throwable problem) { return new JsonMappingException(g, msg, problem); }
Since:2.7
/** * @since 2.7 */
public static JsonMappingException from(DeserializationContext ctxt, String msg) { return new JsonMappingException(ctxt.getParser(), msg); }
Since:2.7
/** * @since 2.7 */
public static JsonMappingException from(DeserializationContext ctxt, String msg, Throwable t) { return new JsonMappingException(ctxt.getParser(), msg, t); }
Since:2.7
/** * @since 2.7 */
public static JsonMappingException from(SerializerProvider ctxt, String msg) { return new JsonMappingException(ctxt.getGenerator(), msg); }
Since:2.7
/** * @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 IOException into JsonMappingException: usually only needed to comply with a signature.

NOTE: since 2.9 should usually NOT be used on input-side (deserialization) exceptions; instead use method(s) of InputMismatchException

Since:2.1
/** * Factory method used when "upgrading" an {@link IOException} into * {@link JsonMappingException}: usually only needed to comply with * a signature. *<p> * NOTE: since 2.9 should usually NOT be used on input-side (deserialization) * exceptions; instead use method(s) of <code>InputMismatchException</code> * * @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.
/** * 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.
/** * 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.
/** * 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.
/** * Method for accessing full structural path within type hierarchy * down to problematic property. */
public List<Reference> getPath() { if (_path == null) { return Collections.emptyList(); } return Collections.unmodifiableList(_path); }
Method for accessing description of path that lead to the problem that triggered this exception
/** * Method for accessing description of path that lead to the * problem that triggered this exception */
public String getPathReference() { return getPathReference(new StringBuilder()).toString(); } public StringBuilder getPathReference(StringBuilder sb) { _appendPathDesc(sb); return sb; }
Method called to prepend a reference information in front of current path
/** * Method called to prepend a reference information in front of * current path */
public void prependPath(Object referrer, String fieldName) { Reference ref = new Reference(referrer, fieldName); prependPath(ref); }
Method called to prepend a reference information in front of current path
/** * Method called to prepend a reference information in front of * current path */
public void prependPath(Object referrer, int index) { Reference ref = new Reference(referrer, index); prependPath(ref); } public void prependPath(Reference r) { if (_path == null) { _path = new LinkedList<Reference>(); } /* Also: let's not increase without bounds. Could choose either * head or tail; tail is easier (no need to ever remove), as * well as potentially more useful so let's use it: */ if (_path.size() < MAX_REFS_TO_LIST) { _path.addFirst(r); } } /* /********************************************************** /* Overridden methods /********************************************************** */ @Override // since 2.7.5 @JsonIgnore // as per [databind#1368] public Object getProcessor() { return _processor; } @Override public String getLocalizedMessage() { return _buildMessage(); }
Method is overridden so that we can properly inject description of problem path, if such is defined.
/** * Method is overridden so that we can properly inject description * of problem path, if such is defined. */
@Override public String getMessage() { return _buildMessage(); } protected String _buildMessage() { // First: if we have no path info, let's just use parent's definition as is String msg = super.getMessage(); if (_path == null) { return msg; } StringBuilder sb = (msg == null) ? new StringBuilder() : new StringBuilder(msg); /* 18-Feb-2009, tatu: initially there was a linefeed between * message and path reference; but unfortunately many systems * (loggers, junit) seem to assume linefeeds are only added to * separate stack trace. */ sb.append(" (through reference chain: "); sb = getPathReference(sb); sb.append(')'); return sb.toString(); } @Override public String toString() { return getClass().getName()+": "+getMessage(); } /* /********************************************************** /* Internal methods /********************************************************** */ protected void _appendPathDesc(StringBuilder sb) { if (_path == null) { return; } Iterator<Reference> it = _path.iterator(); while (it.hasNext()) { sb.append(it.next().toString()); if (it.hasNext()) { sb.append("->"); } } } }