Skip to content
Commits on Source (10)
language: java
# Since Jackson 2.7, build requires jdk7, although module itself works on jdk6 still (for now)
# and since 2.10, will require jdk8 for build
jdk:
- openjdk7
- openjdk8
- oraclejdk11
# Below this line is configuration for deploying to the Sonatype OSS repo
# http://blog.xeiam.com/2013/05/configure-travis-ci-to-deploy-snapshots.html
......@@ -16,7 +17,7 @@ after_success:
branches:
only:
- master
- "2.9"
- "2.10"
env:
global:
......
src/main/resources/META-INF/LICENSE
\ No newline at end of file
......@@ -404,12 +404,29 @@ There is really just one main rule, which is that to accept any code contributio
One additional limitation exists for so-called core components (streaming api, jackson-annotations and jackson-databind): no additional dependendies are allowed beyond:
* Core components may rely on any methods included in the supported JDK
* Minimum JDK version is 1.6 as of Jackson 2.4 and above (1.5 was baseline with 2.3 and earlier)
* Minimum JDK version was 1.5 until (and including) version 2.3
* Minimum JDK version was 1.6 for Jackson 2.4 - 2.7 (inclusive) for all core components
* Minimum is still 1.6 for `jackson-annotations` and `jackson-core`, for all remaining Jackson 2.x versions
* Minimum JDK version is 1.7 for Jackson 2.7 - 2.10 of `jackson-databind` and most non-core components
* Jackson-databind (this package) depends on the other two (annotations, streaming).
This means that anything that has to rely on additional APIs or libraries needs to be built as an extension,
usually a Jackson module.
## Branches
`master` branch is for developing the next major Jackson version -- 3.0 -- but there
are active maintenance branches in which much of development happens:
* `2.10` is for developing the next (and possibly last) minor 2.x version
* `2.8` and `2.9` are for backported fixes for 2.8/2.9 patch versions
Older branches are usually not maintained after being declared as closed
on [Jackson Releases](https://github.com/FasterXML/jackson/wiki/Jackson-Releases) page,
but exist just in case a rare emergency patch is needed.
All released versions have matching git tags (`jackson-dataformats-binary-2.9.4`).
-----
# Differences from Jackson 1.x
......@@ -433,4 +450,3 @@ Related:
* [Core annotations](https://github.com/FasterXML/jackson-annotations) package defines annotations commonly used for configuring databinding details
* [Core parser/generator](https://github.com/FasterXML/jackson-core) package defines low-level incremental/streaming parsers, generators
* [Jackson Project Home](http://wiki.fasterxml.com/JacksonHome) has additional documentation (although much of it for Jackson 1.x)
jackson-databind (2.10.0-1) unstable; urgency=medium
* Team upload.
* New upstream version 2.10.0.
-Fix CVE-2019-14540 and CVE-2019-16335: Polymorphic Typing issues.
(Closes: #940498) Thanks to Salvatore Bonaccorso for the report.
* Declare compliance with Debian Policy 4.4.1.
* Update base-pom.patch for new release.
* Remove Wolodja Wentland from Uploaders. Add myself to it. (Closes: #898140)
-- Markus Koschany <apo@debian.org> Sun, 29 Sep 2019 21:51:57 +0200
jackson-databind (2.9.9.3-1) unstable; urgency=medium
* Team upload.
......
......@@ -3,7 +3,7 @@ Section: java
Priority: optional
Maintainer: Debian Java Maintainers <pkg-java-maintainers@lists.alioth.debian.org>
Uploaders:
Wolodja Wentland <debian@babilen5.org>
Markus Koschany <apo@debian.org>
Build-Depends:
debhelper-compat (= 12),
default-jdk,
......@@ -23,7 +23,7 @@ Build-Depends:
libreplacer-java,
maven-debian-helper (>= 1.6.5),
xmlstarlet
Standards-Version: 4.4.0
Standards-Version: 4.4.1
Vcs-Git: https://salsa.debian.org/java-team/jackson-databind.git
Vcs-Browser: https://salsa.debian.org/java-team/jackson-databind
Homepage: http://wiki.fasterxml.com/JacksonHome
......
......@@ -4,3 +4,5 @@ org.apache.maven.plugins maven-enforcer-plugin * * * *
org.apache.maven.plugins maven-release-plugin * * * *
org.codehaus.mojo cobertura-maven-plugin * * * *
* * * * * test
org.moditect moditect-maven-plugin * * * *
org.jacoco jacoco-maven-plugin * * * *
......@@ -18,7 +18,7 @@ index b031f65..c6660ac 100644
<parent>
<groupId>com.fasterxml.jackson</groupId>
<artifactId>jackson-base</artifactId>
- <version>2.9.9</version>
- <version>2.10.0</version>
+ <version>2.9.8</version>
</parent>
......
......@@ -5,12 +5,12 @@
<parent>
<groupId>com.fasterxml.jackson</groupId>
<artifactId>jackson-base</artifactId>
<version>2.9.9</version>
<version>2.10.0</version>
</parent>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.9.3</version>
<version>2.10.0</version>
<name>jackson-databind</name>
<packaging>bundle</packaging>
<description>General data-binding functionality for Jackson: works on core streaming API</description>
......@@ -21,11 +21,11 @@
<connection>scm:git:git@github.com:FasterXML/jackson-databind.git</connection>
<developerConnection>scm:git:git@github.com:FasterXML/jackson-databind.git</developerConnection>
<url>http://github.com/FasterXML/jackson-databind</url>
<tag>jackson-databind-2.9.9.3</tag>
<tag>jackson-databind-2.10.0</tag>
</scm>
<properties>
<!-- With Jackson 2.9 we will require JDK 7 (except for annotations/streaming),
<!-- With Jackson 2.10 baseline is JDK 7 (except for annotations/streaming),
and new language features (diamond pattern) may be used.
JDK classes are still loaded dynamically since there isn't much downside
(small number of types); this allows use on JDK 6 platforms still (including
......@@ -36,7 +36,8 @@
<!-- Can not use default, since group id != Java package name here -->
<osgi.export>com.fasterxml.jackson.databind.*;version=${project.version}</osgi.export>
<osgi.import> <!-- fix for databind#2299: using jackson-databind in an OSGi environment under Android -->
<!-- fix for databind#2299: using jackson-databind in an OSGi environment under Android -->
<osgi.import>
org.w3c.dom.bootstrap;resolution:=optional,
*
</osgi.import>
......@@ -45,8 +46,7 @@
<packageVersion.dir>com/fasterxml/jackson/databind/cfg</packageVersion.dir>
<packageVersion.package>com.fasterxml.jackson.databind.cfg</packageVersion.package>
<!-- since 2.9.1: NOTE! can not use packageVersion.package as is -->
<jdk.module.name>com.fasterxml.jackson.databind</jdk.module.name>
<version.powermock>2.0.0</version.powermock>
</properties>
<dependencies>
......@@ -70,16 +70,22 @@
libs for which we use reflection for code, but direct dep for testing
-->
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-core</artifactId>
<version>${version.powermock}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>1.7.4</version>
<version>${version.powermock}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>1.7.4</version>
<artifactId>powermock-api-mockito2</artifactId>
<version>${version.powermock}</version>
<scope>test</scope>
</dependency>
<!-- For testing TestNoClassDefFoundDeserializer -->
......@@ -105,6 +111,28 @@
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.4</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<!-- attached to Maven test phase -->
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Important: enable enforcer plug-in: -->
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
......@@ -137,8 +165,8 @@
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<links combine.children="append">
<link>http://fasterxml.github.com/jackson-annotations/javadoc/2.9</link>
<link>http://fasterxml.github.com/jackson-core/javadoc/2.9</link>
<link>http://fasterxml.github.com/jackson-annotations/javadoc/2.10</link>
<link>http://fasterxml.github.com/jackson-core/javadoc/2.10</link>
</links>
</configuration>
</plugin>
......@@ -149,11 +177,12 @@
<artifactId>replacer</artifactId>
</plugin>
<!-- 18-Oct-2016, tatu: Try to make coveralls work -->
<!-- 04-Mar-2019, tatu: Add rudimentary JDK9+ module info. To build with JDK 8
will have to use `moduleInfoFile` as anything else requires JDK 9+
-->
<plugin>
<groupId>org.eluder.coveralls</groupId>
<artifactId>coveralls-maven-plugin</artifactId>
<version>4.3.0</version>
<groupId>org.moditect</groupId>
<artifactId>moditect-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
......
......@@ -341,6 +341,9 @@ Sadayuki Furuhashi (frsyuki@github)
* Reported #941: Deserialization from "{}" to ObjectNode field causes
"out of END_OBJECT token" error
(2.6.3)
* Reported #2077: `JsonTypeInfo` with a subtype having `JsonFormat.Shape.ARRAY`
and no fields generates `{}` not `[]`
(2.10.0)
David Haraburda (dharaburda@github)
* Contributed #918: Add `MapperFeature.ALLOW_EXPLICIT_PROPERTY_RENAMING`
......@@ -661,6 +664,10 @@ svarzee@github
* Reported #2109, suggested fix: Canonical string for reference type is built incorrectly
(2.8.11.3 / 2.9.7)
Kaki King (kingkk9279@g)
* Reported #2449: Block one more gadget type (cve CVE-2019-14540)
(2.9.10)
Connor Kuhn (ckuhn@github)
* Contributed #1341: FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY
(2.9.0)
......@@ -760,7 +767,9 @@ Joe Schafer (jschaf@github)
Deblock Thomas (deblockt@github)
* Reported, contributed fix for #1912: `BeanDeserializerModifier.updateBuilder()` does not
work to set custom deserializer on a property (since 2.9.0)
(contributed by Deblock T)
(2.9.5)
* Reported, suggested fix for #2280: JsonMerge not work with constructor args
(2.10.0)
lilei@venusgroup.com.cn:
* Reported #1931: Two more `c3p0` gadgets to exploit default typing issue
......@@ -815,9 +824,25 @@ Brandon Krieger (bkrieger@github)
* Reported #2064: Cannot set custom format for `SqlDateSerializer` globally
(2.9.7)
Thibaut Robert (trobert@github)
* Requested #2059: Remove `final` modifier for `TypeFactory`
(2.10.0)
Christopher Smith (chrylis@github)
* Suggested #2115: Support naive deserialization of `Serializable` values as "untyped",
same as `java.lang.Object`
(2.10.0)
Édouard Mercier (edouardmercier@github)
* Requested #2116: Make NumberSerializers.Base public and its inherited classes not final
(2.9.6)
Semyon Levin (remal@github)
* Contributed #2120: `NioPathDeserializer` improvement
(2.9.7)
* Contributed #2133: Improve `DeserializationProblemHandler.handleUnexpectedToken()`
to allow handling of Collection problems
(2.10.0)
Pavel Nikitin (morj@github)
* Requested #2181: Don't re-use dynamic serializers for property-updating copy constructors
......@@ -838,7 +863,7 @@ Joffrey Bion (joffrey-bion@github)
Collections$UnmodifiableRandomAccessList
(2.9.9)
Christoph (cfiehe@github.com)
Christoph Fiehe (cfiehe@github.com)
* Contributed #2299: Fix for using jackson-databind in an OSGi environment under Android
(2.9.9)
......@@ -859,3 +884,90 @@ Michael Simons (michael-simons@github)
* Reported #2395: `NullPointerException` from `ResolvedRecursiveType` (regression due to
fix for #2331)
(2.9.9.3)
Joe Barnett (josephlbarnett@github)
* Reported, contributed fix for #2404: FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY setting
ignored when creator properties are buffered
(2.9.10)
Zihui Ren (renzihui@github)
* Suggested #2129: Add `SerializationFeature.WRITE_ENUM_KEYS_USING_INDEX`, separate from value setting
(2.10.0)
Yiqiu Huang (huangyq23@github
* Reported #2164: `FactoryBasedEnumDeserializer` does not respect
`DeserializationFeature.WRAP_EXCEPTIONS`
(2.10.0)
Alexander Saites (saites@github)
* Reported #2189: `TreeTraversingParser` does not check int bounds
(2.10.0)
Christoph Breitkopf (bokesan@github)
* Reported #2217: Suboptimal memory allocation in `TextNode.getBinaryValue()`
(2.10.0)
Pavel Chervakov (pacher@github)
* Reported #2230: `WRITE_BIGDECIMAL_AS_PLAIN` is ignored if `@JsonFormat` is used
(2.10.0)
Ben Anderson (andersonbd1@github)
* Reported, suggested fix for #2309: READ_ENUMS_USING_TO_STRING doesn't support null values
(2.10.0)
Manuel Hegner (manuel-hegner@github)
* Suggested #2311: Unnecessary MultiView creation for property writers
(2.10.0)
Chris Mercer (cmercer@github)
* Reported #2331: `JsonMappingException` through nested getter with generic wildcard return type
(2.10.0)
Robert Greig (rgreig@github)
* Reported #2336: `MapDeserializer` can not merge `Map`s with polymorphic values
(2.10.0)
Victor Noël (victornoel@github)
* Reported #2338: Suboptimal return type for `JsonNode.withArray()`
(2.10.0)
* Reported #2339: Suboptimal return type for `ObjectNode.set()`
(2.10.0)
David Harris (toadzky@github)
* Reported #2378: `@JsonAlias` doesn't work with AutoValue
(2.10.0)
Sam Smith (Oracle Security Researcher)
* Suggested #2398: Replace recursion in `TokenBuffer.copyCurrentStructure()` with iteration
Vladimir Tsanev (tsachev@github)
* Contributed #2415: Builder-based POJO deserializer should pass builder instance, not type,
to `handleUnknownVanilla()` to fix earlier #822
(2.10.0)
Marcos Passos (marcospassos@github(
* Contributed #2432: Add support for module bundles
(2.10.0)
David Becker (dsbecker@github)
* Suggested #2433: Improve `NullNode.equals()`
(2.10.0)
Hesham Massoud (heshamMassoud@github)
* Reported, contributed fix for #2442: `ArrayNode.addAll()` adds raw `null` values
which cause NPE on `deepCopy()`
(2.10.0)
David Connelly (dconnelly@github)
* Reported #2446: Java 11: Unable to load JDK7 types (annotations, java.nio.file.Path):
no Java7 support added
(2.10.0)
Wahey (KevynBct@github)
* Reported #2466: Didn't find class "java.nio.file.Path" below Android api 26
(2.10.0)
Martín Coll (colltoaction@github)
* Contributed #2467: Accept `JsonTypeInfo.As.WRAPPER_ARRAY` with no second argument to
deserialize as "null value"
(2.10.0)
......@@ -4,24 +4,141 @@ Project: jackson-databind
=== Releases ===
------------------------------------------------------------------------
2.9.9.3 (06-Aug-2019)
#2395: `NullPointerException` from `ResolvedRecursiveType` (regression due to fix for #2331)
(reported by Michael S)
2.9.9.2 (27-Jul-2019)
2.10.0 (26-Sep-2019)
#18: Make `JsonNode` serializable
#1093: Default typing does not work with `writerFor(Object.class)`
(reported by hoomanv@github)
#1675: Remove "impossible" `IOException` in `readTree()` and `readValue()` `ObjectMapper`
methods which accept Strings
(requested by matthew-pwnieexpress@github)
#1954: Add Builder pattern for creating configured `ObjectMapper` instances
#1995: Limit size of `DeserializerCache`, auto-flush on exceeding
#2059: Remove `final` modifier for `TypeFactory`
(requested by Thibaut R)
#2077: `JsonTypeInfo` with a subtype having `JsonFormat.Shape.ARRAY` and
no fields generates `{}` not `[]`
(reported by Sadayuki F)
#2115: Support naive deserialization of `Serializable` values as "untyped", same
as `java.lang.Object`
(requested by Christopher S)
#2116: Make NumberSerializers.Base public and its inherited classes not final
(requested by Édouard M)
#2126: `DeserializationContext.instantiationException()` throws `InvalidDefinitionException`
#2129: Add `SerializationFeature.WRITE_ENUM_KEYS_USING_INDEX`, separate from value setting
(suggested by renzihui@github)
#2133: Improve `DeserializationProblemHandler.handleUnexpectedToken()` to allow handling of
Collection problems
(contributed by Semyon L)
#2149: Add `MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES`
(suggested by Craig P)
#2153: Add `JsonMapper` to replace generic `ObjectMapper` usage
#2164: `FactoryBasedEnumDeserializer` does not respect
`DeserializationFeature.WRAP_EXCEPTIONS`
(reported by Yiqiu H)
#2187: Make `JsonNode.toString()` use shared `ObjectMapper` to produce valid json
#2189: `TreeTraversingParser` does not check int bounds
(reported by Alexander S)
#2195: Add abstraction `PolymorphicTypeValidator`, for limiting subtypes allowed by
default typing, `@JsonTypeInfo`
#2196: Type safety for `readValue()` with `TypeReference`
(suggested by nguyenfilip@github)
#2204: Add `JsonNode.isEmpty()` as convenience alias
#2211: Change of behavior (2.8 -> 2.9) with `ObjectMapper.readTree(input)` with no content
#2217: Suboptimal memory allocation in `TextNode.getBinaryValue()`
(reported by Christoph B)
#2220: Force serialization always for `convertValue()`; avoid short-cuts
#2223: Add `missingNode()` method in `JsonNodeFactory`
#2227: Minor cleanup of exception message for `Enum` binding failure
(reported by RightHandedMonkey@github)
#2230: `WRITE_BIGDECIMAL_AS_PLAIN` is ignored if `@JsonFormat` is used
(reported by Pavel C)
#2236: Type id not provided on `Double.NaN`, `Infinity` with `@JsonTypeInfo`
(reported by C-B-B@github)
#2237: Add "required" methods in `JsonNode`: `required(String | int)`,
`requiredAt(JsonPointer)`
#2241: Add `PropertyNamingStrategy.LOWER_DOT_CASE` for dot-delimited names
(contributed by zenglian@github.com)
#2251: Getter that returns an abstract collection breaks a delegating `@JsonCreator`
#2265: Inconsistent handling of Collections$UnmodifiableList vs Collections$UnmodifiableRandomAccessListq
#2273: Add basic Java 9+ module info
#2280: JsonMerge not work with constructor args
(reported by Deblock T)
#2309: READ_ENUMS_USING_TO_STRING doesn't support null values
(reported, fix suggested by Ben A)
#2311: Unnecessary MultiView creation for property writers
(suggested by Manuel H)
#2331: `JsonMappingException` through nested getter with generic wildcard return type
#2387: Block yet another deserialization gadget (CVE-2019-14379)
#2389: Block yet another deserialization gadget (CVE-2019-14439)
(reported by xiexq)
2.9.9.1 (03-Jul-2019)
(reported by sunchezz89@github)
#2336: `MapDeserializer` can not merge `Map`s with polymorphic values
(reported by Robert G)
#2338: Suboptimal return type for `JsonNode.withArray()`
(reported by Victor N)
#2339: Suboptimal return type for `ObjectNode.set()`
(reported by Victor N)
#2349: Add option `DefaultTyping.EVERYTHING` to support Kotlin data classes
#2357: Lack of path on MismatchedInputException
(suggested by TheEin@github)
#2378: `@JsonAlias` doesn't work with AutoValue
(reported by David H)
#2390: `Iterable` serialization breaks when adding `@JsonFilter` annotation
(reported by Chris M)
#2392: `BeanDeserializerModifier.modifyDeserializer()` not applied to custom bean deserializers
(reported by andreasbaus@github)
#2393: `TreeTraversingParser.getLongValue()` incorrectly checks `canConvertToInt()`
(reported by RabbidDog@github)
#2398: Replace recursion in `TokenBuffer.copyCurrentStructure()` with iteration
(reported by Sam S)
#2415: Builder-based POJO deserializer should pass builder instance, not type,
to `handleUnknownVanilla()`
(proposed by Vladimir T, follow up to #822)
#2416: Optimize `ValueInstantiator` construction for default `Collection`, `Map` types
#2422: `scala.collection.immutable.ListMap` fails to serialize since 2.9.3
(reported by dejanlokar1@github)
#2425: Add global config override setting for `@JsonFormat.lenient()`
#2428: Use "activateDefaultTyping" over "enableDefaultTyping" in 2.10 with new methods
#2430: Change `ObjectMapper.valueToTree()` to convert `null` to `NullNode`
#2432: Add support for module bundles
(contributed by Marcos P)
#2433: Improve `NullNode.equals()`
(suggested by David B)
#2442: `ArrayNode.addAll()` adds raw `null` values which cause NPE on `deepCopy()`
and `toString()`
(reported, fix contributed by Hesham M)
#2446: Java 11: Unable to load JDK7 types (annotations, java.nio.file.Path): no Java7 support added
(reported by David C)
#2451: Add new `JsonValueFormat` value, `UUID`
#2453: Add `DeserializationContext.readTree(JsonParser)` convenience method
#2458: `Nulls` property metadata ignored for creators
(reported by XakepSDK@github)
#2466: Didn't find class "java.nio.file.Path" below Android api 26
(reported by KevynBct@github)
#2467: Accept `JsonTypeInfo.As.WRAPPER_ARRAY` with no second argument to
deserialize as "null value"
(contributed by Martin C)
2.9.10 (21-Sep-2019)
#2331: `JsonMappingException` through nested getter with generic wildcard return type
#2334: Block one more gadget type (CVE-2019-12384)
#2341: Block one more gadget type (CVE-2019-12814)
#2374: `ObjectMapper. getRegisteredModuleIds()` throws NPE if no modules registered
(reported by Edgar A)
#2387: Block yet another deserialization gadget (CVE-2019-14379)
#2389: Block yet another deserialization gadget (CVE-2019-14439)
(reported by xiexq)
#2404: FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY setting ignored when
creator properties are buffered
(contributed by Joe B)
#2410: Block one more gadget type (CVE-2019-14540)
(reported by iSafeBlue@github / blue@ixsec.org)
#2420: Block one more gadget type (no CVE allocated yet)
(reported by crazylirui@gmail.com)
#2449: Block one more gadget type (CVE-2019-14540)
(reported by kingkk)
#2460: Block one mode gadget type (ehcache, no CVE allocated yet)
(reported by Fei Lu)
#2462: Block two more gadget types (commons-configuration)
#2469: Block one more gadget type (xalan2)
2.9.9 (16-May-2019)
......@@ -40,7 +157,6 @@ Project: jackson-databind
#2324: `StringCollectionDeserializer` fails with custom collection
(reported byb Daniil B)
#2326: Block one more gadget type (CVE-2019-12086)
<<<<<<< HEAD:release-notes/VERSION-2.x
- Prevent String coercion of `null` in `WritableObjectId` when calling `JsonGenerator.writeObjectId()`,
mostly relevant for formats like YAML that have native Object Ids
......
......@@ -24,7 +24,7 @@ public abstract class AbstractTypeResolver
* resolvers, until a concrete type is located.
*
* @param config Configuration in use; should always be of type
* <code>DeserializationConfig</code>
* {@code DeserializationConfig}
*/
public JavaType findTypeMapping(DeserializationConfig config, JavaType type) {
return null;
......
......@@ -630,21 +630,19 @@ public abstract class AnnotationIntrospector
/**
* Method for getting a serializer definition on specified method
* or field. Type of definition is either instance (of type
* {@link JsonSerializer}) or Class (of type
* <code>Class&lt;JsonSerializer></code>); if value of different
* type is returned, a runtime exception may be thrown by caller.
* or field. Type of definition is either instance (of type {@link JsonSerializer})
* or Class (of {@code Class&lt;JsonSerializer} implementation subtype);
* if value of different type is returned, a runtime exception may be thrown by caller.
*/
public Object findSerializer(Annotated am) {
return null;
}
/**
* Method for getting a serializer definition for keys of associated <code>Map</code> property.
* Type of definition is either instance (of type
* {@link JsonSerializer}) or Class (of type
* <code>Class&lt;JsonSerializer></code>); if value of different
* type is returned, a runtime exception may be thrown by caller.
* Method for getting a serializer definition for keys of associated {@code java.util.Map} property.
* Type of definition is either instance (of type {@link JsonSerializer})
* or Class (of type {@code Class&lt;JsonSerializer>});
* if value of different type is returned, a runtime exception may be thrown by caller.
*/
public Object findKeySerializer(Annotated am) {
return null;
......@@ -652,10 +650,10 @@ public abstract class AnnotationIntrospector
/**
* Method for getting a serializer definition for content (values) of
* associated <code>Collection</code>, <code>array</code> or <code>Map</code> property.
* Type of definition is either instance (of type
* {@link JsonSerializer}) or Class (of type
* <code>Class&lt;JsonSerializer></code>); if value of different
* associated <code>Collection</code>, <code>array</code> or {@code Map} property.
* Type of definition is either instance (of type {@link JsonSerializer})
* or Class (of type {@code Class&lt;JsonSerializer>});
* if value of different
* type is returned, a runtime exception may be thrown by caller.
*/
public Object findContentSerializer(Annotated am) {
......@@ -993,9 +991,8 @@ public abstract class AnnotationIntrospector
/**
* Method for getting a deserializer definition on specified method
* or field.
* Type of definition is either instance (of type
* {@link JsonDeserializer}) or Class (of type
* <code>Class&lt;JsonDeserializer></code>); if value of different
* Type of definition is either instance (of type {@link JsonDeserializer})
* or Class (of type {@code Class&lt;JsonDeserializer>});
* type is returned, a runtime exception may be thrown by caller.
*/
public Object findDeserializer(Annotated am) {
......@@ -1005,9 +1002,9 @@ public abstract class AnnotationIntrospector
/**
* Method for getting a deserializer definition for keys of
* associated <code>Map</code> property.
* Type of definition is either instance (of type
* {@link JsonDeserializer}) or Class (of type
* <code>Class&lt;JsonDeserializer></code>); if value of different
* Type of definition is either instance (of type {@link JsonDeserializer})
* or Class (of type {@code Class&lt;JsonDeserializer>});
* if value of different
* type is returned, a runtime exception may be thrown by caller.
*/
public Object findKeyDeserializer(Annotated am) {
......@@ -1018,9 +1015,9 @@ public abstract class AnnotationIntrospector
* Method for getting a deserializer definition for content (values) of
* associated <code>Collection</code>, <code>array</code> or
* <code>Map</code> property.
* Type of definition is either instance (of type
* {@link JsonDeserializer}) or Class (of type
* <code>Class&lt;JsonDeserializer></code>); if value of different
* Type of definition is either instance (of type {@link JsonDeserializer})
* or Class (of type {@code Class&lt;JsonDeserializer>});
* if value of different
* type is returned, a runtime exception may be thrown by caller.
*/
public Object findContentDeserializer(Annotated am) {
......
......@@ -10,6 +10,8 @@ import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.ObjectIdInfo;
import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator;
import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator.Validity;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.databind.util.ClassUtil;
import com.fasterxml.jackson.databind.util.Converter;
......@@ -163,33 +165,36 @@ public abstract class DatabindContext
/**
* Lookup method called when code needs to resolve class name from input;
* usually simple lookup
* usually simple lookup.
* Note that unlike {@link #resolveAndValidateSubType} this method DOES NOT
* validate subtype against configured {@link PolymorphicTypeValidator}: usually
* because such check has already been made.
*
* @since 2.9
*/
public JavaType resolveSubType(JavaType baseType, String subClass)
public JavaType resolveSubType(JavaType baseType, String subClassName)
throws JsonMappingException
{
// 30-Jan-2010, tatu: Most ids are basic class names; so let's first
// check if any generics info is added; and only then ask factory
// to do translation when necessary
if (subClass.indexOf('<') > 0) {
if (subClassName.indexOf('<') > 0) {
// note: may want to try combining with specialization (esp for EnumMap)?
// 17-Aug-2017, tatu: As per [databind#1735] need to ensure assignment
// compatibility -- needed later anyway, and not doing so may open
// security issues.
JavaType t = getTypeFactory().constructFromCanonical(subClass);
JavaType t = getTypeFactory().constructFromCanonical(subClassName);
if (t.isTypeOrSubTypeOf(baseType.getRawClass())) {
return t;
}
} else {
Class<?> cls;
try {
cls = getTypeFactory().findClass(subClass);
cls = getTypeFactory().findClass(subClassName);
} catch (ClassNotFoundException e) { // let caller handle this problem
return null;
} catch (Exception e) {
throw invalidTypeIdException(baseType, subClass, String.format(
throw invalidTypeIdException(baseType, subClassName, String.format(
"problem: (%s) %s",
e.getClass().getName(),
ClassUtil.exceptionMessage(e)));
......@@ -198,7 +203,92 @@ public abstract class DatabindContext
return getTypeFactory().constructSpecializedType(baseType, cls);
}
}
throw invalidTypeIdException(baseType, subClass, "Not a subtype");
throw invalidTypeIdException(baseType, subClassName, "Not a subtype");
}
/**
* Lookup method similar to {@link #resolveSubType} but one that also validates
* that resulting subtype is valid according to given {@link PolymorphicTypeValidator}.
*
* @since 2.10
*/
public JavaType resolveAndValidateSubType(JavaType baseType, String subClass,
PolymorphicTypeValidator ptv)
throws JsonMappingException
{
// Off-line the special case of generic (parameterized) type:
final int ltIndex = subClass.indexOf('<');
if (ltIndex > 0) {
return _resolveAndValidateGeneric(baseType, subClass, ptv, ltIndex);
}
final MapperConfig<?> config = getConfig();
PolymorphicTypeValidator.Validity vld = ptv.validateSubClassName(config, baseType, subClass);
if (vld == Validity.DENIED) {
return _throwSubtypeNameNotAllowed(baseType, subClass, ptv);
}
final Class<?> cls;
try {
cls = getTypeFactory().findClass(subClass);
} catch (ClassNotFoundException e) { // let caller handle this problem
return null;
} catch (Exception e) {
throw invalidTypeIdException(baseType, subClass, String.format(
"problem: (%s) %s",
e.getClass().getName(),
ClassUtil.exceptionMessage(e)));
}
if (!baseType.isTypeOrSuperTypeOf(cls)) {
return _throwNotASubtype(baseType, subClass);
}
final JavaType subType = config.getTypeFactory().constructSpecializedType(baseType, cls);
// May skip check if type was allowed by subclass name already
if (vld != Validity.ALLOWED) {
if (ptv.validateSubType(config, baseType, subType) != Validity.ALLOWED) {
return _throwSubtypeClassNotAllowed(baseType, subClass, ptv);
}
}
return subType;
}
private JavaType _resolveAndValidateGeneric(JavaType baseType, String subClass,
PolymorphicTypeValidator ptv, int ltIndex)
throws JsonMappingException
{
final MapperConfig<?> config = getConfig();
// 24-Apr-2019, tatu: Not 100% sure if we should pass name with type parameters
// or not, but guessing it's more convenient not to have to worry about it so
// strip out
PolymorphicTypeValidator.Validity vld = ptv.validateSubClassName(config, baseType, subClass.substring(0, ltIndex));
if (vld == Validity.DENIED) {
return _throwSubtypeNameNotAllowed(baseType, subClass, ptv);
}
JavaType subType = getTypeFactory().constructFromCanonical(subClass);
if (!subType.isTypeOrSubTypeOf(baseType.getRawClass())) {
return _throwNotASubtype(baseType, subClass);
}
// Unless we were approved already by name, check that actual sub-class acceptable:
if (vld != Validity.ALLOWED) {
if (ptv.validateSubType(config, baseType, subType) != Validity.ALLOWED) {
return _throwSubtypeClassNotAllowed(baseType, subClass, ptv);
}
}
return subType;
}
protected <T> T _throwNotASubtype(JavaType baseType, String subType) throws JsonMappingException {
throw invalidTypeIdException(baseType, subType, "Not a subtype");
}
protected <T> T _throwSubtypeNameNotAllowed(JavaType baseType, String subType,
PolymorphicTypeValidator ptv) throws JsonMappingException {
throw invalidTypeIdException(baseType, subType,
"Configured `PolymorphicTypeValidator` (of type "+ClassUtil.classNameOf(ptv)+") denied resolution");
}
protected <T> T _throwSubtypeClassNotAllowed(JavaType baseType, String subType,
PolymorphicTypeValidator ptv) throws JsonMappingException {
throw invalidTypeIdException(baseType, subType,
"Configured `PolymorphicTypeValidator` (of type "+ClassUtil.classNameOf(ptv)+") denied resolution");
}
/**
......
......@@ -3,7 +3,7 @@ package com.fasterxml.jackson.databind;
import java.util.*;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.json.JsonReadFeature;
import com.fasterxml.jackson.databind.cfg.*;
import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler;
import com.fasterxml.jackson.databind.introspect.*;
......@@ -498,6 +498,10 @@ public final class DeserializationConfig
*/
public DeserializationConfig with(FormatFeature feature)
{
// 08-Oct-2018, tatu: Alas, complexity due to newly (2.10) refactored json-features:
if (feature instanceof JsonReadFeature) {
return _withJsonReadFeatures(feature);
}
int newSet = _formatReadFeatures | feature.getMask();
int newMask = _formatReadFeaturesToChange | feature.getMask();
return ((_formatReadFeatures == newSet) && (_formatReadFeaturesToChange == newMask)) ? this :
......@@ -514,6 +518,10 @@ public final class DeserializationConfig
*/
public DeserializationConfig withFeatures(FormatFeature... features)
{
// 08-Oct-2018, tatu: Alas, complexity due to newly (2.10) refactored json-features:
if (features.length > 0 && (features[0] instanceof JsonReadFeature)) {
return _withJsonReadFeatures(features);
}
int newSet = _formatReadFeatures;
int newMask = _formatReadFeaturesToChange;
for (FormatFeature f : features) {
......@@ -535,6 +543,10 @@ public final class DeserializationConfig
*/
public DeserializationConfig without(FormatFeature feature)
{
// 08-Oct-2018, tatu: Alas, complexity due to newly (2.10) refactored json-features:
if (feature instanceof JsonReadFeature) {
return _withoutJsonReadFeatures(feature);
}
int newSet = _formatReadFeatures & ~feature.getMask();
int newMask = _formatReadFeaturesToChange | feature.getMask();
return ((_formatReadFeatures == newSet) && (_formatReadFeaturesToChange == newMask)) ? this :
......@@ -551,6 +563,10 @@ public final class DeserializationConfig
*/
public DeserializationConfig withoutFeatures(FormatFeature... features)
{
// 08-Oct-2018, tatu: Alas, complexity due to newly (2.10) refactored json-features:
if (features.length > 0 && (features[0] instanceof JsonReadFeature)) {
return _withoutJsonReadFeatures(features);
}
int newSet = _formatReadFeatures;
int newMask = _formatReadFeaturesToChange;
for (FormatFeature f : features) {
......@@ -564,6 +580,60 @@ public final class DeserializationConfig
newSet, newMask);
}
// temporary for 2.10
private DeserializationConfig _withJsonReadFeatures(FormatFeature... features) {
int parserSet = _parserFeatures;
int parserMask = _parserFeaturesToChange;
int newSet = _formatReadFeatures;
int newMask = _formatReadFeaturesToChange;
for (FormatFeature f : features) {
final int mask = f.getMask();
newSet |= mask;
newMask |= mask;
if (f instanceof JsonReadFeature) {
JsonParser.Feature oldF = ((JsonReadFeature) f).mappedFeature();
if (oldF != null) {
final int pmask = oldF.getMask();
parserSet |= pmask;
parserMask |= pmask;
}
}
}
return ((_formatReadFeatures == newSet) && (_formatReadFeaturesToChange == newMask)
&& (_parserFeatures == parserSet) && (_parserFeaturesToChange == parserMask)
) ? this :
new DeserializationConfig(this, _mapperFeatures, _deserFeatures,
parserSet, parserMask, newSet, newMask);
}
// temporary for 2.10
private DeserializationConfig _withoutJsonReadFeatures(FormatFeature... features) {
int parserSet = _parserFeatures;
int parserMask = _parserFeaturesToChange;
int newSet = _formatReadFeatures;
int newMask = _formatReadFeaturesToChange;
for (FormatFeature f : features) {
final int mask = f.getMask();
newSet &= ~mask;
newMask |= mask;
if (f instanceof JsonReadFeature) {
JsonParser.Feature oldF = ((JsonReadFeature) f).mappedFeature();
if (oldF != null) {
final int pmask = oldF.getMask();
parserSet &= ~pmask;
parserMask |= pmask;
}
}
}
return ((_formatReadFeatures == newSet) && (_formatReadFeaturesToChange == newMask)
&& (_parserFeatures == parserSet) && (_parserFeaturesToChange == parserMask)
) ? this :
new DeserializationConfig(this, _mapperFeatures, _deserFeatures,
parserSet, parserMask, newSet, newMask);
}
/*
/**********************************************************
/* Life-cycle, deserialization-specific factory methods
......
......@@ -22,7 +22,9 @@ import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.fasterxml.jackson.databind.exc.InvalidTypeIdException;
import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
import com.fasterxml.jackson.databind.exc.ValueInstantiationException;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
......@@ -153,10 +155,7 @@ public abstract class DeserializationContext
protected DeserializationContext(DeserializerFactory df,
DeserializerCache cache)
{
if (df == null) {
throw new IllegalArgumentException("Cannot pass null DeserializerFactory");
}
_factory = df;
_factory = Objects.requireNonNull(df, "Cannot pass null DeserializerFactory");
if (cache == null) {
cache = new DeserializerCache();
}
......@@ -755,7 +754,7 @@ public abstract class DeserializationContext
JsonDeserializer<Object> deser = findRootValueDeserializer(type);
if (deser == null) {
reportBadDefinition(type,
"Could not find JsonDeserializer for type "+type);
"Could not find JsonDeserializer for type "+ClassUtil.getTypeDescription(type));
}
return (T) deser.deserialize(p, this);
}
......@@ -781,11 +780,29 @@ public abstract class DeserializationContext
if (deser == null) {
return reportBadDefinition(type, String.format(
"Could not find JsonDeserializer for type %s (via property %s)",
type, ClassUtil.nameOf(prop)));
ClassUtil.getTypeDescription(type), ClassUtil.nameOf(prop)));
}
return (T) deser.deserialize(p, this);
}
/**
* @since 2.10
*/
public JsonNode readTree(JsonParser p) throws IOException {
JsonToken t = p.currentToken();
if (t == null) {
t = p.nextToken();
if (t == null) {
return getNodeFactory().missingNode();
}
}
if (t == JsonToken.VALUE_NULL) {
return getNodeFactory().nullNode();
}
return (JsonNode) findRootValueDeserializer(_config.constructType(JsonNode.class))
.deserialize(p, this);
}
/*
/**********************************************************
/* Methods for problem handling
......@@ -860,7 +877,9 @@ public abstract class DeserializationContext
}
throw weirdStringException(keyValue, keyClass, String.format(
"DeserializationProblemHandler.handleWeirdStringValue() for type %s returned value of type %s",
keyClass, key.getClass()));
ClassUtil.getClassDescription(keyClass),
ClassUtil.getClassDescription(key)
));
}
h = h.next();
}
......@@ -904,7 +923,9 @@ public abstract class DeserializationContext
}
throw weirdStringException(value, targetClass, String.format(
"DeserializationProblemHandler.handleWeirdStringValue() for type %s returned value of type %s",
targetClass, instance.getClass()));
ClassUtil.getClassDescription(targetClass),
ClassUtil.getClassDescription(instance)
));
}
h = h.next();
}
......@@ -947,7 +968,9 @@ public abstract class DeserializationContext
}
throw weirdNumberException(value, targetClass, _format(
"DeserializationProblemHandler.handleWeirdNumberValue() for type %s returned value of type %s",
targetClass, key.getClass()));
ClassUtil.getClassDescription(targetClass),
ClassUtil.getClassDescription(key)
));
}
h = h.next();
}
......@@ -970,7 +993,9 @@ public abstract class DeserializationContext
}
throw JsonMappingException.from(p, _format(
"DeserializationProblemHandler.handleWeirdNativeValue() for type %s returned value of type %s",
targetType, goodValue.getClass()));
ClassUtil.getClassDescription(targetType),
ClassUtil.getClassDescription(goodValue)
));
}
}
throw weirdNativeValueException(badValue, raw);
......@@ -1014,7 +1039,9 @@ targetType, goodValue.getClass()));
}
reportBadDefinition(constructType(instClass), String.format(
"DeserializationProblemHandler.handleMissingInstantiator() for type %s returned value of type %s",
instClass, ClassUtil.classNameOf(instance)));
ClassUtil.getClassDescription(instClass),
ClassUtil.getClassDescription((instance)
)));
}
h = h.next();
}
......@@ -1064,12 +1091,18 @@ targetType, goodValue.getClass()));
}
reportBadDefinition(constructType(instClass), String.format(
"DeserializationProblemHandler.handleInstantiationProblem() for type %s returned value of type %s",
instClass, ClassUtil.classNameOf(instance)));
ClassUtil.getClassDescription(instClass),
ClassUtil.classNameOf(instance)
));
}
h = h.next();
}
// 18-May-2016, tatu: Only wrap if not already a valid type to throw
ClassUtil.throwIfIOE(t);
// [databind#2164]: but see if wrapping is desired
if (!isEnabled(DeserializationFeature.WRAP_EXCEPTIONS)) {
ClassUtil.throwIfRTE(t);
}
throw instantiationException(instClass, t);
}
......@@ -1090,7 +1123,7 @@ targetType, goodValue.getClass()));
public Object handleUnexpectedToken(Class<?> instClass, JsonParser p)
throws IOException
{
return handleUnexpectedToken(instClass, p.getCurrentToken(), p, null);
return handleUnexpectedToken(constructType(instClass), p.getCurrentToken(), p, null);
}
/**
......@@ -1111,32 +1144,76 @@ targetType, goodValue.getClass()));
public Object handleUnexpectedToken(Class<?> instClass, JsonToken t,
JsonParser p, String msg, Object... msgArgs)
throws IOException
{
return handleUnexpectedToken(constructType(instClass), t, p, msg, msgArgs);
}
/**
* Method that deserializers should call if the first token of the value to
* deserialize is of unexpected type (that is, type of token that deserializer
* cannot handle). This could occur, for example, if a Number deserializer
* encounter {@link JsonToken#START_ARRAY} instead of
* {@link JsonToken#VALUE_NUMBER_INT} or {@link JsonToken#VALUE_NUMBER_FLOAT}.
*
* @param targetType Type that was to be instantiated
* @param p Parser that points to the JSON value to decode
*
* @return Object that should be constructed, if any; has to be of type <code>instClass</code>
*
* @since 2.10
*/
public Object handleUnexpectedToken(JavaType targetType, JsonParser p)
throws IOException
{
return handleUnexpectedToken(targetType, p.getCurrentToken(), p, null);
}
/**
* Method that deserializers should call if the first token of the value to
* deserialize is of unexpected type (that is, type of token that deserializer
* cannot handle). This could occur, for example, if a Number deserializer
* encounter {@link JsonToken#START_ARRAY} instead of
* {@link JsonToken#VALUE_NUMBER_INT} or {@link JsonToken#VALUE_NUMBER_FLOAT}.
*
* @param targetType Type that was to be instantiated
* @param t Token encountered that does match expected
* @param p Parser that points to the JSON value to decode
*
* @return Object that should be constructed, if any; has to be of type <code>instClass</code>
*
* @since 2.10
*/
public Object handleUnexpectedToken(JavaType targetType, JsonToken t,
JsonParser p, String msg, Object... msgArgs)
throws IOException
{
msg = _format(msg, msgArgs);
LinkedNode<DeserializationProblemHandler> h = _config.getProblemHandlers();
while (h != null) {
Object instance = h.value().handleUnexpectedToken(this,
instClass, t, p, msg);
targetType, t, p, msg);
if (instance != DeserializationProblemHandler.NOT_HANDLED) {
if (_isCompatible(instClass, instance)) {
if (_isCompatible(targetType.getRawClass(), instance)) {
return instance;
}
reportBadDefinition(constructType(instClass), String.format(
reportBadDefinition(targetType, String.format(
"DeserializationProblemHandler.handleUnexpectedToken() for type %s returned value of type %s",
ClassUtil.nameOf(instClass), ClassUtil.classNameOf(instance)));
ClassUtil.getClassDescription(targetType),
ClassUtil.classNameOf(instance)
));
}
h = h.next();
}
if (msg == null) {
if (t == null) {
msg = String.format("Unexpected end-of-input when binding data into %s",
ClassUtil.nameOf(instClass));
ClassUtil.getTypeDescription(targetType));
} else {
msg = String.format("Cannot deserialize instance of %s out of %s token",
ClassUtil.nameOf(instClass), t);
ClassUtil.getTypeDescription(targetType), t);
}
}
reportInputMismatch(instClass, msg);
reportInputMismatch(targetType, msg);
return null; // never gets here
}
......@@ -1176,7 +1253,8 @@ targetType, goodValue.getClass()));
return type;
}
throw invalidTypeIdException(baseType, id,
"problem handler tried to resolve into non-subtype: "+type);
"problem handler tried to resolve into non-subtype: "+
ClassUtil.getTypeDescription(type));
}
h = h.next();
}
......@@ -1206,7 +1284,8 @@ targetType, goodValue.getClass()));
return type;
}
throw invalidTypeIdException(baseType, null,
"problem handler tried to resolve into non-subtype: "+type);
"problem handler tried to resolve into non-subtype: "+
ClassUtil.getTypeDescription(type));
}
h = h.next();
}
......@@ -1218,6 +1297,27 @@ targetType, goodValue.getClass()));
throw missingTypeIdException(baseType, extraDesc);
}
/**
* Method that deserializer may call if it is called to do an update ("merge")
* but deserializer operates on a non-mergeable type. Although this should
* usually be caught earlier, sometimes it may only be caught during operation
* and if so this is the method to call.
* Note that if {@link MapperFeature#IGNORE_MERGE_FOR_UNMERGEABLE} is enabled,
* this method will simply return null; otherwise {@link InvalidDefinitionException}
* will be thrown.
*
* @since 2.10
*/
public void handleBadMerge(JsonDeserializer<?> deser) throws JsonMappingException
{
if (!isEnabled(MapperFeature.IGNORE_MERGE_FOR_UNMERGEABLE)) {
JavaType type = constructType(deser.handledType());
String msg = String.format("Invalid configuration: values of type %s cannot be merged",
ClassUtil.getTypeDescription(type));
throw InvalidDefinitionException.from(getParser(), msg, type);
}
}
/**
* @since 2.9.2
*/
......@@ -1309,12 +1409,11 @@ targetType, goodValue.getClass()));
*
* @since 2.9
*/
public <T> T reportInputMismatch(BeanProperty prop,
public <T> T reportInputMismatch(JsonDeserializer<?> src,
String msg, Object... msgArgs) throws JsonMappingException
{
msg = _format(msg, msgArgs);
JavaType type = (prop == null) ? null : prop.getType();
throw MismatchedInputException.from(getParser(), type, msg);
throw MismatchedInputException.from(getParser(), src.handledType(), msg);
}
/**
......@@ -1323,11 +1422,11 @@ targetType, goodValue.getClass()));
*
* @since 2.9
*/
public <T> T reportInputMismatch(JsonDeserializer<?> src,
public <T> T reportInputMismatch(Class<?> targetType,
String msg, Object... msgArgs) throws JsonMappingException
{
msg = _format(msg, msgArgs);
throw MismatchedInputException.from(getParser(), src.handledType(), msg);
throw MismatchedInputException.from(getParser(), targetType, msg);
}
/**
......@@ -1336,7 +1435,7 @@ targetType, goodValue.getClass()));
*
* @since 2.9
*/
public <T> T reportInputMismatch(Class<?> targetType,
public <T> T reportInputMismatch(JavaType targetType,
String msg, Object... msgArgs) throws JsonMappingException
{
msg = _format(msg, msgArgs);
......@@ -1349,11 +1448,49 @@ targetType, goodValue.getClass()));
*
* @since 2.9
*/
public <T> T reportInputMismatch(JavaType targetType,
public <T> T reportInputMismatch(BeanProperty prop,
String msg, Object... msgArgs) throws JsonMappingException
{
msg = _format(msg, msgArgs);
throw MismatchedInputException.from(getParser(), targetType, msg);
JavaType type = (prop == null) ? null : prop.getType();
final MismatchedInputException e = MismatchedInputException.from(getParser(), type, msg);
// [databind#2357]: Include property name, if we have it
if (prop != null) {
AnnotatedMember member = prop.getMember();
if (member != null) {
e.prependPath(member.getDeclaringClass(), prop.getName());
}
}
throw e;
}
/**
* Helper method used to indicate a problem with input in cases where more
* specific <code>reportXxx()</code> method was not available.
*
* @since 2.10
*/
public <T> T reportPropertyInputMismatch(Class<?> targetType, String propertyName,
String msg, Object... msgArgs) throws JsonMappingException
{
msg = _format(msg, msgArgs);
MismatchedInputException e = MismatchedInputException.from(getParser(), targetType, msg);
if (propertyName != null) {
e.prependPath(targetType, propertyName);
}
throw e;
}
/**
* Helper method used to indicate a problem with input in cases where more
* specific <code>reportXxx()</code> method was not available.
*
* @since 2.10
*/
public <T> T reportPropertyInputMismatch(JavaType targetType, String propertyName,
String msg, Object... msgArgs) throws JsonMappingException
{
return reportPropertyInputMismatch(targetType.getRawClass(), propertyName, msg, msgArgs);
}
public <T> T reportTrailingTokens(Class<?> targetType,
......@@ -1453,25 +1590,13 @@ trailingToken, ClassUtil.nameOf(targetType)
}
/**
* Method that deserializer may call if it is called to do an update ("merge")
* but deserializer operates on a non-mergeable type. Although this should
* usually be caught earlier, sometimes it may only be caught during operation
* and if so this is the method to call.
* Note that if {@link MapperFeature#IGNORE_MERGE_FOR_UNMERGEABLE} is enabled,
* this method will simply return null; otherwise {@link InvalidDefinitionException}
* will be thrown.
*
* @since 2.9
* @deprecated Since 2.10 use {@link #handleBadMerge} instead
*/
public <T> T reportBadMerge(JsonDeserializer<?> deser) throws JsonMappingException
{
if (isEnabled(MapperFeature.IGNORE_MERGE_FOR_UNMERGEABLE)) {
@Deprecated // since 2.10
public <T> T reportBadMerge(JsonDeserializer<?> deser) throws JsonMappingException {
handleBadMerge(deser);
return null;
}
JavaType type = constructType(deser.handledType());
String msg = String.format("Invalid configuration: values of type %s cannot be merged", type);
throw InvalidDefinitionException.from(getParser(), msg, type);
}
/*
/**********************************************************
......@@ -1540,16 +1665,15 @@ trailingToken, ClassUtil.nameOf(targetType)
*
* @param value String value from input being deserialized
* @param instClass Type that String should be deserialized into
* @param msg Message that describes specific problem
* @param msgBase Message that describes specific problem
*
* @since 2.1
*/
public JsonMappingException weirdStringException(String value, Class<?> instClass,
String msg) {
return InvalidFormatException.from(_parser,
String.format("Cannot deserialize value of type %s from String %s: %s",
ClassUtil.nameOf(instClass), _quotedString(value), msg),
value, instClass);
String msgBase) {
final String msg = String.format("Cannot deserialize value of type %s from String %s: %s",
ClassUtil.nameOf(instClass), _quotedString(value), msgBase);
return InvalidFormatException.from(_parser, msg, value, instClass);
}
/**
......@@ -1595,8 +1719,6 @@ trailingToken, ClassUtil.nameOf(targetType)
* if necessary.
*/
public JsonMappingException instantiationException(Class<?> instClass, Throwable cause) {
// Most likely problem with Creator definition, right?
final JavaType type = constructType(instClass);
String excMsg;
if (cause == null) {
excMsg = "N/A";
......@@ -1605,9 +1727,9 @@ trailingToken, ClassUtil.nameOf(targetType)
}
String msg = String.format("Cannot construct instance of %s, problem: %s",
ClassUtil.nameOf(instClass), excMsg);
InvalidDefinitionException e = InvalidDefinitionException.from(_parser, msg, type);
e.initCause(cause);
return e;
// [databind#2162]: use specific exception type as we don't know if it's
// due to type definition, input, or neither
return ValueInstantiationException.from(_parser, msg, constructType(instClass), cause);
}
/**
......@@ -1620,11 +1742,12 @@ trailingToken, ClassUtil.nameOf(targetType)
* if necessary.
*/
public JsonMappingException instantiationException(Class<?> instClass, String msg0) {
// Most likely problem with Creator definition, right?
JavaType type = constructType(instClass);
String msg = String.format("Cannot construct instance of %s: %s",
ClassUtil.nameOf(instClass), msg0);
return InvalidDefinitionException.from(_parser, msg, type);
// [databind#2162]: use specific exception type as we don't know if it's
// due to type definition, input, or neither
return ValueInstantiationException.from(_parser,
String.format("Cannot construct instance of %s: %s",
ClassUtil.nameOf(instClass), msg0),
constructType(instClass));
}
@Override
......
......@@ -34,6 +34,12 @@ public enum DeserializationFeature implements ConfigFeature
* If enabled such values will be deserialized as {@link java.math.BigDecimal}s;
* if disabled, will be deserialized as {@link Double}s.
*<p>
* NOTE: one aspect of {@link java.math.BigDecimal} handling that may need
* configuring is whether trailing zeroes are trimmed:
* {@link com.fasterxml.jackson.databind.node.JsonNodeFactory} has
* {@link com.fasterxml.jackson.databind.node.JsonNodeFactory#withExactBigDecimals} for
* changing default behavior (default is for trailing zeroes to be trimmed).
*<p>
* Feature is disabled by default, meaning that "untyped" floating
* point numbers will by default be deserialized as {@link Double}s
* (choice is for performance reason -- BigDecimals are slower than
......@@ -84,9 +90,9 @@ public enum DeserializationFeature implements ConfigFeature
/**
* Feature that determines whether JSON Array is mapped to
* <code>Object[]</code> or <code>List&lt;Object></code> when binding
* <code>Object[]</code> or <code>List&lt;Object&gt;</code> when binding
* "untyped" objects (ones with nominal type of <code>java.lang.Object</code>).
* If true, binds as <code>Object[]</code>; if false, as <code>List&lt;Object></code>.
* If true, binds as <code>Object[]</code>; if false, as <code>List&lt;Object&gt;</code>.
*<p>
* Feature is disabled by default, meaning that JSON arrays are bound as
* {@link java.util.List}s.
......
......@@ -125,12 +125,9 @@ public abstract class JsonDeserializer<T>
public T deserialize(JsonParser p, DeserializationContext ctxt, T intoValue)
throws IOException
{
if (ctxt.isEnabled(MapperFeature.IGNORE_MERGE_FOR_UNMERGEABLE)) {
ctxt.handleBadMerge(this);
return deserialize(p, ctxt);
}
throw new UnsupportedOperationException("Cannot update object of type "
+intoValue.getClass().getName()+" (by deserializer of type "+getClass().getName()+")");
}
/**
* Deserialization called when type being deserialized is defined to
......@@ -154,6 +151,22 @@ public abstract class JsonDeserializer<T>
return typeDeserializer.deserializeTypedFromAny(p, ctxt);
}
/**
* Method similar to {@link #deserializeWithType(JsonParser,DeserializationContext,TypeDeserializer)}
* but called when merging value. Considered "bad merge" by default implementation,
* but if {@link MapperFeature#IGNORE_MERGE_FOR_UNMERGEABLE} is enabled will simple delegate to
* {@link #deserializeWithType(JsonParser, DeserializationContext, TypeDeserializer)}.
*
* @since 2.10
*/
public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
TypeDeserializer typeDeserializer, T intoValue)
throws IOException
{
ctxt.handleBadMerge(this);
return deserializeWithType(p, ctxt, typeDeserializer);
}
/*
/**********************************************************
/* Fluent factory methods for constructing decorated versions
......@@ -267,9 +280,8 @@ public abstract class JsonDeserializer<T>
* Java null, but for some types (especially primitives) it may be
* necessary to use non-null values.
*<p>
* Since version 2.6 (in which the context argument was added), call is
* expected to be made each and every time a null token needs to
* be handled.
* This method may be called once, or multiple times, depending on what
* {@link #getNullAccessPattern()} returns.
*<p>
* Default implementation simply returns null.
*
......@@ -282,6 +294,11 @@ public abstract class JsonDeserializer<T>
}
/**
* This method may be called in conjunction with calls to
* {@link #getNullValue(DeserializationContext)}, to check whether it needs
* to be called just once (static values), or each time empty value is
* needed.
*<p>
* Default implementation indicates that "null value" to use for input null
* is simply Java `null` for all deserializers, unless overridden by sub-classes.
* This information may be used as optimization.
......@@ -317,13 +334,11 @@ public abstract class JsonDeserializer<T>
* (most commonly when deserializing from empty JSON Strings).
* Usually this is same as {@link #getNullValue} (which in turn
* is usually simply Java null), but it can be overridden
* for types. Or, if type should never be converted from empty
* for specific types. Or, if type should never be converted from empty
* String, method can also throw an exception.
*<p>
* Since version 2.6 (in which the context argument was added), call is
* expected to be made each and every time an empty value is needed.
*<p>
* Since version 2.9 does not require return of `T` any more.
* This method may be called once, or multiple times, depending on what
* {@link #getEmptyAccessPattern()} returns.
*<p>
* Default implementation simply calls {@link #getNullValue} and
* returns value.
......
......@@ -81,6 +81,17 @@ public abstract class JsonNode
@Override
public int size() { return 0; }
/**
* Convenience method that is functionally same as:
*<pre>
* size() == 0
*<pre>
* for all node types.
*
* @since 2.10
*/
public boolean isEmpty() { return size() == 0; }
@Override
public final boolean isValueNode()
{
......@@ -311,7 +322,7 @@ public abstract class JsonNode
* Note, however, that even if this method returns false, it
* is possible that conversion would be possible from other numeric
* types -- to check if this is possible, use
* {@link #canConvertToInt()} instead.
* {@link #canConvertToLong()} instead.
*
* @return True if the value contained by this node is stored as Java <code>long</code>
*/
......@@ -665,6 +676,68 @@ public abstract class JsonNode
return defaultValue;
}
/*
/**********************************************************************
/* Public API, extended traversal (2.10) with "required()"
/**********************************************************************
*/
/**
* @since 2.10
*/
public <T extends JsonNode> T require() {
return _this();
}
/**
* @since 2.10
*/
public <T extends JsonNode> T requireNonNull() {
return _this();
}
/**
* @since 2.10
*/
public JsonNode required(String fieldName) {
return _reportRequiredViolation("Node of type `%s` has no fields", getClass().getName());
}
/**
* @since 2.10
*/
public JsonNode required(int index) {
return _reportRequiredViolation("Node of type `%s` has no indexed values", getClass().getName());
}
/**
* @since 2.10
*/
public JsonNode requiredAt(String pathExpr) {
return requiredAt(JsonPointer.compile(pathExpr));
}
/**
* @since 2.10
*/
public final JsonNode requiredAt(final JsonPointer pathExpr) {
JsonPointer currentExpr = pathExpr;
JsonNode curr = this;
// Note: copied from `at()`
while (true) {
if (currentExpr.matches()) {
return curr;
}
curr = curr._at(currentExpr);
if (curr == null) {
_reportRequiredViolation("No node at '%s' (unmatched part: '%s')",
pathExpr, currentExpr);
}
currentExpr = currentExpr.tail();
}
}
/*
/**********************************************************
/* Public API, value find / existence check methods
......@@ -893,8 +966,10 @@ public abstract class JsonNode
* If the node method is called on is not Object node,
* or if property exists and has value that is not Object node,
* {@link UnsupportedOperationException} is thrown
*<p>
* NOTE: since 2.10 has had co-variant return type
*/
public JsonNode with(String propertyName) {
public <T extends JsonNode> T with(String propertyName) {
throw new UnsupportedOperationException("JsonNode not of type ObjectNode (but "
+getClass().getName()+"), cannot call with() on it");
}
......@@ -906,8 +981,10 @@ public abstract class JsonNode
* If the node method is called on is not Object node,
* or if property exists and has value that is not Array node,
* {@link UnsupportedOperationException} is thrown
*<p>
* NOTE: since 2.10 has had co-variant return type
*/
public JsonNode withArray(String propertyName) {
public <T extends JsonNode> T withArray(String propertyName) {
throw new UnsupportedOperationException("JsonNode not of type ObjectNode (but "
+getClass().getName()+"), cannot call withArray() on it");
}
......@@ -947,10 +1024,10 @@ public abstract class JsonNode
*/
/**
* Method that will produce developer-readable representation of the
* node; which may <b>or may not</b> be as valid JSON.
* If you want valid JSON output (or output formatted using one of
* other Jackson supported data formats) make sure to use
* Method that will produce (as of Jackson 2.10) valid JSON using
* default settings of databind, as String.
* If you want other kinds of JSON output (or output formatted using one of
* other Jackson-supported data formats) make sure to use
* {@link ObjectMapper} or {@link ObjectWriter} to serialize an
* instance, for example:
*<pre>
......@@ -964,6 +1041,16 @@ public abstract class JsonNode
@Override
public abstract String toString();
/**
* Alternative to {@link #toString} that will serialize this node using
* Jackson default pretty-printer.
*
* @since 2.10
*/
public String toPrettyString() {
return toString();
}
/**
* Equality for node objects is defined as full (deep) value
* equality. This means that it is possible to compare complete
......@@ -975,4 +1062,25 @@ public abstract class JsonNode
*/
@Override
public abstract boolean equals(Object o);
/*
/**********************************************************************
/* Helper methods, for sub-classes
/**********************************************************************
*/
// @since 2.10
@SuppressWarnings("unchecked")
protected <T extends JsonNode> T _this() {
return (T) this;
}
/**
* Helper method that throws {@link IllegalArgumentException} as a result of
* violating "required-constraint" for this node (for {@link #required} or related
* methods).
*/
protected <T> T _reportRequiredViolation(String msgTemplate, Object...args) {
throw new IllegalArgumentException(String.format(msgTemplate, args));
}
}
......@@ -195,15 +195,15 @@ public enum MapperFeature implements ConfigFeature
INFER_PROPERTY_MUTATORS(true),
/**
* Feature that determines handling of <code>java.beans.ConstructorProperties<code>
* Feature that determines handling of {@code java.beans.ConstructorProperties}
* annotation: when enabled, it is considered as alias of
* {@link com.fasterxml.jackson.annotation.JsonCreator}, to mean that constructor
* should be considered a property-based Creator; when disabled, only constructor
* parameter name information is used, but constructor is NOT considered an explicit
* Creator (although may be discovered as one using other annotations or heuristics).
*<p>
* Feature is mostly used to help interoperability with frameworks like Lombok
* that may automatically generate <code>ConstructorProperties</code> annotation
* Feature is mostly used to help inter-operability with frameworks like Lombok
* that may automatically generate {@code ConstructorProperties} annotation
* but without necessarily meaning that constructor should be used as Creator
* for deserialization.
*<p>
......@@ -361,7 +361,7 @@ public enum MapperFeature implements ConfigFeature
*<p>
* Note that there is additional performance overhead since incoming property
* names need to be lower-cased before comparison, for cases where there are upper-case
* letters. Overhead for names that are already lower-case should be negligible however.
* letters. Overhead for names that are already lower-case should be negligible.
*<p>
* Feature is disabled by default.
*
......@@ -373,7 +373,7 @@ public enum MapperFeature implements ConfigFeature
/**
* Feature that determines if Enum deserialization should be case sensitive or not.
* If enabled, Enum deserialization will ignore case, that is, case of incoming String
* value and enum id (dependant on other settings, either `name()`, `toString()`, or
* value and enum id (depending on other settings, either `name()`, `toString()`, or
* explicit override) do not need to match.
* <p>
* Feature is disabled by default.
......@@ -382,6 +382,20 @@ public enum MapperFeature implements ConfigFeature
*/
ACCEPT_CASE_INSENSITIVE_ENUMS(false),
/**
* Feature that permits parsing some enumerated text-based value types but ignoring the case
* of the values on deserialization: for example, date/time type deserializers.
* Support for this feature depends on deserializer implementations using it.
*<p>
* Note, however, that regular {@code Enum} types follow {@link #ACCEPT_CASE_INSENSITIVE_ENUMS}
* setting instead.
*<p>
* Feature is disabled by default.
*
* @since 2.10
*/
ACCEPT_CASE_INSENSITIVE_VALUES(false),
/**
* Feature that can be enabled to make property names be
* overridden by wrapper name (usually detected with annotations
......@@ -482,9 +496,8 @@ public enum MapperFeature implements ConfigFeature
* merging is skipped and new value is created (<code>true</code>) or
* an exception is thrown (false).
*<p>
* Feature is disabled by default since non-mergeable property types are ignored
* even if defaults call for merging, and usually explicit per-type or per-property
* settings for such types should result in an exception.
* Feature is enabled by default, to allow use of merge defaults even in presence
* of some unmergeable properties.
*
* @since 2.9
*/
......
......@@ -162,8 +162,14 @@ public class MappingIterator<T> implements Iterator<T>, Closeable
}
}
/**
* Method for getting an "empty" iterator instance: one that never
* has more values; may be freely shared.
*
* @since 2.10 Existed earlier but {@code public} since 2.10
*/
@SuppressWarnings("unchecked")
protected static <T> MappingIterator<T> emptyIterator() {
public static <T> MappingIterator<T> emptyIterator() {
return (MappingIterator<T>) EMPTY_ITERATOR;
}
......@@ -185,15 +191,16 @@ public class MappingIterator<T> implements Iterator<T>, Closeable
}
}
@SuppressWarnings("unchecked")
@Override
public T next()
{
try {
return nextValue();
} catch (JsonMappingException e) {
throw new RuntimeJsonMappingException(e.getMessage(), e);
return (T) _handleMappingException(e);
} catch (IOException e) {
throw new RuntimeException(e.getMessage(), e);
return (T) _handleIOException(e);
}
}
......@@ -218,10 +225,6 @@ public class MappingIterator<T> implements Iterator<T>, Closeable
/**********************************************************
*/
/*
*/
/**
* Equivalent of {@link #next} but one that may throw checked
* exceptions from Jackson due to invalid input.
......