1717import tools .jackson .databind .cfg .ContextAttributes ;
1818import tools .jackson .databind .cfg .DatatypeFeature ;
1919import tools .jackson .databind .cfg .DeserializationContexts ;
20+ import tools .jackson .databind .deser .CollectingProblemHandler ;
2021import tools .jackson .databind .deser .DeserializationContextExt ;
2122import tools .jackson .databind .deser .DeserializationProblemHandler ;
23+ import tools .jackson .databind .exc .CollectedProblem ;
24+ import tools .jackson .databind .exc .DeferredBindingException ;
2225import tools .jackson .databind .node .ArrayNode ;
2326import tools .jackson .databind .node .JsonNodeFactory ;
2427import tools .jackson .databind .node .ObjectNode ;
@@ -693,53 +696,60 @@ public ObjectReader withHandler(DeserializationProblemHandler h) {
693696 }
694697
695698 /**
696- * Enables error collection mode by registering a
697- * {@link tools.jackson.databind.deser.CollectingProblemHandler} with default
698- * error limit (100 problems).
699+ * Returns a new {@link ObjectReader} configured to collect deserialization problems
700+ * instead of failing on the first error. Uses default problem limit (100 problems).
699701 *
700- * <p>The returned reader is immutable and thread-safe. Each call to
701- * {@link #readValueCollecting} allocates a fresh problem bucket, so concurrent
702+ * <p><b>IMPORTANT</b>: This method registers a {@link CollectingProblemHandler} which
703+ * <b>replaces any previously configured {@link DeserializationProblemHandler}</b>.
704+ * If you need custom problem handling in addition to collection, you must implement
705+ * your own handler that delegates to {@code CollectingProblemHandler} or chain handlers.
706+ *
707+ * <p>Future versions may support handler chaining; for now, only one handler is active.
708+ *
709+ * <p><b>Thread-safety</b>: The returned reader is immutable and thread-safe. Each call to
710+ * {@link #readValueCollectingProblems} allocates a fresh problem bucket, so concurrent
702711 * calls do not interfere.
703712 *
704713 * <p>Usage:
705714 * <pre>
706715 * ObjectReader reader = mapper.reader()
707716 * .forType(MyBean.class)
708- * .collectErrors ();
717+ * .problemCollectingReader ();
709718 *
710- * MyBean bean = reader.readValueCollecting (json);
719+ * MyBean bean = reader.readValueCollectingProblems (json);
711720 * </pre>
712721 *
713- * @return A new ObjectReader configured for error collection
722+ * @return A new ObjectReader configured for problem collection
714723 * @since 3.1
715724 */
716- public ObjectReader collectErrors () {
717- return collectErrors (100 ); // Default limit
725+ public ObjectReader problemCollectingReader () {
726+ return problemCollectingReader (100 ); // Default limit
718727 }
719728
720729 /**
721- * Enables error collection mode with a custom problem limit.
730+ * Enables problem collection mode with a custom problem limit.
722731 *
723732 * <p><b>Thread-safety</b>: The returned reader is immutable and thread-safe.
724- * Each call to {@link #readValueCollecting } allocates a fresh problem bucket,
733+ * Each call to {@link #readValueCollectingProblems } allocates a fresh problem bucket,
725734 * so concurrent calls do not interfere.
726735 *
727736 * @param maxProblems Maximum number of problems to collect (must be > 0)
728- * @return A new ObjectReader configured for error collection
737+ * @return A new ObjectReader configured for problem collection
738+ * @throws IllegalArgumentException if maxProblems is <= 0
729739 * @since 3.1
730740 */
731- public ObjectReader collectErrors (int maxProblems ) {
741+ public ObjectReader problemCollectingReader (int maxProblems ) {
732742 if (maxProblems <= 0 ) {
733743 throw new IllegalArgumentException ("maxProblems must be positive" );
734744 }
735745
736746 // Store ONLY the max limit in config (not the bucket)
737- // Bucket will be allocated fresh per-call in readValueCollecting ()
747+ // Bucket will be allocated fresh per-call in readValueCollectingProblems ()
738748 ContextAttributes attrs = _config .getAttributes ()
739- .withSharedAttribute (tools . jackson . databind . deser . CollectingProblemHandler .ATTR_MAX_PROBLEMS , maxProblems );
749+ .withSharedAttribute (CollectingProblemHandler .ATTR_MAX_PROBLEMS , maxProblems );
740750
741751 DeserializationConfig newConfig = _config
742- .withHandler (new tools . jackson . databind . deser . CollectingProblemHandler ())
752+ .withHandler (new CollectingProblemHandler ())
743753 .with (attrs );
744754
745755 // Return new immutable reader (no mutable state)
@@ -1383,46 +1393,66 @@ public <T> T readValue(TokenBuffer src) throws JacksonException
13831393
13841394 /**
13851395 * Deserializes JSON content into a Java object, collecting multiple
1386- * errors if encountered. If any problems were collected, throws
1387- * {@link tools.jackson.databind.exc. DeferredBindingException} with all problems.
1396+ * problems if encountered. If any problems were collected, throws
1397+ * {@link DeferredBindingException} with all problems.
13881398 *
13891399 * <p><b>Usage</b>: This method should be called on an ObjectReader created via
1390- * {@link #collectErrors ()} or {@link #collectErrors (int)}. If called on a regular
1391- * reader (without error collection enabled), it behaves the same as
1400+ * {@link #problemCollectingReader ()} or {@link #problemCollectingReader (int)}. If called on a regular
1401+ * reader (without problem collection enabled), it behaves the same as
13921402 * {@link #readValue(JsonParser)} since no handler is registered.
13931403 *
13941404 * <p><b>Error handling</b>:
13951405 * <ul>
13961406 * <li>Recoverable errors are accumulated and thrown as
1397- * {@link tools.jackson.databind.exc. DeferredBindingException} after parsing</li>
1407+ * {@link DeferredBindingException} after parsing</li>
13981408 * <li>Hard (non-recoverable) failures throw immediately, with collected problems
13991409 * attached as suppressed exceptions</li>
14001410 * <li>When the configured limit is reached, collection stops</li>
14011411 * </ul>
14021412 *
1413+ * <p><b>Exception Handling Strategy</b>:
1414+ *
1415+ * <p>This method catches only {@link DatabindException} subtypes (not all
1416+ * {@link JacksonException}s) because:
1417+ *
1418+ * <ul>
1419+ * <li>Core streaming errors ({@link tools.jackson.core.exc.StreamReadException},
1420+ * {@link tools.jackson.core.exc.StreamWriteException}) represent structural
1421+ * JSON problems that cannot be recovered from (malformed JSON, I/O errors)</li>
1422+ *
1423+ * <li>Only databind-level errors (type conversion, unknown properties, instantiation
1424+ * failures) are potentially recoverable and suitable for collection</li>
1425+ *
1426+ * <li>Catching all JacksonExceptions would hide critical parsing errors that should
1427+ * fail fast</li>
1428+ * </ul>
1429+ *
1430+ * <p>If a hard failure occurs after some problems have been collected, those problems
1431+ * are attached as suppressed exceptions to the thrown exception for debugging purposes.
1432+ *
14031433 * <p><b>Thread-safety</b>: Each call allocates a fresh problem bucket,
14041434 * so multiple concurrent calls on the same reader instance are safe.
14051435 *
1406- * <p><b>Parser filtering</b>: Unlike convenience overloads ({@link #readValueCollecting (String)},
1407- * {@link #readValueCollecting (byte[])}, etc.), this method does <i>not</i> apply
1436+ * <p><b>Parser filtering</b>: Unlike convenience overloads ({@link #readValueCollectingProblems (String)},
1437+ * {@link #readValueCollectingProblems (byte[])}, etc.), this method does <i>not</i> apply
14081438 * parser filtering. Callers are responsible for filter wrapping if needed.
14091439 *
14101440 * @param <T> Type to deserialize
14111441 * @param p JsonParser to read from (will not be closed by this method)
14121442 * @return Deserialized object
1413- * @throws tools.jackson.databind.exc. DeferredBindingException if recoverable problems were collected
1414- * @throws tools.jackson.databind. DatabindException if a non-recoverable error occurred
1443+ * @throws DeferredBindingException if recoverable problems were collected
1444+ * @throws DatabindException if a non-recoverable error occurred
14151445 * @since 3.1
14161446 */
1417- public <T > T readValueCollecting (JsonParser p ) throws JacksonException {
1447+ public <T > T readValueCollectingProblems (JsonParser p ) throws JacksonException {
14181448 _assertNotNull ("p" , p );
14191449
14201450 // CRITICAL: Allocate a FRESH bucket for THIS call (thread-safety)
1421- List <tools . jackson . databind . exc . CollectedProblem > bucket = new ArrayList <>();
1451+ List <CollectedProblem > bucket = new ArrayList <>();
14221452
14231453 // Create per-call attributes with the fresh bucket
14241454 ContextAttributes perCallAttrs = _config .getAttributes ()
1425- .withPerCallAttribute (tools . jackson . databind . deser . CollectingProblemHandler .class , bucket );
1455+ .withPerCallAttribute (CollectingProblemHandler .class , bucket );
14261456
14271457 // Create a temporary ObjectReader with per-call attributes using public API
14281458 ObjectReader perCallReader = this .with (perCallAttrs );
@@ -1435,42 +1465,42 @@ public <T> T readValueCollecting(JsonParser p) throws JacksonException {
14351465 if (!bucket .isEmpty ()) {
14361466 // Check if limit was reached - read from per-call config to honor overrides
14371467 Integer maxProblems = (Integer ) perCallReader .getConfig ().getAttributes ()
1438- .getAttribute (tools . jackson . databind . deser . CollectingProblemHandler .ATTR_MAX_PROBLEMS );
1468+ .getAttribute (CollectingProblemHandler .ATTR_MAX_PROBLEMS );
14391469 boolean limitReached = (maxProblems != null &&
14401470 bucket .size () >= maxProblems );
14411471
1442- throw new tools . jackson . databind . exc . DeferredBindingException (p , bucket , limitReached );
1472+ throw new DeferredBindingException (p , bucket , limitReached );
14431473 }
14441474
14451475 return result ;
14461476
1447- } catch (tools . jackson . databind . exc . DeferredBindingException e ) {
1477+ } catch (DeferredBindingException e ) {
14481478 throw e ; // Already properly formatted
14491479
14501480 } catch (DatabindException e ) {
14511481 // Hard failure occurred; attach collected problems as suppressed
14521482 if (!bucket .isEmpty ()) {
14531483 // Read from per-call config to honor overrides
14541484 Integer maxProblems = (Integer ) perCallReader .getConfig ().getAttributes ()
1455- .getAttribute (tools . jackson . databind . deser . CollectingProblemHandler .ATTR_MAX_PROBLEMS );
1485+ .getAttribute (CollectingProblemHandler .ATTR_MAX_PROBLEMS );
14561486 boolean limitReached = (maxProblems != null &&
14571487 bucket .size () >= maxProblems );
14581488
1459- e .addSuppressed (new tools . jackson . databind . exc . DeferredBindingException (p , bucket , limitReached ));
1489+ e .addSuppressed (new DeferredBindingException (p , bucket , limitReached ));
14601490 }
14611491 throw e ;
14621492 }
14631493 }
14641494
14651495 /**
1466- * Convenience overload for {@link #readValueCollecting (JsonParser)}.
1496+ * Convenience overload for {@link #readValueCollectingProblems (JsonParser)}.
14671497 */
1468- public <T > T readValueCollecting (String content ) throws JacksonException {
1498+ public <T > T readValueCollectingProblems (String content ) throws JacksonException {
14691499 _assertNotNull ("content" , content );
14701500 DeserializationContextExt ctxt = _deserializationContext ();
14711501 JsonParser p = _considerFilter (_parserFactory .createParser (ctxt , content ), true );
14721502 try {
1473- return readValueCollecting (p );
1503+ return readValueCollectingProblems (p );
14741504 } finally {
14751505 try {
14761506 p .close ();
@@ -1481,15 +1511,15 @@ public <T> T readValueCollecting(String content) throws JacksonException {
14811511 }
14821512
14831513 /**
1484- * Convenience overload for {@link #readValueCollecting (JsonParser)}.
1514+ * Convenience overload for {@link #readValueCollectingProblems (JsonParser)}.
14851515 */
14861516 @ SuppressWarnings ("unchecked" )
1487- public <T > T readValueCollecting (byte [] content ) throws JacksonException {
1517+ public <T > T readValueCollectingProblems (byte [] content ) throws JacksonException {
14881518 _assertNotNull ("content" , content );
14891519 DeserializationContextExt ctxt = _deserializationContext ();
14901520 JsonParser p = _considerFilter (_parserFactory .createParser (ctxt , content ), true );
14911521 try {
1492- return readValueCollecting (p );
1522+ return readValueCollectingProblems (p );
14931523 } finally {
14941524 try {
14951525 p .close ();
@@ -1500,15 +1530,15 @@ public <T> T readValueCollecting(byte[] content) throws JacksonException {
15001530 }
15011531
15021532 /**
1503- * Convenience overload for {@link #readValueCollecting (JsonParser)}.
1533+ * Convenience overload for {@link #readValueCollectingProblems (JsonParser)}.
15041534 */
15051535 @ SuppressWarnings ("unchecked" )
1506- public <T > T readValueCollecting (File src ) throws JacksonException {
1536+ public <T > T readValueCollectingProblems (File src ) throws JacksonException {
15071537 _assertNotNull ("src" , src );
15081538 DeserializationContextExt ctxt = _deserializationContext ();
15091539 JsonParser p = _considerFilter (_parserFactory .createParser (ctxt , src ), true );
15101540 try {
1511- return readValueCollecting (p );
1541+ return readValueCollectingProblems (p );
15121542 } finally {
15131543 try {
15141544 p .close ();
@@ -1519,15 +1549,15 @@ public <T> T readValueCollecting(File src) throws JacksonException {
15191549 }
15201550
15211551 /**
1522- * Convenience overload for {@link #readValueCollecting (JsonParser)}.
1552+ * Convenience overload for {@link #readValueCollectingProblems (JsonParser)}.
15231553 */
15241554 @ SuppressWarnings ("unchecked" )
1525- public <T > T readValueCollecting (InputStream src ) throws JacksonException {
1555+ public <T > T readValueCollectingProblems (InputStream src ) throws JacksonException {
15261556 _assertNotNull ("src" , src );
15271557 DeserializationContextExt ctxt = _deserializationContext ();
15281558 JsonParser p = _considerFilter (_parserFactory .createParser (ctxt , src ), true );
15291559 try {
1530- return readValueCollecting (p );
1560+ return readValueCollectingProblems (p );
15311561 } finally {
15321562 try {
15331563 p .close ();
@@ -1538,15 +1568,15 @@ public <T> T readValueCollecting(InputStream src) throws JacksonException {
15381568 }
15391569
15401570 /**
1541- * Convenience overload for {@link #readValueCollecting (JsonParser)}.
1571+ * Convenience overload for {@link #readValueCollectingProblems (JsonParser)}.
15421572 */
15431573 @ SuppressWarnings ("unchecked" )
1544- public <T > T readValueCollecting (Reader src ) throws JacksonException {
1574+ public <T > T readValueCollectingProblems (Reader src ) throws JacksonException {
15451575 _assertNotNull ("src" , src );
15461576 DeserializationContextExt ctxt = _deserializationContext ();
15471577 JsonParser p = _considerFilter (_parserFactory .createParser (ctxt , src ), true );
15481578 try {
1549- return readValueCollecting (p );
1579+ return readValueCollectingProblems (p );
15501580 } finally {
15511581 try {
15521582 p .close ();
0 commit comments