Merge branch '2.7' into 2.8
diff --git a/.gitattributes b/.gitattributes
index 4cf2927..780a8a8 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,3 +1,7 @@
# Do not merge `pom.xml` from older version, as it will typically conflict
+#
+# note: also need:
+# git config --global merge.ours.driver true
+# (and if ever need to disable change `true` to `false`
pom.xml merge=ours
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 0000000..0874104
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,6 @@
+We appreciate issues as very valuable contributions, but just to make sure here are things that are important to do before filing an issue:
+
+* Only report issues, and ask Usage Questions on [Jackson-users](https://groups.google.com/forum/#!search/jackson-users) list -- ypu are more likely to get help that way
+* Check to see if this issue has already been reported (quick glance at existing issues): no deep search necessary, just quick sanity check
+* Include version information for Jackson version you use
+* (optional but highly recommended) Verify that the problem occurs with the latest patch of same minor version; and even better, if possible, try using the latest stable patch version
diff --git a/.travis.yml b/.travis.yml
index 4248da6..d05a18b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -14,11 +14,10 @@
branches:
only:
- master
- - "2.6"
- "2.7"
+ - "2.8"
env:
global:
- secure: "ZIFMeNzZCvLYWICiUyHFSm8Ypz6lYFbF/+LGz6a+Sh8B9tSnvYvlrE4gSFJj3NzRWY0RG/vizoyFEpVgxOnzXDMLuuy/dreupnHR1FfVmiaiceK4JHRwAzl0lQzr2hdVwJbqpX0GfrxKmbg8Jvv9WPKtfM6X4167ElVj4O/8IRc="
- secure: "VOC/J8eFbM2OeysBp0Gx0G73MySRATCoN7hc9Ok0kJXE/dbE/2KOoFlTwwxUgErwJXkjJKKXafYcbq/22S7wkaoDW5XAvplSu3skxdE0NRGVzozcDMXO5UoGlt9Ti4XIBMCvy7bB/NO+J5IgNedCtcNnBsrGZCdodW3DTYtqQLs="
-
diff --git a/README.md b/README.md
index 1c3d553..0bff2e5 100644
--- a/README.md
+++ b/README.md
@@ -11,6 +11,7 @@
[![Build Status](https://travis-ci.org/FasterXML/jackson-databind.svg?branch=master)](https://travis-ci.org/FasterXML/jackson-databind) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.fasterxml.jackson.core/jackson-databind/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.fasterxml.jackson.core/jackson-databind)
[![Javadoc](https://javadoc-emblem.rhcloud.com/doc/com.fasterxml.jackson.core/jackson-databind/badge.svg)](http://www.javadoc.io/doc/com.fasterxml.jackson.core/jackson-databind)
+[![Coverage Status](https://coveralls.io/repos/github/FasterXML/jackson-databind/badge.svg?branch=master)](https://coveralls.io/github/FasterXML/jackson-databind?branch=master)
-----
@@ -375,7 +376,7 @@
But here are couple of potentially useful use cases:
```java
-// Convert from int[] to List<Integer>
+// Convert from List<Integer> to int[]
List<Integer> sourceList = ...;
int[] ints = mapper.convertValue(sourceList, int[].class);
// Convert a POJO into Map!
@@ -425,7 +426,7 @@
# Further reading
-* [Overall Jackson Docs](../../../jackson/docs)
+* [Overall Jackson Docs](../../../jackson-docs)
* [Project wiki page](https://github.com/FasterXML/jackson-databind/wiki)
Related:
diff --git a/attic/MappingIteratorReadTest.java b/attic/MappingIteratorReadTest.java
deleted file mode 100644
index 25aa64c..0000000
--- a/attic/MappingIteratorReadTest.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.fasterxml.jackson.databind.seq;
-
-import java.util.List;
-
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.*;
-
-/**
- * Test(s) for [databind#823], adding support for reading root-level
- * sequences.
- */
-public class MappingIteratorReadTest extends BaseMapTest
-{
- public void testSimple() throws Exception
- {
- final ObjectMapper MAPPER = new ObjectMapper();
- final String JSON = aposToQuotes("{'str':'a'}{'str':'b'}\n {'str':'c'}");
- MappingIterator<StringWrapper> it = MAPPER
- .readValue(JSON, new TypeReference<MappingIterator<StringWrapper>>() { });
- List<StringWrapper> all = it.readAll();
- assertEquals(3, all.size());
- assertEquals("c", all.get(2).str);
- it.close();
- }
-}
diff --git a/pom.xml b/pom.xml
index 07a9be6..b19b69b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,12 +5,12 @@
<parent>
<groupId>com.fasterxml.jackson</groupId>
<artifactId>jackson-parent</artifactId>
- <version>2.7</version>
+ <version>2.8</version>
</parent>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
- <version>2.7.9-SNAPSHOT</version>
+ <version>2.8.4-SNAPSHOT</version>
<name>jackson-databind</name>
<packaging>bundle</packaging>
<description>General data-binding functionality for Jackson: works on core streaming API</description>
@@ -25,13 +25,12 @@
</scm>
<properties>
- <!-- Ok, so, Jackson 2.7 require JDK 1.7 (except for annotations/streaming). But
- we do not have strict need for Java 7 bytecode. And, unfortunately, it looks
- like we have some issues with Mockito tests if we try to go "full 1.7"...
- So that is why... this:
+ <!-- With Jackson 2.8 we will require JDK 7 (except for annotations/streaming),
+ and new language features (diamond pattern) may be used.
+ Whether JDK dependencies are directly used is an open question as of Feb-2016
-->
- <javac.src.version>1.6</javac.src.version>
- <javac.target.version>1.6</javac.target.version>
+ <javac.src.version>1.7</javac.src.version>
+ <javac.target.version>1.7</javac.target.version>
<!-- Can not use default, since group id != Java package name here -->
<osgi.export>com.fasterxml.jackson.databind.*;version=${project.version}</osgi.export>
@@ -51,23 +50,28 @@
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
- <version>2.7.8</version>
+ <version>2.8.3</version>
</dependency>
<!-- and for testing we need a few libraries
libs for which we use reflection for code, but direct dep for testing
-->
- <!-- Mock -->
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
- <version>1.6.3</version>
+ <version>1.6.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
- <version>1.6.3</version>
+ <version>1.6.5</version>
<scope>test</scope>
</dependency>
<!-- For testing TestNoClassDefFoundDeserializer -->
@@ -77,6 +81,12 @@
<version>1.0.0</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>com.google.jimfs</groupId>
+ <artifactId>jimfs</artifactId>
+ <version>1.1</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
diff --git a/release-notes/CREDITS b/release-notes/CREDITS
index cb7fd57..bed2b15 100644
--- a/release-notes/CREDITS
+++ b/release-notes/CREDITS
@@ -147,6 +147,10 @@
(2.6.3)
* Reported #1120: String value omitted from weirdStringException
(2.6.6)
+ * Reported, fixed #1235: `java.nio.file.Path` support incomplete
+ (2.8.0)
+ * Reported #1270: Generic type returned from type id resolver seems to be ignored
+ (2.8.0)
Steve Sanbeg: (sanbeg@github)
* Contributed #482: Make date parsing error behavior consistent with JDK
@@ -480,3 +484,74 @@
* Reported, suggested fix for #1368: Problem serializing `JsonMappingException` due to addition
of non-ignored `processor` property (added in 2.7)
(2.7.8)
+
+Artur Jonkisz (ajonkisz@github)
+ * Reported #960: `@JsonCreator` not working on a factory with no arguments for ae enum type
+ (2.8.0)
+
+Mikhail Kokho (mkokho@github)
+ * Contributed impl for #990: Allow failing on `null` values for creator (add
+ `DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES`)
+ (2.8.0)
+
+Aleksandr Oksenenko (oleksandr-oksenenko@github)
+ * Reported #999: External property is not deserialized
+ (2.8.0)
+
+Lokesh Kumar (LokeshN@github)
+ * Contributed impl for #1082: Can not use static Creator factory methods for `Enum`s,
+ with JsonCreator.Mode.PROPERTIES
+ (2.8.0)
+ * Reported #1217: `@JsonIgnoreProperties` on Pojo fields not working for deserialization
+ (2.8.0)
+
+Ross Goldberg
+ * Reported #1165, provided fix for: `CoreXMLDeserializers` does not handle
+ time-only `XMLGregorianCalendar`s
+ (2.8.0)
+
+Maarten Billemont (lhunath@github)
+ * Suggested #1184: Allow overriding of `transient` with explicit inclusion with `@JsonProperty`
+ (2.8.0)
+
+Vladimir Kulev (lightoze@github)
+ * Reported #1028: Ignore USE_BIG_DECIMAL_FOR_FLOATS for NaN/Infinity
+ (2.8.0)
+
+Ari Fogel (arifogel@github)
+ * Reported #1261, contributed fix for: `@JsonIdentityInfo` deserialization fails with
+ combination of forward references, `@JsonCreator`
+ (2.8.0)
+
+Andriy Plokhotnyuk (plokhotnyuk@github)
+ * Requested #1277: Add caching of resolved generic types for `TypeFactory`
+ (2.8.0)
+
+Arek Gabiga (arekgabiga@github)
+ * Reported #1297: Deserialization of generic type with Map.class
+ (2.8.1)
+
+Chris Jester-Young (cky@github)
+ * Contributed #1335: Unconditionally call `TypeIdResolver.getDescForKnownTypeIds`
+ (2.8.2)
+
+Andrew Snare (asnare@github)
+ * Reported #1315: Binding numeric values can BigDecimal lose precision
+ (2.8.2)
+
+Gili Tzabari (cowwoc@github)
+ * Reported #1351: `@JsonInclude(NON_DEFAULT)` doesn't omit null fields
+ (2.8.3)
+
+Oleg Zhukov (OlegZhukov@github)
+ * Reported #1384: `@JsonDeserialize(keyUsing = ...)` does not work correctly
+ together with `DefaultTyping.NON_FINAL`
+ (2.8.4)
+
+Pavel Popov (tolkonepiu@github)
+ * Contributed fix #1389: Problem with handling of multi-argument creator with Enums
+ (2.8.4)
+
+Connor Kuhn (ckuhn@github)
+ * Contributed #1341: FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY
+ (2.9.0)
diff --git a/release-notes/VERSION b/release-notes/VERSION
index 99d94ae..cf2038f 100644
--- a/release-notes/VERSION
+++ b/release-notes/VERSION
@@ -4,6 +4,93 @@
=== Releases ===
------------------------------------------------------------------------
+2.8.4 (not yet released)
+
+#1384: `@JsonDeserialize(keyUsing = ...)` does not work correctly together with
+ DefaultTyping.NON_FINAL
+ (reported by Oleg Z)
+#1389 Problem with handling of multi-argument creator with Enums
+ (fix contributed by Pavel P)
+
+2.8.3 (17-Sep-2016)
+
+#929: `@JsonCreator` not working on a factory with multiple arguments for a enum type
+#1351: `@JsonInclude(NON_DEFAULT)` doesn't omit null fields
+ (reported by Gili T)
+#1353: Improve error-handling for `java.net.URL` deserialization
+#1361: Change `TokenBuffer` to use new `writeEmbeddedObject()` if possible
+
+2.8.2 (30-Aug-2016)
+
+#1315: Binding numeric values can BigDecimal lose precision
+ (reported by Andrew S)
+#1327: Class level `@JsonInclude(JsonInclude.Include.NON_EMPTY)` is ignored
+ (reported by elruwen@github)
+#1335: Unconditionally call `TypeIdResolver.getDescForKnownTypeIds`
+ (contributed by Chris J-Y)
+
+2.8.1 (20-Jul-2016)
+
+#1256: `Optional.empty()` not excluded if property declared with type `Object`
+#1288: Type id not exposed for `JsonTypeInfo.As.EXTERNAL_PROPERTY` even when `visible` set to `true`
+ (reported by libetl@github)
+#1289: Optimize construction of `ArrayList`, `LinkedHashMap` instances
+#1291: Backward-incompatible behaviour of 2.8: deserializing enum types
+ with two static factory methods fail by default
+#1297: Deserialization of generic type with Map.class
+ (reported by Arek G)
+#1302: NPE for `ResolvedRecursiveType` in 2.8.0 due to caching
+
+2.8.0 (04-Jul-2016)
+
+#621: Allow definition of "ignorable types" without annotation (using
+ `Mapper.configOverride(type).setIsIgnoredType(true)`
+#867: Support `SerializationFeature.WRITE_EMPTY_JSON_ARRAYS ` for `JsonNode`
+#903: Add `JsonGenerator` reference to `SerializerProvider`
+#931: Add new method in `Deserializers.Base` to support `ReferenceType`
+#960: `@JsonCreator` not working on a factory with no arguments for an enum type
+ (reported by Artur J)
+#990: Allow failing on `null` values for creator (add
+ `DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES`)
+ (contributed by mkokho@github)
+#999: External property is not deserialized
+ (reported by Aleksandr O)
+#1017: Add new mapping exception type ('InvalidTypeIdException') for subtype resolution errors
+ (suggested by natnan@github)
+#1028: Ignore USE_BIG_DECIMAL_FOR_FLOATS for NaN/Infinity
+ (reported by Vladimir K, lightoze@github)
+#1047: Allow use of `@JsonAnySetter` on a Map-valued field, no need for setter
+#1082: Can not use static Creator factory methods for `Enum`s, with JsonCreator.Mode.PROPERTIES
+ (contributed by Lokesh K)
+#1084: Change `TypeDeserializerBase` to take `JavaType` for `defaultImpl`, NOT `Class`
+#1126: Allow deserialization of unknown Enums using a predefined value
+ (contributed by Alejandro R)
+#1136: Implement `TokenBuffer.writeEmbeddedObject(Object)`
+ (suggested by Gregoire C, gcxRun@github)
+#1165: CoreXMLDeserializers does not handle time-only XMLGregorianCalendars
+ (reported, contributed fix by Ross G)
+#1181: Add the ability to specify the initial capacity of the ArrayNode
+ (suggested by Matt V, mveitas@github)
+#1184: Allow overriding of `transient` with explicit inclusion with `@JsonProperty`
+ (suggested by Maarten B)
+#1187: Refactor `AtomicReferenceDeserializer` into `ReferenceTypeDeserializer`
+#1204: Add a convenience accessor `JavaType.hasContentType()` (true for container or reference type)
+#1206: Add "anchor type" member for `ReferenceType`
+#1211: Change `JsonValueSerializer` to get `AnnotatedMethod`, not "raw" method
+#1217: `@JsonIgnoreProperties` on Pojo fields not working for deserialization
+ (reported by Lokesh K)
+#1221: Use `Throwable.addSuppressed()` directly and/or via try-with-resources
+#1232: Add support for `JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES`
+#1233: Add support for `JsonFormat.Feature.WRITE_SORTED_MAP_ENTRIES`
+#1235: `java.nio.file.Path` support incomplete
+ (reported by, fix contributed by Benson M)
+#1261: JsonIdentityInfo broken deserialization involving forward references and/or cycles
+ (reported by, fix contributed by Ari F)
+#1270: Generic type returned from type id resolver seems to be ignored
+ (reported by Benson M)
+#1277: Add caching of resolved generic types for `TypeFactory`
+ (requested by Andriy P)
+
2.7.8 (26-Sep-2016)
#877: @JsonIgnoreProperties`: ignoring the "cause" property of `Throwable` on GAE
diff --git a/src/main/java/com/fasterxml/jackson/databind/AbstractTypeResolver.java b/src/main/java/com/fasterxml/jackson/databind/AbstractTypeResolver.java
index da6c889..587c29f 100644
--- a/src/main/java/com/fasterxml/jackson/databind/AbstractTypeResolver.java
+++ b/src/main/java/com/fasterxml/jackson/databind/AbstractTypeResolver.java
@@ -30,11 +30,13 @@
return null;
}
- // !!! 29-Nov-2015, tatu: TODO: mark deprecated in 2.8
/**
* Older variant of {@link #resolveAbstractType(DeserializationConfig, BeanDescription)};
- * obsoleted in 2.7, to be deprecated in 2.8
+ * obsoleted in 2.7
+ *
+ * @deprecated since 2.8 (may be removed from 2.9 or later)
*/
+ @Deprecated
public JavaType resolveAbstractType(DeserializationConfig config,
JavaType type) {
return null;
@@ -46,10 +48,7 @@
* when no concrete implementation was found.
* It will be called after checking all other possibilities,
* including defaulting.
- *<p>
- * Default implementation will call obsolete method for Jackson 2.7,
- * to try to keep some level of backwards compatibility.
- *
+ *
* @param config Configuration in use; should always be of type
* <code>DeserializationConfig</code>
* @param typeDesc Description of the POJO type to resolve
@@ -62,6 +61,6 @@
*/
public JavaType resolveAbstractType(DeserializationConfig config,
BeanDescription typeDesc) {
- return resolveAbstractType(config, typeDesc.getType());
+ return null;
}
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java b/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java
index 3966997..c1e3655 100644
--- a/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java
+++ b/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java
@@ -5,6 +5,7 @@
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.Version;
@@ -226,15 +227,46 @@
}
/**
- * Method for finding list of properties to ignore for given class
- * (null is returned if not specified).
- * List of property names is applied
- * after other detection mechanisms, to filter out these specific
- * properties from being serialized and deserialized.
- *
+ * Method for finding information about properties to ignore either by
+ * name, or by more general specification ("ignore all unknown").
+ * This method combines multiple aspects of ignorals and deprecates
+ * earlier methods such as
+ * {@link #findPropertiesToIgnore(Annotated, boolean)} and
+ * {@link #findIgnoreUnknownProperties(AnnotatedClass)}.
+ *
+ * @since 2.8
+ */
+ public JsonIgnoreProperties.Value findPropertyIgnorals(Annotated ac)
+ {
+ // 28-Apr-2016, tatu: For backwards compatibility let's delegate to older
+ // methods, for Jackson 2.8
+ String[] ignorals = findPropertiesToIgnore(ac, true);
+ Boolean b = (ac instanceof AnnotatedClass) ?
+ findIgnoreUnknownProperties((AnnotatedClass) ac) : null;
+ JsonIgnoreProperties.Value v;
+ if (ignorals == null) {
+ if (b == null) {
+ return null;
+ }
+ v = JsonIgnoreProperties.Value.empty();
+ } else {
+ v = JsonIgnoreProperties.Value.forIgnoredProperties(ignorals);
+ }
+ if (b != null) {
+ v = b.booleanValue() ? v.withIgnoreUnknown() : v.withoutIgnoreUnknown();
+ }
+ return v;
+ }
+
+ /**
* @param forSerialization True if requesting properties to ignore for serialization;
* false if for deserialization
+ *
+ * @since 2.6
+ *
+ * @deprecated Since 2.8, use {@link #findPropertyIgnorals} instead
*/
+ @Deprecated // since 2.8
public String[] findPropertiesToIgnore(Annotated ac, boolean forSerialization) {
return null;
}
@@ -242,15 +274,17 @@
/**
* @deprecated Since 2.6, use variant that takes second argument.
*/
- @Deprecated
+ @Deprecated // since 2.6
public String[] findPropertiesToIgnore(Annotated ac) {
- // Changed in 2.7 to call from old to new; with 2.6 was opposite
- return findPropertiesToIgnore(ac, true);
+ return null;
}
-
+
/**
* Method for checking whether an annotation indicates that all unknown properties
+ *
+ * @deprecated Since 2.8, use {@link #findPropertyIgnorals} instead
*/
+ @Deprecated // since 2.8
public Boolean findIgnoreUnknownProperties(AnnotatedClass ac) { return null; }
/**
@@ -981,14 +1015,19 @@
return false;
}
- // TODO: Deprecate in 2.8?
/**
* Method for determining the String value to use for serializing
* given enumeration entry; used when serializing enumerations
* as Strings (the standard method).
*
* @return Serialized enum value.
+ *
+ * @deprecated Since 2.8: use {@link #findEnumValues} instead because this method
+ * does not properly handle override settings (defaults to <code>enum.name</code>
+ * without indicating whether that is explicit or not), and is inefficient to
+ * call one-by-one.
*/
+ @Deprecated
public String findEnumValue(Enum<?> value) {
return value.name();
}
@@ -1018,6 +1057,18 @@
return names;
}
+ /**
+ * Finds the Enum value that should be considered the default value, if possible.
+ *
+ * @param enumCls The Enum class to scan for the default value.
+ * @return null if none found or it's not possible to determine one.
+ *
+ * @since 2.8
+ */
+ public Enum<?> findDefaultEnumValue(Class<Enum<?>> enumCls) {
+ return null;
+ }
+
/*
/**********************************************************
/* Deserialization: general annotations
diff --git a/src/main/java/com/fasterxml/jackson/databind/BeanDescription.java b/src/main/java/com/fasterxml/jackson/databind/BeanDescription.java
index dc7838f..2a4257e 100644
--- a/src/main/java/com/fasterxml/jackson/databind/BeanDescription.java
+++ b/src/main/java/com/fasterxml/jackson/databind/BeanDescription.java
@@ -21,12 +21,6 @@
*/
public abstract class BeanDescription
{
- /*
- /**********************************************************
- /* Configuration
- /**********************************************************
- */
-
/**
* Bean type information, including raw class and possible
* * generics information
@@ -80,7 +74,7 @@
* types of member object, such as return and argument types of
* methods and constructors, and types of fields.
*
- * @deprecated Since 2.7, use {@link #resolveType(java.lang.reflect.Type)} instead.
+ * @deprecated Since 2.7, should not need to access bindings directly
*/
@Deprecated
public abstract TypeBindings bindingsForBeanType();
@@ -88,7 +82,11 @@
/**
* Method for resolving given JDK type, using this bean as the
* generic type resolution context.
+ *
+ * @deprecated Since 2.8, should simply call <code>getType</code> of
+ * property accessor directly.
*/
+ @Deprecated
public abstract JavaType resolveType(java.lang.reflect.Type jdkType);
/**
@@ -172,6 +170,15 @@
public abstract AnnotatedMethod findAnySetter();
/**
+ * Method used to locate the field of the class that implements
+ * {@link com.fasterxml.jackson.annotation.JsonAnySetter} If no such method
+ * exists null is returned. If more than one are found, an exception is thrown.
+ *
+ * @since 2.8
+ */
+ public abstract AnnotatedMember findAnySetterField();
+
+ /**
* Method for locating the getter method that is annotated with
* {@link com.fasterxml.jackson.annotation.JsonValue} annotation,
* if any. If multiple ones are found,
@@ -188,6 +195,12 @@
*/
/**
+ * Method for finding annotation-indicated inclusion definition (if any);
+ * possibly overriding given default value.
+ *<p>
+ * NOTE: does NOT use global inclusion default settings as the base, unless
+ * passed as `defValue`.
+ *
* @since 2.7
*/
public abstract JsonInclude.Value findPropertyInclusion(JsonInclude.Value defValue);
diff --git a/src/main/java/com/fasterxml/jackson/databind/BeanProperty.java b/src/main/java/com/fasterxml/jackson/databind/BeanProperty.java
index 924641d..525aa11 100644
--- a/src/main/java/com/fasterxml/jackson/databind/BeanProperty.java
+++ b/src/main/java/com/fasterxml/jackson/databind/BeanProperty.java
@@ -136,7 +136,10 @@
* use {@link #findPropertyFormat} if such defaults would be useful.
*
* @since 2.6
+ *
+ * @deprecated since 2.8 use {@link #findPropertyFormat} instead.
*/
+ @Deprecated
public JsonFormat.Value findFormatOverrides(AnnotationIntrospector intr);
/**
@@ -195,7 +198,6 @@
*/
public static class Std implements BeanProperty
{
-
protected final PropertyName _name;
protected final JavaType _type;
protected final PropertyName _wrapperName;
@@ -234,16 +236,6 @@
this(base._name, newType, base._wrapperName, base._contextAnnotations, base._member, base._metadata);
}
- @Deprecated // since 2.3
- public Std(String name, JavaType type, PropertyName wrapperName,
- Annotations contextAnnotations, AnnotatedMember member,
- boolean isRequired)
- {
- this(new PropertyName(name), type, wrapperName, contextAnnotations,
- member,
- isRequired ? PropertyMetadata.STD_REQUIRED : PropertyMetadata.STD_OPTIONAL);
- }
-
public Std withType(JavaType type) {
return new Std(this, type);
}
@@ -307,13 +299,6 @@
@Override public PropertyMetadata getMetadata() { return _metadata; }
@Override public AnnotatedMember getMember() { return _member; }
- /**
- *<p>
- * TODO: move to {@link BeanProperty} in near future, once all standard
- * implementations define it.
- *
- * @since 2.5
- */
@Override
public boolean isVirtual() { return false; }
diff --git a/src/main/java/com/fasterxml/jackson/databind/DatabindContext.java b/src/main/java/com/fasterxml/jackson/databind/DatabindContext.java
index 849b9cc..f42a0bb 100644
--- a/src/main/java/com/fasterxml/jackson/databind/DatabindContext.java
+++ b/src/main/java/com/fasterxml/jackson/databind/DatabindContext.java
@@ -4,9 +4,8 @@
import java.util.Locale;
import java.util.TimeZone;
-import com.fasterxml.jackson.annotation.JsonFormat;
-import com.fasterxml.jackson.annotation.ObjectIdGenerator;
-import com.fasterxml.jackson.annotation.ObjectIdResolver;
+import com.fasterxml.jackson.annotation.*;
+
import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.introspect.Annotated;
@@ -90,7 +89,7 @@
* @since 2.7
*/
public abstract JsonFormat.Value getDefaultPropertyFormat(Class<?> baseType);
-
+
/*
/**********************************************************
/* Generic attributes (2.3+)
diff --git a/src/main/java/com/fasterxml/jackson/databind/DeserializationConfig.java b/src/main/java/com/fasterxml/jackson/databind/DeserializationConfig.java
index 58487eb..68556ad 100644
--- a/src/main/java/com/fasterxml/jackson/databind/DeserializationConfig.java
+++ b/src/main/java/com/fasterxml/jackson/databind/DeserializationConfig.java
@@ -3,12 +3,10 @@
import java.text.DateFormat;
import java.util.*;
-import com.fasterxml.jackson.annotation.JsonAutoDetect;
-import com.fasterxml.jackson.annotation.JsonFormat;
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
-import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.annotation.*;
+
import com.fasterxml.jackson.core.*;
+
import com.fasterxml.jackson.databind.cfg.*;
import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler;
import com.fasterxml.jackson.databind.introspect.*;
@@ -104,9 +102,9 @@
*/
public DeserializationConfig(BaseSettings base,
SubtypeResolver str, SimpleMixInResolver mixins,
- RootNameLookup rootNames)
+ RootNameLookup rootNames, ConfigOverrides configOverrides)
{
- super(base, str, mixins, rootNames);
+ super(base, str, mixins, rootNames, configOverrides);
_deserFeatures = collectFeatureDefaults(DeserializationFeature.class);
_nodeFactory = JsonNodeFactory.instance;
_problemHandlers = null;
@@ -116,6 +114,15 @@
_formatReadFeaturesToChange = 0;
}
+ /**
+ * @deprecated Since 2.8, remove from 2.9 or later
+ */
+ @Deprecated
+ public DeserializationConfig(BaseSettings base, SubtypeResolver str,
+ SimpleMixInResolver mixins, RootNameLookup rootNames) {
+ this(base, str, mixins, rootNames, null);
+ }
+
private DeserializationConfig(DeserializationConfig src,
int mapperFeatures, int deserFeatures,
int parserFeatures, int parserFeatureMask,
@@ -233,15 +240,14 @@
}
/**
- * Copy-constructor used for making a copy to be used by new {@link ObjectMapper}
- * or {@link ObjectReader}.
+ * Copy-constructor used for making a copy used by new {@link ObjectMapper}.
*
- * @since 2.6
+ * @since 2.8
*/
protected DeserializationConfig(DeserializationConfig src, SimpleMixInResolver mixins,
- RootNameLookup rootNames)
+ RootNameLookup rootNames, ConfigOverrides configOverrides)
{
- super(src, mixins, rootNames);
+ super(src, mixins, rootNames, configOverrides);
_deserFeatures = src._deserFeatures;
_problemHandlers = src._problemHandlers;
_nodeFactory = src._nodeFactory;
@@ -767,35 +773,12 @@
return getClassIntrospector().forDirectClassAnnotations(this, type, this);
}
- @Override
- public VisibilityChecker<?> getDefaultVisibilityChecker()
- {
- VisibilityChecker<?> vchecker = super.getDefaultVisibilityChecker();
- if (!isEnabled(MapperFeature.AUTO_DETECT_SETTERS)) {
- vchecker = vchecker.withSetterVisibility(Visibility.NONE);
- }
- if (!isEnabled(MapperFeature.AUTO_DETECT_CREATORS)) {
- vchecker = vchecker.withCreatorVisibility(Visibility.NONE);
- }
- if (!isEnabled(MapperFeature.AUTO_DETECT_GETTERS)) {
- vchecker = vchecker.withGetterVisibility(Visibility.NONE);
- }
- if (!isEnabled(MapperFeature.AUTO_DETECT_IS_GETTERS)) {
- vchecker = vchecker.withIsGetterVisibility(Visibility.NONE);
- }
- if (!isEnabled(MapperFeature.AUTO_DETECT_FIELDS)) {
- vchecker = vchecker.withFieldVisibility(Visibility.NONE);
- }
- return vchecker;
- }
-
/*
/**********************************************************
/* Configuration: default settings with per-type overrides
/**********************************************************
*/
-
- // property inclusion not used on deserialization yet (2.7): may be added in future
+
@Override
public JsonInclude.Value getDefaultPropertyInclusion() {
return EMPTY_INCLUDE;
@@ -803,13 +786,28 @@
@Override
public JsonInclude.Value getDefaultPropertyInclusion(Class<?> baseType) {
+ ConfigOverride overrides = findConfigOverride(baseType);
+ if (overrides != null) {
+ JsonInclude.Value v = overrides.getInclude();
+ if (v != null) {
+ return v;
+ }
+ }
return EMPTY_INCLUDE;
}
@Override
- public JsonFormat.Value getDefaultPropertyFormat(Class<?> baseType) {
- // !!! TODO: per-type defaults
- return EMPTY_FORMAT;
+ public JsonInclude.Value getDefaultPropertyInclusion(Class<?> baseType,
+ JsonInclude.Value defaultIncl)
+ {
+ ConfigOverride overrides = findConfigOverride(baseType);
+ if (overrides != null) {
+ JsonInclude.Value v = overrides.getInclude();
+ if (v != null) {
+ return v;
+ }
+ }
+ return defaultIncl;
}
/*
diff --git a/src/main/java/com/fasterxml/jackson/databind/DeserializationContext.java b/src/main/java/com/fasterxml/jackson/databind/DeserializationContext.java
index 3e504bd..0609c1e 100644
--- a/src/main/java/com/fasterxml/jackson/databind/DeserializationContext.java
+++ b/src/main/java/com/fasterxml/jackson/databind/DeserializationContext.java
@@ -12,12 +12,16 @@
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.cfg.ContextAttributes;
import com.fasterxml.jackson.databind.deser.*;
+import com.fasterxml.jackson.databind.deser.impl.ObjectIdReader;
import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId;
import com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer;
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.introspect.Annotated;
+import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.databind.util.*;
@@ -236,7 +240,7 @@
public final JsonFormat.Value getDefaultPropertyFormat(Class<?> baseType) {
return _config.getDefaultPropertyFormat(baseType);
}
-
+
@Override
public final AnnotationIntrospector getAnnotationIntrospector() {
return _config.getAnnotationIntrospector();
@@ -405,11 +409,6 @@
/**********************************************************
*/
- @Deprecated // since 2.3, use overloaded variant
- public boolean hasValueDeserializerFor(JavaType type) {
- return hasValueDeserializerFor(type, null);
- }
-
/**
* Method for checking whether we could find a deserializer
* for given type.
@@ -518,9 +517,6 @@
*/
public abstract ReadableObjectId findObjectId(Object id, ObjectIdGenerator<?> generator, ObjectIdResolver resolver);
- @Deprecated // since 2.4
- public abstract ReadableObjectId findObjectId(Object id, ObjectIdGenerator<?> generator);
-
/**
* Method called to ensure that every object id encounter during processing
* are resolved.
@@ -690,12 +686,12 @@
return deser;
}
- @Deprecated // since 2.5; remove from 2.7
+ @Deprecated // since 2.5; remove from 2.9
public JsonDeserializer<?> handlePrimaryContextualization(JsonDeserializer<?> deser, BeanProperty prop) throws JsonMappingException {
return handlePrimaryContextualization(deser, prop, TypeFactory.unknownType());
}
- @Deprecated // since 2.5; remove from 2.7
+ @Deprecated // since 2.5; remove from 2.9
public JsonDeserializer<?> handleSecondaryContextualization(JsonDeserializer<?> deser, BeanProperty prop) throws JsonMappingException {
if (deser instanceof ContextualDeserializer) {
deser = ((ContextualDeserializer) deser).createContextual(this, prop);
@@ -769,7 +765,7 @@
public <T> T readValue(JsonParser p, JavaType type) throws IOException {
JsonDeserializer<Object> deser = findRootValueDeserializer(type);
if (deser == null) {
- throw mappingException("Could not find JsonDeserializer for type %s", type);
+ reportMappingException("Could not find JsonDeserializer for type %s", type);
}
return (T) deser.deserialize(p, this);
}
@@ -794,7 +790,8 @@
JsonDeserializer<Object> deser = findContextualValueDeserializer(type, prop);
if (deser == null) {
String propName = (prop == null) ? "NULL" : ("'"+prop.getName()+"'");
- throw mappingException("Could not find JsonDeserializer for type %s (via property %s)",
+ reportMappingException(
+ "Could not find JsonDeserializer for type %s (via property %s)",
type, propName);
}
return (T) deser.deserialize(p, this);
@@ -802,43 +799,416 @@
/*
/**********************************************************
- /* Methods for problem handling, reporting
+ /* Methods for problem handling
/**********************************************************
*/
/**
- * Method deserializers can call to inform configured {@link DeserializationProblemHandler}s
- * of an unrecognized property.
+ * Method that deserializers should call if they encounter an unrecognized
+ * property (and once that is not explicitly designed as ignorable), to
+ * inform possibly configured {@link DeserializationProblemHandler}s and
+ * let it handle the problem.
*
* @return True if there was a configured problem handler that was able to handle the
* problem
*/
public boolean handleUnknownProperty(JsonParser p, JsonDeserializer<?> deser,
Object instanceOrClass, String propName)
- throws IOException, JsonProcessingException
+ throws IOException
{
LinkedNode<DeserializationProblemHandler> h = _config.getProblemHandlers();
- if (h != null) {
- while (h != null) {
- // Can bail out if it's handled
- if (h.value().handleUnknownProperty(this, p, deser, instanceOrClass, propName)) {
- return true;
- }
- h = h.next();
+ while (h != null) {
+ // Can bail out if it's handled
+ if (h.value().handleUnknownProperty(this, p, deser, instanceOrClass, propName)) {
+ return true;
}
+ h = h.next();
}
- return false;
+ // Nope, not handled. Potentially that's a problem...
+ if (!isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)) {
+ p.skipChildren();
+ return true;
+ }
+ // Do we know properties that are expected instead?
+ Collection<Object> propIds = (deser == null) ? null : deser.getKnownPropertyNames();
+ throw UnrecognizedPropertyException.from(_parser,
+ instanceOrClass, propName, propIds);
}
/**
- * Helper method for reporting a problem with unhandled unknown exception
+ * Method that deserializers should call if they encounter a String value
+ * that can not be converted to expected key of a {@link java.util.Map}
+ * valued property.
+ * Default implementation will try to call {@link DeserializationProblemHandler#handleWeirdNumberValue}
+ * on configured handlers, if any, to allow for recovery; if recovery does not
+ * succeed, will throw {@link InvalidFormatException} with given message.
+ *
+ * @param keyClass Expected type for key
+ * @param keyValue String value from which to deserialize key
+ * @param msg Error message template caller wants to use if exception is to be thrown
+ * @param msgArgs Optional arguments to use for message, if any
+ *
+ * @return Key value to use
+ *
+ * @throws IOException To indicate unrecoverable problem, usually based on <code>msg</code>
+ *
+ * @since 2.8
+ */
+ public Object handleWeirdKey(Class<?> keyClass, String keyValue,
+ String msg, Object... msgArgs)
+ throws IOException
+ {
+ // but if not handled, just throw exception
+ if (msgArgs.length > 0) {
+ msg = String.format(msg, msgArgs);
+ }
+ LinkedNode<DeserializationProblemHandler> h = _config.getProblemHandlers();
+ while (h != null) {
+ // Can bail out if it's handled
+ Object key = h.value().handleWeirdKey(this, keyClass, keyValue, msg);
+ if (key != DeserializationProblemHandler.NOT_HANDLED) {
+ // Sanity check for broken handlers, otherwise nasty to debug:
+ if ((key == null) || keyClass.isInstance(key)) {
+ return key;
+ }
+ throw weirdStringException(keyValue, keyClass, String.format(
+ "DeserializationProblemHandler.handleWeirdStringValue() for type %s returned value of type %s",
+ keyClass, key.getClass()));
+ }
+ h = h.next();
+ }
+ throw weirdKeyException(keyClass, keyValue, msg);
+ }
+
+ /**
+ * Method that deserializers should call if they encounter a String value
+ * that can not be converted to target property type, in cases where some
+ * String values could be acceptable (either with different settings,
+ * or different value).
+ * Default implementation will try to call {@link DeserializationProblemHandler#handleWeirdStringValue}
+ * on configured handlers, if any, to allow for recovery; if recovery does not
+ * succeed, will throw {@link InvalidFormatException} with given message.
+ *
+ * @param targetClass Type of property into which incoming number should be converted
+ * @param value String value from which to deserialize property value
+ * @param msg Error message template caller wants to use if exception is to be thrown
+ * @param msgArgs Optional arguments to use for message, if any
+ *
+ * @return Property value to use
+ *
+ * @throws IOException To indicate unrecoverable problem, usually based on <code>msg</code>
+ *
+ * @since 2.8
+ */
+ public Object handleWeirdStringValue(Class<?> targetClass, String value,
+ String msg, Object... msgArgs)
+ throws IOException
+ {
+ // but if not handled, just throw exception
+ if (msgArgs.length > 0) {
+ msg = String.format(msg, msgArgs);
+ }
+ LinkedNode<DeserializationProblemHandler> h = _config.getProblemHandlers();
+ while (h != null) {
+ // Can bail out if it's handled
+ Object instance = h.value().handleWeirdStringValue(this, targetClass, value, msg);
+ if (instance != DeserializationProblemHandler.NOT_HANDLED) {
+ // Sanity check for broken handlers, otherwise nasty to debug:
+ if ((instance == null) || targetClass.isInstance(instance)) {
+ return instance;
+ }
+ throw weirdStringException(value, targetClass, String.format(
+ "DeserializationProblemHandler.handleWeirdStringValue() for type %s returned value of type %s",
+ targetClass, instance.getClass()));
+ }
+ h = h.next();
+ }
+ throw weirdStringException(value, targetClass, msg);
+ }
+
+ /**
+ * Method that deserializers should call if they encounter a numeric value
+ * that can not be converted to target property type, in cases where some
+ * numeric values could be acceptable (either with different settings,
+ * or different numeric value).
+ * Default implementation will try to call {@link DeserializationProblemHandler#handleWeirdNumberValue}
+ * on configured handlers, if any, to allow for recovery; if recovery does not
+ * succeed, will throw {@link InvalidFormatException} with given message.
+ *
+ * @param targetClass Type of property into which incoming number should be converted
+ * @param value Number value from which to deserialize property value
+ * @param msg Error message template caller wants to use if exception is to be thrown
+ * @param msgArgs Optional arguments to use for message, if any
+ *
+ * @return Property value to use
+ *
+ * @throws IOException To indicate unrecoverable problem, usually based on <code>msg</code>
+ *
+ * @since 2.8
+ */
+ public Object handleWeirdNumberValue(Class<?> targetClass, Number value,
+ String msg, Object... msgArgs)
+ throws IOException
+ {
+ if (msgArgs.length > 0) {
+ msg = String.format(msg, msgArgs);
+ }
+ LinkedNode<DeserializationProblemHandler> h = _config.getProblemHandlers();
+ while (h != null) {
+ // Can bail out if it's handled
+ Object key = h.value().handleWeirdNumberValue(this, targetClass, value, msg);
+ if (key != DeserializationProblemHandler.NOT_HANDLED) {
+ // Sanity check for broken handlers, otherwise nasty to debug:
+ if ((key == null) || targetClass.isInstance(key)) {
+ return key;
+ }
+ throw weirdNumberException(value, targetClass, String.format(
+ "DeserializationProblemHandler.handleWeirdNumberValue() for type %s returned value of type %s",
+ targetClass, key.getClass()));
+ }
+ h = h.next();
+ }
+ throw weirdNumberException(value, targetClass, msg);
+ }
+
+ /**
+ * Method that deserializers should call if they fail to instantiate value
+ * due to lack of viable instantiator (usually creator, that is, constructor
+ * or static factory method). Method should be called at point where value
+ * has not been decoded, so that handler has a chance to handle decoding
+ * using alternate mechanism, and handle underlying content (possibly by
+ * just skipping it) to keep input state valid
+ *
+ * @param instClass 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.8
+ */
+ public Object handleMissingInstantiator(Class<?> instClass, JsonParser p,
+ String msg, Object... msgArgs)
+ throws IOException
+ {
+ if (msgArgs.length > 0) {
+ msg = String.format(msg, msgArgs);
+ }
+ LinkedNode<DeserializationProblemHandler> h = _config.getProblemHandlers();
+ while (h != null) {
+ // Can bail out if it's handled
+ Object instance = h.value().handleMissingInstantiator(this,
+ instClass, p, msg);
+ if (instance != DeserializationProblemHandler.NOT_HANDLED) {
+ // Sanity check for broken handlers, otherwise nasty to debug:
+ if ((instance == null) || instClass.isInstance(instance)) {
+ return instance;
+ }
+ throw instantiationException(instClass, String.format(
+ "DeserializationProblemHandler.handleMissingInstantiator() for type %s returned value of type %s",
+ instClass, instance.getClass()));
+ }
+ h = h.next();
+ }
+ throw instantiationException(instClass, msg);
+ }
+
+ /**
+ * Method that deserializers should call if they fail to instantiate value
+ * due to an exception that was thrown by constructor (or other mechanism used
+ * to create instances).
+ * Default implementation will try to call {@link DeserializationProblemHandler#handleInstantiationProblem}
+ * on configured handlers, if any, to allow for recovery; if recovery does not
+ * succeed, will throw exception constructed with {@link #instantiationException}.
+ *
+ * @param instClass Type that was to be instantiated
+ * @param argument (optional) Argument that was passed to constructor or equivalent
+ * instantiator; often a {@link java.lang.String}.
+ * @param t Exception that caused failure
+ *
+ * @return Object that should be constructed, if any; has to be of type <code>instClass</code>
+ *
+ * @since 2.8
+ */
+ public Object handleInstantiationProblem(Class<?> instClass, Object argument,
+ Throwable t)
+ throws IOException
+ {
+ LinkedNode<DeserializationProblemHandler> h = _config.getProblemHandlers();
+ while (h != null) {
+ // Can bail out if it's handled
+ Object instance = h.value().handleInstantiationProblem(this, instClass, argument, t);
+ if (instance != DeserializationProblemHandler.NOT_HANDLED) {
+ // Sanity check for broken handlers, otherwise nasty to debug:
+ if ((instance == null) || instClass.isInstance(instance)) {
+ return instance;
+ }
+ throw instantiationException(instClass, String.format(
+ "DeserializationProblemHandler.handleInstantiationProblem() for type %s returned value of type %s",
+ instClass, instance.getClass()));
+ }
+ h = h.next();
+ }
+ // 18-May-2016, tatu: Only wrap if not already a valid type to throw
+ if (t instanceof IOException) {
+ throw (IOException) t;
+ }
+ throw instantiationException(instClass, t);
+ }
+
+ /**
+ * 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
+ * can not 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 instClass 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.8
+ */
+ public Object handleUnexpectedToken(Class<?> instClass, JsonParser p)
+ throws IOException
+ {
+ return handleUnexpectedToken(instClass, 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
+ * can not 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 instClass 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.8
+ */
+ public Object handleUnexpectedToken(Class<?> instClass, JsonToken t,
+ JsonParser p,
+ String msg, Object... msgArgs)
+ throws IOException
+ {
+ if (msgArgs.length > 0) {
+ msg = String.format(msg, msgArgs);
+ }
+ LinkedNode<DeserializationProblemHandler> h = _config.getProblemHandlers();
+ while (h != null) {
+ Object instance = h.value().handleUnexpectedToken(this,
+ instClass, t, p, msg);
+ if (instance != DeserializationProblemHandler.NOT_HANDLED) {
+ if ((instance == null) || instClass.isInstance(instance)) {
+ return instance;
+ }
+ reportMappingException("DeserializationProblemHandler.handleUnexpectedToken() for type %s returned value of type %s",
+ instClass, instance.getClass());
+ }
+ h = h.next();
+ }
+ if (msg == null) {
+ if (t == null) {
+ msg = String.format("Unexpected end-of-input when binding data into %s",
+ _calcName(instClass));
+ } else {
+ msg = String.format("Can not deserialize instance of %s out of %s token",
+ _calcName(instClass), t);
+ }
+ }
+ reportMappingException(msg);
+ return null; // never gets here
+ }
+
+ /**
+ * Method that deserializers should call if they encounter a type id
+ * (for polymorphic deserialization) that can not be resolved to an
+ * actual type; usually since there is no mapping defined.
+ * Default implementation will try to call {@link DeserializationProblemHandler#handleUnknownTypeId}
+ * on configured handlers, if any, to allow for recovery; if recovery does not
+ * succeed, will throw exception constructed with {@link #unknownTypeIdException}.
+ *
+ * @param baseType Base type from which resolution starts
+ * @param id Type id that could not be converted
+ * @param extraDesc Additional problem description to add to default exception message,
+ * if resolution fails.
+ *
+ * @return {@link JavaType} that id resolves to
+ *
+ * @throws IOException To indicate unrecoverable problem, if resolution can not
+ * be made to work
+ *
+ * @since 2.8
+ */
+ public JavaType handleUnknownTypeId(JavaType baseType, String id,
+ TypeIdResolver idResolver, String extraDesc) throws IOException
+ {
+ LinkedNode<DeserializationProblemHandler> h = _config.getProblemHandlers();
+ while (h != null) {
+ // Can bail out if it's handled
+ JavaType type = h.value().handleUnknownTypeId(this, baseType, id, idResolver, extraDesc);
+ if (type != null) {
+ if (type.hasRawClass(Void.class)) {
+ return null;
+ }
+ // But ensure there's type compatibility
+ if (type.isTypeOrSubTypeOf(baseType.getRawClass())) {
+ return type;
+ }
+ throw unknownTypeIdException(baseType, id,
+ "problem handler tried to resolve into non-subtype: "+type);
+ }
+ h = h.next();
+ }
+ // 24-May-2016, tatu: Actually we may still not want to fail quite yet
+ if (!isEnabled(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE)) {
+ return null;
+ }
+ throw unknownTypeIdException(baseType, id, extraDesc);
+ }
+
+ /*
+ /**********************************************************
+ /* Methods for problem reporting, in cases where recovery
+ /* is not considered possible
+ /**********************************************************
+ */
+
+ /**
+ * Method for deserializers to call
+ * when the token encountered was of type different than what <b>should</b>
+ * be seen at that position, usually within a sequence of expected tokens.
+ * Note that this method will throw a {@link JsonMappingException} and no
+ * recovery is attempted (via {@link DeserializationProblemHandler}, as
+ * problem is considered to be difficult to recover from, in general.
+ *
+ * @since 2.8
+ */
+ public void reportWrongTokenException(JsonParser p,
+ JsonToken expToken, String msg, Object... msgArgs)
+ throws JsonMappingException
+ {
+ if ((msg != null) && (msgArgs.length > 0)) {
+ msg = String.format(msg, msgArgs);
+ }
+ throw wrongTokenException(p, expToken, msg);
+ }
+
+ /**
+ * Helper method for reporting a problem with unhandled unknown property.
*
* @param instanceOrClass Either value being populated (if one has been
* instantiated), or Class that indicates type that would be (or
* have been) instantiated
* @param deser Deserializer that had the problem, if called by deserializer
* (or on behalf of one)
+ *
+ * @deprecated Since 2.8 call {@link #handleUnknownProperty} instead
*/
+ @Deprecated
public void reportUnknownProperty(Object instanceOrClass, String fieldName,
JsonDeserializer<?> deser)
throws JsonMappingException
@@ -855,128 +1225,89 @@
/**
* @since 2.8
*/
- public JsonMappingException reportMappingException(String msg, Object... msgArgs)
+ public void reportMappingException(String msg, Object... msgArgs)
throws JsonMappingException
{
if (msgArgs.length > 0) {
msg = String.format(msg, msgArgs);
}
- throw mappingException(msg);
+ throw JsonMappingException.from(getParser(), msg);
}
/**
* @since 2.8
*/
- public JsonMappingException reportInstantiationException(Class<?> instClass, Throwable t)
+ public void reportMissingContent(String msg, Object... msgArgs)
throws JsonMappingException
{
- throw instantiationException(instClass, t);
- }
-
- /**
- * @since 2.8
- */
- public JsonMappingException reportInstantiationException(Class<?> instClass,
- String msg, Object... msgArgs)
- throws JsonMappingException
- {
- if (msgArgs.length > 0) {
+ if (msg == null) {
+ msg = "No content to map due to end-of-input";
+ } else if (msgArgs.length > 0) {
msg = String.format(msg, msgArgs);
}
- throw instantiationException(instClass, msg);
+ throw JsonMappingException.from(getParser(), msg);
}
/**
* @since 2.8
*/
- public <T> T reportWeirdStringException(String value, Class<?> instClass,
- String msg, Object... msgArgs)
+ public void reportUnresolvedObjectId(ObjectIdReader oidReader, Object bean)
throws JsonMappingException
{
- if (msgArgs.length > 0) {
- msg = String.format(msg, msgArgs);
+ String msg = String.format("No Object Id found for an instance of %s, to assign to property '%s'",
+ bean.getClass().getName(), oidReader.propertyName);
+ throw JsonMappingException.from(getParser(), msg);
+ }
+
+ /**
+ * Helper method called to indicate problem in POJO (serialization) definitions or settings
+ * regarding specific Java type, unrelated to actual JSON content to map.
+ * Default behavior is to construct and throw a {@link JsonMappingException}.
+ *
+ * @since 2.9
+ */
+ public <T> T reportBadTypeDefinition(BeanDescription bean,
+ String message, Object... args) throws JsonMappingException {
+ if (args != null && args.length > 0) {
+ message = String.format(message, args);
}
- throw weirdStringException(value, instClass, msg);
+ String beanDesc = (bean == null) ? "N/A" : _desc(bean.getType().getGenericSignature());
+ throw mappingException("Invalid type definition for type %s: %s",
+ beanDesc, message);
}
/**
- * @since 2.8
+ * Helper method called to indicate problem in POJO (serialization) definitions or settings
+ * regarding specific property (of a type), unrelated to actual JSON content to map.
+ * Default behavior is to construct and throw a {@link JsonMappingException}.
+ *
+ * @since 2.9
*/
- public <T> T reportWeirdNumberException(Number value, Class<?> instClass,
- String msg, Object... msgArgs)
- throws JsonMappingException
- {
- if (msgArgs.length > 0) {
- msg = String.format(msg, msgArgs);
+ public <T> T reportBadPropertyDefinition(BeanDescription bean, BeanPropertyDefinition prop,
+ String message, Object... args) throws JsonMappingException {
+ if (args != null && args.length > 0) {
+ message = String.format(message, args);
}
- throw weirdNumberException(value, instClass, msg);
- }
-
- /**
- * @since 2.8
- */
- public <T> T reportWeirdKeyException(Class<?> keyClass, String keyValue,
- String msg, Object... msgArgs)
- throws JsonMappingException
- {
- if (msgArgs.length > 0) {
- msg = String.format(msg, msgArgs);
- }
- throw weirdKeyException(keyClass, keyValue, msg);
- }
-
- /**
- * @since 2.8
- */
- public <T> T reportWrongTokenException(JsonParser p,
- JsonToken expToken, String msg, Object... msgArgs)
- throws JsonMappingException
- {
- if (msgArgs.length > 0) {
- msg = String.format(msg, msgArgs);
- }
- throw wrongTokenException(p, expToken, msg);
- }
-
- /**
- * @since 2.8
- */
- public <T> T reportUnknownTypeException(JavaType type, String id,
- String extraDesc) throws JsonMappingException
- {
- throw unknownTypeException(type, id, extraDesc);
- }
-
- /**
- * @since 2.8
- */
- public <T> T reportEndOfInputException(Class<?> instClass) throws JsonMappingException {
- throw endOfInputException(instClass);
+ String propName = (prop == null) ? "N/A" : _quotedString(prop.getName());
+ String beanDesc = (bean == null) ? "N/A" : _desc(bean.getType().getGenericSignature());
+ throw mappingException("Invalid definition for property %s (of type %s): %s",
+ propName, beanDesc, message);
}
/*
/**********************************************************
- /* Methods for constructing exceptions
+ /* Methods for constructing exceptions, "untyped"
/**********************************************************
*/
-
- /**
- * Helper method for constructing generic mapping exception for specified type
- */
- public JsonMappingException mappingException(Class<?> targetClass) {
- return mappingException(targetClass, _parser.getCurrentToken());
- }
- public JsonMappingException mappingException(Class<?> targetClass, JsonToken token) {
- String tokenDesc = (token == null) ? "<end of input>" : String.format("%s token", token);
- return JsonMappingException.from(_parser,
- String.format("Can not deserialize instance of %s out of %s",
- _calcName(targetClass), tokenDesc));
- }
-
/**
* Helper method for constructing generic mapping exception with specified
- * message and current location information
+ * message and current location information.
+ * Note that application code should almost always call
+ * one of <code>handleXxx</code> methods, or {@link #reportMappingException(String, Object...)}
+ * instead.
+ *
+ * @since 2.6
*/
public JsonMappingException mappingException(String message) {
return JsonMappingException.from(getParser(), message);
@@ -985,74 +1316,58 @@
/**
* Helper method for constructing generic mapping exception with specified
* message and current location information
+ * Note that application code should almost always call
+ * one of <code>handleXxx</code> methods, or {@link #reportMappingException(String, Object...)}
+ * instead.
*
* @since 2.6
*/
public JsonMappingException mappingException(String msgTemplate, Object... args) {
- String message = String.format(msgTemplate, args);
- return JsonMappingException.from(getParser(), message);
- }
-
- /**
- * Helper method for constructing instantiation exception for specified type,
- * to indicate problem with physically constructing instance of
- * specified class (missing constructor, exception from constructor)
- */
- public JsonMappingException instantiationException(Class<?> instClass, Throwable t) {
- return JsonMappingException.from(_parser,
- String.format("Can not construct instance of %s, problem: %s", instClass.getName(), t.getMessage()), t);
- }
-
- public JsonMappingException instantiationException(Class<?> instClass, String msg) {
- return JsonMappingException.from(_parser,
- String.format("Can not construct instance of %s, problem: %s", instClass.getName(), msg));
+ if (args != null && args.length > 0) {
+ msgTemplate = String.format(msgTemplate, args);
+ }
+ return JsonMappingException.from(getParser(), msgTemplate);
}
/**
- * Method that will construct an exception suitable for throwing when
- * some String values are acceptable, but the one encountered is not.
+ * Helper method for constructing generic mapping exception for specified type
*
- * @param value String value from input being deserialized
- * @param instClass Type that String should be deserialized into
- * @param msg Message that describes specific problem
- *
- * @since 2.1
+ * @deprecated Since 2.8 use {@link #handleUnexpectedToken(Class, JsonParser)} instead
*/
- public JsonMappingException weirdStringException(String value, Class<?> instClass, String msg) {
- return InvalidFormatException.from(_parser,
- String.format("Can not construct instance of %s from String value (%s): %s",
- instClass.getName(), _quotedString(value), msg),
- value, instClass);
+ @Deprecated
+ public JsonMappingException mappingException(Class<?> targetClass) {
+ return mappingException(targetClass, _parser.getCurrentToken());
}
/**
- * Helper method for constructing exception to indicate that input JSON
- * Number was not suitable for deserializing into given target type.
+ * @deprecated Since 2.8 use {@link #handleUnexpectedToken(Class, JsonParser)} instead
*/
- public JsonMappingException weirdNumberException(Number value, Class<?> instClass, String msg) {
- return InvalidFormatException.from(_parser,
- String.format("Can not construct instance of %s from number value (%s): %s",
- instClass.getName(), String.valueOf(value), msg),
- value, instClass);
+ @Deprecated
+ public JsonMappingException mappingException(Class<?> targetClass, JsonToken token) {
+ String tokenDesc = (token == null) ? "<end of input>" : String.format("%s token", token);
+ return JsonMappingException.from(_parser,
+ String.format("Can not deserialize instance of %s out of %s",
+ _calcName(targetClass), tokenDesc));
}
- /**
- * Helper method for constructing exception to indicate that given JSON
- * Object field name was not in format to be able to deserialize specified
- * key type.
+ /*
+ /**********************************************************
+ /* Methods for constructing semantic exceptions; usually not
+ /* to be called directly, call `handleXxx()` instead
+ /**********************************************************
*/
- public JsonMappingException weirdKeyException(Class<?> keyClass, String keyValue, String msg) {
- return InvalidFormatException.from(_parser,
- String.format("Can not construct Map key of type %s from String (%s): %s",
- keyClass.getName(), _quotedString(keyValue), msg),
- keyValue, keyClass);
- }
/**
- * Helper method for indicating that the current token was expected to be another
- * token.
+ * Helper method for constructing {@link JsonMappingException} to indicate
+ * that the token encountered was of type different than what <b>should</b>
+ * be seen at that position, usually within a sequence of expected tokens.
+ * Note that most of the time this method should NOT be directly called;
+ * instead, {@link #reportWrongTokenException} should be called and will
+ * call this method as necessary.
*/
- public JsonMappingException wrongTokenException(JsonParser p, JsonToken expToken, String msg0) {
+ public JsonMappingException wrongTokenException(JsonParser p, JsonToken expToken,
+ String msg0)
+ {
String msg = String.format("Unexpected token (%s), expected %s",
p.getCurrentToken(), expToken);
if (msg0 != null) {
@@ -1062,17 +1377,118 @@
}
/**
- * Helper method for constructing exception to indicate that given
- * type id (parsed from JSON) could not be converted to a Java type.
+ * Helper method for constructing exception to indicate that given JSON
+ * Object field name was not in format to be able to deserialize specified
+ * key type.
+ * Note that most of the time this method should NOT be called; instead,
+ * {@link #handleWeirdKey} should be called which will call this method
+ * if necessary.
*/
- @Deprecated // since 2.5, use overloaded variant
- public JsonMappingException unknownTypeException(JavaType type, String id) {
- return JsonMappingException.from(_parser, "Could not resolve type id '"+id+"' into a subtype of "+type);
+ public JsonMappingException weirdKeyException(Class<?> keyClass, String keyValue,
+ String msg) {
+ return InvalidFormatException.from(_parser,
+ String.format("Can not deserialize Map key of type %s from String %s: %s",
+ keyClass.getName(), _quotedString(keyValue), msg),
+ keyValue, keyClass);
}
/**
- * @since 2.5
+ * Helper method for constructing exception to indicate that input JSON
+ * String was not suitable for deserializing into given target type.
+ * Note that most of the time this method should NOT be called; instead,
+ * {@link #handleWeirdStringValue} should be called which will call this method
+ * if necessary.
+ *
+ * @param value String value from input being deserialized
+ * @param instClass Type that String should be deserialized into
+ * @param msg Message that describes specific problem
+ *
+ * @since 2.1
*/
+ public JsonMappingException weirdStringException(String value, Class<?> instClass,
+ String msg) {
+ return InvalidFormatException.from(_parser,
+ String.format("Can not deserialize value of type %s from String %s: %s",
+ instClass.getName(), _quotedString(value), msg),
+ value, instClass);
+ }
+
+ /**
+ * Helper method for constructing exception to indicate that input JSON
+ * Number was not suitable for deserializing into given target type.
+ * Note that most of the time this method should NOT be called; instead,
+ * {@link #handleWeirdNumberValue} should be called which will call this method
+ * if necessary.
+ */
+ public JsonMappingException weirdNumberException(Number value, Class<?> instClass,
+ String msg) {
+ return InvalidFormatException.from(_parser,
+ String.format("Can not deserialize value of type %s from number %s: %s",
+ instClass.getName(), String.valueOf(value), msg),
+ value, instClass);
+ }
+
+ /**
+ * Helper method for constructing instantiation exception for specified type,
+ * to indicate problem with physically constructing instance of
+ * specified class (missing constructor, exception from constructor)
+ *<p>
+ * Note that most of the time this method should NOT be called; instead,
+ * {@link #handleInstantiationProblem} should be called which will call this method
+ * if necessary.
+ */
+ public JsonMappingException instantiationException(Class<?> instClass, Throwable t) {
+ return JsonMappingException.from(_parser,
+ String.format("Can not construct instance of %s, problem: %s",
+ instClass.getName(), t.getMessage()), t);
+ }
+
+ /**
+ * Helper method for constructing instantiation exception for specified type,
+ * to indicate that instantiation failed due to missing instantiator
+ * (creator; constructor or factory method).
+ *<p>
+ * Note that most of the time this method should NOT be called; instead,
+ * {@link #handleMissingInstantiator} should be called which will call this method
+ * if necessary.
+ */
+ public JsonMappingException instantiationException(Class<?> instClass, String msg) {
+ return JsonMappingException.from(_parser,
+ String.format("Can not construct instance of %s: %s",
+ instClass.getName(), msg));
+ }
+
+ /**
+ * Helper method for constructing exception to indicate that given type id
+ * could not be resolved to a valid subtype of specified base type, during
+ * polymorphic deserialization.
+ *<p>
+ * Note that most of the time this method should NOT be called; instead,
+ * {@link #handleUnknownTypeId} should be called which will call this method
+ * if necessary.
+ */
+ public JsonMappingException unknownTypeIdException(JavaType baseType, String typeId,
+ String extraDesc) {
+ String msg = String.format("Could not resolve type id '%s' into a subtype of %s",
+ typeId, baseType);
+ if (extraDesc != null) {
+ msg = msg + ": "+extraDesc;
+ }
+ return InvalidTypeIdException.from(_parser, msg, baseType, typeId);
+ }
+
+ /*
+ /**********************************************************
+ /* Deprecated exception factory methods
+ /**********************************************************
+ */
+
+ /**
+ * @since 2.5
+ *
+ * @deprecated Since 2.8 use {@link #handleUnknownTypeId} instead
+ */
+ @Deprecated
public JsonMappingException unknownTypeException(JavaType type, String id,
String extraDesc) {
String msg = String.format("Could not resolve type id '%s' into a subtype of %s",
@@ -1083,6 +1499,13 @@
return JsonMappingException.from(_parser, msg);
}
+ /**
+ * Helper method for constructing exception to indicate that end-of-input was
+ * reached while still expecting more tokens to deserialize value of specified type.
+ *
+ * @deprecated Since 2.8; currently no way to catch EOF at databind level
+ */
+ @Deprecated
public JsonMappingException endOfInputException(Class<?> instClass) {
return JsonMappingException.from(_parser, "Unexpected end-of-input when trying to deserialize a "
+instClass.getName());
@@ -1090,7 +1513,7 @@
/*
/**********************************************************
- /* Overridable internal methods
+ /* Other internal methods
/**********************************************************
*/
@@ -1112,12 +1535,6 @@
protected String determineClassName(Object instance) {
return ClassUtil.getClassDescription(instance);
}
-
- /*
- /**********************************************************
- /* Other internal methods
- /**********************************************************
- */
protected String _calcName(Class<?> cls) {
if (cls.isArray()) {
diff --git a/src/main/java/com/fasterxml/jackson/databind/DeserializationFeature.java b/src/main/java/com/fasterxml/jackson/databind/DeserializationFeature.java
index 53da367..1505419 100644
--- a/src/main/java/com/fasterxml/jackson/databind/DeserializationFeature.java
+++ b/src/main/java/com/fasterxml/jackson/databind/DeserializationFeature.java
@@ -230,7 +230,34 @@
* @since 2.6
*/
FAIL_ON_MISSING_CREATOR_PROPERTIES(false),
-
+
+ /**
+ * Feature that determines what happens if one or more Creator properties (properties
+ * bound to parameters of Creator method (constructor or static factory method))
+ * are bound to null values - either from the JSON or as a default value. This
+ * is useful if you want to avoid nulls in your codebase, and particularly useful
+ * if you are using Java or Scala optionals for non-mandatory fields.
+ * Feature is disabled by default, so that no exception is thrown for missing creator
+ * property values, unless they are explicitly marked as `required`.
+ *
+ * @since 2.8
+ */
+ FAIL_ON_NULL_CREATOR_PROPERTIES(false),
+
+ /**
+ * Feature that determines what happens when a property annotated with
+ * {@link com.fasterxml.jackson.annotation.JsonTypeInfo.As#EXTERNAL_PROPERTY} is missing,
+ * but associated type id is available. If enabled, {@link JsonMappingException} is always
+ * thrown when property value is missing (if type id does exist);
+ * if disabled, exception is only thrown if property is marked as `required`.
+ *<p>
+ * Feature is enabled by default, so that exception is thrown when a subtype property is
+ * missing.
+ *
+ * @since 2.9
+ */
+ FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY(true),
+
/**
* Feature that determines whether Jackson code should catch
* and wrap {@link Exception}s (but never {@link Error}s!)
@@ -354,6 +381,18 @@
READ_UNKNOWN_ENUM_VALUES_AS_NULL(false),
/**
+ * Feature that allows unknown Enum values to be ignored and a predefined value specified through
+ * {@link com.fasterxml.jackson.annotation.JsonEnumDefaultValue @JsonEnumDefaultValue} annotation.
+ * If disabled, unknown Enum values will throw exceptions.
+ * If enabled, but no predefined default Enum value is specified, an exception will be thrown as well.
+ *<p>
+ * Feature is disabled by default.
+ *
+ * @since 2.8
+ */
+ READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE(false),
+
+ /**
* Feature that controls whether numeric timestamp values are expected
* to be written using nanosecond timestamps (enabled) or not (disabled),
* <b>if and only if</b> datatype supports such resolution.
@@ -377,6 +416,13 @@
* If enabled, contextual <code>TimeZone</code> will essentially override any other
* TimeZone information; if disabled, it will only be used if value itself does not
* contain any TimeZone information.
+ *<p>
+ * Note that exact behavior depends on date/time types in question; and specifically
+ * JDK type of {@link java.util.Date} does NOT have in-built timezone information
+ * so this setting has no effect.
+ *<p>
+ * As of Jackson 2.8, this feature is supported only by extension modules for Joda
+ * and Java 8 date/tyime datatypes.
*
* @since 2.2
*/
diff --git a/src/main/java/com/fasterxml/jackson/databind/JavaType.java b/src/main/java/com/fasterxml/jackson/databind/JavaType.java
index 752edd1..292cc39 100644
--- a/src/main/java/com/fasterxml/jackson/databind/JavaType.java
+++ b/src/main/java/com/fasterxml/jackson/databind/JavaType.java
@@ -131,6 +131,26 @@
public abstract JavaType withContentValueHandler(Object h);
/**
+ * Mutant factory method that will try to copy handlers that the specified
+ * source type instance had, if any; this must be done recursively where
+ * necessary (as content types may be structured).
+ *
+ * @since 2.8.4
+ */
+ public JavaType withHandlersFrom(JavaType src) {
+ JavaType type = this;
+ Object h = src.getTypeHandler();
+ if (h != _typeHandler) {
+ type = type.withTypeHandler(h);
+ }
+ h = src.getValueHandler();
+ if (h != _valueHandler) {
+ type = type.withValueHandler(h);
+ }
+ return type;
+ }
+
+ /**
* Mutant factory method that may be called on structured types
* that have a so-called content type (element of arrays, value type
* of Maps, referenced type of referential types),
@@ -223,6 +243,17 @@
public final boolean hasRawClass(Class<?> clz) { return _class == clz; }
/**
+ * Accessor that allows determining whether {@link #getContentType()} should
+ * return a non-null value (that is, there is a "content type") or not.
+ * True if {@link #isContainerType()} or {@link #isReferenceType()} return true.
+ *
+ * @since 2.8
+ */
+ public boolean hasContentType() {
+ return true;
+ }
+
+ /**
* @since 2.6
*/
public final boolean isTypeOrSubTypeOf(Class<?> clz) {
@@ -447,6 +478,18 @@
*/
public boolean hasValueHandler() { return _valueHandler != null; }
+ /**
+ * Helper method that checks whether this type, or its (optional) key
+ * or content type has {@link #getValueHandler} or {@link #getTypeHandler()};
+ * that is, are there any non-standard handlers associated with this
+ * type object.
+ *
+ * @since 2.8
+ */
+ public boolean hasHandlers() {
+ return (_typeHandler != null) || (_valueHandler != null);
+ }
+
/*
/**********************************************************
/* Support for producing signatures
diff --git a/src/main/java/com/fasterxml/jackson/databind/JsonMappingException.java b/src/main/java/com/fasterxml/jackson/databind/JsonMappingException.java
index d73b75c..5c7f742 100644
--- a/src/main/java/com/fasterxml/jackson/databind/JsonMappingException.java
+++ b/src/main/java/com/fasterxml/jackson/databind/JsonMappingException.java
@@ -45,14 +45,10 @@
*/
public static class Reference implements Serializable
{
- private static final long serialVersionUID = 1L;
+ private static final long serialVersionUID = 2L; // changes between 2.7 and 2.8
- /**
- * Object through which reference was resolved. Can be either
- * actual instance (usually the case for serialization), or
- * Class (usually the case for deserialization).
- */
- protected Object _from;
+ // transient since 2.8
+ protected transient Object _from;
/**
* Name of field (for beans) or key (for Maps) that is part
@@ -66,7 +62,7 @@
* 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".
+ * denote "not known" (or not relevant).
*/
protected int _index = -1;
@@ -76,9 +72,9 @@
* non-serializable (and has to be dropped) but we still want to pass
* actual description along.
*
- * @since 2.7.4
+ * @since 2.8
*/
- protected String _asString;
+ protected String _desc;
/**
* Default constructor for deserialization/sub-classing purposes
@@ -100,23 +96,28 @@
_index = index;
}
- private Reference(Reference src, String asString, Object newFrom) {
- _asString = asString;
- _from = newFrom;
- _fieldName = src._fieldName;
- _index = src._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; }
- public void setFrom(Object o) { _from = o; }
- public void setFieldName(String n) { _fieldName = n; }
- public void setIndex(int ix) { _index = ix; }
-
+ /**
+ * 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; }
-
- @Override public String toString() {
- if (_asString == null) {
+ public String getDescription() {
+ if (_desc == null) {
StringBuilder sb = new StringBuilder();
if (_from == null) { // can this ever occur?
@@ -145,22 +146,26 @@
sb.append('?');
}
sb.append(']');
- _asString = sb.toString();
+ _desc = sb.toString();
}
- return _asString;
+ return _desc;
+ }
+
+ @Override
+ public String toString() {
+ return getDescription();
}
/**
* May need some cleaning here, given that `from` may or may not be serializable.
*
- * since 2.7.4
+ * since 2.8
*/
Object writeReplace() {
- // as per [databind#1195], reference may cause trouble, if non-serializable
- // instance (either directly or transitively); and even use of Class would often
- // be problematic. Because of this, clear up `_from` always, but ensure that
- // description is preserved
- return new Reference(this, toString(), null);
+ // as per [databind#1195], need to ensure description is not null, since
+ // `_from` is transient
+ getDescription();
+ return this;
}
}
@@ -295,11 +300,7 @@
* @since 2.7
*/
public static JsonMappingException from(SerializerProvider ctxt, String msg) {
- /* 17-Aug-2015, tatu: As per [databind#903] this is bit problematic as
- * SerializerProvider instance does not currently hold on to generator...
- */
- JsonGenerator g = null;
- return new JsonMappingException(g, msg);
+ return new JsonMappingException(ctxt.getGenerator(), msg);
}
/**
@@ -309,8 +310,7 @@
/* 17-Aug-2015, tatu: As per [databind#903] this is bit problematic as
* SerializerProvider instance does not currently hold on to generator...
*/
- JsonGenerator g = null;
- return new JsonMappingException(g, msg, problem);
+ return new JsonMappingException(ctxt.getGenerator(), msg, problem);
}
/**
diff --git a/src/main/java/com/fasterxml/jackson/databind/JsonSerializer.java b/src/main/java/com/fasterxml/jackson/databind/JsonSerializer.java
index 88e5f50..3506cd2 100644
--- a/src/main/java/com/fasterxml/jackson/databind/JsonSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/JsonSerializer.java
@@ -157,16 +157,16 @@
if (clz == null) {
clz = value.getClass();
}
- throw serializers.mappingException("Type id handling not implemented for type %s (by serializer of type %s)",
+ serializers.reportMappingProblem("Type id handling not implemented for type %s (by serializer of type %s)",
clz.getName(), getClass().getName());
}
-
+
/*
/**********************************************************
/* Other accessors
/**********************************************************
*/
-
+
/**
* Method for accessing type of Objects this serializer can handle.
* Note that this information is not guaranteed to be exact -- it
@@ -187,10 +187,8 @@
*<p>
* Default implementation will consider only null values to be empty.
*
- * @since 2.0
- *
* @deprecated Since 2.5 Use {@link #isEmpty(SerializerProvider, Object)} instead;
- * will be removed from 2.8
+ * will be removed from 2.9
*/
@Deprecated
public boolean isEmpty(T value) {
@@ -204,7 +202,7 @@
*<p>
* Default implementation will consider only null values to be empty.
*<p>
- * NOTE: replaces {@link #isEmpty(Object)}, deprecated in 2.5
+ * NOTE: replaces {@link #isEmpty(Object)}, which was deprecated in 2.5
*
* @since 2.5
*/
diff --git a/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java b/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java
index 425f251..e163b5f 100644
--- a/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java
+++ b/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java
@@ -21,7 +21,7 @@
/* Introspection features
/******************************************************
*/
-
+
/**
* Feature that determines whether annotation introspection
* is used for configuration; if enabled, configured
@@ -220,11 +220,13 @@
ALLOW_FINAL_FIELDS_AS_MUTATORS(true),
/**
- * Feature that determines for <code>transient</code> modifier for fields
- * is handled: if disabled, it is only taken to mean exclusion of
+ * Feature that determines how <code>transient</code> modifier for fields
+ * is handled: if disabled, it is only taken to mean exclusion of the field
+ * as accessor; if true, removal of the whole property.
*<p>
* Feature is disabled by default, meaning that existence of `transient`
- * for a field does not necessarily lead to ignoral of getters or setters.
+ * for a field does not necessarily lead to ignoral of getters or setters
+ * but just ignoring the use of field for access.
*
* @since 2.6
*/
diff --git a/src/main/java/com/fasterxml/jackson/databind/Module.java b/src/main/java/com/fasterxml/jackson/databind/Module.java
index 37ca295..d7d0d01 100644
--- a/src/main/java/com/fasterxml/jackson/databind/Module.java
+++ b/src/main/java/com/fasterxml/jackson/databind/Module.java
@@ -1,6 +1,7 @@
package com.fasterxml.jackson.databind;
import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.cfg.MutableConfigOverride;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler;
import com.fasterxml.jackson.databind.deser.Deserializers;
@@ -143,6 +144,31 @@
public boolean isEnabled(JsonParser.Feature f);
public boolean isEnabled(JsonGenerator.Feature f);
+
+ /*
+ /**********************************************************
+ /* Mutant accessors
+ /**********************************************************
+ */
+
+ /**
+ * "Mutant accessor" for getting a mutable configuration override object for
+ * given type, needed to add or change per-type overrides applied
+ * to properties of given type.
+ * Usage is through returned object by colling "setter" methods, which
+ * directly modify override object and take effect directly.
+ * For example you can do
+ *<pre>
+ * mapper.configOverride(java.util.Date.class)
+ * .setFormat(JsonFormat.Value.forPattern("yyyy-MM-dd"));
+ *<pre>
+ * to change the default format to use for properties of type
+ * {@link java.util.Date} (possibly further overridden by per-property
+ * annotations)
+ *
+ * @since 2.8
+ */
+ public MutableConfigOverride configOverride(Class<?> type);
/*
/**********************************************************
diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java
index e75eaa2..8e91721 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java
@@ -19,6 +19,8 @@
import com.fasterxml.jackson.databind.cfg.ContextAttributes;
import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
+import com.fasterxml.jackson.databind.cfg.MutableConfigOverride;
+import com.fasterxml.jackson.databind.cfg.ConfigOverrides;
import com.fasterxml.jackson.databind.deser.*;
import com.fasterxml.jackson.databind.introspect.*;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
@@ -28,6 +30,7 @@
import com.fasterxml.jackson.databind.node.*;
import com.fasterxml.jackson.databind.ser.*;
import com.fasterxml.jackson.databind.type.*;
+import com.fasterxml.jackson.databind.util.ClassUtil;
import com.fasterxml.jackson.databind.util.RootNameLookup;
import com.fasterxml.jackson.databind.util.StdDateFormat;
import com.fasterxml.jackson.databind.util.TokenBuffer;
@@ -273,12 +276,6 @@
protected final static VisibilityChecker<?> STD_VISIBILITY_CHECKER = VisibilityChecker.Std.defaultInstance();
/**
- * @deprecated Since 2.6, do not use: will be removed in 2.7 or later
- */
- @Deprecated
- protected final static PrettyPrinter _defaultPrettyPrinter = new DefaultPrettyPrinter();
-
- /**
* Base settings contain defaults used for all {@link ObjectMapper}
* instances.
*/
@@ -288,7 +285,7 @@
STD_VISIBILITY_CHECKER, null, TypeFactory.defaultInstance(),
null, StdDateFormat.instance, null,
Locale.getDefault(),
- null, // to indicate "use default TimeZone"
+ null, // to indicate "use Jackson default TimeZone" (UTC since Jackson 2.7)
Base64Variants.getDefaultVariant() // 2.1
);
@@ -322,6 +319,14 @@
*/
protected SubtypeResolver _subtypeResolver;
+ /**
+ * Currently active per-type configuration overrides, accessed by
+ * declared type of property.
+ *
+ * @since 2.8
+ */
+ protected ConfigOverrides _propertyOverrides;
+
/*
/**********************************************************
/* Configuration settings: mix-in annotations
@@ -481,12 +486,12 @@
_subtypeResolver = src._subtypeResolver;
_typeFactory = src._typeFactory;
_injectableValues = src._injectableValues;
+ _propertyOverrides = src._propertyOverrides.copy();
+ _mixIns = src._mixIns.copy();
- SimpleMixInResolver mixins = src._mixIns.copy();
- _mixIns = mixins;
RootNameLookup rootNames = new RootNameLookup();
- _serializationConfig = new SerializationConfig(src._serializationConfig, mixins, rootNames);
- _deserializationConfig = new DeserializationConfig(src._deserializationConfig, mixins, rootNames);
+ _serializationConfig = new SerializationConfig(src._serializationConfig, _mixIns, rootNames, _propertyOverrides);
+ _deserializationConfig = new DeserializationConfig(src._deserializationConfig, _mixIns, rootNames, _propertyOverrides);
_serializerProvider = src._serializerProvider.copy();
_deserializationContext = src._deserializationContext.copy();
@@ -538,12 +543,13 @@
SimpleMixInResolver mixins = new SimpleMixInResolver(null);
_mixIns = mixins;
-
BaseSettings base = DEFAULT_BASE.withClassIntrospector(defaultClassIntrospector());
+ ConfigOverrides propOverrides = new ConfigOverrides();
+ _propertyOverrides = propOverrides;
_serializationConfig = new SerializationConfig(base,
- _subtypeResolver, mixins, rootNames);
+ _subtypeResolver, mixins, rootNames, propOverrides);
_deserializationConfig = new DeserializationConfig(base,
- _subtypeResolver, mixins, rootNames);
+ _subtypeResolver, mixins, rootNames, propOverrides);
// Some overrides we may need
final boolean needOrder = _jsonFactory.requiresPropertyOrdering();
@@ -772,14 +778,21 @@
public boolean isEnabled(JsonParser.Feature f) {
return mapper.isEnabled(f);
}
-
+
@Override
public boolean isEnabled(JsonGenerator.Feature f) {
return mapper.isEnabled(f);
}
-
+
+ // // // Mutant accessors
+
+ @Override
+ public MutableConfigOverride configOverride(Class<?> type) {
+ return mapper.configOverride(type);
+ }
+
// // // Methods for registering handlers: deserializers
-
+
@Override
public void addDeserializers(Deserializers d) {
DeserializerFactory df = mapper._deserializationContext._factory.withAdditionalDeserializers(d);
@@ -1464,6 +1477,33 @@
*/
/**
+ * Accessor for getting a mutable configuration override object for
+ * given type, needed to add or change per-type overrides applied
+ * to properties of given type.
+ * Usage is through returned object by colling "setter" methods, which
+ * directly modify override object and take effect directly.
+ * For example you can do
+ *<pre>
+ * mapper.configOverride(java.util.Date.class)
+ * .setFormat(JsonFormat.Value.forPattern("yyyy-MM-dd"));
+ *<pre>
+ * to change the default format to use for properties of type
+ * {@link java.util.Date} (possibly further overridden by per-property
+ * annotations)
+ *
+ * @since 2.8
+ */
+ public MutableConfigOverride configOverride(Class<?> type) {
+ return _propertyOverrides.findOrCreateOverride(type);
+ }
+
+ /*
+ /**********************************************************
+ /* Configuration, basic type handling
+ /**********************************************************
+ */
+
+ /**
* Accessor for getting currently configured {@link TypeFactory} instance.
*/
public TypeFactory getTypeFactory() {
@@ -1557,10 +1597,10 @@
* @since 2.4
*/
public ObjectMapper setConfig(DeserializationConfig config) {
- _deserializationConfig = config;
- return this;
+ _deserializationConfig = config;
+ return this;
}
-
+
/*
/**********************************************************
/* Configuration, serialization
@@ -1717,7 +1757,7 @@
/**
* Method for overriding default TimeZone to use for formatting.
- * Default value used is UTC (NOT local timezone).
+ * Default value used is UTC (NOT default TimeZone of JVM).
*/
public ObjectMapper setTimeZone(TimeZone tz) {
_deserializationConfig = _deserializationConfig.with(tz);
@@ -2044,10 +2084,10 @@
*/
@Override
@SuppressWarnings("unchecked")
- public <T> T readValue(JsonParser jp, Class<T> valueType)
+ public <T> T readValue(JsonParser p, Class<T> valueType)
throws IOException, JsonParseException, JsonMappingException
{
- return (T) _readValue(getDeserializationConfig(), jp, _typeFactory.constructType(valueType));
+ return (T) _readValue(getDeserializationConfig(), p, _typeFactory.constructType(valueType));
}
/**
@@ -2068,10 +2108,10 @@
*/
@Override
@SuppressWarnings("unchecked")
- public <T> T readValue(JsonParser jp, TypeReference<?> valueTypeRef)
+ public <T> T readValue(JsonParser p, TypeReference<?> valueTypeRef)
throws IOException, JsonParseException, JsonMappingException
{
- return (T) _readValue(getDeserializationConfig(), jp, _typeFactory.constructType(valueTypeRef));
+ return (T) _readValue(getDeserializationConfig(), p, _typeFactory.constructType(valueTypeRef));
}
/**
@@ -2091,10 +2131,10 @@
*/
@Override
@SuppressWarnings("unchecked")
- public final <T> T readValue(JsonParser jp, ResolvedType valueType)
+ public final <T> T readValue(JsonParser p, ResolvedType valueType)
throws IOException, JsonParseException, JsonMappingException
{
- return (T) _readValue(getDeserializationConfig(), jp, (JavaType) valueType);
+ return (T) _readValue(getDeserializationConfig(), p, (JavaType) valueType);
}
/**
@@ -2110,10 +2150,10 @@
* expected for result type (or has other mismatch issues)
*/
@SuppressWarnings("unchecked")
- public <T> T readValue(JsonParser jp, JavaType valueType)
+ public <T> T readValue(JsonParser p, JavaType valueType)
throws IOException, JsonParseException, JsonMappingException
{
- return (T) _readValue(getDeserializationConfig(), jp, valueType);
+ return (T) _readValue(getDeserializationConfig(), p, valueType);
}
/**
@@ -2137,7 +2177,7 @@
* of type {@link JsonParser} supports (JSON for default case)
*/
@Override
- public <T extends TreeNode> T readTree(JsonParser jp)
+ public <T extends TreeNode> T readTree(JsonParser p)
throws IOException, JsonProcessingException
{
/* 02-Mar-2009, tatu: One twist; deserialization provider
@@ -2148,14 +2188,14 @@
* calling readValue(), since that'll choke on it otherwise
*/
DeserializationConfig cfg = getDeserializationConfig();
- JsonToken t = jp.getCurrentToken();
+ JsonToken t = p.getCurrentToken();
if (t == null) {
- t = jp.nextToken();
+ t = p.nextToken();
if (t == null) {
return null;
}
}
- JsonNode n = (JsonNode) _readValue(cfg, jp, JSON_NODE_TYPE);
+ JsonNode n = (JsonNode) _readValue(cfg, p, JSON_NODE_TYPE);
if (n == null) {
n = getNodeFactory().nullNode();
}
@@ -2584,9 +2624,9 @@
JsonNode result;
try {
writeValue(buf, fromValue);
- JsonParser jp = buf.asParser();
- result = readTree(jp);
- jp.close();
+ JsonParser p = buf.asParser();
+ result = readTree(p);
+ p.close();
} catch (IOException e) { // should not occur, no real i/o...
throw new IllegalArgumentException(e.getMessage(), e);
}
@@ -2745,8 +2785,6 @@
public <T> T readValue(URL src, Class<T> valueType)
throws IOException, JsonParseException, JsonMappingException
{
- // !!! TODO
-// _setupClassLoaderForDeserialization(valueType);
return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueType));
}
@@ -2792,8 +2830,6 @@
public <T> T readValue(String content, Class<T> valueType)
throws IOException, JsonParseException, JsonMappingException
{
- // !!! TODO
-// _setupClassLoaderForDeserialization(valueType);
return (T) _readMapAndClose(_jsonFactory.createParser(content), _typeFactory.constructType(valueType));
}
@@ -2839,8 +2875,6 @@
public <T> T readValue(Reader src, Class<T> valueType)
throws IOException, JsonParseException, JsonMappingException
{
- // !!! TODO
-// _setupClassLoaderForDeserialization(valueType);
return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueType));
}
@@ -2862,8 +2896,6 @@
public <T> T readValue(InputStream src, Class<T> valueType)
throws IOException, JsonParseException, JsonMappingException
{
- // !!! TODO
-// _setupClassLoaderForDeserialization(valueType);
return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueType));
}
@@ -2885,8 +2917,6 @@
public <T> T readValue(byte[] src, Class<T> valueType)
throws IOException, JsonParseException, JsonMappingException
{
- // !!! TODO
-// _setupClassLoaderForDeserialization(valueType);
return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueType));
}
@@ -2895,8 +2925,6 @@
Class<T> valueType)
throws IOException, JsonParseException, JsonMappingException
{
- // !!! TODO
-// _setupClassLoaderForDeserialization(valueType);
return (T) _readMapAndClose(_jsonFactory.createParser(src, offset, len), _typeFactory.constructType(valueType));
}
@@ -2929,7 +2957,20 @@
{
return (T) _readMapAndClose(_jsonFactory.createParser(src, offset, len), valueType);
}
-
+
+ @SuppressWarnings("unchecked")
+ public <T> T readValue(DataInput src, Class<T> valueType) throws IOException
+ {
+ return (T) _readMapAndClose(_jsonFactory.createParser(src),
+ _typeFactory.constructType(valueType));
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> T readValue(DataInput src, JavaType valueType) throws IOException
+ {
+ return (T) _readMapAndClose(_jsonFactory.createParser(src), valueType);
+ }
+
/*
/**********************************************************
/* Extended Public API: serialization
@@ -2965,6 +3006,15 @@
}
/**
+ * @since 2.8
+ */
+ public void writeValue(DataOutput out, Object value)
+ throws IOException
+ {
+ _configAndWriteValue(_jsonFactory.createGenerator(out, JsonEncoding.UTF8), value);
+ }
+
+ /**
* Method that can be used to serialize any Java value as
* JSON output, using Writer provided.
*<p>
@@ -2991,7 +3041,7 @@
@SuppressWarnings("resource")
public String writeValueAsString(Object value)
throws JsonProcessingException
- {
+ {
// alas, we have to pull the recycler directly here...
SegmentedStringWriter sw = new SegmentedStringWriter(_jsonFactory._getBufferRecycler());
try {
@@ -3508,23 +3558,23 @@
_serializerProvider(config).serializeValue(buf, fromValue);
// then matching read, inlined 'readValue' with minor mods:
- final JsonParser jp = buf.asParser();
+ final JsonParser p = buf.asParser();
Object result;
// ok to pass in existing feature flags; unwrapping handled by mapper
final DeserializationConfig deserConfig = getDeserializationConfig();
- JsonToken t = _initForReading(jp);
+ JsonToken t = _initForReading(p);
if (t == JsonToken.VALUE_NULL) {
- DeserializationContext ctxt = createDeserializationContext(jp, deserConfig);
+ DeserializationContext ctxt = createDeserializationContext(p, deserConfig);
result = _findRootDeserializer(ctxt, toValueType).getNullValue(ctxt);
} else if (t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) {
result = null;
} else { // pointing to event other than null
- DeserializationContext ctxt = createDeserializationContext(jp, deserConfig);
+ DeserializationContext ctxt = createDeserializationContext(p, deserConfig);
JsonDeserializer<Object> deser = _findRootDeserializer(ctxt, toValueType);
- // note: no handling of unwarpping
- result = deser.deserialize(jp, ctxt);
+ // note: no handling of unwrapping
+ result = deser.deserialize(p, ctxt);
}
- jp.close();
+ p.close();
return result;
} catch (IOException e) { // should not occur, no real i/o...
throw new IllegalArgumentException(e.getMessage(), e);
@@ -3604,14 +3654,6 @@
protected DefaultSerializerProvider _serializerProvider(SerializationConfig config) {
return _serializerProvider.createInstance(config, _serializerFactory);
}
-
- /**
- * @deprecated Since 2.6, use {@link SerializationConfig#constructDefaultPrettyPrinter()} directly
- */
- @Deprecated
- protected PrettyPrinter _defaultPrettyPrinter() {
- return _serializationConfig.constructDefaultPrettyPrinter();
- }
/**
* Method called to configure the generator as necessary and then
@@ -3626,54 +3668,13 @@
_configAndWriteCloseable(g, value, cfg);
return;
}
- boolean closed = false;
try {
_serializerProvider(cfg).serializeValue(g, value);
- closed = true;
- g.close();
- } finally {
- /* won't try to close twice; also, must catch exception (so it
- * will not mask exception that is pending)
- */
- if (!closed) {
- /* 04-Mar-2014, tatu: But! Let's try to prevent auto-closing of
- * structures, which typically causes more damage.
- */
- g.disable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT);
- try {
- g.close();
- } catch (IOException ioe) { }
- }
- }
- }
-
- @Deprecated // since 2.7, not used internally any more
- protected final void _configAndWriteValue(JsonGenerator g, Object value, Class<?> viewClass)
- throws IOException
- {
- SerializationConfig cfg = getSerializationConfig().withView(viewClass);
- cfg.initialize(g); // since 2.5
-
- // [JACKSON-282]: consider Closeable
- if (cfg.isEnabled(SerializationFeature.CLOSE_CLOSEABLE) && (value instanceof Closeable)) {
- _configAndWriteCloseable(g, value, cfg);
+ } catch (Exception e) {
+ ClassUtil.closeOnFailAndThrowAsIAE(g, e);
return;
}
- boolean closed = false;
- try {
- _serializerProvider(cfg).serializeValue(g, value);
- closed = true;
- g.close();
- } finally {
- if (!closed) {
- // 04-Mar-2014, tatu: But! Let's try to prevent auto-closing of
- // structures, which typically causes more damage.
- g.disable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT);
- try {
- g.close();
- } catch (IOException ioe) { }
- }
- }
+ g.close();
}
/**
@@ -3686,30 +3687,16 @@
Closeable toClose = (Closeable) value;
try {
_serializerProvider(cfg).serializeValue(g, value);
- JsonGenerator tmpGen = g;
- g = null;
- tmpGen.close();
Closeable tmpToClose = toClose;
toClose = null;
tmpToClose.close();
- } finally {
- // Need to close both generator and value, as long as they haven't yet been closed
- if (g != null) {
- // 04-Mar-2014, tatu: But! Let's try to prevent auto-closing of
- // structures, which typically causes more damage.
- g.disable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT);
- try {
- g.close();
- } catch (IOException ioe) { }
- }
- if (toClose != null) {
- try {
- toClose.close();
- } catch (IOException ioe) { }
- }
+ } catch (Exception e) {
+ ClassUtil.closeOnFailAndThrowAsIAE(g, toClose, e);
+ return;
}
+ g.close();
}
-
+
/**
* Helper method used when value to serialize is {@link Closeable} and its <code>close()</code>
* method is to be called right after serialization has been called
@@ -3723,16 +3710,11 @@
if (cfg.isEnabled(SerializationFeature.FLUSH_AFTER_WRITE_VALUE)) {
g.flush();
}
- Closeable tmpToClose = toClose;
- toClose = null;
- tmpToClose.close();
- } finally {
- if (toClose != null) {
- try {
- toClose.close();
- } catch (IOException ioe) { }
- }
+ } catch (Exception e) {
+ ClassUtil.closeOnFailAndThrowAsIAE(null, toClose, e);
+ return;
}
+ toClose.close();
}
/*
@@ -3746,75 +3728,71 @@
* for deserializing a single root value.
* Can be overridden if a custom context is needed.
*/
- protected DefaultDeserializationContext createDeserializationContext(JsonParser jp,
+ protected DefaultDeserializationContext createDeserializationContext(JsonParser p,
DeserializationConfig cfg) {
- return _deserializationContext.createInstance(cfg, jp, _injectableValues);
+ return _deserializationContext.createInstance(cfg, p, _injectableValues);
}
/**
* Actual implementation of value reading+binding operation.
*/
- protected Object _readValue(DeserializationConfig cfg, JsonParser jp, JavaType valueType)
- throws IOException, JsonParseException, JsonMappingException
+ protected Object _readValue(DeserializationConfig cfg, JsonParser p, JavaType valueType)
+ throws IOException
{
/* First: may need to read the next token, to initialize
* state (either before first read from parser, or after
* previous token has been cleared)
*/
Object result;
- JsonToken t = _initForReading(jp);
+ JsonToken t = _initForReading(p);
if (t == JsonToken.VALUE_NULL) {
- // [JACKSON-643]: Ask JsonDeserializer what 'null value' to use:
- DeserializationContext ctxt = createDeserializationContext(jp, cfg);
+ // Ask JsonDeserializer what 'null value' to use:
+ DeserializationContext ctxt = createDeserializationContext(p, cfg);
result = _findRootDeserializer(ctxt, valueType).getNullValue(ctxt);
} else if (t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) {
result = null;
} else { // pointing to event other than null
- DeserializationContext ctxt = createDeserializationContext(jp, cfg);
+ DeserializationContext ctxt = createDeserializationContext(p, cfg);
JsonDeserializer<Object> deser = _findRootDeserializer(ctxt, valueType);
// ok, let's get the value
if (cfg.useRootWrapping()) {
- result = _unwrapAndDeserialize(jp, ctxt, cfg, valueType, deser);
+ result = _unwrapAndDeserialize(p, ctxt, cfg, valueType, deser);
} else {
- result = deser.deserialize(jp, ctxt);
+ result = deser.deserialize(p, ctxt);
}
}
// Need to consume the token too
- jp.clearCurrentToken();
+ p.clearCurrentToken();
return result;
}
- protected Object _readMapAndClose(JsonParser jp, JavaType valueType)
- throws IOException, JsonParseException, JsonMappingException
+ protected Object _readMapAndClose(JsonParser p0, JavaType valueType)
+ throws IOException
{
- try {
+ try (JsonParser p = p0) {
Object result;
- JsonToken t = _initForReading(jp);
+ JsonToken t = _initForReading(p);
if (t == JsonToken.VALUE_NULL) {
- // [JACKSON-643]: Ask JsonDeserializer what 'null value' to use:
- DeserializationContext ctxt = createDeserializationContext(jp,
+ // Ask JsonDeserializer what 'null value' to use:
+ DeserializationContext ctxt = createDeserializationContext(p,
getDeserializationConfig());
result = _findRootDeserializer(ctxt, valueType).getNullValue(ctxt);
} else if (t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) {
result = null;
} else {
DeserializationConfig cfg = getDeserializationConfig();
- DeserializationContext ctxt = createDeserializationContext(jp, cfg);
+ DeserializationContext ctxt = createDeserializationContext(p, cfg);
JsonDeserializer<Object> deser = _findRootDeserializer(ctxt, valueType);
if (cfg.useRootWrapping()) {
- result = _unwrapAndDeserialize(jp, ctxt, cfg, valueType, deser);
+ result = _unwrapAndDeserialize(p, ctxt, cfg, valueType, deser);
} else {
- result = deser.deserialize(jp, ctxt);
+ result = deser.deserialize(p, ctxt);
}
ctxt.checkUnresolvedObjectId();
}
// Need to consume the token too
- jp.clearCurrentToken();
+ p.clearCurrentToken();
return result;
- } finally {
- try {
- jp.close();
- } catch (IOException ioe) { }
}
}
@@ -3863,25 +3841,29 @@
// 12-Jun-2015, tatu: Should try to support namespaces etc but...
String expSimpleName = expRootName.getSimpleName();
if (p.getCurrentToken() != JsonToken.START_OBJECT) {
- throw JsonMappingException.from(p, "Current token not START_OBJECT (needed to unwrap root name '"
- +expSimpleName+"'), but "+p.getCurrentToken());
+ ctxt.reportWrongTokenException(p, JsonToken.START_OBJECT,
+ "Current token not START_OBJECT (needed to unwrap root name '%s'), but %s",
+ expSimpleName, p.getCurrentToken());
+
}
if (p.nextToken() != JsonToken.FIELD_NAME) {
- throw JsonMappingException.from(p, "Current token not FIELD_NAME (to contain expected root name '"
+ ctxt.reportWrongTokenException(p, JsonToken.FIELD_NAME,
+ "Current token not FIELD_NAME (to contain expected root name '"
+expSimpleName+"'), but "+p.getCurrentToken());
}
String actualName = p.getCurrentName();
if (!expSimpleName.equals(actualName)) {
- throw JsonMappingException.from(p, "Root name '"+actualName+"' does not match expected ('"
- +expSimpleName+"') for type "+rootType);
+ ctxt.reportMappingException("Root name '%s' does not match expected ('%s') for type %s",
+ actualName, expSimpleName, rootType);
}
// ok, then move to value itself....
p.nextToken();
Object result = deser.deserialize(p, ctxt);
// and last, verify that we now get matching END_OBJECT
if (p.nextToken() != JsonToken.END_OBJECT) {
- throw JsonMappingException.from(p, "Current token not END_OBJECT (to match wrapper object with root name '"
- +expSimpleName+"'), but "+p.getCurrentToken());
+ ctxt.reportWrongTokenException(p, JsonToken.END_OBJECT,
+ "Current token not END_OBJECT (to match wrapper object with root name '%s'), but %s",
+ expSimpleName, p.getCurrentToken());
}
return result;
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java
index 2279c1f..f006f82 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java
@@ -338,11 +338,8 @@
/**********************************************************
*/
- /**
- * NOTE: changed from static to non-static in 2.5; unfortunate but
- * necessary change to support overridability
- */
- protected JsonToken _initForReading(JsonParser p) throws IOException
+ protected JsonToken _initForReading(DeserializationContext ctxt, JsonParser p)
+ throws IOException
{
if (_schema != null) {
p.setSchema(_schema);
@@ -358,26 +355,28 @@
t = p.nextToken();
if (t == null) {
// Throw mapping exception, since it's failure to map, not an actual parsing problem
- throw JsonMappingException.from(p, "No content to map due to end-of-input");
+ ctxt.reportMissingContent(null); // default msg is fine
}
}
return t;
}
/**
- * Alternative to {@link #_initForReading(JsonParser)} used in cases where reading
+ * Alternative to {@link #_initForReading} used in cases where reading
* of multiple values means that we may or may not want to advance the stream,
* but need to do other initialization.
*<p>
* Base implementation only sets configured {@link FormatSchema}, if any, on parser.
*
- * @since 2.5
+ * @since 2.8
*/
- protected void _initForMultiRead(JsonParser p) throws IOException {
+ protected void _initForMultiRead(DeserializationContext ctxt, JsonParser p)
+ throws IOException
+ {
if (_schema != null) {
p.setSchema(_schema);
}
- _config.initialize(p); // since 2.5
+ _config.initialize(p);
}
/*
@@ -528,6 +527,24 @@
*/
/**
+ * Convenience method to bind from {@link JsonPointer}.
+ * {@link JsonPointerBasedFilter} is registered and will be used for parsing later.
+ * @since 2.6
+ */
+ public ObjectReader at(final String value) {
+ return new ObjectReader(this, new JsonPointerBasedFilter(value));
+ }
+
+ /**
+ * Convenience method to bind from {@link JsonPointer}
+ * {@link JsonPointerBasedFilter} is registered and will be used for parsing later.
+ * @since 2.6
+ */
+ public ObjectReader at(final JsonPointer pointer) {
+ return new ObjectReader(this, new JsonPointerBasedFilter(pointer));
+ }
+
+ /**
* Mutant factory method that will construct a new instance that has
* specified underlying {@link DeserializationConfig}.
*<p>
@@ -1280,7 +1297,19 @@
return (T) _bindAndClose(_considerFilter(treeAsTokens(src), false));
}
-
+
+ /**
+ * @since 2.8
+ */
+ @SuppressWarnings("unchecked")
+ public <T> T readValue(DataInput src) throws IOException
+ {
+ if (_dataFormatReaders != null) {
+ _reportUndetectableSource(src);
+ }
+ return (T) _bindAndClose(_considerFilter(_parserFactory.createParser(src), false));
+ }
+
/**
* Method that reads content from given input source,
* using configuration of this reader, and binds it as JSON Tree.
@@ -1296,7 +1325,6 @@
if (_dataFormatReaders != null) {
return _detectBindAndCloseAsTree(in);
}
-
return _bindAndCloseAsTree(_considerFilter(_parserFactory.createParser(in), false));
}
@@ -1315,7 +1343,6 @@
if (_dataFormatReaders != null) {
_reportUndetectableSource(r);
}
-
return _bindAndCloseAsTree(_considerFilter(_parserFactory.createParser(r), false));
}
@@ -1334,10 +1361,17 @@
if (_dataFormatReaders != null) {
_reportUndetectableSource(json);
}
-
return _bindAndCloseAsTree(_considerFilter(_parserFactory.createParser(json), false));
}
+ public JsonNode readTree(DataInput src) throws IOException
+ {
+ if (_dataFormatReaders != null) {
+ _reportUndetectableSource(src);
+ }
+ return _bindAndCloseAsTree(_considerFilter(_parserFactory.createParser(src), false));
+ }
+
/*
/**********************************************************
/* Deserialization methods; reading sequence of values
@@ -1404,9 +1438,9 @@
_reportUndetectableSource(src);
}
JsonParser p = _considerFilter(_parserFactory.createParser(src), true);
- _initForMultiRead(p);
- p.nextToken();
DeserializationContext ctxt = createDeserializationContext(p);
+ _initForMultiRead(ctxt, p);
+ p.nextToken();
return _newIterator(p, ctxt, _findRootDeserializer(ctxt), true);
}
@@ -1423,9 +1457,9 @@
_reportUndetectableSource(json);
}
JsonParser p = _considerFilter(_parserFactory.createParser(json), true);
- _initForMultiRead(p);
- p.nextToken();
DeserializationContext ctxt = createDeserializationContext(p);
+ _initForMultiRead(ctxt, p);
+ p.nextToken();
return _newIterator(p, ctxt, _findRootDeserializer(ctxt), true);
}
@@ -1478,6 +1512,17 @@
return _bindAndReadValues(_considerFilter(_parserFactory.createParser(src), true));
}
+ /**
+ * @since 2.8
+ */
+ public <T> MappingIterator<T> readValues(DataInput src) throws IOException
+ {
+ if (_dataFormatReaders != null) {
+ _reportUndetectableSource(src);
+ }
+ return _bindAndReadValues(_considerFilter(_parserFactory.createParser(src), true));
+ }
+
/*
/**********************************************************
/* Implementation of rest of ObjectCodec methods
@@ -1516,10 +1561,10 @@
* before first read from parser, or after previous token has been cleared)
*/
Object result;
- JsonToken t = _initForReading(p);
+ final DeserializationContext ctxt = createDeserializationContext(p);
+ JsonToken t = _initForReading(ctxt, p);
if (t == JsonToken.VALUE_NULL) {
if (valueToUpdate == null) {
- DeserializationContext ctxt = createDeserializationContext(p);
result = _findRootDeserializer(ctxt).getNullValue(ctxt);
} else {
result = valueToUpdate;
@@ -1527,7 +1572,6 @@
} else if (t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) {
result = valueToUpdate;
} else { // pointing to event other than null
- DeserializationContext ctxt = createDeserializationContext(p);
JsonDeserializer<Object> deser = _findRootDeserializer(ctxt);
if (_unwrapRoot) {
result = _unwrapAndDeserialize(p, ctxt, _valueType, deser);
@@ -1555,14 +1599,15 @@
? p : new FilteringParserDelegate(p, _filter, false, multiValue);
}
- protected Object _bindAndClose(JsonParser p) throws IOException
+ protected Object _bindAndClose(JsonParser p0) throws IOException
{
- try {
+ try (JsonParser p = p0) {
Object result;
- JsonToken t = _initForReading(p);
+
+ DeserializationContext ctxt = createDeserializationContext(p);
+ JsonToken t = _initForReading(ctxt, p);
if (t == JsonToken.VALUE_NULL) {
if (_valueToUpdate == null) {
- DeserializationContext ctxt = createDeserializationContext(p);
result = _findRootDeserializer(ctxt).getNullValue(ctxt);
} else {
result = _valueToUpdate;
@@ -1570,7 +1615,6 @@
} else if (t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) {
result = _valueToUpdate;
} else {
- DeserializationContext ctxt = createDeserializationContext(p);
JsonDeserializer<Object> deser = _findRootDeserializer(ctxt);
if (_unwrapRoot) {
result = _unwrapAndDeserialize(p, ctxt, _valueType, deser);
@@ -1584,31 +1628,23 @@
}
}
return result;
- } finally {
- try {
- p.close();
- } catch (IOException ioe) { }
}
}
- protected JsonNode _bindAndCloseAsTree(JsonParser p) throws IOException {
- try {
+ protected JsonNode _bindAndCloseAsTree(JsonParser p0) throws IOException {
+ try (JsonParser p = p0) {
return _bindAsTree(p);
- } finally {
- try {
- p.close();
- } catch (IOException ioe) { }
}
}
protected JsonNode _bindAsTree(JsonParser p) throws IOException
{
JsonNode result;
- JsonToken t = _initForReading(p);
+ DeserializationContext ctxt = createDeserializationContext(p);
+ JsonToken t = _initForReading(ctxt, p);
if (t == JsonToken.VALUE_NULL || t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) {
result = NullNode.instance;
} else {
- DeserializationContext ctxt = createDeserializationContext(p);
JsonDeserializer<Object> deser = _findTreeDeserializer(ctxt);
if (_unwrapRoot) {
result = (JsonNode) _unwrapAndDeserialize(p, ctxt, JSON_NODE_TYPE, deser);
@@ -1626,9 +1662,9 @@
*/
protected <T> MappingIterator<T> _bindAndReadValues(JsonParser p) throws IOException
{
- _initForMultiRead(p);
- p.nextToken();
DeserializationContext ctxt = createDeserializationContext(p);
+ _initForMultiRead(ctxt, p);
+ p.nextToken();
return _newIterator(p, ctxt, _findRootDeserializer(ctxt), true);
}
@@ -1640,17 +1676,19 @@
String expSimpleName = expRootName.getSimpleName();
if (p.getCurrentToken() != JsonToken.START_OBJECT) {
- throw JsonMappingException.from(p, "Current token not START_OBJECT (needed to unwrap root name '"
- +expSimpleName+"'), but "+p.getCurrentToken());
+ ctxt.reportWrongTokenException(p, JsonToken.START_OBJECT,
+ "Current token not START_OBJECT (needed to unwrap root name '%s'), but %s",
+ expSimpleName, p.getCurrentToken());
}
if (p.nextToken() != JsonToken.FIELD_NAME) {
- throw JsonMappingException.from(p, "Current token not FIELD_NAME (to contain expected root name '"
- +expSimpleName+"'), but "+p.getCurrentToken());
+ ctxt.reportWrongTokenException(p, JsonToken.FIELD_NAME,
+ "Current token not FIELD_NAME (to contain expected root name '%s'), but %s",
+ expSimpleName, p.getCurrentToken());
}
String actualName = p.getCurrentName();
if (!expSimpleName.equals(actualName)) {
- throw JsonMappingException.from(p, "Root name '"+actualName+"' does not match expected ('"
- +expSimpleName+"') for type "+rootType);
+ ctxt.reportMappingException("Root name '%s' does not match expected ('%s') for type %s",
+ actualName, expSimpleName, rootType);
}
// ok, then move to value itself....
p.nextToken();
@@ -1663,8 +1701,9 @@
}
// and last, verify that we now get matching END_OBJECT
if (p.nextToken() != JsonToken.END_OBJECT) {
- throw JsonMappingException.from(p, "Current token not END_OBJECT (to match wrapper object with root name '"
- +expSimpleName+"'), but "+p.getCurrentToken());
+ ctxt.reportWrongTokenException(p, JsonToken.END_OBJECT,
+ "Current token not END_OBJECT (to match wrapper object with root name '%s'), but %s",
+ expSimpleName, p.getCurrentToken());
}
return result;
}
@@ -1805,7 +1844,7 @@
// Sanity check: must have actual type...
JavaType t = _valueType;
if (t == null) {
- throw JsonMappingException.from(ctxt, "No value type configured for ObjectReader");
+ ctxt.reportMappingException("No value type configured for ObjectReader");
}
// First: have we already seen it?
@@ -1816,7 +1855,7 @@
// Nope: need to ask provider to resolve it
deser = ctxt.findRootValueDeserializer(t);
if (deser == null) { // can this happen?
- throw JsonMappingException.from(ctxt, "Can not find a deserializer for type "+t);
+ ctxt.reportMappingException("Can not find a deserializer for type %s", t);
}
_rootDeserializers.put(t, deser);
return deser;
@@ -1833,8 +1872,8 @@
// Nope: need to ask provider to resolve it
deser = ctxt.findRootValueDeserializer(JSON_NODE_TYPE);
if (deser == null) { // can this happen?
- throw JsonMappingException.from(ctxt,
- "Can not find a deserializer for type "+JSON_NODE_TYPE);
+ ctxt.reportMappingException("Can not find a deserializer for type %s",
+ JSON_NODE_TYPE);
}
_rootDeserializers.put(JSON_NODE_TYPE, deser);
}
@@ -1868,22 +1907,4 @@
}
return deser;
}
-
- /**
- * Convenience method to bind from {@link JsonPointer}.
- * {@link JsonPointerBasedFilter} is registered and will be used for parsing later.
- * @since 2.6
- */
- public ObjectReader at(final String value) {
- return new ObjectReader(this, new JsonPointerBasedFilter(value));
- }
-
- /**
- * Convenience method to bind from {@link JsonPointer}
- * {@link JsonPointerBasedFilter} is registered and will be used for parsing later.
- * @since 2.6
- */
- public ObjectReader at(final JsonPointer pointer) {
- return new ObjectReader(this, new JsonPointerBasedFilter(pointer));
- }
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectWriter.java b/src/main/java/com/fasterxml/jackson/databind/ObjectWriter.java
index 1758925..9574c15 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ObjectWriter.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ObjectWriter.java
@@ -17,6 +17,7 @@
import com.fasterxml.jackson.databind.ser.*;
import com.fasterxml.jackson.databind.ser.impl.TypeWrappedSerializer;
import com.fasterxml.jackson.databind.type.TypeFactory;
+import com.fasterxml.jackson.databind.util.ClassUtil;
/**
* Builder object that can be used for per-serialization configuration of
@@ -760,6 +761,14 @@
}
/**
+ * @since 2.8
+ */
+ public SequenceWriter writeValues(DataOutput out) throws IOException {
+ return _newSequenceWriter(false,
+ _generatorFactory.createGenerator(out), true);
+ }
+
+ /**
* Method for creating a {@link SequenceWriter} to write an array of
* root-level values, using configuration of this {@link ObjectWriter}.
* Resulting writer needs to be {@link SequenceWriter#close()}d after all
@@ -838,6 +847,13 @@
_generatorFactory.createGenerator(out, JsonEncoding.UTF8), true);
}
+ /**
+ * @since 2.8
+ */
+ public SequenceWriter writeValuesAsArray(DataOutput out) throws IOException {
+ return _newSequenceWriter(true, _generatorFactory.createGenerator(out), true);
+ }
+
/*
/**********************************************************
/* Simple accessors
@@ -903,8 +919,7 @@
* Method that can be used to serialize any Java value as
* JSON output, using provided {@link JsonGenerator}.
*/
- public void writeValue(JsonGenerator gen, Object value)
- throws IOException, JsonGenerationException, JsonMappingException
+ public void writeValue(JsonGenerator gen, Object value) throws IOException
{
_configureGenerator(gen);
if (_config.isEnabled(SerializationFeature.CLOSE_CLOSEABLE)
@@ -916,16 +931,11 @@
if (_config.isEnabled(SerializationFeature.FLUSH_AFTER_WRITE_VALUE)) {
gen.flush();
}
- Closeable tmpToClose = toClose;
- toClose = null;
- tmpToClose.close();
- } finally {
- if (toClose != null) {
- try {
- toClose.close();
- } catch (IOException ioe) { }
- }
+ } catch (Exception e) {
+ ClassUtil.closeOnFailAndThrowAsIAE(null, toClose, e);
+ return;
}
+ toClose.close();
} else {
_prefetch.serialize(gen, value, _serializerProvider());
if (_config.isEnabled(SerializationFeature.FLUSH_AFTER_WRITE_VALUE)) {
@@ -984,6 +994,15 @@
}
/**
+ * @since 2.8
+ */
+ public void writeValue(DataOutput out, Object value)
+ throws IOException
+ {
+ _configAndWriteValue(_generatorFactory.createGenerator(out), value);
+ }
+
+ /**
* Method that can be used to serialize any Java value as
* a String. Functionally equivalent to calling
* {@link #writeValue(Writer,Object)} with {@link java.io.StringWriter}
@@ -1124,24 +1143,13 @@
_writeCloseable(gen, value);
return;
}
- boolean closed = false;
try {
_prefetch.serialize(gen, value, _serializerProvider());
- closed = true;
- gen.close();
- } finally {
- // won't try to close twice; also, must catch exception (so it
- // will not mask exception that is pending)
- if (!closed) {
- /* 04-Mar-2014, tatu: But! Let's try to prevent auto-closing of
- * structures, which typically causes more damage.
- */
- gen.disable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT);
- try {
- gen.close();
- } catch (IOException ioe) { }
- }
+ } catch (Exception e) {
+ ClassUtil.closeOnFailAndThrowAsIAE(gen, e);
+ return;
}
+ gen.close();
}
/**
@@ -1154,31 +1162,14 @@
Closeable toClose = (Closeable) value;
try {
_prefetch.serialize(gen, value, _serializerProvider());
- JsonGenerator tmpGen = gen;
- gen = null;
- tmpGen.close();
Closeable tmpToClose = toClose;
toClose = null;
tmpToClose.close();
- } finally {
- /* Need to close both generator and value, as long as they haven't yet
- * been closed
- */
- if (gen != null) {
- /* 04-Mar-2014, tatu: But! Let's try to prevent auto-closing of
- * structures, which typically causes more damage.
- */
- gen.disable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT);
- try {
- gen.close();
- } catch (IOException ioe) { }
- }
- if (toClose != null) {
- try {
- toClose.close();
- } catch (IOException ioe) { }
- }
+ } catch (Exception e) {
+ ClassUtil.closeOnFailAndThrowAsIAE(gen, toClose, e);
+ return;
}
+ gen.close();
}
/**
diff --git a/src/main/java/com/fasterxml/jackson/databind/PropertyNamingStrategy.java b/src/main/java/com/fasterxml/jackson/databind/PropertyNamingStrategy.java
index fcd02c5..e302426 100644
--- a/src/main/java/com/fasterxml/jackson/databind/PropertyNamingStrategy.java
+++ b/src/main/java/com/fasterxml/jackson/databind/PropertyNamingStrategy.java
@@ -422,7 +422,7 @@
public static class LowerCaseWithUnderscoresStrategy extends SnakeCaseStrategy {}
/**
- * @deprecated In 2.7 use {@link SnakeCaseStrategy} instead
+ * @deprecated In 2.7 use {@link UpperCamelCaseStrategy} instead
*/
@Deprecated
public static class PascalCaseStrategy extends UpperCamelCaseStrategy {}
diff --git a/src/main/java/com/fasterxml/jackson/databind/SerializationConfig.java b/src/main/java/com/fasterxml/jackson/databind/SerializationConfig.java
index bf0c3f0..f57590f 100644
--- a/src/main/java/com/fasterxml/jackson/databind/SerializationConfig.java
+++ b/src/main/java/com/fasterxml/jackson/databind/SerializationConfig.java
@@ -3,14 +3,12 @@
import java.text.DateFormat;
import java.util.*;
-import com.fasterxml.jackson.annotation.JsonAutoDetect;
-import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
-import com.fasterxml.jackson.annotation.JsonFormat;
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.annotation.*;
+
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.core.util.Instantiatable;
+
import com.fasterxml.jackson.databind.cfg.*;
import com.fasterxml.jackson.databind.introspect.ClassIntrospector;
import com.fasterxml.jackson.databind.introspect.SimpleMixInResolver;
@@ -46,12 +44,13 @@
// since 2.7
// Default is "USE_DEFAULTS, USE_DEFAULTS"
protected final static JsonInclude.Value DEFAULT_INCLUSION = JsonInclude.Value.empty();
-
+
/*
/**********************************************************
/* Configured helper objects
/**********************************************************
*/
+
/**
* Object used for resolving filter ids to filter instances.
* Non-null if explicitly defined; null by default.
@@ -134,9 +133,9 @@
*/
public SerializationConfig(BaseSettings base,
SubtypeResolver str, SimpleMixInResolver mixins,
- RootNameLookup rootNames)
+ RootNameLookup rootNames, ConfigOverrides configOverrides)
{
- super(base, str, mixins, rootNames);
+ super(base, str, mixins, rootNames, configOverrides);
_serFeatures = collectFeatureDefaults(SerializationFeature.class);
_filterProvider = null;
_defaultPrettyPrinter = DEFAULT_PRETTY_PRINTER;
@@ -147,6 +146,17 @@
_serializationInclusion = DEFAULT_INCLUSION;
}
+ /**
+ * @deprecated Since 2.8, remove from 2.9 or later
+ */
+ @Deprecated
+ public SerializationConfig(BaseSettings base,
+ SubtypeResolver str, SimpleMixInResolver mixins,
+ RootNameLookup rootNames)
+ {
+ this(base, str, mixins, rootNames, null);
+ }
+
private SerializationConfig(SerializationConfig src, SubtypeResolver str)
{
super(src, str);
@@ -290,15 +300,14 @@
}
/**
- * Copy-constructor used for making a copy to be used by new {@link ObjectMapper}
- * or {@link ObjectReader}.
+ * Copy-constructor used for making a copy to be used by new {@link ObjectMapper}.
*
- * @since 2.6
+ * @since 2.8
*/
protected SerializationConfig(SerializationConfig src, SimpleMixInResolver mixins,
- RootNameLookup rootNames)
+ RootNameLookup rootNames, ConfigOverrides configOverrides)
{
- super(src, mixins, rootNames);
+ super(src, mixins, rootNames, configOverrides);
_serFeatures = src._serFeatures;
_serializationInclusion = src._serializationInclusion;
_filterProvider = src._filterProvider;
@@ -857,29 +866,6 @@
return getClassIntrospector().forDirectClassAnnotations(this, type, this);
}
- @Override
- public VisibilityChecker<?> getDefaultVisibilityChecker()
- {
- VisibilityChecker<?> vchecker = super.getDefaultVisibilityChecker();
- // then global overrides (disabling)
- if (!isEnabled(MapperFeature.AUTO_DETECT_SETTERS)) {
- vchecker = vchecker.withSetterVisibility(Visibility.NONE);
- }
- if (!isEnabled(MapperFeature.AUTO_DETECT_CREATORS)) {
- vchecker = vchecker.withCreatorVisibility(Visibility.NONE);
- }
- if (!isEnabled(MapperFeature.AUTO_DETECT_GETTERS)) {
- vchecker = vchecker.withGetterVisibility(Visibility.NONE);
- }
- if (!isEnabled(MapperFeature.AUTO_DETECT_IS_GETTERS)) {
- vchecker = vchecker.withIsGetterVisibility(Visibility.NONE);
- }
- if (!isEnabled(MapperFeature.AUTO_DETECT_FIELDS)) {
- vchecker = vchecker.withFieldVisibility(Visibility.NONE);
- }
- return vchecker;
- }
-
/*
/**********************************************************
/* Configuration: default settings with per-type overrides
@@ -903,16 +889,30 @@
@Override
public JsonInclude.Value getDefaultPropertyInclusion(Class<?> baseType) {
- // !!! TODO: per-type defaults
+ ConfigOverride overrides = findConfigOverride(baseType);
+ if (overrides != null) {
+ JsonInclude.Value v = overrides.getInclude();
+ if (v != null) {
+ return v;
+ }
+ }
return _serializationInclusion;
}
@Override
- public JsonFormat.Value getDefaultPropertyFormat(Class<?> baseType) {
- // !!! TODO: per-type defaults
- return EMPTY_FORMAT;
+ public JsonInclude.Value getDefaultPropertyInclusion(Class<?> baseType,
+ JsonInclude.Value defaultIncl)
+ {
+ ConfigOverride overrides = findConfigOverride(baseType);
+ if (overrides != null) {
+ JsonInclude.Value v = overrides.getInclude();
+ if (v != null) {
+ return v;
+ }
+ }
+ return defaultIncl;
}
-
+
/*
/**********************************************************
/* Configuration: other
diff --git a/src/main/java/com/fasterxml/jackson/databind/SerializationFeature.java b/src/main/java/com/fasterxml/jackson/databind/SerializationFeature.java
index fe40906..e8d1cb3 100644
--- a/src/main/java/com/fasterxml/jackson/databind/SerializationFeature.java
+++ b/src/main/java/com/fasterxml/jackson/databind/SerializationFeature.java
@@ -288,8 +288,16 @@
* Note that this does not change behavior of {@link java.util.Map}s, or
* "Collection-like" types.
*<p>
+ * NOTE: unlike other {@link SerializationFeature}s, this feature <b>can not</b> be
+ * dynamically changed on per-call basis, because its effect is considered during
+ * construction of serializers and property handlers.
+ *<p>
* Feature is enabled by default.
+ *
+ * @deprecated Since 2.8 there are better mechanism for specifying filtering; specifically
+ * using {@link com.fasterxml.jackson.annotation.JsonInclude} or configuration overrides.
*/
+ @Deprecated // since 2.8
WRITE_EMPTY_JSON_ARRAYS(true),
/**
diff --git a/src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java b/src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java
index 90df103..bce67fa 100644
--- a/src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java
+++ b/src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java
@@ -7,11 +7,13 @@
import java.util.TimeZone;
import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.ObjectIdGenerator;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.cfg.ContextAttributes;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.introspect.Annotated;
+import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.ser.*;
import com.fasterxml.jackson.databind.ser.impl.FailingSerializer;
@@ -153,7 +155,7 @@
/* State, for non-blueprint instances: generic
/**********************************************************
*/
-
+
/**
* For fast lookups, we will have a local non-shared read-only
* map that contains serializers previously fetched.
@@ -312,7 +314,8 @@
/*
/**********************************************************
- /* DatabindContext implementation
+ /* DatabindContext implementation (and closely related
+ /* but ser-specific)
/**********************************************************
*/
@@ -355,7 +358,14 @@
public final JsonFormat.Value getDefaultPropertyFormat(Class<?> baseType) {
return _config.getDefaultPropertyFormat(baseType);
}
-
+
+ /**
+ * @since 2.8
+ */
+ public final JsonInclude.Value getDefaultPropertyInclusion(Class<?> baseType) {
+ return _config.getDefaultPropertyInclusion();
+ }
+
/**
* Method for accessing default Locale to use: convenience method for
*<pre>
@@ -1071,12 +1081,12 @@
}
}
- public final void defaultSerializeNull(JsonGenerator jgen) throws IOException
+ public final void defaultSerializeNull(JsonGenerator gen) throws IOException
{
if (_stdNullValueSerializer) { // minor perf optimization
- jgen.writeNull();
+ gen.writeNull();
} else {
- _nullValueSerializer.serialize(null, jgen, this);
+ _nullValueSerializer.serialize(null, gen, this);
}
}
@@ -1087,13 +1097,95 @@
*/
/**
+ * Factory method for constructing a {@link JsonMappingException};
+ * usually only indirectly used by calling
+ * {@link #reportMappingProblem(String, Object...)}.
+ *
* @since 2.6
*/
public JsonMappingException mappingException(String message, Object... args) {
if (args != null && args.length > 0) {
message = String.format(message, args);
}
- return JsonMappingException.from(this, message);
+ return JsonMappingException.from(getGenerator(), message);
+ }
+
+ /**
+ * Factory method for constructing a {@link JsonMappingException};
+ * usually only indirectly used by calling
+ * {@link #reportMappingProblem(Throwable, String, Object...)}
+ *
+ * @since 2.8
+ */
+ protected JsonMappingException mappingException(Throwable t, String message, Object... args) {
+ if (args != null && args.length > 0) {
+ message = String.format(message, args);
+ }
+ return JsonMappingException.from(getGenerator(), message, t);
+ }
+
+ /**
+ * Helper method called to indicate problem; default behavior is to construct and
+ * throw a {@link JsonMappingException}, but in future may collect more than one
+ * and only throw after certain number, or at the end of serialization.
+ *
+ * @since 2.8
+ */
+ public void reportMappingProblem(String message, Object... args) throws JsonMappingException {
+ throw mappingException(message, args);
+ }
+
+ /**
+ * Helper method called to indicate problem; default behavior is to construct and
+ * throw a {@link JsonMappingException}, but in future may collect more than one
+ * and only throw after certain number, or at the end of serialization.
+ *
+ * @since 2.8
+ */
+ public void reportMappingProblem(Throwable t, String message, Object... args) throws JsonMappingException {
+ throw mappingException(t, message, args);
+ }
+
+ /**
+ * Helper method called to indicate problem in POJO (serialization) definitions or settings
+ * regarding specific Java type, unrelated to actual JSON content to map.
+ * Default behavior is to construct and throw a {@link JsonMappingException}.
+ *
+ * @since 2.9
+ */
+ public <T> T reportBadTypeDefinition(BeanDescription bean,
+ String message, Object... args) throws JsonMappingException {
+ if (args != null && args.length > 0) {
+ message = String.format(message, args);
+ }
+ String beanDesc = (bean == null) ? "N/A" : _desc(bean.getType().getGenericSignature());
+ throw mappingException("Invalid type definition for type %s: %s",
+ beanDesc, message);
+ }
+
+ /**
+ * Helper method called to indicate problem in POJO (serialization) definitions or settings
+ * regarding specific property (of a type), unrelated to actual JSON content to map.
+ * Default behavior is to construct and throw a {@link JsonMappingException}.
+ *
+ * @since 2.9
+ */
+ public <T> T reportBadPropertyDefinition(BeanDescription bean, BeanPropertyDefinition prop,
+ String message, Object... args) throws JsonMappingException {
+ if (args != null && args.length > 0) {
+ message = String.format(message, args);
+ }
+ String propName = (prop == null) ? "N/A" : _quotedString(prop.getName());
+ String beanDesc = (bean == null) ? "N/A" : _desc(bean.getType().getGenericSignature());
+ throw mappingException("Invalid definition for property %s (of type %s): %s",
+ propName, beanDesc, message);
+ }
+
+ /**
+ * @since 2.8
+ */
+ public JsonGenerator getGenerator() {
+ return null;
}
/*
@@ -1112,11 +1204,10 @@
return;
}
}
- throw JsonMappingException.from(this,
- "Incompatible types: declared root type ("+rootType+") vs "
- +value.getClass().getName());
+ reportMappingProblem("Incompatible types: declared root type (%s) vs %s",
+ rootType, value.getClass().getName());
}
-
+
/**
* Method that will try to find a serializer, either from cache
* or by constructing one; but will not return an "unknown" serializer
@@ -1169,7 +1260,8 @@
/* We better only expose checked exceptions, since those
* are what caller is expected to handle
*/
- throw JsonMappingException.from(this, iae.getMessage(), iae);
+ reportMappingProblem(iae, iae.getMessage());
+ return null; // never gets here
}
if (ser != null) {
@@ -1189,7 +1281,8 @@
/* We better only expose checked exceptions, since those
* are what caller is expected to handle
*/
- throw JsonMappingException.from(this, iae.getMessage(), iae);
+ reportMappingProblem(iae, iae.getMessage());
+ return null; // never gets here
}
if (ser != null) {
@@ -1247,6 +1340,20 @@
/**********************************************************
*/
+ protected String _desc(Object value) {
+ if (value == null) {
+ return "N/A";
+ }
+ return "'"+value+"'";
+ }
+
+ protected String _quotedString(Object value) {
+ if (value == null) {
+ return "N/A";
+ }
+ return String.valueOf(value);
+ }
+
protected final DateFormat _dateFormat()
{
if (_dateFormat != null) {
diff --git a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonAppend.java b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonAppend.java
index ade257f..445f605 100644
--- a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonAppend.java
+++ b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonAppend.java
@@ -34,14 +34,15 @@
/**
* Indicator used to determine whether properties defined are to be
- * appended before (false) or prepended before (true) regular properties.
+ * appended after (false) or prepended before (true) regular properties.
* Affects all kinds of properties defined using this annotation.
*/
public boolean prepend() default false;
-
+
/**
* Definition of a single attribute-backed property.
- * Attribute-backed properties will be appended after regular properties
+ * Attribute-backed properties will be appended after (or prepended before,
+ * as per {@link #prepend}) regular properties
* in specified order, although their placement may be further changed
* by the usual property-ordering functionality (alphabetic sorting;
* explicit ordering)
diff --git a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonDeserialize.java b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonDeserialize.java
index 740b65c..ba6a35f 100644
--- a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonDeserialize.java
+++ b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonDeserialize.java
@@ -25,6 +25,17 @@
* )
*</pre>
*<p>
+ * Something to note on usage:
+ *<ul>
+ * <li>All other annotations regarding behavior during building should be on <b>Builder</b>
+ * class and NOT on target POJO class: for example @JsonIgnoreProperties should be on
+ * Builder to prevent "unknown property" errors.
+ * </li>
+ * <li>Similarly configuration overrides (see {@link com.fasterxml.jackson.databind.ObjectMapper#configOverride})
+ * should be targeted at Builder class, not target POJO class.
+ * </li>
+ * </ul>
+ *
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
diff --git a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonPOJOBuilder.java b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonPOJOBuilder.java
index 9e8d46f..3bab933 100644
--- a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonPOJOBuilder.java
+++ b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonPOJOBuilder.java
@@ -14,8 +14,16 @@
* Annotation is typically used if the naming convention
* of a Builder class is different from defaults:
*<ul>
+ * <li>By default, setters are expected to have names like `withName()` (for property "name");
+ * override by {@link #withPrefix()} property.
+ * </li>
* </ul>
- *
+ *<p>
+ * In addition to configuration using this annotation, note that many other configuration
+ * annotations are also applied to Builders, for example
+ * {@link com.fasterxml.jackson.annotation.JsonIgnoreProperties} can be used to ignore
+ * "unknown" properties.
+ *
* @since 2.0
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE})
diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/BaseSettings.java b/src/main/java/com/fasterxml/jackson/databind/cfg/BaseSettings.java
index 66c24b7..3421e87 100644
--- a/src/main/java/com/fasterxml/jackson/databind/cfg/BaseSettings.java
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/BaseSettings.java
@@ -121,7 +121,8 @@
/**
* Default {@link java.util.TimeZone} used with serialization formats,
* if (and only if!) explicitly set by use; otherwise `null` to indicate
- * "use default", which currently (Jackson 2.6) means "GMT"
+ * "use default", which means "UTC" (from Jackson 2.7); earlier versions
+ * (up to 2.6) used "GMT".
*<p>
* Note that if a new value is set, timezone is also assigned to
* {@link #_dateFormat} of this object.
diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigOverride.java b/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigOverride.java
new file mode 100644
index 0000000..18e6304
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigOverride.java
@@ -0,0 +1,57 @@
+package com.fasterxml.jackson.databind.cfg;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+/**
+ * Configuration object that is accessed by databinding functionality
+ * to find overrides to configuration of properties, based on declared
+ * type of the property. Such overrides have precedence over annotations
+ * attached to actual type ({@link java.lang.Class}), but can be further
+ * overridden by annotations attached to the property itself.
+ *
+ * @since 2.8
+ */
+public abstract class ConfigOverride
+{
+ /**
+ * Definitions of format overrides, if any.
+ */
+ protected JsonFormat.Value _format;
+
+ /**
+ * Definitions of inclusion overrides, if any.
+ */
+ protected JsonInclude.Value _include;
+
+ /**
+ * Definitions of property ignoral (whether to serialize, deserialize
+ * given logical property) overrides, if any.
+ */
+ protected JsonIgnoreProperties.Value _ignorals;
+
+ /**
+ * Flag that indicates whether "is ignorable type" is specified for this type;
+ * and if so, is it to be ignored (true) or not ignored (false); `null` is
+ * used to indicate "not specified", in which case other configuration (class
+ * annotation) is used.
+ */
+ protected Boolean _isIgnoredType;
+
+ protected ConfigOverride() { }
+ protected ConfigOverride(ConfigOverride src) {
+ _format = src._format;
+ _include = src._include;
+ _ignorals = src._ignorals;
+ _isIgnoredType = src._isIgnoredType;
+ }
+
+ public JsonFormat.Value getFormat() { return _format; }
+ public JsonInclude.Value getInclude() { return _include; }
+ public JsonIgnoreProperties.Value getIgnorals() { return _ignorals; }
+
+ public Boolean getIsIgnoredType() {
+ return _isIgnoredType;
+ }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigOverrides.java b/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigOverrides.java
new file mode 100644
index 0000000..9fd944e
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigOverrides.java
@@ -0,0 +1,59 @@
+package com.fasterxml.jackson.databind.cfg;
+
+import java.util.*;
+
+/**
+ * Container for individual {@link ConfigOverride} values.
+ *
+ * @since 2.8
+ */
+public class ConfigOverrides
+ implements java.io.Serializable
+{
+ private static final long serialVersionUID = 1L;
+
+ protected Map<Class<?>, MutableConfigOverride> _overrides;
+
+ public ConfigOverrides() {
+ _overrides = null;
+ }
+
+ protected ConfigOverrides(Map<Class<?>, MutableConfigOverride> overrides) {
+ _overrides = overrides;
+ }
+
+ public ConfigOverrides copy()
+ {
+ if (_overrides == null) {
+ return new ConfigOverrides();
+ }
+ Map<Class<?>, MutableConfigOverride> newOverrides = _newMap();
+ for (Map.Entry<Class<?>, MutableConfigOverride> entry : _overrides.entrySet()) {
+ newOverrides.put(entry.getKey(), entry.getValue().copy());
+ }
+ return new ConfigOverrides(newOverrides);
+ }
+
+ public ConfigOverride findOverride(Class<?> type) {
+ if (_overrides == null) {
+ return null;
+ }
+ return _overrides.get(type);
+ }
+
+ public MutableConfigOverride findOrCreateOverride(Class<?> type) {
+ if (_overrides == null) {
+ _overrides = _newMap();
+ }
+ MutableConfigOverride override = _overrides.get(type);
+ if (override == null) {
+ override = new MutableConfigOverride();
+ _overrides.put(type, override);
+ }
+ return override;
+ }
+
+ protected Map<Class<?>, MutableConfigOverride> _newMap() {
+ return new HashMap<Class<?>, MutableConfigOverride>();
+ }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfig.java b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfig.java
index 194db6e..1ae5732 100644
--- a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfig.java
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfig.java
@@ -5,14 +5,13 @@
import java.util.TimeZone;
import com.fasterxml.jackson.annotation.*;
-
import com.fasterxml.jackson.core.Base64Variant;
import com.fasterxml.jackson.core.SerializableString;
import com.fasterxml.jackson.core.io.SerializedString;
import com.fasterxml.jackson.core.type.TypeReference;
-
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.introspect.Annotated;
+import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
import com.fasterxml.jackson.databind.introspect.ClassIntrospector;
import com.fasterxml.jackson.databind.introspect.VisibilityChecker;
import com.fasterxml.jackson.databind.jsontype.SubtypeResolver;
@@ -358,13 +357,27 @@
/**
* Accessor for default property inclusion to use for serialization,
- * considering possible per-type override for given base type.
+ * considering possible per-type override for given base type.<br>
+ * NOTE: if no override found, defaults to value returned by
+ * {@link #getDefaultPropertyInclusion()}.
*
* @since 2.7
*/
public abstract JsonInclude.Value getDefaultPropertyInclusion(Class<?> baseType);
/**
+ * Accessor for default property inclusion to use for serialization,
+ * considering possible per-type override for given base type; but
+ * if none found, returning given <code>defaultIncl</code>
+ *
+ * @param defaultIncl Inclusion setting to return if no overrides found.
+ *
+ * @since 2.8.2
+ */
+ public abstract JsonInclude.Value getDefaultPropertyInclusion(Class<?> baseType,
+ JsonInclude.Value defaultIncl);
+
+ /**
* Accessor for default format settings to use for serialization (and, to a degree
* deserialization), considering baseline settings and per-type defaults
* for given base type (if any).
@@ -372,7 +385,37 @@
* @since 2.7
*/
public abstract JsonFormat.Value getDefaultPropertyFormat(Class<?> baseType);
-
+
+ /**
+ * Accessor for default property ignorals to use, if any, for given base type,
+ * based on config overrides settings (see {@link #findConfigOverride(Class)}).
+ *
+ * @since 2.8
+ */
+ public abstract JsonIgnoreProperties.Value getDefaultPropertyIgnorals(Class<?> baseType);
+
+ /**
+ * Helper method that may be called to see if there are property ignoral
+ * definitions from annotations (via {@link AnnotatedClass}) or through
+ * "config overrides". If both exist, config overrides have precedence
+ * over class annotations.
+ *
+ * @since 2.8
+ */
+ public abstract JsonIgnoreProperties.Value getDefaultPropertyIgnorals(Class<?> baseType,
+ AnnotatedClass actualClass);
+
+ /**
+ * Accessor for finding possible {@link ConfigOverride} to use for
+ * properties of given type. Note that only directly associate override
+ * is found; no type hierarchy traversal is performed.
+ *
+ * @since 2.8
+ *
+ * @return Override object if there is an override for specified type; `null` if not
+ */
+ public abstract ConfigOverride findConfigOverride(Class<?> type);
+
/*
/**********************************************************
/* Configuration: other
diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java
index 11513ec..6f90f9b 100644
--- a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java
@@ -1,20 +1,15 @@
package com.fasterxml.jackson.databind.cfg;
import java.text.DateFormat;
-import java.util.Locale;
-import java.util.Map;
-import java.util.TimeZone;
+import java.util.*;
-import com.fasterxml.jackson.annotation.JsonAutoDetect;
-import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.core.Base64Variant;
-import com.fasterxml.jackson.databind.AnnotationIntrospector;
-import com.fasterxml.jackson.databind.JavaType;
-import com.fasterxml.jackson.databind.MapperFeature;
-import com.fasterxml.jackson.databind.PropertyName;
-import com.fasterxml.jackson.databind.PropertyNamingStrategy;
+import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.introspect.ClassIntrospector;
import com.fasterxml.jackson.databind.introspect.ClassIntrospector.MixInResolver;
+import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
import com.fasterxml.jackson.databind.introspect.SimpleMixInResolver;
import com.fasterxml.jackson.databind.introspect.VisibilityChecker;
import com.fasterxml.jackson.databind.jsontype.SubtypeResolver;
@@ -47,6 +42,9 @@
/**
* Registered concrete subtypes that can be used instead of (or
* in addition to) ones declared using annotations.
+ *<p>
+ * Note that instances are stateful and as such may need to be copied,
+ * and may NOT be demoted down to {@link BaseSettings}.
*/
protected final SubtypeResolver _subtypeResolver;
@@ -74,10 +72,23 @@
protected final ContextAttributes _attributes;
/**
+ * Simple cache used for finding out possible root name for root name
+ * wrapping.
+ *<p>
+ * Note that instances are stateful (for caching) and as such may need to be copied,
+ * and may NOT be demoted down to {@link BaseSettings}.
+ *
* @since 2.6
*/
protected final RootNameLookup _rootNames;
-
+
+ /**
+ * Configuration overrides to apply, keyed by type of property.
+ *
+ * @since 2.8
+ */
+ protected final ConfigOverrides _configOverrides;
+
/*
/**********************************************************
/* Construction
@@ -87,10 +98,12 @@
/**
* Constructor used when creating a new instance (compared to
* that of creating fluent copies)
+ *
+ * @since 2.8
*/
protected MapperConfigBase(BaseSettings base,
SubtypeResolver str, SimpleMixInResolver mixins,
- RootNameLookup rootNames)
+ RootNameLookup rootNames, ConfigOverrides configOverrides)
{
super(base, DEFAULT_MAPPER_FEATURES);
_mixIns = mixins;
@@ -100,8 +113,20 @@
_view = null;
// default to "no attributes"
_attributes = ContextAttributes.getEmpty();
+ _configOverrides = configOverrides;
}
-
+
+ /**
+ * @deprecated Since 2.8, remove from 2.9 or later
+ */
+ @Deprecated
+ protected MapperConfigBase(BaseSettings base,
+ SubtypeResolver str, SimpleMixInResolver mixins,
+ RootNameLookup rootNames)
+ {
+ this(base, str, mixins, rootNames, null);
+ }
+
/**
* Pass-through constructor used when no changes are needed to the
* base class.
@@ -115,6 +140,7 @@
_rootName = src._rootName;
_view = src._view;
_attributes = src._attributes;
+ _configOverrides = src._configOverrides;
}
protected MapperConfigBase(MapperConfigBase<CFG,T> src, BaseSettings base)
@@ -126,6 +152,7 @@
_rootName = src._rootName;
_view = src._view;
_attributes = src._attributes;
+ _configOverrides = src._configOverrides;
}
protected MapperConfigBase(MapperConfigBase<CFG,T> src, int mapperFeatures)
@@ -137,6 +164,7 @@
_rootName = src._rootName;
_view = src._view;
_attributes = src._attributes;
+ _configOverrides = src._configOverrides;
}
protected MapperConfigBase(MapperConfigBase<CFG,T> src, SubtypeResolver str) {
@@ -147,6 +175,7 @@
_rootName = src._rootName;
_view = src._view;
_attributes = src._attributes;
+ _configOverrides = src._configOverrides;
}
protected MapperConfigBase(MapperConfigBase<CFG,T> src, PropertyName rootName) {
@@ -157,6 +186,7 @@
_rootName = rootName;
_view = src._view;
_attributes = src._attributes;
+ _configOverrides = src._configOverrides;
}
protected MapperConfigBase(MapperConfigBase<CFG,T> src, Class<?> view)
@@ -168,6 +198,7 @@
_rootName = src._rootName;
_view = view;
_attributes = src._attributes;
+ _configOverrides = src._configOverrides;
}
/**
@@ -182,6 +213,7 @@
_rootName = src._rootName;
_view = src._view;
_attributes = src._attributes;
+ _configOverrides = src._configOverrides;
}
/**
@@ -196,13 +228,14 @@
_rootName = src._rootName;
_view = src._view;
_attributes = attr;
+ _configOverrides = src._configOverrides;
}
/**
- * @since 2.6
+ * @since 2.8
*/
protected MapperConfigBase(MapperConfigBase<CFG,T> src, SimpleMixInResolver mixins,
- RootNameLookup rootNames)
+ RootNameLookup rootNames, ConfigOverrides configOverrides)
{
super(src);
_mixIns = mixins;
@@ -211,8 +244,39 @@
_rootName = src._rootName;
_view = src._view;
_attributes = src._attributes;
+ _configOverrides = configOverrides;
}
-
+
+ /*
+ /**********************************************************
+ /* Overrides
+ /**********************************************************
+ */
+
+ // note: demoted in 2.8 from sub-classes, as there's no difference
+ @Override
+ public VisibilityChecker<?> getDefaultVisibilityChecker()
+ {
+ VisibilityChecker<?> vchecker = super.getDefaultVisibilityChecker();
+ // then global overrides (disabling)
+ if (!isEnabled(MapperFeature.AUTO_DETECT_SETTERS)) {
+ vchecker = vchecker.withSetterVisibility(Visibility.NONE);
+ }
+ if (!isEnabled(MapperFeature.AUTO_DETECT_CREATORS)) {
+ vchecker = vchecker.withCreatorVisibility(Visibility.NONE);
+ }
+ if (!isEnabled(MapperFeature.AUTO_DETECT_GETTERS)) {
+ vchecker = vchecker.withGetterVisibility(Visibility.NONE);
+ }
+ if (!isEnabled(MapperFeature.AUTO_DETECT_IS_GETTERS)) {
+ vchecker = vchecker.withIsGetterVisibility(Visibility.NONE);
+ }
+ if (!isEnabled(MapperFeature.AUTO_DETECT_FIELDS)) {
+ vchecker = vchecker.withFieldVisibility(Visibility.NONE);
+ }
+ return vchecker;
+ }
+
/*
/**********************************************************
/* Addition fluent factory methods, common to all sub-types
@@ -446,6 +510,54 @@
/*
/**********************************************************
+ /* Property config override access
+ /**********************************************************
+ */
+
+ @Override
+ public final ConfigOverride findConfigOverride(Class<?> type) {
+ return _configOverrides.findOverride(type);
+ }
+
+ @Override
+ public final JsonFormat.Value getDefaultPropertyFormat(Class<?> type) {
+ ConfigOverride overrides = _configOverrides.findOverride(type);
+ if (overrides != null) {
+ JsonFormat.Value v = overrides.getFormat();
+ if (v != null) {
+ return v;
+ }
+ }
+ return EMPTY_FORMAT;
+ }
+
+ @Override
+ public final JsonIgnoreProperties.Value getDefaultPropertyIgnorals(Class<?> type) {
+ ConfigOverride overrides = _configOverrides.findOverride(type);
+ if (overrides != null) {
+ JsonIgnoreProperties.Value v = overrides.getIgnorals();
+ if (v != null) {
+ return v;
+ }
+ }
+ // 01-May-2015, tatu: Could return `Value.empty()` but for now `null`
+ // seems simpler as callers can avoid processing.
+ return null;
+ }
+
+ @Override
+ public final JsonIgnoreProperties.Value getDefaultPropertyIgnorals(Class<?> baseType,
+ AnnotatedClass actualClass)
+ {
+ AnnotationIntrospector intr = getAnnotationIntrospector();
+ JsonIgnoreProperties.Value base = (intr == null) ? null
+ : intr.findPropertyIgnorals(actualClass);
+ JsonIgnoreProperties.Value overrides = getDefaultPropertyIgnorals(baseType);
+ return JsonIgnoreProperties.Value.merge(base, overrides);
+ }
+
+ /*
+ /**********************************************************
/* Other config access
/**********************************************************
*/
@@ -465,7 +577,7 @@
}
return _rootNames.findRootName(rawRootType, this);
}
-
+
/*
/**********************************************************
/* ClassIntrospector.MixInResolver impl:
diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/MutableConfigOverride.java b/src/main/java/com/fasterxml/jackson/databind/cfg/MutableConfigOverride.java
new file mode 100644
index 0000000..902e18b
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/MutableConfigOverride.java
@@ -0,0 +1,51 @@
+package com.fasterxml.jackson.databind.cfg;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+/**
+ * Extension of {@link ConfigOverride} that allows changing of
+ * contained configuration settings. Exposed to
+ * {@link com.fasterxml.jackson.databind.Module}s that want to set
+ * overrides, but not exposed to functionality that wants to apply
+ * overrides.
+ *
+ * @since 2.8
+ */
+public class MutableConfigOverride
+ extends ConfigOverride
+ implements java.io.Serializable
+{
+ private static final long serialVersionUID = 1L;
+
+ public MutableConfigOverride() { super(); }
+
+ protected MutableConfigOverride(MutableConfigOverride src) {
+ super(src);
+ }
+
+ protected MutableConfigOverride copy() {
+ return new MutableConfigOverride(this);
+ }
+
+ public MutableConfigOverride setFormat(JsonFormat.Value v) {
+ _format = v;
+ return this;
+ }
+
+ public MutableConfigOverride setInclude(JsonInclude.Value v) {
+ _include = v;
+ return this;
+ }
+
+ public MutableConfigOverride setIgnorals(JsonIgnoreProperties.Value v) {
+ _ignorals = v;
+ return this;
+ }
+
+ public MutableConfigOverride setIsIgnoredType(Boolean v) {
+ _isIgnoredType = v;
+ return this;
+ }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/AbstractDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/AbstractDeserializer.java
index 7b13fba..0342fac 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/AbstractDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/AbstractDeserializer.java
@@ -146,9 +146,8 @@
public Object deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException
{
- // This method should never be called...
- throw ctxt.instantiationException(_baseType.getRawClass(),
- "abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information");
+ return ctxt.handleMissingInstantiator(_baseType.getRawClass(), p,
+ "abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information");
}
/*
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java b/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java
index fd1972e..f661908 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java
@@ -6,6 +6,7 @@
import java.util.concurrent.atomic.AtomicReference;
import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.core.JsonLocation;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig;
@@ -259,8 +260,9 @@
instantiator = insts.findValueInstantiator(config, beanDesc, instantiator);
// let's do sanity check; easier to spot buggy handlers
if (instantiator == null) {
- throw JsonMappingException.from(ctxt.getParser(),
- "Broken registered ValueInstantiators (of type "+insts.getClass().getName()+"): returned null ValueInstantiator");
+ ctxt.reportMappingException(
+ "Broken registered ValueInstantiators (of type %s): returned null ValueInstantiator",
+ insts.getClass().getName());
}
}
}
@@ -839,22 +841,10 @@
metadata = PropertyMetadata.construct(req, desc, idx, def);
}
}
- // 15-Oct-2015, tatu: Not 100% if context needed; removing it does not make any
- // existing unit tests fail. Still seems like the right thing to do.
- JavaType t0 = beanDesc.resolveType(param.getParameterType());
- BeanProperty.Std property = new BeanProperty.Std(name, t0,
+ JavaType type = resolveMemberAndTypeAnnotations(ctxt, param, param.getType());
+ BeanProperty.Std property = new BeanProperty.Std(name, type,
intr.findWrapperName(param),
beanDesc.getClassAnnotations(), param, metadata);
- JavaType type = resolveType(ctxt, beanDesc, t0, param);
- if (type != t0) {
- property = property.withType(type);
- }
- // Is there an annotation that specifies exact deserializer?
- JsonDeserializer<?> deser = findDeserializerFromAnnotation(ctxt, param);
-
- // If yes, we are mostly done:
- type = modifyTypeByAnnotation(ctxt, param, type);
-
// Type deserializer: either comes from property (and already resolved)
TypeDeserializer typeDeser = (TypeDeserializer) type.getTypeHandler();
// or if not, based on type being referenced:
@@ -866,8 +856,12 @@
SettableBeanProperty prop = new CreatorProperty(name, type, property.getWrapperName(),
typeDeser, beanDesc.getClassAnnotations(), param, index, injectableValueId,
metadata);
+ JsonDeserializer<?> deser = findDeserializerFromAnnotation(ctxt, param);
+ if (deser == null) {
+ deser = type.getValueHandler();
+ }
if (deser != null) {
- // As per [Issue#462] need to ensure we contextualize deserializer before passing it on
+ // As per [databind#462] need to ensure we contextualize deserializer before passing it on
deser = ctxt.handlePrimaryContextualization(deser, prop, type);
prop = prop.withValueDeserializer(deser);
}
@@ -1108,16 +1102,16 @@
// First: is there annotation-specified deserializer for values?
@SuppressWarnings("unchecked")
JsonDeserializer<Object> contentDeser = (JsonDeserializer<Object>) contentType.getValueHandler();
-
+
// Ok: need a key deserializer (null indicates 'default' here)
KeyDeserializer keyDes = (KeyDeserializer) keyType.getValueHandler();
- // Then optional type info (1.5); either attached to type, or resolved separately:
+ // Then optional type info; either attached to type, or resolved separately:
TypeDeserializer contentTypeDeser = contentType.getTypeHandler();
// but if not, may still be possible to find:
if (contentTypeDeser == null) {
contentTypeDeser = findTypeDeserializer(config, contentType);
}
-
+
// 23-Nov-2010, tatu: Custom deserializer?
JsonDeserializer<?> deser = _findCustomMapDeserializer(type, config, beanDesc,
keyDes, contentTypeDeser, contentDeser);
@@ -1154,7 +1148,7 @@
// But if so, also need to re-check creators...
beanDesc = config.introspectForCreation(type);
} else {
- // [Issue#292]: Actually, may be fine, but only if polymorphich deser enabled
+ // [databind#292]: Actually, may be fine, but only if polymorphic deser enabled
if (type.getTypeHandler() == null) {
throw new IllegalArgumentException("Can not find a deserializer for non-concrete Map type "+type);
}
@@ -1163,14 +1157,20 @@
}
if (deser == null) {
ValueInstantiator inst = findValueInstantiator(ctxt, beanDesc);
+ // 01-May-2016, tatu: Which base type to use here gets tricky, since
+ // most often it ought to be `Map` or `EnumMap`, but due to abstract
+ // mapping it will more likely be concrete type like `HashMap`.
+ // So, for time being, just pass `Map.class`
MapDeserializer md = new MapDeserializer(type, inst, keyDes, contentDeser, contentTypeDeser);
- AnnotationIntrospector ai = config.getAnnotationIntrospector();
- md.setIgnorableProperties(ai.findPropertiesToIgnore(beanDesc.getClassInfo(), false));
+ JsonIgnoreProperties.Value ignorals = config.getDefaultPropertyIgnorals(Map.class,
+ beanDesc.getClassInfo());
+ Set<String> ignored = (ignorals == null) ? null
+ : ignorals.findIgnoredForDeserialization();
+ md.setIgnorableProperties(ignored);
deser = md;
}
}
}
- // and then new with 2.2: ability to post-process it too (Issue#120)
if (_factoryConfig.hasDeserializerModifiers()) {
for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) {
deser = mod.modifyMapDeserializer(config, type, beanDesc, deser);
@@ -1237,23 +1237,27 @@
final Class<?> enumClass = type.getRawClass();
// 23-Nov-2010, tatu: Custom deserializer?
JsonDeserializer<?> deser = _findCustomEnumDeserializer(enumClass, config, beanDesc);
+
if (deser == null) {
+ ValueInstantiator valueInstantiator = _constructDefaultValueInstantiator(ctxt, beanDesc);
+ SettableBeanProperty[] creatorProps = (valueInstantiator == null) ? null
+ : valueInstantiator.getFromObjectArguments(ctxt.getConfig());
// May have @JsonCreator for static factory method:
for (AnnotatedMethod factory : beanDesc.getFactoryMethods()) {
if (ctxt.getAnnotationIntrospector().hasCreatorAnnotation(factory)) {
- int argCount = factory.getParameterCount();
- if (argCount == 1) {
- Class<?> returnType = factory.getRawReturnType();
- // usually should be class, but may be just plain Enum<?> (for Enum.valueOf()?)
- if (returnType.isAssignableFrom(enumClass)) {
- deser = EnumDeserializer.deserializerForCreator(config, enumClass, factory);
- break;
- }
+ if (factory.getParameterCount() == 0) { // [databind#960]
+ deser = EnumDeserializer.deserializerForNoArgsCreator(config, enumClass, factory);
+ break;
}
- throw new IllegalArgumentException("Unsuitable method ("+factory+") decorated with @JsonCreator (for Enum type "
- +enumClass.getName()+")");
+ Class<?> returnType = factory.getRawReturnType();
+ // usually should be class, but may be just plain Enum<?> (for Enum.valueOf()?)
+ if (returnType.isAssignableFrom(enumClass)) {
+ deser = EnumDeserializer.deserializerForCreator(config, enumClass, factory, valueInstantiator, creatorProps);
+ break;
+ }
}
}
+
// Need to consider @JsonValue if one found
if (deser == null) {
deser = new EnumDeserializer(constructEnumResolver(enumClass,
@@ -1306,9 +1310,7 @@
if (deser == null) {
// Just one referential type as of JDK 1.7 / Java 7: AtomicReference (Java 8 adds Optional)
if (AtomicReference.class.isAssignableFrom(type.getRawClass())) {
- // 19-Apr-2016, tatu: By default we'd get something that expect to see an
- // AtomicReference... but what we need is something else, so...
- return new AtomicReferenceDeserializer(contentType, contentTypeDeser, contentDeser);
+ return new AtomicReferenceDeserializer(type, contentTypeDeser, contentDeser);
}
}
if (deser != null) {
@@ -1559,7 +1561,7 @@
return StringDeserializer.instance;
}
if (rawType == CLASS_ITERABLE) {
- // [Issue#199]: Can and should 'upgrade' to a Collection type:
+ // [databind#199]: Can and should 'upgrade' to a Collection type:
TypeFactory tf = ctxt.getTypeFactory();
JavaType[] tps = tf.findTypeParameters(type, CLASS_ITERABLE);
JavaType elemType = (tps == null || tps.length != 1) ? TypeFactory.unknownType() : tps[0];
@@ -1758,18 +1760,24 @@
/**
* Helper method called to check if a class or method
- * has annotation that tells which class to use for deserialization.
+ * has annotation that tells which class to use for deserialization; and if
+ * so, to instantiate, that deserializer to use.
+ * Note that deserializer will NOT yet be contextualized so caller needs to
+ * take care to call contextualization appropriately.
* Returns null if no such annotation found.
*/
protected JsonDeserializer<Object> findDeserializerFromAnnotation(DeserializationContext ctxt,
Annotated ann)
throws JsonMappingException
{
- Object deserDef = ctxt.getAnnotationIntrospector().findDeserializer(ann);
- if (deserDef == null) {
- return null;
+ AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
+ if (intr != null) {
+ Object deserDef = intr.findDeserializer(ann);
+ if (deserDef != null) {
+ return ctxt.deserializerInstance(ann, deserDef);
+ }
}
- return ctxt.deserializerInstance(ann, deserDef);
+ return null;
}
/**
@@ -1778,35 +1786,109 @@
* Returns null if no such annotation found.
*/
protected KeyDeserializer findKeyDeserializerFromAnnotation(DeserializationContext ctxt,
- Annotated ann)
+ Annotated ann)
throws JsonMappingException
{
- Object deserDef = ctxt.getAnnotationIntrospector().findKeyDeserializer(ann);
- if (deserDef == null) {
- return null;
+ AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
+ if (intr != null) {
+ Object deserDef = intr.findKeyDeserializer(ann);
+ if (deserDef != null) {
+ return ctxt.keyDeserializerInstance(ann, deserDef);
+ }
}
- return ctxt.keyDeserializerInstance(ann, deserDef);
+ return null;
}
/**
+ * Helper method used to resolve additional type-related annotation information
+ * like type overrides, or handler (serializer, deserializer) overrides,
+ * so that from declared field, property or constructor parameter type
+ * is used as the base and modified based on annotations, if any.
+ *
+ * @since 2.8 Combines functionality of <code>modifyTypeByAnnotation</code>
+ * and <code>resolveType</code>
+ */
+ protected JavaType resolveMemberAndTypeAnnotations(DeserializationContext ctxt,
+ AnnotatedMember member, JavaType type)
+ throws JsonMappingException
+ {
+ AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
+ if (intr == null) {
+ return type;
+ }
+
+ // First things first: see if we can find annotations on declared
+ // type
+
+ if (type.isMapLikeType()) {
+ JavaType keyType = type.getKeyType();
+ if (keyType != null) {
+ Object kdDef = intr.findKeyDeserializer(member);
+ KeyDeserializer kd = ctxt.keyDeserializerInstance(member, kdDef);
+ if (kd != null) {
+ type = ((MapLikeType) type).withKeyValueHandler(kd);
+ keyType = type.getKeyType(); // just in case it's used below
+ }
+ }
+ }
+
+ if (type.hasContentType()) { // that is, is either container- or reference-type
+ Object cdDef = intr.findContentDeserializer(member);
+ JsonDeserializer<?> cd = ctxt.deserializerInstance(member, cdDef);
+ if (cd != null) {
+ type = type.withContentValueHandler(cd);
+ }
+ TypeDeserializer contentTypeDeser = findPropertyContentTypeDeserializer(
+ ctxt.getConfig(), type, (AnnotatedMember) member);
+ if (contentTypeDeser != null) {
+ type = type.withContentTypeHandler(contentTypeDeser);
+ }
+ }
+ TypeDeserializer valueTypeDeser = findPropertyTypeDeserializer(ctxt.getConfig(),
+ type, (AnnotatedMember) member);
+ if (valueTypeDeser != null) {
+ type = type.withTypeHandler(valueTypeDeser);
+ }
+
+ // Second part: find actual type-override annotations on member, if any
+
+ // 18-Jun-2016, tatu: Should we re-do checks for annotations on refined
+ // subtypes as well? Code pre-2.8 did not do this, but if we get bug
+ // reports may need to consider
+ type = intr.refineDeserializationType(ctxt.getConfig(), member, type);
+ return type;
+ }
+
+ protected EnumResolver constructEnumResolver(Class<?> enumClass,
+ DeserializationConfig config, AnnotatedMethod jsonValueMethod)
+ {
+ if (jsonValueMethod != null) {
+ Method accessor = jsonValueMethod.getAnnotated();
+ if (config.canOverrideAccessModifiers()) {
+ ClassUtil.checkAndFixAccess(accessor, config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
+ }
+ return EnumResolver.constructUnsafeUsingMethod(enumClass, accessor, config.getAnnotationIntrospector());
+ }
+ // 14-Mar-2016, tatu: We used to check `DeserializationFeature.READ_ENUMS_USING_TO_STRING`
+ // here, but that won't do: it must be dynamically changeable...
+ return EnumResolver.constructUnsafe(enumClass, config.getAnnotationIntrospector());
+ }
+
+ /*
+ /**********************************************************
+ /* Deprecated helper methods
+ /**********************************************************
+ */
+
+ /**
* Method called to see if given method has annotations that indicate
* a more specific type than what the argument specifies.
- * If annotations are present, they must specify compatible Class;
- * instance of which can be assigned using the method. This means
- * that the Class has to be raw class of type, or its sub-class
- * (or, implementing class if original Class instance is an interface).
*
- * @param a Method or field that the type is associated with
- * @param type Type of field, or the setter argument
- *
- * @return Original type if no annotations are present; or a more
- * specific type derived from it if type annotation(s) was found
- *
- * @throws JsonMappingException if invalid annotation is found
+ * @deprecated Since 2.8; call {@link #resolveMemberAndTypeAnnotations} instead
*/
- @SuppressWarnings({ "unchecked" })
- protected <T extends JavaType> T modifyTypeByAnnotation(DeserializationContext ctxt,
- Annotated a, T type)
+ @Deprecated
+ protected JavaType modifyTypeByAnnotation(DeserializationContext ctxt,
+ Annotated a, JavaType type)
throws JsonMappingException
{
AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
@@ -1815,6 +1897,7 @@
}
// First, deserializers for key/value types?
+ /*
if (type.isMapLikeType()) {
JavaType keyType = type.getKeyType();
// 21-Mar-2011, tatu: ... and associated deserializer too (unless already assigned)
@@ -1840,90 +1923,26 @@
}
}
}
+ */
// then: type refinement(s)?
- type = (T) intr.refineDeserializationType(ctxt.getConfig(), a, type);
- return type;
+ return intr.refineDeserializationType(ctxt.getConfig(), a, type);
}
/**
- * Helper method used to resolve method return types and field
- * types. The main trick here is that the containing bean may
- * have type variable binding information (when deserializing
- * using generic type passed as type reference), which is
- * needed in some cases.
+ * @deprecated since 2.8 call {@link #resolveMemberAndTypeAnnotations} instead.
*/
+ @Deprecated // since 2.8
protected JavaType resolveType(DeserializationContext ctxt,
BeanDescription beanDesc, JavaType type, AnnotatedMember member)
throws JsonMappingException
{
- AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
- if (intr == null) {
- return type;
- }
-
- // Also need to handle keyUsing, contentUsing
-
- if (type.isMapLikeType()) {
- JavaType keyType = type.getKeyType();
- if (keyType != null) {
- Object kdDef = intr.findKeyDeserializer(member);
- KeyDeserializer kd = ctxt.keyDeserializerInstance(member, kdDef);
- if (kd != null) {
- type = ((MapLikeType) type).withKeyValueHandler(kd);
- keyType = type.getKeyType(); // just in case it's used below
- }
- }
- }
-
- if (type.isContainerType() || type.isReferenceType()) {
- Object cdDef = intr.findContentDeserializer(member);
- JsonDeserializer<?> cd = ctxt.deserializerInstance(member, cdDef);
- if (cd != null) {
- type = type.withContentValueHandler(cd);
- }
- /* 04-Feb-2010, tatu: Need to figure out JAXB annotations that indicate type
- * information to use for polymorphic members; and specifically types for
- * collection values (contents).
- * ... but only applies to members (fields, methods), not classes
- */
- if (member instanceof AnnotatedMember) {
- TypeDeserializer contentTypeDeser = findPropertyContentTypeDeserializer(
- ctxt.getConfig(), type, (AnnotatedMember) member);
- if (contentTypeDeser != null) {
- type = type.withContentTypeHandler(contentTypeDeser);
- }
- }
- }
- TypeDeserializer valueTypeDeser;
-
- if (member instanceof AnnotatedMember) { // JAXB allows per-property annotations
- valueTypeDeser = findPropertyTypeDeserializer(ctxt.getConfig(),
- type, (AnnotatedMember) member);
- } else { // classes just have Jackson annotations
- // probably only occurs if 'property' is null anyway
- valueTypeDeser = findTypeDeserializer(ctxt.getConfig(), type);
- }
- if (valueTypeDeser != null) {
- type = type.withTypeHandler(valueTypeDeser);
- }
- return type;
- }
-
- protected EnumResolver constructEnumResolver(Class<?> enumClass,
- DeserializationConfig config, AnnotatedMethod jsonValueMethod)
- {
- if (jsonValueMethod != null) {
- Method accessor = jsonValueMethod.getAnnotated();
- if (config.canOverrideAccessModifiers()) {
- ClassUtil.checkAndFixAccess(accessor, config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
- }
- return EnumResolver.constructUnsafeUsingMethod(enumClass, accessor);
- }
- // 14-Mar-2016, tatu: We used to check `DeserializationFeature.READ_ENUMS_USING_TO_STRING`
- // here, but that won't do: it must be dynamically changeable...
- return EnumResolver.constructUnsafe(enumClass, config.getAnnotationIntrospector());
+ return resolveMemberAndTypeAnnotations(ctxt, member, type);
}
+ /**
+ * @deprecated since 2.8 call <code>findJsonValueMethod</code> on {@link BeanDescription} instead
+ */
+ @Deprecated // not used, possibly remove as early as 2.9
protected AnnotatedMethod _findJsonValueFor(DeserializationConfig config, JavaType enumType)
{
if (enumType == null) {
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java
index 0e2b874..1960d57 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java
@@ -6,6 +6,7 @@
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.impl.*;
+import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId.Referring;
import com.fasterxml.jackson.databind.util.NameTransformer;
import com.fasterxml.jackson.databind.util.TokenBuffer;
@@ -30,6 +31,14 @@
private static final long serialVersionUID = 1L;
+ /**
+ * Lazily constructed exception used as root cause if reporting problem
+ * with creator method that returns <code>null</code> (which is not allowed)
+ *
+ * @since 3.8
+ */
+ protected transient Exception _nullFromCreator;
+
/*
/**********************************************************
/* Life-cycle, construction, initialization
@@ -68,10 +77,14 @@
super(src, oir);
}
- public BeanDeserializer(BeanDeserializerBase src, HashSet<String> ignorableProps) {
+ public BeanDeserializer(BeanDeserializerBase src, Set<String> ignorableProps) {
super(src, ignorableProps);
}
+ public BeanDeserializer(BeanDeserializerBase src, BeanPropertyMap props) {
+ super(src, props);
+ }
+
@Override
public JsonDeserializer<Object> unwrappingDeserializer(NameTransformer unwrapper)
{
@@ -94,16 +107,21 @@
}
@Override
- public BeanDeserializer withIgnorableProperties(HashSet<String> ignorableProps) {
+ public BeanDeserializer withIgnorableProperties(Set<String> ignorableProps) {
return new BeanDeserializer(this, ignorableProps);
}
@Override
+ public BeanDeserializerBase withBeanProperties(BeanPropertyMap props) {
+ return new BeanDeserializer(this, props);
+ }
+
+ @Override
protected BeanDeserializerBase asArrayDeserializer() {
SettableBeanProperty[] props = _beanProperties.getPropertiesInInsertionOrder();
return new BeanAsArrayDeserializer(this, props);
}
-
+
/*
/**********************************************************
/* JsonDeserializer implementation
@@ -112,9 +130,6 @@
/**
* Main deserialization method for bean-based objects (POJOs).
- *<p>
- * NOTE: was declared 'final' in 2.2; should NOT be to let extensions
- * like Afterburner change definition.
*/
@Override
public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
@@ -168,9 +183,10 @@
return deserializeFromObject(p, ctxt);
default:
}
- throw ctxt.mappingException(handledType());
+ return ctxt.handleUnexpectedToken(handledType(), p);
}
+ @Deprecated // since 2.8; remove unless getting used
protected Object _missingToken(JsonParser p, DeserializationContext ctxt) throws IOException {
throw ctxt.endOfInputException(handledType());
}
@@ -366,10 +382,10 @@
final PropertyBasedCreator creator = _propertyBasedCreator;
PropertyValueBuffer buffer = creator.startBuilding(p, ctxt, _objectIdReader);
- // 04-Jan-2010, tatu: May need to collect unknown properties for polymorphic cases
TokenBuffer unknown = null;
JsonToken t = p.getCurrentToken();
+ List<BeanReferring> referrings = null;
for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) {
String propName = p.getCurrentName();
p.nextToken(); // to point to value
@@ -384,11 +400,11 @@
try {
bean = creator.build(ctxt, buffer);
} catch (Exception e) {
- wrapInstantiationProblem(e, ctxt);
- bean = null; // never gets here
+ bean = wrapInstantiationProblem(e, ctxt);
}
if (bean == null) {
- throw ctxt.instantiationException(_beanType.getRawClass(), "JSON Creator returned null");
+ return ctxt.handleInstantiationProblem(handledType(), null,
+ _creatorReturnedNullException());
}
// [databind#631]: Assign current value, to be accessible by custom serializers
p.setCurrentValue(bean);
@@ -412,11 +428,22 @@
// regular property? needs buffering
SettableBeanProperty prop = _beanProperties.find(propName);
if (prop != null) {
- buffer.bufferProperty(prop, _deserializeWithErrorWrapping(p, ctxt, prop));
+ try {
+ buffer.bufferProperty(prop, _deserializeWithErrorWrapping(p, ctxt, prop));
+ } catch (UnresolvedForwardReference reference) {
+ // 14-Jun-2016, tatu: As per [databind#1261], looks like we need additional
+ // handling of forward references here. Not exactly sure why existing
+ // facilities did not cover, but this does appear to solve the problem
+ BeanReferring referring = handleUnresolvedReference(ctxt,
+ prop, buffer, reference);
+ if (referrings == null) {
+ referrings = new ArrayList<BeanReferring>();
+ }
+ referrings.add(referring);
+ }
continue;
}
- // As per [JACKSON-313], things marked as ignorable should not be
- // passed to any setter
+ // Things marked as ignorable should not be passed to any setter
if (_ignorableProps != null && _ignorableProps.contains(propName)) {
handleIgnoredProperty(p, ctxt, handledType(), propName);
continue;
@@ -446,6 +473,11 @@
wrapInstantiationProblem(e, ctxt);
bean = null; // never gets here
}
+ if (referrings != null) {
+ for (BeanReferring referring : referrings) {
+ referring.setBean(bean);
+ }
+ }
if (unknown != null) {
// polymorphic?
if (bean.getClass() != _beanType.getRawClass()) {
@@ -457,6 +489,20 @@
return bean;
}
+ /**
+ * @since 2.8
+ */
+ private BeanReferring handleUnresolvedReference(DeserializationContext ctxt,
+ SettableBeanProperty prop, PropertyValueBuffer buffer,
+ UnresolvedForwardReference reference)
+ throws JsonMappingException
+ {
+ BeanReferring referring = new BeanReferring(ctxt, reference,
+ prop.getType(), buffer, prop);
+ reference.getRoid().appendReferring(referring);
+ return referring;
+ }
+
protected final Object _deserializeWithErrorWrapping(JsonParser p,
DeserializationContext ctxt, SettableBeanProperty prop)
throws IOException
@@ -496,9 +542,9 @@
p2.close();
return ob;
}
- throw ctxt.mappingException(handledType());
+ return ctxt.handleUnexpectedToken(handledType(), p);
}
-
+
/*
/**********************************************************
/* Deserializing when we have to consider an active View
@@ -672,8 +718,7 @@
try {
bean = creator.build(ctxt, buffer);
} catch (Exception e) {
- wrapInstantiationProblem(e, ctxt);
- continue; // never gets here
+ bean = wrapInstantiationProblem(e, ctxt);
}
// [databind#631]: Assign current value, to be accessible by custom serializers
p.setCurrentValue(bean);
@@ -688,7 +733,8 @@
// !!! 08-Jul-2011, tatu: Could probably support; but for now
// it's too complicated, so bail out
tokens.close();
- throw ctxt.mappingException("Can not create polymorphic instances with unwrapped values");
+ ctxt.reportMappingException("Can not create polymorphic instances with unwrapped values");
+ return null;
}
return _unwrappedPropertyHandler.processUnwrapped(p, ctxt, bean, tokens);
}
@@ -853,7 +899,8 @@
if (bean.getClass() != _beanType.getRawClass()) {
// !!! 08-Jul-2011, tatu: Could theoretically support; but for now
// it's too complicated, so bail out
- throw ctxt.mappingException("Can not create polymorphic instances with unwrapped values");
+ ctxt.reportMappingException("Can not create polymorphic instances with external type ids");
+ return null;
}
return ext.complete(p, ctxt, bean);
}
@@ -889,8 +936,53 @@
try {
return ext.complete(p, ctxt, buffer, creator);
} catch (Exception e) {
- wrapInstantiationProblem(e, ctxt);
- return null; // never gets here
+ return wrapInstantiationProblem(e, ctxt);
+ }
+ }
+
+ /**
+ * Helper method for getting a lazily construct exception to be reported
+ * to {@link DeserializationContext#handleInstantiationProblem(Class, Object, Throwable)}.
+ *
+ * @since 2.8
+ */
+ protected Exception _creatorReturnedNullException() {
+ if (_nullFromCreator == null) {
+ _nullFromCreator = new NullPointerException("JSON Creator returned null");
+ }
+ return _nullFromCreator;
+ }
+
+ /**
+ * @since 2.8
+ */
+ static class BeanReferring extends Referring
+ {
+ private final DeserializationContext _context;
+ private final SettableBeanProperty _prop;
+ private Object _bean;
+
+ BeanReferring(DeserializationContext ctxt, UnresolvedForwardReference ref,
+ JavaType valueType, PropertyValueBuffer buffer, SettableBeanProperty prop)
+ {
+ super(ref, valueType);
+ _context = ctxt;
+ _prop = prop;
+ }
+
+ public void setBean(Object bean) {
+ _bean = bean;
+ }
+
+ @Override
+ public void handleResolvedForwardReference(Object id, Object value) throws IOException
+ {
+ if (_bean == null) {
+ _context.reportMappingException(
+"Can not resolve ObjectId forward reference using property '%s' (of type %s): Bean not yet resolved",
+_prop.getName(), _prop.getDeclaringClass().getName());
+ }
+ _prop.set(_bean, value);
}
}
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java
index 48cadfc..a82a159 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java
@@ -6,6 +6,7 @@
import java.util.*;
import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerator;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
@@ -139,7 +140,7 @@
* track of recognized but ignorable properties: these will
* be skipped without errors or warnings.
*/
- final protected HashSet<String> _ignorableProps;
+ final protected Set<String> _ignorableProps;
/**
* Flag that can be set to ignore and skip unknown properties.
@@ -206,7 +207,7 @@
protected BeanDeserializerBase(BeanDeserializerBuilder builder,
BeanDescription beanDesc,
BeanPropertyMap properties, Map<String, SettableBeanProperty> backRefs,
- HashSet<String> ignorableProps, boolean ignoreAllUnknown,
+ Set<String> ignorableProps, boolean ignoreAllUnknown,
boolean hasViews)
{
super(beanDesc.getType());
@@ -354,7 +355,7 @@
}
}
- public BeanDeserializerBase(BeanDeserializerBase src, HashSet<String> ignorableProps)
+ public BeanDeserializerBase(BeanDeserializerBase src, Set<String> ignorableProps)
{
super(src._beanType);
@@ -378,7 +379,40 @@
_vanillaProcessing = src._vanillaProcessing;
_objectIdReader = src._objectIdReader;
- _beanProperties = src._beanProperties;
+
+ // 01-May-2016, tatu: [databind#1217]: Remove properties from mapping,
+ // to avoid them being deserialized
+ _beanProperties = src._beanProperties.withoutProperties(ignorableProps);
+ }
+
+ /**
+ * @since 2.8
+ */
+ protected BeanDeserializerBase(BeanDeserializerBase src, BeanPropertyMap beanProps)
+ {
+ super(src._beanType);
+
+ _classAnnotations = src._classAnnotations;
+ _beanType = src._beanType;
+
+ _valueInstantiator = src._valueInstantiator;
+ _delegateDeserializer = src._delegateDeserializer;
+ _propertyBasedCreator = src._propertyBasedCreator;
+
+ _beanProperties = beanProps;
+ _backRefs = src._backRefs;
+ _ignorableProps = src._ignorableProps;
+ _ignoreAllUnknown = src._ignoreAllUnknown;
+ _anySetter = src._anySetter;
+ _injectables = src._injectables;
+ _objectIdReader = src._objectIdReader;
+
+ _nonStandardCreation = src._nonStandardCreation;
+ _unwrappedPropertyHandler = src._unwrappedPropertyHandler;
+ _needViewProcesing = src._needViewProcesing;
+ _serializationShape = src._serializationShape;
+
+ _vanillaProcessing = src._vanillaProcessing;
}
@Override
@@ -386,9 +420,20 @@
public abstract BeanDeserializerBase withObjectIdReader(ObjectIdReader oir);
- public abstract BeanDeserializerBase withIgnorableProperties(HashSet<String> ignorableProps);
+ public abstract BeanDeserializerBase withIgnorableProperties(Set<String> ignorableProps);
/**
+ * Mutant factory method that custom sub-classes must override; not left as
+ * abstract to prevent more drastic backwards compatibility problems.
+ *
+ * @since 2.8
+ */
+ public BeanDeserializerBase withBeanProperties(BeanPropertyMap props) {
+ throw new UnsupportedOperationException("Class "+getClass().getName()
+ +" does not override `withBeanProperties()`, needs to");
+ }
+
+ /**
* Fluent factory for creating a variant that can handle
* POJO output as a JSON Array. Implementations may ignore this request
* if no such input is possible.
@@ -415,8 +460,16 @@
ExternalTypeHandler.Builder extTypes = null;
// if ValueInstantiator can use "creator" approach, need to resolve it here...
SettableBeanProperty[] creatorProps;
+
if (_valueInstantiator.canCreateFromObjectWith()) {
creatorProps = _valueInstantiator.getFromObjectArguments(ctxt.getConfig());
+
+ // 21-Jun-2015, tatu: This resolution was duplicated later on and seems like
+ // it really should be only done at a later point. So commented out in 2.8.
+ // However, just in case there was a reason for it, leaving commented out
+ // here instead of immediately removing.
+
+ /*
// also: need to try to resolve 'external' type ids...
for (SettableBeanProperty prop : creatorProps) {
if (prop.hasValueTypeDeserializer()) {
@@ -429,6 +482,7 @@
}
}
}
+ */
} else {
creatorProps = null;
}
@@ -461,7 +515,7 @@
// Need to link managed references with matching back references
prop = _resolveManagedReferenceProperty(ctxt, prop);
- // [databind#351[: need to wrap properties that require object id resolution.
+ // [databind#351]: need to wrap properties that require object id resolution.
if (!(prop instanceof ManagedReferenceProperty)) {
prop = _resolvedObjectIdProperty(ctxt, prop);
}
@@ -518,13 +572,11 @@
}
}
}
-
// "any setter" may also need to be resolved now
if (_anySetter != null && !_anySetter.hasValueDeserializer()) {
_anySetter = _anySetter.withValueDeserializer(findDeserializer(ctxt,
_anySetter.getType(), _anySetter.getProperty()));
}
-
// as well as delegate-based constructor:
if (_valueInstantiator.canCreateUsingDelegate()) {
JavaType delegateType = _valueInstantiator.getDelegateType(ctxt.getConfig());
@@ -555,7 +607,9 @@
}
if (extTypes != null) {
- _externalTypeIdHandler = extTypes.build();
+ // 21-Jun-2016, tatu: related to [databind#999], may need to link type ids too,
+ // so need to pass collected properties
+ _externalTypeIdHandler = extTypes.build(_beanProperties);
// we consider this non-standard, to offline handling
_nonStandardCreation = true;
}
@@ -669,22 +723,39 @@
}
// And possibly add more properties to ignore
if (accessor != null) {
- String[] ignorals = intr.findPropertiesToIgnore(accessor, false);
- if (ignorals != null && ignorals.length != 0) {
- HashSet<String> newIgnored = ArrayBuilders.setAndArray(contextual._ignorableProps, ignorals);
- contextual = contextual.withIgnorableProperties(newIgnored);
+ JsonIgnoreProperties.Value ignorals = intr.findPropertyIgnorals(accessor);
+ if (ignorals != null) {
+ Set<String> ignored = ignorals.findIgnoredForDeserialization();
+ if (!ignored.isEmpty()) {
+ Set<String> prev = contextual._ignorableProps;
+ if ((prev != null) && !prev.isEmpty()) {
+ ignored = new HashSet<String>(ignored);
+ ignored.addAll(prev);
+ }
+ contextual = contextual.withIgnorableProperties(ignored);
+ }
}
}
// One more thing: are we asked to serialize POJO as array?
+ JsonFormat.Value format = findFormatOverrides(ctxt, property, handledType());
JsonFormat.Shape shape = null;
- if (accessor != null) {
- JsonFormat.Value format = intr.findFormat((Annotated) accessor);
-
- if (format != null) {
+ if (format != null) {
+ if (format.hasShape()) {
shape = format.getShape();
}
+ // 16-May-2016, tatu: How about per-property case-insensitivity?
+ Boolean B = format.getFeature(JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);
+ if (B != null) {
+ // !!! TODO
+ BeanPropertyMap propsOrig = _beanProperties;
+ BeanPropertyMap props = propsOrig.withCaseInsensitivity(B.booleanValue());
+ if (props != propsOrig) {
+ contextual = contextual.withBeanProperties(props);
+ }
+ }
}
+
if (shape == null) {
shape = _serializationShape;
}
@@ -729,7 +800,7 @@
* in case where object id resolution is required.
*/
protected SettableBeanProperty _resolvedObjectIdProperty(DeserializationContext ctxt,
- SettableBeanProperty prop)
+ SettableBeanProperty prop) throws JsonMappingException
{
ObjectIdInfo objectIdInfo = prop.getObjectIdInfo();
JsonDeserializer<Object> valueDeser = prop.getValueDeserializer();
@@ -1073,7 +1144,9 @@
} else {
// should we worry about UUIDs? They should be fine, right?
// 07-Aug-2014, tatu: Maybe, but not necessarily; had issues with
- // Smile format; [Smile#19], possibly related.
+ // Smile format; [dataformat-smile#19], possibly related.
+ // 01-Sep-2016, tatu: For non-JSON, might want to consider `writeEmbeddedObject`
+ // but that won't work for default impl (JSON and most dataformats)
buf.writeObject(rawId);
}
JsonParser bufParser = buf.asParser();
@@ -1124,11 +1197,11 @@
}
// should only occur for abstract types...
if (_beanType.isAbstract()) {
- throw JsonMappingException.from(p, "Can not instantiate abstract type "+_beanType
- +" (need to add/enable type information?)");
+ return ctxt.handleMissingInstantiator(handledType(), p,
+ "abstract type (need to add/enable type information?)");
}
- throw JsonMappingException.from(p, "No suitable constructor found for type "
- +_beanType+": can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)");
+ return ctxt.handleMissingInstantiator(_beanType.getRawClass(), p,
+ "no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?)");
}
protected abstract Object _deserializeUsingPropertyBased(final JsonParser p,
@@ -1147,7 +1220,8 @@
case INT:
if (_delegateDeserializer != null) {
if (!_valueInstantiator.canCreateFromInt()) {
- Object bean = _valueInstantiator.createUsingDelegate(ctxt, _delegateDeserializer.deserialize(p, ctxt));
+ Object bean = _valueInstantiator.createUsingDelegate(ctxt,
+ _delegateDeserializer.deserialize(p, ctxt));
if (_injectables != null) {
injectValues(ctxt, bean);
}
@@ -1158,7 +1232,8 @@
case LONG:
if (_delegateDeserializer != null) {
if (!_valueInstantiator.canCreateFromInt()) {
- Object bean = _valueInstantiator.createUsingDelegate(ctxt, _delegateDeserializer.deserialize(p, ctxt));
+ Object bean = _valueInstantiator.createUsingDelegate(ctxt,
+ _delegateDeserializer.deserialize(p, ctxt));
if (_injectables != null) {
injectValues(ctxt, bean);
}
@@ -1169,13 +1244,16 @@
}
// actually, could also be BigInteger, so:
if (_delegateDeserializer != null) {
- Object bean = _valueInstantiator.createUsingDelegate(ctxt, _delegateDeserializer.deserialize(p, ctxt));
+ Object bean = _valueInstantiator.createUsingDelegate(ctxt,
+ _delegateDeserializer.deserialize(p, ctxt));
if (_injectables != null) {
injectValues(ctxt, bean);
}
return bean;
}
- throw ctxt.instantiationException(handledType(), "no suitable creator method found to deserialize from JSON integer number");
+ return ctxt.handleMissingInstantiator(handledType(), p,
+ "no suitable creator method found to deserialize from Number value (%s)",
+ p.getNumberValue());
}
public Object deserializeFromString(JsonParser p, DeserializationContext ctxt) throws IOException
@@ -1224,7 +1302,9 @@
if (_delegateDeserializer != null) {
return _valueInstantiator.createUsingDelegate(ctxt, _delegateDeserializer.deserialize(p, ctxt));
}
- throw ctxt.instantiationException(handledType(), "no suitable creator method found to deserialize from JSON floating-point number");
+ return ctxt.handleMissingInstantiator(handledType(), p,
+ "no suitable creator method found to deserialize from Number value (%s)",
+ p.getNumberValue());
}
/**
@@ -1255,7 +1335,7 @@
}
return bean;
} catch (Exception e) {
- wrapInstantiationProblem(e, ctxt);
+ return wrapInstantiationProblem(e, ctxt);
}
}
// fallback to non-array delegate
@@ -1268,6 +1348,7 @@
return bean;
} catch (Exception e) {
wrapInstantiationProblem(e, ctxt);
+ return null;
}
}
if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
@@ -1277,8 +1358,7 @@
}
final Object value = deserialize(p, ctxt);
if (p.nextToken() != JsonToken.END_ARRAY) {
- throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY,
- "Attempted to unwrap single value array for single '" + _valueClass.getName() + "' value but there was more than a single value in the array");
+ handleMissingEndArrayForSingle(p, ctxt);
}
return value;
}
@@ -1287,9 +1367,10 @@
if (t == JsonToken.END_ARRAY) {
return null;
}
- throw ctxt.mappingException(handledType(), JsonToken.START_ARRAY);
+ return ctxt.handleUnexpectedToken(handledType(),
+ JsonToken.START_ARRAY, p, null);
}
- throw ctxt.mappingException(handledType());
+ return ctxt.handleUnexpectedToken(handledType(), p);
}
public Object deserializeFromEmbedded(JsonParser p, DeserializationContext ctxt)
@@ -1544,7 +1625,7 @@
return t;
}
- protected void wrapInstantiationProblem(Throwable t, DeserializationContext ctxt)
+ protected Object wrapInstantiationProblem(Throwable t, DeserializationContext ctxt)
throws IOException
{
while (t instanceof InvocationTargetException && t.getCause() != null) {
@@ -1563,6 +1644,6 @@
throw (RuntimeException) t;
}
}
- throw ctxt.instantiationException(_beanType.getRawClass(), t);
+ return ctxt.handleInstantiationProblem(_beanType.getRawClass(), null, t);
}
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBuilder.java b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBuilder.java
index 6975148..5ca54d5 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBuilder.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBuilder.java
@@ -20,16 +20,23 @@
{
/*
/**********************************************************
+ /* Configuration
+ /**********************************************************
+ */
+
+ final protected DeserializationConfig _config;
+
+ /*
+ /**********************************************************
/* General information about POJO
/**********************************************************
*/
+ /**
+ * Introspected information about POJO for deserializer to handle
+ */
final protected BeanDescription _beanDesc;
- final protected boolean _defaultViewInclusion;
-
- final protected boolean _caseInsensitivePropertyComparison;
-
/*
/**********************************************************
/* Accumulated information about properties
@@ -63,6 +70,10 @@
*/
protected ValueInstantiator _valueInstantiator;
+ /**
+ * Handler for Object Id values, if Object Ids are enabled for the
+ * bean type.
+ */
protected ObjectIdReader _objectIdReader;
/**
@@ -88,7 +99,7 @@
* In addition, Builder may have additional configuration
*/
protected JsonPOJOBuilder.Value _builderConfig;
-
+
/*
/**********************************************************
/* Life-cycle: construction
@@ -99,8 +110,7 @@
DeserializationConfig config)
{
_beanDesc = beanDesc;
- _defaultViewInclusion = config.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION);
- _caseInsensitivePropertyComparison = config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);
+ _config = config;
}
/**
@@ -110,8 +120,7 @@
protected BeanDeserializerBuilder(BeanDeserializerBuilder src)
{
_beanDesc = src._beanDesc;
- _defaultViewInclusion = src._defaultViewInclusion;
- _caseInsensitivePropertyComparison = src._caseInsensitivePropertyComparison;
+ _config = src._config;
// let's make copy of properties
_properties.putAll(src._properties);
@@ -174,6 +183,10 @@
if (_backRefProperties == null) {
_backRefProperties = new HashMap<String, SettableBeanProperty>(4);
}
+ // 15-Sep-2016, tatu: For some reason fixing access at point of `build()` does
+ // NOT work (2 failing unit tests). Not 100% clear why, but for now force
+ // access set early; unfortunate, but since it works....
+ prop.fixAccess(_config);
_backRefProperties.put(referenceName, prop);
// also: if we had property with same name, actually remove it
if (_properties != null) {
@@ -190,10 +203,15 @@
if (_injectables == null) {
_injectables = new ArrayList<ValueInjector>();
}
+ boolean fixAccess = _config.canOverrideAccessModifiers();
+ boolean forceAccess = fixAccess && _config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS);
+ if (fixAccess) {
+ member.fixAccess(forceAccess);
+ }
_injectables.add(new ValueInjector(propName, propType,
contextAnnotations, member, valueId));
}
-
+
/**
* Method that will add property name as one of properties that can
* be ignored if not recognized.
@@ -299,7 +317,7 @@
public JsonPOJOBuilder.Value getBuilderConfig() {
return _builderConfig;
}
-
+
/*
/**********************************************************
/* Build method(s)
@@ -309,21 +327,20 @@
/**
* Method for constructing a {@link BeanDeserializer}, given all
* information collected.
- *<p>
- * NOTE: Signature of this method did unfortunately change between Jackson 2.1
- * and Jackson 2.2
*/
public JsonDeserializer<?> build()
{
Collection<SettableBeanProperty> props = _properties.values();
- BeanPropertyMap propertyMap = BeanPropertyMap.construct(props, _caseInsensitivePropertyComparison);
+ _fixAccess(props);
+
+ BeanPropertyMap propertyMap = BeanPropertyMap.construct(props,
+ _config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES));
propertyMap.assignIndexes();
// view processing must be enabled if:
// (a) fields are not included by default (when deserializing with view), OR
// (b) one of properties has view(s) to included in defined
- boolean anyViews = !_defaultViewInclusion;
-
+ boolean anyViews = !_config.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION);
if (!anyViews) {
for (SettableBeanProperty prop : props) {
if (prop.hasViews()) {
@@ -342,7 +359,7 @@
ObjectIdValueProperty prop = new ObjectIdValueProperty(_objectIdReader, PropertyMetadata.STD_REQUIRED);
propertyMap = propertyMap.withProperty(prop);
}
-
+
return new BeanDeserializer(this,
_beanDesc, propertyMap, _backRefProperties, _ignorableProps, _ignoreAllUnknown,
anyViews);
@@ -370,8 +387,10 @@
if (_buildMethod == null) {
// as per [databind#777], allow empty name
if (!expBuildMethodName.isEmpty()) {
- throw new IllegalArgumentException("Builder class "+_beanDesc.getBeanClass().getName()
- +" does not have build method (name: '"+expBuildMethodName+"')");
+ throw new IllegalArgumentException(String.format(
+ "Builder class %s does not have build method (name: '%s')",
+ _beanDesc.getBeanClass().getName(),
+ expBuildMethodName));
}
} else {
// also: type of the method must be compatible
@@ -387,10 +406,12 @@
}
// And if so, we can try building the deserializer
Collection<SettableBeanProperty> props = _properties.values();
- BeanPropertyMap propertyMap = BeanPropertyMap.construct(props, _caseInsensitivePropertyComparison);
+ _fixAccess(props);
+ BeanPropertyMap propertyMap = BeanPropertyMap.construct(props,
+ _config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES));
propertyMap.assignIndexes();
- boolean anyViews = !_defaultViewInclusion;
+ boolean anyViews = !_config.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION);
if (!anyViews) {
for (SettableBeanProperty prop : props) {
@@ -415,4 +436,50 @@
_beanDesc, propertyMap, _backRefProperties, _ignorableProps, _ignoreAllUnknown,
anyViews);
}
+
+ /*
+ /**********************************************************
+ /* Internal helper method(s)
+ /**********************************************************
+ */
+
+ private void _fixAccess(Collection<SettableBeanProperty> mainProps)
+ {
+ /* 07-Sep-2016, tatu: Ideally we should be able to avoid forcing
+ * access to properties that are likely ignored, but due to
+ * renaming it seems this is not a safe thing to do (there was
+ * at least one failing test). May need to dig deeper in future;
+ * for now let's just play it safe.
+ */
+ /*
+ Set<String> ignorable = _ignorableProps;
+ if (ignorable == null) {
+ ignorable = Collections.emptySet();
+ }
+ */
+ for (SettableBeanProperty prop : mainProps) {
+ /*
+ // first: no point forcing access on to-be-ignored properties
+ if (ignorable.contains(prop.getName())) {
+ continue;
+ }
+ */
+ prop.fixAccess(_config);
+ }
+ // 15-Sep-2016, tatu: Access via back-ref properties has been done earlier
+ // as it has to, for some reason, so not repeated here.
+/*
+ if (_backRefProperties != null) {
+ for (SettableBeanProperty prop : _backRefProperties.values()) {
+ prop.fixAccess(_config);
+ }
+ }
+ */
+ if (_anySetter != null) {
+ _anySetter.fixAccess(_config);
+ }
+ if (_buildMethod != null) {
+ _buildMethod.fixAccess(_config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
+ }
+ }
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java
index 90a19c9..7815e53 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java
@@ -2,17 +2,16 @@
import java.util.*;
-import com.fasterxml.jackson.annotation.ObjectIdGenerator;
-import com.fasterxml.jackson.annotation.ObjectIdGenerators;
-import com.fasterxml.jackson.annotation.ObjectIdResolver;
+import com.fasterxml.jackson.annotation.*;
+
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig;
+import com.fasterxml.jackson.databind.cfg.ConfigOverride;
import com.fasterxml.jackson.databind.deser.impl.*;
import com.fasterxml.jackson.databind.deser.std.ThrowableDeserializer;
import com.fasterxml.jackson.databind.introspect.*;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
-import com.fasterxml.jackson.databind.util.ArrayBuilders;
import com.fasterxml.jackson.databind.util.ClassUtil;
import com.fasterxml.jackson.databind.util.SimpleBeanPropertyDefinition;
@@ -116,7 +115,7 @@
*/
// 29-Nov-2015, tatu: Also, filter out calls to primitive types, they are
// not something we could materialize anything for
- if (type.isAbstract() && !type.isPrimitive()) {
+ if (type.isAbstract() && !type.isPrimitive() && !type.isEnumType()) {
// Let's make it possible to materialize abstract types.
JavaType concreteType = materializeAbstractType(ctxt, type, beanDesc);
if (concreteType != null) {
@@ -127,7 +126,6 @@
return buildBeanDeserializer(ctxt, concreteType, beanDesc);
}
}
-
// Otherwise, may want to check handlers for standard types, from superclass:
@SuppressWarnings("unchecked")
JsonDeserializer<Object> deser = (JsonDeserializer<Object>) findStdDeserializer(ctxt, type, beanDesc);
@@ -220,7 +218,7 @@
try {
valueInstantiator = findValueInstantiator(ctxt, beanDesc);
} catch (NoClassDefFoundError error) {
- return new NoClassDefFoundDeserializer<Object>(error);
+ return new ErrorThrowingDeserializer(error);
}
BeanDeserializerBuilder builder = constructBeanDeserializerBuilder(ctxt, beanDesc);
builder.setValueInstantiator(valueInstantiator);
@@ -233,7 +231,6 @@
addInjectables(ctxt, beanDesc, builder);
final DeserializationConfig config = ctxt.getConfig();
- // [JACKSON-440]: update builder now that all information is in?
if (_factoryConfig.hasDeserializerModifiers()) {
for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) {
builder = mod.updateBuilder(config, beanDesc, builder);
@@ -448,27 +445,42 @@
builder.getValueInstantiator().getFromObjectArguments(ctxt.getConfig());
final boolean isConcrete = !beanDesc.getType().isAbstract();
- // Things specified as "ok to ignore"?
- AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
- if (intr != null) {
- Boolean B = intr.findIgnoreUnknownProperties(beanDesc.getClassInfo());
- if (B != null) {
- builder.setIgnoreUnknownProperties(B.booleanValue());
+ // 01-May-2016, tatu: Which base type to use here gets tricky, since
+ // it may often make most sense to use general type for overrides,
+ // but what we have here may be more specific impl type. But for now
+ // just use it as is.
+ JsonIgnoreProperties.Value ignorals = ctxt.getConfig()
+ .getDefaultPropertyIgnorals(beanDesc.getBeanClass(),
+ beanDesc.getClassInfo());
+ Set<String> ignored;
+
+ if (ignorals != null) {
+ boolean ignoreAny = ignorals.getIgnoreUnknown();
+ builder.setIgnoreUnknownProperties(ignoreAny);
+ // Or explicit/implicit definitions?
+ ignored = ignorals.getIgnored();
+ for (String propName : ignored) {
+ builder.addIgnorable(propName);
}
+ } else {
+ ignored = Collections.emptySet();
}
- // Or explicit/implicit definitions?
- Set<String> ignored = ArrayBuilders.arrayToSet(intr.findPropertiesToIgnore(beanDesc.getClassInfo(), false));
- for (String propName : ignored) {
- builder.addIgnorable(propName);
- }
+
// Also, do we have a fallback "any" setter?
- AnnotatedMethod anySetter = beanDesc.findAnySetter();
- if (anySetter != null) {
- builder.setAnySetter(constructAnySetter(ctxt, beanDesc, anySetter));
+ AnnotatedMethod anySetterMethod = beanDesc.findAnySetter();
+ AnnotatedMember anySetterField = null;
+ if (anySetterMethod != null) {
+ builder.setAnySetter(constructAnySetter(ctxt, beanDesc, anySetterMethod));
+ }
+ else {
+ anySetterField = beanDesc.findAnySetterField();
+ if(anySetterField != null) {
+ builder.setAnySetter(constructAnySetter(ctxt, beanDesc, anySetterField));
+ }
}
// NOTE: we do NOT add @JsonIgnore'd properties into blocked ones if there's any-setter
// Implicit ones via @JsonIgnore and equivalent?
- if (anySetter == null) {
+ if (anySetterMethod == null && anySetterField == null) {
Collection<String> ignored2 = beanDesc.getIgnoredPropertyNames();
if (ignored2 != null) {
for (String propName : ignored2) {
@@ -536,8 +548,9 @@
}
}
if (cprop == null) {
- throw ctxt.mappingException("Could not find creator property with name '%s' (in class %s)",
+ ctxt.reportMappingException("Could not find creator property with name '%s' (in class %s)",
name, beanDesc.getBeanClass().getName());
+ continue;
}
if (prop != null) {
cprop.setFallbackSetter(prop);
@@ -591,9 +604,9 @@
rawPropertyType = property.getField().getRawType();
}
- // [JACKSON-429] Some types are declared as ignorable as well
+ // Some types are declared as ignorable as well
if ((rawPropertyType != null)
- && (isIgnorableType(ctxt.getConfig(), beanDesc, rawPropertyType, ignoredTypes))) {
+ && isIgnorableType(ctxt.getConfig(), beanDesc, rawPropertyType, ignoredTypes)) {
// important: make ignorable, to avoid errors if value is actually seen
builder.addIgnorable(name);
continue;
@@ -626,8 +639,8 @@
}
SimpleBeanPropertyDefinition propDef = SimpleBeanPropertyDefinition.construct(
ctxt.getConfig(), m);
- builder.addBackReferenceProperty(name, constructSettableProperty(
- ctxt, beanDesc, propDef, type));
+ builder.addBackReferenceProperty(name, constructSettableProperty(ctxt,
+ beanDesc, propDef, type));
}
}
}
@@ -642,13 +655,8 @@
{
Map<Object, AnnotatedMember> raw = beanDesc.findInjectables();
if (raw != null) {
- boolean fixAccess = ctxt.canOverrideAccessModifiers();
- boolean forceAccess = fixAccess && ctxt.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS);
for (Map.Entry<Object, AnnotatedMember> entry : raw.entrySet()) {
AnnotatedMember m = entry.getValue();
- if (fixAccess) {
- m.fixAccess(forceAccess); // to ensure we can call it
- }
builder.addInjectable(PropertyName.construct(m.getName()),
m.getType(),
beanDesc.getClassAnnotations(), m, entry.getKey());
@@ -660,37 +668,41 @@
* Method called to construct fallback {@link SettableAnyProperty}
* for handling unknown bean properties, given a method that
* has been designated as such setter.
+ *
+ * @param mutator Either 2-argument method (setter, with key and value), or Field
+ * that contains Map; either way accessor used for passing "any values"
*/
+ @SuppressWarnings("unchecked")
protected SettableAnyProperty constructAnySetter(DeserializationContext ctxt,
- BeanDescription beanDesc, AnnotatedMethod setter)
+ BeanDescription beanDesc, AnnotatedMember mutator)
throws JsonMappingException
{
- if (ctxt.canOverrideAccessModifiers()) {
- setter.fixAccess(ctxt.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); // to ensure we can call it
+ //find the java type based on the annotated setter method or setter field
+ JavaType type = null;
+ if (mutator instanceof AnnotatedMethod) {
+ // we know it's a 2-arg method, second arg is the value
+ type = ((AnnotatedMethod) mutator).getParameterType(1);
+ } else if (mutator instanceof AnnotatedField) {
+ // get the type from the content type of the map object
+ type = ((AnnotatedField) mutator).getType().getContentType();
}
- // we know it's a 2-arg method, second arg is the value
- JavaType type = setter.getParameterType(1);
- BeanProperty.Std property = new BeanProperty.Std(PropertyName.construct(setter.getName()),
- type, null, beanDesc.getClassAnnotations(), setter,
+ // First: various annotations on type itself, as well as type-overrides
+ // on accessor need to be resolved
+ type = resolveMemberAndTypeAnnotations(ctxt, mutator, type);
+ BeanProperty.Std prop = new BeanProperty.Std(PropertyName.construct(mutator.getName()),
+ type, null, beanDesc.getClassAnnotations(), mutator,
PropertyMetadata.STD_OPTIONAL);
- type = resolveType(ctxt, beanDesc, type, setter);
-
- /* AnySetter can be annotated with @JsonDeserialize (etc) just like a
- * regular setter... so let's see if those are used.
- * Returns null if no annotations, in which case binding will
- * be done at a later point.
- */
- JsonDeserializer<Object> deser = findDeserializerFromAnnotation(ctxt, setter);
- /* Otherwise, method may specify more specific (sub-)class for
- * value (no need to check if explicit deser was specified):
- */
- type = modifyTypeByAnnotation(ctxt, setter, type);
+ // and then possible direct deserializer override on accessor
+ JsonDeserializer<Object> deser = findDeserializerFromAnnotation(ctxt, mutator);
if (deser == null) {
deser = type.getValueHandler();
}
+ if (deser != null) {
+ // As per [databind#462] need to ensure we contextualize deserializer before passing it on
+ deser = (JsonDeserializer<Object>) ctxt.handlePrimaryContextualization(deser, prop, type);
+ }
TypeDeserializer typeDeser = type.getTypeHandler();
- return new SettableAnyProperty(property, setter, type,
- deser, typeDeser);
+ return new SettableAnyProperty(prop, mutator, type, deser, typeDeser);
}
/**
@@ -707,43 +719,31 @@
{
// need to ensure method is callable (for non-public)
AnnotatedMember mutator = propDef.getNonConstructorMutator();
-
- if (ctxt.canOverrideAccessModifiers()) {
- // [databind#877]: explicitly prevent forced access to `cause` of `Throwable`;
- // never needed and attempts may cause problems on some platforms.
- // !!! NOTE: should be handled better for 2.8 and later
- if ((mutator instanceof AnnotatedField)
- && "cause".equals(mutator.getName())
- && Throwable.class.isAssignableFrom(propType0.getRawClass())) {
- ;
- } else {
- mutator.fixAccess(ctxt.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
- }
+ // 08-Sep-2016, tatu: issues like [databind#1342] suggest something fishy
+ // going on; add sanity checks to try to pin down actual problem...
+ // Possibly passing creator parameter?
+ if (mutator == null) {
+ ctxt.reportBadPropertyDefinition(beanDesc, propDef, "No non-constructor mutator available");
}
- // note: this works since we know there's exactly one argument for methods
- BeanProperty.Std property = new BeanProperty.Std(propDef.getFullName(),
- propType0, propDef.getWrapperName(),
- beanDesc.getClassAnnotations(), mutator, propDef.getMetadata());
- JavaType type = resolveType(ctxt, beanDesc, propType0, mutator);
- // did type change?
- if (type != propType0) {
- property = property.withType(type);
- }
-
- // First: does the Method specify the deserializer to use? If so, let's use it.
- JsonDeserializer<Object> propDeser = findDeserializerFromAnnotation(ctxt, mutator);
- type = modifyTypeByAnnotation(ctxt, mutator, type);
+ JavaType type = resolveMemberAndTypeAnnotations(ctxt, mutator, propType0);
+ // Does the Method specify the deserializer to use? If so, let's use it.
TypeDeserializer typeDeser = type.getTypeHandler();
SettableBeanProperty prop;
if (mutator instanceof AnnotatedMethod) {
prop = new MethodProperty(propDef, type, typeDeser,
beanDesc.getClassAnnotations(), (AnnotatedMethod) mutator);
} else {
+ // 08-Sep-2016, tatu: wonder if we should verify it is `AnnotatedField` to be safe?
prop = new FieldProperty(propDef, type, typeDeser,
beanDesc.getClassAnnotations(), (AnnotatedField) mutator);
}
- if (propDeser != null) {
- prop = prop.withValueDeserializer(propDeser);
+ JsonDeserializer<?> deser = findDeserializerFromAnnotation(ctxt, mutator);
+ if (deser == null) {
+ deser = type.getValueHandler();
+ }
+ if (deser != null) {
+ deser = ctxt.handlePrimaryContextualization(deser, prop, type);
+ prop = prop.withValueDeserializer(deser);
}
// need to retain name of managed forward references:
AnnotationIntrospector.ReferenceProperty ref = propDef.findReferenceType();
@@ -751,7 +751,7 @@
prop.setManagedReferenceName(ref.getName());
}
ObjectIdInfo objectIdInfo = propDef.findObjectIdInfo();
- if(objectIdInfo != null){
+ if (objectIdInfo != null){
prop.setObjectIdInfo(objectIdInfo);
}
return prop;
@@ -766,21 +766,17 @@
throws JsonMappingException
{
final AnnotatedMethod getter = propDef.getGetter();
- // need to ensure it is callable now:
- if (ctxt.canOverrideAccessModifiers()) {
- getter.fixAccess(ctxt.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
- }
- JavaType type = getter.getType();
- // First: does the Method specify the deserializer to use? If so, let's use it.
- JsonDeserializer<Object> propDeser = findDeserializerFromAnnotation(ctxt, getter);
- type = modifyTypeByAnnotation(ctxt, getter, type);
- // As per [Issue#501], need full resolution:
- type = resolveType(ctxt, beanDesc, type, getter);
+ JavaType type = resolveMemberAndTypeAnnotations(ctxt, getter, getter.getType());
TypeDeserializer typeDeser = type.getTypeHandler();
SettableBeanProperty prop = new SetterlessProperty(propDef, type, typeDeser,
beanDesc.getClassAnnotations(), getter);
- if (propDeser != null) {
- prop = prop.withValueDeserializer(propDeser);
+ JsonDeserializer<?> deser = findDeserializerFromAnnotation(ctxt, getter);
+ if (deser == null) {
+ deser = type.getValueHandler();
+ }
+ if (deser != null) {
+ deser = ctxt.handlePrimaryContextualization(deser, prop, type);
+ prop = prop.withValueDeserializer(deser);
}
return prop;
}
@@ -809,7 +805,7 @@
throw new IllegalArgumentException("Can not deserialize Proxy class "+type.getName()+" as a Bean");
}
/* also: can't deserialize some local classes: static are ok; in-method not;
- * and with [JACKSON-594], other non-static inner classes are ok
+ * other non-static inner classes are ok
*/
typeStr = ClassUtil.isLocalType(type, true);
if (typeStr != null) {
@@ -829,9 +825,20 @@
if (status != null) {
return status.booleanValue();
}
- BeanDescription desc = config.introspectClassAnnotations(type);
- status = config.getAnnotationIntrospector().isIgnorableType(desc.getClassInfo());
- // We default to 'false', i.e. not ignorable
- return (status == null) ? false : status.booleanValue();
+ // 21-Apr-2016, tatu: For 2.8, can specify config overrides
+ ConfigOverride override = config.findConfigOverride(type);
+ if (override != null) {
+ status = override.getIsIgnoredType();
+ }
+ if (status == null) {
+ BeanDescription desc = config.introspectClassAnnotations(type);
+ status = config.getAnnotationIntrospector().isIgnorableType(desc.getClassInfo());
+ // We default to 'false', i.e. not ignorable
+ if (status == null) {
+ status = Boolean.FALSE;
+ }
+ }
+ ignoredTypes.put(type, status);
+ return status.booleanValue();
}
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BuilderBasedDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/BuilderBasedDeserializer.java
index 26a00cf..7b92423 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/BuilderBasedDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BuilderBasedDeserializer.java
@@ -39,7 +39,7 @@
public BuilderBasedDeserializer(BeanDeserializerBuilder builder,
BeanDescription beanDesc,
BeanPropertyMap properties, Map<String, SettableBeanProperty> backRefs,
- HashSet<String> ignorableProps, boolean ignoreAllUnknown,
+ Set<String> ignorableProps, boolean ignoreAllUnknown,
boolean hasViews)
{
super(builder, beanDesc, properties, backRefs,
@@ -77,10 +77,15 @@
_buildMethod = src._buildMethod;
}
- public BuilderBasedDeserializer(BuilderBasedDeserializer src, HashSet<String> ignorableProps) {
+ public BuilderBasedDeserializer(BuilderBasedDeserializer src, Set<String> ignorableProps) {
super(src, ignorableProps);
_buildMethod = src._buildMethod;
}
+
+ public BuilderBasedDeserializer(BuilderBasedDeserializer src, BeanPropertyMap props) {
+ super(src, props);
+ _buildMethod = src._buildMethod;
+ }
@Override
public JsonDeserializer<Object> unwrappingDeserializer(NameTransformer unwrapper)
@@ -93,17 +98,22 @@
}
@Override
- public BuilderBasedDeserializer withObjectIdReader(ObjectIdReader oir) {
+ public BeanDeserializerBase withObjectIdReader(ObjectIdReader oir) {
return new BuilderBasedDeserializer(this, oir);
}
@Override
- public BuilderBasedDeserializer withIgnorableProperties(HashSet<String> ignorableProps) {
+ public BeanDeserializerBase withIgnorableProperties(Set<String> ignorableProps) {
return new BuilderBasedDeserializer(this, ignorableProps);
}
@Override
- protected BeanAsArrayBuilderDeserializer asArrayDeserializer() {
+ public BeanDeserializerBase withBeanProperties(BeanPropertyMap props) {
+ return new BuilderBasedDeserializer(this, props);
+ }
+
+ @Override
+ protected BeanDeserializerBase asArrayDeserializer() {
SettableBeanProperty[] props = _beanProperties.getPropertiesInInsertionOrder();
return new BeanAsArrayBuilderDeserializer(this, props, _buildMethod);
}
@@ -124,8 +134,7 @@
try {
return _buildMethod.getMember().invoke(builder);
} catch (Exception e) {
- wrapInstantiationProblem(e, ctxt);
- return null;
+ return wrapInstantiationProblem(e, ctxt);
}
}
@@ -134,7 +143,7 @@
*/
@Override
public final Object deserialize(JsonParser p, DeserializationContext ctxt)
- throws IOException, JsonProcessingException
+ throws IOException
{
JsonToken t = p.getCurrentToken();
@@ -148,27 +157,29 @@
return finishBuild(ctxt, builder);
}
// and then others, generally requiring use of @JsonCreator
- switch (t) {
- case VALUE_STRING:
- return finishBuild(ctxt, deserializeFromString(p, ctxt));
- case VALUE_NUMBER_INT:
- return finishBuild(ctxt, deserializeFromNumber(p, ctxt));
- case VALUE_NUMBER_FLOAT:
- return finishBuild(ctxt, deserializeFromDouble(p, ctxt));
- case VALUE_EMBEDDED_OBJECT:
- return p.getEmbeddedObject();
- case VALUE_TRUE:
- case VALUE_FALSE:
- return finishBuild(ctxt, deserializeFromBoolean(p, ctxt));
- case START_ARRAY:
- // these only work if there's a (delegating) creator...
- return finishBuild(ctxt, deserializeFromArray(p, ctxt));
- case FIELD_NAME:
- case END_OBJECT: // added to resolve [JACKSON-319], possible related issues
- return finishBuild(ctxt, deserializeFromObject(p, ctxt));
- default:
- throw ctxt.mappingException(handledType());
+ if (t != null) {
+ switch (t) {
+ case VALUE_STRING:
+ return finishBuild(ctxt, deserializeFromString(p, ctxt));
+ case VALUE_NUMBER_INT:
+ return finishBuild(ctxt, deserializeFromNumber(p, ctxt));
+ case VALUE_NUMBER_FLOAT:
+ return finishBuild(ctxt, deserializeFromDouble(p, ctxt));
+ case VALUE_EMBEDDED_OBJECT:
+ return p.getEmbeddedObject();
+ case VALUE_TRUE:
+ case VALUE_FALSE:
+ return finishBuild(ctxt, deserializeFromBoolean(p, ctxt));
+ case START_ARRAY:
+ // these only work if there's a (delegating) creator...
+ return finishBuild(ctxt, deserializeFromArray(p, ctxt));
+ case FIELD_NAME:
+ case END_OBJECT:
+ return finishBuild(ctxt, deserializeFromObject(p, ctxt));
+ default:
+ }
}
+ return ctxt.handleUnexpectedToken(handledType(), p);
}
/**
@@ -179,7 +190,7 @@
@Override
public Object deserialize(JsonParser p, DeserializationContext ctxt,
Object builder)
- throws IOException, JsonProcessingException
+ throws IOException
{
/* Important: we call separate method which does NOT call
* 'finishBuild()', to avoid problems with recursion
@@ -391,8 +402,7 @@
try {
bean = creator.build(ctxt, buffer);
} catch (Exception e) {
- wrapInstantiationProblem(e, ctxt);
- return null; // never gets here
+ bean = wrapInstantiationProblem(e, ctxt);
}
if (unknown != null) {
// polymorphic?
@@ -590,7 +600,8 @@
if (bean.getClass() != _beanType.getRawClass()) {
// !!! 08-Jul-2011, tatu: Could probably support; but for now
// it's too complicated, so bail out
- throw ctxt.mappingException("Can not create polymorphic instances with unwrapped values");
+ ctxt.reportMappingException("Can not create polymorphic instances with unwrapped values");
+ return null;
}
return _unwrappedPropertyHandler.processUnwrapped(p, ctxt, bean, tokens);
}
@@ -624,8 +635,7 @@
try {
bean = creator.build(ctxt, buffer);
} catch (Exception e) {
- wrapInstantiationProblem(e, ctxt);
- return null; // never gets here
+ return wrapInstantiationProblem(e, ctxt);
}
return _unwrappedPropertyHandler.processUnwrapped(p, ctxt, bean, tokens);
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/CreatorProperty.java b/src/main/java/com/fasterxml/jackson/databind/deser/CreatorProperty.java
index 6a9ee45..a64ebef 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/CreatorProperty.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/CreatorProperty.java
@@ -4,7 +4,7 @@
import java.lang.annotation.Annotation;
import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonProcessingException;
+
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.introspect.AnnotatedParameter;
@@ -114,6 +114,13 @@
return new CreatorProperty(this, deser);
}
+ @Override
+ public void fixAccess(DeserializationConfig config) {
+ if (_fallbackSetter != null) {
+ _fallbackSetter.fixAccess(config);
+ }
+ }
+
/**
* NOTE: one exception to immutability, due to problems with CreatorProperty instances
* being shared between Bean, separate PropertyBasedCreator
@@ -173,19 +180,17 @@
*/
@Override
- public void deserializeAndSet(JsonParser jp, DeserializationContext ctxt,
- Object instance)
- throws IOException, JsonProcessingException
+ public void deserializeAndSet(JsonParser p, DeserializationContext ctxt,
+ Object instance) throws IOException
{
- set(instance, deserialize(jp, ctxt));
+ set(instance, deserialize(p, ctxt));
}
@Override
- public Object deserializeSetAndReturn(JsonParser jp,
- DeserializationContext ctxt, Object instance)
- throws IOException, JsonProcessingException
+ public Object deserializeSetAndReturn(JsonParser p,
+ DeserializationContext ctxt, Object instance) throws IOException
{
- return setAndReturn(instance, deserialize(jp, ctxt));
+ return setAndReturn(instance, deserialize(p, ctxt));
}
@Override
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/DefaultDeserializationContext.java b/src/main/java/com/fasterxml/jackson/databind/deser/DefaultDeserializationContext.java
index 002c55a..d759ddd 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/DefaultDeserializationContext.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/DefaultDeserializationContext.java
@@ -6,8 +6,9 @@
import com.fasterxml.jackson.annotation.ObjectIdGenerator;
import com.fasterxml.jackson.annotation.ObjectIdResolver;
import com.fasterxml.jackson.annotation.ObjectIdGenerator.IdKey;
-import com.fasterxml.jackson.annotation.SimpleObjectIdResolver;
+
import com.fasterxml.jackson.core.JsonParser;
+
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId;
@@ -140,12 +141,6 @@
return new ReadableObjectId(key);
}
- @Deprecated // since 2.4
- @Override
- public ReadableObjectId findObjectId(Object id, ObjectIdGenerator<?> gen) {
- return findObjectId(id, gen, new SimpleObjectIdResolver());
- }
-
@Override
public void checkUnresolvedObjectId() throws UnresolvedForwardReference
{
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/DeserializationProblemHandler.java b/src/main/java/com/fasterxml/jackson/databind/deser/DeserializationProblemHandler.java
index 6cf8423..e374867 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/DeserializationProblemHandler.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/DeserializationProblemHandler.java
@@ -3,11 +3,13 @@
import java.io.IOException;
import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
/**
* This is the class that can be registered (via
@@ -31,7 +33,15 @@
public abstract class DeserializationProblemHandler
{
/**
- * Method called when a JSON Map ("Object") entry with an unrecognized
+ * Marker value returned by some handler methods to indicate that
+ * they could not handle problem and produce replacement value.
+ *
+ * @since 2.7
+ */
+ public final static Object NOT_HANDLED = new Object();
+
+ /**
+ * Method called when a JSON Object property with an unrecognized
* name is encountered.
* Content (supposedly) matching the property are accessible via
* parser that can be obtained from passed deserialization context.
@@ -39,29 +49,268 @@
* true to indicate it did handle property successfully.
* Skipping is usually done like so:
*<pre>
- * jp.skipChildren();
+ * parser.skipChildren();
*</pre>
*<p>
- * Note: version 1.2 added new deserialization feature
- * (<code>DeserializationConfig.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES</code>).
- * It will only have effect <b>after</b> handler is called, and only
+ * Note: {@link com.fasterxml.jackson.databind.DeserializationFeature#FAIL_ON_UNKNOWN_PROPERTIES})
+ * takes effect only <b>after</b> handler is called, and only
* if handler did <b>not</b> handle the problem.
*
* @param beanOrClass Either bean instance being deserialized (if one
* has been instantiated so far); or Class that indicates type that
* will be instantiated (if no instantiation done yet: for example
* when bean uses non-default constructors)
- * @param jp Parser to use for handling problematic content
+ * @param p Parser to use for handling problematic content
*
* @return True if the problem is resolved (and content available used or skipped);
* false if the handler did not anything and the problem is unresolved. Note that in
* latter case caller will either throw an exception or explicitly skip the content,
* depending on configuration.
*/
- public boolean handleUnknownProperty(DeserializationContext ctxt, JsonParser jp,
+ public boolean handleUnknownProperty(DeserializationContext ctxt, JsonParser p,
JsonDeserializer<?> deserializer, Object beanOrClass, String propertyName)
- throws IOException, JsonProcessingException
+ throws IOException
{
return false;
}
+
+ /**
+ * Method called when a property name from input can not be converted to a
+ * non-Java-String key type (passed as <code>rawKeyType</code>) due to format problem.
+ * Handler may choose to do one of 3 things:
+ *<ul>
+ * <li>Indicate it does not know what to do by returning {@link #NOT_HANDLED}
+ * </li>
+ * <li>Throw a {@link IOException} to indicate specific fail message (instead of
+ * standard exception caller would throw
+ * </li>
+ * <li>Return actual key value to use as replacement, and continue processing.
+ * </li>
+ * </ul>
+ *
+ * @param failureMsg Message that will be used by caller (by calling
+ * {@link DeserializationContext#weirdKeyException(Class, String, String)})
+ * to indicate type of failure unless handler produces key to use
+ *
+ * @return Either {@link #NOT_HANDLED} to indicate that handler does not know
+ * what to do (and exception may be thrown), or value to use as key (possibly
+ * <code>null</code>
+ *
+ * @since 2.8
+ */
+ public Object handleWeirdKey(DeserializationContext ctxt,
+ Class<?> rawKeyType, String keyValue,
+ String failureMsg)
+ throws IOException
+ {
+ return NOT_HANDLED;
+ }
+
+ /**
+ * Method called when a String value
+ * can not be converted to a non-String value type due to specific problem
+ * (as opposed to String values never being usable).
+ * Handler may choose to do one of 3 things:
+ *<ul>
+ * <li>Indicate it does not know what to do by returning {@link #NOT_HANDLED}
+ * </li>
+ * <li>Throw a {@link IOException} to indicate specific fail message (instead of
+ * standard exception caller would throw
+ * </li>
+ * <li>Return actual converted value (of type <code>targetType</code>) to use as
+ * replacement, and continue processing.
+ * </li>
+ * </ul>
+ *
+ * @param failureMsg Message that will be used by caller (by calling
+ * {@link DeserializationContext#weirdNumberException})
+ * to indicate type of failure unless handler produces key to use
+ *
+ * @return Either {@link #NOT_HANDLED} to indicate that handler does not know
+ * what to do (and exception may be thrown), or value to use as key (possibly
+ * <code>null</code>
+ *
+ * @since 2.8
+ */
+ public Object handleWeirdStringValue(DeserializationContext ctxt,
+ Class<?> targetType, String valueToConvert,
+ String failureMsg)
+ throws IOException
+ {
+ return NOT_HANDLED;
+ }
+
+ /**
+ * Method called when a numeric value (integral or floating-point from input
+ * can not be converted to a non-numeric value type due to specific problem
+ * (as opposed to numeric values never being usable).
+ * Handler may choose to do one of 3 things:
+ *<ul>
+ * <li>Indicate it does not know what to do by returning {@link #NOT_HANDLED}
+ * </li>
+ * <li>Throw a {@link IOException} to indicate specific fail message (instead of
+ * standard exception caller would throw
+ * </li>
+ * <li>Return actual converted value (of type <code>targetType</code>) to use as
+ * replacement, and continue processing.
+ * </li>
+ * </ul>
+ *
+ * @param failureMsg Message that will be used by caller (by calling
+ * {@link DeserializationContext#weirdNumberException})
+ * to indicate type of failure unless handler produces key to use
+ *
+ * @return Either {@link #NOT_HANDLED} to indicate that handler does not know
+ * what to do (and exception may be thrown), or value to use as key (possibly
+ * <code>null</code>
+ *
+ * @since 2.8
+ */
+ public Object handleWeirdNumberValue(DeserializationContext ctxt,
+ Class<?> targetType, Number valueToConvert,
+ String failureMsg)
+ throws IOException
+ {
+ return NOT_HANDLED;
+ }
+
+ /**
+ * 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
+ * can not 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}.
+ *<ul>
+ * <li>Indicate it does not know what to do by returning {@link #NOT_HANDLED}
+ * </li>
+ * <li>Throw a {@link IOException} to indicate specific fail message (instead of
+ * standard exception caller would throw
+ * </li>
+ * <li>Handle content to match (by consuming or skipping it), and return actual
+ * instantiated value (of type <code>targetType</code>) to use as replacement;
+ * value may be `null` as well as expected target type.
+ * </li>
+ * </ul>
+ *
+ * @param failureMsg Message that will be used by caller
+ * to indicate type of failure unless handler produces value to use
+ *
+ * @return Either {@link #NOT_HANDLED} to indicate that handler does not know
+ * what to do (and exception may be thrown), or value to use (possibly
+ * <code>null</code>
+ *
+ * @since 2.8
+ */
+ public Object handleUnexpectedToken(DeserializationContext ctxt,
+ Class<?> targetType, JsonToken t, JsonParser p,
+ String failureMsg)
+ throws IOException
+ {
+ return NOT_HANDLED;
+ }
+
+ /**
+ * Method called when instance creation for a type fails due to an exception.
+ * Handler may choose to do one of following things:
+ *<ul>
+ * <li>Indicate it does not know what to do by returning {@link #NOT_HANDLED}
+ * </li>
+ * <li>Throw a {@link IOException} to indicate specific fail message (instead of
+ * standard exception caller would throw
+ * </li>
+ * <li>Return actual instantiated value (of type <code>targetType</code>) to use as
+ * replacement, and continue processing.
+ * </li>
+ * <li>Return <code>null</code> to use null as value but not to try further
+ * processing (in cases where properties would otherwise be bound)
+ * </li>
+ * </ul>
+ *
+ * @param instClass Type that was to be instantiated
+ * @param argument (optional) Additional argument that was passed to creator, if any
+ * @param t Exception that caused instantiation failure
+ *
+ * @return Either {@link #NOT_HANDLED} to indicate that handler does not know
+ * what to do (and exception may be thrown), or value to use (possibly
+ * <code>null</code>
+ *
+ * @since 2.8
+ */
+ public Object handleInstantiationProblem(DeserializationContext ctxt,
+ Class<?> instClass, Object argument, Throwable t)
+ throws IOException
+ {
+ return NOT_HANDLED;
+ }
+
+ /**
+ * Method called when instance creation for a type fails due to lack of an
+ * instantiator. Method is called before actual deserialization from input
+ * is attempted, so handler may do one of following things:
+ *<ul>
+ * <li>Indicate it does not know what to do by returning {@link #NOT_HANDLED}
+ * </li>
+ * <li>Throw a {@link IOException} to indicate specific fail message (instead of
+ * standard exception caller would throw
+ * </li>
+ * <li>Handle content to match (by consuming or skipping it), and return actual
+ * instantiated value (of type <code>targetType</code>) to use as replacement;
+ * value may be `null` as well as expected target type.
+ * </li>
+ * </ul>
+ *
+ * @param instClass Type that was to be instantiated
+ * @param p Parser to use for accessing content that needs handling, to either
+ * use it or skip it (latter with {@link JsonParser#skipChildren()}.
+ *
+ * @return Either {@link #NOT_HANDLED} to indicate that handler does not know
+ * what to do (and exception may be thrown), or value to use (possibly
+ * <code>null</code>
+ *
+ * @since 2.8
+ */
+ public Object handleMissingInstantiator(DeserializationContext ctxt,
+ Class<?> instClass, JsonParser p, String msg)
+ throws IOException
+ {
+ return NOT_HANDLED;
+ }
+
+ /**
+ * Handler method called if resolution of type id from given String failed
+ * to produce a subtype; usually because logical id is not mapped to actual
+ * implementation class.
+ * Handler may choose to do one of following things:
+ *<ul>
+ * <li>Indicate it does not know what to do by returning `null`
+ * </li>
+ * <li>Indicate that nothing should be deserialized, by return `Void.class`
+ * </li>
+ * <li>Throw a {@link IOException} to indicate specific fail message (instead of
+ * standard exception caller would throw
+ * </li>
+ * <li>Return actual resolved type to use for type id.
+ * </li>
+ * </ul>
+ *
+ * @param ctxt Deserialization context to use for accessing information or
+ * constructing exception to throw
+ * @param baseType Base type to use for resolving subtype id
+ * @param subTypeId Subtype id that failed to resolve
+ * @param failureMsg Informational message that would be thrown as part of
+ * exception, if resolution still fails
+ *
+ * @return Actual type to use, if resolved; `null` if handler does not know what
+ * to do; or `Void.class` to indicate that nothing should be deserialized for
+ * type with the id (which caller may choose to do... or not)
+ *
+ * @since 2.8
+ */
+ public JavaType handleUnknownTypeId(DeserializationContext ctxt,
+ JavaType baseType, String subTypeId, TypeIdResolver idResolver,
+ String failureMsg)
+ throws IOException
+ {
+ return null;
+ }
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/DeserializerCache.java b/src/main/java/com/fasterxml/jackson/databind/deser/DeserializerCache.java
index a4ab8d7..0db1558 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/DeserializerCache.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/DeserializerCache.java
@@ -353,7 +353,7 @@
}
// otherwise need to do bit of introspection
JavaType delegateType = conv.getInputType(ctxt.getTypeFactory());
- // One more twist, as per [Issue#288]; probably need to get new BeanDesc
+ // One more twist, as per [databind#288]; probably need to get new BeanDesc
if (!delegateType.hasRawClass(type.getRawClass())) {
beanDesc = config.introspect(delegateType);
}
@@ -382,13 +382,13 @@
return factory.createMapLikeDeserializer(ctxt, mlt, beanDesc);
}
if (type.isCollectionLikeType()) {
- /* 03-Aug-2012, tatu: As per [Issue#40], one exception is if shape
+ /* 03-Aug-2012, tatu: As per [databind#40], one exception is if shape
* is to be Shape.OBJECT. Ideally we'd determine it bit later on
* (to allow custom handler checks), but that won't work for other
* reasons. So do it here.
*/
JsonFormat.Value format = beanDesc.findExpectedFormat(null);
- if (format == null || format.getShape() != JsonFormat.Shape.OBJECT) {
+ if ((format == null) || format.getShape() != JsonFormat.Shape.OBJECT) {
CollectionLikeType clt = (CollectionLikeType) type;
if (clt.isTrueCollectionType()) {
return factory.createCollectionDeserializer(ctxt, (CollectionType) clt, beanDesc);
@@ -569,21 +569,23 @@
// NOTE: changed 2.6 -> 2.7 to pass context; no way to make backwards compatible
protected JsonDeserializer<Object> _handleUnknownValueDeserializer(DeserializationContext ctxt, JavaType type)
- throws JsonMappingException
+ throws JsonMappingException
{
/* Let's try to figure out the reason, to give better error
* messages
*/
Class<?> rawClass = type.getRawClass();
if (!ClassUtil.isConcrete(rawClass)) {
- throw JsonMappingException.from(ctxt, "Can not find a Value deserializer for abstract type "+type);
+ ctxt.reportMappingException("Can not find a Value deserializer for abstract type %s", type);
}
- throw JsonMappingException.from(ctxt, "Can not find a Value deserializer for type "+type);
+ ctxt.reportMappingException("Can not find a Value deserializer for type %s", type);
+ return null;
}
protected KeyDeserializer _handleUnknownKeyDeserializer(DeserializationContext ctxt, JavaType type)
throws JsonMappingException
{
- throw JsonMappingException.from(ctxt, "Can not find a (Map) Key deserializer for type "+type);
+ ctxt.reportMappingException("Can not find a (Map) Key deserializer for type %s", type);
+ return null;
}
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/Deserializers.java b/src/main/java/com/fasterxml/jackson/databind/deser/Deserializers.java
index eb9948e..360a08f 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/Deserializers.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/Deserializers.java
@@ -76,6 +76,8 @@
* by property annotation
*
* @return Deserializer to use for the type; or null if this provider does not know how to construct it
+ *
+ * @since 2.7
*/
public JsonDeserializer<?> findReferenceDeserializer(ReferenceType refType,
DeserializationConfig config, BeanDescription beanDesc,
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/SettableAnyProperty.java b/src/main/java/com/fasterxml/jackson/databind/deser/SettableAnyProperty.java
index 92261be..8ee86d4 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/SettableAnyProperty.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/SettableAnyProperty.java
@@ -1,10 +1,13 @@
package com.fasterxml.jackson.databind.deser;
import java.io.IOException;
+import java.util.Map;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId.Referring;
+import com.fasterxml.jackson.databind.introspect.AnnotatedField;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
@@ -30,8 +33,10 @@
/**
* Annotated variant is needed for JDK serialization only
*/
- final protected AnnotatedMethod _setter;
+ final protected AnnotatedMember _setter;
+ final boolean _setterIsField;
+
protected final JavaType _type;
protected JsonDeserializer<Object> _valueDeserializer;
@@ -44,7 +49,7 @@
/**********************************************************
*/
- public SettableAnyProperty(BeanProperty property, AnnotatedMethod setter, JavaType type,
+ public SettableAnyProperty(BeanProperty property, AnnotatedMember setter, JavaType type,
JsonDeserializer<Object> valueDeser, TypeDeserializer typeDeser)
{
_property = property;
@@ -52,11 +57,7 @@
_type = type;
_valueDeserializer = valueDeser;
_valueTypeDeserializer = typeDeser;
- }
-
- public SettableAnyProperty withValueDeserializer(JsonDeserializer<Object> deser) {
- return new SettableAnyProperty(_property, _setter, _type,
- deser, _valueTypeDeserializer);
+ _setterIsField = setter instanceof AnnotatedField;
}
/**
@@ -69,8 +70,19 @@
_type = src._type;
_valueDeserializer = src._valueDeserializer;
_valueTypeDeserializer = src._valueTypeDeserializer;
+ _setterIsField = src._setterIsField;
}
-
+
+ public SettableAnyProperty withValueDeserializer(JsonDeserializer<Object> deser) {
+ return new SettableAnyProperty(_property, _setter, _type,
+ deser, _valueTypeDeserializer);
+ }
+
+ public void fixAccess(DeserializationConfig config) {
+ _setter.fixAccess(
+ config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
+ }
+
/*
/**********************************************************
/* JDK serialization handling
@@ -138,11 +150,27 @@
return _valueDeserializer.deserialize(p, ctxt);
}
+ @SuppressWarnings("unchecked")
public void set(Object instance, String propName, Object value) throws IOException
{
try {
- // note: can not use 'setValue()' due to taking 2 args
- _setter.getAnnotated().invoke(instance, propName, value);
+ // if annotation in the field (only map is supported now)
+ if (_setterIsField) {
+ AnnotatedField field = (AnnotatedField) _setter;
+ Map<Object,Object> val = (Map<Object,Object>) field.getValue(instance);
+ /* 01-Jun-2016, tatu: At this point it is not quite clear what to do if
+ * field is `null` -- we can not necessarily count on zero-args
+ * constructor except for a small set of types, so for now just
+ * ignore if null. May need to figure out something better in future.
+ */
+ if (val != null) {
+ // add the property key and value
+ val.put(propName, value);
+ }
+ } else {
+ // note: can not use 'setValue()' due to taking 2 args
+ ((AnnotatedMethod) _setter).callOnWith(instance, propName, value);
+ }
} catch (Exception e) {
_throwAsIOE(e, propName, value);
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/SettableBeanProperty.java b/src/main/java/com/fasterxml/jackson/databind/deser/SettableBeanProperty.java
index 64dd0e4..7615c6c 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/SettableBeanProperty.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/SettableBeanProperty.java
@@ -4,12 +4,10 @@
import java.lang.annotation.Annotation;
import com.fasterxml.jackson.core.*;
+
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.impl.FailingDeserializer;
-import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
-import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
-import com.fasterxml.jackson.databind.introspect.ConcreteBeanPropertyBase;
-import com.fasterxml.jackson.databind.introspect.ObjectIdInfo;
+import com.fasterxml.jackson.databind.introspect.*;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.util.Annotations;
@@ -309,7 +307,18 @@
}
_propertyIndex = index;
}
-
+
+ /**
+ * Method called to ensure that the mutator has proper access rights to
+ * be called, as per configuration. Overridden by implementations that
+ * have mutators that require access, fields and setters.
+ *
+ * @since 2.8.3
+ */
+ public void fixAccess(DeserializationConfig config) {
+ ;
+ }
+
/*
/**********************************************************
/* BeanProperty impl
@@ -480,7 +489,7 @@
public final Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
{
JsonToken t = p.getCurrentToken();
-
+
if (t == JsonToken.VALUE_NULL) {
return _valueDeserializer.getNullValue(ctxt);
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/ValueInstantiator.java b/src/main/java/com/fasterxml/jackson/databind/deser/ValueInstantiator.java
index a0db47e..cce445a 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/ValueInstantiator.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/ValueInstantiator.java
@@ -2,8 +2,8 @@
import java.io.IOException;
-import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.impl.PropertyValueBuffer;
import com.fasterxml.jackson.databind.introspect.AnnotatedParameter;
import com.fasterxml.jackson.databind.introspect.AnnotatedWithParams;
@@ -37,10 +37,30 @@
*/
/**
+ * Accessor for raw (type-erased) type of instances to create.
+ *<p>
+ * NOTE: since this method has not existed since beginning of
+ * Jackson 2.0 series, default implementation will just return
+ * <code>Object.class</code>; implementations are expected
+ * to override it with real value.
+ *
+ * @since 2.8
+ */
+ public Class<?> getValueClass() {
+ return Object.class;
+ }
+
+ /**
* Method that returns description of the value type this instantiator
* handles. Used for error messages, diagnostics.
*/
- public abstract String getValueTypeDesc();
+ public String getValueTypeDesc() {
+ Class<?> cls = getValueClass();
+ if (cls == null) {
+ return "UNKNOWN";
+ }
+ return cls.getName();
+ }
/**
* Method that will return true if any of <code>canCreateXxx</code> method
@@ -161,8 +181,8 @@
* null or empty List.
*/
public Object createUsingDefault(DeserializationContext ctxt) throws IOException {
- throw ctxt.mappingException("Can not instantiate value of type %s; no default creator found",
- getValueTypeDesc());
+ return ctxt.handleMissingInstantiator(getValueClass(), ctxt.getParser(),
+ "no default no-arguments constructor found");
}
/**
@@ -174,8 +194,34 @@
* a non-empty List of arguments.
*/
public Object createFromObjectWith(DeserializationContext ctxt, Object[] args) throws IOException {
- throw ctxt.mappingException("Can not instantiate value of type %s with arguments",
- getValueTypeDesc());
+ // sanity check; shouldn't really get called if no Creator specified
+ return ctxt.handleMissingInstantiator(getValueClass(), ctxt.getParser(),
+ "no creator with arguments specified");
+ }
+
+ /**
+ * Method that delegates to
+ * {@link #createFromObjectWith(DeserializationContext, Object[])} by
+ * default, but can be overridden if the application should have customized
+ * behavior with respect to missing properties.
+ *<p>
+ * The default implementation of this method uses
+ * {@link PropertyValueBuffer#getParameters(SettableBeanProperty[])} to read
+ * and validate all properties in bulk, possibly substituting defaults for
+ * missing properties or throwing exceptions for missing properties. An
+ * overridden implementation of this method could, for example, use
+ * {@link PropertyValueBuffer#hasParameter(SettableBeanProperty)} and
+ * {@link PropertyValueBuffer#getParameter(SettableBeanProperty)} to safely
+ * read the present properties only, and to have some other behavior for the
+ * missing properties.
+ *
+ * @since 2.8
+ */
+ public Object createFromObjectWith(DeserializationContext ctxt,
+ SettableBeanProperty[] props, PropertyValueBuffer buffer)
+ throws IOException
+ {
+ return createFromObjectWith(ctxt, buffer.getParameters(props));
}
/**
@@ -183,8 +229,8 @@
* an intermediate "delegate" value to pass to createor method
*/
public Object createUsingDelegate(DeserializationContext ctxt, Object delegate) throws IOException {
- throw ctxt.mappingException("Can not instantiate value of type %s using delegate",
- getValueTypeDesc());
+ return ctxt.handleMissingInstantiator(getValueClass(), ctxt.getParser(),
+ "no delegate creator specified");
}
/**
@@ -192,8 +238,8 @@
* an intermediate "delegate" value to pass to createor method
*/
public Object createUsingArrayDelegate(DeserializationContext ctxt, Object delegate) throws IOException {
- throw ctxt.mappingException("Can not instantiate value of type %s using delegate",
- getValueTypeDesc());
+ return ctxt.handleMissingInstantiator(getValueClass(), ctxt.getParser(),
+ "no array delegate creator specified");
}
/*
@@ -208,23 +254,27 @@
}
public Object createFromInt(DeserializationContext ctxt, int value) throws IOException {
- throw ctxt.mappingException("Can not instantiate value of type %s from Integer number (%s, int)",
- getValueTypeDesc(), value);
+ return ctxt.handleMissingInstantiator(getValueClass(), ctxt.getParser(),
+ "no int/Int-argument constructor/factory method to deserialize from Number value (%s)",
+ value);
}
public Object createFromLong(DeserializationContext ctxt, long value) throws IOException {
- throw ctxt.mappingException("Can not instantiate value of type %s from Integer number (%s, long)",
- getValueTypeDesc(), value);
+ return ctxt.handleMissingInstantiator(getValueClass(), ctxt.getParser(),
+ "no long/Long-argument constructor/factory method to deserialize from Number value (%s)",
+ value);
}
public Object createFromDouble(DeserializationContext ctxt, double value) throws IOException {
- throw ctxt.mappingException("Can not instantiate value of type %s from Floating-point number (%s, double)",
- getValueTypeDesc(), value);
+ return ctxt.handleMissingInstantiator(getValueClass(), ctxt.getParser(),
+ "no double/Double-argument constructor/factory method to deserialize from Number value (%s)",
+ value);
}
-
+
public Object createFromBoolean(DeserializationContext ctxt, boolean value) throws IOException {
- throw ctxt.mappingException("Can not instantiate value of type %s from Boolean value (%s)",
- getValueTypeDesc(), value);
+ return ctxt.handleMissingInstantiator(getValueClass(), ctxt.getParser(),
+ "no boolean/Boolean-argument constructor/factory method to deserialize from boolean value (%s)",
+ value);
}
/*
@@ -292,7 +342,7 @@
* @since 2.4 (demoted from <code>StdValueInstantiator</code>)
*/
protected Object _createFromStringFallbacks(DeserializationContext ctxt, String value)
- throws IOException, JsonProcessingException
+ throws IOException
{
/* 28-Sep-2011, tatu: Ok this is not clean at all; but since there are legacy
* systems that expect conversions in some cases, let's just add a minimal
@@ -313,7 +363,41 @@
return null;
}
}
- throw ctxt.mappingException("Can not instantiate value of type %s from String value ('%s'); no single-String constructor/factory method",
- getValueTypeDesc(), value);
+ return ctxt.handleMissingInstantiator(getValueClass(), ctxt.getParser(),
+ "no String-argument constructor/factory method to deserialize from String value ('%s')",
+ value);
+ }
+
+ /*
+ /**********************************************************
+ /* Standard Base implementation (since 2.8)
+ /**********************************************************
+ */
+
+ /**
+ * Partial {@link ValueInstantiator} implementation that is strongly recommended
+ * to be used instead of directly extending {@link ValueInstantiator} itself.
+ */
+ public static class Base extends ValueInstantiator
+ {
+ protected final Class<?> _valueType;
+
+ public Base(Class<?> type) {
+ _valueType = type;
+ }
+
+ public Base(JavaType type) {
+ _valueType = type.getRawClass();
+ }
+
+ @Override
+ public String getValueTypeDesc() {
+ return _valueType.getName();
+ }
+
+ @Override
+ public Class<?> getValueClass() {
+ return _valueType;
+ }
}
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayBuilderDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayBuilderDeserializer.java
index 1ca180b..dabe451 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayBuilderDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayBuilderDeserializer.java
@@ -1,10 +1,9 @@
package com.fasterxml.jackson.databind.deser.impl;
import java.io.IOException;
-import java.util.HashSet;
+import java.util.Set;
import com.fasterxml.jackson.core.*;
-
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.*;
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
@@ -59,19 +58,25 @@
}
@Override
- public BeanAsArrayBuilderDeserializer withObjectIdReader(ObjectIdReader oir) {
+ public BeanDeserializerBase withObjectIdReader(ObjectIdReader oir) {
return new BeanAsArrayBuilderDeserializer(_delegate.withObjectIdReader(oir),
_orderedProperties, _buildMethod);
}
@Override
- public BeanAsArrayBuilderDeserializer withIgnorableProperties(HashSet<String> ignorableProps) {
+ public BeanDeserializerBase withIgnorableProperties(Set<String> ignorableProps) {
return new BeanAsArrayBuilderDeserializer(_delegate.withIgnorableProperties(ignorableProps),
_orderedProperties, _buildMethod);
}
@Override
- protected BeanAsArrayBuilderDeserializer asArrayDeserializer() {
+ public BeanDeserializerBase withBeanProperties(BeanPropertyMap props) {
+ return new BeanAsArrayBuilderDeserializer(_delegate.withBeanProperties(props),
+ _orderedProperties, _buildMethod);
+ }
+
+ @Override
+ protected BeanDeserializerBase asArrayDeserializer() {
return this;
}
@@ -87,11 +92,10 @@
try {
return _buildMethod.getMember().invoke(builder);
} catch (Exception e) {
- wrapInstantiationProblem(e, ctxt);
- return null;
+ return wrapInstantiationProblem(e, ctxt);
}
}
-
+
@Override
public Object deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException
@@ -128,8 +132,9 @@
}
// Ok; extra fields? Let's fail, unless ignoring extra props is fine
if (!_ignoreAllUnknown) {
- throw ctxt.mappingException("Unexpected JSON values; expected at most %d properties (in JSON Array)",
+ ctxt.reportMappingException("Unexpected JSON values; expected at most %d properties (in JSON Array)",
propCount);
+ // fall through
}
// otherwise, skip until end
while (p.nextToken() != JsonToken.END_ARRAY) {
@@ -173,13 +178,15 @@
// Ok; extra fields? Let's fail, unless ignoring extra props is fine
if (!_ignoreAllUnknown) {
- throw ctxt.mappingException("Unexpected JSON values; expected at most %d properties (in JSON Array)",
+ ctxt.reportWrongTokenException(p, JsonToken.END_ARRAY,
+ "Unexpected JSON values; expected at most %d properties (in JSON Array)",
propCount);
+ // never gets here
}
// otherwise, skip until end
- while (p.nextToken() != JsonToken.END_ARRAY) {
+ do {
p.skipChildren();
- }
+ } while (p.nextToken() != JsonToken.END_ARRAY);
return finishBuild(ctxt, builder);
}
@@ -207,7 +214,7 @@
throws IOException
{
if (_nonStandardCreation) {
- return _deserializeWithCreator(p, ctxt);
+ return deserializeFromObjectUsingNonDefault(p, ctxt);
}
Object builder = _valueInstantiator.createUsingDefault(ctxt);
if (_injectables != null) {
@@ -241,8 +248,10 @@
}
// Ok; extra fields? Let's fail, unless ignoring extra props is fine
if (!_ignoreAllUnknown) {
- throw ctxt.mappingException("Unexpected JSON values; expected at most %d properties (in JSON Array)",
+ ctxt.reportWrongTokenException(p, JsonToken.END_ARRAY,
+ "Unexpected JSON value(s); expected at most %d properties (in JSON Array)",
propCount);
+ // will never reach here as exception has been thrown
}
// otherwise, skip until end
while (p.nextToken() != JsonToken.END_ARRAY) {
@@ -250,25 +259,6 @@
}
return builder;
}
-
- protected Object _deserializeWithCreator(JsonParser p, DeserializationContext ctxt)
- throws IOException
- {
- if (_delegateDeserializer != null) {
- return _valueInstantiator.createUsingDelegate(ctxt,
- _delegateDeserializer.deserialize(p, ctxt));
- }
- if (_propertyBasedCreator != null) {
- return _deserializeUsingPropertyBased(p, ctxt);
- }
- // should only occur for abstract types...
- if (_beanType.isAbstract()) {
- throw JsonMappingException.from(p, "Can not instantiate abstract type "+_beanType
- +" (need to add/enable type information?)");
- }
- throw JsonMappingException.from(p, "No suitable constructor found for type "
- +_beanType+": can not instantiate from JSON object (need to add/enable type information?)");
- }
/**
* Method called to deserialize bean using "property-based creator":
@@ -324,9 +314,10 @@
* supported (since ordering of elements may not be guaranteed);
* but make explicitly non-supported for now.
*/
- throw ctxt.mappingException("Can not support implicit polymorphic deserialization for POJOs-as-Arrays style: "
+ ctxt.reportMappingException("Can not support implicit polymorphic deserialization for POJOs-as-Arrays style: "
+"nominal type %s, actual type %s",
_beanType.getRawClass().getName(), builder.getClass().getName());
+ return null;
}
}
continue;
@@ -344,8 +335,7 @@
try {
builder = creator.build(ctxt, buffer);
} catch (Exception e) {
- wrapInstantiationProblem(e, ctxt);
- return null; // never gets here
+ return wrapInstantiationProblem(e, ctxt);
}
}
return builder;
@@ -361,7 +351,8 @@
throws IOException
{
// Let's start with failure
- throw ctxt.mappingException("Can not deserialize a POJO (of type %s) from non-Array representation (token: %s): "
+ return ctxt.handleUnexpectedToken(handledType(), p.getCurrentToken(), p,
+ "Can not deserialize a POJO (of type %s) from non-Array representation (token: %s): "
+"type/property designed to be serialized as JSON Array",
_beanType.getRawClass().getName(),
p.getCurrentToken());
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayDeserializer.java
index 04869be..1cff5ca 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayDeserializer.java
@@ -1,11 +1,10 @@
package com.fasterxml.jackson.databind.deser.impl;
import java.io.IOException;
-import java.util.HashSet;
+import java.util.Set;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
-
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.*;
import com.fasterxml.jackson.databind.util.NameTransformer;
@@ -61,18 +60,24 @@
}
@Override
- public BeanAsArrayDeserializer withObjectIdReader(ObjectIdReader oir) {
+ public BeanDeserializerBase withObjectIdReader(ObjectIdReader oir) {
return new BeanAsArrayDeserializer(_delegate.withObjectIdReader(oir),
_orderedProperties);
}
@Override
- public BeanAsArrayDeserializer withIgnorableProperties(HashSet<String> ignorableProps) {
+ public BeanDeserializerBase withIgnorableProperties(Set<String> ignorableProps) {
return new BeanAsArrayDeserializer(_delegate.withIgnorableProperties(ignorableProps),
_orderedProperties);
}
@Override
+ public BeanDeserializerBase withBeanProperties(BeanPropertyMap props) {
+ return new BeanAsArrayDeserializer(_delegate.withBeanProperties(props),
+ _orderedProperties);
+ }
+
+ @Override
protected BeanDeserializerBase asArrayDeserializer() {
return this;
}
@@ -122,13 +127,15 @@
}
// Ok; extra fields? Let's fail, unless ignoring extra props is fine
if (!_ignoreAllUnknown) {
- throw ctxt.mappingException("Unexpected JSON values; expected at most %d properties (in JSON Array)",
+ ctxt.reportWrongTokenException(p, JsonToken.END_ARRAY,
+ "Unexpected JSON values; expected at most %d properties (in JSON Array)",
propCount);
+ // never gets here
}
// otherwise, skip until end
- while (p.nextToken() != JsonToken.END_ARRAY) {
+ do {
p.skipChildren();
- }
+ } while (p.nextToken() != JsonToken.END_ARRAY);
return bean;
}
@@ -169,17 +176,18 @@
// Ok; extra fields? Let's fail, unless ignoring extra props is fine
if (!_ignoreAllUnknown) {
- throw ctxt.mappingException("Unexpected JSON values; expected at most %d properties (in JSON Array)",
+ ctxt.reportWrongTokenException(p, JsonToken.END_ARRAY,
+ "Unexpected JSON values; expected at most %d properties (in JSON Array)",
propCount);
+ // never gets here
}
// otherwise, skip until end
- while (p.nextToken() != JsonToken.END_ARRAY) {
+ do {
p.skipChildren();
- }
+ } while (p.nextToken() != JsonToken.END_ARRAY);
return bean;
}
-
// needed since 2.1
@Override
public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt)
@@ -187,7 +195,7 @@
{
return _deserializeFromNonArray(p, ctxt);
}
-
+
/*
/**********************************************************
/* Helper methods, non-standard creation
@@ -202,7 +210,7 @@
throws IOException
{
if (_nonStandardCreation) {
- return _deserializeWithCreator(p, ctxt);
+ return deserializeFromObjectUsingNonDefault(p, ctxt);
}
final Object bean = _valueInstantiator.createUsingDefault(ctxt);
// [databind#631]: Assign current value, to be accessible by custom serializers
@@ -238,33 +246,17 @@
}
// Ok; extra fields? Let's fail, unless ignoring extra props is fine
if (!_ignoreAllUnknown) {
- throw ctxt.mappingException("Unexpected JSON values; expected at most %d properties (in JSON Array)",
+ ctxt.reportWrongTokenException(p, JsonToken.END_ARRAY,
+ "Unexpected JSON values; expected at most %d properties (in JSON Array)",
propCount);
+ // will never reach here as exception has been thrown
}
// otherwise, skip until end
- while (p.nextToken() != JsonToken.END_ARRAY) {
+ do {
p.skipChildren();
- }
+ } while (p.nextToken() != JsonToken.END_ARRAY);
return bean;
}
-
- protected Object _deserializeWithCreator(JsonParser p, DeserializationContext ctxt)
- throws IOException
- {
- if (_delegateDeserializer != null) {
- return _valueInstantiator.createUsingDelegate(ctxt, _delegateDeserializer.deserialize(p, ctxt));
- }
- if (_propertyBasedCreator != null) {
- return _deserializeUsingPropertyBased(p, ctxt);
- }
- // should only occur for abstract types...
- if (_beanType.isAbstract()) {
- throw JsonMappingException.from(p, "Can not instantiate abstract type "+_beanType
- +" (need to add/enable type information?)");
- }
- throw JsonMappingException.from(p, "No suitable constructor found for type "
- +_beanType+": can not instantiate from JSON object (need to add/enable type information?)");
- }
/**
* Method called to deserialize bean using "property-based creator":
@@ -322,7 +314,7 @@
* supported (since ordering of elements may not be guaranteed);
* but make explicitly non-supported for now.
*/
- throw ctxt.mappingException("Can not support implicit polymorphic deserialization for POJOs-as-Arrays style: "
+ ctxt.reportMappingException("Can not support implicit polymorphic deserialization for POJOs-as-Arrays style: "
+"nominal type %s, actual type %s",
_beanType.getRawClass().getName(), bean.getClass().getName());
}
@@ -342,8 +334,7 @@
try {
bean = creator.build(ctxt, buffer);
} catch (Exception e) {
- wrapInstantiationProblem(e, ctxt);
- return null; // never gets here
+ return wrapInstantiationProblem(e, ctxt);
}
}
return bean;
@@ -358,8 +349,8 @@
protected Object _deserializeFromNonArray(JsonParser p, DeserializationContext ctxt)
throws IOException
{
- // Let's start with failure
- throw ctxt.mappingException("Can not deserialize a POJO (of type %s) from non-Array representation (token: %s): "
+ return ctxt.handleUnexpectedToken(handledType(), p.getCurrentToken(), p,
+ "Can not deserialize a POJO (of type %s) from non-Array representation (token: %s): "
+"type/property designed to be serialized as JSON Array",
_beanType.getRawClass().getName(),
p.getCurrentToken());
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanPropertyMap.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanPropertyMap.java
index e6c6a79..fd30aaf 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanPropertyMap.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanPropertyMap.java
@@ -61,7 +61,34 @@
_propsInOrder = props.toArray(new SettableBeanProperty[props.size()]);
init(props);
}
-
+
+ /**
+ * @since 2.8
+ */
+ protected BeanPropertyMap(BeanPropertyMap base, boolean caseInsensitive)
+ {
+ _caseInsensitive = caseInsensitive;
+
+ // 16-May-2016, tatu: Alas, not enough to just change flag, need to re-init
+ // as well.
+ _propsInOrder = Arrays.copyOf(base._propsInOrder, base._propsInOrder.length);
+ init(Arrays.asList(_propsInOrder));
+ }
+
+ /**
+ * Mutant factory method that constructs a new instance if desired case-insensitivity
+ * state differs from the state of this instance; if states are the same, returns
+ * <code>this</code>.
+ *
+ * @since 2.8
+ */
+ public BeanPropertyMap withCaseInsensitivity(boolean state) {
+ if (_caseInsensitive == state) {
+ return this;
+ }
+ return new BeanPropertyMap(this, state);
+ }
+
protected void init(Collection<SettableBeanProperty> props)
{
_size = props.size();
@@ -208,7 +235,7 @@
}
/**
- * Factory method for constructing a map where all entries use given
+ * Mutant factory method for constructing a map where all entries use given
* prefix
*/
public BeanPropertyMap renameAll(NameTransformer transformer)
@@ -235,6 +262,36 @@
}
/**
+ * Mutant factory method that will use this instance as the base, and
+ * construct an instance that is otherwise same except for excluding
+ * properties with specified names.
+ *
+ * @since 2.8
+ */
+ public BeanPropertyMap withoutProperties(Collection<String> toExclude)
+ {
+ if (toExclude.isEmpty()) {
+ return this;
+ }
+ final int len = _propsInOrder.length;
+ ArrayList<SettableBeanProperty> newProps = new ArrayList<SettableBeanProperty>(len);
+
+ for (int i = 0; i < len; ++i) {
+ SettableBeanProperty prop = _propsInOrder[i];
+ // 01-May-2015, tatu: Not 100% sure if existing `null`s should be retained;
+ // or, if entries to ignore should be retained as nulls. For now just
+ // prune them out
+ if (prop != null) { // may contain holes, too, check.
+ if (!toExclude.contains(prop.getName())) {
+ newProps.add(prop);
+ }
+ }
+ }
+ // should we try to re-index? Apparently no need
+ return new BeanPropertyMap(_caseInsensitive, newProps);
+ }
+
+ /**
* Specialized method that can be used to replace an existing entry
* (note: entry MUST exist; otherwise exception is thrown) with
* specified replacement.
@@ -316,7 +373,7 @@
if (_caseInsensitive) {
key = key.toLowerCase();
}
-
+
// inlined `_hashCode(key)`
int slot = key.hashCode() & _hashMask;
// int h = key.hashCode();
@@ -481,7 +538,7 @@
if (!wrap || !(t instanceof JsonProcessingException)) {
throw (IOException) t;
}
- } else if (!wrap) { // [JACKSON-407] -- allow disabling wrapping for unchecked exceptions
+ } else if (!wrap) { // allow disabling wrapping for unchecked exceptions
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/CreatorCollector.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/CreatorCollector.java
index 8e38f5d..3a0c53b 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/CreatorCollector.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/CreatorCollector.java
@@ -1,7 +1,8 @@
package com.fasterxml.jackson.databind.deser.impl;
-import java.io.IOException;
+import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Member;
+import java.lang.reflect.Type;
import java.util.*;
import com.fasterxml.jackson.databind.*;
@@ -17,8 +18,7 @@
* Container class for storing information on creators (based on annotations,
* visibility), to be able to build actual instantiator later on.
*/
-public class CreatorCollector
-{
+public class CreatorCollector {
// Since 2.5
protected final static int C_DEFAULT = 0;
protected final static int C_STRING = 1;
@@ -30,11 +30,9 @@
protected final static int C_PROPS = 7;
protected final static int C_ARRAY_DELEGATE = 8;
- protected final static String[] TYPE_DESCS = new String[] {
- "default",
- "String", "int", "long", "double", "boolean",
- "delegate", "property-based"
- };
+ protected final static String[] TYPE_DESCS = new String[] { "default",
+ "from-String", "from-int", "from-long", "from-double",
+ "from-boolean", "delegate", "property-based" };
/// Type of bean being created
final protected BeanDescription _beanDesc;
@@ -45,7 +43,7 @@
* @since 2.7
*/
final protected boolean _forceAccess;
-
+
/**
* Set of creators we have collected so far
*
@@ -54,8 +52,9 @@
protected final AnnotatedWithParams[] _creators = new AnnotatedWithParams[9];
/**
- * Bitmask of creators that were explicitly marked as creators; false for auto-detected
- * (ones included base on naming and/or visibility, not annotation)
+ * Bitmask of creators that were explicitly marked as creators; false for
+ * auto-detected (ones included base on naming and/or visibility, not
+ * annotation)
*
* @since 2.5
*/
@@ -73,47 +72,37 @@
protected AnnotatedParameter _incompleteParameter;
/*
- /**********************************************************
- /* Life-cycle
- /**********************************************************
+ * /********************************************************** /* Life-cycle
+ * /**********************************************************
*/
-
- public CreatorCollector(BeanDescription beanDesc, MapperConfig<?> config)
- {
+
+ public CreatorCollector(BeanDescription beanDesc, MapperConfig<?> config) {
_beanDesc = beanDesc;
_canFixAccess = config.canOverrideAccessModifiers();
- _forceAccess = config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS);
+ _forceAccess = config
+ .isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS);
}
- public ValueInstantiator constructValueInstantiator(DeserializationConfig config)
- {
- final JavaType delegateType = _computeDelegateType(_creators[C_DELEGATE], _delegateArgs);
- final JavaType arrayDelegateType = _computeDelegateType(_creators[C_ARRAY_DELEGATE], _arrayDelegateArgs);
+ public ValueInstantiator constructValueInstantiator(
+ DeserializationConfig config) {
+ final JavaType delegateType = _computeDelegateType(
+ _creators[C_DELEGATE], _delegateArgs);
+ final JavaType arrayDelegateType = _computeDelegateType(
+ _creators[C_ARRAY_DELEGATE], _arrayDelegateArgs);
final JavaType type = _beanDesc.getType();
- // Any non-standard creator will prevent; with one exception: int-valued constructor
- // that standard containers have can be ignored
- if (!_hasNonDefaultCreator) {
- /* 10-May-2014, tatu: If we have nothing special, and we are dealing with one
- * of "well-known" types, can create a non-reflection-based instantiator.
- */
- final Class<?> rawType = type.getRawClass();
- if (rawType == Collection.class || rawType == List.class || rawType == ArrayList.class) {
- return new Vanilla(Vanilla.TYPE_COLLECTION);
- }
- if (rawType == Map.class || rawType == LinkedHashMap.class) {
- return new Vanilla(Vanilla.TYPE_MAP);
- }
- if (rawType == HashMap.class) {
- return new Vanilla(Vanilla.TYPE_HASH_MAP);
- }
- }
-
+ // 11-Jul-2016, tatu: Earlier optimization by replacing the whole
+ // instantiator did not
+ // work well, so let's replace by lower-level check:
+ AnnotatedWithParams defaultCtor = StdTypeConstructor
+ .tryToOptimize(_creators[C_DEFAULT]);
+
StdValueInstantiator inst = new StdValueInstantiator(config, type);
- inst.configureFromObjectSettings(_creators[C_DEFAULT],
- _creators[C_DELEGATE], delegateType, _delegateArgs,
- _creators[C_PROPS], _propertyBasedArgs);
- inst.configureFromArraySettings(_creators[C_ARRAY_DELEGATE], arrayDelegateType, _arrayDelegateArgs);
+ inst.configureFromObjectSettings(defaultCtor, _creators[C_DELEGATE],
+ delegateType, _delegateArgs, _creators[C_PROPS],
+ _propertyBasedArgs);
+ inst.configureFromArraySettings(_creators[C_ARRAY_DELEGATE],
+ arrayDelegateType, _arrayDelegateArgs);
inst.configureFromStringCreator(_creators[C_STRING]);
inst.configureFromIntCreator(_creators[C_INT]);
inst.configureFromLongCreator(_creators[C_LONG]);
@@ -122,45 +111,51 @@
inst.configureIncompleteParameter(_incompleteParameter);
return inst;
}
-
+
/*
- /**********************************************************
- /* Setters
- /**********************************************************
+ * /********************************************************** /* Setters
+ * /**********************************************************
*/
-
+
/**
- * Method called to indicate the default creator: no-arguments
- * constructor or factory method that is called to instantiate
- * a value before populating it with data. Default creator is
- * only used if no other creators are indicated.
+ * Method called to indicate the default creator: no-arguments constructor
+ * or factory method that is called to instantiate a value before populating
+ * it with data. Default creator is only used if no other creators are
+ * indicated.
*
- * @param creator Creator method; no-arguments constructor or static
- * factory method.
+ * @param creator
+ * Creator method; no-arguments constructor or static factory
+ * method.
*/
public void setDefaultCreator(AnnotatedWithParams creator) {
_creators[C_DEFAULT] = _fixAccess(creator);
}
-
- public void addStringCreator(AnnotatedWithParams creator, boolean explicit) {
+
+ public void addStringCreator(AnnotatedWithParams creator,
+ boolean explicit) {
verifyNonDup(creator, C_STRING, explicit);
}
+
public void addIntCreator(AnnotatedWithParams creator, boolean explicit) {
verifyNonDup(creator, C_INT, explicit);
}
+
public void addLongCreator(AnnotatedWithParams creator, boolean explicit) {
verifyNonDup(creator, C_LONG, explicit);
}
- public void addDoubleCreator(AnnotatedWithParams creator, boolean explicit) {
+
+ public void addDoubleCreator(AnnotatedWithParams creator,
+ boolean explicit) {
verifyNonDup(creator, C_DOUBLE, explicit);
}
- public void addBooleanCreator(AnnotatedWithParams creator, boolean explicit) {
+
+ public void addBooleanCreator(AnnotatedWithParams creator,
+ boolean explicit) {
verifyNonDup(creator, C_BOOLEAN, explicit);
}
- public void addDelegatingCreator(AnnotatedWithParams creator, boolean explicit,
- SettableBeanProperty[] injectables)
- {
+ public void addDelegatingCreator(AnnotatedWithParams creator,
+ boolean explicit, SettableBeanProperty[] injectables) {
if (creator.getParameterType(0).isCollectionLikeType()) {
verifyNonDup(creator, C_ARRAY_DELEGATE, explicit);
_arrayDelegateArgs = injectables;
@@ -169,25 +164,26 @@
_delegateArgs = injectables;
}
}
-
- public void addPropertyCreator(AnnotatedWithParams creator, boolean explicit,
- SettableBeanProperty[] properties)
- {
+
+ public void addPropertyCreator(AnnotatedWithParams creator,
+ boolean explicit, SettableBeanProperty[] properties) {
verifyNonDup(creator, C_PROPS, explicit);
// Better ensure we have no duplicate names either...
if (properties.length > 1) {
- HashMap<String,Integer> names = new HashMap<String,Integer>();
+ HashMap<String, Integer> names = new HashMap<String, Integer>();
for (int i = 0, len = properties.length; i < len; ++i) {
String name = properties[i].getName();
- /* [Issue-13]: Need to consider Injectables, which may not have
- * a name at all, and need to be skipped
- */
- if (name.length() == 0 && properties[i].getInjectableValueId() != null) {
+ // Need to consider Injectables, which may not have
+ // a name at all, and need to be skipped
+ if (name.length() == 0
+ && properties[i].getInjectableValueId() != null) {
continue;
}
Integer old = names.put(name, Integer.valueOf(i));
if (old != null) {
- throw new IllegalArgumentException("Duplicate creator property \""+name+"\" (index "+old+" vs "+i+")");
+ throw new IllegalArgumentException(String.format(
+ "Duplicate creator property \"%s\" (index %s vs %d)",
+ name, old, i));
}
}
}
@@ -201,42 +197,47 @@
}
// Bunch of methods deprecated in 2.5, to be removed from 2.6 or later
-
+
@Deprecated // since 2.5
public void addStringCreator(AnnotatedWithParams creator) {
addStringCreator(creator, false);
}
+
@Deprecated // since 2.5
public void addIntCreator(AnnotatedWithParams creator) {
addBooleanCreator(creator, false);
}
+
@Deprecated // since 2.5
public void addLongCreator(AnnotatedWithParams creator) {
addBooleanCreator(creator, false);
}
+
@Deprecated // since 2.5
public void addDoubleCreator(AnnotatedWithParams creator) {
addBooleanCreator(creator, false);
}
+
@Deprecated // since 2.5
public void addBooleanCreator(AnnotatedWithParams creator) {
addBooleanCreator(creator, false);
}
@Deprecated // since 2.5
- public void addDelegatingCreator(AnnotatedWithParams creator, CreatorProperty[] injectables) {
+ public void addDelegatingCreator(AnnotatedWithParams creator,
+ CreatorProperty[] injectables) {
addDelegatingCreator(creator, false, injectables);
}
@Deprecated // since 2.5
- public void addPropertyCreator(AnnotatedWithParams creator, CreatorProperty[] properties) {
+ public void addPropertyCreator(AnnotatedWithParams creator,
+ CreatorProperty[] properties) {
addPropertyCreator(creator, false, properties);
}
/*
- /**********************************************************
- /* Accessors
- /**********************************************************
+ * /********************************************************** /* Accessors
+ * /**********************************************************
*/
/**
@@ -259,16 +260,14 @@
public boolean hasPropertyBasedCreator() {
return _creators[C_PROPS] != null;
}
-
+
/*
- /**********************************************************
- /* Helper methods
- /**********************************************************
+ * /********************************************************** /* Helper
+ * methods /**********************************************************
*/
private JavaType _computeDelegateType(AnnotatedWithParams creator,
- SettableBeanProperty[] delegateArgs)
- {
+ SettableBeanProperty[] delegateArgs) {
if (!_hasNonDefaultCreator || (creator == null)) {
return null;
}
@@ -285,16 +284,16 @@
return creator.getParameterType(ix);
}
- private <T extends AnnotatedMember> T _fixAccess(T member)
- {
+ private <T extends AnnotatedMember> T _fixAccess(T member) {
if (member != null && _canFixAccess) {
- ClassUtil.checkAndFixAccess((Member) member.getAnnotated(), _forceAccess);
+ ClassUtil.checkAndFixAccess((Member) member.getAnnotated(),
+ _forceAccess);
}
return member;
}
- protected void verifyNonDup(AnnotatedWithParams newOne, int typeIndex, boolean explicit)
- {
+ protected void verifyNonDup(AnnotatedWithParams newOne, int typeIndex,
+ boolean explicit) {
final int mask = (1 << typeIndex);
_hasNonDefaultCreator = true;
AnnotatedWithParams oldOne = _creators[typeIndex];
@@ -302,7 +301,8 @@
if (oldOne != null) {
boolean verify;
- if ((_explicitCreators & mask) != 0) { // already had explicitly annotated, leave as-is
+ if ((_explicitCreators & mask) != 0) { // already had explicitly
+ // annotated, leave as-is
// but skip, if new one not annotated
if (!explicit) {
return;
@@ -321,11 +321,27 @@
Class<?> newType = newOne.getRawParameterType(0);
if (oldType == newType) {
- throw new IllegalArgumentException("Conflicting "+TYPE_DESCS[typeIndex]
- +" creators: already had explicitly marked "+oldOne+", encountered "+newOne);
+ // 13-Jul-2016, tatu: One more thing to check; since Enum
+ // classes always have
+ // implicitly created `valueOf()`, let's resolve in favor of
+ // other implicit
+ // creator (`fromString()`)
+ if (_isEnumValueOf(newOne)) {
+ return; // ignore
+ }
+ if (_isEnumValueOf(oldOne)) {
+ ;
+ } else {
+ throw new IllegalArgumentException(String.format(
+ "Conflicting %s creators: already had %s creator %s, encountered another: %s",
+ TYPE_DESCS[typeIndex],
+ explicit ? "explicitly marked"
+ : "implicitly discovered",
+ oldOne, newOne));
+ }
}
// otherwise, which one to choose?
- if (newType.isAssignableFrom(oldType)) {
+ else if (newType.isAssignableFrom(oldType)) {
// new type more generic, use old
return;
}
@@ -338,53 +354,178 @@
_creators[typeIndex] = _fixAccess(newOne);
}
+ /**
+ * Helper method for recognizing `Enum.valueOf()` factory method
+ *
+ * @since 2.8.1
+ */
+ protected boolean _isEnumValueOf(AnnotatedWithParams creator) {
+ return creator.getDeclaringClass().isEnum()
+ && "valueOf".equals(creator.getName());
+ }
+
/*
- /**********************************************************
- /* Helper class(es)
- /**********************************************************
+ * /********************************************************** /* Helper
+ * class(es) /**********************************************************
*/
- protected final static class Vanilla
- extends ValueInstantiator
- implements java.io.Serializable
- {
+ /**
+ * Replacement for default constructor to use for a small set of
+ * "well-known" types.
+ * <p>
+ * Note: replaces earlier <code>Vanilla</code>
+ * <code>ValueInstantiator</code> implementation
+ *
+ * @since 2.8.1 (replacing earlier <code>Vanilla</code> instantiator
+ */
+ protected final static class StdTypeConstructor extends AnnotatedWithParams
+ implements java.io.Serializable {
private static final long serialVersionUID = 1L;
- public final static int TYPE_COLLECTION = 1;
- public final static int TYPE_MAP = 2;
- public final static int TYPE_HASH_MAP = 3;
+ public final static int TYPE_ARRAY_LIST = 1;
+ public final static int TYPE_HASH_MAP = 2;
+ public final static int TYPE_LINKED_HASH_MAP = 3;
+
+ private final AnnotatedWithParams _base;
private final int _type;
-
- public Vanilla(int t) {
+
+ public StdTypeConstructor(AnnotatedWithParams base, int t) {
+ super(base, null);
+ _base = base;
_type = t;
}
-
-
- @Override
- public String getValueTypeDesc() {
- switch (_type) {
- case TYPE_COLLECTION: return ArrayList.class.getName();
- case TYPE_MAP: return LinkedHashMap.class.getName();
- case TYPE_HASH_MAP: return HashMap.class.getName();
+
+ public static AnnotatedWithParams tryToOptimize(
+ AnnotatedWithParams src) {
+ if (src != null) {
+ final Class<?> rawType = src.getDeclaringClass();
+ if (rawType == List.class || rawType == ArrayList.class) {
+ return new StdTypeConstructor(src, TYPE_ARRAY_LIST);
+ }
+ if (rawType == LinkedHashMap.class) {
+ return new StdTypeConstructor(src, TYPE_LINKED_HASH_MAP);
+ }
+ if (rawType == HashMap.class) {
+ return new StdTypeConstructor(src, TYPE_HASH_MAP);
+ }
}
- return Object.class.getName();
+ return src;
+ }
+
+ protected final Object _construct() {
+ switch (_type) {
+ case TYPE_ARRAY_LIST:
+ return new ArrayList<Object>();
+ case TYPE_LINKED_HASH_MAP:
+ return new LinkedHashMap<String, Object>();
+ case TYPE_HASH_MAP:
+ return new HashMap<String, Object>();
+ }
+ throw new IllegalStateException("Unknown type " + _type);
}
@Override
- public boolean canInstantiate() { return true; }
+ public int getParameterCount() {
+ return _base.getParameterCount();
+ }
@Override
- public boolean canCreateUsingDefault() { return true; }
+ public Class<?> getRawParameterType(int index) {
+ return _base.getRawParameterType(index);
+ }
@Override
- public Object createUsingDefault(DeserializationContext ctxt) throws IOException {
- switch (_type) {
- case TYPE_COLLECTION: return new ArrayList<Object>();
- case TYPE_MAP: return new LinkedHashMap<String,Object>();
- case TYPE_HASH_MAP: return new HashMap<String,Object>();
- }
- throw new IllegalStateException("Unknown type "+_type);
+ public JavaType getParameterType(int index) {
+ return _base.getParameterType(index);
+ }
+
+ @Override
+ @Deprecated
+ public Type getGenericParameterType(int index) {
+ return _base.getGenericParameterType(index);
+ }
+
+ @Override
+ public Object call() throws Exception {
+ return _construct();
+ }
+
+ @Override
+ public Object call(Object[] args) throws Exception {
+ return _construct();
+ }
+
+ @Override
+ public Object call1(Object arg) throws Exception {
+ return _construct();
+ }
+
+ @Override
+ public Class<?> getDeclaringClass() {
+ return _base.getDeclaringClass();
+ }
+
+ @Override
+ public Member getMember() {
+ return _base.getMember();
+ }
+
+ @Override
+ public void setValue(Object pojo, Object value)
+ throws UnsupportedOperationException, IllegalArgumentException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Object getValue(Object pojo)
+ throws UnsupportedOperationException, IllegalArgumentException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Annotated withAnnotations(AnnotationMap fallback) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public AnnotatedElement getAnnotated() {
+ return _base.getAnnotated();
+ }
+
+ @Override
+ protected int getModifiers() {
+ return _base.getMember().getModifiers();
+ }
+
+ @Override
+ public String getName() {
+ return _base.getName();
+ }
+
+ @Override
+ public JavaType getType() {
+ return _base.getType();
+ }
+
+ @Override
+ public Class<?> getRawType() {
+ return _base.getRawType();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return (o == this);
+ }
+
+ @Override
+ public int hashCode() {
+ return _base.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return _base.toString();
}
}
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/ErrorThrowingDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ErrorThrowingDeserializer.java
new file mode 100644
index 0000000..97d43a6
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ErrorThrowingDeserializer.java
@@ -0,0 +1,29 @@
+package com.fasterxml.jackson.databind.deser.impl;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+
+/**
+ * A deserializer that stores an {@link Error} caught during constructing
+ * of the deserializer, which needs to be deferred and only during actual
+ * attempt to deserialize a value of given type.
+ * Note that null and empty values can be deserialized without error.
+ *
+ * @since 2.9 Note: prior to this version was named <code>NoClassDefFoundDeserializer</code>
+ */
+public class ErrorThrowingDeserializer extends JsonDeserializer<Object>
+{
+ private final Error _cause;
+
+ public ErrorThrowingDeserializer(NoClassDefFoundError cause) {
+ _cause = cause;
+ }
+
+ @Override
+ public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
+ throw _cause;
+ }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/ExternalTypeHandler.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ExternalTypeHandler.java
index 26af9bc..4069f38 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/ExternalTypeHandler.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ExternalTypeHandler.java
@@ -23,12 +23,12 @@
private final String[] _typeIds;
private final TokenBuffer[] _tokens;
-
+
protected ExternalTypeHandler(ExtTypedProperty[] properties,
HashMap<String, Integer> nameToPropertyIndex,
String[] typeIds, TokenBuffer[] tokens)
{
- _properties = properties;
+ _properties = properties;
_nameToPropertyIndex = nameToPropertyIndex;
_typeIds = typeIds;
_tokens = tokens;
@@ -42,7 +42,11 @@
_typeIds = new String[len];
_tokens = new TokenBuffer[len];
}
-
+
+ /**
+ * Method called to start collection process by creating non-blueprint
+ * instances.
+ */
public ExternalTypeHandler start() {
return new ExternalTypeHandler(this);
}
@@ -53,7 +57,7 @@
* containing POJO has similarly named property as the external type id;
* otherwise {@link #handlePropertyValue} should be called instead.
*/
- public boolean handleTypePropertyValue(JsonParser jp, DeserializationContext ctxt,
+ public boolean handleTypePropertyValue(JsonParser p, DeserializationContext ctxt,
String propName, Object bean)
throws IOException
{
@@ -66,12 +70,12 @@
if (!prop.hasTypePropertyName(propName)) {
return false;
}
- String typeId = jp.getText();
+ String typeId = p.getText();
// note: can NOT skip child values (should always be String anyway)
boolean canDeserialize = (bean != null) && (_tokens[index] != null);
// Minor optimization: deserialize properties as soon as we have all we need:
if (canDeserialize) {
- _deserializeAndSet(jp, ctxt, bean, index, typeId);
+ _deserializeAndSet(p, ctxt, bean, index, typeId);
// clear stored data, to avoid deserializing+setting twice:
_tokens[index] = null;
} else {
@@ -79,7 +83,7 @@
}
return true;
}
-
+
/**
* Method called to ask handler to handle value of given property,
* at point where parser points to the first token of the value.
@@ -153,15 +157,21 @@
}
// 26-Oct-2012, tatu: As per [databind#94], must allow use of 'defaultImpl'
if (!_properties[i].hasDefaultType()) {
- throw ctxt.mappingException("Missing external type id property '%s'",
+ ctxt.reportMappingException("Missing external type id property '%s'",
_properties[i].getTypePropertyName());
+ } else {
+ typeId = _properties[i].getDefaultTypeId();
}
- typeId = _properties[i].getDefaultTypeId();
}
} else if (_tokens[i] == null) {
SettableBeanProperty prop = _properties[i].getProperty();
- throw ctxt.mappingException("Missing property '%s' for external type id '%s'",
- prop.getName(), _properties[i].getTypePropertyName());
+
+ if(prop.isRequired() ||
+ ctxt.isEnabled(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY)) {
+ ctxt.reportMappingException("Missing property '%s' for external type id '%s'",
+ prop.getName(), _properties[i].getTypePropertyName());
+ }
+ return bean;
}
_deserializeAndSet(p, ctxt, bean, i, typeId);
}
@@ -172,7 +182,7 @@
* Variant called when creation of the POJO involves buffering of creator properties
* as well as property-based creator.
*/
- public Object complete(JsonParser jp, DeserializationContext ctxt,
+ public Object complete(JsonParser p, DeserializationContext ctxt,
PropertyValueBuffer buffer, PropertyBasedCreator creator)
throws IOException
{
@@ -181,37 +191,47 @@
Object[] values = new Object[len];
for (int i = 0; i < len; ++i) {
String typeId = _typeIds[i];
+ final ExtTypedProperty extProp = _properties[i];
+
if (typeId == null) {
// let's allow missing both type and property (may already have been set, too)
if (_tokens[i] == null) {
continue;
}
// but not just one
- // 26-Oct-2012, tatu: As per [Issue#94], must allow use of 'defaultImpl'
- if (!_properties[i].hasDefaultType()) {
- throw ctxt.mappingException("Missing external type id property '%s'",
- _properties[i].getTypePropertyName());
+ // 26-Oct-2012, tatu: As per [databind#94], must allow use of 'defaultImpl'
+ if (!extProp.hasDefaultType()) {
+ ctxt.reportMappingException("Missing external type id property '%s'",
+ extProp.getTypePropertyName());
+ } else {
+ typeId = extProp.getDefaultTypeId();
}
- typeId = _properties[i].getDefaultTypeId();
} else if (_tokens[i] == null) {
- SettableBeanProperty prop = _properties[i].getProperty();
- throw ctxt.mappingException("Missing property '%s' for external type id '%s'",
+ SettableBeanProperty prop = extProp.getProperty();
+ ctxt.reportMappingException("Missing property '%s' for external type id '%s'",
prop.getName(), _properties[i].getTypePropertyName());
}
- values[i] = _deserialize(jp, ctxt, i, typeId);
- }
- // second: fill in creator properties:
- for (int i = 0; i < len; ++i) {
- SettableBeanProperty prop = _properties[i].getProperty();
- if (creator.findCreatorProperty(prop.getName()) != null) {
+ values[i] = _deserialize(p, ctxt, i, typeId);
+
+ final SettableBeanProperty prop = extProp.getProperty();
+ // also: if it's creator prop, fill in
+ if (prop.getCreatorIndex() >= 0) {
buffer.assignParameter(prop, values[i]);
+
+ // [databind#999] And maybe there's creator property for type id too?
+ SettableBeanProperty typeProp = extProp.getTypeProperty();
+ // for now, should only be needed for creator properties, too
+ if ((typeProp != null) && (typeProp.getCreatorIndex() >= 0)) {
+ buffer.assignParameter(typeProp, typeId);
+ }
}
}
+
Object bean = creator.build(ctxt, buffer);
// third: assign non-creator properties
for (int i = 0; i < len; ++i) {
SettableBeanProperty prop = _properties[i].getProperty();
- if (creator.findCreatorProperty(prop.getName()) == null) {
+ if (prop.getCreatorIndex() < 0) {
prop.set(bean, values[i]);
}
}
@@ -285,6 +305,30 @@
_nameToPropertyIndex.put(typeDeser.getPropertyName(), index);
}
+ /**
+ * Method called after all external properties have been assigned, to further
+ * link property with polymorphic value with possible property for type id
+ * itself. This is needed to support type ids as Creator properties.
+ *
+ * @since 2.8
+ */
+ public ExternalTypeHandler build(BeanPropertyMap otherProps) {
+ // 21-Jun-2016, tatu: as per [databind#999], may need to link type id property also
+ final int len = _properties.size();
+ ExtTypedProperty[] extProps = new ExtTypedProperty[len];
+ for (int i = 0; i < len; ++i) {
+ ExtTypedProperty extProp = _properties.get(i);
+ String typePropId = extProp.getTypePropertyName();
+ SettableBeanProperty typeProp = otherProps.find(typePropId);
+ if (typeProp != null) {
+ extProp.linkTypeProperty(typeProp);
+ }
+ extProps[i] = extProp;
+ }
+ return new ExternalTypeHandler(extProps, _nameToPropertyIndex, null, null);
+ }
+
+ @Deprecated // since 2.8; may be removed as early as 2.9
public ExternalTypeHandler build() {
return new ExternalTypeHandler(_properties.toArray(new ExtTypedProperty[_properties.size()]),
_nameToPropertyIndex, null, null);
@@ -297,6 +341,11 @@
private final TypeDeserializer _typeDeserializer;
private final String _typePropertyName;
+ /**
+ * @since 2.8
+ */
+ private SettableBeanProperty _typeProperty;
+
public ExtTypedProperty(SettableBeanProperty property, TypeDeserializer typeDeser)
{
_property = property;
@@ -304,6 +353,13 @@
_typePropertyName = typeDeser.getPropertyName();
}
+ /**
+ * @since 2.8
+ */
+ public void linkTypeProperty(SettableBeanProperty p) {
+ _typeProperty = p;
+ }
+
public boolean hasTypePropertyName(String n) {
return n.equals(_typePropertyName);
}
@@ -330,5 +386,12 @@
public SettableBeanProperty getProperty() {
return _property;
}
+
+ /**
+ * @since 2.8
+ */
+ public SettableBeanProperty getTypeProperty() {
+ return _typeProperty;
+ }
}
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/FailingDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/FailingDeserializer.java
index e6a5a7e..a777bde 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/FailingDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/FailingDeserializer.java
@@ -24,6 +24,7 @@
@Override
public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws JsonMappingException{
- throw ctxt.mappingException(_message);
+ ctxt.reportMappingException(_message);
+ return null;
}
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/FieldProperty.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/FieldProperty.java
index 15fca54..e4e33df 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/FieldProperty.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/FieldProperty.java
@@ -12,6 +12,7 @@
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.util.Annotations;
+import com.fasterxml.jackson.databind.util.ClassUtil;
/**
* This concrete sub-class implements property that is set
@@ -74,7 +75,13 @@
public FieldProperty withValueDeserializer(JsonDeserializer<?> deser) {
return new FieldProperty(this, deser);
}
-
+
+ @Override
+ public void fixAccess(DeserializationConfig config) {
+ ClassUtil.checkAndFixAccess(_field,
+ config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
+ }
+
/*
/**********************************************************
/* BeanProperty impl
@@ -125,7 +132,7 @@
try {
_field.set(instance, value);
} catch (Exception e) {
- // 15-Sep-2015, tatu: How coud we get a ref to JsonParser?
+ // 15-Sep-2015, tatu: How could we get a ref to JsonParser?
_throwAsIOE(e, value);
}
}
@@ -136,7 +143,7 @@
try {
_field.set(instance, value);
} catch (Exception e) {
- // 15-Sep-2015, tatu: How coud we get a ref to JsonParser?
+ // 15-Sep-2015, tatu: How could we get a ref to JsonParser?
_throwAsIOE(e, value);
}
return instance;
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/InnerClassProperty.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/InnerClassProperty.java
index 311802e..1fa76fe 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/InnerClassProperty.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/InnerClassProperty.java
@@ -84,6 +84,11 @@
return new InnerClassProperty(this, deser);
}
+ @Override
+ public void fixAccess(DeserializationConfig config) {
+ _delegate.fixAccess(config);
+ }
+
// // // BeanProperty impl
@Override
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/ManagedReferenceProperty.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ManagedReferenceProperty.java
index 028cc09..f8bcd3d 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/ManagedReferenceProperty.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ManagedReferenceProperty.java
@@ -6,10 +6,7 @@
import java.util.Map;
import com.fasterxml.jackson.core.JsonParser;
-
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JsonDeserializer;
-import com.fasterxml.jackson.databind.PropertyName;
+import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.util.Annotations;
@@ -25,17 +22,17 @@
private static final long serialVersionUID = 1L;
protected final String _referenceName;
-
+
/**
* Flag that indicates whether property to handle is a container type
* (array, Collection, Map) or not.
*/
protected final boolean _isContainer;
-
+
protected final SettableBeanProperty _managedProperty;
protected final SettableBeanProperty _backProperty;
-
+
public ManagedReferenceProperty(SettableBeanProperty forward, String refName,
SettableBeanProperty backward, Annotations contextAnnotations, boolean isContainer)
{
@@ -69,18 +66,24 @@
public ManagedReferenceProperty withName(PropertyName newName) {
return new ManagedReferenceProperty(this, newName);
}
-
+
@Override
public ManagedReferenceProperty withValueDeserializer(JsonDeserializer<?> deser) {
return new ManagedReferenceProperty(this, deser);
}
-
+
+ @Override
+ public void fixAccess(DeserializationConfig config) {
+ _managedProperty.fixAccess(config);
+ _backProperty.fixAccess(config);
+ }
+
/*
/**********************************************************
/* BeanProperty impl
/**********************************************************
*/
-
+
@Override
public <A extends Annotation> A getAnnotation(Class<A> acls) {
return _managedProperty.getAnnotation(acls);
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/MethodProperty.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/MethodProperty.java
index 86d8f9a..d73b1b9 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/MethodProperty.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/MethodProperty.java
@@ -68,7 +68,13 @@
public MethodProperty withValueDeserializer(JsonDeserializer<?> deser) {
return new MethodProperty(this, deser);
}
-
+
+ @Override
+ public void fixAccess(DeserializationConfig config) {
+ _annotated.fixAccess(
+ config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
+ }
+
/*
/**********************************************************
/* BeanProperty impl
@@ -113,7 +119,7 @@
return null;
}
}
-
+
@Override
public final void set(Object instance, Object value) throws IOException
{
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/NoClassDefFoundDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/NoClassDefFoundDeserializer.java
deleted file mode 100644
index a094670..0000000
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/NoClassDefFoundDeserializer.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.fasterxml.jackson.databind.deser.impl;
-
-import java.io.IOException;
-
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JsonDeserializer;
-
-/**
- * A deserializer that stores a {@link NoClassDefFoundError} error
- * and throws the stored exception when attempting to deserialize
- * a value. Null and empty values can be deserialized without error.
- *
- * @since 2.5
- */
-public class NoClassDefFoundDeserializer<T> extends JsonDeserializer<T>
-{
- private final NoClassDefFoundError _cause;
-
- public NoClassDefFoundDeserializer(NoClassDefFoundError cause)
- {
- _cause = cause;
- }
-
- @Override
- public T deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException
- {
- throw _cause;
- }
-}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/NullProvider.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/NullProvider.java
deleted file mode 100644
index 24ec174..0000000
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/NullProvider.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package com.fasterxml.jackson.databind.deser.impl;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.databind.JavaType;
-
-/**
- * @deprecated
- */
-@Deprecated // since 2.6, remove in 2.7
-public final class NullProvider
- implements java.io.Serializable
-{
- private static final long serialVersionUID = 1L;
-
- private final Object _nullValue;
-
- private final boolean _isPrimitive;
-
- private final Class<?> _rawType;
-
- public NullProvider(JavaType type, Object nullValue)
- {
- _nullValue = nullValue;
- _isPrimitive = type.isPrimitive();
- _rawType = type.getRawClass();
- }
-
- public Object nullValue(DeserializationContext ctxt) throws JsonProcessingException
- {
- if (_isPrimitive && ctxt.isEnabled(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)) {
- throw ctxt.mappingException("Can not map JSON null into type %s"
- +" (set DeserializationConfig.DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES to 'false' to allow)",
- _rawType.getName());
- }
- return _nullValue;
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/ObjectIdReferenceProperty.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ObjectIdReferenceProperty.java
index c77d756..c7793d3 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/ObjectIdReferenceProperty.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ObjectIdReferenceProperty.java
@@ -49,6 +49,13 @@
}
@Override
+ public void fixAccess(DeserializationConfig config) {
+ if (_forward != null) {
+ _forward.fixAccess(config);
+ }
+ }
+
+ @Override
public <A extends Annotation> A getAnnotation(Class<A> acls) {
return _forward.getAnnotation(acls);
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyBasedCreator.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyBasedCreator.java
index f1d35ec..c9b89fa 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyBasedCreator.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyBasedCreator.java
@@ -133,7 +133,7 @@
public Object build(DeserializationContext ctxt, PropertyValueBuffer buffer) throws IOException
{
Object bean = _valueInstantiator.createFromObjectWith(ctxt,
- buffer.getParameters(_allProperties));
+ _allProperties, buffer);
// returning null isn't quite legal, but let's let caller deal with that
if (bean != null) {
// Object Id to handle?
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer.java
index 32e100b..42d33ca 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer.java
@@ -82,10 +82,10 @@
/**********************************************************
*/
- public PropertyValueBuffer(JsonParser jp, DeserializationContext ctxt, int paramCount,
+ public PropertyValueBuffer(JsonParser p, DeserializationContext ctxt, int paramCount,
ObjectIdReader oir)
{
- _parser = jp;
+ _parser = p;
_context = ctxt;
_paramsNeeded = paramCount;
_objectIdReader = oir;
@@ -98,13 +98,55 @@
}
/**
+ * Returns {@code true} if the given property was seen in the JSON source by
+ * this buffer.
+ *
+ * @since 2.8
+ */
+ public final boolean hasParameter(SettableBeanProperty prop)
+ {
+ if (_paramsSeenBig == null) {
+ return ((_paramsSeen >> prop.getCreatorIndex()) & 1) == 1;
+ } else {
+ return _paramsSeenBig.get(prop.getCreatorIndex());
+ }
+ }
+
+ /**
+ * A variation of {@link #getParameters(SettableBeanProperty[])} that
+ * accepts a single property. Whereas the plural form eagerly fetches and
+ * validates all properties, this method may be used (along with
+ * {@link #hasParameter(SettableBeanProperty)}) to let applications only
+ * fetch the properties defined in the JSON source itself, and to have some
+ * other customized behavior for missing properties.
+ *
+ * @since 2.8
+ */
+ public Object getParameter(SettableBeanProperty prop)
+ throws JsonMappingException
+ {
+ Object value;
+ if (hasParameter(prop)) {
+ value = _creatorParameters[prop.getCreatorIndex()];
+ } else {
+ value = _creatorParameters[prop.getCreatorIndex()] = _findMissing(prop);
+ }
+ if (value == null && _context.isEnabled(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES)) {
+ throw _context.mappingException(
+ "Null value for creator property '%s'; DeserializationFeature.FAIL_ON_NULL_FOR_CREATOR_PARAMETERS enabled",
+ prop.getName(), prop.getCreatorIndex());
+ }
+ return value;
+ }
+
+ /**
* Method called to do necessary post-processing such as injection of values
* and verification of values for required properties,
* after either {@link #assignParameter(SettableBeanProperty, Object)}
* returns <code>true</code> (to indicate all creator properties are found), or when
* then whole JSON Object has been processed,
*/
- protected Object[] getParameters(SettableBeanProperty[] props)
+ public Object[] getParameters(SettableBeanProperty[] props)
throws JsonMappingException
{
// quick check to see if anything else is needed
@@ -125,6 +167,16 @@
}
}
}
+
+ if (_context.isEnabled(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES)) {
+ for (int ix = 0; ix < props.length; ++ix) {
+ if (_creatorParameters[ix] == null) {
+ _context.reportMappingException("Null value for creator property '%s'; DeserializationFeature.FAIL_ON_NULL_FOR_CREATOR_PARAMETERS enabled",
+ props[ix].getName(), props[ix].getCreatorIndex());
+ }
+ }
+ }
+
return _creatorParameters;
}
@@ -138,11 +190,11 @@
}
// Second: required?
if (prop.isRequired()) {
- throw _context.mappingException("Missing required creator property '%s' (index %d)",
+ _context.reportMappingException("Missing required creator property '%s' (index %d)",
prop.getName(), prop.getCreatorIndex());
}
if (_context.isEnabled(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES)) {
- throw _context.mappingException("Missing creator property '%s' (index %d); DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES enabled",
+ _context.reportMappingException("Missing creator property '%s' (index %d); DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES enabled",
prop.getName(), prop.getCreatorIndex());
}
// Third: default value
@@ -156,7 +208,6 @@
/**********************************************************
*/
-
/**
* Helper method called to see if given non-creator property is the "id property";
* and if so, handle appropriately.
@@ -187,9 +238,8 @@
return idProp.setAndReturn(bean, _idValue);
}
} else {
- // TODO: is this an error case?
- throw ctxt.mappingException("No _idValue when handleIdValue called, on instance of %s",
- bean.getClass().getName());
+ // 07-Jun-2016, tatu: Trying to improve error messaging here...
+ ctxt.reportUnresolvedObjectId(_objectIdReader, bean);
}
}
return bean;
@@ -228,17 +278,7 @@
}
return false;
}
-
- /**
- * @deprecated Since 2.6
- */
- @Deprecated
- public boolean assignParameter(int index, Object value) {
- // !!! TODO: remove from 2.7
- _creatorParameters[index] = value;
- return false;
- }
-
+
public void bufferProperty(SettableBeanProperty prop, Object value) {
_buffered = new PropertyValue.Regular(_buffered, value, prop);
}
@@ -251,4 +291,3 @@
_buffered = new PropertyValue.Map(_buffered, value, key);
}
}
-
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/ReadableObjectId.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ReadableObjectId.java
index a06d80c..6c3b804 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/ReadableObjectId.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ReadableObjectId.java
@@ -9,6 +9,7 @@
import com.fasterxml.jackson.annotation.ObjectIdResolver;
import com.fasterxml.jackson.core.JsonLocation;
import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.deser.UnresolvedForwardReference;
/**
@@ -18,14 +19,9 @@
public class ReadableObjectId
{
/**
- * @deprecated Change visibility, if possible; prefer using {@link #resolve()}, which is able
- * to handle external id resolving mechanism.
+ * @since 2.8 (with this name, formerly `public Object item`)
*/
- @Deprecated // at least since 2.5. Remove from 2.7
- public Object item;
-
- @Deprecated
- public final Object id;
+ protected Object _item;
protected final ObjectIdGenerator.IdKey _key;
@@ -33,15 +29,8 @@
protected ObjectIdResolver _resolver;
- @Deprecated // at least since 2.5. Remove from 2.7
- public ReadableObjectId(Object id) {
- this.id = id;
- _key = null;
- }
-
public ReadableObjectId(ObjectIdGenerator.IdKey key) {
_key = key;
- id = key.key;
}
public void setResolver(ObjectIdResolver resolver) {
@@ -66,7 +55,8 @@
public void bindItem(Object ob) throws IOException
{
_resolver.bindItem(_key, ob);
- item = ob;
+ _item = ob;
+ Object id = _key.key;
if (_referringProperties != null) {
Iterator<Referring> it = _referringProperties.iterator();
_referringProperties = null;
@@ -77,7 +67,7 @@
}
public Object resolve(){
- return (item = _resolver.resolveId(_key));
+ return (_item = _resolver.resolveId(_key));
}
public boolean hasReferringProperties() {
@@ -144,6 +134,11 @@
_beanType = beanType;
}
+ public Referring(UnresolvedForwardReference ref, JavaType beanType) {
+ _reference = ref;
+ _beanType = beanType.getRawClass();
+ }
+
public JsonLocation getLocation() { return _reference.getLocation(); }
public Class<?> getBeanType() { return _beanType; }
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/SetterlessProperty.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/SetterlessProperty.java
index 4daaaed..1d7e5ba 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/SetterlessProperty.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/SetterlessProperty.java
@@ -33,7 +33,8 @@
protected final Method _getter;
public SetterlessProperty(BeanPropertyDefinition propDef, JavaType type,
- TypeDeserializer typeDeser, Annotations contextAnnotations, AnnotatedMethod method) {
+ TypeDeserializer typeDeser, Annotations contextAnnotations, AnnotatedMethod method)
+ {
super(propDef, type, typeDeser, contextAnnotations);
_annotated = method;
_getter = method.getAnnotated();
@@ -60,7 +61,13 @@
public SetterlessProperty withValueDeserializer(JsonDeserializer<?> deser) {
return new SetterlessProperty(this, deser);
}
-
+
+ @Override
+ public void fixAccess(DeserializationConfig config) {
+ _annotated.fixAccess(
+ config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
+ }
+
/*
/**********************************************************
/* BeanProperty impl
@@ -94,9 +101,10 @@
// For [#501] fix we need to implement this but:
if (_valueTypeDeserializer != null) {
- throw JsonMappingException.from(p,
- "Problem deserializing 'setterless' property (\""+getName()+"\"): no way to handle typed deser with setterless yet");
-// return _valueDeserializer.deserializeWithType(jp, ctxt, _valueTypeDeserializer);
+ ctxt.reportMappingException(
+ "Problem deserializing 'setterless' property (\"%s\"): no way to handle typed deser with setterless yet",
+ getName());
+// return _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer);
}
// Ok: then, need to fetch Collection/Map to modify:
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/AtomicReferenceDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/AtomicReferenceDeserializer.java
index eba75e1..8faa423 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/AtomicReferenceDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/AtomicReferenceDeserializer.java
@@ -1,87 +1,54 @@
package com.fasterxml.jackson.databind.deser.std;
-import java.io.IOException;
import java.util.concurrent.atomic.AtomicReference;
-import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
-import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
public class AtomicReferenceDeserializer
- extends StdDeserializer<AtomicReference<?>>
- implements ContextualDeserializer
+ extends ReferenceTypeDeserializer<AtomicReference<Object>>
{
private static final long serialVersionUID = 1L;
- /**
- * Type of value that we reference
+ /*
+ /**********************************************************
+ /* Life-cycle
+ /**********************************************************
*/
- protected final JavaType _referencedType;
-
- protected final TypeDeserializer _valueTypeDeserializer;
- protected final JsonDeserializer<?> _valueDeserializer;
-
- /**
- * @param referencedType Parameterization of this reference
- */
- public AtomicReferenceDeserializer(JavaType referencedType) {
- this(referencedType, null, null);
+ @Deprecated // since 2.8
+ public AtomicReferenceDeserializer(JavaType fullType) {
+ this(fullType, null, null);
}
- public AtomicReferenceDeserializer(JavaType referencedType,
+ public AtomicReferenceDeserializer(JavaType fullType,
TypeDeserializer typeDeser, JsonDeserializer<?> deser)
{
- super(AtomicReference.class);
- _referencedType = referencedType;
- _valueDeserializer = deser;
- _valueTypeDeserializer = typeDeser;
+ super(fullType, typeDeser, deser);
}
+ /*
+ /**********************************************************
+ /* Abstract method implementations
+ /**********************************************************
+ */
+
+ @Override
public AtomicReferenceDeserializer withResolved(TypeDeserializer typeDeser, JsonDeserializer<?> valueDeser) {
- if ((valueDeser == _valueDeserializer) && (typeDeser == _valueTypeDeserializer)) {
- return this;
- }
- return new AtomicReferenceDeserializer(_referencedType, typeDeser, valueDeser);
+ return new AtomicReferenceDeserializer(_fullType, typeDeser, valueDeser);
}
@Override
- public AtomicReference<?> getNullValue(DeserializationContext ctxt) {
- return new AtomicReference<Object>();
- }
-
- @Deprecated // remove in 2.7
- @Override
- public AtomicReference<?> getNullValue() {
+ public AtomicReference<Object> getNullValue(DeserializationContext ctxt) {
return new AtomicReference<Object>();
}
@Override
- public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException
- {
- JsonDeserializer<?> deser = _valueDeserializer;
- TypeDeserializer typeDeser = _valueTypeDeserializer;
-
- if (deser == null) {
- deser = ctxt.findContextualValueDeserializer(_referencedType, property);
- } else { // otherwise directly assigned, probably not contextual yet:
- deser = ctxt.handleSecondaryContextualization(deser, property, _referencedType);
- }
- if (typeDeser != null) {
- typeDeser = typeDeser.forProperty(property);
- }
- return withResolved(typeDeser, deser);
- }
-
- @Override
- public AtomicReference<?> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
- Object contents = (_valueTypeDeserializer == null)
- ? _valueDeserializer.deserialize(p, ctxt)
- : _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer);
+ public AtomicReference<Object> referenceValue(Object contents) {
return new AtomicReference<Object>(contents);
}
+ /*
@Override
public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
TypeDeserializer typeDeser) throws IOException
@@ -105,4 +72,5 @@
}
return new AtomicReference<Object>(_valueTypeDeserializer.deserializeTypedFromAny(p, ctxt));
}
+ */
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/CollectionDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/CollectionDeserializer.java
index 0218603..0dd2a45 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/CollectionDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/CollectionDeserializer.java
@@ -302,12 +302,12 @@
}
@Override
- public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
+ public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
TypeDeserializer typeDeserializer)
throws IOException
{
// In future could check current token... for now this should be enough:
- return typeDeserializer.deserializeTypedFromArray(jp, ctxt);
+ return typeDeserializer.deserializeTypedFromArray(p, ctxt);
}
/**
@@ -315,6 +315,7 @@
* throw an exception, or try to handle value as if member of implicit
* array, depending on configuration.
*/
+ @SuppressWarnings("unchecked")
protected final Collection<Object> handleNonArray(JsonParser p, DeserializationContext ctxt,
Collection<Object> result)
throws IOException
@@ -324,7 +325,7 @@
((_unwrapSingle == null) &&
ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY));
if (!canWrap) {
- throw ctxt.mappingException(_collectionType.getRawClass());
+ return (Collection<Object>) ctxt.handleUnexpectedToken(_collectionType.getRawClass(), p);
}
JsonDeserializer<Object> valueDes = _valueDeserializer;
final TypeDeserializer typeDeser = _valueTypeDeserializer;
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/DateDeserializers.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/DateDeserializers.java
index 239f2fd..4a840bc 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/DateDeserializers.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/DateDeserializers.java
@@ -6,10 +6,8 @@
import java.util.*;
import com.fasterxml.jackson.annotation.JsonFormat;
-
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
-
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
@@ -17,7 +15,6 @@
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
-import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.util.StdDateFormat;
/**
@@ -104,7 +101,8 @@
throws JsonMappingException
{
if (property != null) {
- JsonFormat.Value format = ctxt.getAnnotationIntrospector().findFormat((Annotated) property.getMember());
+ JsonFormat.Value format = findFormatOverrides(ctxt, property,
+ this.handledType());
if (format != null) {
TimeZone tz = format.getTimeZone();
// First: fully custom pattern?
@@ -155,8 +153,8 @@
try {
return _customFormat.parse(str);
} catch (ParseException e) {
- throw new IllegalArgumentException("Failed to parse Date value '"+str
- +"' (format: \""+_formatString+"\"): "+e.getMessage());
+ return (java.util.Date) ctxt.handleWeirdStringValue(handledType(), str,
+ "expected format \"%s\"", _formatString);
}
}
}
@@ -166,8 +164,7 @@
final Date parsed = _parseDate(p, ctxt);
t = p.nextToken();
if (t != JsonToken.END_ARRAY) {
- throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY,
- "Attempted to unwrap single value array for single 'java.util.Date' value but there was more than a single value in the array");
+ handleMissingEndArrayForSingle(p, ctxt);
}
return parsed;
}
@@ -181,7 +178,7 @@
/* Deserializer implementations for Date types
/**********************************************************
*/
-
+
@JacksonStdImpl
public static class CalendarDeserializer extends DateBasedDeserializer<Calendar>
{
@@ -230,7 +227,7 @@
}
return c;
} catch (Exception e) {
- throw ctxt.instantiationException(_calendarClass, e);
+ return (Calendar) ctxt.handleInstantiationProblem(_calendarClass, d, e);
}
}
}
@@ -242,6 +239,7 @@
* {@link DeserializationContext#parseDate} that this basic
* deserializer calls.
*/
+ @JacksonStdImpl
public static class DateDeserializer extends DateBasedDeserializer<Date>
{
public final static DateDeserializer instance = new DateDeserializer();
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java
index a442b1e..679c417 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java
@@ -1,15 +1,13 @@
package com.fasterxml.jackson.databind.deser.std;
import java.io.IOException;
-import java.lang.reflect.Method;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
-import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
-import com.fasterxml.jackson.databind.exc.InvalidFormatException;
+import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
+import com.fasterxml.jackson.databind.deser.ValueInstantiator;
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
-import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.util.ClassUtil;
import com.fasterxml.jackson.databind.util.CompactStringObjectMap;
import com.fasterxml.jackson.databind.util.EnumResolver;
@@ -24,10 +22,12 @@
{
private static final long serialVersionUID = 1L;
- /**
- * @since 2.6
- */
protected Object[] _enumsByIndex;
+
+ /**
+ * @since 2.8
+ */
+ private final Enum<?> _enumDefaultValue;
/**
* @since 2.7.3
@@ -47,27 +47,57 @@
super(byNameResolver.getEnumClass());
_lookupByName = byNameResolver.constructLookup();
_enumsByIndex = byNameResolver.getRawEnums();
+ _enumDefaultValue = byNameResolver.getDefaultValue();
+ }
+
+ /**
+ * @deprecated Since 2.8
+ */
+ @Deprecated
+ public static JsonDeserializer<?> deserializerForCreator(DeserializationConfig config,
+ Class<?> enumClass, AnnotatedMethod factory) {
+ return deserializerForCreator(config, enumClass, factory, null, null);
}
/**
* Factory method used when Enum instances are to be deserialized
* using a creator (static factory method)
*
- * @return Deserializer based on given factory method, if type was suitable;
- * null if type can not be used
+ * @return Deserializer based on given factory method
+ *
+ * @since 2.8
*/
public static JsonDeserializer<?> deserializerForCreator(DeserializationConfig config,
- Class<?> enumClass, AnnotatedMethod factory)
+ Class<?> enumClass, AnnotatedMethod factory,
+ ValueInstantiator valueInstantiator, SettableBeanProperty[] creatorProps)
{
- // note: caller has verified there's just one arg; but we must verify its type
- Class<?> paramClass = factory.getRawParameterType(0);
if (config.canOverrideAccessModifiers()) {
ClassUtil.checkAndFixAccess(factory.getMember(),
config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
}
- return new FactoryBasedDeserializer(enumClass, factory, paramClass);
+ return new FactoryBasedEnumDeserializer(enumClass, factory,
+ factory.getParameterType(0),
+ valueInstantiator, creatorProps);
}
-
+
+ /**
+ * Factory method used when Enum instances are to be deserialized
+ * using a zero-/no-args factory method
+ *
+ * @return Deserializer based on given no-args factory method
+ *
+ * @since 2.8
+ */
+ public static JsonDeserializer<?> deserializerForNoArgsCreator(DeserializationConfig config,
+ Class<?> enumClass, AnnotatedMethod factory)
+ {
+ if (config.canOverrideAccessModifiers()) {
+ ClassUtil.checkAndFixAccess(factory.getMember(),
+ config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
+ }
+ return new FactoryBasedEnumDeserializer(enumClass, factory);
+ }
+
/*
/**********************************************************
/* Default JsonDeserializer implementation
@@ -89,7 +119,7 @@
// Usually should just get string value:
if (curr == JsonToken.VALUE_STRING || curr == JsonToken.FIELD_NAME) {
CompactStringObjectMap lookup = ctxt.isEnabled(DeserializationFeature.READ_ENUMS_USING_TO_STRING)
- ? _getToStringLookup() : _lookupByName;
+ ? _getToStringLookup(ctxt) : _lookupByName;
final String name = p.getText();
Object result = lookup.find(name);
if (result == null) {
@@ -102,14 +132,21 @@
// ... unless told not to do that
int index = p.getIntValue();
if (ctxt.isEnabled(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)) {
- _failOnNumber(ctxt, p, index);
+ return ctxt.handleWeirdNumberValue(_enumClass(), index,
+ "not allowed to deserialize Enum value out of number: disable DeserializationConfig.DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS to allow"
+ );
}
if (index >= 0 && index < _enumsByIndex.length) {
return _enumsByIndex[index];
}
+ if ((_enumDefaultValue != null)
+ && ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE)) {
+ return _enumDefaultValue;
+ }
if (!ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
- throw ctxt.weirdNumberException(index, _enumClass(),
- "index value outside legal index range [0.."+(_enumsByIndex.length-1)+"]");
+ return ctxt.handleWeirdNumberValue(_enumClass(), index,
+ "index value outside legal index range [0..%s]",
+ _enumsByIndex.length-1);
}
return null;
}
@@ -135,152 +172,64 @@
char c = name.charAt(0);
if (c >= '0' && c <= '9') {
try {
- int ix = Integer.parseInt(name);
+ int index = Integer.parseInt(name);
if (ctxt.isEnabled(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)) {
- _failOnNumber(ctxt, p, ix);
+ return ctxt.handleWeirdNumberValue(_enumClass(), index,
+ "not allowed to deserialize Enum value out of number: disable DeserializationConfig.DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS to allow"
+ );
}
- if (ix >= 0 && ix < _enumsByIndex.length) {
- return _enumsByIndex[ix];
+ if (index >= 0 && index < _enumsByIndex.length) {
+ return _enumsByIndex[index];
}
} catch (NumberFormatException e) {
// fine, ignore, was not an integer
}
}
}
+ if ((_enumDefaultValue != null)
+ && ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE)) {
+ return _enumDefaultValue;
+ }
if (!ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
- throw ctxt.weirdStringException(name, _enumClass(),
- "value not one of declared Enum instance names: "+lookup.keys());
+ return ctxt.handleWeirdStringValue(_enumClass(), name,
+ "value not one of declared Enum instance names: %s", lookup.keys());
}
return null;
}
protected Object _deserializeOther(JsonParser p, DeserializationContext ctxt) throws IOException
{
- JsonToken curr = p.getCurrentToken();
// [databind#381]
if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)
&& p.isExpectedStartArrayToken()) {
p.nextToken();
final Object parsed = deserialize(p, ctxt);
- curr = p.nextToken();
+ JsonToken curr = p.nextToken();
if (curr != JsonToken.END_ARRAY) {
- throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY,
- "Attempted to unwrap single value array for single '" + _enumClass().getName() + "' value but there was more than a single value in the array");
+ handleMissingEndArrayForSingle(p, ctxt);
}
return parsed;
}
- throw ctxt.mappingException(_enumClass());
- }
-
- protected void _failOnNumber(DeserializationContext ctxt, JsonParser p, int index)
- throws IOException
- {
- throw InvalidFormatException.from(p,
- String.format("Not allowed to deserialize Enum value out of JSON number (%d): disable DeserializationConfig.DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS to allow",
- index),
- index, _enumClass());
+ return ctxt.handleUnexpectedToken(_enumClass(), p);
}
protected Class<?> _enumClass() {
return handledType();
}
- protected CompactStringObjectMap _getToStringLookup()
+ protected CompactStringObjectMap _getToStringLookup(DeserializationContext ctxt)
{
CompactStringObjectMap lookup = _lookupByToString;
// note: exact locking not needed; all we care for here is to try to
// reduce contention for the initial resolution
if (lookup == null) {
synchronized (this) {
- lookup = EnumResolver.constructUnsafeUsingToString(_enumClass())
- .constructLookup();
+ lookup = EnumResolver.constructUnsafeUsingToString(_enumClass(),
+ ctxt.getAnnotationIntrospector())
+ .constructLookup();
}
_lookupByToString = lookup;
}
return lookup;
}
-
- /*
- /**********************************************************
- /* Additional helper classes
- /**********************************************************
- */
-
- /**
- * Deserializer that uses a single-String static factory method
- * for locating Enum values by String id.
- */
- protected static class FactoryBasedDeserializer
- extends StdDeserializer<Object>
- implements ContextualDeserializer
- {
- private static final long serialVersionUID = 1;
-
- // Marker type; null if String expected; otherwise numeric wrapper
- protected final Class<?> _inputType;
- protected final Method _factory;
- protected final JsonDeserializer<?> _deser;
-
- public FactoryBasedDeserializer(Class<?> cls, AnnotatedMethod f,
- Class<?> inputType)
- {
- super(cls);
- _factory = f.getAnnotated();
- _inputType = inputType;
- _deser = null;
- }
-
- protected FactoryBasedDeserializer(FactoryBasedDeserializer base,
- JsonDeserializer<?> deser) {
- super(base._valueClass);
- _inputType = base._inputType;
- _factory = base._factory;
- _deser = deser;
- }
-
- @Override
- public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
- BeanProperty property)
- throws JsonMappingException
- {
- if ((_deser == null) && (_inputType != String.class)) {
- return new FactoryBasedDeserializer(this,
- ctxt.findContextualValueDeserializer(ctxt.constructType(_inputType), property));
- }
- return this;
- }
-
- @Override
- public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
- {
- Object value;
- if (_deser != null) {
- value = _deser.deserialize(p, ctxt);
- } else {
- JsonToken curr = p.getCurrentToken();
- if (curr == JsonToken.VALUE_STRING || curr == JsonToken.FIELD_NAME) {
- value = p.getText();
- } else {
- value = p.getValueAsString();
- }
- }
- try {
- return _factory.invoke(_valueClass, value);
- } catch (Exception e) {
- Throwable t = ClassUtil.getRootCause(e);
- if (t instanceof IOException) {
- throw (IOException) t;
- }
- throw ctxt.instantiationException(_valueClass, t);
- }
- }
-
- @Override
- public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException {
- if (_deser == null) { // String never has type info
- return deserialize(p, ctxt);
- }
- return typeDeserializer.deserializeTypedFromAny(p, ctxt);
- }
- }
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumMapDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumMapDeserializer.java
index 6fd73c1..d227834 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumMapDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumMapDeserializer.java
@@ -122,35 +122,36 @@
*/
@Override
- public EnumMap<?,?> deserialize(JsonParser jp, DeserializationContext ctxt)
+ public EnumMap<?,?> deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException
{
// Ok: must point to START_OBJECT
- if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
- return _deserializeFromEmpty(jp, ctxt);
+ if (p.getCurrentToken() != JsonToken.START_OBJECT) {
+ return _deserializeFromEmpty(p, ctxt);
}
EnumMap result = constructMap();
final JsonDeserializer<Object> valueDes = _valueDeserializer;
final TypeDeserializer typeDeser = _valueTypeDeserializer;
- while ((jp.nextToken()) == JsonToken.FIELD_NAME) {
- String keyName = jp.getCurrentName(); // just for error message
+ while ((p.nextToken()) == JsonToken.FIELD_NAME) {
+ String keyName = p.getCurrentName(); // just for error message
// but we need to let key deserializer handle it separately, nonetheless
Enum<?> key = (Enum<?>) _keyDeserializer.deserializeKey(keyName, ctxt);
if (key == null) {
if (!ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
- throw ctxt.weirdStringException(keyName, _enumClass, "value not one of declared Enum instance names for "
- +_mapType.getKeyType());
+ return (EnumMap<?,?>) ctxt.handleWeirdStringValue(_enumClass, keyName,
+ "value not one of declared Enum instance names for %s",
+ _mapType.getKeyType());
}
/* 24-Mar-2012, tatu: Null won't work as a key anyway, so let's
* just skip the entry then. But we must skip the value as well, if so.
*/
- jp.nextToken();
- jp.skipChildren();
+ p.nextToken();
+ p.skipChildren();
continue;
}
// And then the value...
- JsonToken t = jp.nextToken();
+ JsonToken t = p.nextToken();
/* note: MUST check for nulls separately: deserializers will
* not handle them (and maybe fail or return bogus data)
*/
@@ -160,9 +161,9 @@
if (t == JsonToken.VALUE_NULL) {
value = valueDes.getNullValue(ctxt);
} else if (typeDeser == null) {
- value = valueDes.deserialize(jp, ctxt);
+ value = valueDes.deserialize(p, ctxt);
} else {
- value = valueDes.deserializeWithType(jp, ctxt, typeDeser);
+ value = valueDes.deserializeWithType(p, ctxt, typeDeser);
}
} catch (Exception e) {
wrapAndThrow(e, result, keyName);
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumSetDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumSetDeserializer.java
index a20fa04..2136210 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumSetDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumSetDeserializer.java
@@ -137,7 +137,7 @@
* deserializers)
*/
if (t == JsonToken.VALUE_NULL) {
- throw ctxt.mappingException(_enumClass);
+ return (EnumSet<?>) ctxt.handleUnexpectedToken(_enumClass, p);
}
Enum<?> value = _enumDeserializer.deserialize(p, ctxt);
/* 24-Mar-2012, tatu: As per [JACKSON-810], may actually get nulls;
@@ -177,13 +177,13 @@
ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY));
if (!canWrap) {
- throw ctxt.mappingException(EnumSet.class);
+ return (EnumSet<?>) ctxt.handleUnexpectedToken(EnumSet.class, p);
}
EnumSet result = constructSet();
// First: since `null`s not allowed, slightly simpler...
if (p.hasToken(JsonToken.VALUE_NULL)) {
- throw ctxt.mappingException(_enumClass);
+ return (EnumSet<?>) ctxt.handleUnexpectedToken(_enumClass, p);
}
try {
Enum<?> value = _enumDeserializer.deserialize(p, ctxt);
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/FactoryBasedEnumDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/FactoryBasedEnumDeserializer.java
new file mode 100644
index 0000000..cf4096b
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/FactoryBasedEnumDeserializer.java
@@ -0,0 +1,213 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.BeanProperty;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
+import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
+import com.fasterxml.jackson.databind.deser.ValueInstantiator;
+import com.fasterxml.jackson.databind.deser.impl.PropertyBasedCreator;
+import com.fasterxml.jackson.databind.deser.impl.PropertyValueBuffer;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.util.ClassUtil;
+
+/**
+ * Deserializer that uses a single-String static factory method
+ * for locating Enum values by String id.
+ *
+ * @since 2.8 (as stand-alone class; was static inner class of {@link EnumDeserializer}
+ */
+class FactoryBasedEnumDeserializer
+ extends StdDeserializer<Object>
+ implements ContextualDeserializer
+{
+ private static final long serialVersionUID = 1;
+
+ // Marker type; null if String expected; otherwise numeric wrapper
+ protected final JavaType _inputType;
+ protected final boolean _hasArgs;
+ protected final AnnotatedMethod _factory;
+ protected final JsonDeserializer<?> _deser;
+ protected final ValueInstantiator _valueInstantiator;
+ protected final SettableBeanProperty[] _creatorProps;
+
+ /**
+ * Lazily instantiated property-based creator.
+ *
+ * @since 2.8
+ */
+ private transient PropertyBasedCreator _propCreator;
+
+ public FactoryBasedEnumDeserializer(Class<?> cls, AnnotatedMethod f, JavaType paramType,
+ ValueInstantiator valueInstantiator, SettableBeanProperty[] creatorProps)
+ {
+ super(cls);
+ _factory = f;
+ _hasArgs = true;
+ // We'll skip case of `String`, as well as no type (zero-args):
+ _inputType = paramType.hasRawClass(String.class) ? null : paramType;
+ _deser = null;
+ _valueInstantiator = valueInstantiator;
+ _creatorProps = creatorProps;
+ }
+
+ /**
+ * @since 2.8
+ */
+ public FactoryBasedEnumDeserializer(Class<?> cls, AnnotatedMethod f)
+ {
+ super(cls);
+ _factory = f;
+ _hasArgs = false;
+ _inputType = null;
+ _deser = null;
+ _valueInstantiator = null;
+ _creatorProps = null;
+ }
+
+ protected FactoryBasedEnumDeserializer(FactoryBasedEnumDeserializer base,
+ JsonDeserializer<?> deser) {
+ super(base._valueClass);
+ _inputType = base._inputType;
+ _factory = base._factory;
+ _hasArgs = base._hasArgs;
+ _valueInstantiator = base._valueInstantiator;
+ _creatorProps = base._creatorProps;
+
+ _deser = deser;
+ }
+
+ @Override
+ public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
+ BeanProperty property)
+ throws JsonMappingException
+ {
+ if ((_deser == null) && (_inputType != null) && (_creatorProps == null)) {
+ return new FactoryBasedEnumDeserializer(this,
+ ctxt.findContextualValueDeserializer(_inputType, property));
+ }
+ return this;
+ }
+
+ @Override
+ public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
+ {
+ Object value = null;
+ if (_deser != null) {
+ value = _deser.deserialize(p, ctxt);
+ } else if (_hasArgs) {
+ JsonToken curr = p.getCurrentToken();
+ //There can be a JSON object passed for deserializing an Enum,
+ //the below case handles it.
+ if (curr == JsonToken.VALUE_STRING || curr == JsonToken.FIELD_NAME) {
+ value = p.getText();
+ } else if ((_creatorProps != null) && p.isExpectedStartObjectToken()) {
+ if (_propCreator == null) {
+ _propCreator = PropertyBasedCreator.construct(ctxt, _valueInstantiator, _creatorProps);
+ }
+ p.nextToken();
+ return deserializeEnumUsingPropertyBased(p, ctxt, _propCreator);
+ } else {
+ value = p.getValueAsString();
+ }
+ } else { // zero-args; just skip whatever value there may be
+ p.skipChildren();
+ try {
+ return _factory.call();
+ } catch (Exception e) {
+ Throwable t = ClassUtil.throwRootCauseIfIOE(e);
+ return ctxt.handleInstantiationProblem(_valueClass, null, t);
+ }
+ }
+ try {
+ return _factory.callOnWith(_valueClass, value);
+ } catch (Exception e) {
+ Throwable t = ClassUtil.throwRootCauseIfIOE(e);
+ return ctxt.handleInstantiationProblem(_valueClass, value, t);
+ }
+ }
+
+ @Override
+ public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException {
+ if (_deser == null) { // String never has type info
+ return deserialize(p, ctxt);
+ }
+ return typeDeserializer.deserializeTypedFromAny(p, ctxt);
+ }
+
+ // Method to deserialize the Enum using property based methodology
+ protected Object deserializeEnumUsingPropertyBased(final JsonParser p, final DeserializationContext ctxt,
+ final PropertyBasedCreator creator) throws IOException
+ {
+ PropertyValueBuffer buffer = creator.startBuilding(p, ctxt, null);
+
+ JsonToken t = p.getCurrentToken();
+ for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) {
+ String propName = p.getCurrentName();
+ p.nextToken(); // to point to value
+
+ SettableBeanProperty creatorProp = creator.findCreatorProperty(propName);
+ if (creatorProp != null) {
+ buffer.assignParameter(creatorProp, _deserializeWithErrorWrapping(p, ctxt, creatorProp));
+ continue;
+ }
+ if (buffer.readIdProperty(propName)) {
+ continue;
+ }
+ }
+ return creator.build(ctxt, buffer);
+ }
+
+ // ************ Got the below methods from BeanDeserializer ********************//
+
+ protected final Object _deserializeWithErrorWrapping(JsonParser p, DeserializationContext ctxt,
+ SettableBeanProperty prop) throws IOException {
+ try {
+ return prop.deserialize(p, ctxt);
+ } catch (Exception e) {
+ wrapAndThrow(e, _valueClass.getClass(), prop.getName(), ctxt);
+ // never gets here, unless caller declines to throw an exception
+ return null;
+ }
+ }
+
+ public void wrapAndThrow(Throwable t, Object bean, String fieldName, DeserializationContext ctxt)
+ throws IOException
+ {
+ throw JsonMappingException.wrapWithPath(throwOrReturnThrowable(t, ctxt), bean, fieldName);
+ }
+
+ private Throwable throwOrReturnThrowable(Throwable t, DeserializationContext ctxt) throws IOException
+ {
+ while (t instanceof InvocationTargetException && t.getCause() != null) {
+ t = t.getCause();
+ }
+ // Errors to be passed as is
+ if (t instanceof Error) {
+ throw (Error) t;
+ }
+ boolean wrap = (ctxt == null) || ctxt.isEnabled(DeserializationFeature.WRAP_EXCEPTIONS);
+ // Ditto for IOExceptions; except we may want to wrap JSON
+ // exceptions
+ if (t instanceof IOException) {
+ if (!wrap || !(t instanceof JsonProcessingException)) {
+ throw (IOException) t;
+ }
+ } else if (!wrap) {
+ if (t instanceof RuntimeException) {
+ throw (RuntimeException) t;
+ }
+ }
+ return t;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/FromStringDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/FromStringDeserializer.java
index e27d6a9..1fdf9f4 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/FromStringDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/FromStringDeserializer.java
@@ -3,6 +3,7 @@
import java.io.*;
import java.net.InetAddress;
import java.net.InetSocketAddress;
+import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.nio.charset.Charset;
@@ -100,16 +101,6 @@
@Override
public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
{
- // Issue#381
- if (p.getCurrentToken() == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
- p.nextToken();
- final T value = deserialize(p, ctxt);
- if (p.nextToken() != JsonToken.END_ARRAY) {
- throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY,
- "Attempted to unwrap single value array for single '" + _valueClass.getName() + "' value but there was more than a single value in the array");
- }
- return value;
- }
// 22-Sep-2012, tatu: For 2.1, use this new method, may force coercion:
String text = p.getValueAsString();
if (text != null) { // has String representation
@@ -125,6 +116,8 @@
}
} catch (IllegalArgumentException iae) {
cause = iae;
+ } catch (MalformedURLException me) {
+ cause = me;
}
String msg = "not a valid textual representation";
if (cause != null) {
@@ -133,6 +126,7 @@
msg = msg + ", problem: "+m2;
}
}
+ // 05-May-2016, tatu: Unlike most usage, this seems legit, so...
JsonMappingException e = ctxt.weirdStringException(text, _valueClass, msg);
if (cause != null) {
e.initCause(cause);
@@ -140,7 +134,17 @@
throw e;
// nothing to do here, yet? We'll fail anyway
}
- if (p.getCurrentToken() == JsonToken.VALUE_EMBEDDED_OBJECT) {
+ JsonToken t = p.getCurrentToken();
+ // [databind#381]
+ if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
+ p.nextToken();
+ final T value = deserialize(p, ctxt);
+ if (p.nextToken() != JsonToken.END_ARRAY) {
+ handleMissingEndArrayForSingle(p, ctxt);
+ }
+ return value;
+ }
+ if (t == JsonToken.VALUE_EMBEDDED_OBJECT) {
// Trivial cases; null to null, instance of type itself returned as is
Object ob = p.getEmbeddedObject();
if (ob == null) {
@@ -151,15 +155,16 @@
}
return _deserializeEmbedded(ob, ctxt);
}
- throw ctxt.mappingException(_valueClass);
+ return (T) ctxt.handleUnexpectedToken(_valueClass, p);
}
protected abstract T _deserialize(String value, DeserializationContext ctxt) throws IOException;
protected T _deserializeEmbedded(Object ob, DeserializationContext ctxt) throws IOException {
// default impl: error out
- throw ctxt.mappingException("Don't know how to convert embedded Object of type %s into %s",
+ ctxt.reportMappingException("Don't know how to convert embedded Object of type %s into %s",
ob.getClass().getName(), _valueClass.getName());
+ return null;
}
protected T _deserializeFromEmptyString() throws IOException {
@@ -216,7 +221,8 @@
try {
return ctxt.findClass(value);
} catch (Exception e) {
- throw ctxt.instantiationException(_valueClass, ClassUtil.getRootCause(e));
+ return ctxt.handleInstantiationProblem(_valueClass, value,
+ ClassUtil.getRootCause(e));
}
case STD_JAVA_TYPE:
return ctxt.getTypeFactory().constructFromCanonical(value);
@@ -261,16 +267,15 @@
int j = value.indexOf(':', i);
int port = j > -1 ? Integer.parseInt(value.substring(j + 1)) : 0;
return new InetSocketAddress(value.substring(0, i + 1), port);
- } else {
- int ix = value.indexOf(':');
- if (ix >= 0 && value.indexOf(':', ix + 1) < 0) {
- // host:port
- int port = Integer.parseInt(value.substring(ix+1));
- return new InetSocketAddress(value.substring(0, ix), port);
- }
- // host or unbracketed IPv6, without port number
- return new InetSocketAddress(value, 0);
}
+ int ix = value.indexOf(':');
+ if (ix >= 0 && value.indexOf(':', ix + 1) < 0) {
+ // host:port
+ int port = Integer.parseInt(value.substring(ix+1));
+ return new InetSocketAddress(value.substring(0, ix), port);
+ }
+ // host or unbracketed IPv6, without port number
+ return new InetSocketAddress(value, 0);
}
throw new IllegalArgumentException();
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/JsonLocationInstantiator.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/JsonLocationInstantiator.java
index c5aaa99..be32d08 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/JsonLocationInstantiator.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/JsonLocationInstantiator.java
@@ -15,13 +15,13 @@
* {@link ValueInstantiator} (not that explicit one would be very
* hard but...)
*/
-public class JsonLocationInstantiator extends ValueInstantiator
+public class JsonLocationInstantiator
+ extends ValueInstantiator.Base
{
- @Override
- public String getValueTypeDesc() {
- return JsonLocation.class.getName();
+ public JsonLocationInstantiator() {
+ super(JsonLocation.class);
}
-
+
@Override
public boolean canCreateFromObjectWith() { return true; }
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/JsonNodeDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/JsonNodeDeserializer.java
index ac705c6..05b0562 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/JsonNodeDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/JsonNodeDeserializer.java
@@ -102,7 +102,7 @@
if (p.hasToken(JsonToken.END_OBJECT)) {
return ctxt.getNodeFactory().objectNode();
}
- throw ctxt.mappingException(ObjectNode.class);
+ return (ObjectNode) ctxt.handleUnexpectedToken(ObjectNode.class, p);
}
}
@@ -123,7 +123,7 @@
if (p.isExpectedStartArrayToken()) {
return deserializeArray(p, ctxt, ctxt.getNodeFactory());
}
- throw ctxt.mappingException(ArrayNode.class);
+ return (ArrayNode) ctxt.handleUnexpectedToken(ArrayNode.class, p);
}
}
}
@@ -164,6 +164,7 @@
/**********************************************************
*/
+ @Deprecated // since 2.8
protected void _reportProblem(JsonParser p, String msg) throws JsonMappingException {
throw JsonMappingException.from(p, msg);
}
@@ -187,9 +188,10 @@
JsonNode oldValue, JsonNode newValue)
throws JsonProcessingException
{
- // [Issue#237]: Report an error if asked to do so:
+ // [databind#237]: Report an error if asked to do so:
if (ctxt.isEnabled(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY)) {
- _reportProblem(p, "Duplicate field '"+fieldName+"' for ObjectNode: not allowed when FAIL_ON_READING_DUP_TREE_KEY enabled");
+ ctxt.reportMappingException("Duplicate field '%s' for ObjectNode: not allowed when FAIL_ON_READING_DUP_TREE_KEY enabled",
+ fieldName);
}
}
@@ -212,7 +214,7 @@
return node;
}
if (t != JsonToken.FIELD_NAME) {
- throw ctxt.mappingException(handledType(), p.getCurrentToken());
+ return (ObjectNode) ctxt.handleUnexpectedToken(handledType(), p);
}
key = p.getCurrentName();
}
@@ -265,9 +267,6 @@
ArrayNode node = nodeFactory.arrayNode();
while (true) {
JsonToken t = p.nextToken();
- if (t == null) {
- throw ctxt.mappingException("Unexpected end-of-input when binding data into ArrayNode");
- }
switch (t.id()) {
case JsonTokenId.ID_START_OBJECT:
node.add(deserializeObject(p, ctxt, nodeFactory));
@@ -333,8 +332,8 @@
//case END_OBJECT:
//case END_ARRAY:
default:
- throw ctxt.mappingException(handledType());
}
+ return (JsonNode) ctxt.handleUnexpectedToken(handledType(), p);
}
protected final JsonNode _fromInt(JsonParser p, DeserializationContext ctxt,
@@ -366,8 +365,19 @@
final JsonNodeFactory nodeFactory) throws IOException
{
JsonParser.NumberType nt = p.getNumberType();
- if (nt == JsonParser.NumberType.BIG_DECIMAL
- || ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) {
+ if (nt == JsonParser.NumberType.BIG_DECIMAL) {
+ return nodeFactory.numberNode(p.getDecimalValue());
+ }
+ if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) {
+ // 20-May-2016, tatu: As per [databind#1028], need to be careful
+ // (note: JDK 1.8 would have `Double.isFinite()`)
+ // 21-Aug-2016, tatu: Not optimal, really, because this may result in
+ // value getting parsed twice. But has to do for now, to resolve
+ // [databind#1315]
+ double d = p.getDoubleValue();
+ if (Double.isInfinite(d) || Double.isNaN(d)) {
+ return nodeFactory.numberNode(d);
+ }
return nodeFactory.numberNode(p.getDecimalValue());
}
if (nt == JsonParser.NumberType.FLOAT) {
@@ -379,7 +389,6 @@
protected final JsonNode _fromEmbedded(JsonParser p, DeserializationContext ctxt,
JsonNodeFactory nodeFactory) throws IOException
{
- // [JACKSON-796]
Object ob = p.getEmbeddedObject();
if (ob == null) { // should this occur?
return nodeFactory.nullNode();
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/MapDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/MapDeserializer.java
index 23599ff..3dc7c4b 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/MapDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/MapDeserializer.java
@@ -3,6 +3,7 @@
import java.io.IOException;
import java.util.*;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
@@ -83,7 +84,7 @@
// // Any properties to ignore if seen?
- protected HashSet<String> _ignorableProperties;
+ protected Set<String> _ignorableProperties;
/*
/**********************************************************
@@ -131,7 +132,7 @@
protected MapDeserializer(MapDeserializer src,
KeyDeserializer keyDeser, JsonDeserializer<Object> valueDeser,
TypeDeserializer valueTypeDeser,
- HashSet<String> ignorable)
+ Set<String> ignorable)
{
super(src._mapType);
_mapType = src._mapType;
@@ -154,7 +155,7 @@
@SuppressWarnings("unchecked")
protected MapDeserializer withResolved(KeyDeserializer keyDeser,
TypeDeserializer valueTypeDeser, JsonDeserializer<?> valueDeser,
- HashSet<String> ignorable)
+ Set<String> ignorable)
{
if ((_keyDeserializer == keyDeser) && (_valueDeserializer == valueDeser)
@@ -188,6 +189,11 @@
null : ArrayBuilders.arrayToSet(ignorable);
}
+ public void setIgnorableProperties(Set<String> ignorable) {
+ _ignorableProperties = (ignorable == null || ignorable.size() == 0) ?
+ null : ignorable;
+ }
+
/*
/**********************************************************
/* Validation, post-processing (ResolvableDeserializer)
@@ -250,16 +256,19 @@
if (vtd != null) {
vtd = vtd.forProperty(property);
}
- HashSet<String> ignored = _ignorableProperties;
+ Set<String> ignored = _ignorableProperties;
AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
if (intr != null && property != null) {
AnnotatedMember member = property.getMember();
if (member != null) {
- String[] moreToIgnore = intr.findPropertiesToIgnore(member, false);
- if (moreToIgnore != null) {
- ignored = (ignored == null) ? new HashSet<String>() : new HashSet<String>(ignored);
- for (String str : moreToIgnore) {
- ignored.add(str);
+ JsonIgnoreProperties.Value ignorals = intr.findPropertyIgnorals(member);
+ if (ignorals != null) {
+ Set<String> ignoresToAdd = ignorals.findIgnoredForDeserialization();
+ if (!ignoresToAdd.isEmpty()) {
+ ignored = (ignored == null) ? new HashSet<String>() : new HashSet<String>(ignored);
+ for (String str : ignoresToAdd) {
+ ignored.add(str);
+ }
}
}
}
@@ -325,7 +334,8 @@
_delegateDeserializer.deserialize(p, ctxt));
}
if (!_hasDefaultCreator) {
- throw ctxt.instantiationException(getMapClass(), "No default constructor found");
+ return (Map<Object,Object> ) ctxt.handleMissingInstantiator(getMapClass(), p,
+ "no default constructor found");
}
// Ok: must point to START_OBJECT, FIELD_NAME or END_OBJECT
JsonToken t = p.getCurrentToken();
@@ -339,13 +349,14 @@
}
final Map<Object,Object> result = (Map<Object,Object>) _valueInstantiator.createUsingDefault(ctxt);
if (_standardStringKey) {
- _readAndBindStringMap(p, ctxt, result);
+ _readAndBindStringKeyMap(p, ctxt, result);
return result;
}
_readAndBind(p, ctxt, result);
return result;
}
+ @SuppressWarnings("unchecked")
@Override
public Map<Object,Object> deserialize(JsonParser p, DeserializationContext ctxt,
Map<Object,Object> result)
@@ -357,10 +368,10 @@
// Ok: must point to START_OBJECT or FIELD_NAME
JsonToken t = p.getCurrentToken();
if (t != JsonToken.START_OBJECT && t != JsonToken.FIELD_NAME) {
- throw ctxt.mappingException(getMapClass());
+ return (Map<Object,Object>) ctxt.handleUnexpectedToken(getMapClass(), p);
}
if (_standardStringKey) {
- _readAndBindStringMap(p, ctxt, result);
+ _readAndBindStringKeyMap(p, ctxt, result);
return result;
}
_readAndBind(p, ctxt, result);
@@ -415,7 +426,7 @@
return;
}
if (t != JsonToken.FIELD_NAME) {
- throw ctxt.mappingException(_mapType.getRawClass(), p.getCurrentToken());
+ ctxt.reportWrongTokenException(p, JsonToken.FIELD_NAME, null);
}
keyStr = p.getCurrentName();
}
@@ -456,7 +467,7 @@
* {@link java.lang.String}s, and there is no custom deserialized
* specified.
*/
- protected final void _readAndBindStringMap(JsonParser p, DeserializationContext ctxt,
+ protected final void _readAndBindStringKeyMap(JsonParser p, DeserializationContext ctxt,
Map<Object,Object> result) throws IOException
{
final JsonDeserializer<Object> valueDes = _valueDeserializer;
@@ -476,7 +487,7 @@
return;
}
if (t != JsonToken.FIELD_NAME) {
- throw ctxt.mappingException(_mapType.getRawClass(), p.getCurrentToken());
+ ctxt.reportWrongTokenException(p, JsonToken.FIELD_NAME, null);
}
key = p.getCurrentName();
}
@@ -656,7 +667,7 @@
* The resolved object associated with {@link #key} comes before the values in
* {@link #next}.
*/
- final static class MapReferring extends Referring {
+ static class MapReferring extends Referring {
private final MapReferringAccumulator _parent;
public final Map<Object, Object> next = new LinkedHashMap<Object, Object>();
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/MapEntryDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/MapEntryDeserializer.java
index 5e03d52..7899712 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/MapEntryDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/MapEntryDeserializer.java
@@ -167,74 +167,78 @@
/**********************************************************
*/
+ @SuppressWarnings("unchecked")
@Override
- public Map.Entry<Object,Object> deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException
+ public Map.Entry<Object,Object> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
{
// Ok: must point to START_OBJECT, FIELD_NAME or END_OBJECT
- JsonToken t = jp.getCurrentToken();
+ JsonToken t = p.getCurrentToken();
if (t != JsonToken.START_OBJECT && t != JsonToken.FIELD_NAME && t != JsonToken.END_OBJECT) {
- // [JACKSON-620] (empty) String may be ok however:
+ // String may be ok however:
// slightly redundant (since String was passed above), but
- return _deserializeFromEmpty(jp, ctxt);
+ return _deserializeFromEmpty(p, ctxt);
}
if (t == JsonToken.START_OBJECT) {
- t = jp.nextToken();
+ t = p.nextToken();
}
if (t != JsonToken.FIELD_NAME) {
if (t == JsonToken.END_OBJECT) {
- throw ctxt.mappingException("Can not deserialize a Map.Entry out of empty JSON Object");
+ ctxt.reportMappingException("Can not deserialize a Map.Entry out of empty JSON Object");
+ return null;
}
- throw ctxt.mappingException(handledType(), t);
+ return (Map.Entry<Object,Object>) ctxt.handleUnexpectedToken(handledType(), p);
}
-
+
final KeyDeserializer keyDes = _keyDeserializer;
final JsonDeserializer<Object> valueDes = _valueDeserializer;
final TypeDeserializer typeDeser = _valueTypeDeserializer;
- final String keyStr = jp.getCurrentName();
+ final String keyStr = p.getCurrentName();
Object key = keyDes.deserializeKey(keyStr, ctxt);
Object value = null;
// And then the value...
- t = jp.nextToken();
+ t = p.nextToken();
try {
// Note: must handle null explicitly here; value deserializers won't
if (t == JsonToken.VALUE_NULL) {
value = valueDes.getNullValue(ctxt);
} else if (typeDeser == null) {
- value = valueDes.deserialize(jp, ctxt);
+ value = valueDes.deserialize(p, ctxt);
} else {
- value = valueDes.deserializeWithType(jp, ctxt, typeDeser);
+ value = valueDes.deserializeWithType(p, ctxt, typeDeser);
}
} catch (Exception e) {
wrapAndThrow(e, Map.Entry.class, keyStr);
}
// Close, but also verify that we reached the END_OBJECT
- t = jp.nextToken();
+ t = p.nextToken();
if (t != JsonToken.END_OBJECT) {
if (t == JsonToken.FIELD_NAME) { // most likely
- throw ctxt.mappingException("Problem binding JSON into Map.Entry: more than one entry in JSON (second field: '"+jp.getCurrentName()+"')");
+ ctxt.reportMappingException("Problem binding JSON into Map.Entry: more than one entry in JSON (second field: '"+p.getCurrentName()+"')");
+ } else {
+ // how would this occur?
+ ctxt.reportMappingException("Problem binding JSON into Map.Entry: unexpected content after JSON Object entry: "+t);
}
- // how would this occur?
- throw ctxt.mappingException("Problem binding JSON into Map.Entry: unexpected content after JSON Object entry: "+t);
+ return null;
}
return new AbstractMap.SimpleEntry<Object,Object>(key, value);
}
@Override
- public Map.Entry<Object,Object> deserialize(JsonParser jp, DeserializationContext ctxt,
+ public Map.Entry<Object,Object> deserialize(JsonParser p, DeserializationContext ctxt,
Map.Entry<Object,Object> result) throws IOException
{
throw new IllegalStateException("Can not update Map.Entry values");
}
@Override
- public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
+ public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
TypeDeserializer typeDeserializer)
throws IOException, JsonProcessingException
{
// In future could check current token... for now this should be enough:
- return typeDeserializer.deserializeTypedFromObject(jp, ctxt);
+ return typeDeserializer.deserializeTypedFromObject(p, ctxt);
}
/*
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/NumberDeserializers.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/NumberDeserializers.java
index b8ade5b..1137dcd 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/NumberDeserializers.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/NumberDeserializers.java
@@ -132,7 +132,7 @@
public final T getNullValue(DeserializationContext ctxt) throws JsonMappingException
{
if (_primitive && ctxt.isEnabled(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)) {
- throw ctxt.mappingException(
+ ctxt.reportMappingException(
"Can not map JSON null into type %s (set DeserializationConfig.DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES to 'false' to allow)",
handledType().toString());
}
@@ -140,17 +140,11 @@
}
@Override
- @Deprecated // remove in 2.7
- public final T getNullValue() {
- return _nullValue;
- }
-
- @Override
public T getEmptyValue(DeserializationContext ctxt) throws JsonMappingException {
// [databind#1095]: Should not allow coercion from into null from Empty String
// either, if `null` not allowed
if (_primitive && ctxt.isEnabled(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)) {
- throw ctxt.mappingException(
+ ctxt.reportMappingException(
"Can not map Empty String as null into type %s (set DeserializationConfig.DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES to 'false' to allow)",
handledType().toString());
}
@@ -279,14 +273,13 @@
p.nextToken();
final Character C = deserialize(p, ctxt);
if (p.nextToken() != JsonToken.END_ARRAY) {
- throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY,
- "Attempted to unwrap single value array for single '" + _valueClass.getName() + "' value but there was more than a single value in the array"
- );
+ handleMissingEndArrayForSingle(p, ctxt);
}
return C;
}
+ default:
}
- throw ctxt.mappingException(_valueClass, p.getCurrentToken());
+ return (Character) ctxt.handleUnexpectedToken(_valueClass, p);
}
}
@@ -477,23 +470,22 @@
}
return Long.valueOf(value);
} catch (IllegalArgumentException iae) {
- throw ctxt.weirdStringException(text, _valueClass, "not a valid number");
+ return ctxt.handleWeirdStringValue(_valueClass, text,
+ "not a valid number");
}
case JsonTokenId.ID_START_ARRAY:
if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
p.nextToken();
final Object value = deserialize(p, ctxt);
if (p.nextToken() != JsonToken.END_ARRAY) {
- throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY,
- "Attempted to unwrap single value array for single '" + _valueClass.getName() + "' value but there was more than a single value in the array"
- );
+ handleMissingEndArrayForSingle(p, ctxt);
}
return value;
}
break;
}
// Otherwise, no can do:
- throw ctxt.mappingException(_valueClass, p.getCurrentToken());
+ return ctxt.handleUnexpectedToken(_valueClass, p);
}
/**
@@ -561,9 +553,7 @@
p.nextToken();
final BigInteger value = deserialize(p, ctxt);
if (p.nextToken() != JsonToken.END_ARRAY) {
- throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY,
- "Attempted to unwrap single value array for single 'BigInteger' value but there was more than a single value in the array"
- );
+ handleMissingEndArrayForSingle(p, ctxt);
}
return value;
}
@@ -576,11 +566,12 @@
try {
return new BigInteger(text);
} catch (IllegalArgumentException iae) {
- throw ctxt.weirdStringException(text, _valueClass, "not a valid representation");
+ return (BigInteger) ctxt.handleWeirdStringValue(_valueClass, text,
+ "not a valid representation");
}
}
// String is ok too, can easily convert; otherwise, no can do:
- throw ctxt.mappingException(_valueClass, p.getCurrentToken());
+ return (BigInteger) ctxt.handleUnexpectedToken(_valueClass, p);
}
}
@@ -609,23 +600,22 @@
try {
return new BigDecimal(text);
} catch (IllegalArgumentException iae) {
- throw ctxt.weirdStringException(text, _valueClass, "not a valid representation");
+ return (BigDecimal) ctxt.handleWeirdStringValue(_valueClass, text,
+ "not a valid representation");
}
case JsonTokenId.ID_START_ARRAY:
if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
p.nextToken();
final BigDecimal value = deserialize(p, ctxt);
if (p.nextToken() != JsonToken.END_ARRAY) {
- throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY,
- "Attempted to unwrap single value array for single 'BigDecimal' value but there was more than a single value in the array"
- );
+ handleMissingEndArrayForSingle(p, ctxt);
}
return value;
}
break;
}
// Otherwise, no can do:
- throw ctxt.mappingException(_valueClass, p.getCurrentToken());
+ return (BigDecimal) ctxt.handleUnexpectedToken(_valueClass, p);
}
}
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/ObjectArrayDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/ObjectArrayDeserializer.java
index 1a3b747..101217b 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/ObjectArrayDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/ObjectArrayDeserializer.java
@@ -147,7 +147,7 @@
// Important: do NOT cache if polymorphic values, or ones with custom deserializer
return (_elementDeserializer == null) && (_elementTypeDeserializer == null);
}
-
+
/*
/**********************************************************
/* ContainerDeserializerBase API
@@ -266,12 +266,13 @@
ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY));
if (!canWrap) {
// One exception; byte arrays are generally serialized as base64, so that should be handled
- if (p.getCurrentToken() == JsonToken.VALUE_STRING
+ JsonToken t = p.getCurrentToken();
+ if (t == JsonToken.VALUE_STRING
// note: not `byte[]`, but `Byte[]` -- former is primitive array
&& _elementClass == Byte.class) {
return deserializeFromBase64(p, ctxt);
}
- throw ctxt.mappingException(_arrayType.getRawClass());
+ return (Object[]) ctxt.handleUnexpectedToken(_arrayType.getRawClass(), p);
}
JsonToken t = p.getCurrentToken();
Object value;
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers.java
index 6e7b4f8..4221b30 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers.java
@@ -98,7 +98,8 @@
*/
return typeDeserializer.deserializeTypedFromArray(p, ctxt);
}
-
+
+ @SuppressWarnings("unchecked")
protected T handleNonArray(JsonParser p, DeserializationContext ctxt) throws IOException
{
// [JACKSON-620] Empty String can become null...
@@ -114,12 +115,12 @@
if (canWrap) {
return handleSingleElementUnwrapped(p, ctxt);
}
- throw ctxt.mappingException(_valueClass);
+ return (T) ctxt.handleUnexpectedToken(_valueClass, p);
}
protected abstract T handleSingleElementUnwrapped(JsonParser p,
DeserializationContext ctxt) throws IOException;
-
+
/*
/********************************************************
/* Actual deserializers: efficient String[], char[] deserializers
@@ -165,12 +166,16 @@
// Let's actually build as a String, then get chars
StringBuilder sb = new StringBuilder(64);
while ((t = p.nextToken()) != JsonToken.END_ARRAY) {
- if (t != JsonToken.VALUE_STRING) {
- throw ctxt.mappingException(Character.TYPE);
+ String str;
+ if (t == JsonToken.VALUE_STRING) {
+ str = p.getText();
+ } else {
+ CharSequence cs = (CharSequence) ctxt.handleUnexpectedToken(Character.TYPE, p);
+ str = cs.toString();
}
- String str = p.getText();
if (str.length() != 1) {
- throw JsonMappingException.from(p, "Can not convert a JSON String of length "+str.length()+" into a char element of char array");
+ ctxt.reportMappingException("Can not convert a JSON String of length %d into a char element of char array",
+ str.length());
}
sb.append(str.charAt(0));
}
@@ -192,14 +197,14 @@
}
// not recognized, just fall through
}
- throw ctxt.mappingException(_valueClass);
+ return (char[]) ctxt.handleUnexpectedToken(_valueClass, p);
}
@Override
protected char[] handleSingleElementUnwrapped(JsonParser p,
DeserializationContext ctxt) throws IOException {
- // not sure how this should work so just return `null` so:
- throw ctxt.mappingException(_valueClass);
+ // not sure how this should work...
+ return (char[]) ctxt.handleUnexpectedToken(_valueClass, p);
}
}
@@ -311,11 +316,13 @@
// should we catch overflow exceptions?
value = p.getByteValue();
} else {
- // [JACKSON-79]: should probably accept nulls as 0
- if (t != JsonToken.VALUE_NULL) {
- throw ctxt.mappingException(_valueClass.getComponentType());
+ // should probably accept nulls as 0
+ if (t == JsonToken.VALUE_NULL) {
+ value = (byte) 0;
+ } else {
+ Number n = (Number) ctxt.handleUnexpectedToken(_valueClass.getComponentType(), p);
+ value = n.byteValue();
}
- value = (byte) 0;
}
if (ix >= chunk.length) {
chunk = builder.appendCompletedChunk(chunk, ix);
@@ -340,10 +347,11 @@
value = p.getByteValue();
} else {
// should probably accept nulls as 'false'
- if (t != JsonToken.VALUE_NULL) {
- throw ctxt.mappingException(_valueClass.getComponentType());
+ if (t == JsonToken.VALUE_NULL) {
+ return null;
}
- value = (byte) 0;
+ Number n = (Number) ctxt.handleUnexpectedToken(_valueClass.getComponentType(), p);
+ value = n.byteValue();
}
return new byte[] { value };
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/ReferenceTypeDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/ReferenceTypeDeserializer.java
new file mode 100644
index 0000000..f7a80d3
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/ReferenceTypeDeserializer.java
@@ -0,0 +1,131 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.type.ReferenceType;
+
+/**
+ * Base deserializer implementation for properties {@link ReferenceType} values.
+ * Implements most of functionality, only leaving couple of abstract
+ * methods for sub-classes to implement
+ *
+ * @since 2.8
+ */
+public abstract class ReferenceTypeDeserializer<T>
+ extends StdDeserializer<T>
+ implements ContextualDeserializer
+{
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Full type of property (or root value) for which this deserializer
+ * has been constructed and contextualized.
+ */
+ protected final JavaType _fullType;
+
+ protected final TypeDeserializer _valueTypeDeserializer;
+
+ protected final JsonDeserializer<?> _valueDeserializer;
+
+ /*
+ /**********************************************************
+ /* Life-cycle
+ /**********************************************************
+ */
+
+ public ReferenceTypeDeserializer(JavaType fullType,
+ TypeDeserializer typeDeser, JsonDeserializer<?> deser)
+ {
+ super(fullType);
+ _fullType = fullType;
+ _valueDeserializer = deser;
+ _valueTypeDeserializer = typeDeser;
+ }
+
+ @Override
+ public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException
+ {
+ JsonDeserializer<?> deser = _valueDeserializer;
+ if (deser == null) {
+ deser = ctxt.findContextualValueDeserializer(_fullType.getReferencedType(), property);
+ } else { // otherwise directly assigned, probably not contextual yet:
+ deser = ctxt.handleSecondaryContextualization(deser, property, _fullType.getReferencedType());
+ }
+ TypeDeserializer typeDeser = _valueTypeDeserializer;
+ if (typeDeser != null) {
+ typeDeser = typeDeser.forProperty(property);
+ }
+ if ((deser == _valueDeserializer) && (typeDeser == _valueTypeDeserializer)) {
+ return this;
+ }
+ return withResolved(typeDeser, deser);
+ }
+
+ /*
+ /**********************************************************
+ /* Abstract methods for sub-classes to implement
+ /**********************************************************
+ */
+
+ protected abstract ReferenceTypeDeserializer<T> withResolved(TypeDeserializer typeDeser, JsonDeserializer<?> valueDeser);
+
+ @Override
+ public abstract T getNullValue(DeserializationContext ctxt);
+
+ public abstract T referenceValue(Object contents);
+
+ /*
+ /**********************************************************
+ /* Overridden accessors
+ /**********************************************************
+ */
+
+ @Override
+ public JavaType getValueType() { return _fullType; }
+
+ /*
+ /**********************************************************
+ /* Deserialization
+ /**********************************************************
+ */
+
+ @Override
+ public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
+ Object contents = (_valueTypeDeserializer == null)
+ ? _valueDeserializer.deserialize(p, ctxt)
+ : _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer);
+ return referenceValue(contents);
+ }
+
+ @Override
+ public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
+ TypeDeserializer typeDeserializer) throws IOException
+ {
+ final JsonToken t = p.getCurrentToken();
+ if (t == JsonToken.VALUE_NULL) { // can this actually happen?
+ return getNullValue(ctxt);
+ }
+ // 22-Oct-2015, tatu: This handling is probably not needed (or is wrong), but
+ // could be result of older (pre-2.7) Jackson trying to serialize natural types.
+ // Because of this, let's allow for now, unless proven problematic
+ /*
+ if ((t != null) && t.isScalarValue()) {
+ return deserialize(p, ctxt);
+ }
+ */
+ // 19-Apr-2016, tatu: Alas, due to there typically really being anything for Reference wrapper
+ // itself, need to just ignore `typeDeser`, use TypeDeserializer we do have for contents
+ // and it might just work.
+
+ if (_valueTypeDeserializer == null) {
+ return deserialize(p, ctxt);
+ }
+ return referenceValue(_valueTypeDeserializer.deserializeTypedFromAny(p, ctxt));
+ }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StackTraceElementDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StackTraceElementDeserializer.java
index 6dc16a3..61572c6 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StackTraceElementDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StackTraceElementDeserializer.java
@@ -7,7 +7,6 @@
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.databind.JsonMappingException;
public class StackTraceElementDeserializer
extends StdScalarDeserializer<StackTraceElement>
@@ -17,46 +16,66 @@
public StackTraceElementDeserializer() { super(StackTraceElement.class); }
@Override
- public StackTraceElement deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException
+ public StackTraceElement deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
{
- JsonToken t = jp.getCurrentToken();
+ JsonToken t = p.getCurrentToken();
// Must get an Object
if (t == JsonToken.START_OBJECT) {
String className = "", methodName = "", fileName = "";
+ // Java 9 adds couple more things
+ String moduleName = null, moduleVersion = null;
int lineNumber = -1;
- while ((t = jp.nextValue()) != JsonToken.END_OBJECT) {
- String propName = jp.getCurrentName();
+ while ((t = p.nextValue()) != JsonToken.END_OBJECT) {
+ String propName = p.getCurrentName();
+ // TODO: with Java 8, convert to switch
if ("className".equals(propName)) {
- className = jp.getText();
+ className = p.getText();
} else if ("fileName".equals(propName)) {
- fileName = jp.getText();
+ fileName = p.getText();
} else if ("lineNumber".equals(propName)) {
if (t.isNumeric()) {
- lineNumber = jp.getIntValue();
+ lineNumber = p.getIntValue();
} else {
- throw JsonMappingException.from(jp, "Non-numeric token ("+t+") for property 'lineNumber'");
+ return (StackTraceElement) ctxt.handleUnexpectedToken(handledType(), t, p,
+"Non-numeric token (%s) for property 'lineNumber'", t);
}
} else if ("methodName".equals(propName)) {
- methodName = jp.getText();
+ methodName = p.getText();
} else if ("nativeMethod".equals(propName)) {
// no setter, not passed via constructor: ignore
+ } else if ("moduleName".equals(propName)) {
+ moduleName = p.getText();
+ } else if ("moduleVersion".equals(propName)) {
+ moduleVersion = p.getText();
} else {
- handleUnknownProperty(jp, ctxt, _valueClass, propName);
+ handleUnknownProperty(p, ctxt, _valueClass, propName);
}
}
- return new StackTraceElement(className, methodName, fileName, lineNumber);
+ return constructValue(ctxt, className, methodName, fileName, lineNumber,
+ moduleName, moduleVersion);
} else if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
- jp.nextToken();
- final StackTraceElement value = deserialize(jp, ctxt);
- if (jp.nextToken() != JsonToken.END_ARRAY) {
- throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY,
- "Attempted to unwrap single value array for single 'java.lang.StackTraceElement' value but there was more than a single value in the array"
- );
+ p.nextToken();
+ final StackTraceElement value = deserialize(p, ctxt);
+ if (p.nextToken() != JsonToken.END_ARRAY) {
+ handleMissingEndArrayForSingle(p, ctxt);
}
return value;
}
-
- throw ctxt.mappingException(_valueClass, t);
+ return (StackTraceElement) ctxt.handleUnexpectedToken(_valueClass, p);
}
-}
\ No newline at end of file
+
+ /**
+ * Overridable factory method used for constructing {@link StackTraceElement}s.
+ *
+ * @since 2.8
+ */
+ protected StackTraceElement constructValue(DeserializationContext ctxt,
+ String className, String methodName, String fileName, int lineNumber,
+ String moduleName, String moduleVersion)
+ {
+ // 21-May-2016, tatu: With Java 9, need to use different constructor, probably
+ // via different module, and throw exception here if extra args passed
+ return new StackTraceElement(className, methodName, fileName, lineNumber);
+ }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java
index 7848985..f26c2dd 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java
@@ -116,8 +116,9 @@
* this method if they are to handle type information.
*/
@Override
- public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException {
- return typeDeserializer.deserializeTypedFromAny(jp, ctxt);
+ public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
+ TypeDeserializer typeDeserializer) throws IOException {
+ return typeDeserializer.deserializeTypedFromAny(p, ctxt);
}
/*
@@ -128,24 +129,24 @@
/**********************************************************
*/
- protected final boolean _parseBooleanPrimitive(JsonParser jp, DeserializationContext ctxt) throws IOException
+ protected final boolean _parseBooleanPrimitive(JsonParser p, DeserializationContext ctxt) throws IOException
{
- JsonToken t = jp.getCurrentToken();
+ JsonToken t = p.getCurrentToken();
if (t == JsonToken.VALUE_TRUE) return true;
if (t == JsonToken.VALUE_FALSE) return false;
if (t == JsonToken.VALUE_NULL) return false;
- // [JACKSON-78]: should accept ints too, (0 == false, otherwise true)
+ // should accept ints too, (0 == false, otherwise true)
if (t == JsonToken.VALUE_NUMBER_INT) {
// 11-Jan-2012, tatus: May be outside of int...
- if (jp.getNumberType() == NumberType.INT) {
- return (jp.getIntValue() != 0);
+ if (p.getNumberType() == NumberType.INT) {
+ return (p.getIntValue() != 0);
}
- return _parseBooleanFromOther(jp, ctxt);
+ return _parseBooleanFromOther(p, ctxt);
}
// And finally, let's allow Strings to be converted too
if (t == JsonToken.VALUE_STRING) {
- String text = jp.getText().trim();
+ String text = p.getText().trim();
// [#422]: Allow aliases
if ("true".equals(text) || "True".equals(text)) {
return true;
@@ -156,21 +157,22 @@
if (_hasTextualNull(text)) {
return false;
}
- throw ctxt.weirdStringException(text, _valueClass, "only \"true\" or \"false\" recognized");
+ Boolean b = (Boolean) ctxt.handleWeirdStringValue(_valueClass, text,
+ "only \"true\" or \"false\" recognized");
+ return (b == null) ? false : b.booleanValue();
}
// [databind#381]
if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
- jp.nextToken();
- final boolean parsed = _parseBooleanPrimitive(jp, ctxt);
- t = jp.nextToken();
+ p.nextToken();
+ final boolean parsed = _parseBooleanPrimitive(p, ctxt);
+ t = p.nextToken();
if (t != JsonToken.END_ARRAY) {
- throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY,
- "Attempted to unwrap single value array for single 'boolean' value but there was more than a single value in the array");
+ handleMissingEndArrayForSingle(p, ctxt);
}
return parsed;
}
// Otherwise, no can do:
- throw ctxt.mappingException(_valueClass, t);
+ return ((Boolean) ctxt.handleUnexpectedToken(_valueClass, p)).booleanValue();
}
protected final Boolean _parseBoolean(JsonParser p, DeserializationContext ctxt)
@@ -183,7 +185,7 @@
if (t == JsonToken.VALUE_FALSE) {
return Boolean.FALSE;
}
- // [JACKSON-78]: should accept ints too, (0 == false, otherwise true)
+ // should accept ints too, (0 == false, otherwise true)
if (t == JsonToken.VALUE_NUMBER_INT) {
// 11-Jan-2012, tatus: May be outside of int...
if (p.getNumberType() == NumberType.INT) {
@@ -197,7 +199,7 @@
// And finally, let's allow Strings to be converted too
if (t == JsonToken.VALUE_STRING) {
String text = p.getText().trim();
- // [#422]: Allow aliases
+ // [databind#422]: Allow aliases
if ("true".equals(text) || "True".equals(text)) {
return Boolean.TRUE;
}
@@ -210,21 +212,21 @@
if (_hasTextualNull(text)) {
return (Boolean) getNullValue(ctxt);
}
- throw ctxt.weirdStringException(text, _valueClass, "only \"true\" or \"false\" recognized");
+ return (Boolean) ctxt.handleWeirdStringValue(_valueClass, text,
+ "only \"true\" or \"false\" recognized");
}
- // Issue#381
+ // [databind#381]
if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
p.nextToken();
final Boolean parsed = _parseBoolean(p, ctxt);
t = p.nextToken();
if (t != JsonToken.END_ARRAY) {
- throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY,
- "Attempted to unwrap single value array for single 'Boolean' value but there was more than a single value in the array");
+ handleMissingEndArrayForSingle(p, ctxt);
}
return parsed;
}
// Otherwise, no can do:
- throw ctxt.mappingException(_valueClass, t);
+ return (Boolean) ctxt.handleUnexpectedToken(_valueClass, p);
}
protected final boolean _parseBooleanFromOther(JsonParser p, DeserializationContext ctxt)
@@ -261,12 +263,15 @@
}
value = NumberInput.parseInt(text);
} catch (IllegalArgumentException iae) {
- throw ctxt.weirdStringException(text, _valueClass, "not a valid Byte value");
+ return (Byte) ctxt.handleWeirdStringValue(_valueClass, text,
+ "not a valid Byte value");
}
// So far so good: but does it fit?
// as per [JACKSON-804], allow range up to 255, inclusive
if (value < Byte.MIN_VALUE || value > 255) {
- throw ctxt.weirdStringException(text, _valueClass, "overflow, value can not be represented as 8-bit value");
+ return (Byte) ctxt.handleWeirdStringValue(_valueClass, text,
+ "overflow, value can not be represented as 8-bit value");
+ // fall-through for deferred fails
}
return Byte.valueOf((byte) value);
}
@@ -279,18 +284,17 @@
if (t == JsonToken.VALUE_NULL) {
return (Byte) getNullValue(ctxt);
}
- // Issue#381
+ // [databind#381]
if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
p.nextToken();
final Byte parsed = _parseByte(p, ctxt);
t = p.nextToken();
if (t != JsonToken.END_ARRAY) {
- throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY,
- "Attempted to unwrap single value array for single 'Byte' value but there was more than a single value in the array");
+ handleMissingEndArrayForSingle(p, ctxt);
}
return parsed;
}
- throw ctxt.mappingException(_valueClass, t);
+ return (Byte) ctxt.handleUnexpectedToken(_valueClass, p);
}
protected Short _parseShort(JsonParser p, DeserializationContext ctxt)
@@ -313,11 +317,13 @@
}
value = NumberInput.parseInt(text);
} catch (IllegalArgumentException iae) {
- throw ctxt.weirdStringException(text, _valueClass, "not a valid Short value");
+ return (Short) ctxt.handleWeirdStringValue(_valueClass, text,
+ "not a valid Short value");
}
// So far so good: but does it fit?
if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
- throw ctxt.weirdStringException(text, _valueClass, "overflow, value can not be represented as 16-bit value");
+ return (Short) ctxt.handleWeirdStringValue(_valueClass, text,
+ "overflow, value can not be represented as 16-bit value");
}
return Short.valueOf((short) value);
}
@@ -330,28 +336,28 @@
if (t == JsonToken.VALUE_NULL) {
return (Short) getNullValue(ctxt);
}
- // Issue#381
+ // [databind#381]
if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
p.nextToken();
final Short parsed = _parseShort(p, ctxt);
t = p.nextToken();
if (t != JsonToken.END_ARRAY) {
- throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY,
- "Attempted to unwrap single value array for single 'Short' value but there was more than a single value in the array");
+ handleMissingEndArrayForSingle(p, ctxt);
}
return parsed;
}
- throw ctxt.mappingException(_valueClass, t);
+ return (Short) ctxt.handleUnexpectedToken(_valueClass, p);
}
- protected final short _parseShortPrimitive(JsonParser jp, DeserializationContext ctxt)
+ protected final short _parseShortPrimitive(JsonParser p, DeserializationContext ctxt)
throws IOException
{
- int value = _parseIntPrimitive(jp, ctxt);
+ int value = _parseIntPrimitive(p, ctxt);
// So far so good: but does it fit?
if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
- throw ctxt.weirdStringException(String.valueOf(value),
- _valueClass, "overflow, value can not be represented as 16-bit value");
+ Number v = (Number) ctxt.handleWeirdStringValue(_valueClass, String.valueOf(value),
+ "overflow, value can not be represented as 16-bit value");
+ return (v == null) ? (short) 0 : v.shortValue();
}
return (short) value;
}
@@ -373,8 +379,10 @@
if (len > 9) {
long l = Long.parseLong(text);
if (l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) {
- throw ctxt.weirdStringException(text, _valueClass,
- "Overflow: numeric value ("+text+") out of range of int ("+Integer.MIN_VALUE+" - "+Integer.MAX_VALUE+")");
+ Number v = (Number) ctxt.handleWeirdStringValue(_valueClass, text,
+ "Overflow: numeric value (%s) out of range of int (%d -%d)",
+ text, Integer.MIN_VALUE, Integer.MAX_VALUE);
+ return (v == null) ? 0 : v.intValue();
}
return (int) l;
}
@@ -383,7 +391,9 @@
}
return NumberInput.parseInt(text);
} catch (IllegalArgumentException iae) {
- throw ctxt.weirdStringException(text, _valueClass, "not a valid int value");
+ Number v = (Number) ctxt.handleWeirdStringValue(_valueClass, text,
+ "not a valid int value");
+ return (v == null) ? 0 : v.intValue();
}
}
if (t == JsonToken.VALUE_NUMBER_FLOAT) {
@@ -400,13 +410,12 @@
final int parsed = _parseIntPrimitive(p, ctxt);
t = p.nextToken();
if (t != JsonToken.END_ARRAY) {
- throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY,
- "Attempted to unwrap single value array for single 'int' value but there was more than a single value in the array");
+ handleMissingEndArrayForSingle(p, ctxt);
}
return parsed;
}
// Otherwise, no can do:
- throw ctxt.mappingException(_valueClass, t);
+ return ((Number) ctxt.handleUnexpectedToken(_valueClass, p)).intValue();
}
protected final Integer _parseInteger(JsonParser p, DeserializationContext ctxt)
@@ -431,8 +440,9 @@
if (len > 9) {
long l = Long.parseLong(text);
if (l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) {
- throw ctxt.weirdStringException(text, _valueClass,
+ return (Integer) ctxt.handleWeirdStringValue(_valueClass, text,
"Overflow: numeric value ("+text+") out of range of Integer ("+Integer.MIN_VALUE+" - "+Integer.MAX_VALUE+")");
+ // fall-through
}
return Integer.valueOf((int) l);
}
@@ -441,8 +451,10 @@
}
return Integer.valueOf(NumberInput.parseInt(text));
} catch (IllegalArgumentException iae) {
- throw ctxt.weirdStringException(text, _valueClass, "not a valid Integer value");
+ return (Integer) ctxt.handleWeirdStringValue(_valueClass, text,
+ "not a valid Integer value");
}
+ // fall-through
case JsonTokenId.ID_NULL:
return (Integer) getNullValue(ctxt);
case JsonTokenId.ID_START_ARRAY:
@@ -450,15 +462,14 @@
p.nextToken();
final Integer parsed = _parseInteger(p, ctxt);
if (p.nextToken() != JsonToken.END_ARRAY) {
- throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY,
- "Attempted to unwrap single value array for single 'Integer' value but there was more than a single value in the array");
+ handleMissingEndArrayForSingle(p, ctxt);
}
return parsed;
}
break;
}
// Otherwise, no can do:
- throw ctxt.mappingException(_valueClass, p.getCurrentToken());
+ return (Integer) ctxt.handleUnexpectedToken(_valueClass, p);
}
protected final Long _parseLong(JsonParser p, DeserializationContext ctxt) throws IOException
@@ -485,7 +496,9 @@
try {
return Long.valueOf(NumberInput.parseLong(text));
} catch (IllegalArgumentException iae) { }
- throw ctxt.weirdStringException(text, _valueClass, "not a valid Long value");
+ return (Long) ctxt.handleWeirdStringValue(_valueClass, text,
+ "not a valid Long value");
+ // fall-through
case JsonTokenId.ID_NULL:
return (Long) getNullValue(ctxt);
case JsonTokenId.ID_START_ARRAY:
@@ -494,15 +507,14 @@
final Long parsed = _parseLong(p, ctxt);
JsonToken t = p.nextToken();
if (t != JsonToken.END_ARRAY) {
- throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY,
- "Attempted to unwrap single value array for single 'Long' value but there was more than a single value in the array");
+ handleMissingEndArrayForSingle(p, ctxt);
}
return parsed;
}
break;
}
// Otherwise, no can do:
- throw ctxt.mappingException(_valueClass, p.getCurrentToken());
+ return (Long) ctxt.handleUnexpectedToken(_valueClass, p);
}
protected final long _parseLongPrimitive(JsonParser p, DeserializationContext ctxt)
@@ -524,37 +536,40 @@
try {
return NumberInput.parseLong(text);
} catch (IllegalArgumentException iae) { }
- throw ctxt.weirdStringException(text, _valueClass, "not a valid long value");
+ {
+ Number v = (Number) ctxt.handleWeirdStringValue(_valueClass, text,
+ "not a valid long value");
+ return (v == null) ? 0 : v.longValue();
+ }
case JsonTokenId.ID_NULL:
return 0L;
case JsonTokenId.ID_START_ARRAY:
if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
- p.nextToken();
- final long parsed = _parseLongPrimitive(p, ctxt);
- JsonToken t = p.nextToken();
- if (t != JsonToken.END_ARRAY) {
- throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY,
- "Attempted to unwrap single value array for single 'long' value but there was more than a single value in the array");
- }
- return parsed;
+ p.nextToken();
+ final long parsed = _parseLongPrimitive(p, ctxt);
+ JsonToken t = p.nextToken();
+ if (t != JsonToken.END_ARRAY) {
+ handleMissingEndArrayForSingle(p, ctxt);
+ }
+ return parsed;
}
break;
}
- throw ctxt.mappingException(_valueClass, p.getCurrentToken());
+ return ((Number) ctxt.handleUnexpectedToken(_valueClass, p)).longValue();
}
-
- protected final Float _parseFloat(JsonParser jp, DeserializationContext ctxt)
+
+ protected final Float _parseFloat(JsonParser p, DeserializationContext ctxt)
throws IOException
{
// We accept couple of different types; obvious ones first:
- JsonToken t = jp.getCurrentToken();
+ JsonToken t = p.getCurrentToken();
if (t == JsonToken.VALUE_NUMBER_INT || t == JsonToken.VALUE_NUMBER_FLOAT) { // coercing should work too
- return jp.getFloatValue();
+ return p.getFloatValue();
}
// And finally, let's allow Strings to be converted too
if (t == JsonToken.VALUE_STRING) {
- String text = jp.getText().trim();
+ String text = p.getText().trim();
if (text.length() == 0) {
return (Float) getEmptyValue(ctxt);
}
@@ -581,36 +596,35 @@
try {
return Float.parseFloat(text);
} catch (IllegalArgumentException iae) { }
- throw ctxt.weirdStringException(text, _valueClass, "not a valid Float value");
+ return (Float) ctxt.handleWeirdStringValue(_valueClass, text,
+ "not a valid Float value");
}
if (t == JsonToken.VALUE_NULL) {
return (Float) getNullValue(ctxt);
}
- // Issue#381
if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
- jp.nextToken();
- final Float parsed = _parseFloat(jp, ctxt);
- t = jp.nextToken();
+ p.nextToken();
+ final Float parsed = _parseFloat(p, ctxt);
+ t = p.nextToken();
if (t != JsonToken.END_ARRAY) {
- throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY,
- "Attempted to unwrap single value array for single 'Byte' value but there was more than a single value in the array");
+ handleMissingEndArrayForSingle(p, ctxt);
}
return parsed;
}
// Otherwise, no can do:
- throw ctxt.mappingException(_valueClass, t);
+ return (Float) ctxt.handleUnexpectedToken(_valueClass, p);
}
-
- protected final float _parseFloatPrimitive(JsonParser jp, DeserializationContext ctxt)
+
+ protected final float _parseFloatPrimitive(JsonParser p, DeserializationContext ctxt)
throws IOException
{
- JsonToken t = jp.getCurrentToken();
+ JsonToken t = p.getCurrentToken();
if (t == JsonToken.VALUE_NUMBER_INT || t == JsonToken.VALUE_NUMBER_FLOAT) { // coercing should work too
- return jp.getFloatValue();
+ return p.getFloatValue();
}
if (t == JsonToken.VALUE_STRING) {
- String text = jp.getText().trim();
+ String text = p.getText().trim();
if (text.length() == 0 || _hasTextualNull(text)) {
return 0.0f;
}
@@ -632,36 +646,36 @@
try {
return Float.parseFloat(text);
} catch (IllegalArgumentException iae) { }
- throw ctxt.weirdStringException(text, _valueClass, "not a valid float value");
+ Number v = (Number) ctxt.handleWeirdStringValue(_valueClass, text,
+ "not a valid float value");
+ return (v == null) ? 0 : v.floatValue();
}
if (t == JsonToken.VALUE_NULL) {
return 0.0f;
}
- // Issue#381
if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
- jp.nextToken();
- final float parsed = _parseFloatPrimitive(jp, ctxt);
- t = jp.nextToken();
+ p.nextToken();
+ final float parsed = _parseFloatPrimitive(p, ctxt);
+ t = p.nextToken();
if (t != JsonToken.END_ARRAY) {
- throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY,
- "Attempted to unwrap single value array for single 'float' value but there was more than a single value in the array");
+ handleMissingEndArrayForSingle(p, ctxt);
}
return parsed;
}
// Otherwise, no can do:
- throw ctxt.mappingException(_valueClass, t);
+ return ((Number) ctxt.handleUnexpectedToken(_valueClass, p)).floatValue();
}
- protected final Double _parseDouble(JsonParser jp, DeserializationContext ctxt)
+ protected final Double _parseDouble(JsonParser p, DeserializationContext ctxt)
throws IOException
{
- JsonToken t = jp.getCurrentToken();
+ JsonToken t = p.getCurrentToken();
if (t == JsonToken.VALUE_NUMBER_INT || t == JsonToken.VALUE_NUMBER_FLOAT) { // coercing should work too
- return jp.getDoubleValue();
+ return p.getDoubleValue();
}
if (t == JsonToken.VALUE_STRING) {
- String text = jp.getText().trim();
+ String text = p.getText().trim();
if (text.length() == 0) {
return (Double) getEmptyValue(ctxt);
}
@@ -688,37 +702,37 @@
try {
return parseDouble(text);
} catch (IllegalArgumentException iae) { }
- throw ctxt.weirdStringException(text, _valueClass, "not a valid Double value");
+ return (Double) ctxt.handleWeirdStringValue(_valueClass, text,
+ "not a valid Double value");
}
if (t == JsonToken.VALUE_NULL) {
return (Double) getNullValue(ctxt);
}
if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
- jp.nextToken();
- final Double parsed = _parseDouble(jp, ctxt);
- t = jp.nextToken();
+ p.nextToken();
+ final Double parsed = _parseDouble(p, ctxt);
+ t = p.nextToken();
if (t != JsonToken.END_ARRAY) {
- throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY,
- "Attempted to unwrap single value array for single 'Double' value but there was more than a single value in the array");
+ handleMissingEndArrayForSingle(p, ctxt);
}
return parsed;
}
- // Otherwise, no can do:
- throw ctxt.mappingException(_valueClass, t);
+ // Otherwise, no can do:
+ return (Double) ctxt.handleUnexpectedToken(_valueClass, p);
}
- protected final double _parseDoublePrimitive(JsonParser jp, DeserializationContext ctxt)
+ protected final double _parseDoublePrimitive(JsonParser p, DeserializationContext ctxt)
throws IOException
{
// We accept couple of different types; obvious ones first:
- JsonToken t = jp.getCurrentToken();
+ JsonToken t = p.getCurrentToken();
if (t == JsonToken.VALUE_NUMBER_INT || t == JsonToken.VALUE_NUMBER_FLOAT) { // coercing should work too
- return jp.getDoubleValue();
+ return p.getDoubleValue();
}
// And finally, let's allow Strings to be converted too
if (t == JsonToken.VALUE_STRING) {
- String text = jp.getText().trim();
+ String text = p.getText().trim();
if (text.length() == 0 || _hasTextualNull(text)) {
return 0.0;
}
@@ -742,24 +756,25 @@
try {
return parseDouble(text);
} catch (IllegalArgumentException iae) { }
- throw ctxt.weirdStringException(text, _valueClass, "not a valid double value");
+ Number v = (Number) ctxt.handleWeirdStringValue(_valueClass, text,
+ "not a valid double value");
+ return (v == null) ? 0 : v.doubleValue();
}
if (t == JsonToken.VALUE_NULL) {
return 0.0;
}
- // Issue#381
+ // [databind#381]
if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
- jp.nextToken();
- final double parsed = _parseDoublePrimitive(jp, ctxt);
- t = jp.nextToken();
+ p.nextToken();
+ final double parsed = _parseDoublePrimitive(p, ctxt);
+ t = p.nextToken();
if (t != JsonToken.END_ARRAY) {
- throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY,
- "Attempted to unwrap single value array for single 'Byte' value but there was more than a single value in the array");
+ handleMissingEndArrayForSingle(p, ctxt);
}
return parsed;
}
- // Otherwise, no can do:
- throw ctxt.mappingException(_valueClass, t);
+ // Otherwise, no can do:
+ return ((Number) ctxt.handleUnexpectedToken(_valueClass, p)).doubleValue();
}
protected java.util.Date _parseDate(JsonParser p, DeserializationContext ctxt)
@@ -773,36 +788,40 @@
return (java.util.Date) getNullValue(ctxt);
}
if (t == JsonToken.VALUE_STRING) {
- String value = null;
- try {
- // As per [JACKSON-203], take empty Strings to mean
- value = p.getText().trim();
- if (value.length() == 0) {
- return (Date) getEmptyValue(ctxt);
- }
- if (_hasTextualNull(value)) {
- return (java.util.Date) getNullValue(ctxt);
- }
- return ctxt.parseDate(value);
- } catch (IllegalArgumentException iae) {
- throw ctxt.weirdStringException(value, _valueClass,
- "not a valid representation (error: "+iae.getMessage()+")");
- }
+ return _parseDate(p.getText().trim(), ctxt);
}
// [databind#381]
- if (t == JsonToken.START_ARRAY) {
- if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
- p.nextToken();
- final Date parsed = _parseDate(p, ctxt);
- t = p.nextToken();
- if (t != JsonToken.END_ARRAY) {
- throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY,
- "Attempted to unwrap single value array for single 'java.util.Date' value but there was more than a single value in the array");
- }
- return parsed;
- }
+ if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
+ p.nextToken();
+ final Date parsed = _parseDate(p, ctxt);
+ t = p.nextToken();
+ if (t != JsonToken.END_ARRAY) {
+ handleMissingEndArrayForSingle(p, ctxt);
+ }
+ return parsed;
}
- throw ctxt.mappingException(_valueClass, t);
+ return (java.util.Date) ctxt.handleUnexpectedToken(_valueClass, p);
+ }
+
+ /**
+ * @since 2.8
+ */
+ protected java.util.Date _parseDate(String value, DeserializationContext ctxt)
+ throws IOException
+ {
+ try {
+ // Take empty Strings to mean 'empty' Value, usually 'null':
+ if (value.length() == 0) {
+ return (Date) getEmptyValue(ctxt);
+ }
+ if (_hasTextualNull(value)) {
+ return (java.util.Date) getNullValue(ctxt);
+ }
+ return ctxt.parseDate(value);
+ } catch (IllegalArgumentException iae) {
+ return (java.util.Date) ctxt.handleWeirdStringValue(_valueClass, value,
+ "not a valid representation (error: %s)", iae.getMessage());
+ }
}
/**
@@ -831,12 +850,11 @@
return p.getText();
}
// [databind#381]
- if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
+ if ((t == JsonToken.START_ARRAY) && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
p.nextToken();
final String parsed = _parseString(p, ctxt);
if (p.nextToken() != JsonToken.END_ARRAY) {
- throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY,
- "Attempted to unwrap single value array for single 'String' value but there was more than a single value in the array");
+ handleMissingEndArrayForSingle(p, ctxt);
}
return parsed;
}
@@ -844,7 +862,7 @@
if (value != null) {
return value;
}
- throw ctxt.mappingException(String.class, p.getCurrentToken());
+ return (String) ctxt.handleUnexpectedToken(String.class, p);
}
/**
@@ -853,29 +871,30 @@
*
* @since 2.5
*/
- protected T _deserializeFromEmpty(JsonParser jp, DeserializationContext ctxt)
+ @SuppressWarnings("unchecked")
+ protected T _deserializeFromEmpty(JsonParser p, DeserializationContext ctxt)
throws IOException
{
- JsonToken t = jp.getCurrentToken();
+ JsonToken t = p.getCurrentToken();
if (t == JsonToken.START_ARRAY) {
if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT)) {
- t = jp.nextToken();
+ t = p.nextToken();
if (t == JsonToken.END_ARRAY) {
return null;
}
- throw ctxt.mappingException(handledType(), JsonToken.START_ARRAY);
+ return (T) ctxt.handleUnexpectedToken(handledType(), p);
}
} else if (t == JsonToken.VALUE_STRING) {
if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) {
- String str = jp.getText().trim();
+ String str = p.getText().trim();
if (str.isEmpty()) {
return null;
}
}
}
- throw ctxt.mappingException(handledType());
+ return (T) ctxt.handleUnexpectedToken(handledType(), p);
}
-
+
/**
* Helper method called to determine if we are seeing String value of
* "null", and, further, that it should be coerced to null just like
@@ -1058,12 +1077,8 @@
* Bean property. Method can deal with the problem as it sees fit (ignore,
* throw exception); but if it does return, it has to skip the matching
* Json content parser has.
- *<p>
- * NOTE: method signature was changed in version 1.5; explicit JsonParser
- * <b>must</b> be passed since it may be something other than what
- * context has. Prior versions did not include the first parameter.
*
- * @param jp Parser that points to value of the unknown property
+ * @param p Parser that points to value of the unknown property
* @param ctxt Context for deserialization; allows access to the parser,
* error reporting functionality
* @param instanceOrClass Instance that is being populated by this
@@ -1071,29 +1086,36 @@
* If null, will assume type is what {@link #getValueClass} returns.
* @param propName Name of the property that can not be mapped
*/
- protected void handleUnknownProperty(JsonParser jp, DeserializationContext ctxt, Object instanceOrClass, String propName)
+ protected void handleUnknownProperty(JsonParser p, DeserializationContext ctxt, Object instanceOrClass, String propName)
throws IOException
{
if (instanceOrClass == null) {
instanceOrClass = handledType();
}
// Maybe we have configured handler(s) to take care of it?
- if (ctxt.handleUnknownProperty(jp, this, instanceOrClass, propName)) {
+ if (ctxt.handleUnknownProperty(p, this, instanceOrClass, propName)) {
return;
}
- // Nope, not handled. Potentially that's a problem...
- ctxt.reportUnknownProperty(instanceOrClass, propName, this);
-
/* But if we do get this far, need to skip whatever value we
- * are pointing to now.
+ * are pointing to now (although handler is likely to have done that already)
*/
- jp.skipChildren();
+ p.skipChildren();
}
- protected void _failDoubleToIntCoercion(JsonParser jp, DeserializationContext ctxt,
+ protected void handleMissingEndArrayForSingle(JsonParser p, DeserializationContext ctxt)
+ throws IOException
+ {
+ ctxt.reportWrongTokenException(p, JsonToken.END_ARRAY,
+"Attempted to unwrap single value array for single '%s' value but there was more than a single value in the array",
+handledType().getName());
+ // 05-May-2016, tatu: Should recover somehow (maybe skip until END_ARRAY);
+ // but for now just fall through
+ }
+
+ protected void _failDoubleToIntCoercion(JsonParser p, DeserializationContext ctxt,
String type) throws IOException
{
- throw ctxt.mappingException("Can not coerce a floating-point value ('%s') into %s; enable `DeserializationFeature.ACCEPT_FLOAT_AS_INT` to allow",
- jp.getValueAsString(), type);
+ ctxt.reportMappingException("Can not coerce a floating-point value ('%s') into %s; enable `DeserializationFeature.ACCEPT_FLOAT_AS_INT` to allow",
+ p.getValueAsString(), type);
}
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdKeyDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdKeyDeserializer.java
index d95749a..b43c092 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdKeyDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdKeyDeserializer.java
@@ -3,13 +3,10 @@
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
+import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
-import java.util.Calendar;
-import java.util.Currency;
-import java.util.Date;
-import java.util.Locale;
-import java.util.UUID;
+import java.util.*;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.io.NumberInput;
@@ -114,7 +111,7 @@
}
return new StdKeyDeserializer(kind, raw);
}
-
+
@Override
public Object deserializeKey(String key, DeserializationContext ctxt)
throws IOException
@@ -128,12 +125,12 @@
return result;
}
} catch (Exception re) {
- throw ctxt.weirdKeyException(_keyClass, key, "not a valid representation: "+re.getMessage());
+ return ctxt.handleWeirdKey(_keyClass, key, "not a valid representation, problem: %s", re.getMessage());
}
if (_keyClass.isEnum() && ctxt.getConfig().isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
return null;
}
- throw ctxt.weirdKeyException(_keyClass, key, "not a valid representation");
+ return ctxt.handleWeirdKey(_keyClass, key, "not a valid representation");
}
public Class<?> getKeyClass() { return _keyClass; }
@@ -148,13 +145,13 @@
if ("false".equals(key)) {
return Boolean.FALSE;
}
- throw ctxt.weirdKeyException(_keyClass, key, "value not 'true' or 'false'");
+ return ctxt.handleWeirdKey(_keyClass, key, "value not 'true' or 'false'");
case TYPE_BYTE:
{
int value = _parseInt(key);
- // as per [JACKSON-804], allow range up to 255, inclusive
+ // allow range up to 255, inclusive (to support "unsigned" byte)
if (value < Byte.MIN_VALUE || value > 255) {
- throw ctxt.weirdKeyException(_keyClass, key, "overflow, value can not be represented as 8-bit value");
+ return ctxt.handleWeirdKey(_keyClass, key, "overflow, value can not be represented as 8-bit value");
}
return Byte.valueOf((byte) value);
}
@@ -162,7 +159,8 @@
{
int value = _parseInt(key);
if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
- throw ctxt.weirdKeyException(_keyClass, key, "overflow, value can not be represented as 16-bit value");
+ return ctxt.handleWeirdKey(_keyClass, key, "overflow, value can not be represented as 16-bit value");
+ // fall-through and truncate if need be
}
return Short.valueOf((short) value);
}
@@ -170,7 +168,7 @@
if (key.length() == 1) {
return Character.valueOf(key.charAt(0));
}
- throw ctxt.weirdKeyException(_keyClass, key, "can only convert 1-character Strings");
+ return ctxt.handleWeirdKey(_keyClass, key, "can only convert 1-character Strings");
case TYPE_INT:
return _parseInt(key);
@@ -186,13 +184,13 @@
try {
return _deser._deserialize(key, ctxt);
} catch (IOException e) {
- throw ctxt.weirdKeyException(_keyClass, key, "unable to parse key as locale");
+ return ctxt.handleWeirdKey(_keyClass, key, "unable to parse key as locale");
}
case TYPE_CURRENCY:
try {
return _deser._deserialize(key, ctxt);
} catch (IOException e) {
- throw ctxt.weirdKeyException(_keyClass, key, "unable to parse key as currency");
+ return ctxt.handleWeirdKey(_keyClass, key, "unable to parse key as currency");
}
case TYPE_DATE:
return ctxt.parseDate(key);
@@ -200,19 +198,32 @@
java.util.Date date = ctxt.parseDate(key);
return (date == null) ? null : ctxt.constructCalendar(date);
case TYPE_UUID:
- return UUID.fromString(key);
+ try {
+ return UUID.fromString(key);
+ } catch (Exception e) {
+ return ctxt.handleWeirdKey(_keyClass, key, "problem: %s", e.getMessage());
+ }
case TYPE_URI:
- return URI.create(key);
+ try {
+ return URI.create(key);
+ } catch (Exception e) {
+ return ctxt.handleWeirdKey(_keyClass, key, "problem: %s", e.getMessage());
+ }
case TYPE_URL:
- return new URL(key);
+ try {
+ return new URL(key);
+ } catch (MalformedURLException e) {
+ return ctxt.handleWeirdKey(_keyClass, key, "problem: %s", e.getMessage());
+ }
case TYPE_CLASS:
try {
return ctxt.findClass(key);
} catch (Exception e) {
- throw ctxt.weirdKeyException(_keyClass, key, "unable to parse key as Class");
+ return ctxt.handleWeirdKey(_keyClass, key, "unable to parse key as Class");
}
+ default:
+ throw new IllegalStateException("Internal error: unknown key type "+_keyClass);
}
- return null;
}
/*
@@ -304,10 +315,10 @@
if (result != null) {
return result;
}
+ return ctxt.handleWeirdKey(_keyClass, key, "not a valid representation");
} catch (Exception re) {
- throw ctxt.weirdKeyException(_keyClass, key, "not a valid representation: "+re.getMessage());
+ return ctxt.handleWeirdKey(_keyClass, key, "not a valid representation: %s", re.getMessage());
}
- throw ctxt.weirdKeyException(_keyClass, key, "not a valid representation");
}
public Class<?> getKeyClass() { return _keyClass; }
@@ -337,7 +348,7 @@
}
@Override
- public Object _parse(String key, DeserializationContext ctxt) throws JsonMappingException
+ public Object _parse(String key, DeserializationContext ctxt) throws IOException
{
if (_factory != null) {
try {
@@ -347,21 +358,23 @@
}
}
EnumResolver res = ctxt.isEnabled(DeserializationFeature.READ_ENUMS_USING_TO_STRING)
- ? _getToStringResolver() : _byNameResolver;
+ ? _getToStringResolver(ctxt) : _byNameResolver;
Enum<?> e = res.findEnum(key);
if ((e == null) && !ctxt.getConfig().isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
- throw ctxt.weirdKeyException(_keyClass, key, "not one of values excepted for Enum class: "
- +res.getEnumIds());
+ return ctxt.handleWeirdKey(_keyClass, key, "not one of values excepted for Enum class: %s",
+ res.getEnumIds());
+ // fall-through if problems are collected, not immediately thrown
}
return e;
}
- private EnumResolver _getToStringResolver()
+ private EnumResolver _getToStringResolver(DeserializationContext ctxt)
{
EnumResolver res = _byToStringResolver;
if (res == null) {
synchronized (this) {
- res = EnumResolver.constructUnsafeUsingToString(_byNameResolver.getEnumClass());
+ res = EnumResolver.constructUnsafeUsingToString(_byNameResolver.getEnumClass(),
+ ctxt.getAnnotationIntrospector());
}
}
return res;
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdValueInstantiator.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdValueInstantiator.java
index 991d6e3..adf6083 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdValueInstantiator.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdValueInstantiator.java
@@ -27,6 +27,11 @@
*/
protected final String _valueTypeDesc;
+ /**
+ * @since 2.8
+ */
+ protected final Class<?> _valueClass;
+
// // // Default (no-args) construction
/**
@@ -69,12 +74,18 @@
/**********************************************************
*/
+ /**
+ * @deprecated Since 2.7 use constructor that takes {@link JavaType} instead
+ */
+ @Deprecated
public StdValueInstantiator(DeserializationConfig config, Class<?> valueType) {
_valueTypeDesc = (valueType == null) ? "UNKNOWN TYPE" : valueType.getName();
+ _valueClass = (valueType == null) ? Object.class : valueType;
}
public StdValueInstantiator(DeserializationConfig config, JavaType valueType) {
_valueTypeDesc = (valueType == null) ? "UNKNOWN TYPE" : valueType.toString();
+ _valueClass = (valueType == null) ? Object.class : valueType.getRawClass();
}
/**
@@ -84,6 +95,7 @@
protected StdValueInstantiator(StdValueInstantiator src)
{
_valueTypeDesc = src._valueTypeDesc;
+ _valueClass = src._valueClass;
_defaultCreator = src._defaultCreator;
@@ -166,6 +178,11 @@
public String getValueTypeDesc() {
return _valueTypeDesc;
}
+
+ @Override
+ public Class<?> getValueClass() {
+ return _valueClass;
+ }
@Override
public boolean canCreateFromString() {
@@ -237,12 +254,13 @@
public Object createUsingDefault(DeserializationContext ctxt) throws IOException
{
if (_defaultCreator == null) { // sanity-check; caller should check
- throw new IllegalStateException("No default constructor for "+getValueTypeDesc());
+ return super.createUsingDefault(ctxt);
}
try {
return _defaultCreator.call();
} catch (Throwable t) {
- throw rewrapCtorProblem(ctxt, t);
+ return ctxt.handleInstantiationProblem(_defaultCreator.getDeclaringClass(),
+ null, rewrapCtorProblem(ctxt, t));
}
}
@@ -250,12 +268,13 @@
public Object createFromObjectWith(DeserializationContext ctxt, Object[] args) throws IOException
{
if (_withArgsCreator == null) { // sanity-check; caller should check
- throw new IllegalStateException("No with-args constructor for "+getValueTypeDesc());
+ return super.createFromObjectWith(ctxt, args);
}
try {
return _withArgsCreator.call(args);
} catch (Throwable t) {
- throw rewrapCtorProblem(ctxt, t);
+ return ctxt.handleInstantiationProblem(_withArgsCreator.getDeclaringClass(),
+ args, rewrapCtorProblem(ctxt, t));
}
}
@@ -290,41 +309,49 @@
try {
return _fromStringCreator.call1(value);
} catch (Throwable t) {
- throw rewrapCtorProblem(ctxt, t);
+ return ctxt.handleInstantiationProblem(_fromStringCreator.getDeclaringClass(),
+ value, rewrapCtorProblem(ctxt, t));
}
}
@Override
public Object createFromInt(DeserializationContext ctxt, int value) throws IOException
{
- try {
- // First: "native" int methods work best:
- if (_fromIntCreator != null) {
- return _fromIntCreator.call1(Integer.valueOf(value));
+ // First: "native" int methods work best:
+ if (_fromIntCreator != null) {
+ Object arg = Integer.valueOf(value);
+ try {
+ return _fromIntCreator.call1(arg);
+ } catch (Throwable t0) {
+ return ctxt.handleInstantiationProblem(_fromIntCreator.getDeclaringClass(),
+ arg, rewrapCtorProblem(ctxt, t0));
}
- // but if not, can do widening conversion
- if (_fromLongCreator != null) {
- return _fromLongCreator.call1(Long.valueOf(value));
- }
- } catch (Throwable t) {
- throw rewrapCtorProblem(ctxt, t);
}
- throw ctxt.mappingException("Can not instantiate value of type %s from Integral number (%s); no single-int-arg constructor/factory method",
- getValueTypeDesc(), value);
+ // but if not, can do widening conversion
+ if (_fromLongCreator != null) {
+ Object arg = Long.valueOf(value);
+ try {
+ return _fromLongCreator.call1(arg);
+ } catch (Throwable t0) {
+ return ctxt.handleInstantiationProblem(_fromLongCreator.getDeclaringClass(),
+ arg, rewrapCtorProblem(ctxt, t0));
+ }
+ }
+ return super.createFromInt(ctxt, value);
}
@Override
public Object createFromLong(DeserializationContext ctxt, long value) throws IOException
{
if (_fromLongCreator == null) {
- throw ctxt.mappingException("Can not instantiate value of type %s"
- +" from Long integral number (%s); no single-long-arg constructor/factory method",
- getValueTypeDesc(), value);
+ return super.createFromLong(ctxt, value);
}
+ Object arg = Long.valueOf(value);
try {
- return _fromLongCreator.call1(Long.valueOf(value));
- } catch (Throwable t) {
- throw rewrapCtorProblem(ctxt, t);
+ return _fromLongCreator.call1(arg);
+ } catch (Throwable t0) {
+ return ctxt.handleInstantiationProblem(_fromLongCreator.getDeclaringClass(),
+ arg, rewrapCtorProblem(ctxt, t0));
}
}
@@ -332,14 +359,14 @@
public Object createFromDouble(DeserializationContext ctxt, double value) throws IOException
{
if (_fromDoubleCreator == null) {
- throw ctxt.mappingException("Can not instantiate value of type %s"
- +" from Floating-point number (%s); no one-double/Double-arg constructor/factory method",
- getValueTypeDesc(), value);
+ return super.createFromDouble(ctxt, value);
}
+ Object arg = Double.valueOf(value);
try {
- return _fromDoubleCreator.call1(Double.valueOf(value));
- } catch (Throwable t) {
- throw rewrapCtorProblem(ctxt, t);
+ return _fromDoubleCreator.call1(arg);
+ } catch (Throwable t0) {
+ return ctxt.handleInstantiationProblem(_fromDoubleCreator.getDeclaringClass(),
+ arg, rewrapCtorProblem(ctxt, t0));
}
}
@@ -347,14 +374,14 @@
public Object createFromBoolean(DeserializationContext ctxt, boolean value) throws IOException
{
if (_fromBooleanCreator == null) {
- throw ctxt.mappingException("Can not instantiate value of type %s"
- +" from Boolean value (%s); no single-boolean/Boolean-arg constructor/factory method",
- getValueTypeDesc(), value);
+ return super.createFromBoolean(ctxt, value);
}
+ final Boolean arg = Boolean.valueOf(value);
try {
- return _fromBooleanCreator.call1(Boolean.valueOf(value));
- } catch (Throwable t) {
- throw rewrapCtorProblem(ctxt, t);
+ return _fromBooleanCreator.call1(arg);
+ } catch (Throwable t0) {
+ return ctxt.handleInstantiationProblem(_fromBooleanCreator.getDeclaringClass(),
+ arg, rewrapCtorProblem(ctxt, t0));
}
}
@@ -425,9 +452,7 @@
return (JsonMappingException) curr;
}
}
- String msg = String.format("Instantiation of %s value failed (%s): %s",
- getValueTypeDesc(), t.getClass().getName(), t.getMessage());
- return JsonMappingException.from(ctxt.getParser(), msg, t);
+ return ctxt.instantiationException(getValueClass(), t);
}
/**
@@ -440,9 +465,7 @@
if (t instanceof JsonMappingException) {
return (JsonMappingException) t;
}
- String msg = String.format("Instantiation of %s value failed (%s): %s",
- getValueTypeDesc(), t.getClass().getName(), t.getMessage());
- return JsonMappingException.from(ctxt.getParser(), msg, t);
+ return ctxt.instantiationException(getValueClass(), t);
}
/**
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StringArrayDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StringArrayDeserializer.java
index a4b0fe2..38f319f 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StringArrayDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StringArrayDeserializer.java
@@ -184,6 +184,6 @@
return null;
}
}
- throw ctxt.mappingException(_valueClass);
+ return (String[]) ctxt.handleUnexpectedToken(_valueClass, p);
}
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer.java
index b093dfc..0ef9714 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer.java
@@ -249,6 +249,7 @@
* throw an exception, or try to handle value as if member of implicit
* array, depending on configuration.
*/
+ @SuppressWarnings("unchecked")
private final Collection<String> handleNonArray(JsonParser p, DeserializationContext ctxt, Collection<String> result) throws IOException
{
// implicit arrays from single values?
@@ -256,7 +257,7 @@
((_unwrapSingle == null) &&
ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY));
if (!canWrap) {
- throw ctxt.mappingException(_collectionType.getRawClass());
+ return (Collection<String>) ctxt.handleUnexpectedToken(_collectionType.getRawClass(), p);
}
// Strings are one of "native" (intrinsic) types, so there's never type deserializer involved
JsonDeserializer<String> valueDes = _valueDeserializer;
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StringDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StringDeserializer.java
index c420536..f4dd6dc 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StringDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StringDeserializer.java
@@ -36,8 +36,7 @@
p.nextToken();
final String parsed = _parseString(p, ctxt);
if (p.nextToken() != JsonToken.END_ARRAY) {
- throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY,
- "Attempted to unwrap single value array for single 'String' value but there was more than a single value in the array");
+ handleMissingEndArrayForSingle(p, ctxt);
}
return parsed;
}
@@ -58,7 +57,7 @@
if (text != null) {
return text;
}
- throw ctxt.mappingException(_valueClass, p.getCurrentToken());
+ return (String) ctxt.handleUnexpectedToken(_valueClass, p);
}
// Since we can never have type info ("natural type"; String, Boolean, Integer, Double):
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/ThrowableDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/ThrowableDeserializer.java
index afd5f8b..0906ad5 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/ThrowableDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/ThrowableDeserializer.java
@@ -38,7 +38,7 @@
protected ThrowableDeserializer(BeanDeserializer src, NameTransformer unwrapper) {
super(src, unwrapper);
}
-
+
@Override
public JsonDeserializer<Object> unwrappingDeserializer(NameTransformer unwrapper) {
if (getClass() != ThrowableDeserializer.class) {
@@ -51,7 +51,6 @@
return new ThrowableDeserializer(this, unwrapper);
}
-
/*
/************************************************************
/* Overridden methods
@@ -70,15 +69,15 @@
_delegateDeserializer.deserialize(p, ctxt));
}
if (_beanType.isAbstract()) { // for good measure, check this too
- throw JsonMappingException.from(p, "Can not instantiate abstract type "+_beanType
- +" (need to add/enable type information?)");
+ return ctxt.handleMissingInstantiator(handledType(), p,
+ "abstract type (need to add/enable type information?)");
}
boolean hasStringCreator = _valueInstantiator.canCreateFromString();
boolean hasDefaultCtor = _valueInstantiator.canCreateUsingDefault();
// and finally, verify we do have single-String arg constructor (if no @JsonCreator)
if (!hasStringCreator && !hasDefaultCtor) {
- throw JsonMappingException.from(p,"Can not deserialize Throwable of type "+_beanType
- +" without having a default contructor, a single-String-arg constructor; or explicit @JsonCreator");
+ return ctxt.handleMissingInstantiator(handledType(), p,
+ "Throwable needs a default contructor, a single-String-arg constructor; or explicit @JsonCreator");
}
Object throwable = null;
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/UUIDDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/UUIDDeserializer.java
index 5fa5e11..797569e 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/UUIDDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/UUIDDeserializer.java
@@ -39,7 +39,7 @@
byte[] stuff = Base64Variants.getDefaultVariant().decode(id);
return _fromBytes(stuff, ctxt);
}
- _badFormat(id, ctxt);
+ return _badFormat(id, ctxt);
}
// verify hyphens first:
@@ -73,27 +73,25 @@
return null; // never gets here
}
- private void _badFormat(String uuidStr, DeserializationContext ctxt)
- throws JsonMappingException
+ private UUID _badFormat(String uuidStr, DeserializationContext ctxt)
+ throws IOException
{
- throw InvalidFormatException.from(ctxt.getParser(),
- String.format("UUID has to be represented by standard 36-char representation: input String '%s'",
- uuidStr),
- uuidStr, handledType());
+ return (UUID) ctxt.handleWeirdStringValue(handledType(), uuidStr,
+ "UUID has to be represented by standard 36-char representation");
}
-
- static int intFromChars(String str, int index, DeserializationContext ctxt) throws JsonMappingException {
+
+ int intFromChars(String str, int index, DeserializationContext ctxt) throws JsonMappingException {
return (byteFromChars(str, index, ctxt) << 24)
+ (byteFromChars(str, index+2, ctxt) << 16)
+ (byteFromChars(str, index+4, ctxt) << 8)
+ byteFromChars(str, index+6, ctxt);
}
- static int shortFromChars(String str, int index, DeserializationContext ctxt) throws JsonMappingException {
+ int shortFromChars(String str, int index, DeserializationContext ctxt) throws JsonMappingException {
return (byteFromChars(str, index, ctxt) << 8) + byteFromChars(str, index+2, ctxt);
}
- static int byteFromChars(String str, int index, DeserializationContext ctxt) throws JsonMappingException
+ int byteFromChars(String str, int index, DeserializationContext ctxt) throws JsonMappingException
{
final char c1 = str.charAt(index);
final char c2 = str.charAt(index+1);
@@ -110,11 +108,13 @@
return _badChar(str, index+1, ctxt, c2);
}
- static int _badChar(String uuidStr, int index, DeserializationContext ctxt, char c) throws JsonMappingException {
- String msg = String.format(
-"Non-hex character '%c' (value 0x%s), not valid for UUID String: input String '%s'",
- c, Integer.toHexString(c), uuidStr);
- throw InvalidFormatException.from(ctxt.getParser(), msg, uuidStr, UUID.class);
+ int _badChar(String uuidStr, int index, DeserializationContext ctxt, char c) throws JsonMappingException {
+ // 15-May-2016, tatu: Ideally should not throw, but call `handleWeirdStringValue`...
+ // however, control flow is gnarly here, so for now just throw
+ throw ctxt.weirdStringException(uuidStr, handledType(),
+ String.format(
+ "Non-hex character '%c' (value 0x%s), not valid for UUID String",
+ c, Integer.toHexString(c)));
}
private UUID _fromBytes(byte[] bytes, DeserializationContext ctxt) throws JsonMappingException {
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/UntypedObjectDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/UntypedObjectDeserializer.java
index c962214..38637e6 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/UntypedObjectDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/UntypedObjectDeserializer.java
@@ -274,7 +274,7 @@
// case JsonTokenId.ID_END_ARRAY: // invalid
default:
}
- throw ctxt.mappingException(Object.class);
+ return ctxt.handleUnexpectedToken(Object.class, p);
}
@Override
@@ -330,7 +330,7 @@
return null;
default:
}
- throw ctxt.mappingException(Object.class);
+ return ctxt.handleUnexpectedToken(Object.class, p);
}
/*
@@ -338,24 +338,24 @@
/* Internal methods
/**********************************************************
*/
-
+
/**
* Method called to map a JSON Array into a Java value.
*/
- protected Object mapArray(JsonParser jp, DeserializationContext ctxt) throws IOException
+ protected Object mapArray(JsonParser p, DeserializationContext ctxt) throws IOException
{
// Minor optimization to handle small lists (default size for ArrayList is 10)
- if (jp.nextToken() == JsonToken.END_ARRAY) {
+ if (p.nextToken() == JsonToken.END_ARRAY) {
return new ArrayList<Object>(2);
}
- Object value = deserialize(jp, ctxt);
- if (jp.nextToken() == JsonToken.END_ARRAY) {
+ Object value = deserialize(p, ctxt);
+ if (p.nextToken() == JsonToken.END_ARRAY) {
ArrayList<Object> l = new ArrayList<Object>(2);
l.add(value);
return l;
}
- Object value2 = deserialize(jp, ctxt);
- if (jp.nextToken() == JsonToken.END_ARRAY) {
+ Object value2 = deserialize(p, ctxt);
+ if (p.nextToken() == JsonToken.END_ARRAY) {
ArrayList<Object> l = new ArrayList<Object>(2);
l.add(value);
l.add(value2);
@@ -368,14 +368,14 @@
values[ptr++] = value2;
int totalSize = ptr;
do {
- value = deserialize(jp, ctxt);
+ value = deserialize(p, ctxt);
++totalSize;
if (ptr >= values.length) {
values = buffer.appendCompletedChunk(values);
ptr = 0;
}
values[ptr++] = value;
- } while (jp.nextToken() != JsonToken.END_ARRAY);
+ } while (p.nextToken() != JsonToken.END_ARRAY);
// let's create full array then
ArrayList<Object> result = new ArrayList<Object>(totalSize);
buffer.completeAndClearBuffer(values, ptr, result);
@@ -397,7 +397,7 @@
key1 = p.getCurrentName();
} else {
if (t != JsonToken.END_OBJECT) {
- throw ctxt.mappingException(handledType(), p.getCurrentToken());
+ return ctxt.handleUnexpectedToken(handledType(), p);
}
key1 = null;
}
@@ -444,23 +444,23 @@
/**
* Method called to map a JSON Array into a Java Object array (Object[]).
*/
- protected Object[] mapArrayToArray(JsonParser jp, DeserializationContext ctxt) throws IOException
+ protected Object[] mapArrayToArray(JsonParser p, DeserializationContext ctxt) throws IOException
{
// Minor optimization to handle small lists (default size for ArrayList is 10)
- if (jp.nextToken() == JsonToken.END_ARRAY) {
+ if (p.nextToken() == JsonToken.END_ARRAY) {
return NO_OBJECTS;
}
ObjectBuffer buffer = ctxt.leaseObjectBuffer();
Object[] values = buffer.resetAndStart();
int ptr = 0;
do {
- Object value = deserialize(jp, ctxt);
+ Object value = deserialize(p, ctxt);
if (ptr >= values.length) {
values = buffer.appendCompletedChunk(values);
ptr = 0;
}
values[ptr++] = value;
- } while (jp.nextToken() != JsonToken.END_ARRAY);
+ } while (p.nextToken() != JsonToken.END_ARRAY);
return buffer.completeAndClearBuffer(values, ptr);
}
@@ -540,58 +540,58 @@
//case JsonTokenId.ID_END_ARRAY: // invalid
default:
- throw ctxt.mappingException(Object.class);
}
+ return ctxt.handleUnexpectedToken(Object.class, p);
}
@Override
- public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException
+ public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException
{
- switch (jp.getCurrentTokenId()) {
+ switch (p.getCurrentTokenId()) {
case JsonTokenId.ID_START_ARRAY:
case JsonTokenId.ID_START_OBJECT:
case JsonTokenId.ID_FIELD_NAME:
- return typeDeserializer.deserializeTypedFromAny(jp, ctxt);
+ return typeDeserializer.deserializeTypedFromAny(p, ctxt);
case JsonTokenId.ID_STRING:
- return jp.getText();
+ return p.getText();
case JsonTokenId.ID_NUMBER_INT:
if (ctxt.isEnabled(DeserializationFeature.USE_BIG_INTEGER_FOR_INTS)) {
- return jp.getBigIntegerValue();
+ return p.getBigIntegerValue();
}
- return jp.getNumberValue();
+ return p.getNumberValue();
case JsonTokenId.ID_NUMBER_FLOAT:
if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) {
- return jp.getDecimalValue();
+ return p.getDecimalValue();
}
- return Double.valueOf(jp.getDoubleValue());
+ return Double.valueOf(p.getDoubleValue());
case JsonTokenId.ID_TRUE:
return Boolean.TRUE;
case JsonTokenId.ID_FALSE:
return Boolean.FALSE;
case JsonTokenId.ID_EMBEDDED_OBJECT:
- return jp.getEmbeddedObject();
+ return p.getEmbeddedObject();
case JsonTokenId.ID_NULL: // should not get this far really but...
return null;
default:
- throw ctxt.mappingException(Object.class);
}
+ return ctxt.handleUnexpectedToken(Object.class, p);
}
- protected Object mapArray(JsonParser jp, DeserializationContext ctxt) throws IOException
+ protected Object mapArray(JsonParser p, DeserializationContext ctxt) throws IOException
{
- Object value = deserialize(jp, ctxt);
- if (jp.nextToken() == JsonToken.END_ARRAY) {
+ Object value = deserialize(p, ctxt);
+ if (p.nextToken() == JsonToken.END_ARRAY) {
ArrayList<Object> l = new ArrayList<Object>(2);
l.add(value);
return l;
}
- Object value2 = deserialize(jp, ctxt);
- if (jp.nextToken() == JsonToken.END_ARRAY) {
+ Object value2 = deserialize(p, ctxt);
+ if (p.nextToken() == JsonToken.END_ARRAY) {
ArrayList<Object> l = new ArrayList<Object>(2);
l.add(value);
l.add(value2);
@@ -604,14 +604,14 @@
values[ptr++] = value2;
int totalSize = ptr;
do {
- value = deserialize(jp, ctxt);
+ value = deserialize(p, ctxt);
++totalSize;
if (ptr >= values.length) {
values = buffer.appendCompletedChunk(values);
ptr = 0;
}
values[ptr++] = value;
- } while (jp.nextToken() != JsonToken.END_ARRAY);
+ } while (p.nextToken() != JsonToken.END_ARRAY);
// let's create full array then
ArrayList<Object> result = new ArrayList<Object>(totalSize);
buffer.completeAndClearBuffer(values, ptr, result);
@@ -658,18 +658,18 @@
/**
* Method called to map a JSON Array into a Java Object array (Object[]).
*/
- protected Object[] mapArrayToArray(JsonParser jp, DeserializationContext ctxt) throws IOException {
+ protected Object[] mapArrayToArray(JsonParser p, DeserializationContext ctxt) throws IOException {
ObjectBuffer buffer = ctxt.leaseObjectBuffer();
Object[] values = buffer.resetAndStart();
int ptr = 0;
do {
- Object value = deserialize(jp, ctxt);
+ Object value = deserialize(p, ctxt);
if (ptr >= values.length) {
values = buffer.appendCompletedChunk(values);
ptr = 0;
}
values[ptr++] = value;
- } while (jp.nextToken() != JsonToken.END_ARRAY);
+ } while (p.nextToken() != JsonToken.END_ARRAY);
return buffer.completeAndClearBuffer(values, ptr);
}
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/exc/InvalidTypeIdException.java b/src/main/java/com/fasterxml/jackson/databind/exc/InvalidTypeIdException.java
new file mode 100644
index 0000000..8b05215
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/exc/InvalidTypeIdException.java
@@ -0,0 +1,53 @@
+package com.fasterxml.jackson.databind.exc;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonMappingException;
+
+/**
+ * Exception thrown when resolution of a type id fails.
+ *
+ * @since 2.8
+ */
+public class InvalidTypeIdException extends JsonMappingException
+{
+ private static final long serialVersionUID = 1L; // silly Eclipse, warnings
+
+ /**
+ * Basetype for which subtype was to be resolved
+ */
+ protected final JavaType _baseType;
+
+ /**
+ * Type id that failed to be resolved to a subtype
+ */
+ protected final String _typeId;
+
+ /*
+ /**********************************************************
+ /* Life-cycle
+ /**********************************************************
+ */
+
+ public InvalidTypeIdException(JsonParser p, String msg,
+ JavaType baseType, String typeId)
+ {
+ super(p, msg);
+ _baseType = baseType;
+ _typeId = typeId;
+ }
+
+ public static InvalidTypeIdException from(JsonParser p, String msg,
+ JavaType baseType, String typeId) {
+ return new InvalidTypeIdException(p, msg, baseType, typeId);
+ }
+
+ /*
+ /**********************************************************
+ /* Accessors
+ /**********************************************************
+ */
+
+ public JavaType getBaseType() { return _baseType; }
+ public String getTypeId() { return _typeId; }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/CoreXMLDeserializers.java b/src/main/java/com/fasterxml/jackson/databind/ext/CoreXMLDeserializers.java
index 0e0fe4d..64a06d8 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ext/CoreXMLDeserializers.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ext/CoreXMLDeserializers.java
@@ -65,7 +65,7 @@
* javax.xml types {@link QName}, {@link Duration} and {@link XMLGregorianCalendar}.
* Combined into a single class to eliminate bunch of one-off implementation
* classes, to reduce resulting jar size (mostly).
- *
+ *
* @since 2.4
*/
public static class Std extends FromStringDeserializer<Object>
@@ -80,36 +80,56 @@
}
@Override
- public Object deserialize(JsonParser jp, DeserializationContext ctxt)
- throws IOException, JsonProcessingException
+ public Object deserialize(JsonParser p, DeserializationContext ctxt)
+ throws IOException
{
- // For most types, use super impl; but not for GregorianCalendar
+ // For most types, use super impl; but GregorianCalendar also allows
+ // integer value (timestamp), which needs separate handling
if (_kind == TYPE_G_CALENDAR) {
- Date d = _parseDate(jp, ctxt);
- if (d == null) {
- return null;
+ if (p.hasToken(JsonToken.VALUE_NUMBER_INT)) {
+ return _gregorianFromDate(ctxt, _parseDate(p, ctxt));
}
- GregorianCalendar calendar = new GregorianCalendar();
- calendar.setTime(d);
- TimeZone tz = ctxt.getTimeZone();
- if (tz != null) {
- calendar.setTimeZone(tz);
- }
- return _dataTypeFactory.newXMLGregorianCalendar(calendar);
}
- return super.deserialize(jp, ctxt);
+ return super.deserialize(p, ctxt);
}
@Override
- protected Object _deserialize(String value, DeserializationContext ctxt) throws IllegalArgumentException
+ protected Object _deserialize(String value, DeserializationContext ctxt)
+ throws IOException
{
switch (_kind) {
case TYPE_DURATION:
return _dataTypeFactory.newDuration(value);
case TYPE_QNAME:
return QName.valueOf(value);
+ case TYPE_G_CALENDAR:
+ Date d;
+ try {
+ d = _parseDate(value, ctxt);
+ }
+ catch (JsonMappingException e) {
+ // try to parse from native XML Schema 1.0 lexical representation String,
+ // which includes time-only formats not handled by parseXMLGregorianCalendarFromJacksonFormat(...)
+ return _dataTypeFactory.newXMLGregorianCalendar(value);
+ }
+ return _gregorianFromDate(ctxt, d);
}
throw new IllegalStateException();
}
+
+ protected XMLGregorianCalendar _gregorianFromDate(DeserializationContext ctxt,
+ Date d)
+ {
+ if (d == null) {
+ return null;
+ }
+ GregorianCalendar calendar = new GregorianCalendar();
+ calendar.setTime(d);
+ TimeZone tz = ctxt.getTimeZone();
+ if (tz != null) {
+ calendar.setTimeZone(tz);
+ }
+ return _dataTypeFactory.newXMLGregorianCalendar(calendar);
+ }
}
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/DOMDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/ext/DOMDeserializer.java
index 78b1a00..74bc18d 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ext/DOMDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ext/DOMDeserializer.java
@@ -35,6 +35,9 @@
parserFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
} catch(ParserConfigurationException pce) {
// not much point to do anything; could log but...
+ } catch (Error e) {
+ // 14-Jul-2016, tatu: Not sure how or why, but during code coverage runs
+ // (via Cobertura) we get `java.lang.AbstractMethodError` so... ignore that too
}
DEFAULT_PARSER_FACTORY = parserFactory;
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/Java7Support.java b/src/main/java/com/fasterxml/jackson/databind/ext/Java7Support.java
new file mode 100644
index 0000000..5edf8a0
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ext/Java7Support.java
@@ -0,0 +1,48 @@
+package com.fasterxml.jackson.databind.ext;
+
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.PropertyName;
+import com.fasterxml.jackson.databind.introspect.Annotated;
+import com.fasterxml.jackson.databind.introspect.AnnotatedParameter;
+
+/**
+ * To support Java7-incomplete platforms, we will offer support for JDK 7
+ * annotations through this class, loaded dynamically; if loading fails,
+ * support will be missing. This class is the non-JDK-7-dependent API,
+ * and {@link Java7SupportImpl} is JDK7-dependent implementation of
+ * functionality.
+ */
+public abstract class Java7Support
+{
+ private final static Java7Support IMPL;
+
+ static {
+ Java7Support impl = null;
+ try {
+ Class<?> cls = Class.forName("com.fasterxml.jackson.databind.ext.Java7SupportImpl");
+ impl = (Java7Support) cls.newInstance();
+ } catch (Throwable t) {
+ // 24-Nov-2015, tatu: Should we log or not?
+ java.util.logging.Logger.getLogger(Java7Support.class.getName())
+ .warning("Unable to load JDK7 types (annotations, java.nio.file.Path): no Java7 support added");
+ }
+ IMPL = impl;
+ }
+
+ public static Java7Support instance() {
+ return IMPL;
+ }
+
+ public abstract Boolean findTransient(Annotated a);
+
+ public abstract Boolean hasCreatorAnnotation(Annotated a);
+
+ public abstract PropertyName findConstructorName(AnnotatedParameter p);
+
+ public abstract Class<?> getClassJavaNioFilePath();
+
+ public abstract JsonDeserializer<?> getDeserializerForJavaNioFilePath(Class<?> rawType);
+
+ public abstract JsonSerializer<?> getSerializerForJavaNioFilePath(Class<?> rawType);
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/Java7SupportImpl.java b/src/main/java/com/fasterxml/jackson/databind/ext/Java7SupportImpl.java
new file mode 100644
index 0000000..4546d03
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ext/Java7SupportImpl.java
@@ -0,0 +1,86 @@
+package com.fasterxml.jackson.databind.ext;
+
+import java.beans.ConstructorProperties;
+import java.beans.Transient;
+import java.nio.file.Path;
+
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.PropertyName;
+import com.fasterxml.jackson.databind.introspect.Annotated;
+import com.fasterxml.jackson.databind.introspect.AnnotatedParameter;
+import com.fasterxml.jackson.databind.introspect.AnnotatedWithParams;
+
+/**
+ * @since 2.8
+ */
+public class Java7SupportImpl extends Java7Support
+{
+ @SuppressWarnings("unused") // compiler warns, just needed side-effects
+ private final Class<?> _bogus;
+
+ public Java7SupportImpl() {
+ // Trigger loading of annotations that only JDK 7 has...
+ Class<?> cls = Transient.class;
+ cls = ConstructorProperties.class;
+ _bogus = cls;
+ }
+
+ @Override
+ public Class<?> getClassJavaNioFilePath() {
+ return Path.class;
+ }
+
+ @Override
+ public JsonDeserializer<?> getDeserializerForJavaNioFilePath(Class<?> rawType) {
+ if (rawType == Path.class) {
+ return new NioPathDeserializer();
+ }
+ return null;
+ }
+
+ @Override
+ public JsonSerializer<?> getSerializerForJavaNioFilePath(Class<?> rawType) {
+ if (Path.class.isAssignableFrom(rawType)) {
+ return new NioPathSerializer();
+ }
+ return null;
+ }
+
+ @Override
+ public Boolean findTransient(Annotated a) {
+ Transient t = a.getAnnotation(Transient.class);
+ if (t != null) {
+ return t.value();
+ }
+ return null;
+ }
+
+ @Override
+ public Boolean hasCreatorAnnotation(Annotated a) {
+ ConstructorProperties props = a.getAnnotation(ConstructorProperties.class);
+ // 08-Nov-2015, tatu: One possible check would be to ensure there is at least
+ // one name iff constructor has arguments. But seems unnecessary for now.
+ if (props != null) {
+ return Boolean.TRUE;
+ }
+ return null;
+ }
+
+ @Override
+ public PropertyName findConstructorName(AnnotatedParameter p)
+ {
+ AnnotatedWithParams ctor = p.getOwner();
+ if (ctor != null) {
+ ConstructorProperties props = ctor.getAnnotation(ConstructorProperties.class);
+ if (props != null) {
+ String[] names = props.value();
+ int ix = p.getIndex();
+ if (ix < names.length) {
+ return PropertyName.construct(names[ix]);
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/NioPathDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/ext/NioPathDeserializer.java
new file mode 100644
index 0000000..48a39eb
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ext/NioPathDeserializer.java
@@ -0,0 +1,41 @@
+package com.fasterxml.jackson.databind.ext;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer;
+
+/**
+ * @since 2.8
+ */
+public class NioPathDeserializer extends StdScalarDeserializer<Path>
+{
+ private static final long serialVersionUID = 1;
+
+ public NioPathDeserializer() { super(Path.class); }
+
+ @Override
+ public Path deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
+ if (!p.hasToken(JsonToken.VALUE_STRING)) {
+ return (Path) ctxt.handleUnexpectedToken(Path.class, p);
+ }
+ final String value = p.getText();
+ // If someone gives us an input with no : at all, treat as local path, instead of failing
+ // with invalid URI.
+ if (value.indexOf(':') < 0) {
+ return Paths.get(value);
+ }
+ try {
+ URI uri = new URI(value);
+ return Paths.get(uri);
+ } catch (URISyntaxException e) {
+ return (Path) ctxt.handleInstantiationProblem(handledType(), value, e);
+ }
+ }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/NioPathSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ext/NioPathSerializer.java
new file mode 100644
index 0000000..1abfab5
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ext/NioPathSerializer.java
@@ -0,0 +1,25 @@
+
+package com.fasterxml.jackson.databind.ext;
+
+import java.io.IOException;
+import java.nio.file.Path;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer;
+
+/**
+ * @since 2.8
+ */
+public class NioPathSerializer extends StdScalarSerializer<Path>
+{
+ private static final long serialVersionUID = 1;
+
+ public NioPathSerializer() { super(Path.class); }
+
+ @Override
+ public void serialize(Path value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
+ // write the Path as a URI, always.
+ gen.writeString(value.toUri().toString());
+ }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/OptionalHandlerFactory.java b/src/main/java/com/fasterxml/jackson/databind/ext/OptionalHandlerFactory.java
index 7e880e3..b3d9e58 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ext/OptionalHandlerFactory.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ext/OptionalHandlerFactory.java
@@ -3,7 +3,6 @@
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.Deserializers;
import com.fasterxml.jackson.databind.ser.Serializers;
-import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
/**
* Helper class used for isolating details of handling optional+external types
@@ -37,8 +36,6 @@
private final static String DESERIALIZER_FOR_DOM_DOCUMENT = "com.fasterxml.jackson.databind.ext.DOMDeserializer$DocumentDeserializer";
private final static String DESERIALIZER_FOR_DOM_NODE = "com.fasterxml.jackson.databind.ext.DOMDeserializer$NodeDeserializer";
- private final static String DESERIALIZER_FOR_PATH = "com.fasterxml.jackson.databind.ext.PathDeserializer";
-
// // Since 2.7, we will assume DOM classes are always found, both due to JDK 1.6 minimum
// // and because Android (and presumably GAE) have these classes
@@ -61,17 +58,14 @@
// // But Java7 type(s) may or may not be; dynamic lookup should be fine, still
// // (note: also assume it comes from JDK so that ClassLoader issues with OSGi
// // can, I hope, be avoided?)
-
- private final static Class<?> CLASS_JAVA7_PATH;
+
+ private static final Java7Support _jdk7Helper;
static {
- Class<?> cls = null;
+ Java7Support x = null;
try {
- cls = Class.forName("java.nio.file.Path");
- } catch (Exception e) {
- // not optimal but will do
- System.err.println("WARNING: could not load Java7 Path class");
- }
- CLASS_JAVA7_PATH = cls;
+ x = Java7Support.instance();
+ } catch (Throwable t) { }
+ _jdk7Helper = x;
}
public final static OptionalHandlerFactory instance = new OptionalHandlerFactory();
@@ -89,8 +83,11 @@
{
final Class<?> rawType = type.getRawClass();
- if ((CLASS_JAVA7_PATH != null) && CLASS_JAVA7_PATH.isAssignableFrom(rawType)) {
- return ToStringSerializer.instance;
+ if (_jdk7Helper != null) {
+ JsonSerializer<?> ser = _jdk7Helper.getSerializerForJavaNioFilePath(rawType);
+ if (ser != null) {
+ return ser;
+ }
}
if ((CLASS_DOM_NODE != null) && CLASS_DOM_NODE.isAssignableFrom(rawType)) {
return (JsonSerializer<?>) instantiate(SERIALIZER_FOR_DOM_NODE);
@@ -116,8 +113,11 @@
{
final Class<?> rawType = type.getRawClass();
- if ((CLASS_JAVA7_PATH != null) && CLASS_JAVA7_PATH.isAssignableFrom(rawType)) {
- return (JsonDeserializer<?>) instantiate(DESERIALIZER_FOR_PATH);
+ if (_jdk7Helper != null) {
+ JsonDeserializer<?> deser = _jdk7Helper.getDeserializerForJavaNioFilePath(rawType);
+ if (deser != null) {
+ return deser;
+ }
}
if ((CLASS_DOM_NODE != null) && CLASS_DOM_NODE.isAssignableFrom(rawType)) {
return (JsonDeserializer<?>) instantiate(DESERIALIZER_FOR_DOM_NODE);
diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/PathDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/ext/PathDeserializer.java
deleted file mode 100644
index 7b910db..0000000
--- a/src/main/java/com/fasterxml/jackson/databind/ext/PathDeserializer.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.fasterxml.jackson.databind.ext;
-
-import java.io.IOException;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonToken;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer;
-
-public class PathDeserializer extends StdScalarDeserializer<Path>
-{
- private static final long serialVersionUID = 1;
-
- public PathDeserializer() { super(Path.class); }
-
- @Override
- public Path deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
- JsonToken t= p.getCurrentToken();
- if (t != null) {
- if (t.isScalarValue()) {
- return Paths.get(p.getValueAsString());
- }
- // 16-Oct-2015: should we perhaps allow JSON Arrays (of Strings) as well?
- }
- throw ctxt.mappingException(Path.class, t);
- }
-}
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/Annotated.java b/src/main/java/com/fasterxml/jackson/databind/introspect/Annotated.java
index 90ed524..2d7c064 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/Annotated.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/Annotated.java
@@ -63,7 +63,7 @@
public abstract JavaType getType();
/**
- * @deprecated Since 2.7 Use {@link #getType()} instead. To be removed from 2.8.
+ * @deprecated Since 2.7 Use {@link #getType()} instead. To be removed from 2.9
*/
@Deprecated
public final JavaType getType(TypeBindings bogus) {
@@ -77,7 +77,7 @@
* as a result use of this method was deprecated in Jackson 2.7: see
* {@link #getType} for replacement.
*
- * @deprecated Since 2.7 should instead use {@link #getType()}. To be removed from 2.8
+ * @deprecated Since 2.7 should instead use {@link #getType()}. To be removed from 2.9
*/
@Deprecated
public Type getGenericType() {
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java
index cfb820e..7f7256d 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java
@@ -424,24 +424,33 @@
*/
private void resolveCreators()
{
- // Then see which constructors we have
- List<AnnotatedConstructor> constructors = null;
- ClassUtil.Ctor[] declaredCtors = ClassUtil.getConstructors(_class);
// Constructor also always members of this class
TypeResolutionContext typeContext = this;
- // 30-Apr-2016, tatu: [databind#1215]: Actually, while true, this does
- // NOT apply to context since sub-class may have type bindings
-// TypeResolutionContext typeContext = new TypeResolutionContext.Basic(_typeFactory, _type.getBindings());
- for (ClassUtil.Ctor ctor : declaredCtors) {
- if (_isIncludableConstructor(ctor.getConstructor())) {
- if (ctor.getParamCount() == 0) {
- _defaultConstructor = _constructDefaultConstructor(ctor, typeContext);
- } else {
- if (constructors == null) {
- constructors = new ArrayList<AnnotatedConstructor>(Math.max(10, declaredCtors.length));
+ // 30-Apr-2016, tatu: [databind#1215]: Actually, while true, this does
+ // NOT apply to context since sub-class may have type bindings
+// TypeResolutionContext typeContext = new TypeResolutionContext.Basic(_typeFactory, _type.getBindings());
+
+ // Then see which constructors we have
+ List<AnnotatedConstructor> constructors = null;
+
+ // 18-Jun-2016, tatu: Enum constructors will never be useful (unlike
+ // possibly static factory methods); but they can be royal PITA
+ // due to some oddities by JVM; see:
+ // [https://github.com/FasterXML/jackson-module-parameter-names/issues/35]
+ // for more. So, let's just skip them.
+ if (!_type.isEnumType()) {
+ ClassUtil.Ctor[] declaredCtors = ClassUtil.getConstructors(_class);
+ for (ClassUtil.Ctor ctor : declaredCtors) {
+ if (_isIncludableConstructor(ctor.getConstructor())) {
+ if (ctor.getParamCount() == 0) {
+ _defaultConstructor = _constructDefaultConstructor(ctor, typeContext);
+ } else {
+ if (constructors == null) {
+ constructors = new ArrayList<AnnotatedConstructor>(Math.max(10, declaredCtors.length));
+ }
+ constructors.add(_constructNonDefaultConstructor(ctor, typeContext));
}
- constructors.add(_constructNonDefaultConstructor(ctor, typeContext));
}
}
}
@@ -457,7 +466,6 @@
}
}
-
/* And then... let's remove all constructors that are deemed
* ignorable after all annotations have been properly collapsed.
*/
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedConstructor.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedConstructor.java
index 93f0876..a5e81ad 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedConstructor.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedConstructor.java
@@ -19,7 +19,7 @@
* @since 2.1
*/
protected Serialization _serialization;
-
+
/*
/**********************************************************
/* Life-cycle
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMember.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMember.java
index 03388e8..423f209 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMember.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMember.java
@@ -133,9 +133,12 @@
* @since 2.7
*/
public final void fixAccess(boolean force) {
- ClassUtil.checkAndFixAccess(getMember(), force);
+ Member m = getMember();
+ if (m != null) { // may be null for virtual members
+ ClassUtil.checkAndFixAccess(m, force);
+ }
}
-
+
/**
* @deprecated Since 2.7 use {@link #fixAccess(boolean)} instead
*/
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethod.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethod.java
index 8ec3737..e7d75ac 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethod.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethod.java
@@ -77,8 +77,8 @@
/**
* For methods, this returns declared return type, which is only
- * useful with getters (setters do not return anything; hence "void"
- * type is returned here)
+ * useful with getters (setters do not return anything; hence `Void`
+ * would be returned here)
*/
@Override
public JavaType getType() {
@@ -121,7 +121,15 @@
public final Object call1(Object arg) throws Exception {
return _method.invoke(null, arg);
}
-
+
+ public final Object callOn(Object pojo) throws Exception {
+ return _method.invoke(pojo);
+ }
+
+ public final Object callOnWith(Object pojo, Object... args) throws Exception {
+ return _method.invoke(pojo, args);
+ }
+
/*
/********************************************************
/* AnnotatedMember impl
@@ -192,7 +200,7 @@
+getFullName()+": "+e.getMessage(), e);
}
}
-
+
/*
/*****************************************************
/* Extended API, generic
@@ -203,7 +211,7 @@
return getDeclaringClass().getName() + "#" + getName() + "("
+getParameterCount()+" params)";
}
-
+
public Class<?>[] getRawParameterTypes()
{
if (_paramClasses == null) {
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedWithParams.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedWithParams.java
index a176f7d..8cf5a79 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedWithParams.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedWithParams.java
@@ -33,6 +33,14 @@
}
/**
+ * @since 2.8.1
+ */
+ protected AnnotatedWithParams(AnnotatedWithParams base, AnnotationMap[] paramAnnotations) {
+ super(base);
+ _paramAnnotations = paramAnnotations;
+ }
+
+ /**
* Method called to override a method parameter annotation,
* usually due to a mix-in
* annotation masking or overriding an annotation 'real' method
@@ -60,33 +68,6 @@
/*
/**********************************************************
- /* Helper methods for subclasses
- /**********************************************************
- */
-
- /*
- protected JavaType getType(TypeBindings bindings, TypeVariable<?>[] typeParams)
- {
- // [JACKSON-468] Need to consider local type binding declarations too...
- if (typeParams != null && typeParams.length > 0) {
- bindings = bindings.childInstance();
- for (TypeVariable<?> var : typeParams) {
- String name = var.getName();
- // to prevent infinite loops, need to first add placeholder ("<T extends Enum<T>>" etc)
- bindings._addPlaceholder(name);
- // About only useful piece of information is the lower bound (which is at least Object.class)
- Type lowerBound = var.getBounds()[0];
- JavaType type = (lowerBound == null) ? TypeFactory.unknownType()
- : bindings.resolveType(lowerBound);
- bindings.addBinding(var.getName(), type);
- }
- }
- return bindings.resolveType(getGenericType());
- }
- */
-
- /*
- /**********************************************************
/* Extended API
/**********************************************************
*/
@@ -116,7 +97,7 @@
public abstract JavaType getParameterType(int index);
/**
- * @deprecated Since 2.7, remove in 2.8
+ * @deprecated Since 2.7, remove in 2.9
*/
@Deprecated
public abstract Type getGenericParameterType(int index);
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair.java
index a7bfa3f..97e5edc 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair.java
@@ -7,6 +7,7 @@
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.Version;
@@ -114,6 +115,18 @@
}
@Override
+ public JsonIgnoreProperties.Value findPropertyIgnorals(Annotated a)
+ {
+ JsonIgnoreProperties.Value v2 = _secondary.findPropertyIgnorals(a);
+ JsonIgnoreProperties.Value v1 = _primary.findPropertyIgnorals(a);
+
+ if (v2 == null) { // shouldn't occur but
+ return v1;
+ }
+ return v2.withOverrides(v1);
+ }
+
+ @Override
@Deprecated // since 2.6
public String[] findPropertiesToIgnore(Annotated ac) {
String[] result = _primary.findPropertiesToIgnore(ac);
@@ -124,6 +137,7 @@
}
@Override
+ @Deprecated // since 2.8
public String[] findPropertiesToIgnore(Annotated ac, boolean forSerialization) {
String[] result = _primary.findPropertiesToIgnore(ac, forSerialization);
if (result == null) {
@@ -133,6 +147,7 @@
}
@Override
+ @Deprecated // since 2.8
public Boolean findIgnoreUnknownProperties(AnnotatedClass ac)
{
Boolean result = _primary.findIgnoreUnknownProperties(ac);
@@ -552,8 +567,9 @@
public boolean hasAsValueAnnotation(AnnotatedMethod am) {
return _primary.hasAsValueAnnotation(am) || _secondary.hasAsValueAnnotation(am);
}
-
+
@Override
+ @Deprecated
public String findEnumValue(Enum<?> value) {
String r = _primary.findEnumValue(value);
return (r == null) ? _secondary.findEnumValue(value) : r;
@@ -566,7 +582,13 @@
names = _primary.findEnumValues(enumType, enumValues, names);
return names;
}
-
+
+ @Override
+ public Enum<?> findDefaultEnumValue(Class<Enum<?>> enumCls) {
+ Enum<?> en = _primary.findDefaultEnumValue(enumCls);
+ return (en == null) ? _secondary.findDefaultEnumValue(enumCls) : en;
+ }
+
// // // Deserialization: general annotations
@Override
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/BasicBeanDescription.java b/src/main/java/com/fasterxml/jackson/databind/introspect/BasicBeanDescription.java
index 494e9c0..5f16ff5 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/BasicBeanDescription.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/BasicBeanDescription.java
@@ -6,6 +6,7 @@
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
+
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
@@ -22,9 +23,6 @@
* use cases for that, nor is such usage tested or supported.
* Separation from API is mostly to isolate some implementation details
* here and keep API simple.
- *<p>
- * Note that since 2.6 this class has been a thin shell around
- * {@link POJOPropertiesCollector}, which handles most of actual work.
*/
public class BasicBeanDescription extends BeanDescription
{
@@ -49,12 +47,6 @@
* Information collected about the class introspected.
*/
final protected AnnotatedClass _classInfo;
-
- /**
- * We may need type bindings for the bean type. If so, we'll
- * construct it lazily
- */
- protected TypeBindings _bindings;
/*
/**********************************************************
@@ -71,7 +63,7 @@
* Details of Object Id to include, if any
*/
protected ObjectIdInfo _objectIdInfo;
-
+
/*
/**********************************************************
/* Life-cycle
@@ -84,7 +76,12 @@
super(type);
_propCollector = coll;
_config = coll.getConfig();
- _annotationIntrospector = (_config == null) ? null : _config.getAnnotationIntrospector();
+ // NOTE: null config only for some pre-constructed types
+ if (_config == null) {
+ _annotationIntrospector = null;
+ } else {
+ _annotationIntrospector = _config.getAnnotationIntrospector();
+ }
_classInfo = classDef;
}
@@ -98,7 +95,12 @@
super(type);
_propCollector = null;
_config = config;
- _annotationIntrospector = (_config == null) ? null : _config.getAnnotationIntrospector();
+ // NOTE: null config only for some pre-constructed types
+ if (_config == null) {
+ _annotationIntrospector = null;
+ } else {
+ _annotationIntrospector = _config.getAnnotationIntrospector();
+ }
_classInfo = classDef;
_properties = props;
}
@@ -250,6 +252,7 @@
}
@Override
+ @Deprecated // since 2.8
public JavaType resolveType(java.lang.reflect.Type jdkType) {
if (jdkType == null) {
return null;
@@ -339,10 +342,24 @@
@Override
public JsonFormat.Value findExpectedFormat(JsonFormat.Value defValue)
{
+ // 15-Apr-2016, tatu: Let's check both per-type defaults and annotations; per-type
+ // defaults having higher precedence, so start with that
if (_annotationIntrospector != null) {
JsonFormat.Value v = _annotationIntrospector.findFormat(_classInfo);
if (v != null) {
- return v;
+ if (defValue == null) {
+ defValue = v;
+ } else {
+ defValue = defValue.withOverrides(v);
+ }
+ }
+ }
+ JsonFormat.Value v = _config.getDefaultPropertyFormat(_classInfo.getRawType());
+ if (v != null) {
+ if (defValue == null) {
+ defValue = v;
+ } else {
+ defValue = defValue.withOverrides(v);
}
}
return defValue;
@@ -374,7 +391,7 @@
if (_annotationIntrospector != null) {
JsonInclude.Value incl = _annotationIntrospector.findPropertyInclusion(_classInfo);
if (incl != null) {
- return defValue.withOverrides(incl);
+ return (defValue == null) ? incl : defValue.withOverrides(incl);
}
}
return defValue;
@@ -404,6 +421,23 @@
}
@Override
+ public AnnotatedMember findAnySetterField() throws IllegalArgumentException {
+ AnnotatedMember anySetter = (_propCollector == null) ? null : _propCollector.getAnySetterField();
+ if (anySetter != null) {
+ /*
+ * For now let's require a Map; in future can add support for other
+ * types like perhaps Iterable<Map.Entry>?
+ */
+ Class<?> type = anySetter.getRawType();
+ if (!Map.class.isAssignableFrom(type)) {
+ throw new IllegalArgumentException("Invalid 'any-setter' annotation on field " + anySetter.getName()
+ + "(): type is not instance of java.util.Map");
+ }
+ }
+ return anySetter;
+ }
+
+ @Override
public Map<String,AnnotatedMember> findBackReferenceProperties()
{
HashMap<String,AnnotatedMember> result = null;
@@ -523,7 +557,7 @@
if ("valueOf".equals(name)) {
return true;
}
- // [Issue#208] Also accept "fromString()", if takes String or CharSequence
+ // [databind#208] Also accept "fromString()", if takes String or CharSequence
if ("fromString".equals(name)) {
if (1 == am.getParameterCount()) {
Class<?> cls = am.getRawParameterType(0);
@@ -536,50 +570,9 @@
}
/**
- * @deprecated Since 2.4, use <code>findCreatorParameterNames()</code> instead.
+ * @deprecated since 2.8
*/
- @Deprecated
- public List<String> findCreatorPropertyNames()
- {
- List<PropertyName> params = findCreatorParameterNames();
- if (params.isEmpty()) {
- return Collections.emptyList();
- }
- List<String> result = new ArrayList<String>(params.size());
- for (PropertyName name : params) {
- result.add(name.getSimpleName());
- }
- return result;
- }
-
- /**
- * @deprecated Since 2.5, does not seem to be used at all.
- */
- @Deprecated
- public List<PropertyName> findCreatorParameterNames()
- {
- for (int i = 0; i < 2; ++i) {
- List<? extends AnnotatedWithParams> l = (i == 0)
- ? getConstructors() : getFactoryMethods();
- for (AnnotatedWithParams creator : l) {
- int argCount = creator.getParameterCount();
- if (argCount < 1) continue;
- PropertyName name = _findCreatorPropertyName(creator.getParameter(0));
- if (name == null || name.isEmpty()) {
- continue;
- }
- List<PropertyName> names = new ArrayList<PropertyName>();
- names.add(name);
- for (int p = 1; p < argCount; ++p) {
- name = _findCreatorPropertyName(creator.getParameter(p));
- names.add(name);
- }
- return names;
- }
- }
- return Collections.emptyList();
- }
-
+ @Deprecated // since 2.8, not used at least since 2.7
protected PropertyName _findCreatorPropertyName(AnnotatedParameter param)
{
PropertyName name = _annotationIntrospector.findNameForDeserialization(param);
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/BeanPropertyDefinition.java b/src/main/java/com/fasterxml/jackson/databind/introspect/BeanPropertyDefinition.java
index 0a85b30..4039230 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/BeanPropertyDefinition.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/BeanPropertyDefinition.java
@@ -230,6 +230,8 @@
/**
* Method used to check if this property has specific inclusion override
* associated with it or not.
+ * It should NOT check for any default settings (global, per-type, or
+ * containing POJO settings)
*
* @since 2.5
*/
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/ConcreteBeanPropertyBase.java b/src/main/java/com/fasterxml/jackson/databind/introspect/ConcreteBeanPropertyBase.java
index 99de5d0..7ea742b 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/ConcreteBeanPropertyBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/ConcreteBeanPropertyBase.java
@@ -28,9 +28,9 @@
/**
* Lazily accessed value for per-property format override definition.
*
- * @since 2.6
+ * @since 2.8
*/
- protected transient JsonFormat.Value _format;
+ protected transient JsonFormat.Value _propertyFormat;
protected ConcreteBeanPropertyBase(PropertyMetadata md) {
_metadata = (md == null) ? PropertyMetadata.STD_REQUIRED_OR_OPTIONAL : md;
@@ -38,7 +38,7 @@
protected ConcreteBeanPropertyBase(ConcreteBeanPropertyBase src) {
_metadata = src._metadata;
- _format = src._format;
+ _propertyFormat = src._propertyFormat;
}
@Override
@@ -53,17 +53,15 @@
@Override
@Deprecated
public final JsonFormat.Value findFormatOverrides(AnnotationIntrospector intr) {
- JsonFormat.Value f = _format;
- if (f == null) { // not yet looked up, do that
- if (intr != null) {
- AnnotatedMember member = getMember();
- if (member != null) {
- f = intr.findFormat(member);
- }
+ JsonFormat.Value f = null;
+ if (intr != null) {
+ AnnotatedMember member = getMember();
+ if (member != null) {
+ f = intr.findFormat(member);
}
- if (f == null) {
- f = EMPTY_FORMAT;
- }
+ }
+ if (f == null) {
+ f = EMPTY_FORMAT;
}
return f;
}
@@ -71,18 +69,27 @@
@Override
public JsonFormat.Value findPropertyFormat(MapperConfig<?> config, Class<?> baseType)
{
- // 08-Oct-2015, tatu: Unlike with Format, let's not cache locally here, for now?
- JsonFormat.Value v0 = config.getDefaultPropertyFormat(baseType);
- AnnotationIntrospector intr = config.getAnnotationIntrospector();
- AnnotatedMember member = getMember();
- if ((intr == null) || (member == null)) {
- return v0;
- }
- JsonFormat.Value v = intr.findFormat(member);
+ // 15-Apr-2016, tatu: Let's calculate lazily, retain; assumption being however that
+ // baseType is always the same
+ JsonFormat.Value v = _propertyFormat;
if (v == null) {
- return v0;
+ JsonFormat.Value v1 = config.getDefaultPropertyFormat(baseType);
+ JsonFormat.Value v2 = null;
+ AnnotationIntrospector intr = config.getAnnotationIntrospector();
+ if (intr != null) {
+ AnnotatedMember member = getMember();
+ if (member != null) {
+ v2 = intr.findFormat(member);
+ }
+ }
+ if (v1 == null) {
+ v = (v2 == null) ? EMPTY_FORMAT : v2;
+ } else {
+ v = (v2 == null) ? v1 : v1.withOverrides(v2);
+ }
+ _propertyFormat = v;
}
- return v0.withOverrides(v);
+ return v;
}
@Override
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java b/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java
index d74a2e9..1d19240 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java
@@ -1,7 +1,5 @@
package com.fasterxml.jackson.databind.introspect;
-import java.beans.ConstructorProperties;
-import java.beans.Transient;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.*;
@@ -12,6 +10,7 @@
import com.fasterxml.jackson.databind.annotation.*;
import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
+import com.fasterxml.jackson.databind.ext.Java7Support;
import com.fasterxml.jackson.databind.jsontype.NamedType;
import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
@@ -57,17 +56,15 @@
JsonManagedReference.class
};
- private static final Java7Support _jdk7Helper;
+ // NOTE: loading of Java7 dependencies is encapsulated by handlers in Java7Support,
+ // here we do not really need any handling; but for extra-safety use try-catch
+ private static final Java7Support _java7Helper;
static {
Java7Support x = null;
try {
- x = Java7Support.class.newInstance();
- } catch (Throwable t) {
- // 24-Nov-2015, tatu: Should we log or not?
- java.util.logging.Logger.getLogger(JacksonAnnotationIntrospector.class.getName())
- .warning("Unable to load JDK7 annotation types; will have to skip");
- }
- _jdk7Helper = x;
+ x = Java7Support.instance();
+ } catch (Throwable t) { }
+ _java7Helper = x;
}
/**
@@ -175,6 +172,7 @@
* explicit serialized name
*/
@Override
+ @Deprecated
public String findEnumValue(Enum<?> value)
{
// 11-Jun-2015, tatu: As per [databind#677], need to allow explicit naming.
@@ -233,6 +231,20 @@
return names;
}
+ /**
+ * Finds the Enum value that should be considered the default value, if possible.
+ * <p>
+ * This implementation relies on {@link JsonEnumDefaultValue} annotation to determine the default value if present.
+ *
+ * @param enumCls The Enum class to scan for the default value.
+ * @return null if none found or it's not possible to determine one.
+ * @since 2.8
+ */
+ @Override
+ public Enum<?> findDefaultEnumValue(Class<Enum<?>> enumCls) {
+ return ClassUtil.findFirstAnnotatedEnumValue(enumCls, JsonEnumDefaultValue.class);
+ }
+
/*
/**********************************************************
/* General class annotations
@@ -253,36 +265,43 @@
return PropertyName.construct(ann.value(), ns);
}
- @Override
- @Deprecated // since 2.6, remove from 2.7 or later
- public String[] findPropertiesToIgnore(Annotated ac) {
- JsonIgnoreProperties ignore = _findAnnotation(ac, JsonIgnoreProperties.class);
- return (ignore == null) ? null : ignore.value();
+ @Override // since 2.8
+ public JsonIgnoreProperties.Value findPropertyIgnorals(Annotated a)
+ {
+ JsonIgnoreProperties v = _findAnnotation(a, JsonIgnoreProperties.class);
+ if (v == null) {
+ // could alternatively return `Value.empty()`?
+ return null;
+ }
+ return JsonIgnoreProperties.Value.from(v);
}
-
+
@Override // since 2.6
- public String[] findPropertiesToIgnore(Annotated ac, boolean forSerialization) {
- JsonIgnoreProperties ignore = _findAnnotation(ac, JsonIgnoreProperties.class);
- if (ignore == null) {
+ @Deprecated // since 2.8
+ public String[] findPropertiesToIgnore(Annotated a, boolean forSerialization) {
+ JsonIgnoreProperties.Value v = findPropertyIgnorals(a);
+ if (v == null) {
return null;
}
// 13-May-2015, tatu: As per [databind#95], allow read-only/write-only props
if (forSerialization) {
- if (ignore.allowGetters()) {
+ if (v.getAllowGetters()) {
return null;
}
} else {
- if (ignore.allowSetters()) {
+ if (v.getAllowSetters()) {
return null;
}
}
- return ignore.value();
+ Set<String> ignored = v.getIgnored();
+ return ignored.toArray(new String[ignored.size()]);
}
-
+
@Override
- public Boolean findIgnoreUnknownProperties(AnnotatedClass ac) {
- JsonIgnoreProperties ignore = _findAnnotation(ac, JsonIgnoreProperties.class);
- return (ignore == null) ? null : ignore.ignoreUnknown();
+ @Deprecated // since 2.8
+ public Boolean findIgnoreUnknownProperties(AnnotatedClass a) {
+ JsonIgnoreProperties.Value v = findPropertyIgnorals(a);
+ return (v == null) ? null : v.getIgnoreUnknown();
}
@Override
@@ -855,7 +874,7 @@
}
// now, then, we need a placeholder for member (no real Field/Method):
AnnotatedMember member = new VirtualAnnotatedMember(ac, ac.getRawType(),
- attrName, type.getRawClass());
+ attrName, type);
// and with that and property definition
SimpleBeanPropertyDefinition propDef = SimpleBeanPropertyDefinition.construct(config,
member, propName, metadata, attr.include());
@@ -873,7 +892,7 @@
JavaType type = config.constructType(prop.type());
// now, then, we need a placeholder for member (no real Field/Method):
AnnotatedMember member = new VirtualAnnotatedMember(ac, ac.getRawType(),
- propName.getSimpleName(), type.getRawClass());
+ propName.getSimpleName(), type);
// and with that and property definition
SimpleBeanPropertyDefinition propDef = SimpleBeanPropertyDefinition.construct(config,
member, propName, metadata, prop.include());
@@ -1063,7 +1082,7 @@
}
return null;
}
-
+
@Override
public boolean hasAnySetterAnnotation(AnnotatedMethod am)
{
@@ -1086,9 +1105,8 @@
@Override
public boolean hasCreatorAnnotation(Annotated a)
{
- /* No dedicated disabling; regular @JsonIgnore used
- * if needs to be ignored (and if so, is handled prior
- * to this method getting called)
+ /* No dedicated disabling; regular @JsonIgnore used if needs to be
+ * ignored (and if so, is handled prior to this method getting called)
*/
JsonCreator ann = _findAnnotation(a, JsonCreator.class);
if (ann != null) {
@@ -1098,8 +1116,8 @@
// may or may not consider it a creator
if (_cfgConstructorPropertiesImpliesCreator ) {
if (a instanceof AnnotatedConstructor) {
- if (_jdk7Helper != null) {
- Boolean b = _jdk7Helper.hasCreatorAnnotation(a);
+ if (_java7Helper != null) {
+ Boolean b = _java7Helper.hasCreatorAnnotation(a);
if (b != null) {
return b.booleanValue();
}
@@ -1127,8 +1145,8 @@
if (ann != null) {
return ann.value();
}
- if (_jdk7Helper != null) {
- Boolean b = _jdk7Helper.findTransient(a);
+ if (_java7Helper != null) {
+ Boolean b = _java7Helper.findTransient(a);
if (b != null) {
return b.booleanValue();
}
@@ -1165,8 +1183,8 @@
AnnotatedWithParams ctor = p.getOwner();
if (ctor != null) {
- if (_jdk7Helper != null) {
- PropertyName name = _jdk7Helper.findConstructorName(p);
+ if (_java7Helper != null) {
+ PropertyName name = _java7Helper.findConstructorName(p);
if (name != null) {
return name;
}
@@ -1260,57 +1278,4 @@
/* Helper classes
/**********************************************************
*/
-
- /**
- * To support Java7-incomplete platforms, we will offer support for JDK 7
- * annotations through this class, loaded dynamically; if loading fails,
- * support will be missing.
- */
- private static class Java7Support
- {
- @SuppressWarnings("unused") // compiler warns, just needed side-effects
- private final Class<?> _bogus;
-
- @SuppressWarnings("unused") // compiler warns; called via Reflection
- public Java7Support() {
- // Trigger loading of annotations that only JDK 7 has...
- Class<?> cls = Transient.class;
- cls = ConstructorProperties.class;
- _bogus = cls;
- }
-
- public Boolean findTransient(Annotated a) {
- Transient t = a.getAnnotation(Transient.class);
- if (t != null) {
- return t.value();
- }
- return null;
- }
-
- public Boolean hasCreatorAnnotation(Annotated a) {
- ConstructorProperties props = a.getAnnotation(ConstructorProperties.class);
- // 08-Nov-2015, tatu: One possible check would be to ensure there is at least
- // one name iff constructor has arguments. But seems unnecessary for now.
- if (props != null) {
- return Boolean.TRUE;
- }
- return null;
- }
-
- public PropertyName findConstructorName(AnnotatedParameter p)
- {
- AnnotatedWithParams ctor = p.getOwner();
- if (ctor != null) {
- ConstructorProperties props = ctor.getAnnotation(ConstructorProperties.class);
- if (props != null) {
- String[] names = props.value();
- int ix = p.getIndex();
- if (ix < names.length) {
- return PropertyName.construct(names[ix]);
- }
- }
- }
- return null;
- }
- }
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java b/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java
index dd4733f..97d03d4 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java
@@ -3,6 +3,7 @@
import java.lang.reflect.Modifier;
import java.util.*;
+import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
@@ -83,6 +84,8 @@
protected LinkedList<AnnotatedMember> _anyGetters;
protected LinkedList<AnnotatedMethod> _anySetters;
+
+ protected LinkedList<AnnotatedMember> _anySetterField;
/**
* Method(s) marked with 'JsonValue' annotation
@@ -127,7 +130,7 @@
_config.getDefaultVisibilityChecker());
}
}
-
+
/*
/**********************************************************
/* Public API
@@ -194,6 +197,21 @@
}
return null;
}
+
+ public AnnotatedMember getAnySetterField()
+ {
+ if (!_collected) {
+ collectAll();
+ }
+ if (_anySetterField != null) {
+ if (_anySetterField.size() > 1) {
+ reportProblem("Multiple 'any-Setters' defined ("+_anySetters.get(0)+" vs "
+ +_anySetterField.get(1)+")");
+ }
+ return _anySetterField.getFirst();
+ }
+ return null;
+ }
public AnnotatedMethod getAnySetterMethod()
{
@@ -367,7 +385,8 @@
} else {
pn = ai.findNameForDeserialization(f);
}
- boolean nameExplicit = (pn != null);
+ boolean hasName = (pn != null);
+ boolean nameExplicit = hasName;
if (nameExplicit && pn.isEmpty()) { // empty String meaning "use default name", here just means "same as field name"
pn = _propNameFromSimple(implName);
@@ -383,9 +402,13 @@
// 13-May-2015, tatu: Moved from earlier place (AnnotatedClass) in 2.6
if (f.isTransient()) {
- visible = false;
- if (transientAsIgnoral) {
- ignored = true;
+ // 20-May-2016, tatu: as per [databind#1184] explicit annotation should override
+ // "default" `transient`
+ if (!hasName) {
+ visible = false;
+ if (transientAsIgnoral) {
+ ignored = true;
+ }
}
}
/* [databind#190]: this is the place to prune final fields, if they are not
@@ -396,6 +419,15 @@
if (pruneFinalFields && (pn == null) && !ignored && Modifier.isFinal(f.getModifiers())) {
continue;
}
+
+ //if field has annotation @JsonAnySetter
+ if(f.hasAnnotation(JsonAnySetter.class)) {
+ if (_anySetterField == null) {
+ _anySetterField = new LinkedList<AnnotatedMember>();
+ }
+ _anySetterField.add(f);
+ }
+
_property(props, implName).addField(f, pn, nameExplicit, visible, ignored);
}
}
@@ -406,22 +438,23 @@
protected void _addCreators(Map<String, POJOPropertyBuilder> props)
{
// can be null if annotation processing is disabled...
- if (_annotationIntrospector != null) {
- for (AnnotatedConstructor ctor : _classDef.getConstructors()) {
- if (_creatorProperties == null) {
- _creatorProperties = new LinkedList<POJOPropertyBuilder>();
- }
- for (int i = 0, len = ctor.getParameterCount(); i < len; ++i) {
- _addCreatorParam(props, ctor.getParameter(i));
- }
+ if (_annotationIntrospector == null) {
+ return;
+ }
+ for (AnnotatedConstructor ctor : _classDef.getConstructors()) {
+ if (_creatorProperties == null) {
+ _creatorProperties = new LinkedList<POJOPropertyBuilder>();
}
- for (AnnotatedMethod factory : _classDef.getStaticMethods()) {
- if (_creatorProperties == null) {
- _creatorProperties = new LinkedList<POJOPropertyBuilder>();
- }
- for (int i = 0, len = factory.getParameterCount(); i < len; ++i) {
- _addCreatorParam(props, factory.getParameter(i));
- }
+ for (int i = 0, len = ctor.getParameterCount(); i < len; ++i) {
+ _addCreatorParam(props, ctor.getParameter(i));
+ }
+ }
+ for (AnnotatedMethod factory : _classDef.getStaticMethods()) {
+ if (_creatorProperties == null) {
+ _creatorProperties = new LinkedList<POJOPropertyBuilder>();
+ }
+ for (int i = 0, len = factory.getParameterCount(); i < len; ++i) {
+ _addCreatorParam(props, factory.getParameter(i));
}
}
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertyBuilder.java b/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertyBuilder.java
index 62bcb5e..4636466 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertyBuilder.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertyBuilder.java
@@ -40,9 +40,9 @@
protected final PropertyName _internalName;
protected Linked<AnnotatedField> _fields;
-
+
protected Linked<AnnotatedParameter> _ctorParameters;
-
+
protected Linked<AnnotatedMethod> _getters;
protected Linked<AnnotatedMethod> _setters;
@@ -316,9 +316,9 @@
continue;
}
}
-
- throw new IllegalArgumentException("Conflicting setter definitions for property \""+getName()+"\": "
- +curr.value.getFullName()+" vs "+next.value.getFullName());
+ throw new IllegalArgumentException(String.format(
+ "Conflicting setter definitions for property \"%s\": %s vs %s",
+ getName(), curr.value.getFullName(), next.value.getFullName()));
}
// One more thing; to avoid having to do it again...
_setters = curr.withoutNext();
@@ -500,13 +500,12 @@
}
protected Boolean _findRequired() {
- Boolean b = fromMemberAnnotations(new WithMember<Boolean>() {
+ return fromMemberAnnotations(new WithMember<Boolean>() {
@Override
public Boolean withMember(AnnotatedMember member) {
return _annotationIntrospector.hasRequiredMarker(member);
}
});
- return b;
}
protected String _findDescription() {
@@ -552,14 +551,14 @@
@Override
public JsonInclude.Value findInclusion() {
- if (_annotationIntrospector != null) {
- AnnotatedMember a = getAccessor();
- JsonInclude.Value v = _annotationIntrospector.findPropertyInclusion(a);
- if (v != null) {
- return v;
- }
- }
- return JsonInclude.Value.empty();
+ AnnotatedMember a = getAccessor();
+ // 16-Apr-2106, tatu: Let's include per-type default inclusion too
+ // 17-Aug-2016, tatu: Do NOT include global, or per-type defaults, because
+ // not all of this information (specifically, enclosing type's settings)
+ // is available here
+ JsonInclude.Value v = (_annotationIntrospector == null) ?
+ null : _annotationIntrospector.findPropertyInclusion(a);
+ return (v == null) ? JsonInclude.Value.empty() : v;
}
public JsonProperty.Access findAccess() {
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/VirtualAnnotatedMember.java b/src/main/java/com/fasterxml/jackson/databind/introspect/VirtualAnnotatedMember.java
index 06713d9..13038f0 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/VirtualAnnotatedMember.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/VirtualAnnotatedMember.java
@@ -17,7 +17,10 @@
protected final Class<?> _declaringClass;
- protected final Class<?> _rawType;
+ /**
+ * @since 2.8 with this signature; had <code>_rawType</code> earlier
+ */
+ protected final JavaType _type;
protected final String _name;
@@ -28,14 +31,23 @@
*/
public VirtualAnnotatedMember(TypeResolutionContext typeContext, Class<?> declaringClass,
- String name, Class<?> rawType)
+ String name, JavaType type)
{
super(typeContext, /* AnnotationMap*/ null);
_declaringClass = declaringClass;
- _rawType = rawType;
+ _type = type;
_name = name;
}
+ /**
+ * @deprecated Since 2.8
+ */
+ @Deprecated
+ public VirtualAnnotatedMember(TypeResolutionContext typeContext, Class<?> declaringClass,
+ String name, Class<?> rawType) {
+ this(typeContext, declaringClass, name, typeContext.resolveType(rawType));
+ }
+
@Override
public Annotated withAnnotations(AnnotationMap fallback) {
return this;
@@ -58,12 +70,12 @@
@Override
public Class<?> getRawType() {
- return _rawType;
+ return _type.getRawClass();
}
@Override
public JavaType getType() {
- return _typeContext.resolveType(_rawType);
+ return _type;
}
/*
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/VisibilityChecker.java b/src/main/java/com/fasterxml/jackson/databind/introspect/VisibilityChecker.java
index 0a55496..0e2d0ad 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/VisibilityChecker.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/VisibilityChecker.java
@@ -241,11 +241,11 @@
{
Std curr = this;
if (ann != null) {
- curr = curr.withGetterVisibility(ann.getterVisibility());
- curr = curr.withIsGetterVisibility(ann.isGetterVisibility());
- curr = curr.withSetterVisibility(ann.setterVisibility());
- curr = curr.withCreatorVisibility(ann.creatorVisibility());
- curr = curr.withFieldVisibility(ann.fieldVisibility());
+ curr = curr.withGetterVisibility(ann.getterVisibility());
+ curr = curr.withIsGetterVisibility(ann.isGetterVisibility());
+ curr = curr.withSetterVisibility(ann.setterVisibility());
+ curr = curr.withCreatorVisibility(ann.creatorVisibility());
+ curr = curr.withFieldVisibility(ann.fieldVisibility());
}
return curr;
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeIdResolver.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeIdResolver.java
index 5ea8386..a7e7e94 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeIdResolver.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeIdResolver.java
@@ -1,5 +1,7 @@
package com.fasterxml.jackson.databind.jsontype;
+import java.io.IOException;
+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.DatabindContext;
import com.fasterxml.jackson.databind.JavaType;
@@ -42,7 +44,7 @@
/* Conversions between types and type ids
/**********************************************************
*/
-
+
/**
* Method called to serialize type of the type of given value
* as a String to include in serialized JSON content.
@@ -65,19 +67,12 @@
*/
public String idFromBaseType();
- // !!! TODO: remove from 2.8
- /**
- * @deprecated since 2.5; call {@link #typeFromId(DatabindContext, String)} instead
- */
- @Deprecated // since 2.5
- public JavaType typeFromId(String id);
-
/**
* Method called to resolve type from given type identifier.
*
- * @since 2.5 -- but since 2.3 has existed in {@link com.fasterxml.jackson.databind.jsontype.impl.TypeIdResolverBase}
+ * @since 2.5 (throws clause added in 2.8)
*/
- public JavaType typeFromId(DatabindContext context, String id);
+ public JavaType typeFromId(DatabindContext context, String id) throws IOException;
/**
* Method called for error-reporting and diagnostics purposes.
@@ -85,7 +80,7 @@
* @since 2.7 -- but since 2.5 has existed in {@link com.fasterxml.jackson.databind.jsontype.impl.TypeIdResolverBase}
*/
public String getDescForKnownTypeIds();
-
+
/*
/**********************************************************
/* Accessors for metadata
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeSerializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeSerializer.java
index 3e5373e..09f4be9 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeSerializer.java
@@ -73,9 +73,9 @@
*
* @param value Value that will be serialized, for which type information is
* to be written
- * @param gen Generator to use for writing type information
+ * @param g Generator to use for writing type information
*/
- public abstract void writeTypePrefixForScalar(Object value, JsonGenerator gen) throws IOException;
+ public abstract void writeTypePrefixForScalar(Object value, JsonGenerator g) throws IOException;
/**
* Method called to write initial part of type information for given
@@ -86,9 +86,9 @@
*
* @param value Value that will be serialized, for which type information is
* to be written
- * @param gen Generator to use for writing type information
+ * @param g Generator to use for writing type information
*/
- public abstract void writeTypePrefixForObject(Object value, JsonGenerator gen) throws IOException;
+ public abstract void writeTypePrefixForObject(Object value, JsonGenerator g) throws IOException;
/**
* Method called to write initial part of type information for given
@@ -99,17 +99,17 @@
*
* @param value Value that will be serialized, for which type information is
* to be written
- * @param gen Generator to use for writing type information
+ * @param g Generator to use for writing type information
*/
- public abstract void writeTypePrefixForArray(Object value, JsonGenerator gen) throws IOException;
-
+ public abstract void writeTypePrefixForArray(Object value, JsonGenerator g) throws IOException;
+
/**
* Method called after value has been serialized, to close any scopes opened
* by earlier matching call to {@link #writeTypePrefixForScalar}.
* Actual action to take may depend on various factors, but has to match with
* action {@link #writeTypePrefixForScalar} did (close array or object; or do nothing).
*/
- public abstract void writeTypeSuffixForScalar(Object value, JsonGenerator gen) throws IOException;
+ public abstract void writeTypeSuffixForScalar(Object value, JsonGenerator g) throws IOException;
/**
* Method called after value has been serialized, to close any scopes opened
@@ -117,7 +117,7 @@
* It needs to write closing END_OBJECT marker, and any other decoration
* that needs to be matched.
*/
- public abstract void writeTypeSuffixForObject(Object value, JsonGenerator gen) throws IOException;
+ public abstract void writeTypeSuffixForObject(Object value, JsonGenerator g) throws IOException;
/**
* Method called after value has been serialized, to close any scopes opened
@@ -125,15 +125,15 @@
* It needs to write closing END_ARRAY marker, and any other decoration
* that needs to be matched.
*/
- public abstract void writeTypeSuffixForArray(Object value, JsonGenerator gen) throws IOException;
+ public abstract void writeTypeSuffixForArray(Object value, JsonGenerator g) throws IOException;
/**
* Alternative version of the prefix-for-scalar method, which is given
* actual type to use (instead of using exact type of the value); typically
* a super type of actual value type
*/
- public void writeTypePrefixForScalar(Object value, JsonGenerator gen, Class<?> type) throws IOException {
- writeTypePrefixForScalar(value, gen);
+ public void writeTypePrefixForScalar(Object value, JsonGenerator g, Class<?> type) throws IOException {
+ writeTypePrefixForScalar(value, g);
}
/**
@@ -141,8 +141,8 @@
* actual type to use (instead of using exact type of the value); typically
* a super type of actual value type
*/
- public void writeTypePrefixForObject(Object value, JsonGenerator gen, Class<?> type) throws IOException {
- writeTypePrefixForObject(value, gen);
+ public void writeTypePrefixForObject(Object value, JsonGenerator g, Class<?> type) throws IOException {
+ writeTypePrefixForObject(value, g);
}
/**
@@ -150,8 +150,8 @@
* actual type to use (instead of using exact type of the value); typically
* a super type of actual value type
*/
- public void writeTypePrefixForArray(Object value, JsonGenerator gen, Class<?> type) throws IOException {
- writeTypePrefixForArray(value, gen);
+ public void writeTypePrefixForArray(Object value, JsonGenerator g, Class<?> type) throws IOException {
+ writeTypePrefixForArray(value, g);
}
/*
@@ -170,11 +170,11 @@
*
* @param value Value that will be serialized, for which type information is
* to be written
- * @param gen Generator to use for writing type information
+ * @param g Generator to use for writing type information
* @param typeId Exact type id to use
*/
- public abstract void writeCustomTypePrefixForScalar(Object value, JsonGenerator gen, String typeId) throws IOException;
-
+ public abstract void writeCustomTypePrefixForScalar(Object value, JsonGenerator g, String typeId) throws IOException;
+
/**
* Method called to write initial part of type information for given
* value, when it will be output as JSON Object value (not as JSON
@@ -185,16 +185,16 @@
*
* @param value Value that will be serialized, for which type information is
* to be written
- * @param gen Generator to use for writing type information
+ * @param g Generator to use for writing type information
* @param typeId Exact type id to use
*/
- public abstract void writeCustomTypePrefixForObject(Object value, JsonGenerator gen, String typeId) throws IOException;
+ public abstract void writeCustomTypePrefixForObject(Object value, JsonGenerator g, String typeId) throws IOException;
- public abstract void writeCustomTypePrefixForArray(Object value, JsonGenerator gen, String typeId) throws IOException;
+ public abstract void writeCustomTypePrefixForArray(Object value, JsonGenerator g, String typeId) throws IOException;
- public abstract void writeCustomTypeSuffixForScalar(Object value, JsonGenerator gen, String typeId) throws IOException;
+ public abstract void writeCustomTypeSuffixForScalar(Object value, JsonGenerator g, String typeId) throws IOException;
- public abstract void writeCustomTypeSuffixForObject(Object value, JsonGenerator gen, String typeId) throws IOException;
+ public abstract void writeCustomTypeSuffixForObject(Object value, JsonGenerator g, String typeId) throws IOException;
- public abstract void writeCustomTypeSuffixForArray(Object value, JsonGenerator gen, String typeId) throws IOException;
+ public abstract void writeCustomTypeSuffixForArray(Object value, JsonGenerator g, String typeId) throws IOException;
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeDeserializer.java
index f5fdb2b..4172c17 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeDeserializer.java
@@ -23,8 +23,11 @@
{
private static final long serialVersionUID = 1L;
+ /**
+ * @since 2.8
+ */
public AsArrayTypeDeserializer(JavaType bt, TypeIdResolver idRes,
- String typePropertyName, boolean typeIdVisible, Class<?> defaultImpl)
+ String typePropertyName, boolean typeIdVisible, JavaType defaultImpl)
{
super(bt, idRes, typePropertyName, typeIdVisible, defaultImpl);
}
@@ -104,39 +107,49 @@
tb.writeStartObject(); // recreate START_OBJECT
tb.writeFieldName(_typePropertyName);
tb.writeString(typeId);
- p = JsonParserSequence.createFlattened(tb.asParser(p), p);
+ // 02-Jul-2016, tatu: Depending on for JsonParserSequence is initialized it may
+ // try to access current token; ensure there isn't one
+ p.clearCurrentToken();
+ p = JsonParserSequence.createFlattened(false, tb.asParser(p), p);
p.nextToken();
}
Object value = deser.deserialize(p, ctxt);
// And then need the closing END_ARRAY
if (hadStartArray && p.nextToken() != JsonToken.END_ARRAY) {
- throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY,
+ ctxt.reportWrongTokenException(p, JsonToken.END_ARRAY,
"expected closing END_ARRAY after type information and deserialized value");
+ // 05-May-2016, tatu: Not 100% what to do if exception is stored for
+ // future, and not thrown immediately: should probably skip until END_ARRAY
+
+ // ... but for now, fall through
}
return value;
}
- protected String _locateTypeId(JsonParser jp, DeserializationContext ctxt) throws IOException
+ protected String _locateTypeId(JsonParser p, DeserializationContext ctxt) throws IOException
{
- if (!jp.isExpectedStartArrayToken()) {
+ if (!p.isExpectedStartArrayToken()) {
// Need to allow even more customized handling, if something unexpected seen...
// but should there be a way to limit this to likely success cases?
if (_defaultImpl != null) {
return _idResolver.idFromBaseType();
}
- throw ctxt.wrongTokenException(jp, JsonToken.START_ARRAY, "need JSON Array to contain As.WRAPPER_ARRAY type information for class "+baseTypeName());
+ ctxt.reportWrongTokenException(p, JsonToken.START_ARRAY,
+ "need JSON Array to contain As.WRAPPER_ARRAY type information for class "+baseTypeName());
+ return null;
}
// And then type id as a String
- JsonToken t = jp.nextToken();
+ JsonToken t = p.nextToken();
if (t == JsonToken.VALUE_STRING) {
- String result = jp.getText();
- jp.nextToken();
+ String result = p.getText();
+ p.nextToken();
return result;
}
if (_defaultImpl != null) {
return _idResolver.idFromBaseType();
}
- throw ctxt.wrongTokenException(jp, JsonToken.VALUE_STRING, "need JSON String that contains type id (for subtype of "+baseTypeName()+")");
+ ctxt.reportWrongTokenException(p, JsonToken.VALUE_STRING, "need JSON String that contains type id (for subtype of "+baseTypeName()+")");
+ return null;
}
/**
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeSerializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeSerializer.java
index 0291296..adde36e 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeSerializer.java
@@ -20,7 +20,7 @@
@Override
public AsArrayTypeSerializer forProperty(BeanProperty prop) {
- return (_property == prop) ? this : new AsArrayTypeSerializer(this._idResolver, prop);
+ return (_property == prop) ? this : new AsArrayTypeSerializer(_idResolver, prop);
}
@Override
@@ -33,88 +33,88 @@
*/
@Override
- public void writeTypePrefixForObject(Object value, JsonGenerator jgen) throws IOException {
+ public void writeTypePrefixForObject(Object value, JsonGenerator g) throws IOException {
final String typeId = idFromValue(value);
// NOTE: can not always avoid writing type id, even if null
- if (jgen.canWriteTypeId()) {
+ if (g.canWriteTypeId()) {
if (typeId != null) {
- jgen.writeTypeId(typeId);
+ g.writeTypeId(typeId);
}
} else {
- jgen.writeStartArray();
- jgen.writeString(typeId);
+ g.writeStartArray();
+ g.writeString(typeId);
}
- jgen.writeStartObject();
+ g.writeStartObject();
}
@Override
- public void writeTypePrefixForObject(Object value, JsonGenerator jgen, Class<?> type) throws IOException {
+ public void writeTypePrefixForObject(Object value, JsonGenerator g, Class<?> type) throws IOException {
final String typeId = idFromValueAndType(value, type);
// NOTE: can not always avoid writing type id, even if null
- if (jgen.canWriteTypeId()) {
+ if (g.canWriteTypeId()) {
if (typeId != null) {
- jgen.writeTypeId(typeId);
+ g.writeTypeId(typeId);
}
} else {
- jgen.writeStartArray();
- jgen.writeString(typeId);
+ g.writeStartArray();
+ g.writeString(typeId);
}
- jgen.writeStartObject();
+ g.writeStartObject();
}
@Override
- public void writeTypePrefixForArray(Object value, JsonGenerator jgen) throws IOException {
+ public void writeTypePrefixForArray(Object value, JsonGenerator g) throws IOException {
final String typeId = idFromValue(value);
- if (jgen.canWriteTypeId()) {
+ if (g.canWriteTypeId()) {
if (typeId != null) {
- jgen.writeTypeId(typeId);
+ g.writeTypeId(typeId);
}
} else {
- jgen.writeStartArray();
- jgen.writeString(typeId);
+ g.writeStartArray();
+ g.writeString(typeId);
}
- jgen.writeStartArray();
+ g.writeStartArray();
}
@Override
- public void writeTypePrefixForArray(Object value, JsonGenerator jgen, Class<?> type) throws IOException {
+ public void writeTypePrefixForArray(Object value, JsonGenerator g, Class<?> type) throws IOException {
final String typeId = idFromValueAndType(value, type);
- if (jgen.canWriteTypeId()) {
+ if (g.canWriteTypeId()) {
if (typeId != null) {
- jgen.writeTypeId(typeId);
+ g.writeTypeId(typeId);
}
} else {
- jgen.writeStartArray();
- jgen.writeString(typeId);
+ g.writeStartArray();
+ g.writeString(typeId);
}
- jgen.writeStartArray();
+ g.writeStartArray();
}
@Override
- public void writeTypePrefixForScalar(Object value, JsonGenerator jgen) throws IOException {
+ public void writeTypePrefixForScalar(Object value, JsonGenerator g) throws IOException {
final String typeId = idFromValue(value);
- if (jgen.canWriteTypeId()) {
+ if (g.canWriteTypeId()) {
if (typeId != null) {
- jgen.writeTypeId(typeId);
+ g.writeTypeId(typeId);
}
} else {
// only need the wrapper array
- jgen.writeStartArray();
- jgen.writeString(typeId);
+ g.writeStartArray();
+ g.writeString(typeId);
}
}
@Override
- public void writeTypePrefixForScalar(Object value, JsonGenerator jgen, Class<?> type) throws IOException {
+ public void writeTypePrefixForScalar(Object value, JsonGenerator g, Class<?> type) throws IOException {
final String typeId = idFromValueAndType(value, type);
- if (jgen.canWriteTypeId()) {
+ if (g.canWriteTypeId()) {
if (typeId != null) {
- jgen.writeTypeId(typeId);
+ g.writeTypeId(typeId);
}
} else {
// only need the wrapper array
- jgen.writeStartArray();
- jgen.writeString(typeId);
+ g.writeStartArray();
+ g.writeString(typeId);
}
}
@@ -125,27 +125,27 @@
*/
@Override
- public void writeTypeSuffixForObject(Object value, JsonGenerator jgen) throws IOException {
- jgen.writeEndObject();
- if (!jgen.canWriteTypeId()) {
- jgen.writeEndArray();
+ public void writeTypeSuffixForObject(Object value, JsonGenerator g) throws IOException {
+ g.writeEndObject();
+ if (!g.canWriteTypeId()) {
+ g.writeEndArray();
}
}
@Override
- public void writeTypeSuffixForArray(Object value, JsonGenerator jgen) throws IOException {
+ public void writeTypeSuffixForArray(Object value, JsonGenerator g) throws IOException {
// first array caller needs to close, then wrapper array
- jgen.writeEndArray();
- if (!jgen.canWriteTypeId()) {
- jgen.writeEndArray();
+ g.writeEndArray();
+ if (!g.canWriteTypeId()) {
+ g.writeEndArray();
}
}
@Override
- public void writeTypeSuffixForScalar(Object value, JsonGenerator jgen) throws IOException {
- if (!jgen.canWriteTypeId()) {
+ public void writeTypeSuffixForScalar(Object value, JsonGenerator g) throws IOException {
+ if (!g.canWriteTypeId()) {
// just the wrapper array to close
- jgen.writeEndArray();
+ g.writeEndArray();
}
}
@@ -156,61 +156,61 @@
*/
@Override
- public void writeCustomTypePrefixForObject(Object value, JsonGenerator jgen, String typeId) throws IOException {
- if (jgen.canWriteTypeId()) {
+ public void writeCustomTypePrefixForObject(Object value, JsonGenerator g, String typeId) throws IOException {
+ if (g.canWriteTypeId()) {
if (typeId != null) {
- jgen.writeTypeId(typeId);
+ g.writeTypeId(typeId);
}
} else {
- jgen.writeStartArray();
- jgen.writeString(typeId);
+ g.writeStartArray();
+ g.writeString(typeId);
}
- jgen.writeStartObject();
+ g.writeStartObject();
}
@Override
- public void writeCustomTypePrefixForArray(Object value, JsonGenerator jgen, String typeId) throws IOException {
- if (jgen.canWriteTypeId()) {
+ public void writeCustomTypePrefixForArray(Object value, JsonGenerator g, String typeId) throws IOException {
+ if (g.canWriteTypeId()) {
if (typeId != null) {
- jgen.writeTypeId(typeId);
+ g.writeTypeId(typeId);
}
} else {
- jgen.writeStartArray();
- jgen.writeString(typeId);
+ g.writeStartArray();
+ g.writeString(typeId);
}
- jgen.writeStartArray();
+ g.writeStartArray();
}
@Override
- public void writeCustomTypePrefixForScalar(Object value, JsonGenerator jgen, String typeId) throws IOException {
- if (jgen.canWriteTypeId()) {
+ public void writeCustomTypePrefixForScalar(Object value, JsonGenerator g, String typeId) throws IOException {
+ if (g.canWriteTypeId()) {
if (typeId != null) {
- jgen.writeTypeId(typeId);
+ g.writeTypeId(typeId);
}
} else {
- jgen.writeStartArray();
- jgen.writeString(typeId);
+ g.writeStartArray();
+ g.writeString(typeId);
}
}
@Override
- public void writeCustomTypeSuffixForObject(Object value, JsonGenerator jgen, String typeId) throws IOException {
- if (!jgen.canWriteTypeId()) {
- writeTypeSuffixForObject(value, jgen); // standard impl works fine
+ public void writeCustomTypeSuffixForObject(Object value, JsonGenerator g, String typeId) throws IOException {
+ if (!g.canWriteTypeId()) {
+ writeTypeSuffixForObject(value, g); // standard impl works fine
}
}
@Override
- public void writeCustomTypeSuffixForArray(Object value, JsonGenerator jgen, String typeId) throws IOException {
- if (!jgen.canWriteTypeId()) {
- writeTypeSuffixForArray(value, jgen); // standard impl works fine
+ public void writeCustomTypeSuffixForArray(Object value, JsonGenerator g, String typeId) throws IOException {
+ if (!g.canWriteTypeId()) {
+ writeTypeSuffixForArray(value, g); // standard impl works fine
}
}
@Override
- public void writeCustomTypeSuffixForScalar(Object value, JsonGenerator jgen, String typeId) throws IOException {
- if (!jgen.canWriteTypeId()) {
- writeTypeSuffixForScalar(value, jgen); // standard impl works fine
+ public void writeCustomTypeSuffixForScalar(Object value, JsonGenerator g, String typeId) throws IOException {
+ if (!g.canWriteTypeId()) {
+ writeTypeSuffixForScalar(value, g); // standard impl works fine
}
}
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsExistingPropertyTypeSerializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsExistingPropertyTypeSerializer.java
index 1fc5983..fd10302 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsExistingPropertyTypeSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsExistingPropertyTypeSerializer.java
@@ -11,52 +11,52 @@
/**
* Type serializer used with {@link As#EXISTING_PROPERTY} inclusion mechanism.
* Expects type information to be a well-defined property on all sub-classes.
- *
- * @author fleeman (modeled after code by tatus)
*/
public class AsExistingPropertyTypeSerializer
extends AsPropertyTypeSerializer
{
- public AsExistingPropertyTypeSerializer(TypeIdResolver idRes, BeanProperty property, String propName)
+ public AsExistingPropertyTypeSerializer(TypeIdResolver idRes,
+ BeanProperty property, String propName)
{
super(idRes, property, propName);
}
@Override
public AsExistingPropertyTypeSerializer forProperty(BeanProperty prop) {
- return (_property == prop) ? this : new AsExistingPropertyTypeSerializer(this._idResolver, prop, this._typePropertyName);
+ return (_property == prop) ? this :
+ new AsExistingPropertyTypeSerializer(_idResolver, prop, _typePropertyName);
}
@Override
public As getTypeInclusion() { return As.EXISTING_PROPERTY; }
@Override
- public void writeTypePrefixForObject(Object value, JsonGenerator jgen) throws IOException
+ public void writeTypePrefixForObject(Object value, JsonGenerator gen) throws IOException
{
final String typeId = idFromValue(value);
- if ((typeId != null) && jgen.canWriteTypeId()) {
- jgen.writeTypeId(typeId);
+ if ((typeId != null) && gen.canWriteTypeId()) {
+ gen.writeTypeId(typeId);
}
- jgen.writeStartObject();
+ gen.writeStartObject();
}
@Override
- public void writeTypePrefixForObject(Object value, JsonGenerator jgen, Class<?> type) throws IOException
+ public void writeTypePrefixForObject(Object value, JsonGenerator gen, Class<?> type) throws IOException
{
final String typeId = idFromValueAndType(value, type);
- if ((typeId != null) && jgen.canWriteTypeId()) {
- jgen.writeTypeId(typeId);
+ if ((typeId != null) && gen.canWriteTypeId()) {
+ gen.writeTypeId(typeId);
}
- jgen.writeStartObject();
+ gen.writeStartObject();
}
@Override
- public void writeCustomTypePrefixForObject(Object value, JsonGenerator jgen, String typeId) throws IOException
+ public void writeCustomTypePrefixForObject(Object value, JsonGenerator gen, String typeId) throws IOException
{
- if ((typeId != null) && jgen.canWriteTypeId()) {
- jgen.writeTypeId(typeId);
+ if ((typeId != null) && gen.canWriteTypeId()) {
+ gen.writeTypeId(typeId);
}
- jgen.writeStartObject();
+ gen.writeStartObject();
}
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsExternalTypeDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsExternalTypeDeserializer.java
index 3bd4539..c6b8d49 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsExternalTypeDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsExternalTypeDeserializer.java
@@ -19,13 +19,17 @@
{
private static final long serialVersionUID = 1L;
+ /**
+ * @since 2.8
+ */
public AsExternalTypeDeserializer(JavaType bt, TypeIdResolver idRes,
- String typePropertyName, boolean typeIdVisible, Class<?> defaultImpl)
+ String typePropertyName, boolean typeIdVisible, JavaType defaultImpl)
{
super(bt, idRes, typePropertyName, typeIdVisible, defaultImpl);
}
- public AsExternalTypeDeserializer(AsExternalTypeDeserializer src, BeanProperty property) {
+ public AsExternalTypeDeserializer(AsExternalTypeDeserializer src,
+ BeanProperty property) {
super(src, property);
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsPropertyTypeDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsPropertyTypeDeserializer.java
index 101d842..d1d096a 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsPropertyTypeDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsPropertyTypeDeserializer.java
@@ -24,14 +24,20 @@
protected final As _inclusion;
+ /**
+ * @since 2.8
+ */
public AsPropertyTypeDeserializer(JavaType bt, TypeIdResolver idRes,
- String typePropertyName, boolean typeIdVisible, Class<?> defaultImpl)
+ String typePropertyName, boolean typeIdVisible, JavaType defaultImpl)
{
this(bt, idRes, typePropertyName, typeIdVisible, defaultImpl, As.PROPERTY);
}
+ /**
+ * @since 2.8
+ */
public AsPropertyTypeDeserializer(JavaType bt, TypeIdResolver idRes,
- String typePropertyName, boolean typeIdVisible, Class<?> defaultImpl,
+ String typePropertyName, boolean typeIdVisible, JavaType defaultImpl,
As inclusion)
{
super(bt, idRes, typePropertyName, typeIdVisible, defaultImpl);
@@ -112,7 +118,10 @@
tb.writeString(typeId);
}
if (tb != null) { // need to put back skipped properties?
- p = JsonParserSequence.createFlattened(tb.asParser(p), p);
+ // 02-Jul-2016, tatu: Depending on for JsonParserSequence is initialized it may
+ // try to access current token; ensure there isn't one
+ p.clearCurrentToken();
+ p = JsonParserSequence.createFlattened(false, tb.asParser(p), p);
}
// Must point to the next value; tb had no current, jp pointed to VALUE_STRING:
p.nextToken(); // to skip past String value
@@ -122,7 +131,8 @@
// off-lined to keep main method lean and mean...
@SuppressWarnings("resource")
- protected Object _deserializeTypedUsingDefaultImpl(JsonParser p, DeserializationContext ctxt, TokenBuffer tb) throws IOException
+ protected Object _deserializeTypedUsingDefaultImpl(JsonParser p, DeserializationContext ctxt,
+ TokenBuffer tb) throws IOException
{
// As per [JACKSON-614], may have default implementation to use
JsonDeserializer<Object> deser = _findDefaultImplDeserializer(ctxt);
@@ -144,8 +154,9 @@
if (p.getCurrentToken() == JsonToken.START_ARRAY) {
return super.deserializeTypedFromAny(p, ctxt);
}
- throw ctxt.wrongTokenException(p, JsonToken.FIELD_NAME,
+ ctxt.reportWrongTokenException(p, JsonToken.FIELD_NAME,
"missing property '"+_typePropertyName+"' that is to contain type id (for class "+baseTypeName()+")");
+ return null;
}
/* Also need to re-route "unknown" version. Need to think
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeDeserializer.java
index 8904b73..02f26ca 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeDeserializer.java
@@ -23,8 +23,11 @@
{
private static final long serialVersionUID = 1L;
+ /**
+ * @since 2.8
+ */
public AsWrapperTypeDeserializer(JavaType bt, TypeIdResolver idRes,
- String typePropertyName, boolean typeIdVisible, Class<?> defaultImpl)
+ String typePropertyName, boolean typeIdVisible, JavaType defaultImpl)
{
super(bt, idRes, typePropertyName, typeIdVisible, defaultImpl);
}
@@ -90,11 +93,11 @@
if (t == JsonToken.START_OBJECT) {
// should always get field name, but just in case...
if (p.nextToken() != JsonToken.FIELD_NAME) {
- throw ctxt.wrongTokenException(p, JsonToken.FIELD_NAME,
+ ctxt.reportWrongTokenException(p, JsonToken.FIELD_NAME,
"need JSON String that contains type id (for subtype of "+baseTypeName()+")");
}
} else if (t != JsonToken.FIELD_NAME) {
- throw ctxt.wrongTokenException(p, JsonToken.START_OBJECT,
+ ctxt.reportWrongTokenException(p, JsonToken.START_OBJECT,
"need JSON Object to contain As.WRAPPER_OBJECT type information for class "+baseTypeName());
}
final String typeId = p.getText();
@@ -108,14 +111,17 @@
tb.writeStartObject(); // recreate START_OBJECT
tb.writeFieldName(_typePropertyName);
tb.writeString(typeId);
- p = JsonParserSequence.createFlattened(tb.asParser(p), p);
+ // 02-Jul-2016, tatu: Depending on for JsonParserSequence is initialized it may
+ // try to access current token; ensure there isn't one
+ p.clearCurrentToken();
+ p = JsonParserSequence.createFlattened(false, tb.asParser(p), p);
p.nextToken();
}
Object value = deser.deserialize(p, ctxt);
// And then need the closing END_OBJECT
if (p.nextToken() != JsonToken.END_OBJECT) {
- throw ctxt.wrongTokenException(p, JsonToken.END_OBJECT,
+ ctxt.reportWrongTokenException(p, JsonToken.END_OBJECT,
"expected closing END_OBJECT after type information and deserialized value");
}
return value;
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeSerializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeSerializer.java
index 647d031..feab592 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeSerializer.java
@@ -24,7 +24,7 @@
@Override
public AsWrapperTypeSerializer forProperty(BeanProperty prop) {
- return (_property == prop) ? this : new AsWrapperTypeSerializer(this._idResolver, prop);
+ return (_property == prop) ? this : new AsWrapperTypeSerializer(_idResolver, prop);
}
@Override
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/ClassNameIdResolver.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/ClassNameIdResolver.java
index 0dd48d2..fd7b097 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/ClassNameIdResolver.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/ClassNameIdResolver.java
@@ -1,13 +1,10 @@
package com.fasterxml.jackson.databind.jsontype.impl;
+import java.io.IOException;
import java.util.*;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
-import com.fasterxml.jackson.databind.DatabindContext;
-import com.fasterxml.jackson.databind.DeserializationConfig;
-import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.databind.JavaType;
-import com.fasterxml.jackson.databind.cfg.MapperConfig;
+import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.databind.util.ClassUtil;
@@ -32,51 +29,43 @@
@Override
public String idFromValue(Object value) {
- return _idFrom(value, value.getClass());
+ return _idFrom(value, value.getClass(), _typeFactory);
}
@Override
public String idFromValueAndType(Object value, Class<?> type) {
- return _idFrom(value, type);
- }
-
- @Deprecated // since 2.3
- @Override
- public JavaType typeFromId(String id) {
- return _typeFromId(id, null);
+ return _idFrom(value, type, _typeFactory);
}
@Override
- public JavaType typeFromId(DatabindContext context, String id) {
+ public JavaType typeFromId(DatabindContext context, String id) throws IOException {
return _typeFromId(id, context);
}
- protected JavaType _typeFromId(String id, DatabindContext ctxt)
+ protected JavaType _typeFromId(String id, DatabindContext ctxt) throws IOException
{
/* 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
*/
- TypeFactory tf = (ctxt == null) ? _typeFactory : ctxt.getTypeFactory();
+ TypeFactory tf = ctxt.getTypeFactory();
if (id.indexOf('<') > 0) {
- JavaType t = tf.constructFromCanonical(id);
// note: may want to try combining with specialization (esp for EnumMap)?
- return t;
+ return tf.constructFromCanonical(id);
}
Class<?> cls;
try {
cls = tf.findClass(id);
} catch (ClassNotFoundException e) {
- // 24-May-2016, tatu: Ok, this is supremely ugly, from multiple persepctives.
- // But fixes [databind#1098], so has to do for now.
- MapperConfig<?> cfg = ctxt.getConfig();
- if (cfg instanceof DeserializationConfig) {
- DeserializationConfig dc = (DeserializationConfig) cfg;
- if (!dc.isEnabled(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE)) {
- return null;
- }
+ // 24-May-2016, tatu: Ok, this is pretty ugly, but we should always get
+ // DeserializationContext, just playing it safe
+ if (ctxt instanceof DeserializationContext) {
+ DeserializationContext dctxt = (DeserializationContext) ctxt;
+ // First: we may have problem handlers that can deal with it?
+ return dctxt.handleUnknownTypeId(_baseType, id, this, "no such class found");
}
- throw new IllegalArgumentException("Invalid type id '"+id+"' (for id type 'Id.class'): no such class found");
+ // ... meaning that we really should never get here.
+ return null;
} catch (Exception e) {
throw new IllegalArgumentException("Invalid type id '"+id+"' (for id type 'Id.class'): "+e.getMessage(), e);
}
@@ -89,7 +78,7 @@
/**********************************************************
*/
- protected final String _idFrom(Object value, Class<?> cls)
+ protected final String _idFrom(Object value, Class<?> cls, TypeFactory typeFactory)
{
// Need to ensure that "enum subtypes" work too
if (Enum.class.isAssignableFrom(cls)) {
@@ -108,12 +97,12 @@
if (value instanceof EnumSet<?>) { // Regular- and JumboEnumSet...
Class<?> enumClass = ClassUtil.findEnumType((EnumSet<?>) value);
// not optimal: but EnumSet is not a customizable type so this is sort of ok
- str = _typeFactory.constructCollectionType(EnumSet.class, enumClass).toCanonical();
+ str = typeFactory.constructCollectionType(EnumSet.class, enumClass).toCanonical();
} else if (value instanceof EnumMap<?,?>) {
Class<?> enumClass = ClassUtil.findEnumType((EnumMap<?,?>) value);
Class<?> valueClass = Object.class;
// not optimal: but EnumMap is not a customizable type so this is sort of ok
- str = _typeFactory.constructMapType(EnumMap.class, enumClass, valueClass).toCanonical();
+ str = typeFactory.constructMapType(EnumMap.class, enumClass, valueClass).toCanonical();
} else {
String end = str.substring(9);
if ((end.startsWith(".Arrays$") || end.startsWith(".Collections$"))
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/MinimalClassNameIdResolver.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/MinimalClassNameIdResolver.java
index 5ff070f..d208c30 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/MinimalClassNameIdResolver.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/MinimalClassNameIdResolver.java
@@ -1,5 +1,7 @@
package com.fasterxml.jackson.databind.jsontype.impl;
+import java.io.IOException;
+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.DatabindContext;
import com.fasterxml.jackson.databind.JavaType;
@@ -49,7 +51,7 @@
}
@Override
- protected JavaType _typeFromId(String id, DatabindContext ctxt)
+ protected JavaType _typeFromId(String id, DatabindContext ctxt) throws IOException
{
if (id.startsWith(".")) {
StringBuilder sb = new StringBuilder(id.length() + _basePackageName.length());
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/StdTypeResolverBuilder.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/StdTypeResolverBuilder.java
index 1372a67..9ca0c7e 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/StdTypeResolverBuilder.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/StdTypeResolverBuilder.java
@@ -3,13 +3,11 @@
import java.util.Collection;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
+
import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.NoClass;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
-import com.fasterxml.jackson.databind.jsontype.NamedType;
-import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
-import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
-import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
-import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.jsontype.*;
/**
* Default {@link TypeResolverBuilder} implementation.
@@ -101,22 +99,42 @@
if (_idType == JsonTypeInfo.Id.NONE) { return null; }
TypeIdResolver idRes = idResolver(config, baseType, subtypes, false, true);
-
+
+ JavaType defaultImpl;
+
+ if (_defaultImpl == null) {
+ defaultImpl = null;
+ } else {
+ // 20-Mar-2016, tatu: It is important to do specialization go through
+ // TypeFactory to ensure proper resolution; with 2.7 and before, direct
+ // call to JavaType was used, but that can not work reliably with 2.7
+ // 20-Mar-2016, tatu: Can finally add a check for type compatibility BUT
+ // if so, need to add explicit checks for marker types. Not ideal, but
+ // seems like a reasonable compromise.
+ if ((_defaultImpl == Void.class)
+ || (_defaultImpl == NoClass.class)) {
+ defaultImpl = config.getTypeFactory().constructType(_defaultImpl);
+ } else {
+ defaultImpl = config.getTypeFactory()
+ .constructSpecializedType(baseType, _defaultImpl);
+ }
+ }
+
// First, method for converting type info to type id:
switch (_includeAs) {
case WRAPPER_ARRAY:
return new AsArrayTypeDeserializer(baseType, idRes,
- _typeProperty, _typeIdVisible, _defaultImpl);
+ _typeProperty, _typeIdVisible, defaultImpl);
case PROPERTY:
case EXISTING_PROPERTY: // as per [#528] same class as PROPERTY
return new AsPropertyTypeDeserializer(baseType, idRes,
- _typeProperty, _typeIdVisible, _defaultImpl, _includeAs);
+ _typeProperty, _typeIdVisible, defaultImpl, _includeAs);
case WRAPPER_OBJECT:
return new AsWrapperTypeDeserializer(baseType, idRes,
- _typeProperty, _typeIdVisible, _defaultImpl);
+ _typeProperty, _typeIdVisible, defaultImpl);
case EXTERNAL_PROPERTY:
return new AsExternalTypeDeserializer(baseType, idRes,
- _typeProperty, _typeIdVisible, _defaultImpl);
+ _typeProperty, _typeIdVisible, defaultImpl);
}
throw new IllegalStateException("Do not know how to construct standard type serializer for inclusion type: "+_includeAs);
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeDeserializerBase.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeDeserializerBase.java
index 9622759..7284b47 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeDeserializerBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeDeserializerBase.java
@@ -66,8 +66,11 @@
/**********************************************************
*/
+ /**
+ * @since 2.8
+ */
protected TypeDeserializerBase(JavaType baseType, TypeIdResolver idRes,
- String typePropertyName, boolean typeIdVisible, Class<?> defaultImpl)
+ String typePropertyName, boolean typeIdVisible, JavaType defaultImpl)
{
_baseType = baseType;
_idResolver = idRes;
@@ -76,18 +79,7 @@
_typeIdVisible = typeIdVisible;
// defaults are fine, although shouldn't need much concurrency
_deserializers = new ConcurrentHashMap<String, JsonDeserializer<Object>>(16, 0.75f, 2);
- if (defaultImpl == null) {
- _defaultImpl = null;
- } else {
- /* 16-Oct-2011, tatu: should call this via TypeFactory; this is
- * not entirely safe... however, since Collections/Maps are
- * seldom (if ever) base types, may be ok.
- */
- // 01-Nov-2015, tatu: Actually this is still exactly wrong. Should fix.
- // 15-Jan-2016, tatu: ... as witnessed by [databind#1083], patched, but
- // fundamentally this call can't be made to work for all cases
- _defaultImpl = baseType.forcedNarrowBy(defaultImpl);
- }
+ _defaultImpl = defaultImpl;
_property = null;
}
@@ -160,7 +152,14 @@
// As per [JACKSON-614], use the default impl if no type id available:
deser = _findDefaultImplDeserializer(ctxt);
if (deser == null) {
- deser = _handleUnknownTypeId(ctxt, typeId, _idResolver, _baseType);
+ // 10-May-2016, tatu: We may get some help...
+ JavaType actual = _handleUnknownTypeId(ctxt, typeId, _idResolver, _baseType);
+ if (actual == null) { // what should this be taken to mean?
+ // TODO: try to figure out something better
+ return null;
+ }
+ // ... would this actually work?
+ deser = ctxt.findContextualValueDeserializer(actual, _property);
}
} else {
/* 16-Dec-2010, tatu: Since nominal type we get here has no (generic) type parameters,
@@ -178,7 +177,13 @@
* important for some reason?
* Disabling the check will break 2 Enum-related tests.
*/
- type = ctxt.getTypeFactory().constructSpecializedType(_baseType, type.getRawClass());
+ // 19-Jun-2016, tatu: As per [databind#1270] we may actually get full
+ // generic type with custom type resolvers. If so, should try to retain them.
+ // Whether this is sufficient to avoid problems remains to be seen, but for
+ // now it should improve things.
+ if (!type.hasGenericTypes()) {
+ type = ctxt.getTypeFactory().constructSpecializedType(_baseType, type.getRawClass());
+ }
}
deser = ctxt.findContextualValueDeserializer(type, _property);
}
@@ -241,7 +246,8 @@
*/
deser = _findDefaultImplDeserializer(ctxt);
if (deser == null) {
- throw ctxt.mappingException("No (native) type id found when one was expected for polymorphic type handling");
+ ctxt.reportMappingException("No (native) type id found when one was expected for polymorphic type handling");
+ return null;
}
} else {
String typeIdStr = (typeId instanceof String) ? (String) typeId : String.valueOf(typeId);
@@ -261,23 +267,18 @@
* should return that deserializer; otherwise throw an exception to indicate
* the problem.
*
- * @since 2.5
+ * @since 2.8
*/
- protected JsonDeserializer<Object> _handleUnknownTypeId(DeserializationContext ctxt, String typeId,
+ protected JavaType _handleUnknownTypeId(DeserializationContext ctxt, String typeId,
TypeIdResolver idResolver, JavaType baseType)
throws IOException
{
- String extraDesc;
- if (idResolver instanceof TypeIdResolverBase) {
- extraDesc = ((TypeIdResolverBase) idResolver).getDescForKnownTypeIds();
- if (extraDesc == null) {
- extraDesc = "known type ids are not statically known";
- } else {
- extraDesc = "known type ids = " + extraDesc;
- }
+ String extraDesc = idResolver.getDescForKnownTypeIds();
+ if (extraDesc == null) {
+ extraDesc = "known type ids are not statically known";
} else {
- extraDesc = null;
+ extraDesc = "known type ids = " + extraDesc;
}
- throw ctxt.unknownTypeException(_baseType, typeId, extraDesc);
+ return ctxt.handleUnknownTypeId(_baseType, typeId, idResolver, extraDesc);
}
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeIdResolverBase.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeIdResolverBase.java
index 8532543..deeec6f 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeIdResolverBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeIdResolverBase.java
@@ -1,5 +1,7 @@
package com.fasterxml.jackson.databind.jsontype.impl;
+import java.io.IOException;
+
import com.fasterxml.jackson.databind.DatabindContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
@@ -51,27 +53,8 @@
return idFromValueAndType(null, _baseType.getRawClass());
}
- /**
- * @deprecated Since 2.3, override {@link #typeFromId(DatabindContext, String)} instead
- * to get access to contextual information
- */
- @Deprecated
@Override
- public JavaType typeFromId(String id) {
- return typeFromId(null, id);
- }
-
- /**
- * New method, replacement for {@link #typeFromId(String)}, which is given
- * context for accessing information, including configuration and
- * {@link TypeFactory}.
- *
- * @return Type for given id
- *
- * @since 2.3
- */
- @Override
- public JavaType typeFromId(DatabindContext context, String id) {
+ public JavaType typeFromId(DatabindContext context, String id) throws IOException {
// 22-Dec-2015, tatu: Must be overridden by sub-classes, so let's throw
// an exception if not
throw new IllegalStateException("Sub-class "+getClass().getName()+" MUST implement "
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeNameIdResolver.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeNameIdResolver.java
index 9025daf..d607fbf 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeNameIdResolver.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeNameIdResolver.java
@@ -12,7 +12,7 @@
public class TypeNameIdResolver extends TypeIdResolverBase
{
protected final MapperConfig<?> _config;
-
+
/**
* Mappings from class name to type id, used for serialization
*/
@@ -22,7 +22,7 @@
* Mappings from type id to JavaType, used for deserialization
*/
protected final Map<String, JavaType> _idToType;
-
+
protected TypeNameIdResolver(MapperConfig<?> config, JavaType baseType,
Map<String, String> typeToId, Map<String, JavaType> idToType)
{
@@ -79,8 +79,7 @@
public JsonTypeInfo.Id getMechanism() { return JsonTypeInfo.Id.NAME; }
@Override
- public String idFromValue(Object value)
- {
+ public String idFromValue(Object value) {
return idFromClass(value.getClass());
}
@@ -123,12 +122,6 @@
return idFromValue(value);
}
- @Deprecated
- @Override
- public JavaType typeFromId(String id) {
- return _typeFromId(id);
- }
-
@Override
public JavaType typeFromId(DatabindContext context, String id) {
return _typeFromId(id);
@@ -150,13 +143,9 @@
@Override
public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append('[').append(getClass().getName());
- sb.append("; id-to-type=").append(_idToType);
- sb.append(']');
- return sb.toString();
+ return String.format("[%s; id-to-type=%s]", getClass().getName(), _idToType);
}
-
+
/*
/*********************************************************
/* Helper methods
diff --git a/src/main/java/com/fasterxml/jackson/databind/module/SimpleAbstractTypeResolver.java b/src/main/java/com/fasterxml/jackson/databind/module/SimpleAbstractTypeResolver.java
index dbd1bf4..3b0c1fd 100644
--- a/src/main/java/com/fasterxml/jackson/databind/module/SimpleAbstractTypeResolver.java
+++ b/src/main/java/com/fasterxml/jackson/databind/module/SimpleAbstractTypeResolver.java
@@ -81,6 +81,7 @@
}
@Override
+ @Deprecated
public JavaType resolveAbstractType(DeserializationConfig config, JavaType type){
// never materialize anything, so:
return null;
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/ArrayNode.java b/src/main/java/com/fasterxml/jackson/databind/node/ArrayNode.java
index be2e037..345834a 100644
--- a/src/main/java/com/fasterxml/jackson/databind/node/ArrayNode.java
+++ b/src/main/java/com/fasterxml/jackson/databind/node/ArrayNode.java
@@ -31,6 +31,14 @@
}
/**
+ * @since 2.8
+ */
+ public ArrayNode(JsonNodeFactory nf, int capacity) {
+ super(nf);
+ _children = new ArrayList<JsonNode>(capacity);
+ }
+
+ /**
* @since 2.7
*/
public ArrayNode(JsonNodeFactory nf, List<JsonNode> children) {
@@ -66,7 +74,7 @@
public boolean isEmpty(SerializerProvider serializers) {
return _children.isEmpty();
}
-
+
/*
/**********************************************************
/* Implementation of core JsonNode API
@@ -167,13 +175,13 @@
}
typeSer.writeTypeSuffixForArray(this, jg);
}
-
+
/*
/**********************************************************
/* Public API, finding value nodes
/**********************************************************
*/
-
+
@Override
public JsonNode findValue(String fieldName)
{
@@ -185,7 +193,7 @@
}
return null;
}
-
+
@Override
public List<JsonNode> findValues(String fieldName, List<JsonNode> foundSoFar)
{
@@ -203,7 +211,7 @@
}
return foundSoFar;
}
-
+
@Override
public ObjectNode findParent(String fieldName)
{
@@ -224,7 +232,7 @@
}
return foundSoFar;
}
-
+
/*
/**********************************************************
/* Extended ObjectNode API, accessors
@@ -255,7 +263,7 @@
/**
* Method for adding specified node at the end of this array.
- *
+ *
* @return This node, to allow chaining
*/
public ArrayNode add(JsonNode value)
@@ -270,9 +278,9 @@
/**
* Method for adding all child nodes of given Array, appending to
* child nodes this array contains
- *
+ *
* @param other Array to add contents from
- *
+ *
* @return This node (to allow chaining)
*/
public ArrayNode addAll(ArrayNode other)
@@ -283,9 +291,9 @@
/**
* Method for adding given nodes as child nodes of this array node.
- *
+ *
* @param nodes Nodes to add
- *
+ *
* @return This node (to allow chaining)
*/
public ArrayNode addAll(Collection<? extends JsonNode> nodes)
@@ -293,14 +301,14 @@
_children.addAll(nodes);
return this;
}
-
+
/**
* Method for inserting specified child node as an element
* of this Array. If index is 0 or less, it will be inserted as
* the first element; if >= size(), appended at the end, and otherwise
* inserted before existing element in specified index.
* No exceptions are thrown for any index.
- *
+ *
* @return This node (to allow chaining)
*/
public ArrayNode insert(int index, JsonNode value)
@@ -316,7 +324,7 @@
* Method for removing an entry from this ArrayNode.
* Will return value of the entry at specified index, if entry existed;
* null if not.
- *
+ *
* @return Node removed, if any; null if none
*/
public JsonNode remove(int index)
@@ -330,7 +338,7 @@
/**
* Method for removing all elements of this array, leaving the
* array empty.
- *
+ *
* @return This node (to allow chaining)
*/
@Override
@@ -339,7 +347,7 @@
_children.clear();
return this;
}
-
+
/*
/**********************************************************
/* Extended ObjectNode API, mutators, generic; addXxx()/insertXxx()
@@ -375,7 +383,7 @@
/**
* Method that will construct a POJONode and add it at the end
* of this array node.
- *
+ *
* @return This array node, to allow chaining
*/
public ArrayNode addPOJO(Object value)
@@ -390,7 +398,7 @@
/**
* @return This array node, to allow chaining
- *
+ *
* @since 2.6
*/
public ArrayNode addRawValue(RawValue raw) {
@@ -401,10 +409,10 @@
}
return this;
}
-
+
/**
* Method that will add a null value at the end of this array node.
- *
+ *
* @return This array node, to allow chaining
*/
public ArrayNode addNull()
@@ -415,7 +423,7 @@
/**
* Method for adding specified number at the end of this array.
- *
+ *
* @return This array node, to allow chaining
*/
public ArrayNode add(int v) {
@@ -426,7 +434,7 @@
/**
* Alternative method that we need to avoid bumping into NPE issues
* with auto-unboxing.
- *
+ *
* @return This array node, to allow chaining
*/
public ArrayNode add(Integer value) {
@@ -435,10 +443,10 @@
}
return _add(numberNode(value.intValue()));
}
-
+
/**
* Method for adding specified number at the end of this array.
- *
+ *
* @return This array node, to allow chaining
*/
public ArrayNode add(long v) { return _add(numberNode(v)); }
@@ -446,7 +454,7 @@
/**
* Alternative method that we need to avoid bumping into NPE issues
* with auto-unboxing.
- *
+ *
* @return This array node, to allow chaining
*/
public ArrayNode add(Long value) {
@@ -455,10 +463,10 @@
}
return _add(numberNode(value.longValue()));
}
-
+
/**
* Method for adding specified number at the end of this array.
- *
+ *
* @return This array node, to allow chaining
*/
public ArrayNode add(float v) {
@@ -468,7 +476,7 @@
/**
* Alternative method that we need to avoid bumping into NPE issues
* with auto-unboxing.
- *
+ *
* @return This array node, to allow chaining
*/
public ArrayNode add(Float value) {
@@ -477,10 +485,10 @@
}
return _add(numberNode(value.floatValue()));
}
-
+
/**
* Method for adding specified number at the end of this array.
- *
+ *
* @return This array node, to allow chaining
*/
public ArrayNode add(double v) {
@@ -490,7 +498,7 @@
/**
* Alternative method that we need to avoid bumping into NPE issues
* with auto-unboxing.
- *
+ *
* @return This array node, to allow chaining
*/
public ArrayNode add(Double value) {
@@ -499,10 +507,10 @@
}
return _add(numberNode(value.doubleValue()));
}
-
+
/**
* Method for adding specified number at the end of this array.
- *
+ *
* @return This array node, to allow chaining
*/
public ArrayNode add(BigDecimal v) {
@@ -514,7 +522,7 @@
/**
* Method for adding specified String value at the end of this array.
- *
+ *
* @return This array node, to allow chaining
*/
public ArrayNode add(String v) {
@@ -526,7 +534,7 @@
/**
* Method for adding specified boolean value at the end of this array.
- *
+ *
* @return This array node, to allow chaining
*/
public ArrayNode add(boolean v) {
@@ -536,7 +544,7 @@
/**
* Alternative method that we need to avoid bumping into NPE issues
* with auto-unboxing.
- *
+ *
* @return This array node, to allow chaining
*/
public ArrayNode add(Boolean value) {
@@ -545,11 +553,11 @@
}
return _add(booleanNode(value.booleanValue()));
}
-
+
/**
* Method for adding specified binary value at the end of this array
* (note: when serializing as JSON, will be output Base64 encoded)
- *
+ *
* @return This array node, to allow chaining
*/
public ArrayNode add(byte[] v) {
@@ -576,7 +584,7 @@
* Method for creating an {@link ObjectNode}, appending it at the end
* of this array, and returning the <b>newly created node</b>
* (note: NOT 'this' array)
- *
+ *
* @return Newly constructed ObjectNode
*/
public ObjectNode insertObject(int index)
@@ -589,7 +597,7 @@
/**
* Method that will construct a POJONode and
* insert it at specified position in this array.
- *
+ *
* @return This array node, to allow chaining
*/
public ArrayNode insertPOJO(int index, Object value)
@@ -603,7 +611,7 @@
/**
* Method that will insert a null value
* at specified position in this array.
- *
+ *
* @return This array node, to allow chaining
*/
public ArrayNode insertNull(int index)
@@ -615,7 +623,7 @@
/**
* Method that will insert specified numeric value
* at specified position in this array.
- *
+ *
* @return This array node, to allow chaining
*/
public ArrayNode insert(int index, int v) {
@@ -626,7 +634,7 @@
/**
* Alternative method that we need to avoid bumping into NPE issues
* with auto-unboxing.
- *
+ *
* @return This array node, to allow chaining
*/
public ArrayNode insert(int index, Integer value) {
@@ -637,11 +645,11 @@
}
return this;
}
-
+
/**
* Method that will insert specified numeric value
* at specified position in this array.
- *
+ *
* @return This array node, to allow chaining
*/
public ArrayNode insert(int index, long v) {
@@ -651,7 +659,7 @@
/**
* Alternative method that we need to avoid bumping into NPE issues
* with auto-unboxing.
- *
+ *
* @return This array node, to allow chaining
*/
public ArrayNode insert(int index, Long value) {
@@ -660,11 +668,11 @@
}
return _insert(index, numberNode(value.longValue()));
}
-
+
/**
* Method that will insert specified numeric value
* at specified position in this array.
- *
+ *
* @return This array node, to allow chaining
*/
public ArrayNode insert(int index, float v) {
@@ -674,7 +682,7 @@
/**
* Alternative method that we need to avoid bumping into NPE issues
* with auto-unboxing.
- *
+ *
* @return This array node, to allow chaining
*/
public ArrayNode insert(int index, Float value) {
@@ -683,11 +691,11 @@
}
return _insert(index, numberNode(value.floatValue()));
}
-
+
/**
* Method that will insert specified numeric value
* at specified position in this array.
- *
+ *
* @return This array node, to allow chaining
*/
public ArrayNode insert(int index, double v) {
@@ -697,7 +705,7 @@
/**
* Alternative method that we need to avoid bumping into NPE issues
* with auto-unboxing.
- *
+ *
* @return This array node, to allow chaining
*/
public ArrayNode insert(int index, Double value) {
@@ -710,7 +718,7 @@
/**
* Method that will insert specified numeric value
* at specified position in this array.
- *
+ *
* @return This array node, to allow chaining
*/
public ArrayNode insert(int index, BigDecimal v) {
@@ -723,7 +731,7 @@
/**
* Method that will insert specified String
* at specified position in this array.
- *
+ *
* @return This array node, to allow chaining
*/
public ArrayNode insert(int index, String v) {
@@ -736,7 +744,7 @@
/**
* Method that will insert specified String
* at specified position in this array.
- *
+ *
* @return This array node, to allow chaining
*/
public ArrayNode insert(int index, boolean v) {
@@ -746,7 +754,7 @@
/**
* Alternative method that we need to avoid bumping into NPE issues
* with auto-unboxing.
- *
+ *
* @return This array node, to allow chaining
*/
public ArrayNode insert(int index, Boolean value) {
@@ -755,12 +763,12 @@
}
return _insert(index, booleanNode(value.booleanValue()));
}
-
+
/**
* Method that will insert specified binary value
* at specified position in this array
* (note: when written as JSON, will be Base64 encoded)
- *
+ *
* @return This array node, to allow chaining
*/
public ArrayNode insert(int index, byte[] v) {
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/ContainerNode.java b/src/main/java/com/fasterxml/jackson/databind/node/ContainerNode.java
index 536755d..f2eb608 100644
--- a/src/main/java/com/fasterxml/jackson/databind/node/ContainerNode.java
+++ b/src/main/java/com/fasterxml/jackson/databind/node/ContainerNode.java
@@ -31,7 +31,7 @@
@Override
public abstract JsonToken asToken();
-
+
@Override
public String asText() { return ""; }
@@ -65,6 +65,14 @@
public final ArrayNode arrayNode() { return _nodeFactory.arrayNode(); }
/**
+ * Factory method that constructs and returns an {@link ArrayNode} with an initial capacity
+ * Construction is done using registered {@link JsonNodeFactory}
+ * @param capacity the initial capacity of the ArrayNode
+ */
+ @Override
+ public final ArrayNode arrayNode(int capacity) { return _nodeFactory.arrayNode(capacity); }
+
+ /**
* Factory method that constructs and returns an empty {@link ObjectNode}
* Construction is done using registered {@link JsonNodeFactory}.
*/
@@ -113,7 +121,7 @@
public final ValueNode numberNode(Float v) { return _nodeFactory.numberNode(v); }
@Override
public final ValueNode numberNode(Double v) { return _nodeFactory.numberNode(v); }
-
+
@Override
public final TextNode textNode(String text) { return _nodeFactory.textNode(text); }
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/FloatNode.java b/src/main/java/com/fasterxml/jackson/databind/node/FloatNode.java
index c79ac89..b4dc9fb 100644
--- a/src/main/java/com/fasterxml/jackson/databind/node/FloatNode.java
+++ b/src/main/java/com/fasterxml/jackson/databind/node/FloatNode.java
@@ -73,7 +73,7 @@
public long longValue() { return (long) _value; }
@Override
- public float floatValue() { return (float) _value; }
+ public float floatValue() { return _value; }
@Override
public double doubleValue() { return _value; }
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/JsonNodeCreator.java b/src/main/java/com/fasterxml/jackson/databind/node/JsonNodeCreator.java
index 5880370..55c880f 100644
--- a/src/main/java/com/fasterxml/jackson/databind/node/JsonNodeCreator.java
+++ b/src/main/java/com/fasterxml/jackson/databind/node/JsonNodeCreator.java
@@ -9,13 +9,13 @@
* Interface that defines common "creator" functionality implemented
* both by {@link JsonNodeFactory} and {@link ContainerNode} (that is,
* JSON Object and Array nodes).
- *
+ *
* @since 2.3
*/
public interface JsonNodeCreator
{
// Enumerated/singleton types
-
+
public ValueNode booleanNode(boolean v);
public ValueNode nullNode();
@@ -41,7 +41,7 @@
public ValueNode textNode(String text);
// Other value (non-structured) nodes
-
+
public ValueNode binaryNode(byte[] data);
public ValueNode binaryNode(byte[] data, int offset, int length);
public ValueNode pojoNode(Object pojo);
@@ -54,7 +54,7 @@
* Note that the concept may not work with all backends, and since
* no translation of any kinds is done it will not work when converting
* between data formats.
- *
+ *
* @since 2.6
*/
public ValueNode rawValueNode(RawValue value);
@@ -63,5 +63,13 @@
// (bit unkosher, due to forward references... but has to do for now)
public ArrayNode arrayNode();
+
+ /**
+ * Factory method for constructing a JSON Array node with an initial capacity
+ *
+ * @since 2.8
+ */
+ public ArrayNode arrayNode(int capacity);
+
public ObjectNode objectNode();
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/JsonNodeFactory.java b/src/main/java/com/fasterxml/jackson/databind/node/JsonNodeFactory.java
index 0d0d574..70ec2f7 100644
--- a/src/main/java/com/fasterxml/jackson/databind/node/JsonNodeFactory.java
+++ b/src/main/java/com/fasterxml/jackson/databind/node/JsonNodeFactory.java
@@ -8,13 +8,13 @@
/**
* Base class that specifies methods for getting access to
* Node instances (newly constructed, or shared, depending
- * on type), as well as basic implementation of the methods.
+ * on type), as well as basic implementation of the methods.
* Designed to be sub-classed if extended functionality (additions
* to behavior of node types, mostly) is needed.
*/
public class JsonNodeFactory
- implements java.io.Serializable // since 2.1
- ,JsonNodeCreator // since 2.3
+ implements java.io.Serializable, // since 2.1
+ JsonNodeCreator // since 2.3
{
// with 2.2
private static final long serialVersionUID = 1L;
@@ -85,7 +85,7 @@
{
return bigDecimalExact ? decimalsAsIs : decimalsNormalized;
}
-
+
/*
/**********************************************************
/* Factory methods for literal values
@@ -131,7 +131,7 @@
public ValueNode numberNode(Byte value) {
return (value == null) ? nullNode() : IntNode.valueOf(value.intValue());
}
-
+
/**
* Factory method for getting an instance of JSON numeric value
* that expresses given 16-bit integer value
@@ -149,7 +149,7 @@
public ValueNode numberNode(Short value) {
return (value == null) ? nullNode() : ShortNode.valueOf(value);
}
-
+
/**
* Factory method for getting an instance of JSON numeric value
* that expresses given 32-bit integer value
@@ -176,7 +176,7 @@
public NumericNode numberNode(long v) {
return LongNode.valueOf(v);
}
-
+
/**
* Alternate factory method that will handle wrapper value, which may be null.
* Due to possibility of null, returning type is not guaranteed to be
@@ -189,7 +189,7 @@
}
return LongNode.valueOf(value.longValue());
}
-
+
/**
* Factory method for getting an instance of JSON numeric value
* that expresses given unlimited range integer value
@@ -214,7 +214,7 @@
public ValueNode numberNode(Float value) {
return (value == null) ? nullNode() : FloatNode.valueOf(value.floatValue());
}
-
+
/**
* Factory method for getting an instance of JSON numeric value
* that expresses given 64-bit floating point value
@@ -232,7 +232,7 @@
public ValueNode numberNode(Double value) {
return (value == null) ? nullNode() : DoubleNode.valueOf(value.doubleValue());
}
-
+
/**
* Factory method for getting an instance of JSON numeric value
* that expresses given unlimited precision floating point value
@@ -310,6 +310,14 @@
public ArrayNode arrayNode() { return new ArrayNode(this); }
/**
+ * Factory method for constructing a JSON Array node with an initial capacity
+ *
+ * @since 2.8
+ */
+ @Override
+ public ArrayNode arrayNode(int capacity) { return new ArrayNode(this, capacity); }
+
+ /**
* Factory method for constructing an empty JSON Object ("struct") node
*/
@Override
@@ -334,7 +342,7 @@
/* Helper methods
/**********************************************************
*/
-
+
protected boolean _inIntRange(long l)
{
int i = (int) l;
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/ObjectNode.java b/src/main/java/com/fasterxml/jackson/databind/node/ObjectNode.java
index ae4d7c9..387943e 100644
--- a/src/main/java/com/fasterxml/jackson/databind/node/ObjectNode.java
+++ b/src/main/java/com/fasterxml/jackson/databind/node/ObjectNode.java
@@ -279,33 +279,56 @@
* all of its descendants using specified JSON generator.
*/
@Override
- public void serialize(JsonGenerator jg, SerializerProvider provider)
- throws IOException, JsonProcessingException
+ public void serialize(JsonGenerator g, SerializerProvider provider)
+ throws IOException
{
- jg.writeStartObject();
+ @SuppressWarnings("deprecation")
+ boolean trimEmptyArray = (provider != null) &&
+ !provider.isEnabled(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS);
+ g.writeStartObject(this);
for (Map.Entry<String, JsonNode> en : _children.entrySet()) {
- jg.writeFieldName(en.getKey());
- /* 17-Feb-2009, tatu: Can we trust that all nodes will always
- * extend BaseJsonNode? Or if not, at least implement
- * JsonSerializable? Let's start with former, change if
- * we must.
- */
- ((BaseJsonNode) en.getValue()).serialize(jg, provider);
+ /* 17-Feb-2009, tatu: Can we trust that all nodes will always
+ * extend BaseJsonNode? Or if not, at least implement
+ * JsonSerializable? Let's start with former, change if
+ * we must.
+ */
+ BaseJsonNode value = (BaseJsonNode) en.getValue();
+
+ // as per [databind#867], see if WRITE_EMPTY_JSON_ARRAYS feature is disabled,
+ // if the feature is disabled, then should not write an empty array
+ // to the output, so continue to the next element in the iteration
+ if (trimEmptyArray && value.isArray() && value.isEmpty(provider)) {
+ continue;
+ }
+ g.writeFieldName(en.getKey());
+ value.serialize(g, provider);
}
- jg.writeEndObject();
+ g.writeEndObject();
}
@Override
- public void serializeWithType(JsonGenerator jg, SerializerProvider provider,
+ public void serializeWithType(JsonGenerator g, SerializerProvider provider,
TypeSerializer typeSer)
- throws IOException, JsonProcessingException
+ throws IOException
{
- typeSer.writeTypePrefixForObject(this, jg);
+ @SuppressWarnings("deprecation")
+ boolean trimEmptyArray = (provider != null) &&
+ !provider.isEnabled(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS);
+ typeSer.writeTypePrefixForObject(this, g);
for (Map.Entry<String, JsonNode> en : _children.entrySet()) {
- jg.writeFieldName(en.getKey());
- ((BaseJsonNode) en.getValue()).serialize(jg, provider);
+ BaseJsonNode value = (BaseJsonNode) en.getValue();
+
+ // check if WRITE_EMPTY_JSON_ARRAYS feature is disabled,
+ // if the feature is disabled, then should not write an empty array
+ // to the output, so continue to the next element in the iteration
+ if (trimEmptyArray && value.isArray() && value.isEmpty(provider)) {
+ continue;
+ }
+
+ g.writeFieldName(en.getKey());
+ value.serialize(g, provider);
}
- typeSer.writeTypeSuffixForObject(this, jg);
+ typeSer.writeTypeSuffixForObject(this, g);
}
/*
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/TextNode.java b/src/main/java/com/fasterxml/jackson/databind/node/TextNode.java
index e65e589..2e595a1 100644
--- a/src/main/java/com/fasterxml/jackson/databind/node/TextNode.java
+++ b/src/main/java/com/fasterxml/jackson/databind/node/TextNode.java
@@ -57,9 +57,9 @@
* base64 encoded; if so, they are decoded and resulting binary
* data is returned.
*/
+ @SuppressWarnings("resource")
public byte[] getBinaryValue(Base64Variant b64variant) throws IOException
{
- @SuppressWarnings("resource")
ByteArrayBuilder builder = new ByteArrayBuilder(100);
final String str = _value;
int ptr = 0;
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/AnyGetterWriter.java b/src/main/java/com/fasterxml/jackson/databind/ser/AnyGetterWriter.java
index f74e1db..bedf67e 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/AnyGetterWriter.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/AnyGetterWriter.java
@@ -24,7 +24,7 @@
protected JsonSerializer<Object> _serializer;
protected MapSerializer _mapSerializer;
-
+
@SuppressWarnings("unchecked")
public AnyGetterWriter(BeanProperty property,
AnnotatedMember accessor, JsonSerializer<?> serializer)
@@ -37,6 +37,14 @@
}
}
+ /**
+ * @since 0.8.3
+ */
+ public void fixAccess(SerializationConfig config) {
+ _accessor.fixAccess(
+ config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
+ }
+
public void getAndSerialize(Object bean, JsonGenerator gen, SerializerProvider provider)
throws Exception
{
@@ -45,8 +53,8 @@
return;
}
if (!(value instanceof Map<?,?>)) {
- throw JsonMappingException.from(gen, "Value returned by 'any-getter' ("
- +_accessor.getName()+"()) not java.util.Map but "+value.getClass().getName());
+ provider.reportMappingProblem("Value returned by 'any-getter' %s() not java.util.Map but %s",
+ _accessor.getName(), value.getClass().getName());
}
// 23-Feb-2015, tatu: Nasty, but has to do (for now)
if (_mapSerializer != null) {
@@ -68,8 +76,8 @@
return;
}
if (!(value instanceof Map<?,?>)) {
- throw JsonMappingException.from(gen, "Value returned by 'any-getter' ("
- +_accessor.getName()+"()) not java.util.Map but "+value.getClass().getName());
+ provider.reportMappingProblem("Value returned by 'any-getter' (%s()) not java.util.Map but %s",
+ _accessor.getName(), value.getClass().getName());
}
// 19-Oct-2014, tatu: Should we try to support @JsonInclude options here?
if (_mapSerializer != null) {
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/BasicSerializerFactory.java b/src/main/java/com/fasterxml/jackson/databind/ser/BasicSerializerFactory.java
index 523ae3d..29c4a49 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/BasicSerializerFactory.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/BasicSerializerFactory.java
@@ -9,6 +9,7 @@
import java.util.*;
import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
@@ -234,7 +235,7 @@
if (config.canOverrideAccessModifiers()) {
ClassUtil.checkAndFixAccess(m, config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
}
- ser = new JsonValueSerializer(m, delegate);
+ ser = new JsonValueSerializer(am, delegate);
} else {
ser = StdKeySerializers.getFallbackKeySerializer(config, keyType.getRawClass());
}
@@ -353,7 +354,7 @@
ClassUtil.checkAndFixAccess(m, prov.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
}
JsonSerializer<Object> ser = findSerializerFromAnnotation(prov, valueMethod);
- return new JsonValueSerializer(m, ser);
+ return new JsonValueSerializer(valueMethod, ser);
}
// No well-known annotations...
return null;
@@ -388,16 +389,10 @@
if (Map.Entry.class.isAssignableFrom(raw)) {
// 18-Oct-2015, tatu: With 2.7, need to dig type info:
JavaType mapEntryType = type.findSuperType(Map.Entry.class);
-
+
// 28-Apr-2015, tatu: TypeFactory does it all for us already so
- JavaType kt = mapEntryType.containedType(0);
- if (kt == null) {
- kt = TypeFactory.unknownType();
- }
- JavaType vt = mapEntryType.containedType(1);
- if (vt == null) {
- vt = TypeFactory.unknownType();
- }
+ JavaType kt = mapEntryType.containedTypeOrUnknown(0);
+ JavaType vt = mapEntryType.containedTypeOrUnknown(1);
return buildMapEntrySerializer(prov.getConfig(), type, beanDesc, staticTyping, kt, vt);
}
if (ByteBuffer.class.isAssignableFrom(raw)) {
@@ -768,8 +763,15 @@
ser = findSerializerByAnnotations(prov, type, beanDesc); // (2) Annotations
if (ser == null) {
Object filterId = findFilterId(config, beanDesc);
- AnnotationIntrospector ai = config.getAnnotationIntrospector();
- MapSerializer mapSer = MapSerializer.construct(ai.findPropertiesToIgnore(beanDesc.getClassInfo(), true),
+ // 01-May-2016, tatu: Which base type to use here gets tricky, since
+ // most often it ought to be `Map` or `EnumMap`, but due to abstract
+ // mapping it will more likely be concrete type like `HashMap`.
+ // So, for time being, just pass `Map.class`
+ JsonIgnoreProperties.Value ignorals = config.getDefaultPropertyIgnorals(Map.class,
+ beanDesc.getClassInfo());
+ Set<String> ignored = (ignorals == null) ? null
+ : ignorals.findIgnoredForSerialization();
+ MapSerializer mapSer = MapSerializer.construct(ignored,
type, staticTyping, elementTypeSerializer,
keySerializer, elementValueSerializer, filterId);
Object suppressableValue = findSuppressableContentValue(config,
@@ -800,8 +802,12 @@
JavaType contentType, BeanDescription beanDesc)
throws JsonMappingException
{
+ /* 16-Apr-2016, tatu: Should this consider possible property-config overrides?
+ * Quite possibly yes, but would need to carefully check that content type being
+ * used is appropriate.
+ */
JsonInclude.Value inclV = beanDesc.findPropertyInclusion(config.getDefaultPropertyInclusion());
-
+
if (inclV == null) {
return null;
}
@@ -897,16 +903,6 @@
return new IteratorSerializer(valueType, staticTyping, createTypeSerializer(config, valueType));
}
- @Deprecated // since 2.5
- protected JsonSerializer<?> buildIteratorSerializer(SerializationConfig config,
- JavaType type, BeanDescription beanDesc, boolean staticTyping) throws JsonMappingException
- {
- JavaType[] params = config.getTypeFactory().findTypeParameters(type, Iterator.class);
- JavaType vt = (params == null || params.length != 1) ?
- TypeFactory.unknownType() : params[0];
- return buildIteratorSerializer(config, type, beanDesc, staticTyping, vt);
- }
-
/**
* @since 2.5
*/
@@ -918,18 +914,6 @@
return new IterableSerializer(valueType, staticTyping, createTypeSerializer(config, valueType));
}
- @Deprecated // since 2.5
- protected JsonSerializer<?> buildIterableSerializer(SerializationConfig config,
- JavaType type, BeanDescription beanDesc,
- boolean staticTyping)
- throws JsonMappingException
- {
- JavaType[] params = config.getTypeFactory().findTypeParameters(type, Iterable.class);
- JavaType vt = (params == null || params.length != 1) ?
- TypeFactory.unknownType() : params[0];
- return buildIterableSerializer(config, type, beanDesc, staticTyping, vt);
- }
-
/**
* @since 2.5
*/
@@ -961,7 +945,7 @@
@SuppressWarnings("unchecked")
Class<Enum<?>> enumClass = (Class<Enum<?>>) type.getRawClass();
JsonSerializer<?> ser = EnumSerializer.construct(enumClass, config, beanDesc, format);
- // [Issue#120]: Allow post-processing
+ // [databind#120]: Allow post-processing
if (_factoryConfig.hasSerializerModifiers()) {
for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {
ser = mod.modifyEnumSerializer(config, type, beanDesc, ser);
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyWriter.java b/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyWriter.java
index b5972fb..49c87af 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyWriter.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyWriter.java
@@ -25,18 +25,18 @@
/**
* Base bean property handler class, which implements common parts of
- * reflection-based functionality for accessing a property value
- * and serializing it.
- *<p>
+ * reflection-based functionality for accessing a property value and serializing
+ * it.
+ * <p>
* Note that current design tries to keep instances immutable (semi-functional
- * style); mostly because these instances are exposed to application
- * code and this is to reduce likelihood of data corruption and
- * synchronization issues.
+ * style); mostly because these instances are exposed to application code and
+ * this is to reduce likelihood of data corruption and synchronization issues.
*/
-@JacksonStdImpl // since 2.6. NOTE: sub-classes typically are not
-public class BeanPropertyWriter
- extends PropertyWriter // which extends `ConcreteBeanPropertyBase`
- implements java.io.Serializable // since 2.6
+@JacksonStdImpl
+// since 2.6. NOTE: sub-classes typically are not
+public class BeanPropertyWriter extends PropertyWriter // which extends
+ // `ConcreteBeanPropertyBase`
+ implements java.io.Serializable // since 2.6
{
// As of 2.7
private static final long serialVersionUID = 1L;
@@ -47,19 +47,19 @@
public final static Object MARKER_FOR_EMPTY = JsonInclude.Include.NON_EMPTY;
/*
- /**********************************************************
+ /***********************************************************
/* Basic property metadata: name, type, other
- /**********************************************************
+ /***********************************************************
*/
/**
- * Logical name of the property; will be used as the field name
- * under which value for the property is written.
- *<p>
- * NOTE: do NOT change name of this field; it is accessed by
- * Afterburner module (until 2.4; not directly from 2.5)
- * ALSO NOTE: ... and while it really ought to be `SerializableString`,
- * changing that is also binary-incompatible change. So nope.
+ * Logical name of the property; will be used as the field name under which
+ * value for the property is written.
+ * <p>
+ * NOTE: do NOT change name of this field; it is accessed by Afterburner
+ * module (until 2.4; not directly from 2.5) ALSO NOTE: ... and while it
+ * really ought to be `SerializableString`, changing that is also
+ * binary-incompatible change. So nope.
*/
protected final SerializedString _name;
@@ -71,110 +71,106 @@
protected final PropertyName _wrapperName;
/**
- * Type property is declared to have, either in class definition
- * or associated annotations.
+ * Type property is declared to have, either in class definition or
+ * associated annotations.
*/
protected final JavaType _declaredType;
/**
- * Type to use for locating serializer; normally same as return
- * type of the accessor method, but may be overridden by annotations.
+ * Type to use for locating serializer; normally same as return type of the
+ * accessor method, but may be overridden by annotations.
*/
protected final JavaType _cfgSerializationType;
/**
- * Base type of the property, if the declared type is "non-trivial";
- * meaning it is either a structured type (collection, map, array),
- * or parameterized. Used to retain type information about contained
- * type, which is mostly necessary if type meta-data is to be
- * included.
+ * Base type of the property, if the declared type is "non-trivial"; meaning
+ * it is either a structured type (collection, map, array), or
+ * parameterized. Used to retain type information about contained type,
+ * which is mostly necessary if type meta-data is to be included.
*/
protected JavaType _nonTrivialBaseType;
/**
- * Annotations from context (most often, class that declares property,
- * or in case of sub-class serializer, from that sub-class)
- *<p>
- * NOTE: transient just to support JDK serializability; Annotations
- * do not serialize. At all.
+ * Annotations from context (most often, class that declares property, or in
+ * case of sub-class serializer, from that sub-class)
+ * <p>
+ * NOTE: transient just to support JDK serializability; Annotations do not
+ * serialize. At all.
*/
protected final transient Annotations _contextAnnotations;
/*
- /**********************************************************
+ /***********************************************************
/* Settings for accessing property value to serialize
- /**********************************************************
+ /***********************************************************
*/
/**
- * Member (field, method) that represents property and allows access
- * to associated annotations.
+ * Member (field, method) that represents property and allows access to
+ * associated annotations.
*/
protected final AnnotatedMember _member;
-
+
/**
- * Accessor method used to get property value, for
- * method-accessible properties.
- * Null if and only if {@link #_field} is null.
- *<p>
+ * Accessor method used to get property value, for method-accessible
+ * properties. Null if and only if {@link #_field} is null.
+ * <p>
* `transient` (and non-final) only to support JDK serializability.
*/
protected transient Method _accessorMethod;
-
+
/**
- * Field that contains the property value for field-accessible
- * properties.
+ * Field that contains the property value for field-accessible properties.
* Null if and only if {@link #_accessorMethod} is null.
- *<p>
+ * <p>
* `transient` (and non-final) only to support JDK serializability.
*/
protected transient Field _field;
/*
- /**********************************************************
+ /***********************************************************
/* Serializers needed
- /**********************************************************
+ /***********************************************************
*/
/**
- * Serializer to use for writing out the value: null if it can not
- * be known statically; non-null if it can.
+ * Serializer to use for writing out the value: null if it can not be known
+ * statically; non-null if it can.
*/
protected JsonSerializer<Object> _serializer;
/**
- * Serializer used for writing out null values, if any: if null,
- * null values are to be suppressed.
+ * Serializer used for writing out null values, if any: if null, null values
+ * are to be suppressed.
*/
protected JsonSerializer<Object> _nullSerializer;
/**
- * If property being serialized needs type information to be
- * included this is the type serializer to use.
- * Declared type (possibly augmented with annotations) of property
- * is used for determining exact mechanism to use (compared to
- * actual runtime type used for serializing actual state).
+ * If property being serialized needs type information to be included this
+ * is the type serializer to use. Declared type (possibly augmented with
+ * annotations) of property is used for determining exact mechanism to use
+ * (compared to actual runtime type used for serializing actual state).
*/
protected TypeSerializer _typeSerializer;
/**
* In case serializer is not known statically (i.e. <code>_serializer</code>
- * is null), we will use a lookup structure for storing dynamically
- * resolved mapping from type(s) to serializer(s).
+ * is null), we will use a lookup structure for storing dynamically resolved
+ * mapping from type(s) to serializer(s).
*/
protected transient PropertySerializerMap _dynamicSerializers;
/*
- /**********************************************************
+ /***********************************************************
/* Filtering
- /**********************************************************
+ /***********************************************************
*/
/**
- * Whether null values are to be suppressed (nothing written out if
- * value is null) or not. Note that this is a configuration value
- * during construction, and actual handling relies on setting
- * (or not) of {@link #_nullSerializer}.
+ * Whether null values are to be suppressed (nothing written out if value is
+ * null) or not. Note that this is a configuration value during
+ * construction, and actual handling relies on setting (or not) of
+ * {@link #_nullSerializer}.
*/
protected final boolean _suppressNulls;
@@ -185,33 +181,32 @@
protected final Object _suppressableValue;
/**
- * Alternate set of property writers used when view-based filtering
- * is available for the Bean.
+ * Alternate set of property writers used when view-based filtering is
+ * available for the Bean.
*/
protected final Class<?>[] _includeInViews;
/*
/**********************************************************
- /* Opaque internal data that bean serializer factory and
+ /* Opaqueinternal data that bean serializer factory and
/* bean serializers can add.
/**********************************************************
*/
- protected transient HashMap<Object,Object> _internalSettings;
+ protected transient HashMap<Object, Object> _internalSettings;
/*
- /**********************************************************
+ /***********************************************************
/* Construction, configuration
- /**********************************************************
+ /***********************************************************
*/
@SuppressWarnings("unchecked")
public BeanPropertyWriter(BeanPropertyDefinition propDef,
AnnotatedMember member, Annotations contextAnnotations,
- JavaType declaredType,
- JsonSerializer<?> ser, TypeSerializer typeSer, JavaType serType,
- boolean suppressNulls, Object suppressableValue)
- {
+ JavaType declaredType, JsonSerializer<?> ser,
+ TypeSerializer typeSer, JavaType serType, boolean suppressNulls,
+ Object suppressableValue) {
super(propDef);
_member = member;
_contextAnnotations = contextAnnotations;
@@ -222,7 +217,8 @@
_declaredType = declaredType;
_serializer = (JsonSerializer<Object>) ser;
- _dynamicSerializers = (ser == null) ? PropertySerializerMap.emptyForProperties() : null;
+ _dynamicSerializers = (ser == null) ? PropertySerializerMap
+ .emptyForProperties() : null;
_typeSerializer = typeSer;
_cfgSerializationType = serType;
@@ -233,7 +229,8 @@
_accessorMethod = (Method) member.getMember();
_field = null;
} else {
- // 01-Dec-2014, tatu: Used to be illegal, but now explicitly allowed for virtual props
+ // 01-Dec-2014, tatu: Used to be illegal, but now explicitly allowed
+ // for virtual props
_accessorMethod = null;
_field = null;
}
@@ -245,9 +242,9 @@
}
/**
- * Constructor that may be of use to virtual properties, when there is need for
- * the zero-arg ("default") constructor, and actual initialization is done
- * after constructor call.
+ * Constructor that may be of use to virtual properties, when there is need
+ * for the zero-arg ("default") constructor, and actual initialization is
+ * done after constructor call.
*
* @since 2.5
*/
@@ -284,13 +281,13 @@
/**
* @since 2.5
*/
- protected BeanPropertyWriter(BeanPropertyWriter base, PropertyName name)
- {
+ protected BeanPropertyWriter(BeanPropertyWriter base, PropertyName name) {
super(base);
- /* 02-Dec-2014, tatu: This is a big mess, alas, what with dependency
- * to MapperConfig to encode, and Afterburner having heartburn
- * for SerializableString (vs SerializedString).
- * Hope it can be resolved/reworked in 2.6 timeframe, if not for 2.5
+ /*
+ * 02-Dec-2014, tatu: This is a big mess, alas, what with dependency to
+ * MapperConfig to encode, and Afterburner having heartburn for
+ * SerializableString (vs SerializedString). Hope it can be
+ * resolved/reworked in 2.6 timeframe, if not for 2.5
*/
_name = new SerializedString(name.getSimpleName());
_wrapperName = base._wrapperName;
@@ -306,7 +303,8 @@
_nullSerializer = base._nullSerializer;
// one more thing: copy internal settings, if any
if (base._internalSettings != null) {
- _internalSettings = new HashMap<Object,Object>(base._internalSettings);
+ _internalSettings = new HashMap<Object, Object>(
+ base._internalSettings);
}
_cfgSerializationType = base._cfgSerializationType;
_dynamicSerializers = base._dynamicSerializers;
@@ -330,7 +328,8 @@
_serializer = base._serializer;
_nullSerializer = base._nullSerializer;
if (base._internalSettings != null) {
- _internalSettings = new HashMap<Object,Object>(base._internalSettings);
+ _internalSettings = new HashMap<Object, Object>(
+ base._internalSettings);
}
_cfgSerializationType = base._cfgSerializationType;
_dynamicSerializers = base._dynamicSerializers;
@@ -352,15 +351,15 @@
/**
* Overridable factory method used by sub-classes
*
- * @since 2.6.0
+ * @since 2.6
*/
protected BeanPropertyWriter _new(PropertyName newName) {
return new BeanPropertyWriter(this, newName);
}
/**
- * Method called to set, reset or clear the configured type serializer
- * for property.
+ * Method called to set, reset or clear the configured type serializer for
+ * property.
*
* @since 2.6
*/
@@ -370,8 +369,6 @@
/**
* Method called to assign value serializer for property
- *
- * @since 2.0
*/
public void assignSerializer(JsonSerializer<Object> ser) {
// may need to disable check in future?
@@ -383,12 +380,10 @@
/**
* Method called to assign null value serializer for property
- *
- * @since 2.0
*/
public void assignNullSerializer(JsonSerializer<Object> nullSer) {
// may need to disable check in future?
- if (_nullSerializer != null && _nullSerializer != nullSer) {
+ if ((_nullSerializer != null) && (_nullSerializer != nullSer)) {
throw new IllegalStateException("Can not override null serializer");
}
_nullSerializer = nullSer;
@@ -404,20 +399,32 @@
/**
* Method called to define type to consider as "non-trivial" basetype,
- * needed for dynamic serialization resolution for complex (usually container)
- * types
+ * needed for dynamic serialization resolution for complex (usually
+ * container) types
*/
public void setNonTrivialBaseType(JavaType t) {
_nonTrivialBaseType = t;
}
+ /**
+ * Method called to ensure that the mutator has proper access rights to
+ * be called, as per configuration. Overridden by implementations that
+ * have mutators that require access, fields and setters.
+ *
+ * @since 2.8.3
+ */
+ public void fixAccess(SerializationConfig config) {
+ _member.fixAccess(config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
+ }
+
/*
- /**********************************************************
+ /***********************************************************
/* JDK Serializability
- /**********************************************************
+ /***********************************************************
*/
- /* Ideally would not require mutable state, and instead would re-create with
+ /*
+ * Ideally would not require mutable state, and instead would re-create with
* final settings. However, as things are, with sub-types and all, simplest
* to just change Field/Method value directly.
*/
@@ -436,21 +443,32 @@
}
/*
- /**********************************************************
+ /************************************************************
/* BeanProperty impl
- /**********************************************************
+ /***********************************************************
*/
// Note: also part of 'PropertyWriter'
- @Override public String getName() { return _name.getValue(); }
+ @Override
+ public String getName() {
+ return _name.getValue();
+ }
// Note: also part of 'PropertyWriter'
- @Override public PropertyName getFullName() { // !!! TODO: impl properly
+ @Override
+ public PropertyName getFullName() { // !!! TODO: impl properly
return new PropertyName(_name.getValue());
}
- @Override public JavaType getType() { return _declaredType; }
- @Override public PropertyName getWrapperName() { return _wrapperName; }
+ @Override
+ public JavaType getType() {
+ return _declaredType;
+ }
+
+ @Override
+ public PropertyName getWrapperName() {
+ return _wrapperName;
+ }
// Note: also part of 'PropertyWriter'
@Override
@@ -461,32 +479,37 @@
// Note: also part of 'PropertyWriter'
@Override
public <A extends Annotation> A getContextAnnotation(Class<A> acls) {
- return (_contextAnnotations == null) ? null : _contextAnnotations.get(acls);
+ return (_contextAnnotations == null) ? null : _contextAnnotations
+ .get(acls);
}
- @Override public AnnotatedMember getMember() { return _member; }
+ @Override
+ public AnnotatedMember getMember() {
+ return _member;
+ }
// @since 2.3 -- needed so it can be overridden by unwrapping writer
- protected void _depositSchemaProperty(ObjectNode propertiesNode, JsonNode schemaNode) {
+ protected void _depositSchemaProperty(ObjectNode propertiesNode,
+ JsonNode schemaNode) {
propertiesNode.set(getName(), schemaNode);
}
/*
- /**********************************************************
+ /***********************************************************
/* Managing and accessing of opaque internal settings
/* (used by extensions)
- /**********************************************************
+ /***********************************************************
*/
-
+
/**
* Method for accessing value of specified internal setting.
*
* @return Value of the setting, if any; null if none.
*/
- public Object getInternalSetting(Object key) {
+ public Object getInternalSetting(Object key) {
return (_internalSettings == null) ? null : _internalSettings.get(key);
}
-
+
/**
* Method for setting specific internal setting to given value
*
@@ -494,7 +517,7 @@
*/
public Object setInternalSetting(Object key, Object value) {
if (_internalSettings == null) {
- _internalSettings = new HashMap<Object,Object>();
+ _internalSettings = new HashMap<Object, Object>();
}
return _internalSettings.put(key, value);
}
@@ -515,35 +538,49 @@
}
return removed;
}
-
+
/*
- /**********************************************************
+ /***********************************************************
/* Accessors
- /**********************************************************
+ /***********************************************************
*/
- public SerializableString getSerializedName() { return _name; }
-
- public boolean hasSerializer() { return _serializer != null; }
- public boolean hasNullSerializer() { return _nullSerializer != null; }
+ public SerializableString getSerializedName() {
+ return _name;
+ }
+
+ public boolean hasSerializer() {
+ return _serializer != null;
+ }
+
+ public boolean hasNullSerializer() {
+ return _nullSerializer != null;
+ }
/**
* @since 2.6
*/
- public TypeSerializer getTypeSerializer() { return _typeSerializer; }
+ public TypeSerializer getTypeSerializer() {
+ return _typeSerializer;
+ }
/**
* Accessor that will return true if this bean property has to support
* "unwrapping"; ability to replace POJO structural wrapping with optional
- * name prefix and/or suffix (or in some cases, just removal of wrapper name).
- *<p>
+ * name prefix and/or suffix (or in some cases, just removal of wrapper
+ * name).
+ * <p>
* Default implementation simply returns false.
*
* @since 2.3
*/
- public boolean isUnwrapping() { return false; }
-
- public boolean willSuppressNulls() { return _suppressNulls; }
+ public boolean isUnwrapping() {
+ return false;
+ }
+
+ public boolean willSuppressNulls() {
+ return _suppressNulls;
+ }
/**
* Method called to check to see if this property has a name that would
@@ -556,33 +593,25 @@
return _wrapperName.equals(name);
}
// Bit convoluted since our support for namespaces is spotty but:
- return name.hasSimpleName(_name.getValue())
- && !name.hasNamespace();
+ return name.hasSimpleName(_name.getValue()) && !name.hasNamespace();
}
-
- // Needed by BeanSerializer#getSchema
- public JsonSerializer<Object> getSerializer() { return _serializer; }
- public JavaType getSerializationType() { return _cfgSerializationType; }
+ // Needed by BeanSerializer#getSchema
+ public JsonSerializer<Object> getSerializer() {
+ return _serializer;
+ }
+
+ public JavaType getSerializationType() {
+ return _cfgSerializationType;
+ }
public Class<?> getRawSerializationType() {
- return (_cfgSerializationType == null) ? null : _cfgSerializationType.getRawClass();
+ return (_cfgSerializationType == null) ? null : _cfgSerializationType
+ .getRawClass();
}
- /*
- public JavaType getFullPropertyType() {
- if (_accessorMethod != null) {
- return _accessorMethod.getType()
- }
- if (_field != null) {
- return _field.getType();
- }
- return null;
- }
- */
-
/**
- * @deprecated Since 2.7, to be removed from 2.8, use {@link #getType()} instead.
+ * @deprecated Since 2.7, to be removed from 2.9, use {@link #getType()} instead.
*/
@Deprecated
public Class<?> getPropertyType() {
@@ -600,7 +629,7 @@
*
* @return The property type, or null if not found.
*
- * @deprecated Since 2.7, to be removed from 2.8, use {@link #getType()} instead.
+ * @deprecated Since 2.7, to be removed from 2.9, use {@link #getType()} instead.
*/
@Deprecated
public Type getGenericPropertyType() {
@@ -613,24 +642,27 @@
return null;
}
- public Class<?>[] getViews() { return _includeInViews; }
+ public Class<?>[] getViews() {
+ return _includeInViews;
+ }
/*
- /**********************************************************
+ /***********************************************************
/* PropertyWriter methods (serialization)
- /**********************************************************
+ /***********************************************************
*/
/**
- * Method called to access property that this bean stands for, from
- * within given bean, and to serialize it as a JSON Object field
- * using appropriate serializer.
+ * Method called to access property that this bean stands for, from within
+ * given bean, and to serialize it as a JSON Object field using appropriate
+ * serializer.
*/
@Override
- public void serializeAsField(Object bean, JsonGenerator gen, SerializerProvider prov) throws Exception
- {
+ public void serializeAsField(Object bean, JsonGenerator gen,
+ SerializerProvider prov) throws Exception {
// inlined 'get()'
- final Object value = (_accessorMethod == null) ? _field.get(bean) : _accessorMethod.invoke(bean);
+ final Object value = (_accessorMethod == null) ? _field.get(bean)
+ : _accessorMethod.invoke(bean);
// Null handling is bit different, check that first
if (value == null) {
@@ -676,33 +708,33 @@
}
/**
- * Method called to indicate that serialization of a field was omitted
- * due to filtering, in cases where backend data format does not allow
- * basic omission.
+ * Method called to indicate that serialization of a field was omitted due
+ * to filtering, in cases where backend data format does not allow basic
+ * omission.
*
* @since 2.3
*/
@Override
- public void serializeAsOmittedField(Object bean, JsonGenerator gen, SerializerProvider prov) throws Exception
- {
+ public void serializeAsOmittedField(Object bean, JsonGenerator gen,
+ SerializerProvider prov) throws Exception {
if (!gen.canOmitFields()) {
gen.writeOmittedField(_name.getValue());
}
}
-
+
/**
- * Alternative to {@link #serializeAsField} that is used when a POJO
- * is serialized as JSON Array; the difference is that no field names
- * are written.
+ * Alternative to {@link #serializeAsField} that is used when a POJO is
+ * serialized as JSON Array; the difference is that no field names are
+ * written.
*
* @since 2.3
*/
@Override
- public void serializeAsElement(Object bean, JsonGenerator gen, SerializerProvider prov)
- throws Exception
- {
+ public void serializeAsElement(Object bean, JsonGenerator gen,
+ SerializerProvider prov) throws Exception {
// inlined 'get()'
- final Object value = (_accessorMethod == null) ? _field.get(bean) : _accessorMethod.invoke(bean);
+ final Object value = (_accessorMethod == null) ? _field.get(bean)
+ : _accessorMethod.invoke(bean);
if (value == null) { // nulls need specialized handling
if (_nullSerializer != null) {
_nullSerializer.serialize(null, gen, prov);
@@ -724,11 +756,14 @@
// and then see if we must suppress certain values (default, empty)
if (_suppressableValue != null) {
if (MARKER_FOR_EMPTY == _suppressableValue) {
- if (ser.isEmpty(prov, value)) { // can NOT suppress entries in tabular output
+ if (ser.isEmpty(prov, value)) { // can NOT suppress entries in
+ // tabular output
serializeAsPlaceholder(bean, gen, prov);
return;
}
- } else if (_suppressableValue.equals(value)) { // can NOT suppress entries in tabular output
+ } else if (_suppressableValue.equals(value)) { // can NOT suppress
+ // entries in tabular
+ // output
serializeAsPlaceholder(bean, gen, prov);
return;
}
@@ -747,39 +782,36 @@
}
/**
- * Method called to serialize a placeholder used in tabular output when
- * real value is not to be included (is filtered out), but when we need
- * an entry so that field indexes will not be off. Typically this should
- * output null or empty String, depending on datatype.
+ * Method called to serialize a placeholder used in tabular output when real
+ * value is not to be included (is filtered out), but when we need an entry
+ * so that field indexes will not be off. Typically this should output null
+ * or empty String, depending on datatype.
*
* @since 2.1
*/
@Override
- public void serializeAsPlaceholder(Object bean, JsonGenerator gen, SerializerProvider prov)
- throws Exception
- {
+ public void serializeAsPlaceholder(Object bean, JsonGenerator gen,
+ SerializerProvider prov) throws Exception {
if (_nullSerializer != null) {
_nullSerializer.serialize(null, gen, prov);
} else {
gen.writeNull();
}
}
-
+
/*
- /**********************************************************
+ /***********************************************************
/* PropertyWriter methods (schema generation)
- /**********************************************************
+ /***********************************************************
*/
// Also part of BeanProperty implementation
@Override
public void depositSchemaProperty(JsonObjectFormatVisitor v,
- SerializerProvider provider)
- throws JsonMappingException
- {
+ SerializerProvider provider) throws JsonMappingException {
if (v != null) {
if (isRequired()) {
- v.property(this);
+ v.property(this);
} else {
v.optionalProperty(this);
}
@@ -789,20 +821,23 @@
// // // Legacy support for JsonFormatVisitable
/**
- * Attempt to add the output of the given {@link BeanPropertyWriter} in the given {@link ObjectNode}.
- * Otherwise, add the default schema {@link JsonNode} in place of the writer's output
+ * Attempt to add the output of the given {@link BeanPropertyWriter} in the
+ * given {@link ObjectNode}. Otherwise, add the default schema
+ * {@link JsonNode} in place of the writer's output
*
- * @param propertiesNode Node which the given property would exist within
- * @param provider Provider that can be used for accessing dynamic aspects of serialization
- * processing
+ * @param propertiesNode
+ * Node which the given property would exist within
+ * @param provider
+ * Provider that can be used for accessing dynamic aspects of
+ * serialization processing
*/
@Override
@Deprecated
- public void depositSchemaProperty(ObjectNode propertiesNode, SerializerProvider provider)
- throws JsonMappingException
- {
+ public void depositSchemaProperty(ObjectNode propertiesNode,
+ SerializerProvider provider) throws JsonMappingException {
JavaType propType = getSerializationType();
- // 03-Dec-2010, tatu: SchemaAware REALLY should use JavaType, but alas it doesn't...
+ // 03-Dec-2010, tatu: SchemaAware REALLY should use JavaType, but alas
+ // it doesn't...
Type hint = (propType == null) ? getType() : propType.getRawClass();
JsonNode schemaNode;
// Maybe it already has annotated/statically configured serializer?
@@ -812,9 +847,11 @@
}
boolean isOptional = !isRequired();
if (ser instanceof SchemaAware) {
- schemaNode = ((SchemaAware) ser).getSchema(provider, hint, isOptional) ;
- } else {
- schemaNode = com.fasterxml.jackson.databind.jsonschema.JsonSchema.getDefaultSchemaNode();
+ schemaNode = ((SchemaAware) ser).getSchema(provider, hint,
+ isOptional);
+ } else {
+ schemaNode = com.fasterxml.jackson.databind.jsonschema.JsonSchema
+ .getDefaultSchemaNode();
}
_depositSchemaProperty(propertiesNode, schemaNode);
}
@@ -825,12 +862,13 @@
/**********************************************************
*/
- protected JsonSerializer<Object> _findAndAddDynamic(PropertySerializerMap map,
- Class<?> type, SerializerProvider provider) throws JsonMappingException
- {
+ protected JsonSerializer<Object> _findAndAddDynamic(
+ PropertySerializerMap map, Class<?> type,
+ SerializerProvider provider) throws JsonMappingException {
PropertySerializerMap.SerializerAndMapResult result;
if (_nonTrivialBaseType != null) {
- JavaType t = provider.constructSpecializedType(_nonTrivialBaseType, type);
+ JavaType t = provider.constructSpecializedType(_nonTrivialBaseType,
+ type);
result = map.findAndAddPrimarySerializer(t, provider, this);
} else {
result = map.findAndAddPrimarySerializer(type, provider, this);
@@ -843,40 +881,44 @@
}
/**
- * Method that can be used to access value of the property this
- * Object describes, from given bean instance.
- *<p>
+ * Method that can be used to access value of the property this Object
+ * describes, from given bean instance.
+ * <p>
* Note: method is final as it should not need to be overridden -- rather,
- * calling method(s) ({@link #serializeAsField}) should be overridden
- * to change the behavior
+ * calling method(s) ({@link #serializeAsField}) should be overridden to
+ * change the behavior
*/
public final Object get(Object bean) throws Exception {
- return (_accessorMethod == null) ? _field.get(bean) : _accessorMethod.invoke(bean);
+ return (_accessorMethod == null) ? _field.get(bean) : _accessorMethod
+ .invoke(bean);
}
/**
* Method called to handle a direct self-reference through this property.
- * Method can choose to indicate an error by throwing {@link JsonMappingException};
- * fully handle serialization (and return true); or indicate that it should be
- * serialized normally (return false).
- *<p>
+ * Method can choose to indicate an error by throwing
+ * {@link JsonMappingException}; fully handle serialization (and return
+ * true); or indicate that it should be serialized normally (return false).
+ * <p>
* Default implementation will throw {@link JsonMappingException} if
- * {@link SerializationFeature#FAIL_ON_SELF_REFERENCES} is enabled;
- * or return <code>false</code> if it is disabled.
+ * {@link SerializationFeature#FAIL_ON_SELF_REFERENCES} is enabled; or
+ * return <code>false</code> if it is disabled.
*
- * @return True if method fully handled self-referential value; false if not (caller
- * is to handle it) or {@link JsonMappingException} if there is no way handle it
+ * @return True if method fully handled self-referential value; false if not
+ * (caller is to handle it) or {@link JsonMappingException} if there
+ * is no way handle it
*/
- protected boolean _handleSelfReference(Object bean, JsonGenerator gen, SerializerProvider prov, JsonSerializer<?> ser)
+ protected boolean _handleSelfReference(Object bean, JsonGenerator gen,
+ SerializerProvider prov, JsonSerializer<?> ser)
throws JsonMappingException {
if (prov.isEnabled(SerializationFeature.FAIL_ON_SELF_REFERENCES)
&& !ser.usesObjectId()) {
// 05-Feb-2013, tatu: Usually a problem, but NOT if we are handling
- // object id; this may be the case for BeanSerializers at least.
- // 13-Feb-2014, tatu: another possible ok case: custom serializer (something
- // OTHER than {@link BeanSerializerBase}
+ // object id; this may be the case for BeanSerializers at least.
+ // 13-Feb-2014, tatu: another possible ok case: custom serializer
+ // (something
+ // OTHER than {@link BeanSerializerBase}
if (ser instanceof BeanSerializerBase) {
- throw JsonMappingException.from(gen, "Direct self-reference leading to cycle");
+ prov.reportMappingProblem("Direct self-reference leading to cycle");
}
}
return false;
@@ -887,16 +929,20 @@
StringBuilder sb = new StringBuilder(40);
sb.append("property '").append(getName()).append("' (");
if (_accessorMethod != null) {
- sb.append("via method ").append(_accessorMethod.getDeclaringClass().getName()).append("#").append(_accessorMethod.getName());
+ sb.append("via method ")
+ .append(_accessorMethod.getDeclaringClass().getName())
+ .append("#").append(_accessorMethod.getName());
} else if (_field != null) {
- sb.append("field \"").append(_field.getDeclaringClass().getName()).append("#").append(_field.getName());
+ sb.append("field \"").append(_field.getDeclaringClass().getName())
+ .append("#").append(_field.getName());
} else {
sb.append("virtual");
}
if (_serializer == null) {
sb.append(", no static serializer");
} else {
- sb.append(", static serializer of type "+_serializer.getClass().getName());
+ sb.append(", static serializer of type "
+ + _serializer.getClass().getName());
}
sb.append(')');
return sb.toString();
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializer.java
index c309bba..7dc0d17 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializer.java
@@ -1,9 +1,9 @@
package com.fasterxml.jackson.databind.ser;
import java.io.IOException;
+import java.util.Set;
import com.fasterxml.jackson.core.JsonGenerator;
-
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.ser.impl.BeanAsArraySerializer;
import com.fasterxml.jackson.databind.ser.impl.ObjectIdWriter;
@@ -64,7 +64,7 @@
super(src, objectIdWriter, filterId);
}
- protected BeanSerializer(BeanSerializerBase src, String[] toIgnore) {
+ protected BeanSerializer(BeanSerializerBase src, Set<String> toIgnore) {
super(src, toIgnore);
}
@@ -99,7 +99,7 @@
}
@Override
- protected BeanSerializerBase withIgnorals(String[] toIgnore) {
+ protected BeanSerializerBase withIgnorals(Set<String> toIgnore) {
return new BeanSerializer(this, toIgnore);
}
@@ -148,9 +148,7 @@
_serializeWithObjectId(bean, gen, provider, true);
return;
}
- gen.writeStartObject();
- // [databind#631]: Assign current value, to be accessible by custom serializers
- gen.setCurrentValue(bean);
+ gen.writeStartObject(bean);
if (_propertyFilterId != null) {
serializeFieldsFiltered(bean, gen, provider);
} else {
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerBuilder.java b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerBuilder.java
index d4aa8e0..95613e1 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerBuilder.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerBuilder.java
@@ -179,11 +179,24 @@
properties = NO_PROPERTIES;
} else {
properties = _properties.toArray(new BeanPropertyWriter[_properties.size()]);
+ if (_config.isEnabled(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS)) {
+ for (int i = 0, end = properties.length; i < end; ++i) {
+ properties[i].fixAccess(_config);
+ }
+ }
+ }
+ if (_anyGetter != null) {
+ _anyGetter.fixAccess(_config);
+ }
+ if (_typeId != null) {
+ if (_config.isEnabled(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS)) {
+ _typeId.fixAccess(_config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
+ }
}
return new BeanSerializer(_beanDesc.getType(), this,
properties, _filteredProperties);
}
-
+
/**
* Factory method for constructing an "empty" serializer; one that
* outputs no properties (but handles JSON objects properly, including
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java
index 4e3760c..921d67e 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java
@@ -3,10 +3,13 @@
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.ObjectIdGenerator;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
+
import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.cfg.ConfigOverride;
import com.fasterxml.jackson.databind.cfg.SerializerFactoryConfig;
import com.fasterxml.jackson.databind.introspect.*;
import com.fasterxml.jackson.databind.jsontype.NamedType;
@@ -19,7 +22,6 @@
import com.fasterxml.jackson.databind.ser.std.MapSerializer;
import com.fasterxml.jackson.databind.ser.std.StdDelegatingSerializer;
import com.fasterxml.jackson.databind.type.ReferenceType;
-import com.fasterxml.jackson.databind.util.ArrayBuilders;
import com.fasterxml.jackson.databind.util.ClassUtil;
import com.fasterxml.jackson.databind.util.Converter;
@@ -141,8 +143,17 @@
boolean staticTyping;
// Next: we may have annotations that further indicate actual type to use (a super type)
final AnnotationIntrospector intr = config.getAnnotationIntrospector();
- JavaType type = (intr == null) ? origType
- : intr.refineSerializationType(config, beanDesc.getClassInfo(), origType);
+ JavaType type;
+
+ if (intr == null) {
+ type = origType;
+ } else {
+ try {
+ type = intr.refineSerializationType(config, beanDesc.getClassInfo(), origType);
+ } catch (JsonMappingException e) {
+ return prov.reportBadTypeDefinition(beanDesc, e.getMessage());
+ }
+ }
if (type == origType) { // no changes, won't force static typing
staticTyping = false;
} else { // changes; assume static typing; plus, need to re-introspect if class differs
@@ -405,7 +416,7 @@
// Any properties to suppress?
props = filterBeanProperties(config, beanDesc, props);
- // [JACKSON-440] Need to allow reordering of properties to serialize
+ // Need to allow reordering of properties to serialize
if (_factoryConfig.hasSerializerModifiers()) {
for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {
props = mod.orderProperties(config, beanDesc, props);
@@ -423,21 +434,18 @@
AnnotatedMember anyGetter = beanDesc.findAnyGetter();
if (anyGetter != null) {
- if (config.canOverrideAccessModifiers()) {
- anyGetter.fixAccess(config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
- }
JavaType type = anyGetter.getType();
// copied from BasicSerializerFactory.buildMapSerializer():
boolean staticTyping = config.isEnabled(MapperFeature.USE_STATIC_TYPING);
JavaType valueType = type.getContentType();
TypeSerializer typeSer = createTypeSerializer(config, valueType);
// last 2 nulls; don't know key, value serializers (yet)
- // 23-Feb-2015, tatu: As per [#705], need to support custom serializers
+ // 23-Feb-2015, tatu: As per [databind#705], need to support custom serializers
JsonSerializer<?> anySer = findSerializerFromAnnotation(prov, anyGetter);
if (anySer == null) {
// TODO: support '@JsonIgnoreProperties' with any setter?
- anySer = MapSerializer.construct(/* ignored props*/ null, type, staticTyping,
- typeSer, null, null, /*filterId*/ null);
+ anySer = MapSerializer.construct(/* ignored props*/ (Set<String>) null,
+ type, staticTyping, typeSer, null, null, /*filterId*/ null);
}
// TODO: can we find full PropertyName?
PropertyName name = PropertyName.construct(anyGetter.getName());
@@ -454,9 +462,8 @@
builder = mod.updateBuilder(config, beanDesc, builder);
}
}
-
+
JsonSerializer<Object> ser = (JsonSerializer<Object>) builder.build();
-
if (ser == null) {
// If we get this far, there were no properties found, so no regular BeanSerializer
// would be constructed. But, couple of exceptions.
@@ -568,7 +575,7 @@
List<BeanPropertyDefinition> properties = beanDesc.findProperties();
final SerializationConfig config = prov.getConfig();
- // [JACKSON-429]: ignore specified types
+ // ignore specified types
removeIgnorableTypes(config, beanDesc, properties);
// and possibly remove ones without matching mutator...
@@ -585,17 +592,11 @@
PropertyBuilder pb = constructPropertyBuilder(config, beanDesc);
ArrayList<BeanPropertyWriter> result = new ArrayList<BeanPropertyWriter>(properties.size());
- final boolean fixAccess = config.canOverrideAccessModifiers();
- final boolean forceAccess = fixAccess && config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS);
-
for (BeanPropertyDefinition property : properties) {
final AnnotatedMember accessor = property.getAccessor();
// Type id? Requires special handling:
if (property.isTypeId()) {
- if (accessor != null) { // only add if we can access... but otherwise?
- if (fixAccess) {
- accessor.fixAccess(forceAccess);
- }
+ if (accessor != null) {
builder.setTypeId(accessor);
}
continue;
@@ -627,15 +628,20 @@
protected List<BeanPropertyWriter> filterBeanProperties(SerializationConfig config,
BeanDescription beanDesc, List<BeanPropertyWriter> props)
{
- AnnotationIntrospector intr = config.getAnnotationIntrospector();
- AnnotatedClass ac = beanDesc.getClassInfo();
- String[] ignored = intr.findPropertiesToIgnore(ac, true);
- if (ignored != null && ignored.length > 0) {
- HashSet<String> ignoredSet = ArrayBuilders.arrayToSet(ignored);
- Iterator<BeanPropertyWriter> it = props.iterator();
- while (it.hasNext()) {
- if (ignoredSet.contains(it.next().getName())) {
- it.remove();
+ // 01-May-2016, tatu: Which base type to use here gets tricky, since
+ // it may often make most sense to use general type for overrides,
+ // but what we have here may be more specific impl type. But for now
+ // just use it as is.
+ JsonIgnoreProperties.Value ignorals = config.getDefaultPropertyIgnorals(beanDesc.getBeanClass(),
+ beanDesc.getClassInfo());
+ if (ignorals != null) {
+ Set<String> ignored = ignorals.findIgnoredForSerialization();
+ if (!ignored.isEmpty()) {
+ Iterator<BeanPropertyWriter> it = props.iterator();
+ while (it.hasNext()) {
+ if (ignored.contains(it.next().getName())) {
+ it.remove();
+ }
}
}
}
@@ -653,7 +659,7 @@
*/
protected void processViews(SerializationConfig config, BeanSerializerBuilder builder)
{
- // [JACKSON-232]: whether non-annotated fields are included by default or not is configurable
+ // whether non-annotated fields are included by default or not is configurable
List<BeanPropertyWriter> props = builder.getProperties();
boolean includeByDefault = config.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION);
final int propCount = props.size();
@@ -700,12 +706,19 @@
Class<?> type = accessor.getRawType();
Boolean result = ignores.get(type);
if (result == null) {
- BeanDescription desc = config.introspectClassAnnotations(type);
- AnnotatedClass ac = desc.getClassInfo();
- result = intr.isIgnorableType(ac);
- // default to false, non-ignorable
+ // 21-Apr-2016, tatu: For 2.8, can specify config overrides
+ ConfigOverride override = config.findConfigOverride(type);
+ if (override != null) {
+ result = override.getIsIgnoredType();
+ }
if (result == null) {
- result = Boolean.FALSE;
+ BeanDescription desc = config.introspectClassAnnotations(type);
+ AnnotatedClass ac = desc.getClassInfo();
+ result = intr.isIgnorableType(ac);
+ // default to false, non-ignorable
+ if (result == null) {
+ result = Boolean.FALSE;
+ }
}
ignores.put(type, result);
}
@@ -725,7 +738,7 @@
Iterator<BeanPropertyDefinition> it = properties.iterator();
while (it.hasNext()) {
BeanPropertyDefinition property = it.next();
- // one caveat: as per [JACKSON-806], only remove implicit properties;
+ // one caveat: only remove implicit properties;
// explicitly annotated ones should remain
if (!property.couldDeserialize() && !property.isExplicitlyIncluded()) {
it.remove();
@@ -778,9 +791,6 @@
throws JsonMappingException
{
final PropertyName name = propDef.getFullName();
- if (prov.canOverrideAccessModifiers()) {
- accessor.fixAccess(prov.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
- }
JavaType type = accessor.getType();
BeanProperty.Std property = new BeanProperty.Std(name, type, propDef.getWrapperName(),
pb.getClassAnnotations(), accessor, propDef.getMetadata());
@@ -803,8 +813,7 @@
}
// and if not JAXB collection/array with annotations, maybe regular type info?
TypeSerializer typeSer = findPropertyTypeSerializer(type, prov.getConfig(), accessor);
- BeanPropertyWriter pbw = pb.buildWriter(prov, propDef, type, annotatedSerializer,
+ return pb.buildWriter(prov, propDef, type, annotatedSerializer,
typeSer, contentTypeSer, accessor, staticTyping);
- return pbw;
}
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/DefaultSerializerProvider.java b/src/main/java/com/fasterxml/jackson/databind/ser/DefaultSerializerProvider.java
index b4984f3..ed8e965 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/DefaultSerializerProvider.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/DefaultSerializerProvider.java
@@ -36,7 +36,7 @@
/*
/**********************************************************
- /* State, for non-blueprint instances: Object Id handling
+ /* State, for non-blueprint instances
/**********************************************************
*/
@@ -47,7 +47,15 @@
protected transient Map<Object, WritableObjectId> _seenObjectIds;
protected transient ArrayList<ObjectIdGenerator<?>> _objectIdGenerators;
-
+
+ /**
+ * Generator used for serialization. Needed mostly for error reporting
+ * purposes.
+ *
+ * @since 2.8
+ */
+ protected transient JsonGenerator _generator;
+
/*
/**********************************************************
/* Life-cycle
@@ -66,6 +74,14 @@
}
/**
+ * Method that sub-classes need to implement: used to create a non-blueprint instances
+ * from the blueprint.
+ * This is needed to retain state during serialization.
+ */
+ public abstract DefaultSerializerProvider createInstance(SerializationConfig config,
+ SerializerFactory jsf);
+
+ /**
* Method needed to ensure that {@link ObjectMapper#copy} will work
* properly; specifically, that caches are cleared, but settings
* will otherwise remain identical; and that no sharing of state
@@ -76,402 +92,48 @@
public DefaultSerializerProvider copy() {
throw new IllegalStateException("DefaultSerializerProvider sub-class not overriding copy()");
}
-
+
/*
/**********************************************************
- /* Extended API: methods that ObjectMapper will call
+ /* Abstract method impls, factory methods
/**********************************************************
*/
-
- /**
- * Overridable method, used to create a non-blueprint instances from the blueprint.
- * This is needed to retain state during serialization.
- */
- public abstract DefaultSerializerProvider createInstance(SerializationConfig config,
- SerializerFactory jsf);
-
- /**
- * The method to be called by {@link ObjectMapper} and {@link ObjectWriter}
- * for serializing given value, using serializers that
- * this provider has access to (via caching and/or creating new serializers
- * as need be).
- */
- public void serializeValue(JsonGenerator gen, Object value) throws IOException
- {
- if (value == null) {
- _serializeNull(gen);
- return;
- }
- Class<?> cls = value.getClass();
- // true, since we do want to cache root-level typed serializers (ditto for null property)
- final JsonSerializer<Object> ser = findTypedValueSerializer(cls, true, null);
-
- // Ok: should we wrap result in an additional property ("root name")?
- final boolean wrap;
- PropertyName rootName = _config.getFullRootName();
-
- if (rootName == null) { // not explicitly specified
- // [JACKSON-163]
- wrap = _config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE);
- if (wrap) {
- gen.writeStartObject();
- PropertyName pname = _config.findRootName(value.getClass());
- gen.writeFieldName(pname.simpleAsEncoded(_config));
- }
- } else if (rootName.isEmpty()) {
- wrap = false;
- } else { // [JACKSON-764]
- // empty String means explicitly disabled; non-empty that it is enabled
- wrap = true;
- gen.writeStartObject();
- gen.writeFieldName(rootName.getSimpleName());
- }
- try {
- ser.serialize(value, gen, this);
- if (wrap) {
- gen.writeEndObject();
- }
- } catch (IOException ioe) { // As per [JACKSON-99], pass IOException and subtypes as-is
- throw ioe;
- } catch (Exception e) { // but wrap RuntimeExceptions, to get path information
- String msg = e.getMessage();
- if (msg == null) {
- msg = "[no message for "+e.getClass().getName()+"]";
- }
- throw new JsonMappingException(gen, msg, e);
- }
- }
-
- /**
- * The method to be called by {@link ObjectMapper} and {@link ObjectWriter}
- * for serializing given value (assumed to be of specified root type,
- * instead of runtime type of value),
- * using serializers that
- * this provider has access to (via caching and/or creating new serializers
- * as need be),
- *
- * @param rootType Type to use for locating serializer to use, instead of actual
- * runtime type. Must be actual type, or one of its super types
- */
- public void serializeValue(JsonGenerator gen, Object value, JavaType rootType) throws IOException
- {
- if (value == null) {
- _serializeNull(gen);
- return;
- }
- // Let's ensure types are compatible at this point
- if (!rootType.getRawClass().isAssignableFrom(value.getClass())) {
- _reportIncompatibleRootType(value, rootType);
- }
- // root value, not reached via property:
- JsonSerializer<Object> ser = findTypedValueSerializer(rootType, true, null);
-
- // Ok: should we wrap result in an additional property ("root name")?
- final boolean wrap;
- PropertyName rootName = _config.getFullRootName();
- if (rootName == null) { // not explicitly specified
- // [JACKSON-163]
- wrap = _config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE);
- if (wrap) {
- gen.writeStartObject();
- PropertyName pname = _config.findRootName(value.getClass());
- gen.writeFieldName(pname.simpleAsEncoded(_config));
- }
- } else if (rootName.isEmpty()) {
- wrap = false;
- } else { // [JACKSON-764]
- // empty String means explicitly disabled; non-empty that it is enabled
- wrap = true;
- gen.writeStartObject();
- gen.writeFieldName(rootName.getSimpleName());
- }
- try {
- ser.serialize(value, gen, this);
- if (wrap) {
- gen.writeEndObject();
- }
- } catch (IOException ioe) { // no wrapping for IO (and derived)
- throw ioe;
- } catch (Exception e) { // but others do need to be, to get path etc
- String msg = e.getMessage();
- if (msg == null) {
- msg = "[no message for "+e.getClass().getName()+"]";
- }
- throw JsonMappingException.from(gen, msg, e);
- }
- }
-
- /**
- * The method to be called by {@link ObjectWriter}
- * for serializing given value (assumed to be of specified root type,
- * instead of runtime type of value), when it may know specific
- * {@link JsonSerializer} to use.
- *
- * @param rootType Type to use for locating serializer to use, instead of actual
- * runtime type, if no serializer is passed
- * @param ser Root Serializer to use, if not null
- *
- * @since 2.1
- */
- public void serializeValue(JsonGenerator gen, Object value, JavaType rootType,
- JsonSerializer<Object> ser) throws IOException
- {
- if (value == null) {
- _serializeNull(gen);
- return;
- }
- // Let's ensure types are compatible at this point
- if ((rootType != null) && !rootType.getRawClass().isAssignableFrom(value.getClass())) {
- _reportIncompatibleRootType(value, rootType);
- }
- // root value, not reached via property:
- if (ser == null) {
- ser = findTypedValueSerializer(rootType, true, null);
- }
- // Ok: should we wrap result in an additional property ("root name")?
- final boolean wrap;
- PropertyName rootName = _config.getFullRootName();
- if (rootName == null) { // not explicitly specified
- // [JACKSON-163]
- wrap = _config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE);
- if (wrap) {
- gen.writeStartObject();
- PropertyName pname = (rootType == null)
- ? _config.findRootName(value.getClass())
- : _config.findRootName(rootType);
- gen.writeFieldName(pname.simpleAsEncoded(_config));
- }
- } else if (rootName.isEmpty()) {
- wrap = false;
- } else { // [JACKSON-764]
- // empty String means explicitly disabled; non-empty that it is enabled
- wrap = true;
- gen.writeStartObject();
- gen.writeFieldName(rootName.getSimpleName());
- }
- try {
- ser.serialize(value, gen, this);
- if (wrap) {
- gen.writeEndObject();
- }
- } catch (IOException ioe) { // no wrapping for IO (and derived)
- throw ioe;
- } catch (Exception e) { // but others do need to be, to get path etc
- String msg = e.getMessage();
- if (msg == null) {
- msg = "[no message for "+e.getClass().getName()+"]";
- }
- throw JsonMappingException.from(gen, msg, e);
- }
- }
-
- /**
- * Alternate serialization call used for polymorphic types, when {@link TypeSerializer}
- * is already known, but the actual serializer may or may not be.
- *
- * @since 2.6
- */
- public void serializePolymorphic(JsonGenerator gen, Object value, JavaType rootType,
- JsonSerializer<Object> valueSer, TypeSerializer typeSer)
- throws IOException
- {
- if (value == null) {
- _serializeNull(gen);
- return;
- }
- // Let's ensure types are compatible at this point
- if ((rootType != null) && !rootType.getRawClass().isAssignableFrom(value.getClass())) {
- _reportIncompatibleRootType(value, rootType);
- }
- /* 12-Jun-2015, tatu: nominal root type is necessary for Maps at least;
- * possibly collections, but can cause problems for other polymorphic
- * types. We really need to distinguish between serialization type,
- * base type; but right we don't. Hence this check
- */
- if (valueSer == null) {
- if ((rootType != null) && rootType.isContainerType()) {
- valueSer = findValueSerializer(rootType, null);
- } else {
- valueSer = findValueSerializer(value.getClass(), null);
- }
- }
-
- final boolean wrap;
- PropertyName rootName = _config.getFullRootName();
- if (rootName == null) {
- wrap = _config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE);
- if (wrap) {
- gen.writeStartObject();
- PropertyName pname = _config.findRootName(value.getClass());
- gen.writeFieldName(pname.simpleAsEncoded(_config));
- }
- } else if (rootName.isEmpty()) {
- wrap = false;
- } else {
- wrap = true;
- gen.writeStartObject();
- gen.writeFieldName(rootName.getSimpleName());
- }
- try {
- valueSer.serializeWithType(value, gen, this, typeSer);
- if (wrap) {
- gen.writeEndObject();
- }
- } catch (IOException ioe) { // no wrapping for IO (and derived)
- throw ioe;
- } catch (Exception e) { // but others do need to be, to get path etc
- String msg = e.getMessage();
- if (msg == null) {
- msg = "[no message for "+e.getClass().getName()+"]";
- }
- throw JsonMappingException.from(gen, msg, e);
- }
- }
-
- /**
- * @deprecated since 2.6; remove from 2.7 or later
- */
- @Deprecated
- public void serializePolymorphic(JsonGenerator gen, Object value, TypeSerializer typeSer)
- throws IOException
- {
- JavaType t = (value == null) ? null : _config.constructType(value.getClass());
- serializePolymorphic(gen, value, t, null, typeSer);
- }
-
- /**
- * Helper method called when root value to serialize is null
- *
- * @since 2.3
- */
- protected void _serializeNull(JsonGenerator gen) throws IOException
- {
- JsonSerializer<Object> ser = getDefaultNullValueSerializer();
- try {
- ser.serialize(null, gen, this);
- } catch (IOException ioe) { // no wrapping for IO (and derived)
- throw ioe;
- } catch (Exception e) { // but others do need to be, to get path etc
- String msg = e.getMessage();
- if (msg == null) {
- msg = "[no message for "+e.getClass().getName()+"]";
- }
- throw JsonMappingException.from(gen, msg, e);
- }
- }
-
- /**
- * The method to be called by {@link ObjectMapper}
- * to generate <a href="http://json-schema.org/">JSON schema</a> for
- * given type.
- *
- * @param type The type for which to generate schema
- *
- * @deprecated Should not be used any more
- */
- @Deprecated // since 2.6
- public com.fasterxml.jackson.databind.jsonschema.JsonSchema generateJsonSchema(Class<?> type)
- throws JsonMappingException
- {
- if (type == null) {
- throw new IllegalArgumentException("A class must be provided");
- }
- /* no need for embedded type information for JSON schema generation (all
- * type information it needs is accessible via "untyped" serializer)
- */
- JsonSerializer<Object> ser = findValueSerializer(type, null);
- JsonNode schemaNode = (ser instanceof SchemaAware) ?
- ((SchemaAware) ser).getSchema(this, null) : com.fasterxml.jackson.databind.jsonschema.JsonSchema.getDefaultSchemaNode();
- if (!(schemaNode instanceof ObjectNode)) {
- throw new IllegalArgumentException("Class " + type.getName()
- +" would not be serialized as a JSON object and therefore has no schema");
- }
- return new com.fasterxml.jackson.databind.jsonschema.JsonSchema((ObjectNode) schemaNode);
- }
- /**
- * The method to be called by {@link ObjectMapper} and {@link ObjectWriter}
- * to to expose the format of the given to to the given visitor
- *
- * @param javaType The type for which to generate format
- * @param visitor the visitor to accept the format
- */
- public void acceptJsonFormatVisitor(JavaType javaType, JsonFormatVisitorWrapper visitor)
- throws JsonMappingException
+ @Override
+ public JsonSerializer<Object> serializerInstance(Annotated annotated, Object serDef) throws JsonMappingException
{
- if (javaType == null) {
- throw new IllegalArgumentException("A class must be provided");
+ if (serDef == null) {
+ return null;
}
- /* no need for embedded type information for JSON schema generation (all
- * type information it needs is accessible via "untyped" serializer)
- */
- visitor.setProvider(this);
- findValueSerializer(javaType, null).acceptJsonFormatVisitor(visitor, javaType);
- }
-
- /**
- * Method that can be called to see if this serializer provider
- * can find a serializer for an instance of given class.
- *<p>
- * Note that no Exceptions are thrown, including unchecked ones:
- * implementations are to swallow exceptions if necessary.
- */
- public boolean hasSerializerFor(Class<?> cls, AtomicReference<Throwable> cause)
- {
- // 07-Nov-2015, tatu: One special case, Object.class; will work only if
- // empty beans are allowed or custom serializer registered. Easiest to
- // check here.
- if (cls == Object.class) {
- if (!_config.isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS)) {
- return true;
- }
- }
+ JsonSerializer<?> ser;
- try {
- JsonSerializer<?> ser = _findExplicitUntypedSerializer(cls);
- return (ser != null);
- } catch (JsonMappingException e) {
- if (cause != null) {
- cause.set(e);
+ if (serDef instanceof JsonSerializer) {
+ ser = (JsonSerializer<?>) serDef;
+ } else {
+ /* Alas, there's no way to force return type of "either class
+ * X or Y" -- need to throw an exception after the fact
+ */
+ if (!(serDef instanceof Class)) {
+ throw new IllegalStateException("AnnotationIntrospector returned serializer definition of type "
+ +serDef.getClass().getName()+"; expected type JsonSerializer or Class<JsonSerializer> instead");
}
- } catch (RuntimeException e) {
- if (cause == null) { // earlier behavior
- throw e;
+ Class<?> serClass = (Class<?>)serDef;
+ // there are some known "no class" markers to consider too:
+ if (serClass == JsonSerializer.None.class || ClassUtil.isBogusClass(serClass)) {
+ return null;
}
- cause.set(e);
+ if (!JsonSerializer.class.isAssignableFrom(serClass)) {
+ throw new IllegalStateException("AnnotationIntrospector returned Class "
+ +serClass.getName()+"; expected Class<JsonSerializer>");
+ }
+ HandlerInstantiator hi = _config.getHandlerInstantiator();
+ ser = (hi == null) ? null : hi.serializerInstance(_config, annotated, serClass);
+ if (ser == null) {
+ ser = (JsonSerializer<?>) ClassUtil.createInstance(serClass,
+ _config.canOverrideAccessModifiers());
+ }
}
- return false;
- }
-
- /*
- /********************************************************
- /* Access to caching details
- /********************************************************
- */
-
- /**
- * Method that can be used to determine how many serializers this
- * provider is caching currently
- * (if it does caching: default implementation does)
- * Exact count depends on what kind of serializers get cached;
- * default implementation caches all serializers, including ones that
- * are eagerly constructed (for optimal access speed)
- *<p>
- * The main use case for this method is to allow conditional flushing of
- * serializer cache, if certain number of entries is reached.
- */
- public int cachedSerializersCount() {
- return _serializerCache.size();
- }
-
- /**
- * Method that will drop all serializers currently cached by this provider.
- * This can be used to remove memory usage (in case some serializers are
- * only used once or so), or to force re-construction of serializers after
- * configuration changes for mapper than owns the provider.
- */
- public void flushCachedSerializers() {
- _serializerCache.flush();
+ return (JsonSerializer<Object>) _handleResolvable(ser);
}
/*
@@ -531,50 +193,423 @@
}
return new IdentityHashMap<Object,WritableObjectId>();
}
-
+
/*
/**********************************************************
- /* Factory method impls
+ /* Extended API: simple accesors
+ /**********************************************************
+ */
+
+ /**
+ * Method that can be called to see if this serializer provider
+ * can find a serializer for an instance of given class.
+ *<p>
+ * Note that no Exceptions are thrown, including unchecked ones:
+ * implementations are to swallow exceptions if necessary.
+ */
+ public boolean hasSerializerFor(Class<?> cls, AtomicReference<Throwable> cause)
+ {
+ // 07-Nov-2015, tatu: One special case, Object.class; will work only if
+ // empty beans are allowed or custom serializer registered. Easiest to
+ // check here.
+ if (cls == Object.class) {
+ if (!_config.isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS)) {
+ return true;
+ }
+ }
+
+ try {
+ JsonSerializer<?> ser = _findExplicitUntypedSerializer(cls);
+ return (ser != null);
+ } catch (JsonMappingException e) {
+ if (cause != null) {
+ cause.set(e);
+ }
+ } catch (RuntimeException e) {
+ if (cause == null) { // earlier behavior
+ throw e;
+ }
+ cause.set(e);
+ }
+ return false;
+ }
+
+ /**
+ * Accessor for the {@link JsonGenerator} currently in use for serializing
+ * content. Null for blueprint instances; non-null for actual active
+ * provider instances.
+ *
+ * @since 2.8
+ */
+ @Override
+ public JsonGenerator getGenerator() {
+ return _generator;
+ }
+
+ /*
+ /**********************************************************
+ /* Extended API called by ObjectMapper: value serialization
/**********************************************************
*/
- @Override
- public JsonSerializer<Object> serializerInstance(Annotated annotated, Object serDef) throws JsonMappingException
+ /**
+ * The method to be called by {@link ObjectMapper} and {@link ObjectWriter}
+ * for serializing given value, using serializers that
+ * this provider has access to (via caching and/or creating new serializers
+ * as need be).
+ */
+ public void serializeValue(JsonGenerator gen, Object value) throws IOException
{
- if (serDef == null) {
- return null;
+ _generator = gen;
+ if (value == null) {
+ _serializeNull(gen);
+ return;
}
- JsonSerializer<?> ser;
-
- if (serDef instanceof JsonSerializer) {
- ser = (JsonSerializer<?>) serDef;
- } else {
- /* Alas, there's no way to force return type of "either class
- * X or Y" -- need to throw an exception after the fact
- */
- if (!(serDef instanceof Class)) {
- throw new IllegalStateException("AnnotationIntrospector returned serializer definition of type "
- +serDef.getClass().getName()+"; expected type JsonSerializer or Class<JsonSerializer> instead");
+ Class<?> cls = value.getClass();
+ // true, since we do want to cache root-level typed serializers (ditto for null property)
+ final JsonSerializer<Object> ser = findTypedValueSerializer(cls, true, null);
+
+ // Ok: should we wrap result in an additional property ("root name")?
+ final boolean wrap;
+ PropertyName rootName = _config.getFullRootName();
+
+ if (rootName == null) { // not explicitly specified
+ wrap = _config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE);
+ if (wrap) {
+ gen.writeStartObject();
+ PropertyName pname = _config.findRootName(value.getClass());
+ gen.writeFieldName(pname.simpleAsEncoded(_config));
}
- Class<?> serClass = (Class<?>)serDef;
- // there are some known "no class" markers to consider too:
- if (serClass == JsonSerializer.None.class || ClassUtil.isBogusClass(serClass)) {
- return null;
- }
- if (!JsonSerializer.class.isAssignableFrom(serClass)) {
- throw new IllegalStateException("AnnotationIntrospector returned Class "
- +serClass.getName()+"; expected Class<JsonSerializer>");
- }
- HandlerInstantiator hi = _config.getHandlerInstantiator();
- ser = (hi == null) ? null : hi.serializerInstance(_config, annotated, serClass);
- if (ser == null) {
- ser = (JsonSerializer<?>) ClassUtil.createInstance(serClass,
- _config.canOverrideAccessModifiers());
- }
+ } else if (rootName.isEmpty()) {
+ wrap = false;
+ } else { // [JACKSON-764]
+ // empty String means explicitly disabled; non-empty that it is enabled
+ wrap = true;
+ gen.writeStartObject();
+ gen.writeFieldName(rootName.getSimpleName());
}
- return (JsonSerializer<Object>) _handleResolvable(ser);
+ try {
+ ser.serialize(value, gen, this);
+ if (wrap) {
+ gen.writeEndObject();
+ }
+ } catch (IOException ioe) { // As per [JACKSON-99], pass IOException and subtypes as-is
+ throw ioe;
+ } catch (Exception e) { // but wrap RuntimeExceptions, to get path information
+ String msg = e.getMessage();
+ if (msg == null) {
+ msg = "[no message for "+e.getClass().getName()+"]";
+ }
+ throw new JsonMappingException(gen, msg, e);
+ }
}
+ /**
+ * The method to be called by {@link ObjectMapper} and {@link ObjectWriter}
+ * for serializing given value (assumed to be of specified root type,
+ * instead of runtime type of value),
+ * using serializers that
+ * this provider has access to (via caching and/or creating new serializers
+ * as need be),
+ *
+ * @param rootType Type to use for locating serializer to use, instead of actual
+ * runtime type. Must be actual type, or one of its super types
+ */
+ public void serializeValue(JsonGenerator gen, Object value, JavaType rootType) throws IOException
+ {
+ _generator = gen;
+ if (value == null) {
+ _serializeNull(gen);
+ return;
+ }
+ // Let's ensure types are compatible at this point
+ if (!rootType.getRawClass().isAssignableFrom(value.getClass())) {
+ _reportIncompatibleRootType(value, rootType);
+ }
+ // root value, not reached via property:
+ JsonSerializer<Object> ser = findTypedValueSerializer(rootType, true, null);
+
+ // Ok: should we wrap result in an additional property ("root name")?
+ final boolean wrap;
+ PropertyName rootName = _config.getFullRootName();
+ if (rootName == null) { // not explicitly specified
+ wrap = _config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE);
+ if (wrap) {
+ gen.writeStartObject();
+ PropertyName pname = _config.findRootName(value.getClass());
+ gen.writeFieldName(pname.simpleAsEncoded(_config));
+ }
+ } else if (rootName.isEmpty()) {
+ wrap = false;
+ } else {
+ // empty String means explicitly disabled; non-empty that it is enabled
+ wrap = true;
+ gen.writeStartObject();
+ gen.writeFieldName(rootName.getSimpleName());
+ }
+ try {
+ ser.serialize(value, gen, this);
+ if (wrap) {
+ gen.writeEndObject();
+ }
+ } catch (IOException ioe) { // no wrapping for IO (and derived)
+ throw ioe;
+ } catch (Exception e) { // but others do need to be, to get path etc
+ String msg = e.getMessage();
+ if (msg == null) {
+ msg = "[no message for "+e.getClass().getName()+"]";
+ }
+ reportMappingProblem(e, msg);
+ }
+ }
+
+ /**
+ * The method to be called by {@link ObjectWriter}
+ * for serializing given value (assumed to be of specified root type,
+ * instead of runtime type of value), when it may know specific
+ * {@link JsonSerializer} to use.
+ *
+ * @param rootType Type to use for locating serializer to use, instead of actual
+ * runtime type, if no serializer is passed
+ * @param ser Root Serializer to use, if not null
+ *
+ * @since 2.1
+ */
+ public void serializeValue(JsonGenerator gen, Object value, JavaType rootType,
+ JsonSerializer<Object> ser) throws IOException
+ {
+ _generator = gen;
+ if (value == null) {
+ _serializeNull(gen);
+ return;
+ }
+ // Let's ensure types are compatible at this point
+ if ((rootType != null) && !rootType.getRawClass().isAssignableFrom(value.getClass())) {
+ _reportIncompatibleRootType(value, rootType);
+ }
+ // root value, not reached via property:
+ if (ser == null) {
+ ser = findTypedValueSerializer(rootType, true, null);
+ }
+ // Ok: should we wrap result in an additional property ("root name")?
+ final boolean wrap;
+ PropertyName rootName = _config.getFullRootName();
+ if (rootName == null) { // not explicitly specified
+ // [JACKSON-163]
+ wrap = _config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE);
+ if (wrap) {
+ gen.writeStartObject();
+ PropertyName pname = (rootType == null)
+ ? _config.findRootName(value.getClass())
+ : _config.findRootName(rootType);
+ gen.writeFieldName(pname.simpleAsEncoded(_config));
+ }
+ } else if (rootName.isEmpty()) {
+ wrap = false;
+ } else { // [JACKSON-764]
+ // empty String means explicitly disabled; non-empty that it is enabled
+ wrap = true;
+ gen.writeStartObject();
+ gen.writeFieldName(rootName.getSimpleName());
+ }
+ try {
+ ser.serialize(value, gen, this);
+ if (wrap) {
+ gen.writeEndObject();
+ }
+ } catch (IOException ioe) { // no wrapping for IO (and derived)
+ throw ioe;
+ } catch (Exception e) { // but others do need to be, to get path etc
+ String msg = e.getMessage();
+ if (msg == null) {
+ msg = "[no message for "+e.getClass().getName()+"]";
+ }
+ reportMappingProblem(e, msg);
+ }
+ }
+
+ /**
+ * Alternate serialization call used for polymorphic types, when {@link TypeSerializer}
+ * is already known, but the actual serializer may or may not be.
+ *
+ * @since 2.6
+ */
+ public void serializePolymorphic(JsonGenerator gen, Object value, JavaType rootType,
+ JsonSerializer<Object> valueSer, TypeSerializer typeSer)
+ throws IOException
+ {
+ _generator = gen;
+ if (value == null) {
+ _serializeNull(gen);
+ return;
+ }
+ // Let's ensure types are compatible at this point
+ if ((rootType != null) && !rootType.getRawClass().isAssignableFrom(value.getClass())) {
+ _reportIncompatibleRootType(value, rootType);
+ }
+ /* 12-Jun-2015, tatu: nominal root type is necessary for Maps at least;
+ * possibly collections, but can cause problems for other polymorphic
+ * types. We really need to distinguish between serialization type,
+ * base type; but right we don't. Hence this check
+ */
+ if (valueSer == null) {
+ if ((rootType != null) && rootType.isContainerType()) {
+ valueSer = findValueSerializer(rootType, null);
+ } else {
+ valueSer = findValueSerializer(value.getClass(), null);
+ }
+ }
+
+ final boolean wrap;
+ PropertyName rootName = _config.getFullRootName();
+ if (rootName == null) {
+ wrap = _config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE);
+ if (wrap) {
+ gen.writeStartObject();
+ PropertyName pname = _config.findRootName(value.getClass());
+ gen.writeFieldName(pname.simpleAsEncoded(_config));
+ }
+ } else if (rootName.isEmpty()) {
+ wrap = false;
+ } else {
+ wrap = true;
+ gen.writeStartObject();
+ gen.writeFieldName(rootName.getSimpleName());
+ }
+ try {
+ valueSer.serializeWithType(value, gen, this, typeSer);
+ if (wrap) {
+ gen.writeEndObject();
+ }
+ } catch (IOException ioe) { // no wrapping for IO (and derived)
+ throw ioe;
+ } catch (Exception e) { // but others do need to be, to get path etc
+ String msg = e.getMessage();
+ if (msg == null) {
+ msg = "[no message for "+e.getClass().getName()+"]";
+ }
+ reportMappingProblem(e, msg);
+ }
+ }
+
+ /**
+ * @deprecated since 2.6; remove from 2.7 or later
+ */
+ @Deprecated
+ public void serializePolymorphic(JsonGenerator gen, Object value, TypeSerializer typeSer)
+ throws IOException
+ {
+ JavaType t = (value == null) ? null : _config.constructType(value.getClass());
+ serializePolymorphic(gen, value, t, null, typeSer);
+ }
+
+ /**
+ * Helper method called when root value to serialize is null
+ *
+ * @since 2.3
+ */
+ protected void _serializeNull(JsonGenerator gen) throws IOException
+ {
+ JsonSerializer<Object> ser = getDefaultNullValueSerializer();
+ try {
+ ser.serialize(null, gen, this);
+ } catch (IOException ioe) { // no wrapping for IO (and derived)
+ throw ioe;
+ } catch (Exception e) { // but others do need to be, to get path etc
+ String msg = e.getMessage();
+ if (msg == null) {
+ msg = "[no message for "+e.getClass().getName()+"]";
+ }
+ reportMappingProblem(e, msg);
+ }
+ }
+ /*
+ /********************************************************
+ /* Access to caching details
+ /********************************************************
+ */
+
+ /**
+ * Method that can be used to determine how many serializers this
+ * provider is caching currently
+ * (if it does caching: default implementation does)
+ * Exact count depends on what kind of serializers get cached;
+ * default implementation caches all serializers, including ones that
+ * are eagerly constructed (for optimal access speed)
+ *<p>
+ * The main use case for this method is to allow conditional flushing of
+ * serializer cache, if certain number of entries is reached.
+ */
+ public int cachedSerializersCount() {
+ return _serializerCache.size();
+ }
+
+ /**
+ * Method that will drop all serializers currently cached by this provider.
+ * This can be used to remove memory usage (in case some serializers are
+ * only used once or so), or to force re-construction of serializers after
+ * configuration changes for mapper than owns the provider.
+ */
+ public void flushCachedSerializers() {
+ _serializerCache.flush();
+ }
+
+ /*
+ /**********************************************************
+ /* Extended API called by ObjectMapper: other
+ /**********************************************************
+ */
+
+ /**
+ * The method to be called by {@link ObjectMapper} and {@link ObjectWriter}
+ * to to expose the format of the given to to the given visitor
+ *
+ * @param javaType The type for which to generate format
+ * @param visitor the visitor to accept the format
+ */
+ public void acceptJsonFormatVisitor(JavaType javaType, JsonFormatVisitorWrapper visitor)
+ throws JsonMappingException
+ {
+ if (javaType == null) {
+ throw new IllegalArgumentException("A class must be provided");
+ }
+ /* no need for embedded type information for JSON schema generation (all
+ * type information it needs is accessible via "untyped" serializer)
+ */
+ visitor.setProvider(this);
+ findValueSerializer(javaType, null).acceptJsonFormatVisitor(visitor, javaType);
+ }
+
+ /**
+ * The method to be called by {@link ObjectMapper}
+ * to generate <a href="http://json-schema.org/">JSON schema</a> for
+ * given type.
+ *
+ * @param type The type for which to generate schema
+ *
+ * @deprecated Should not be used any more
+ */
+ @Deprecated // since 2.6
+ public com.fasterxml.jackson.databind.jsonschema.JsonSchema generateJsonSchema(Class<?> type)
+ throws JsonMappingException
+ {
+ if (type == null) {
+ throw new IllegalArgumentException("A class must be provided");
+ }
+ /* no need for embedded type information for JSON schema generation (all
+ * type information it needs is accessible via "untyped" serializer)
+ */
+ JsonSerializer<Object> ser = findValueSerializer(type, null);
+ JsonNode schemaNode = (ser instanceof SchemaAware) ?
+ ((SchemaAware) ser).getSchema(this, null) : com.fasterxml.jackson.databind.jsonschema.JsonSchema.getDefaultSchemaNode();
+ if (!(schemaNode instanceof ObjectNode)) {
+ throw new IllegalArgumentException("Class " + type.getName()
+ +" would not be serialized as a JSON object and therefore has no schema");
+ }
+ return new com.fasterxml.jackson.databind.jsonschema.JsonSchema((ObjectNode) schemaNode);
+ }
+
+
/*
/**********************************************************
/* Helper classes
@@ -591,7 +626,8 @@
public Impl() { super(); }
public Impl(Impl src) { super(src); }
- protected Impl(SerializerProvider src, SerializationConfig config,SerializerFactory f) {
+ protected Impl(SerializerProvider src, SerializationConfig config,
+ SerializerFactory f) {
super(src, config, f);
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/PropertyBuilder.java b/src/main/java/com/fasterxml/jackson/databind/ser/PropertyBuilder.java
index f89612e..bb0c72e 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/PropertyBuilder.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/PropertyBuilder.java
@@ -16,17 +16,10 @@
{
// @since 2.7
private final static Object NO_DEFAULT_MARKER = Boolean.FALSE;
-
+
final protected SerializationConfig _config;
final protected BeanDescription _beanDesc;
- /**
- * Default inclusion mode for properties of the POJO for which
- * properties are collected; possibly overridden on
- * per-property basis.
- */
- final protected JsonInclude.Value _defaultInclusion;
-
final protected AnnotationIntrospector _annotationIntrospector;
/**
@@ -40,11 +33,41 @@
*/
protected Object _defaultBean;
+ /**
+ * Default inclusion mode for properties of the POJO for which
+ * properties are collected; possibly overridden on
+ * per-property basis. Combines global inclusion defaults and
+ * per-type (annotation and type-override) inclusion overrides.
+ */
+ final protected JsonInclude.Value _defaultInclusion;
+
+ /**
+ * Marker flag used to indicate that "real" default values are to be used
+ * for properties, as per per-type value inclusion of type <code>NON_DEFAULT</code>
+ *
+ * @since 2.8
+ */
+ final protected boolean _useRealPropertyDefaults;
+
public PropertyBuilder(SerializationConfig config, BeanDescription beanDesc)
{
_config = config;
_beanDesc = beanDesc;
- _defaultInclusion = beanDesc.findPropertyInclusion(config.getDefaultPropertyInclusion());
+ // 08-Sep-2016, tatu: This gets tricky, with 3 levels of definitions:
+ // (a) global default inclusion
+ // (b) per-type default inclusion (from annotation or config overrides;
+ // latter having precedence
+ // Cc) per-property override
+ //
+ // and not only requiring merging, but also considering special handling
+ // for NON_DEFAULT in case of (b) (vs (a) or (c))
+ JsonInclude.Value inclPerType = JsonInclude.Value.merge(
+ beanDesc.findPropertyInclusion(JsonInclude.Value.empty()),
+ config.getDefaultPropertyInclusion(beanDesc.getBeanClass(),
+ JsonInclude.Value.empty()));
+ _defaultInclusion = JsonInclude.Value.merge(config.getDefaultPropertyInclusion(),
+ inclPerType);
+ _useRealPropertyDefaults = inclPerType.getValueInclusion() == JsonInclude.Include.NON_DEFAULT;
_annotationIntrospector = _config.getAnnotationIntrospector();
}
@@ -63,6 +86,7 @@
* to use for contained values (only used for properties that are
* of container type)
*/
+ @SuppressWarnings("deprecation")
protected BeanPropertyWriter buildWriter(SerializerProvider prov,
BeanPropertyDefinition propDef, JavaType declaredType, JsonSerializer<?> ser,
TypeSerializer typeSer, TypeSerializer contentTypeSer,
@@ -70,7 +94,12 @@
throws JsonMappingException
{
// do we have annotation that forces type to use (to declared type or its super type)?
- JavaType serializationType = findSerializationType(am, defaultUseStaticTyping, declaredType);
+ JavaType serializationType;
+ try {
+ serializationType = findSerializationType(am, defaultUseStaticTyping, declaredType);
+ } catch (JsonMappingException e) {
+ return prov.reportBadPropertyDefinition(_beanDesc, propDef, e.getMessage());
+ }
// Container types can have separate type serializers for content (value / element) type
if (contentTypeSer != null) {
@@ -85,44 +114,50 @@
JavaType ct = serializationType.getContentType();
// Not exactly sure why, but this used to occur; better check explicitly:
if (ct == null) {
- throw new IllegalStateException("Problem trying to create BeanPropertyWriter for property '"
- +propDef.getName()+"' (of type "+_beanDesc.getType()+"); serialization type "+serializationType+" has no content");
+ prov.reportBadPropertyDefinition(_beanDesc, propDef,
+ "serialization type "+serializationType+" has no content");
}
serializationType = serializationType.withContentTypeHandler(contentTypeSer);
ct = serializationType.getContentType();
}
-
+
Object valueToSuppress = null;
boolean suppressNulls = false;
- JsonInclude.Value inclV = _defaultInclusion.withOverrides(propDef.findInclusion());
+ // 12-Jul-2016, tatu: [databind#1256] Need to make sure we consider type refinement
+ JavaType actualType = (serializationType == null) ? declaredType : serializationType;
+
+ // 17-Aug-2016, tatu: Default inclusion covers global default (for all types), as well
+ // as type-default for enclosing POJO. What we need, then, is per-type default (if any)
+ // for declared property type... and finally property annotation overrides
+ JsonInclude.Value inclV = _config.getDefaultPropertyInclusion(actualType.getRawClass(),
+ _defaultInclusion);
+
+ // property annotation override
+
+ inclV = inclV.withOverrides(propDef.findInclusion());
JsonInclude.Include inclusion = inclV.getValueInclusion();
+
if (inclusion == JsonInclude.Include.USE_DEFAULTS) { // should not occur but...
inclusion = JsonInclude.Include.ALWAYS;
}
-
- /*
- JsonInclude.Include inclusion = propDef.findInclusion().getValueInclusion();
- if (inclusion == JsonInclude.Include.USE_DEFAULTS) { // since 2.6
- inclusion = _defaultInclusion;
- if (inclusion == null) {
- inclusion = JsonInclude.Include.ALWAYS;
- }
- }
- */
-
+
switch (inclusion) {
case NON_DEFAULT:
// 11-Nov-2015, tatu: This is tricky because semantics differ between cases,
- // so that if enclosing class has this, we may need to values of property,
+ // so that if enclosing class has this, we may need to access values of property,
// whereas for global defaults OR per-property overrides, we have more
// static definition. Sigh.
- // First: case of class specifying it; try to find POJO property defaults
- JavaType t = (serializationType == null) ? declaredType : serializationType;
- if (_defaultInclusion.getValueInclusion() == JsonInclude.Include.NON_DEFAULT) {
- valueToSuppress = getPropertyDefaultValue(propDef.getName(), am, t);
+ // First: case of class/type specifying it; try to find POJO property defaults
+ if (_useRealPropertyDefaults) {
+ // 07-Sep-2016, tatu: may also need to front-load access forcing now
+ if (prov.isEnabled(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS)) {
+ am.fixAccess(_config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
+ }
+ valueToSuppress = getPropertyDefaultValue(propDef.getName(), am, actualType);
} else {
- valueToSuppress = getDefaultValue(t);
+ valueToSuppress = getDefaultValue(actualType);
+ suppressNulls = true;
}
if (valueToSuppress == null) {
suppressNulls = true;
@@ -131,13 +166,12 @@
valueToSuppress = ArrayBuilders.getArrayComparator(valueToSuppress);
}
}
-
break;
case NON_ABSENT: // new with 2.6, to support Guava/JDK8 Optionals
// always suppress nulls
suppressNulls = true;
// and for referential types, also "empty", which in their case means "absent"
- if (declaredType.isReferenceType()) {
+ if (actualType.isReferenceType()) {
valueToSuppress = BeanPropertyWriter.MARKER_FOR_EMPTY;
}
break;
@@ -153,7 +187,7 @@
case ALWAYS: // default
default:
// we may still want to suppress empty collections, as per [JACKSON-254]:
- if (declaredType.isContainerType()
+ if (actualType.isContainerType()
&& !_config.isEnabled(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS)) {
valueToSuppress = BeanPropertyWriter.MARKER_FOR_EMPTY;
}
@@ -192,6 +226,7 @@
throws JsonMappingException
{
JavaType secondary = _annotationIntrospector.refineSerializationType(_config, a, declaredType);
+
// 11-Oct-2015, tatu: As of 2.7, not 100% sure following checks are needed. But keeping
// for now, just in case
if (secondary != declaredType) {
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/BeanAsArraySerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/BeanAsArraySerializer.java
index d646ab5..3f764ca 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/BeanAsArraySerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/BeanAsArraySerializer.java
@@ -1,6 +1,7 @@
package com.fasterxml.jackson.databind.ser.impl;
import java.io.IOException;
+import java.util.Set;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.*;
@@ -62,7 +63,7 @@
_defaultSerializer = src;
}
- protected BeanAsArraySerializer(BeanSerializerBase src, String[] toIgnore) {
+ protected BeanAsArraySerializer(BeanSerializerBase src, Set<String> toIgnore) {
super(src, toIgnore);
_defaultSerializer = src;
}
@@ -102,9 +103,9 @@
public BeanSerializerBase withFilterId(Object filterId) {
return new BeanAsArraySerializer(this, _objectIdWriter, filterId);
}
-
+
@Override
- protected BeanAsArraySerializer withIgnorals(String[] toIgnore) {
+ protected BeanAsArraySerializer withIgnorals(Set<String> toIgnore) {
return new BeanAsArraySerializer(this, toIgnore);
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/MapEntrySerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/MapEntrySerializer.java
index 4b36328..7deb535 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/MapEntrySerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/MapEntrySerializer.java
@@ -187,9 +187,7 @@
public void serialize(Map.Entry<?, ?> value, JsonGenerator gen, SerializerProvider provider)
throws IOException
{
- gen.writeStartObject();
- // [databind#631]: Assign current value, to be accessible by custom serializers
- gen.setCurrentValue(value);
+ gen.writeStartObject(value);
if (_valueSerializer != null) {
serializeUsing(value, gen, provider, _valueSerializer);
} else {
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringArraySerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringArraySerializer.java
index c1e574f..bbd9e23 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringArraySerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringArraySerializer.java
@@ -28,6 +28,7 @@
/* Note: not clean in general, but we are betting against
* anyone re-defining properties of String.class here...
*/
+ @SuppressWarnings("deprecation")
private final static JavaType VALUE_TYPE = TypeFactory.defaultInstance().uncheckedSimpleType(String.class);
public final static StringArraySerializer instance = new StringArraySerializer();
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnknownSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnknownSerializer.java
index f6498f1..6c1530b 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnknownSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnknownSerializer.java
@@ -29,7 +29,7 @@
{
// 27-Nov-2009, tatu: As per [JACKSON-201] may or may not fail...
if (provider.isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS)) {
- failForEmpty(gen, value);
+ failForEmpty(provider, value);
}
// But if it's fine, we'll just output empty JSON Object:
gen.writeStartObject();
@@ -41,7 +41,7 @@
TypeSerializer typeSer) throws IOException
{
if (provider.isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS)) {
- failForEmpty(gen, value);
+ failForEmpty(provider, value);
}
typeSer.writeTypePrefixForObject(value, gen);
typeSer.writeTypeSuffixForObject(value, gen);
@@ -64,8 +64,9 @@
visitor.expectAnyFormat(typeHint);
}
- protected void failForEmpty(JsonGenerator gen, Object value) throws JsonMappingException {
- throw JsonMappingException.from(gen,
- "No serializer found for class "+value.getClass().getName()+" and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) )");
+ protected void failForEmpty(SerializerProvider prov, Object value)
+ throws JsonMappingException {
+ prov.reportMappingProblem("No serializer found for class %s and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)",
+ value.getClass().getName());
}
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnwrappingBeanSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnwrappingBeanSerializer.java
index aa502a2..834e03a 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnwrappingBeanSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnwrappingBeanSerializer.java
@@ -1,7 +1,6 @@
package com.fasterxml.jackson.databind.ser.impl;
import com.fasterxml.jackson.core.JsonGenerator;
-
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
@@ -9,6 +8,7 @@
import com.fasterxml.jackson.databind.util.NameTransformer;
import java.io.IOException;
+import java.util.Set;
public class UnwrappingBeanSerializer
extends BeanSerializerBase
@@ -49,7 +49,7 @@
_nameTransformer = src._nameTransformer;
}
- protected UnwrappingBeanSerializer(UnwrappingBeanSerializer src, String[] toIgnore) {
+ protected UnwrappingBeanSerializer(UnwrappingBeanSerializer src, Set<String> toIgnore) {
super(src, toIgnore);
_nameTransformer = src._nameTransformer;
}
@@ -82,7 +82,7 @@
}
@Override
- protected BeanSerializerBase withIgnorals(String[] toIgnore) {
+ protected BeanSerializerBase withIgnorals(Set<String> toIgnore) {
return new UnwrappingBeanSerializer(this, toIgnore);
}
@@ -126,10 +126,8 @@
TypeSerializer typeSer) throws IOException
{
if (provider.isEnabled(SerializationFeature.FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS)) {
- throw JsonMappingException.from(gen,
- "Unwrapped property requires use of type information: can not serialize without disabling `SerializationFeature.FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS`");
+ provider.reportMappingProblem("Unwrapped property requires use of type information: can not serialize without disabling `SerializationFeature.FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS`");
}
-
gen.setCurrentValue(bean); // [databind#631]
if (_objectIdWriter != null) {
_serializeWithObjectId(bean, gen, provider, typeSer);
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/ArraySerializerBase.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/ArraySerializerBase.java
index e1a3f1a..c89409c 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/ArraySerializerBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/ArraySerializerBase.java
@@ -87,14 +87,14 @@
Boolean unwrapSingle);
@Override
- public JsonSerializer<?> createContextual(SerializerProvider provider,
+ public JsonSerializer<?> createContextual(SerializerProvider serializers,
BeanProperty property) throws JsonMappingException
{
Boolean unwrapSingle = null;
// First: if we have a property, may have property-annotation overrides
if (property != null) {
- JsonFormat.Value format = property.findPropertyFormat(provider.getConfig(), _handledType);
+ JsonFormat.Value format = findFormatOverrides(serializers, property, handledType());
if (format != null) {
unwrapSingle = format.getFeature(JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED);
if (unwrapSingle != _unwrapSingle) {
@@ -119,9 +119,9 @@
return;
}
}
+ gen.setCurrentValue(value);
gen.writeStartArray();
// [databind#631]: Assign current value, to be accessible by custom serializers
- gen.setCurrentValue(value);
serializeContents(value, gen, provider);
gen.writeEndArray();
}
@@ -131,7 +131,6 @@
TypeSerializer typeSer)
throws IOException
{
- // note: let's NOT consider [JACKSON-805] here; gets too complicated, and probably just won't work
typeSer.writeTypePrefixForArray(value, gen);
// [databind#631]: Assign current value, to be accessible by custom serializers
gen.setCurrentValue(value);
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/AsArraySerializerBase.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/AsArraySerializerBase.java
index 7d84b33..a1bd6b9 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/AsArraySerializerBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/AsArraySerializerBase.java
@@ -161,7 +161,7 @@
* known statically.
*/
@Override
- public JsonSerializer<?> createContextual(SerializerProvider provider,
+ public JsonSerializer<?> createContextual(SerializerProvider serializers,
BeanProperty property)
throws JsonMappingException
{
@@ -169,43 +169,39 @@
if (typeSer != null) {
typeSer = typeSer.forProperty(property);
}
- /* 29-Sep-2012, tatu: Actually, we need to do much more contextual
- * checking here since we finally know for sure the property,
- * and it may have overrides
- */
JsonSerializer<?> ser = null;
Boolean unwrapSingle = null;
// First: if we have a property, may have property-annotation overrides
if (property != null) {
- final AnnotationIntrospector intr = provider.getAnnotationIntrospector();
+ final AnnotationIntrospector intr = serializers.getAnnotationIntrospector();
AnnotatedMember m = property.getMember();
if (m != null) {
Object serDef = intr.findContentSerializer(m);
if (serDef != null) {
- ser = provider.serializerInstance(m, serDef);
+ ser = serializers.serializerInstance(m, serDef);
}
}
- JsonFormat.Value format = property.findPropertyFormat(provider.getConfig(), _handledType);
- if (format != null) {
- unwrapSingle = format.getFeature(JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED);
- }
+ }
+ JsonFormat.Value format = findFormatOverrides(serializers, property, handledType());
+ if (format != null) {
+ unwrapSingle = format.getFeature(JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED);
}
if (ser == null) {
ser = _elementSerializer;
}
// 18-Feb-2013, tatu: May have a content converter:
- ser = findConvertingContentSerializer(provider, property, ser);
+ ser = findConvertingContentSerializer(serializers, property, ser);
if (ser == null) {
// 30-Sep-2012, tatu: One more thing -- if explicit content type is annotated,
// we can consider it a static case as well.
if (_elementType != null) {
if (_staticTyping && !_elementType.isJavaLangObject()) {
- ser = provider.findValueSerializer(_elementType, property);
+ ser = serializers.findValueSerializer(_elementType, property);
}
}
} else {
- ser = provider.handleSecondaryContextualization(ser, property);
+ ser = serializers.handleSecondaryContextualization(ser, property);
}
if ((ser != _elementSerializer)
|| (property != _property)
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/AtomicReferenceSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/AtomicReferenceSerializer.java
index 4d1fb89..7726bb0 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/AtomicReferenceSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/AtomicReferenceSerializer.java
@@ -1,120 +1,40 @@
package com.fasterxml.jackson.databind.ser.std;
-import java.io.IOException;
import java.util.concurrent.atomic.AtomicReference;
import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.core.JsonGenerator;
+
import com.fasterxml.jackson.databind.*;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-import com.fasterxml.jackson.databind.introspect.Annotated;
-import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
-import com.fasterxml.jackson.databind.ser.ContextualSerializer;
-import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap;
import com.fasterxml.jackson.databind.type.ReferenceType;
import com.fasterxml.jackson.databind.util.NameTransformer;
-// Since 2.6 in its own class
public class AtomicReferenceSerializer
- extends StdSerializer<AtomicReference<?>>
- implements ContextualSerializer
+ extends ReferenceTypeSerializer<AtomicReference<?>>
{
private static final long serialVersionUID = 1L;
- /**
- * Declared type parameter for Optional.
- */
- protected final JavaType _referredType;
-
- protected final BeanProperty _property;
-
- /**
- * Type serializer used for values, if any.
- *
- * @since 2.7
- */
- protected final TypeSerializer _valueTypeSerializer;
-
- /**
- * @since 2.7
- */
- protected final JsonSerializer<Object> _valueSerializer;
-
- /**
- * To support unwrapped values of dynamic types, will need this:
- *
- * @since 2.7
- */
- protected final NameTransformer _unwrapper;
-
- /**
- * Further guidance on serialization-inclusion (or not), regarding
- * contained value (if any).
- *
- * @since 2.7
- */
- protected final JsonInclude.Include _contentInclusion;
-
- /**
- * If element type can not be statically determined, mapping from
- * runtime type to serializer is handled using this object
- *
- * @since 2.7
- */
- protected transient PropertySerializerMap _dynamicSerializers;
-
/*
/**********************************************************
/* Constructors, factory methods
/**********************************************************
*/
- public AtomicReferenceSerializer(ReferenceType fullType, boolean staticTyping, TypeSerializer vts,
- JsonSerializer<Object> ser)
+ public AtomicReferenceSerializer(ReferenceType fullType, boolean staticTyping,
+ TypeSerializer vts, JsonSerializer<Object> ser)
{
- super(fullType);
- _referredType = fullType.getReferencedType();
- _property = null;
- _valueTypeSerializer = vts;
- _valueSerializer = ser;
- _unwrapper = null;
- _contentInclusion = null;
- _dynamicSerializers = PropertySerializerMap.emptyForProperties();
+ super(fullType, staticTyping, vts, ser);
}
- @SuppressWarnings("unchecked")
protected AtomicReferenceSerializer(AtomicReferenceSerializer base, BeanProperty property,
TypeSerializer vts, JsonSerializer<?> valueSer,
NameTransformer unwrapper,
JsonInclude.Include contentIncl)
{
- super(base);
- _referredType = base._referredType;
- _dynamicSerializers = base._dynamicSerializers;
- _property = property;
- _valueTypeSerializer = vts;
- _valueSerializer = (JsonSerializer<Object>) valueSer;
- _unwrapper = unwrapper;
- if ((contentIncl == JsonInclude.Include.USE_DEFAULTS)
- || (contentIncl == JsonInclude.Include.ALWAYS)) {
- _contentInclusion = null;
- } else {
- _contentInclusion = contentIncl;
- }
+ super(base, property, vts, valueSer, unwrapper, contentIncl);
}
@Override
- public JsonSerializer<AtomicReference<?>> unwrappingSerializer(NameTransformer transformer) {
- JsonSerializer<Object> ser = _valueSerializer;
- if (ser != null) {
- ser = ser.unwrappingSerializer(transformer);
- }
- NameTransformer unwrapper = (_unwrapper == null) ? transformer
- : NameTransformer.chainedTransformer(transformer, _unwrapper);
- return withResolved(_property, _valueTypeSerializer, ser, unwrapper, _contentInclusion);
- }
-
protected AtomicReferenceSerializer withResolved(BeanProperty prop,
TypeSerializer vts, JsonSerializer<?> valueSer,
NameTransformer unwrapper,
@@ -130,227 +50,22 @@
/*
/**********************************************************
- /* Contextualization (support for property annotations)
+ /* Abstract method impls
/**********************************************************
*/
@Override
- public JsonSerializer<?> createContextual(SerializerProvider provider,
- BeanProperty property) throws JsonMappingException
- {
- TypeSerializer typeSer = _valueTypeSerializer;
- if (typeSer != null) {
- typeSer = typeSer.forProperty(property);
- }
- // First: do we have an annotation override from property?
- JsonSerializer<?> ser = findAnnotatedContentSerializer(provider, property);;
- if (ser == null) {
- // If not, use whatever was configured by type
- ser = _valueSerializer;
- if (ser == null) {
- // A few conditions needed to be able to fetch serializer here:
- if (_useStatic(provider, property, _referredType)) {
- ser = _findSerializer(provider, _referredType, property);
- }
- } else {
- ser = provider.handlePrimaryContextualization(ser, property);
- }
- }
- // Also: may want to have more refined exclusion based on referenced value
- JsonInclude.Include contentIncl = _contentInclusion;
- if (property != null) {
- JsonInclude.Value incl = property.findPropertyInclusion(provider.getConfig(),
- AtomicReference.class);
- JsonInclude.Include newIncl = incl.getContentInclusion();
- if ((newIncl != contentIncl) && (newIncl != JsonInclude.Include.USE_DEFAULTS)) {
- contentIncl = newIncl;
- }
- }
- return withResolved(property, typeSer, ser, _unwrapper, contentIncl);
- }
-
- protected boolean _useStatic(SerializerProvider provider, BeanProperty property,
- JavaType referredType)
- {
- // First: no serializer for `Object.class`, must be dynamic
- if (referredType.isJavaLangObject()) {
- return false;
- }
- // but if type is final, might as well fetch
- if (referredType.isFinal()) { // or should we allow annotation override? (only if requested...)
- return true;
- }
- // also: if indicated by typing, should be considered static
- if (referredType.useStaticType()) {
- return true;
- }
- // if neither, maybe explicit annotation?
- AnnotationIntrospector intr = provider.getAnnotationIntrospector();
- if ((intr != null) && (property != null)) {
- Annotated ann = property.getMember();
- if (ann != null) {
- JsonSerialize.Typing t = intr.findSerializationTyping(property.getMember());
- if (t == JsonSerialize.Typing.STATIC) {
- return true;
- }
- if (t == JsonSerialize.Typing.DYNAMIC) {
- return false;
- }
- }
- }
- // and finally, may be forced by global static typing (unlikely...)
- return provider.isEnabled(MapperFeature.USE_STATIC_TYPING);
- }
-
- /*
- /**********************************************************
- /* Accessors
- /**********************************************************
- */
-
- @Override
- public boolean isEmpty(SerializerProvider provider, AtomicReference<?> value)
- {
- if (value == null) {
- return true;
- }
- Object contents = value.get();
- if (contents == null) {
- return true;
- }
- if (_contentInclusion == null) {
- return false;
- }
- JsonSerializer<Object> ser = _valueSerializer;
- if (ser == null) {
- try {
- ser = _findCachedSerializer(provider, contents.getClass());
- } catch (JsonMappingException e) { // nasty but necessary
- throw new RuntimeJsonMappingException(e);
- }
- }
- return ser.isEmpty(provider, contents);
-
+ protected boolean _isValueEmpty(AtomicReference<?> value) {
+ return value.get() == null;
}
@Override
- public boolean isUnwrappingSerializer() {
- return (_unwrapper != null);
- }
-
- /*
- /**********************************************************
- /* Serialization methods
- /**********************************************************
- */
-
- @Override
- public void serialize(AtomicReference<?> ref, JsonGenerator g, SerializerProvider provider)
- throws IOException
- {
- Object value = ref.get();
- if (value == null) {
- if (_unwrapper == null) {
- provider.defaultSerializeNull(g);
- }
- return;
- }
- JsonSerializer<Object> ser = _valueSerializer;
- if (ser == null) {
- ser = _findCachedSerializer(provider, value.getClass());
- }
- if (_valueTypeSerializer != null) {
- ser.serializeWithType(value, g, provider, _valueTypeSerializer);
- } else {
- ser.serialize(value, g, provider);
- }
+ protected Object _getReferenced(AtomicReference<?> value) {
+ return value.get();
}
@Override
- public void serializeWithType(AtomicReference<?> ref,
- JsonGenerator g, SerializerProvider provider,
- TypeSerializer typeSer) throws IOException
- {
- Object value = ref.get();
- if (value == null) {
- if (_unwrapper == null) {
- provider.defaultSerializeNull(g);
- }
- return;
- }
-
- // 19-Apr-2016, tatu: In order to basically "skip" the whole wrapper level
- // (which is what non-polymorphic serialization does too), we will need
- // to simply delegate call, I think, and NOT try to use it here.
-
- // Otherwise apply type-prefix/suffix, then std serialize:
- /*
- typeSer.writeTypePrefixForScalar(ref, g);
- serialize(ref, g, provider);
- typeSer.writeTypeSuffixForScalar(ref, g);
- */
-
- JsonSerializer<Object> ser = _valueSerializer;
- if (ser == null) {
- ser = _findCachedSerializer(provider, value.getClass());
- }
- ser.serializeWithType(value, g, provider, typeSer);
+ protected Object _getReferencedIfPresent(AtomicReference<?> value) {
+ return value.get();
}
-
- /*
- /**********************************************************
- /* Introspection support
- /**********************************************************
- */
-
- @Override
- public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
- throws JsonMappingException
- {
- JsonSerializer<?> ser = _valueSerializer;
- if (ser == null) {
- ser = _findSerializer(visitor.getProvider(), _referredType, _property);
- if (_unwrapper != null) {
- ser = ser.unwrappingSerializer(_unwrapper);
- }
- }
- ser.acceptJsonFormatVisitor(visitor, _referredType);
- }
-
- /*
- /**********************************************************
- /* Helper methods
- /**********************************************************
- */
-
- /**
- * Helper method that encapsulates logic of retrieving and caching required
- * serializer.
- */
- private final JsonSerializer<Object> _findCachedSerializer(SerializerProvider provider,
- Class<?> type) throws JsonMappingException
- {
- JsonSerializer<Object> ser = _dynamicSerializers.serializerFor(type);
- if (ser == null) {
- ser = _findSerializer(provider, type, _property);
- if (_unwrapper != null) {
- ser = ser.unwrappingSerializer(_unwrapper);
- }
- _dynamicSerializers = _dynamicSerializers.newWith(type, ser);
- }
- return ser;
- }
-
- private final JsonSerializer<Object> _findSerializer(SerializerProvider provider,
- Class<?> type, BeanProperty prop) throws JsonMappingException
- {
- return provider.findTypedValueSerializer(type, true, prop);
- }
-
- private final JsonSerializer<Object> _findSerializer(SerializerProvider provider,
- JavaType type, BeanProperty prop) throws JsonMappingException
- {
- return provider.findTypedValueSerializer(type, true, prop);
- }
-
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/BeanSerializerBase.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/BeanSerializerBase.java
index 658786d..b1a36a5 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/BeanSerializerBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/BeanSerializerBase.java
@@ -4,12 +4,9 @@
import java.lang.reflect.Type;
import java.util.*;
-import com.fasterxml.jackson.annotation.JsonFormat;
-import com.fasterxml.jackson.annotation.ObjectIdGenerator;
-import com.fasterxml.jackson.annotation.ObjectIdGenerators;
+import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
-import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.introspect.ObjectIdInfo;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitable;
@@ -161,12 +158,16 @@
_serializationShape = src._serializationShape;
}
+ @Deprecated // since 2.8, remove soon
protected BeanSerializerBase(BeanSerializerBase src, String[] toIgnore)
{
+ this(src, ArrayBuilders.arrayToSet(toIgnore));
+ }
+
+ protected BeanSerializerBase(BeanSerializerBase src, Set<String> toIgnore)
+ {
super(src._handledType);
- // Bit clumsy, but has to do:
- HashSet<String> ignoredSet = ArrayBuilders.arrayToSet(toIgnore);
final BeanPropertyWriter[] propsIn = src._props;
final BeanPropertyWriter[] fpropsIn = src._filteredProps;
final int len = propsIn.length;
@@ -177,7 +178,7 @@
for (int i = 0; i < len; ++i) {
BeanPropertyWriter bpw = propsIn[i];
// should be ignored?
- if (ignoredSet.contains(bpw.getName())) {
+ if ((toIgnore != null) && toIgnore.contains(bpw.getName())) {
continue;
}
propsOut.add(bpw);
@@ -207,9 +208,20 @@
* Mutant factory used for creating a new instance with additional
* set of properties to ignore (from properties this instance otherwise has)
*
- * @since 2.0
+ * @since 2.8
*/
- protected abstract BeanSerializerBase withIgnorals(String[] toIgnore);
+ protected abstract BeanSerializerBase withIgnorals(Set<String> toIgnore);
+
+ /**
+ * Mutant factory used for creating a new instance with additional
+ * set of properties to ignore (from properties this instance otherwise has)
+ *
+ * @deprecated since 2.8
+ */
+ @Deprecated
+ protected BeanSerializerBase withIgnorals(String[] toIgnore) {
+ return withIgnorals(ArrayBuilders.arrayToSet(toIgnore));
+ }
/**
* Mutant factory for creating a variant that output POJO as a
@@ -393,38 +405,38 @@
// Let's start with one big transmutation: Enums that are annotated
// to serialize as Objects may want to revert
+ JsonFormat.Value format = findFormatOverrides(provider, property, handledType());
JsonFormat.Shape shape = null;
- if (accessor != null) {
- JsonFormat.Value format = intr.findFormat((Annotated) accessor);
-
- if (format != null) {
- shape = format.getShape();
- // or, alternatively, asked to revert "back to" other representations...
- if (shape != _serializationShape) {
- if (_handledType.isEnum()) {
- switch (shape) {
- case STRING:
- case NUMBER:
- case NUMBER_INT:
- // 12-Oct-2014, tatu: May need to introspect full annotations... but
- // for now, just do class ones
- BeanDescription desc = config.introspectClassAnnotations(_handledType);
- JsonSerializer<?> ser = EnumSerializer.construct(_handledType,
- provider.getConfig(), desc, format);
- return provider.handlePrimaryContextualization(ser, property);
- }
+ if ((format != null) && format.hasShape()) {
+ shape = format.getShape();
+ // or, alternatively, asked to revert "back to" other representations...
+ if ((shape != JsonFormat.Shape.ANY) && (shape != _serializationShape)) {
+ if (_handledType.isEnum()) {
+ switch (shape) {
+ case STRING:
+ case NUMBER:
+ case NUMBER_INT:
+ // 12-Oct-2014, tatu: May need to introspect full annotations... but
+ // for now, just do class ones
+ BeanDescription desc = config.introspectClassAnnotations(_handledType);
+ JsonSerializer<?> ser = EnumSerializer.construct(_handledType,
+ provider.getConfig(), desc, format);
+ return provider.handlePrimaryContextualization(ser, property);
}
}
}
}
ObjectIdWriter oiw = _objectIdWriter;
- String[] ignorals = null;
+ Set<String> ignoredProps = null;
Object newFilterId = null;
-
+
// Then we may have an override for Object Id
if (accessor != null) {
- ignorals = intr.findPropertiesToIgnore(accessor, true);
+ JsonIgnoreProperties.Value ignorals = intr.findPropertyIgnorals(accessor);
+ if (ignorals != null) {
+ ignoredProps = ignorals.findIgnoredForSerialization();
+ }
ObjectIdInfo objectIdInfo = intr.findObjectIdInfo(accessor);
if (objectIdInfo == null) {
// no ObjectId override, but maybe ObjectIdRef?
@@ -502,8 +514,8 @@
}
}
// And possibly add more properties to ignore
- if (ignorals != null && ignorals.length != 0) {
- contextual = contextual.withIgnorals(ignorals);
+ if ((ignoredProps != null) && !ignoredProps.isEmpty()) {
+ contextual = contextual.withIgnorals(ignoredProps);
}
if (newFilterId != null) {
contextual = contextual.withFilterId(newFilterId);
@@ -591,7 +603,7 @@
return;
}
if (startEndObject) {
- gen.writeStartObject();
+ gen.writeStartObject(bean);
}
objectId.writeAsField(gen, provider, w);
if (_propertyFilterId != null) {
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/DateTimeSerializerBase.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/DateTimeSerializerBase.java
index 68dbf40..ea67b88 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/DateTimeSerializerBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/DateTimeSerializerBase.java
@@ -13,7 +13,6 @@
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
-import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.jsonFormatVisitors.*;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.fasterxml.jackson.databind.util.StdDateFormat;
@@ -28,7 +27,7 @@
* Java timestamp, regardless of other settings.
*/
protected final Boolean _useTimestamp;
-
+
/**
* Specific format to use, if not default format: non null value
* also indicates that serialization is to be done as JSON String,
@@ -51,15 +50,13 @@
BeanProperty property) throws JsonMappingException
{
if (property != null) {
- JsonFormat.Value format = serializers.getAnnotationIntrospector().findFormat((Annotated)property.getMember());
+ JsonFormat.Value format = findFormatOverrides(serializers, property, handledType());
if (format != null) {
-
// Simple case first: serialize as numeric timestamp?
JsonFormat.Shape shape = format.getShape();
if (shape.isNumeric()) {
return withFormat(Boolean.TRUE, null);
}
-
if ((shape == JsonFormat.Shape.STRING) || format.hasPattern()
|| format.hasLocale() || format.hasTimeZone()) {
TimeZone tz = format.getTimeZone();
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSerializer.java
index 95e16a5..a80ea2b 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSerializer.java
@@ -11,7 +11,6 @@
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
-import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonStringFormatVisitor;
import com.fasterxml.jackson.databind.node.ArrayNode;
@@ -24,8 +23,6 @@
*<p>
* Based on {@link StdScalarSerializer} since the JSON value is
* scalar (String).
- *
- * @author tatu
*/
@JacksonStdImpl
public class EnumSerializer
@@ -96,10 +93,12 @@
* choice here, however.
*/
@Override
- public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException
+ public JsonSerializer<?> createContextual(SerializerProvider serializers,
+ BeanProperty property) throws JsonMappingException
{
if (property != null) {
- JsonFormat.Value format = prov.getAnnotationIntrospector().findFormat((Annotated) property.getMember());
+ JsonFormat.Value format = findFormatOverrides(serializers,
+ property, handledType());
if (format != null) {
Boolean serializeAsIndex = _isShapeWrittenUsingIndex(property.getType().getRawClass(), format, false);
if (serializeAsIndex != _serializeAsIndex) {
@@ -140,11 +139,16 @@
}
gen.writeString(_values.serializedValueFor(en));
}
-
+
+ /*
+ /**********************************************************
+ /* Schema support
+ /**********************************************************
+ */
+
@Override
public JsonNode getSchema(SerializerProvider provider, Type typeHint)
{
- // [JACKSON-684]: serialize as index?
if (_serializeAsIndex(provider)) {
return createSchemaNode("integer", true);
}
@@ -214,10 +218,12 @@
if (shape == null) {
return null;
}
- if (shape == Shape.ANY || shape == Shape.SCALAR) { // i.e. "default", check dynamically
+ // i.e. "default", check dynamically
+ if (shape == Shape.ANY || shape == Shape.SCALAR) {
return null;
}
- if (shape == Shape.STRING) {
+ // 19-May-2016, tatu: also consider "natural" shape
+ if (shape == Shape.STRING || shape == Shape.NATURAL) {
return Boolean.FALSE;
}
// 01-Oct-2014, tatu: For convenience, consider "as-array" to also mean 'yes, use index')
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/JsonValueSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/JsonValueSerializer.java
index a3998c7..608a7c8 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/JsonValueSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/JsonValueSerializer.java
@@ -2,8 +2,6 @@
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.LinkedHashSet;
import java.util.Set;
@@ -37,8 +35,11 @@
public class JsonValueSerializer
extends StdSerializer<Object>
implements ContextualSerializer, JsonFormatVisitable, SchemaAware
-{
- protected final Method _accessorMethod;
+ {
+ /**
+ * @since 2.8 (was "plain" method before)
+ */
+ protected final AnnotatedMethod _accessorMethod;
protected final JsonSerializer<Object> _valueSerializer;
@@ -58,21 +59,19 @@
/**********************************************************
*/
- // Added in 2.7.4 for forward-compatibility reasons; will be used by default in 2.8.0
- public JsonValueSerializer(AnnotatedMethod valueMethod, JsonSerializer<?> ser) {
- this(valueMethod.getAnnotated(), ser);
- }
-
/**
* @param ser Explicit serializer to use, if caller knows it (which
* occurs if and only if the "value method" was annotated with
* {@link com.fasterxml.jackson.databind.annotation.JsonSerialize#using}), otherwise
* null
+ *
+ * @since 2.8 Earlier method took "raw" Method, but that does not work with access
+ * to information we need
*/
@SuppressWarnings("unchecked")
- public JsonValueSerializer(Method valueMethod, JsonSerializer<?> ser)
+ public JsonValueSerializer(AnnotatedMethod valueMethod, JsonSerializer<?> ser)
{
- super(valueMethod.getReturnType(), false);
+ super(valueMethod.getType());
_accessorMethod = valueMethod;
_valueSerializer = (JsonSerializer<Object>) ser;
_property = null;
@@ -126,9 +125,8 @@
* if not, we don't really know the actual type until we get the instance.
*/
// 10-Mar-2010, tatu: Except if static typing is to be used
- if (provider.isEnabled(MapperFeature.USE_STATIC_TYPING)
- || Modifier.isFinal(_accessorMethod.getReturnType().getModifiers())) {
- JavaType t = provider.constructType(_accessorMethod.getGenericReturnType());
+ JavaType t = _accessorMethod.getType();
+ if (provider.isEnabled(MapperFeature.USE_STATIC_TYPING) || t.isFinal()) {
// false -> no need to cache
/* 10-Mar-2010, tatu: Ideally we would actually separate out type
* serializer from value serializer; but, alas, there's no access
@@ -161,7 +159,7 @@
public void serialize(Object bean, JsonGenerator jgen, SerializerProvider prov) throws IOException
{
try {
- Object value = _accessorMethod.invoke(bean);
+ Object value = _accessorMethod.getValue(bean);
if (value == null) {
prov.defaultSerializeNull(jgen);
return;
@@ -201,7 +199,7 @@
// Regardless of other parts, first need to find value to serialize:
Object value = null;
try {
- value = _accessorMethod.invoke(bean);
+ value = _accessorMethod.getValue(bean);
// and if we got null, can also just write it directly
if (value == null) {
provider.defaultSerializeNull(jgen);
@@ -261,32 +259,25 @@
throws JsonMappingException
{
/* 27-Apr-2015, tatu: First things first; for JSON Schema introspection,
- * Enums are special, and unfortunately we will need to add special
+ * Enum types that use `@JsonValue` are special (but NOT necessarily
+ * anything else that RETURNS an enum!)
+ * So we will need to add special
* handling here (see https://github.com/FasterXML/jackson-module-jsonSchema/issues/57
* for details).
+ *
+ * Note that meaning of JsonValue, then, is very different for Enums. Sigh.
*/
- Class<?> decl = (typeHint == null) ? null : typeHint.getRawClass();
- if (decl == null) {
- decl = _accessorMethod.getDeclaringClass();
- }
- if ((decl != null) && (decl.isEnum())) {
- if (_acceptJsonFormatVisitorForEnum(visitor, typeHint, decl)) {
+ final JavaType type = _accessorMethod.getType();
+ Class<?> declaring = _accessorMethod.getDeclaringClass();
+ if ((declaring != null) && declaring.isEnum()) {
+ if (_acceptJsonFormatVisitorForEnum(visitor, typeHint, declaring)) {
return;
}
}
-
JsonSerializer<Object> ser = _valueSerializer;
if (ser == null) {
- if (typeHint == null) {
- if (_property != null) {
- typeHint = _property.getType();
- }
- if (typeHint == null) {
- typeHint = visitor.getProvider().constructType(_handledType);
- }
- }
- ser = visitor.getProvider().findTypedValueSerializer(typeHint, false, _property);
- if (ser == null) {
+ ser = visitor.getProvider().findTypedValueSerializer(type, false, _property);
+ if (ser == null) { // can this ever occur?
visitor.expectAnyFormat(typeHint);
return;
}
@@ -296,7 +287,7 @@
/**
* Overridable helper method used for special case handling of schema information for
- * Enums
+ * Enums.
*
* @return True if method handled callbacks; false if not; in latter case caller will
* send default callbacks
@@ -313,7 +304,10 @@
Set<String> enums = new LinkedHashSet<String>();
for (Object en : enumType.getEnumConstants()) {
try {
- enums.add(String.valueOf(_accessorMethod.invoke(en)));
+ // 21-Apr-2016, tatu: This is convoluted to the max, but essentially we
+ // call `@JsonValue`-annotated accessor method on all Enum members,
+ // so it all "works out". To some degree.
+ enums.add(String.valueOf(_accessorMethod.callOn(en)));
} catch (Exception e) {
Throwable t = e;
while (t instanceof InvocationTargetException && t.getCause() != null) {
@@ -328,9 +322,8 @@
stringVisitor.enumTypes(enums);
}
return true;
-
}
-
+
protected boolean isNaturalTypeWithStdHandling(Class<?> rawType, JsonSerializer<?> ser)
{
// First: do we have a natural type being handled?
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/MapSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/MapSerializer.java
index f54df0a..9915fb1 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/MapSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/MapSerializer.java
@@ -4,17 +4,16 @@
import java.lang.reflect.Type;
import java.util.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
-
import com.fasterxml.jackson.core.*;
-
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonMapFormatVisitor;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
-import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.ser.ContainerSerializer;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.fasterxml.jackson.databind.ser.PropertyFilter;
@@ -45,7 +44,7 @@
/**
* Set of entries to omit during serialization, if any
*/
- protected final HashSet<String> _ignoredEntries;
+ protected final Set<String> _ignoredEntries;
/**
* Whether static types should be used for serialization of values
@@ -120,13 +119,14 @@
* @since 2.5
*/
@SuppressWarnings("unchecked")
- protected MapSerializer(HashSet<String> ignoredEntries,
+ protected MapSerializer(Set<String> ignoredEntries,
JavaType keyType, JavaType valueType, boolean valueTypeIsStatic,
TypeSerializer vts,
JsonSerializer<?> keySerializer, JsonSerializer<?> valueSerializer)
{
super(Map.class, false);
- _ignoredEntries = ignoredEntries;
+ _ignoredEntries = ((ignoredEntries == null) || ignoredEntries.isEmpty())
+ ? null : ignoredEntries;
_keyType = keyType;
_valueType = valueType;
_valueTypeIsStatic = valueTypeIsStatic;
@@ -152,10 +152,11 @@
@SuppressWarnings("unchecked")
protected MapSerializer(MapSerializer src, BeanProperty property,
JsonSerializer<?> keySerializer, JsonSerializer<?> valueSerializer,
- HashSet<String> ignored)
+ Set<String> ignoredEntries)
{
super(Map.class, false);
- _ignoredEntries = ignored;
+ _ignoredEntries = ((ignoredEntries == null) || ignoredEntries.isEmpty())
+ ? null : ignoredEntries;
_keyType = src._keyType;
_valueType = src._valueType;
_valueTypeIsStatic = src._valueTypeIsStatic;
@@ -231,7 +232,7 @@
*/
public MapSerializer withResolved(BeanProperty property,
JsonSerializer<?> keySerializer, JsonSerializer<?> valueSerializer,
- HashSet<String> ignored, boolean sortKeys)
+ Set<String> ignored, boolean sortKeys)
{
_ensureOverride();
MapSerializer ser = new MapSerializer(this, property, keySerializer, valueSerializer, ignored);
@@ -266,15 +267,29 @@
/**
* @since 2.3
+ *
+ * @deprecated Since 2.8 use the other overload
*/
+ @Deprecated // since 2.8
public static MapSerializer construct(String[] ignoredList, JavaType mapType,
boolean staticValueType, TypeSerializer vts,
JsonSerializer<Object> keySerializer, JsonSerializer<Object> valueSerializer,
Object filterId)
{
- HashSet<String> ignoredEntries = (ignoredList == null || ignoredList.length == 0)
+ Set<String> ignoredEntries = (ignoredList == null || ignoredList.length == 0)
? null : ArrayBuilders.arrayToSet(ignoredList);
+ return construct(ignoredEntries, mapType, staticValueType, vts,
+ keySerializer, valueSerializer, filterId);
+ }
+ /**
+ * @since 2.8
+ */
+ public static MapSerializer construct(Set<String> ignoredEntries, JavaType mapType,
+ boolean staticValueType, TypeSerializer vts,
+ JsonSerializer<Object> keySerializer, JsonSerializer<Object> valueSerializer,
+ Object filterId)
+ {
JavaType keyType, valueType;
if (mapType == null) {
@@ -311,10 +326,6 @@
BeanProperty property)
throws JsonMappingException
{
- /* 29-Sep-2012, tatu: Actually, we need to do much more contextual
- * checking here since we finally know for sure the property,
- * and it may have overrides
- */
JsonSerializer<?> ser = null;
JsonSerializer<?> keySer = null;
final AnnotationIntrospector intr = provider.getAnnotationIntrospector();
@@ -333,12 +344,10 @@
}
}
- if (property != null) {
- JsonInclude.Value inclV = property.findPropertyInclusion(provider.getConfig(), Map.class);
- JsonInclude.Include incl = inclV.getContentInclusion();
- if ((incl != null) && (incl != JsonInclude.Include.USE_DEFAULTS)) {
- suppressableValue = incl;
- }
+ JsonInclude.Value inclV = findIncludeOverrides(provider, property, Map.class);
+ JsonInclude.Include incl = inclV.getContentInclusion();
+ if ((incl != null) && (incl != JsonInclude.Include.USE_DEFAULTS)) {
+ suppressableValue = incl;
}
if (ser == null) {
ser = _valueSerializer;
@@ -363,19 +372,29 @@
} else {
keySer = provider.handleSecondaryContextualization(keySer, property);
}
- HashSet<String> ignored = _ignoredEntries;
+ Set<String> ignored = _ignoredEntries;
boolean sortKeys = false;
- if (intr != null && propertyAcc != null) {
- String[] moreToIgnore = intr.findPropertiesToIgnore(propertyAcc, true);
- if (moreToIgnore != null) {
- ignored = (ignored == null) ? new HashSet<String>() : new HashSet<String>(ignored);
- for (String str : moreToIgnore) {
- ignored.add(str);
+ if ((intr != null) && (propertyAcc != null)) {
+ JsonIgnoreProperties.Value ignorals = intr.findPropertyIgnorals(propertyAcc);
+ if (ignorals != null){
+ Set<String> newIgnored = ignorals.findIgnoredForSerialization();
+ if ((newIgnored != null) && !newIgnored.isEmpty()) {
+ ignored = (ignored == null) ? new HashSet<String>() : new HashSet<String>(ignored);
+ for (String str : newIgnored) {
+ ignored.add(str);
+ }
}
}
Boolean b = intr.findSerializationSortAlphabetically(propertyAcc);
sortKeys = (b != null) && b.booleanValue();
}
+ JsonFormat.Value format = findFormatOverrides(provider, property, Map.class);
+ if (format != null) {
+ Boolean B = format.getFeature(JsonFormat.Feature.WRITE_SORTED_MAP_ENTRIES);
+ if (B != null) {
+ sortKeys = B.booleanValue();
+ }
+ }
MapSerializer mser = withResolved(property, keySer, ser, ignored, sortKeys);
if (suppressableValue != _suppressableValue) {
mser = mser.withContentInclusion(suppressableValue);
@@ -493,9 +512,7 @@
public void serialize(Map<?,?> value, JsonGenerator gen, SerializerProvider provider)
throws IOException
{
- gen.writeStartObject();
- // [databind#631]: Assign current value, to be accessible by custom serializers
- gen.setCurrentValue(value);
+ gen.writeStartObject(value);
if (!value.isEmpty()) {
Object suppressableValue = _suppressableValue;
if (suppressableValue == JsonInclude.Include.ALWAYS) {
@@ -576,7 +593,7 @@
return;
}
final JsonSerializer<Object> keySerializer = _keySerializer;
- final HashSet<String> ignored = _ignoredEntries;
+ final Set<String> ignored = _ignoredEntries;
PropertySerializerMap serializers = _dynamicValueSerializers;
@@ -589,7 +606,7 @@
provider.findNullKeySerializer(_keyType, _property).serialize(null, gen, provider);
} else {
// One twist: is entry ignorable? If so, skip
- if (ignored != null && ignored.contains(keyElem)) continue;
+ if ((ignored != null) && ignored.contains(keyElem)) continue;
keySerializer.serialize(keyElem, gen, provider);
}
@@ -634,7 +651,7 @@
serializeTypedFields(value, gen, provider, suppressableValue);
return;
}
- final HashSet<String> ignored = _ignoredEntries;
+ final Set<String> ignored = _ignoredEntries;
PropertySerializerMap serializers = _dynamicValueSerializers;
for (Map.Entry<?,?> entry : value.entrySet()) {
@@ -698,7 +715,7 @@
throws IOException
{
final JsonSerializer<Object> keySerializer = _keySerializer;
- final HashSet<String> ignored = _ignoredEntries;
+ final Set<String> ignored = _ignoredEntries;
final TypeSerializer typeSer = _valueTypeSerializer;
for (Map.Entry<?,?> entry : value.entrySet()) {
@@ -739,7 +756,7 @@
Object suppressableValue) // since 2.5
throws IOException
{
- final HashSet<String> ignored = _ignoredEntries;
+ final Set<String> ignored = _ignoredEntries;
PropertySerializerMap serializers = _dynamicValueSerializers;
final MapProperty prop = new MapProperty(_valueTypeSerializer, _property);
@@ -811,7 +828,7 @@
Object suppressableValue) // since 2.5
throws IOException
{
- final HashSet<String> ignored = _ignoredEntries;
+ final Set<String> ignored = _ignoredEntries;
PropertySerializerMap serializers = _dynamicValueSerializers;
for (Map.Entry<?,?> entry : value.entrySet()) {
@@ -878,10 +895,9 @@
@Override
public JsonNode getSchema(SerializerProvider provider, Type typeHint)
{
- ObjectNode o = createSchemaNode("object", true);
//(ryan) even though it's possible to statically determine the "value" type of the map,
// there's no way to statically determine the keys, so the "Entries" can't be determined.
- return o;
+ return createSchemaNode("object", true);
}
@Override
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializers.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializers.java
index 3585892..e27722c 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializers.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializers.java
@@ -10,7 +10,6 @@
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
-import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
@@ -21,25 +20,24 @@
*/
@SuppressWarnings("serial")
public class NumberSerializers {
- protected NumberSerializers() {
- }
+ protected NumberSerializers() { }
public static void addAll(Map<String, JsonSerializer<?>> allDeserializers) {
- final JsonSerializer<?> intS = new IntegerSerializer();
- allDeserializers.put(Integer.class.getName(), intS);
- allDeserializers.put(Integer.TYPE.getName(), intS);
- allDeserializers.put(Long.class.getName(), LongSerializer.instance);
- allDeserializers.put(Long.TYPE.getName(), LongSerializer.instance);
+ allDeserializers.put(Integer.class.getName(), new IntegerSerializer(Integer.class));
+ allDeserializers.put(Integer.TYPE.getName(), new IntegerSerializer(Integer.TYPE));
+ allDeserializers.put(Long.class.getName(), new LongSerializer(Long.class));
+ allDeserializers.put(Long.TYPE.getName(), new LongSerializer(Long.TYPE));
+
allDeserializers.put(Byte.class.getName(), IntLikeSerializer.instance);
allDeserializers.put(Byte.TYPE.getName(), IntLikeSerializer.instance);
allDeserializers.put(Short.class.getName(), ShortSerializer.instance);
allDeserializers.put(Short.TYPE.getName(), ShortSerializer.instance);
// Numbers, limited length floating point
+ allDeserializers.put(Double.class.getName(), new DoubleSerializer(Double.class));
+ allDeserializers.put(Double.TYPE.getName(), new DoubleSerializer(Double.TYPE));
allDeserializers.put(Float.class.getName(), FloatSerializer.instance);
allDeserializers.put(Float.TYPE.getName(), FloatSerializer.instance);
- allDeserializers.put(Double.class.getName(), DoubleSerializer.instance);
- allDeserializers.put(Double.TYPE.getName(), DoubleSerializer.instance);
}
/*
@@ -82,19 +80,14 @@
@Override
public JsonSerializer<?> createContextual(SerializerProvider prov,
- BeanProperty property) throws JsonMappingException {
- if (property != null) {
- AnnotatedMember m = property.getMember();
- if (m != null) {
- JsonFormat.Value format = prov.getAnnotationIntrospector()
- .findFormat(m);
- if (format != null) {
- switch (format.getShape()) {
- case STRING:
- return ToStringSerializer.instance;
- default:
- }
- }
+ BeanProperty property) throws JsonMappingException
+ {
+ JsonFormat.Value format = findFormatOverrides(prov, property, handledType());
+ if (format != null) {
+ switch (format.getShape()) {
+ case STRING:
+ return ToStringSerializer.instance;
+ default:
}
}
return this;
@@ -102,9 +95,9 @@
}
/*
- * /********************************************************** /* Concrete
- * serializers, numerics
- * /**********************************************************
+ *************************************************************
+ * Concrete serializers, numerics
+ *************************************************************
*/
@JacksonStdImpl
@@ -134,8 +127,8 @@
*/
@JacksonStdImpl
public final static class IntegerSerializer extends Base<Object> {
- public IntegerSerializer() {
- super(Integer.class, JsonParser.NumberType.INT, "integer");
+ public IntegerSerializer(Class<?> type) {
+ super(type, JsonParser.NumberType.INT, "integer");
}
@Override
@@ -176,10 +169,8 @@
@JacksonStdImpl
public final static class LongSerializer extends Base<Object> {
- final static LongSerializer instance = new LongSerializer();
-
- public LongSerializer() {
- super(Long.class, JsonParser.NumberType.LONG, "number");
+ public LongSerializer(Class<?> cls) {
+ super(cls, JsonParser.NumberType.LONG, "number");
}
@Override
@@ -213,10 +204,8 @@
*/
@JacksonStdImpl
public final static class DoubleSerializer extends Base<Object> {
- final static DoubleSerializer instance = new DoubleSerializer();
-
- public DoubleSerializer() {
- super(Double.class, JsonParser.NumberType.DOUBLE, "number");
+ public DoubleSerializer(Class<?> cls) {
+ super(cls, JsonParser.NumberType.DOUBLE, "number");
}
@Override
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/ObjectArraySerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/ObjectArraySerializer.java
index 250cbbc..d71bc40 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/ObjectArraySerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/ObjectArraySerializer.java
@@ -123,7 +123,7 @@
*/
@Override
- public JsonSerializer<?> createContextual(SerializerProvider provider,
+ public JsonSerializer<?> createContextual(SerializerProvider serializers,
BeanProperty property)
throws JsonMappingException
{
@@ -137,33 +137,33 @@
// First: if we have a property, may have property-annotation overrides
if (property != null) {
AnnotatedMember m = property.getMember();
- final AnnotationIntrospector intr = provider.getAnnotationIntrospector();
+ final AnnotationIntrospector intr = serializers.getAnnotationIntrospector();
if (m != null) {
Object serDef = intr.findContentSerializer(m);
if (serDef != null) {
- ser = provider.serializerInstance(m, serDef);
+ ser = serializers.serializerInstance(m, serDef);
}
}
- JsonFormat.Value format = property.findPropertyFormat(provider.getConfig(), _handledType);
- if (format != null) {
- unwrapSingle = format.getFeature(JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED);
- }
+ }
+ JsonFormat.Value format = findFormatOverrides(serializers, property, handledType());
+ if (format != null) {
+ unwrapSingle = format.getFeature(JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED);
}
if (ser == null) {
ser = _elementSerializer;
}
- // #124: May have a content converter
- ser = findConvertingContentSerializer(provider, property, ser);
+ // [databind#124]: May have a content converter
+ ser = findConvertingContentSerializer(serializers, property, ser);
if (ser == null) {
// 30-Sep-2012, tatu: One more thing -- if explicit content type is annotated,
// we can consider it a static case as well.
if (_elementType != null) {
if (_staticTyping && !_elementType.isJavaLangObject()) {
- ser = provider.findValueSerializer(_elementType, property);
+ ser = serializers.findValueSerializer(_elementType, property);
}
}
} else {
- ser = provider.handleSecondaryContextualization(ser, property);
+ ser = serializers.handleSecondaryContextualization(ser, property);
}
return withResolved(property, vts, ser, unwrapSingle);
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/ReferenceTypeSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/ReferenceTypeSerializer.java
new file mode 100644
index 0000000..64aefbc
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/ReferenceTypeSerializer.java
@@ -0,0 +1,349 @@
+package com.fasterxml.jackson.databind.ser.std;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.introspect.Annotated;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.ser.ContextualSerializer;
+import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap;
+import com.fasterxml.jackson.databind.type.ReferenceType;
+import com.fasterxml.jackson.databind.util.NameTransformer;
+
+/**
+ * Base implementation for values of {@link ReferenceType}.
+ * Implements most of functionality, only leaving couple of abstract
+ * methods for sub-classes to implement
+ *
+ * @since 2.8
+ */
+public abstract class ReferenceTypeSerializer<T>
+ extends StdSerializer<T>
+ implements ContextualSerializer
+{
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Value type
+ */
+ protected final JavaType _referredType;
+
+ protected final BeanProperty _property;
+
+ /**
+ * Type serializer used for values, if any.
+ */
+ protected final TypeSerializer _valueTypeSerializer;
+
+ /**
+ * Serializer for content values, if statically known.
+ */
+ protected final JsonSerializer<Object> _valueSerializer;
+
+ /**
+ * In case of unwrapping, need name transformer.
+ */
+ protected final NameTransformer _unwrapper;
+
+ /**
+ * Further guidance on serialization-inclusion (or not), regarding
+ * contained value (if any).
+ */
+ protected final JsonInclude.Include _contentInclusion;
+
+ /**
+ * If element type can not be statically determined, mapping from
+ * runtime type to serializer is handled using this object
+ */
+ protected transient PropertySerializerMap _dynamicSerializers;
+
+ /*
+ /**********************************************************
+ /* Constructors, factory methods
+ /**********************************************************
+ */
+
+ public ReferenceTypeSerializer(ReferenceType fullType, boolean staticTyping,
+ TypeSerializer vts, JsonSerializer<Object> ser)
+ {
+ super(fullType);
+ _referredType = fullType.getReferencedType();
+ _property = null;
+ _valueTypeSerializer = vts;
+ _valueSerializer = ser;
+ _unwrapper = null;
+ _contentInclusion = null;
+ _dynamicSerializers = PropertySerializerMap.emptyForProperties();
+ }
+
+ @SuppressWarnings("unchecked")
+ protected ReferenceTypeSerializer(ReferenceTypeSerializer<?> base, BeanProperty property,
+ TypeSerializer vts, JsonSerializer<?> valueSer,
+ NameTransformer unwrapper,
+ JsonInclude.Include contentIncl)
+ {
+ super(base);
+ _referredType = base._referredType;
+ _dynamicSerializers = base._dynamicSerializers;
+ _property = property;
+ _valueTypeSerializer = vts;
+ _valueSerializer = (JsonSerializer<Object>) valueSer;
+ _unwrapper = unwrapper;
+ if ((contentIncl == JsonInclude.Include.USE_DEFAULTS)
+ || (contentIncl == JsonInclude.Include.ALWAYS)) {
+ _contentInclusion = null;
+ } else {
+ _contentInclusion = contentIncl;
+ }
+ }
+
+ @Override
+ public JsonSerializer<T> unwrappingSerializer(NameTransformer transformer) {
+ JsonSerializer<Object> ser = _valueSerializer;
+ if (ser != null) {
+ ser = ser.unwrappingSerializer(transformer);
+ }
+ NameTransformer unwrapper = (_unwrapper == null) ? transformer
+ : NameTransformer.chainedTransformer(transformer, _unwrapper);
+ return withResolved(_property, _valueTypeSerializer, ser, unwrapper, _contentInclusion);
+ }
+
+ /*
+ /**********************************************************
+ /* Abstract methods to implement
+ /**********************************************************
+ */
+
+ protected abstract ReferenceTypeSerializer<T> withResolved(BeanProperty prop,
+ TypeSerializer vts, JsonSerializer<?> valueSer,
+ NameTransformer unwrapper,
+ JsonInclude.Include contentIncl);
+
+ protected abstract boolean _isValueEmpty(T value);
+
+ protected abstract Object _getReferenced(T value);
+
+ protected abstract Object _getReferencedIfPresent(T value);
+
+ /*
+ /**********************************************************
+ /* Contextualization (support for property annotations)
+ /**********************************************************
+ */
+
+ @Override
+ public JsonSerializer<?> createContextual(SerializerProvider provider,
+ BeanProperty property) throws JsonMappingException
+ {
+ TypeSerializer typeSer = _valueTypeSerializer;
+ if (typeSer != null) {
+ typeSer = typeSer.forProperty(property);
+ }
+ // First: do we have an annotation override from property?
+ JsonSerializer<?> ser = findAnnotatedContentSerializer(provider, property);;
+ if (ser == null) {
+ // If not, use whatever was configured by type
+ ser = _valueSerializer;
+ if (ser == null) {
+ // A few conditions needed to be able to fetch serializer here:
+ if (_useStatic(provider, property, _referredType)) {
+ ser = _findSerializer(provider, _referredType, property);
+ }
+ } else {
+ ser = provider.handlePrimaryContextualization(ser, property);
+ }
+ }
+ // Also: may want to have more refined exclusion based on referenced value
+ JsonInclude.Include contentIncl = _contentInclusion;
+ JsonInclude.Value incl = findIncludeOverrides(provider, property, handledType());
+ JsonInclude.Include newIncl = incl.getContentInclusion();
+ if ((newIncl != contentIncl) && (newIncl != JsonInclude.Include.USE_DEFAULTS)) {
+ contentIncl = newIncl;
+ }
+ return withResolved(property, typeSer, ser, _unwrapper, contentIncl);
+ }
+
+ protected boolean _useStatic(SerializerProvider provider, BeanProperty property,
+ JavaType referredType)
+ {
+ // First: no serializer for `Object.class`, must be dynamic
+ if (referredType.isJavaLangObject()) {
+ return false;
+ }
+ // but if type is final, might as well fetch
+ if (referredType.isFinal()) { // or should we allow annotation override? (only if requested...)
+ return true;
+ }
+ // also: if indicated by typing, should be considered static
+ if (referredType.useStaticType()) {
+ return true;
+ }
+ // if neither, maybe explicit annotation?
+ AnnotationIntrospector intr = provider.getAnnotationIntrospector();
+ if ((intr != null) && (property != null)) {
+ Annotated ann = property.getMember();
+ if (ann != null) {
+ JsonSerialize.Typing t = intr.findSerializationTyping(property.getMember());
+ if (t == JsonSerialize.Typing.STATIC) {
+ return true;
+ }
+ if (t == JsonSerialize.Typing.DYNAMIC) {
+ return false;
+ }
+ }
+ }
+ // and finally, may be forced by global static typing (unlikely...)
+ return provider.isEnabled(MapperFeature.USE_STATIC_TYPING);
+ }
+
+ /*
+ /**********************************************************
+ /* Accessors
+ /**********************************************************
+ */
+
+ @Override
+ public boolean isEmpty(SerializerProvider provider, T value)
+ {
+ if ((value == null) || _isValueEmpty(value)) {
+ return true;
+ }
+ if (_contentInclusion == null) {
+ return false;
+ }
+ Object contents = _getReferenced(value);
+ JsonSerializer<Object> ser = _valueSerializer;
+ if (ser == null) {
+ try {
+ ser = _findCachedSerializer(provider, contents.getClass());
+ } catch (JsonMappingException e) { // nasty but necessary
+ throw new RuntimeJsonMappingException(e);
+ }
+ }
+ return ser.isEmpty(provider, contents);
+ }
+
+ @Override
+ public boolean isUnwrappingSerializer() {
+ return (_unwrapper != null);
+ }
+
+ /*
+ /**********************************************************
+ /* Serialization methods
+ /**********************************************************
+ */
+
+ @Override
+ public void serialize(T ref, JsonGenerator g, SerializerProvider provider)
+ throws IOException
+ {
+ Object value = _getReferencedIfPresent(ref);
+ if (value == null) {
+ if (_unwrapper == null) {
+ provider.defaultSerializeNull(g);
+ }
+ return;
+ }
+ JsonSerializer<Object> ser = _valueSerializer;
+ if (ser == null) {
+ ser = _findCachedSerializer(provider, value.getClass());
+ }
+ if (_valueTypeSerializer != null) {
+ ser.serializeWithType(value, g, provider, _valueTypeSerializer);
+ } else {
+ ser.serialize(value, g, provider);
+ }
+ }
+
+ @Override
+ public void serializeWithType(T ref,
+ JsonGenerator g, SerializerProvider provider,
+ TypeSerializer typeSer) throws IOException
+ {
+ Object value = _getReferencedIfPresent(ref);
+ if (value == null) {
+ if (_unwrapper == null) {
+ provider.defaultSerializeNull(g);
+ }
+ return;
+ }
+
+ // 19-Apr-2016, tatu: In order to basically "skip" the whole wrapper level
+ // (which is what non-polymorphic serialization does too), we will need
+ // to simply delegate call, I think, and NOT try to use it here.
+
+ // Otherwise apply type-prefix/suffix, then std serialize:
+ /*
+ typeSer.writeTypePrefixForScalar(ref, g);
+ serialize(ref, g, provider);
+ typeSer.writeTypeSuffixForScalar(ref, g);
+ */
+ JsonSerializer<Object> ser = _valueSerializer;
+ if (ser == null) {
+ ser = _findCachedSerializer(provider, value.getClass());
+ }
+ ser.serializeWithType(value, g, provider, typeSer);
+ }
+
+ /*
+ /**********************************************************
+ /* Introspection support
+ /**********************************************************
+ */
+
+ @Override
+ public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+ throws JsonMappingException
+ {
+ JsonSerializer<?> ser = _valueSerializer;
+ if (ser == null) {
+ ser = _findSerializer(visitor.getProvider(), _referredType, _property);
+ if (_unwrapper != null) {
+ ser = ser.unwrappingSerializer(_unwrapper);
+ }
+ }
+ ser.acceptJsonFormatVisitor(visitor, _referredType);
+ }
+
+ /*
+ /**********************************************************
+ /* Helper methods
+ /**********************************************************
+ */
+
+ /**
+ * Helper method that encapsulates logic of retrieving and caching required
+ * serializer.
+ */
+ private final JsonSerializer<Object> _findCachedSerializer(SerializerProvider provider,
+ Class<?> type) throws JsonMappingException
+ {
+ JsonSerializer<Object> ser = _dynamicSerializers.serializerFor(type);
+ if (ser == null) {
+ ser = _findSerializer(provider, type, _property);
+ if (_unwrapper != null) {
+ ser = ser.unwrappingSerializer(_unwrapper);
+ }
+ _dynamicSerializers = _dynamicSerializers.newWith(type, ser);
+ }
+ return ser;
+ }
+
+ private final JsonSerializer<Object> _findSerializer(SerializerProvider provider,
+ Class<?> type, BeanProperty prop) throws JsonMappingException
+ {
+ return provider.findTypedValueSerializer(type, true, prop);
+ }
+
+ private final JsonSerializer<Object> _findSerializer(SerializerProvider provider,
+ JavaType type, BeanProperty prop) throws JsonMappingException
+ {
+ return provider.findTypedValueSerializer(type, true, prop);
+ }
+
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/SerializableSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/SerializableSerializer.java
index 34190b3..793a49c 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/SerializableSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/SerializableSerializer.java
@@ -85,16 +85,14 @@
try {
objectNode.set("properties", _getObjectMapper().readTree(objectProperties));
} catch (IOException e) {
- throw JsonMappingException.from(provider,
- "Failed to parse @JsonSerializableSchema.schemaObjectPropertiesDefinition value");
+ provider.reportMappingProblem("Failed to parse @JsonSerializableSchema.schemaObjectPropertiesDefinition value");
}
}
if (itemDefinition != null) {
try {
objectNode.set("items", _getObjectMapper().readTree(itemDefinition));
} catch (IOException e) {
- throw JsonMappingException.from(provider,
- "Failed to parse @JsonSerializableSchema.schemaItemDefinition value");
+ provider.reportMappingProblem("Failed to parse @JsonSerializableSchema.schemaItemDefinition value");
}
}
// always optional, no need to specify:
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StaticListSerializerBase.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StaticListSerializerBase.java
index 0e40c89..759c46d 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/StaticListSerializerBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StaticListSerializerBase.java
@@ -60,35 +60,36 @@
*/
@Override
- public JsonSerializer<?> createContextual(SerializerProvider provider, BeanProperty property)
- throws JsonMappingException
+ public JsonSerializer<?> createContextual(SerializerProvider serializers,
+ BeanProperty property)
+ throws JsonMappingException
{
JsonSerializer<?> ser = null;
Boolean unwrapSingle = null;
if (property != null) {
- final AnnotationIntrospector intr = provider.getAnnotationIntrospector();
+ final AnnotationIntrospector intr = serializers.getAnnotationIntrospector();
AnnotatedMember m = property.getMember();
if (m != null) {
Object serDef = intr.findContentSerializer(m);
if (serDef != null) {
- ser = provider.serializerInstance(m, serDef);
+ ser = serializers.serializerInstance(m, serDef);
}
}
- JsonFormat.Value format = property.findPropertyFormat(provider.getConfig(), _handledType);
- if (format != null) {
- unwrapSingle = format.getFeature(JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED);
- }
+ }
+ JsonFormat.Value format = findFormatOverrides(serializers, property, handledType());
+ if (format != null) {
+ unwrapSingle = format.getFeature(JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED);
}
if (ser == null) {
ser = _serializer;
}
// [databind#124]: May have a content converter
- ser = findConvertingContentSerializer(provider, property, ser);
+ ser = findConvertingContentSerializer(serializers, property, ser);
if (ser == null) {
- ser = provider.findValueSerializer(String.class, property);
+ ser = serializers.findValueSerializer(String.class, property);
} else {
- ser = provider.handleSecondaryContextualization(ser, property);
+ ser = serializers.handleSecondaryContextualization(ser, property);
}
// Optimization: default serializer just writes String, so we can avoid a call:
if (isDefaultSerializer(ser)) {
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdArraySerializers.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdArraySerializers.java
index 417cc9e..16c7e96 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdArraySerializers.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdArraySerializers.java
@@ -86,6 +86,7 @@
extends ArraySerializerBase<boolean[]>
{
// as above, assuming no one re-defines primitive/wrapper types
+ @SuppressWarnings("deprecation")
private final static JavaType VALUE_TYPE = TypeFactory.defaultInstance().uncheckedSimpleType(Boolean.class);
public BooleanArraySerializer() { super(boolean[].class); }
@@ -131,28 +132,28 @@
}
@Override
- public final void serialize(boolean[] value, JsonGenerator jgen, SerializerProvider provider) throws IOException
+ public final void serialize(boolean[] value, JsonGenerator g, SerializerProvider provider) throws IOException
{
final int len = value.length;
if (len == 1) {
if (((_unwrapSingle == null) &&
provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED))
|| (_unwrapSingle == Boolean.TRUE)) {
- serializeContents(value, jgen, provider);
+ serializeContents(value, g, provider);
return;
}
}
- jgen.writeStartArray(len);
- serializeContents(value, jgen, provider);
- jgen.writeEndArray();
+ g.writeStartArray(len);
+ serializeContents(value, g, provider);
+ g.writeEndArray();
}
@Override
- public void serializeContents(boolean[] value, JsonGenerator jgen, SerializerProvider provider)
+ public void serializeContents(boolean[] value, JsonGenerator g, SerializerProvider provider)
throws IOException, JsonGenerationException
{
for (int i = 0, len = value.length; i < len; ++i) {
- jgen.writeBoolean(value[i]);
+ g.writeBoolean(value[i]);
}
}
@@ -176,6 +177,7 @@
public static class ShortArraySerializer extends TypedPrimitiveArraySerializer<short[]>
{
// as above, assuming no one re-defines primitive/wrapper types
+ @SuppressWarnings("deprecation")
private final static JavaType VALUE_TYPE = TypeFactory.defaultInstance().uncheckedSimpleType(Short.TYPE);
public ShortArraySerializer() { super(short[].class); }
@@ -216,37 +218,37 @@
}
@Override
- public final void serialize(short[] value, JsonGenerator jgen, SerializerProvider provider) throws IOException
+ public final void serialize(short[] value, JsonGenerator g, SerializerProvider provider) throws IOException
{
final int len = value.length;
if (len == 1) {
if (((_unwrapSingle == null) &&
provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED))
|| (_unwrapSingle == Boolean.TRUE)) {
- serializeContents(value, jgen, provider);
+ serializeContents(value, g, provider);
return;
}
}
- jgen.writeStartArray(len);
- serializeContents(value, jgen, provider);
- jgen.writeEndArray();
+ g.writeStartArray(len);
+ serializeContents(value, g, provider);
+ g.writeEndArray();
}
@SuppressWarnings("cast")
@Override
- public void serializeContents(short[] value, JsonGenerator jgen, SerializerProvider provider)
+ public void serializeContents(short[] value, JsonGenerator g, SerializerProvider provider)
throws IOException, JsonGenerationException
{
if (_valueTypeSerializer != null) {
for (int i = 0, len = value.length; i < len; ++i) {
- _valueTypeSerializer.writeTypePrefixForScalar(null, jgen, Short.TYPE);
- jgen.writeNumber(value[i]);
- _valueTypeSerializer.writeTypeSuffixForScalar(null, jgen);
+ _valueTypeSerializer.writeTypePrefixForScalar(null, g, Short.TYPE);
+ g.writeNumber(value[i]);
+ _valueTypeSerializer.writeTypeSuffixForScalar(null, g);
}
return;
}
for (int i = 0, len = value.length; i < len; ++i) {
- jgen.writeNumber((int)value[i]);
+ g.writeNumber((int)value[i]);
}
}
@@ -284,41 +286,41 @@
}
@Override
- public void serialize(char[] value, JsonGenerator jgen, SerializerProvider provider)
+ public void serialize(char[] value, JsonGenerator g, SerializerProvider provider)
throws IOException, JsonGenerationException
{
// [JACKSON-289] allows serializing as 'sparse' char array too:
if (provider.isEnabled(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS)) {
- jgen.writeStartArray(value.length);
- _writeArrayContents(jgen, value);
- jgen.writeEndArray();
+ g.writeStartArray(value.length);
+ _writeArrayContents(g, value);
+ g.writeEndArray();
} else {
- jgen.writeString(value, 0, value.length);
+ g.writeString(value, 0, value.length);
}
}
@Override
- public void serializeWithType(char[] value, JsonGenerator jgen, SerializerProvider provider,
+ public void serializeWithType(char[] value, JsonGenerator g, SerializerProvider provider,
TypeSerializer typeSer)
throws IOException, JsonGenerationException
{
// [JACKSON-289] allows serializing as 'sparse' char array too:
if (provider.isEnabled(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS)) {
- typeSer.writeTypePrefixForArray(value, jgen);
- _writeArrayContents(jgen, value);
- typeSer.writeTypeSuffixForArray(value, jgen);
+ typeSer.writeTypePrefixForArray(value, g);
+ _writeArrayContents(g, value);
+ typeSer.writeTypeSuffixForArray(value, g);
} else { // default is to write as simple String
- typeSer.writeTypePrefixForScalar(value, jgen);
- jgen.writeString(value, 0, value.length);
- typeSer.writeTypeSuffixForScalar(value, jgen);
+ typeSer.writeTypePrefixForScalar(value, g);
+ g.writeString(value, 0, value.length);
+ typeSer.writeTypeSuffixForScalar(value, g);
}
}
- private final void _writeArrayContents(JsonGenerator jgen, char[] value)
+ private final void _writeArrayContents(JsonGenerator g, char[] value)
throws IOException, JsonGenerationException
{
for (int i = 0, len = value.length; i < len; ++i) {
- jgen.writeString(value, i, 1);
+ g.writeString(value, i, 1);
}
}
@@ -343,6 +345,7 @@
public static class IntArraySerializer extends ArraySerializerBase<int[]>
{
// as above, assuming no one re-defines primitive/wrapper types
+ @SuppressWarnings("deprecation")
private final static JavaType VALUE_TYPE = TypeFactory.defaultInstance().uncheckedSimpleType(Integer.TYPE);
public IntArraySerializer() { super(int[].class); }
@@ -391,28 +394,28 @@
}
@Override
- public final void serialize(int[] value, JsonGenerator jgen, SerializerProvider provider) throws IOException
+ public final void serialize(int[] value, JsonGenerator g, SerializerProvider provider) throws IOException
{
final int len = value.length;
if (len == 1) {
if (((_unwrapSingle == null) &&
provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED))
|| (_unwrapSingle == Boolean.TRUE)) {
- serializeContents(value, jgen, provider);
+ serializeContents(value, g, provider);
return;
}
}
- jgen.writeStartArray(len);
- serializeContents(value, jgen, provider);
- jgen.writeEndArray();
+ // 11-May-2016, tatu: As per [core#277] we have efficient `writeArray(...)` available
+ g.setCurrentValue(value);
+ g.writeArray(value, 0, value.length);
}
@Override
- public void serializeContents(int[] value, JsonGenerator jgen, SerializerProvider provider)
+ public void serializeContents(int[] value, JsonGenerator g, SerializerProvider provider)
throws IOException
{
for (int i = 0, len = value.length; i < len; ++i) {
- jgen.writeNumber(value[i]);
+ g.writeNumber(value[i]);
}
}
@@ -432,6 +435,7 @@
public static class LongArraySerializer extends TypedPrimitiveArraySerializer<long[]>
{
// as above, assuming no one re-defines primitive/wrapper types
+ @SuppressWarnings("deprecation")
private final static JavaType VALUE_TYPE = TypeFactory.defaultInstance().uncheckedSimpleType(Long.TYPE);
public LongArraySerializer() { super(long[].class); }
@@ -472,37 +476,37 @@
}
@Override
- public final void serialize(long[] value, JsonGenerator jgen, SerializerProvider provider) throws IOException
+ public final void serialize(long[] value, JsonGenerator g, SerializerProvider provider) throws IOException
{
final int len = value.length;
if (len == 1) {
if (((_unwrapSingle == null) &&
provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED))
|| (_unwrapSingle == Boolean.TRUE)) {
- serializeContents(value, jgen, provider);
+ serializeContents(value, g, provider);
return;
}
}
- jgen.writeStartArray(len);
- serializeContents(value, jgen, provider);
- jgen.writeEndArray();
+ // 11-May-2016, tatu: As per [core#277] we have efficient `writeArray(...)` available
+ g.setCurrentValue(value);
+ g.writeArray(value, 0, value.length);
}
@Override
- public void serializeContents(long[] value, JsonGenerator jgen, SerializerProvider provider)
+ public void serializeContents(long[] value, JsonGenerator g, SerializerProvider provider)
throws IOException
{
if (_valueTypeSerializer != null) {
for (int i = 0, len = value.length; i < len; ++i) {
- _valueTypeSerializer.writeTypePrefixForScalar(null, jgen, Long.TYPE);
- jgen.writeNumber(value[i]);
- _valueTypeSerializer.writeTypeSuffixForScalar(null, jgen);
+ _valueTypeSerializer.writeTypePrefixForScalar(null, g, Long.TYPE);
+ g.writeNumber(value[i]);
+ _valueTypeSerializer.writeTypeSuffixForScalar(null, g);
}
return;
}
for (int i = 0, len = value.length; i < len; ++i) {
- jgen.writeNumber(value[i]);
+ g.writeNumber(value[i]);
}
}
@@ -525,6 +529,7 @@
public static class FloatArraySerializer extends TypedPrimitiveArraySerializer<float[]>
{
// as above, assuming no one re-defines primitive/wrapper types
+ @SuppressWarnings("deprecation")
private final static JavaType VALUE_TYPE = TypeFactory.defaultInstance().uncheckedSimpleType(Float.TYPE);
public FloatArraySerializer() {
@@ -567,36 +572,36 @@
}
@Override
- public final void serialize(float[] value, JsonGenerator gen, SerializerProvider provider) throws IOException
+ public final void serialize(float[] value, JsonGenerator g, SerializerProvider provider) throws IOException
{
final int len = value.length;
if (len == 1) {
if (((_unwrapSingle == null) &&
provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED))
|| (_unwrapSingle == Boolean.TRUE)) {
- serializeContents(value, gen, provider);
+ serializeContents(value, g, provider);
return;
}
}
- gen.writeStartArray(len);
- serializeContents(value, gen, provider);
- gen.writeEndArray();
+ g.writeStartArray(len);
+ serializeContents(value, g, provider);
+ g.writeEndArray();
}
@Override
- public void serializeContents(float[] value, JsonGenerator gen, SerializerProvider provider)
+ public void serializeContents(float[] value, JsonGenerator g, SerializerProvider provider)
throws IOException, JsonGenerationException
{
if (_valueTypeSerializer != null) {
for (int i = 0, len = value.length; i < len; ++i) {
- _valueTypeSerializer.writeTypePrefixForScalar(null, gen, Float.TYPE);
- gen.writeNumber(value[i]);
- _valueTypeSerializer.writeTypeSuffixForScalar(null, gen);
+ _valueTypeSerializer.writeTypePrefixForScalar(null, g, Float.TYPE);
+ g.writeNumber(value[i]);
+ _valueTypeSerializer.writeTypeSuffixForScalar(null, g);
}
return;
}
for (int i = 0, len = value.length; i < len; ++i) {
- gen.writeNumber(value[i]);
+ g.writeNumber(value[i]);
}
}
@@ -616,6 +621,7 @@
public static class DoubleArraySerializer extends ArraySerializerBase<double[]>
{
// as above, assuming no one re-defines primitive/wrapper types
+ @SuppressWarnings("deprecation")
private final static JavaType VALUE_TYPE = TypeFactory.defaultInstance().uncheckedSimpleType(Double.TYPE);
public DoubleArraySerializer() { super(double[].class); }
@@ -664,27 +670,27 @@
}
@Override
- public final void serialize(double[] value, JsonGenerator gen, SerializerProvider provider) throws IOException
+ public final void serialize(double[] value, JsonGenerator g, SerializerProvider provider) throws IOException
{
final int len = value.length;
if (len == 1) {
if (((_unwrapSingle == null) &&
provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED))
|| (_unwrapSingle == Boolean.TRUE)) {
- serializeContents(value, gen, provider);
+ serializeContents(value, g, provider);
return;
}
}
- gen.writeStartArray(len);
- serializeContents(value, gen, provider);
- gen.writeEndArray();
+ // 11-May-2016, tatu: As per [core#277] we have efficient `writeArray(...)` available
+ g.setCurrentValue(value);
+ g.writeArray(value, 0, value.length);
}
@Override
- public void serializeContents(double[] value, JsonGenerator gen, SerializerProvider provider) throws IOException
+ public void serializeContents(double[] value, JsonGenerator g, SerializerProvider provider) throws IOException
{
for (int i = 0, len = value.length; i < len; ++i) {
- gen.writeNumber(value[i]);
+ g.writeNumber(value[i]);
}
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializer.java
index 3fbb8d0..b708a02 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializer.java
@@ -12,8 +12,11 @@
* Specialized serializer that can be used as the generic key
* serializer, when serializing {@link java.util.Map}s to JSON
* Objects.
+ *
+ * @deprecated Since 2.8, use {@link StdKeySerializers.Default} instead.
*/
@SuppressWarnings("serial")
+@Deprecated // since 2.8,
public class StdKeySerializer extends StdSerializer<Object>
{
public StdKeySerializer() { super(Object.class); }
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializers.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializers.java
index 7afcc55..fd6c99b 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializers.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializers.java
@@ -6,12 +6,14 @@
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap;
import com.fasterxml.jackson.databind.util.EnumValues;
@SuppressWarnings("serial")
public class StdKeySerializers
{
+ @SuppressWarnings("deprecation")
protected final static JsonSerializer<Object> DEFAULT_KEY_SERIALIZER = new StdKeySerializer();
protected final static JsonSerializer<Object> DEFAULT_STRING_SERIALIZER = new StringKeySerializer();
@@ -41,7 +43,9 @@
return DEFAULT_STRING_SERIALIZER;
}
if (rawKeyType.isPrimitive() || Number.class.isAssignableFrom(rawKeyType)) {
- return DEFAULT_KEY_SERIALIZER;
+ // 28-Jun-2016, tatu: Used to just return DEFAULT_KEY_SERIALIZER, but makes
+ // more sense to use simpler one directly
+ return new Default(Default.TYPE_TO_STRING, rawKeyType);
}
if (rawKeyType == Class.class) {
return new Default(Default.TYPE_CLASS, rawKeyType);
@@ -56,7 +60,10 @@
if (rawKeyType == java.util.UUID.class) {
return new Default(Default.TYPE_TO_STRING, rawKeyType);
}
- return useDefault ? DEFAULT_KEY_SERIALIZER : null;
+ if (useDefault) {
+ return DEFAULT_KEY_SERIALIZER;
+ }
+ return null;
}
/**
@@ -182,6 +189,11 @@
ser.serialize(value, g, provider);
}
+ @Override
+ public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException {
+ visitStringFormat(visitor, typeHint);
+ }
+
protected JsonSerializer<Object> _findAndAddDynamic(PropertySerializerMap map,
Class<?> type, SerializerProvider provider) throws JsonMappingException
{
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdSerializer.java
index 010f53d..a1108e8 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdSerializer.java
@@ -5,6 +5,7 @@
import java.lang.reflect.Type;
import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.JsonParser.NumberType;
import com.fasterxml.jackson.databind.*;
@@ -440,9 +441,8 @@
throw JsonMappingException.from(provider,
"Can not resolve PropertyFilter with id '"+filterId+"'; no FilterProvider configured");
}
- PropertyFilter filter = filters.findPropertyFilter(filterId, valueToFilter);
// But whether unknown ids are ok just depends on filter provider; if we get null that's fine
- return filter;
+ return filters.findPropertyFilter(filterId, valueToFilter);
}
/**
@@ -484,6 +484,19 @@
}
/**
+ * @since 2.8
+ */
+ protected JsonInclude.Value findIncludeOverrides(SerializerProvider provider,
+ BeanProperty prop, Class<?> typeForDefaults)
+ {
+ if (prop != null) {
+ return prop.findPropertyInclusion(provider.getConfig(), typeForDefaults);
+ }
+ // even without property or AnnotationIntrospector, may have type-specific defaults
+ return provider.getDefaultPropertyInclusion(typeForDefaults);
+ }
+
+ /**
* Convenience method for finding out possibly configured content value serializer.
*
* @since 2.7.4
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/ToStringSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/ToStringSerializer.java
index f9d3a69..5d3aa91 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/ToStringSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/ToStringSerializer.java
@@ -47,12 +47,6 @@
}
@Override
- @Deprecated
- public boolean isEmpty(Object value) {
- return isEmpty(null, value);
- }
-
- @Override
public boolean isEmpty(SerializerProvider prov, Object value) {
if (value == null) {
return true;
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/ArrayType.java b/src/main/java/com/fasterxml/jackson/databind/type/ArrayType.java
index d4543a2..24ac759 100644
--- a/src/main/java/com/fasterxml/jackson/databind/type/ArrayType.java
+++ b/src/main/java/com/fasterxml/jackson/databind/type/ArrayType.java
@@ -182,6 +182,11 @@
}
@Override
+ public boolean hasHandlers() {
+ return super.hasHandlers() || _componentType.hasHandlers();
+ }
+
+ @Override
public StringBuilder getGenericSignature(StringBuilder sb) {
sb.append('[');
return _componentType.getGenericSignature(sb);
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/CollectionLikeType.java b/src/main/java/com/fasterxml/jackson/databind/type/CollectionLikeType.java
index 45d6ca4..f355160 100644
--- a/src/main/java/com/fasterxml/jackson/databind/type/CollectionLikeType.java
+++ b/src/main/java/com/fasterxml/jackson/databind/type/CollectionLikeType.java
@@ -134,6 +134,19 @@
}
@Override
+ public JavaType withHandlersFrom(JavaType src) {
+ JavaType type = super.withHandlersFrom(src);
+ JavaType srcCt = src.getContentType();
+ if (srcCt != null) {
+ JavaType ct = _elementType.withHandlersFrom(srcCt);
+ if (ct != _elementType) {
+ type = type.withContentType(ct);
+ }
+ }
+ return type;
+ }
+
+ @Override
public CollectionLikeType withStaticTyping() {
if (_asStatic) {
return this;
@@ -177,6 +190,11 @@
}
@Override
+ public boolean hasHandlers() {
+ return super.hasHandlers() || _elementType.hasHandlers();
+ }
+
+ @Override
public StringBuilder getErasedSignature(StringBuilder sb) {
return _classSignature(_class, sb, true);
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/CollectionType.java b/src/main/java/com/fasterxml/jackson/databind/type/CollectionType.java
index e0a7856..7b38ab4 100644
--- a/src/main/java/com/fasterxml/jackson/databind/type/CollectionType.java
+++ b/src/main/java/com/fasterxml/jackson/databind/type/CollectionType.java
@@ -42,7 +42,7 @@
}
/**
- * @deprecated Since 2.7, remove from 2.8
+ * @deprecated Since 2.7, remove from 2.9
*/
@Deprecated // since 2.7
public static CollectionType construct(Class<?> rawType, JavaType elemT) {
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/MapLikeType.java b/src/main/java/com/fasterxml/jackson/databind/type/MapLikeType.java
index b8d5ea4..f1d1ee0 100644
--- a/src/main/java/com/fasterxml/jackson/databind/type/MapLikeType.java
+++ b/src/main/java/com/fasterxml/jackson/databind/type/MapLikeType.java
@@ -27,8 +27,9 @@
protected final JavaType _valueType;
/*
- * /********************************************************** /* Life-cycle
- * /**********************************************************
+ /**********************************************************
+ * Life-cycle
+ /**********************************************************
*/
protected MapLikeType(Class<?> mapType, TypeBindings bindings,
@@ -145,6 +146,29 @@
}
@Override
+ public JavaType withHandlersFrom(JavaType src) {
+ JavaType type = super.withHandlersFrom(src);
+ JavaType srcKeyType = src.getKeyType();
+ // "withKeyType()" not part of JavaType, hence must verify:
+ if (type instanceof MapLikeType) {
+ if (srcKeyType != null) {
+ JavaType ct = _keyType.withHandlersFrom(srcKeyType);
+ if (ct != _keyType) {
+ type = ((MapLikeType) type).withKeyType(ct);
+ }
+ }
+ }
+ JavaType srcCt = src.getContentType();
+ if (srcCt != null) {
+ JavaType ct = _valueType.withHandlersFrom(srcCt);
+ if (ct != _valueType) {
+ type = type.withContentType(ct);
+ }
+ }
+ return type;
+ }
+
+ @Override
public MapLikeType withStaticTyping() {
if (_asStatic) {
return this;
@@ -176,8 +200,9 @@
}
/*
- * /********************************************************** /* Public API
- * /**********************************************************
+ /**********************************************************
+ /* Public API
+ /**********************************************************
*/
@Override
@@ -211,6 +236,12 @@
}
@Override
+ public boolean hasHandlers() {
+ return super.hasHandlers() || _valueType.hasHandlers()
+ || _keyType.hasHandlers();
+ }
+
+ @Override
public StringBuilder getErasedSignature(StringBuilder sb) {
return _classSignature(_class, sb, true);
}
@@ -226,8 +257,9 @@
}
/*
- * /********************************************************** /* Extended
- * API /**********************************************************
+ /**********************************************************
+ /* Extended API
+ /**********************************************************
*/
public MapLikeType withKeyTypeHandler(Object h) {
@@ -252,24 +284,22 @@
}
/*
- * /********************************************************** /* Standard
- * methods /**********************************************************
+ /**********************************************************
+ /* Standard methods
+ /**********************************************************
*/
@Override
public String toString() {
- return "[map-like type; class " + _class.getName() + ", " + _keyType
- + " -> " + _valueType + "]";
+ return String.format("[map-like type; class %s, %s -> %s]",
+ _class.getName(), _keyType, _valueType);
}
@Override
public boolean equals(Object o) {
- if (o == this)
- return true;
- if (o == null)
- return false;
- if (o.getClass() != getClass())
- return false;
+ if (o == this) return true;
+ if (o == null) return false;
+ if (o.getClass() != getClass()) return false;
MapLikeType other = (MapLikeType) o;
return (_class == other._class) && _keyType.equals(other._keyType)
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/ReferenceType.java b/src/main/java/com/fasterxml/jackson/databind/type/ReferenceType.java
index 76deae2..5779e83 100644
--- a/src/main/java/com/fasterxml/jackson/databind/type/ReferenceType.java
+++ b/src/main/java/com/fasterxml/jackson/databind/type/ReferenceType.java
@@ -16,38 +16,59 @@
protected final JavaType _referencedType;
+ /**
+ * Essential type used for type ids, for example if type id is needed for
+ * referencing type with polymorphic handling. Typically initialized when
+ * a {@link SimpleType} is upgraded into reference type, but NOT changed
+ * if being sub-classed.
+ *
+ * @since 2.8
+ */
+ protected final JavaType _anchorType;
+
protected ReferenceType(Class<?> cls, TypeBindings bindings,
JavaType superClass, JavaType[] superInts, JavaType refType,
+ JavaType anchorType,
Object valueHandler, Object typeHandler, boolean asStatic)
{
super(cls, bindings, superClass, superInts, refType.hashCode(),
valueHandler, typeHandler, asStatic);
_referencedType = refType;
+ _anchorType = (anchorType == null) ? this : anchorType;
}
/**
+ * Constructor used when upgrading into this type (via {@link #upgradeFrom},
+ * the usual way for {@link ReferenceType}s to come into existence.
+ * Sets up what is considered the "base" reference type
+ *
* @since 2.7
*/
protected ReferenceType(TypeBase base, JavaType refType)
{
super(base);
_referencedType = refType;
+ // we'll establish this as the anchor type
+ _anchorType = this;
}
/**
* Factory method that can be used to "upgrade" a basic type into collection-like
* one; usually done via {@link TypeModifier}
+ *
+ * @param baseType Resolved non-reference type (usually {@link SimpleType}) that is being upgraded
+ * @param refdType Referenced type; usually the first and only type parameter, but not necessarily
*
* @since 2.7
*/
- public static ReferenceType upgradeFrom(JavaType baseType, JavaType refType) {
- if (refType == null) {
+ public static ReferenceType upgradeFrom(JavaType baseType, JavaType refdType) {
+ if (refdType == null) {
throw new IllegalArgumentException("Missing referencedType");
}
// 19-Oct-2015, tatu: Not sure if and how other types could be used as base;
// will cross that bridge if and when need be
if (baseType instanceof TypeBase) {
- return new ReferenceType((TypeBase) baseType, refType);
+ return new ReferenceType((TypeBase) baseType, refdType);
}
throw new IllegalArgumentException("Can not upgrade from an instance of "+baseType.getClass());
}
@@ -59,14 +80,14 @@
JavaType superClass, JavaType[] superInts, JavaType refType)
{
return new ReferenceType(cls, bindings, superClass, superInts,
- refType, null, null, false);
+ refType, null, null, null, false);
}
@Deprecated // since 2.7
public static ReferenceType construct(Class<?> cls, JavaType refType) {
return new ReferenceType(cls, TypeBindings.emptyBindings(),
// !!! TODO: missing supertypes
- null, null, refType, null, null, false);
+ null, null, null, refType, null, null, false);
}
@Override
@@ -75,7 +96,7 @@
return this;
}
return new ReferenceType(_class, _bindings, _superClass, _superInterfaces,
- contentType, _valueHandler, _typeHandler, _asStatic);
+ contentType, _anchorType, _valueHandler, _typeHandler, _asStatic);
}
@Override
@@ -84,8 +105,8 @@
if (h == _typeHandler) {
return this;
}
- return new ReferenceType(_class, _bindings,
- _superClass, _superInterfaces, _referencedType, _valueHandler, h, _asStatic);
+ return new ReferenceType(_class, _bindings, _superClass, _superInterfaces,
+ _referencedType, _anchorType, _valueHandler, h, _asStatic);
}
@Override
@@ -94,8 +115,8 @@
if (h == _referencedType.<Object>getTypeHandler()) {
return this;
}
- return new ReferenceType(_class, _bindings,
- _superClass, _superInterfaces, _referencedType.withTypeHandler(h),
+ return new ReferenceType(_class, _bindings, _superClass, _superInterfaces,
+ _referencedType.withTypeHandler(h), _anchorType,
_valueHandler, _typeHandler, _asStatic);
}
@@ -105,7 +126,8 @@
return this;
}
return new ReferenceType(_class, _bindings,
- _superClass, _superInterfaces, _referencedType, h, _typeHandler,_asStatic);
+ _superClass, _superInterfaces, _referencedType, _anchorType,
+ h, _typeHandler,_asStatic);
}
@Override
@@ -115,7 +137,7 @@
}
JavaType refdType = _referencedType.withValueHandler(h);
return new ReferenceType(_class, _bindings,
- _superClass, _superInterfaces, refdType,
+ _superClass, _superInterfaces, refdType, _anchorType,
_valueHandler, _typeHandler, _asStatic);
}
@@ -124,8 +146,8 @@
if (_asStatic) {
return this;
}
- return new ReferenceType(_class, _bindings,
- _superClass, _superInterfaces, _referencedType.withStaticTyping(),
+ return new ReferenceType(_class, _bindings, _superClass, _superInterfaces,
+ _referencedType.withStaticTyping(), _anchorType,
_valueHandler, _typeHandler, true);
}
@@ -133,7 +155,7 @@
public JavaType refine(Class<?> rawType, TypeBindings bindings,
JavaType superClass, JavaType[] superInterfaces) {
return new ReferenceType(rawType, _bindings,
- superClass, superInterfaces, _referencedType,
+ superClass, superInterfaces, _referencedType, _anchorType,
_valueHandler, _typeHandler, _asStatic);
}
@@ -159,7 +181,7 @@
{
// Should we check that there is a sub-class relationship?
return new ReferenceType(subclass, _bindings,
- _superClass, _superInterfaces, _referencedType,
+ _superClass, _superInterfaces, _referencedType, _anchorType,
_valueHandler, _typeHandler, _asStatic);
}
@@ -173,13 +195,18 @@
public JavaType getContentType() {
return _referencedType;
}
-
+
@Override
public JavaType getReferencedType() {
return _referencedType;
}
@Override
+ public boolean hasContentType() {
+ return true;
+ }
+
+ @Override
public boolean isReferenceType() {
return true;
}
@@ -198,7 +225,25 @@
sb.append(">;");
return sb;
}
-
+
+ /*
+ /**********************************************************
+ /* Extended API
+ /**********************************************************
+ */
+
+ public JavaType getAnchorType() {
+ return _anchorType;
+ }
+
+ /**
+ * Convenience accessor that allows checking whether this is the anchor type
+ * itself; if not, it must be one of supertypes that is also a {@link ReferenceType}
+ */
+ public boolean isAnchorType() {
+ return (_anchorType == this);
+ }
+
/*
/**********************************************************
/* Standard methods
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/ResolvedRecursiveType.java b/src/main/java/com/fasterxml/jackson/databind/type/ResolvedRecursiveType.java
index 13c3e59..21f37fe 100644
--- a/src/main/java/com/fasterxml/jackson/databind/type/ResolvedRecursiveType.java
+++ b/src/main/java/com/fasterxml/jackson/databind/type/ResolvedRecursiveType.java
@@ -103,8 +103,11 @@
public boolean equals(Object o) {
if (o == this) return true;
if (o == null) return false;
- if (o.getClass() != getClass()) return false;
-
- return ((ResolvedRecursiveType) o).getSelfReferencedType().equals(getSelfReferencedType());
+ // Do NOT ever match unresolved references
+ if (_referencedType == null) {
+ return false;
+ }
+ return (o.getClass() == getClass()
+ && _referencedType.equals(((ResolvedRecursiveType) o).getSelfReferencedType()));
}
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/SimpleType.java b/src/main/java/com/fasterxml/jackson/databind/type/SimpleType.java
index 0273c70..c48c0f3 100644
--- a/src/main/java/com/fasterxml/jackson/databind/type/SimpleType.java
+++ b/src/main/java/com/fasterxml/jackson/databind/type/SimpleType.java
@@ -128,7 +128,7 @@
// Should we check that there is a sub-class relationship?
// 15-Jan-2016, tatu: Almost yes, but there are some complications with
// placeholder values (`Void`, `NoClass`), so can not quite do yet.
- // TODO: fix in 2.8
+ // TODO: fix in 2.9
if (!_class.isAssignableFrom(subclass)) {
/*
throw new IllegalArgumentException("Class "+subclass.getName()+" not sub-type of "
@@ -241,6 +241,9 @@
@Override
public boolean isContainerType() { return false; }
+
+ @Override
+ public boolean hasContentType() { return false; }
@Override
public StringBuilder getErasedSignature(StringBuilder sb) {
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/TypeBase.java b/src/main/java/com/fasterxml/jackson/databind/type/TypeBase.java
index 73e1d94..056c727 100644
--- a/src/main/java/com/fasterxml/jackson/databind/type/TypeBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/type/TypeBase.java
@@ -81,14 +81,6 @@
public abstract StringBuilder getErasedSignature(StringBuilder sb);
@Override
- @SuppressWarnings("unchecked")
- public <T> T getValueHandler() { return (T) _valueHandler; }
-
- @Override
- @SuppressWarnings("unchecked")
- public <T> T getTypeHandler() { return (T) _typeHandler; }
-
- @Override
public TypeBindings getBindings() {
return _bindings;
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/TypeFactory.java b/src/main/java/com/fasterxml/jackson/databind/type/TypeFactory.java
index 1e85958..b8acda8 100644
--- a/src/main/java/com/fasterxml/jackson/databind/type/TypeFactory.java
+++ b/src/main/java/com/fasterxml/jackson/databind/type/TypeFactory.java
@@ -114,7 +114,7 @@
* actual generic types), we will use small cache to avoid repetitive
* resolution of core types
*/
- protected final LRUMap<Object,JavaType> _typeCache = new LRUMap<Object,JavaType>(16, 100);
+ protected final LRUMap<Object,JavaType> _typeCache;
/*
/**********************************************************
@@ -142,16 +142,29 @@
*/
private TypeFactory() {
+ this(null);
+ }
+
+ /**
+ * @since 2.8
+ */
+ protected TypeFactory(LRUMap<Object,JavaType> typeCache) {
+ if (typeCache == null) {
+ typeCache = new LRUMap<Object,JavaType>(16, 200);
+ }
+ _typeCache = typeCache;
_parser = new TypeParser(this);
_modifiers = null;
_classLoader = null;
}
- protected TypeFactory(TypeParser p, TypeModifier[] mods) {
- this(p, mods, null);
- }
-
- protected TypeFactory(TypeParser p, TypeModifier[] mods, ClassLoader classLoader) {
+ protected TypeFactory(LRUMap<Object,JavaType> typeCache, TypeParser p,
+ TypeModifier[] mods, ClassLoader classLoader)
+ {
+ if (typeCache == null) {
+ typeCache = new LRUMap<Object,JavaType>(16, 200);
+ }
+ _typeCache = typeCache;
// As per [databind#894] must ensure we have back-linkage from TypeFactory:
_parser = p.withFactory(this);
_modifiers = mods;
@@ -160,17 +173,34 @@
public TypeFactory withModifier(TypeModifier mod)
{
+ LRUMap<Object,JavaType> typeCache = _typeCache;
+ TypeModifier[] mods;
if (mod == null) { // mostly for unit tests
- return new TypeFactory(_parser, _modifiers, _classLoader);
+ mods = null;
+ // 30-Jun-2016, tatu: for some reason expected semantics are to clear cache
+ // in this case; can't recall why, but keeping the same
+ typeCache = null;
+ } else if (_modifiers == null) {
+ mods = new TypeModifier[] { mod };
+ } else {
+ mods = ArrayBuilders.insertInListNoDup(_modifiers, mod);
}
- if (_modifiers == null) {
- return new TypeFactory(_parser, new TypeModifier[] { mod }, _classLoader);
- }
- return new TypeFactory(_parser, ArrayBuilders.insertInListNoDup(_modifiers, mod), _classLoader);
+ return new TypeFactory(typeCache, _parser, mods, _classLoader);
}
-
+
public TypeFactory withClassLoader(ClassLoader classLoader) {
- return new TypeFactory(_parser, _modifiers, classLoader);
+ return new TypeFactory(_typeCache, _parser, _modifiers, classLoader);
+ }
+
+ /**
+ * Mutant factory method that will construct new {@link TypeFactory} with
+ * identical settings except for different cache; most likely one with
+ * bigger maximum size.
+ *
+ * @since 2.8
+ */
+ public TypeFactory withCache(LRUMap<Object,JavaType> cache) {
+ return new TypeFactory(cache, _parser, _modifiers, _classLoader);
}
/**
@@ -251,7 +281,7 @@
Throwable prob = null;
ClassLoader loader = this.getClassLoader();
if (loader == null) {
- loader = Thread.currentThread().getContextClassLoader();
+ loader = Thread.currentThread().getContextClassLoader();
}
if (loader != null) {
try {
@@ -371,8 +401,7 @@
}
// If not, we'll need to do more thorough forward+backwards resolution. Sigh.
- // !!! TODO (as of 28-Jan-2016, at least)
-
+
// 20-Oct-2015, tatu: Container, Map-types somewhat special. There is
// a way to fully resolve and merge hierarchies; but that gets expensive
// so let's, for now, try to create close-enough approximation that
@@ -394,8 +423,9 @@
}
} while (false);
- // except possibly handlers
-// newType = newType.withHandlersFrom(baseType);
+ // 25-Sep-2016, tatu: As per [databind#1384] also need to ensure handlers get
+ // copied as well
+ newType = newType.withHandlersFrom(baseType);
return newType;
// 20-Oct-2015, tatu: Old simplistic approach
@@ -805,12 +835,17 @@
}
/**
- * Method that will force construction of a simple type, without trying to
- * check for more specialized types.
+ * Method that use by core Databind functionality, and that should NOT be called
+ * by application code outside databind package.
*<p>
- * NOTE: no type modifiers are called on type either, so calling this method
- * should only be used if caller really knows what it's doing...
+ * Unchecked here not only means that no checks are made as to whether given class
+ * might be non-simple type (like {@link CollectionType}) but also that most of supertype
+ * information is not gathered. This means that unless called on primitive types or
+ * {@link java.lang.String}, results are probably not what you want to use.
+ *
+ * @deprecated Since 2.8, to indicate users should never call this method.
*/
+ @Deprecated // since 2.8
public JavaType uncheckedSimpleType(Class<?> cls) {
// 18-Oct-2015, tatu: Not sure how much problem missing super-type info is here
return _constructSimple(cls, EMPTY_BINDINGS, null, null);
@@ -979,7 +1014,7 @@
JavaType superClass, JavaType[] superInterfaces)
{
JavaType kt, vt;
-
+
// 28-May-2015, tatu: Properties are special, as per [databind#810]; fake "correct" parameter sig
if (rawClass == Properties.class) {
kt = vt = CORE_TYPE_STRING;
@@ -1170,12 +1205,12 @@
final Object key;
if ((bindings == null) || bindings.isEmpty()) {
key = rawType;
- result = _typeCache.get(key); // ok, cache object is synced
- if (result != null) {
- return result;
- }
} else {
- key = null;
+ key = bindings.asKey(rawType);
+ }
+ result = _typeCache.get(key); // ok, cache object is synced
+ if (result != null) {
+ return result;
}
// 15-Oct-2015, tatu: recursive reference?
@@ -1235,7 +1270,9 @@
}
}
context.resolveSelfReferences(result);
- if (key != null) {
+ // 16-Jul-2016, tatu: [databind#1302] is solved different way, but ideally we shouldn't
+ // cache anything with partially resolved `ResolvedRecursiveType`... so maybe improve
+ if (!result.hasHandlers()) {
_typeCache.putIfAbsent(key, result); // cache object syncs
}
return result;
@@ -1273,6 +1310,10 @@
protected JavaType _fromWellKnownClass(ClassStack context, Class<?> rawType, TypeBindings bindings,
JavaType superClass, JavaType[] superInterfaces)
{
+ if (bindings == null) {
+ bindings = TypeBindings.emptyBindings();
+ }
+
// Quite simple when we resolving exact class/interface; start with that
if (rawType == Map.class) {
return _mapType(rawType, bindings, superClass, superInterfaces);
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/ArrayBuilders.java b/src/main/java/com/fasterxml/jackson/databind/util/ArrayBuilders.java
index 8ce5766..834a96e 100644
--- a/src/main/java/com/fasterxml/jackson/databind/util/ArrayBuilders.java
+++ b/src/main/java/com/fasterxml/jackson/databind/util/ArrayBuilders.java
@@ -181,7 +181,7 @@
}
};
}
-
+
public static <T> HashSet<T> arrayToSet(T[] elements)
{
HashSet<T> result = new HashSet<T>();
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/ClassUtil.java b/src/main/java/com/fasterxml/jackson/databind/util/ClassUtil.java
index a73657c..5d14255 100644
--- a/src/main/java/com/fasterxml/jackson/databind/util/ClassUtil.java
+++ b/src/main/java/com/fasterxml/jackson/databind/util/ClassUtil.java
@@ -1,9 +1,12 @@
package com.fasterxml.jackson.databind.util;
+import java.io.Closeable;
+import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.*;
+import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
@@ -415,7 +418,22 @@
}
throw (Error) t;
}
-
+
+ /**
+ * Method that works like {@link #throwRootCause} if (and only if)
+ * root cause is an {@link IOException}; otherwise returns root cause
+ *
+ * @since 2.8
+ */
+ public static Throwable throwRootCauseIfIOE(Throwable t) throws IOException
+ {
+ t = getRootCause(t);
+ if (t instanceof IOException) {
+ throw (IOException) t;
+ }
+ return t;
+ }
+
/**
* Method that will wrap 't' as an {@link IllegalArgumentException} if it
* is a checked exception; otherwise (runtime exception or error) throw as is
@@ -461,6 +479,73 @@
throwAsIAE(getRootCause(t), msg);
}
+ /**
+ * Helper method that encapsulate logic in trying to close output generator
+ * in case of failure; useful mostly in forcing flush()ing as otherwise
+ * error conditions tend to be hard to diagnose. However, it is often the
+ * case that output state may be corrupt so we need to be prepared for
+ * secondary exception without masking original one.
+ *
+ * @since 2.8
+ */
+ public static void closeOnFailAndThrowAsIAE(JsonGenerator g, Exception fail)
+ throws IOException
+ {
+ /* 04-Mar-2014, tatu: Let's try to prevent auto-closing of
+ * structures, which typically causes more damage.
+ */
+ g.disable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT);
+ try {
+ g.close();
+ } catch (Exception e) {
+ fail.addSuppressed(e);
+ }
+ if (fail instanceof IOException) {
+ throw (IOException) fail;
+ }
+ if (fail instanceof RuntimeException) {
+ throw (RuntimeException) fail;
+ }
+ throw new RuntimeException(fail);
+ }
+
+ /**
+ * Helper method that encapsulate logic in trying to close given {@link Closeable}
+ * in case of failure; useful mostly in forcing flush()ing as otherwise
+ * error conditions tend to be hard to diagnose. However, it is often the
+ * case that output state may be corrupt so we need to be prepared for
+ * secondary exception without masking original one.
+ *
+ * @since 2.8
+ */
+ public static void closeOnFailAndThrowAsIAE(JsonGenerator g,
+ Closeable toClose, Exception fail)
+ throws IOException
+ {
+ if (g != null) {
+ g.disable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT);
+ try {
+ g.close();
+ } catch (Exception e) {
+ fail.addSuppressed(e);
+ }
+ }
+ if (toClose != null) {
+ try {
+ toClose.close();
+ } catch (Exception e) {
+ fail.addSuppressed(e);
+ }
+ }
+ if (fail instanceof IOException) {
+ throw (IOException) fail;
+ }
+ if (fail instanceof RuntimeException) {
+ throw (RuntimeException) fail;
+ }
+ throw new RuntimeException(fail);
+ }
+
/*
/**********************************************************
/* Instantiation
@@ -699,7 +784,7 @@
{
// First things first: if not empty, easy to determine
if (!s.isEmpty()) {
- return findEnumType(s.iterator().next());
+ return findEnumType(s.iterator().next());
}
// Otherwise need to locate using an internal field
return EnumTypeLocator.instance.enumTypeFor(s);
@@ -753,6 +838,36 @@
return (Class<? extends Enum<?>>) cls;
}
+ /**
+ * A method that will look for the first Enum value annotated with the given Annotation.
+ * <p>
+ * If there's more than one value annotated, the first one found will be returned. Which one exactly is used is undetermined.
+ *
+ * @param enumClass The Enum class to scan for a value with the given annotation
+ * @param annotationClass The annotation to look for.
+ * @return the Enum value annotated with the given Annotation or {@code null} if none is found.
+ * @throws IllegalArgumentException if there's a reflection issue accessing the Enum
+ * @since 2.8
+ */
+ public static <T extends Annotation> Enum<?> findFirstAnnotatedEnumValue(Class<Enum<?>> enumClass, Class<T> annotationClass)
+ {
+ Field[] fields = getDeclaredFields(enumClass);
+ for (Field field : fields) {
+ if (field.isEnumConstant()) {
+ Annotation defaultValueAnnotation = field.getAnnotation(annotationClass);
+ if (defaultValueAnnotation != null) {
+ final String name = field.getName();
+ for (Enum<?> enumValue : enumClass.getEnumConstants()) {
+ if (name.equals(enumValue.name())) {
+ return enumValue;
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
/*
/**********************************************************
/* Jackson-specific stuff
@@ -864,7 +979,6 @@
* @since 2.7
*/
public static Class<?> getDeclaringClass(Class<?> cls) {
- // Caching does not seem worthwhile, as per profiling
return isObjectOrPrimitive(cls) ? null : cls.getDeclaringClass();
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/EnumResolver.java b/src/main/java/com/fasterxml/jackson/databind/util/EnumResolver.java
index 59f5dc5..d6ef5e6 100644
--- a/src/main/java/com/fasterxml/jackson/databind/util/EnumResolver.java
+++ b/src/main/java/com/fasterxml/jackson/databind/util/EnumResolver.java
@@ -5,7 +5,7 @@
import com.fasterxml.jackson.databind.AnnotationIntrospector;
- /**
+/**
* Helper class used to resolve String values (either JSON Object field
* names or regular String values) into Java Enum instances.
*/
@@ -19,11 +19,14 @@
protected final HashMap<String, Enum<?>> _enumsById;
- protected EnumResolver(Class<Enum<?>> enumClass, Enum<?>[] enums, HashMap<String, Enum<?>> map)
+ protected final Enum<?> _defaultValue;
+
+ protected EnumResolver(Class<Enum<?>> enumClass, Enum<?>[] enums, HashMap<String, Enum<?>> map, Enum<?> defaultValue)
{
_enumClass = enumClass;
_enums = enums;
_enumsById = map;
+ _defaultValue = defaultValue;
}
/**
@@ -45,14 +48,28 @@
}
map.put(name, enumValues[i]);
}
- return new EnumResolver(enumCls, enumValues, map);
+
+ Enum<?> defaultEnum = ai.findDefaultEnumValue(enumCls);
+
+ return new EnumResolver(enumCls, enumValues, map, defaultEnum);
+ }
+
+ /**
+ * @deprecated Since 2.8, use {@link #constructUsingToString(Class, AnnotationIntrospector)} instead
+ */
+ @Deprecated
+ public static EnumResolver constructUsingToString(Class<Enum<?>> enumCls) {
+ return constructUsingToString(enumCls, null);
}
/**
* Factory method for constructing resolver that maps from Enum.toString() into
* Enum value
+ *
+ * @since 2.8
*/
- public static EnumResolver constructUsingToString(Class<Enum<?>> enumCls)
+ public static EnumResolver constructUsingToString(Class<Enum<?>> enumCls,
+ AnnotationIntrospector ai)
{
Enum<?>[] enumValues = enumCls.getEnumConstants();
HashMap<String, Enum<?>> map = new HashMap<String, Enum<?>>();
@@ -61,11 +78,23 @@
Enum<?> e = enumValues[i];
map.put(e.toString(), e);
}
- return new EnumResolver(enumCls, enumValues, map);
+ Enum<?> defaultEnum = (ai == null) ? null : ai.findDefaultEnumValue(enumCls);
+ return new EnumResolver(enumCls, enumValues, map, defaultEnum);
}
- public static EnumResolver constructUsingMethod(Class<Enum<?>> enumCls,
- Method accessor)
+ /**
+ * @deprecated Since 2.8, use {@link #constructUsingMethod(Class, Method, AnnotationIntrospector)} instead
+ */
+ @Deprecated
+ public static EnumResolver constructUsingMethod(Class<Enum<?>> enumCls, Method accessor) {
+ return constructUsingMethod(enumCls, accessor, null);
+ }
+
+ /**
+ * @since 2.8
+ */
+ public static EnumResolver constructUsingMethod(Class<Enum<?>> enumCls, Method accessor,
+ AnnotationIntrospector ai)
{
Enum<?>[] enumValues = enumCls.getEnumConstants();
HashMap<String, Enum<?>> map = new HashMap<String, Enum<?>>();
@@ -81,7 +110,8 @@
throw new IllegalArgumentException("Failed to access @JsonValue of Enum value "+en+": "+e.getMessage());
}
}
- return new EnumResolver(enumCls, enumValues, map);
+ Enum<?> defaultEnum = (ai != null) ? ai.findDefaultEnumValue(enumCls) : null;
+ return new EnumResolver(enumCls, enumValues, map, defaultEnum);
}
/**
@@ -99,33 +129,56 @@
}
/**
+ * @deprecated Since 2.8, use {@link #constructUnsafeUsingToString(Class, AnnotationIntrospector)} instead
+ */
+ @Deprecated
+ public static EnumResolver constructUnsafeUsingToString(Class<?> rawEnumCls)
+ {
+ return constructUnsafeUsingToString(rawEnumCls, null);
+ }
+
+ /**
* Method that needs to be used instead of {@link #constructUsingToString}
* if static type of enum is not known.
+ *
+ * @since 2.8
*/
@SuppressWarnings({ "unchecked" })
- public static EnumResolver constructUnsafeUsingToString(Class<?> rawEnumCls)
- {
+ public static EnumResolver constructUnsafeUsingToString(Class<?> rawEnumCls,
+ AnnotationIntrospector ai)
+ {
// oh so wrong... not much that can be done tho
Class<Enum<?>> enumCls = (Class<Enum<?>>) rawEnumCls;
- return constructUsingToString(enumCls);
+ return constructUsingToString(enumCls, ai);
+ }
+
+ /**
+ * @deprecated Since 2.8, use {@link #constructUnsafeUsingMethod(Class, Method, AnnotationIntrospector)} instead.
+ */
+ @Deprecated
+ public static EnumResolver constructUnsafeUsingMethod(Class<?> rawEnumCls, Method accessor) {
+ return constructUnsafeUsingMethod(rawEnumCls, accessor, null);
}
/**
* Method used when actual String serialization is indicated using @JsonValue
* on a method.
+ *
+ * @since 2.8
*/
@SuppressWarnings({ "unchecked" })
- public static EnumResolver constructUnsafeUsingMethod(Class<?> rawEnumCls, Method accessor)
+ public static EnumResolver constructUnsafeUsingMethod(Class<?> rawEnumCls, Method accessor,
+ AnnotationIntrospector ai)
{
// wrong as ever but:
Class<Enum<?>> enumCls = (Class<Enum<?>>) rawEnumCls;
- return constructUsingMethod(enumCls, accessor);
+ return constructUsingMethod(enumCls, accessor, ai);
}
public CompactStringObjectMap constructLookup() {
return CompactStringObjectMap.construct(_enumsById);
}
-
+
public Enum<?> findEnum(String key) { return _enumsById.get(key); }
public Enum<?> getEnum(int index) {
@@ -135,6 +188,10 @@
return _enums[index];
}
+ public Enum<?> getDefaultValue(){
+ return _defaultValue;
+ }
+
public Enum<?>[] getRawEnums() {
return _enums;
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/JSONWrappedObject.java b/src/main/java/com/fasterxml/jackson/databind/util/JSONWrappedObject.java
index f411a92..121b1e3 100644
--- a/src/main/java/com/fasterxml/jackson/databind/util/JSONWrappedObject.java
+++ b/src/main/java/com/fasterxml/jackson/databind/util/JSONWrappedObject.java
@@ -41,7 +41,7 @@
* to include (if any).
*/
protected final JavaType _serializationType;
-
+
public JSONWrappedObject(String prefix, String suffix, Object value) {
this(prefix, suffix, value, (JavaType) null);
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/LinkedNode.java b/src/main/java/com/fasterxml/jackson/databind/util/LinkedNode.java
index 75d3caf..fa7cdae 100644
--- a/src/main/java/com/fasterxml/jackson/databind/util/LinkedNode.java
+++ b/src/main/java/com/fasterxml/jackson/databind/util/LinkedNode.java
@@ -4,8 +4,6 @@
* Node of a forward-only linked list.
*
* @author tatu
- *
- * @param <T> Type of contained object
*/
public final class LinkedNode<T>
{
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/ObjectIdMap.java b/src/main/java/com/fasterxml/jackson/databind/util/ObjectIdMap.java
deleted file mode 100644
index 0d558b7..0000000
--- a/src/main/java/com/fasterxml/jackson/databind/util/ObjectIdMap.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.fasterxml.jackson.databind.util;
-
-import java.util.IdentityHashMap;
-
-/**
- * Map used during serialization, to keep track of referable Objects
- * along with lazily evaluated ids.
- */
-@SuppressWarnings("serial")
-public class ObjectIdMap extends IdentityHashMap<Object,Object>
-{
- public ObjectIdMap()
- {
- super(16);
- }
-
- /*
- /**********************************************************
- /* API
- /**********************************************************
- */
-
- /**
- * Method that is called to figure out whether we have already
- * seen given POJO: if yes, we will return its id (first looking
- * it up as necessary); if not, we will mark down that we have
- * seen it but return null.
- */
- public Object findId(Object pojo)
- {
- return get(pojo);
- }
-
- public void insertId(Object pojo, Object id)
- {
- put(pojo, id);
- }
-}
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/TokenBuffer.java b/src/main/java/com/fasterxml/jackson/databind/util/TokenBuffer.java
index 9a375e9..1f05c32 100644
--- a/src/main/java/com/fasterxml/jackson/databind/util/TokenBuffer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/util/TokenBuffer.java
@@ -414,10 +414,15 @@
case VALUE_EMBEDDED_OBJECT:
{
Object value = segment.get(ptr);
+ // 01-Sep-2016, tatu: as per [databind#1361], should use `writeEmbeddedObject()`;
+ // however, may need to consider alternatives for some well-known types
+ // first
if (value instanceof RawValue) {
((RawValue) value).serialize(gen);
- } else {
+ } else if (value instanceof JsonSerializable) {
gen.writeObject(value);
+ } else {
+ gen.writeEmbeddedObject(value);
}
}
break;
@@ -448,7 +453,9 @@
copyCurrentStructure(p);
} while ((t = p.nextToken()) == JsonToken.FIELD_NAME);
if (t != JsonToken.END_OBJECT) {
- throw ctxt.mappingException("Expected END_OBJECT after copying contents of a JsonParser into TokenBuffer, got "+t);
+ ctxt.reportWrongTokenException(p, JsonToken.END_OBJECT,
+ "Expected END_OBJECT after copying contents of a JsonParser into TokenBuffer, got "+t);
+ // never gets here
}
writeEndObject();
return this;
@@ -645,6 +652,18 @@
_writeContext = _writeContext.createChildObjectContext();
}
+ @Override // since 2.8
+ public void writeStartObject(Object forValue) throws IOException
+ {
+ _writeContext.writeValue();
+ _append(JsonToken.START_OBJECT);
+ JsonWriteContext ctxt = _writeContext.createChildObjectContext();
+ _writeContext = ctxt;
+ if (forValue != null) {
+ ctxt.setCurrentValue(forValue);
+ }
+ }
+
@Override
public final void writeEndObject() throws IOException
{
@@ -927,6 +946,11 @@
_hasNativeId = true;
}
+ @Override // since 2.8
+ public void writeEmbeddedObject(Object object) throws IOException {
+ _appendValue(JsonToken.VALUE_EMBEDDED_OBJECT, object);
+ }
+
/*
/**********************************************************
/* JsonGenerator implementation; pass-through copy
@@ -1013,41 +1037,41 @@
}
@Override
- public void copyCurrentStructure(JsonParser jp) throws IOException
+ public void copyCurrentStructure(JsonParser p) throws IOException
{
- JsonToken t = jp.getCurrentToken();
+ JsonToken t = p.getCurrentToken();
// Let's handle field-name separately first
if (t == JsonToken.FIELD_NAME) {
if (_mayHaveNativeIds) {
- _checkNativeIds(jp);
+ _checkNativeIds(p);
}
- writeFieldName(jp.getCurrentName());
- t = jp.nextToken();
+ writeFieldName(p.getCurrentName());
+ t = p.nextToken();
// fall-through to copy the associated value
}
if (_mayHaveNativeIds) {
- _checkNativeIds(jp);
+ _checkNativeIds(p);
}
switch (t) {
case START_ARRAY:
writeStartArray();
- while (jp.nextToken() != JsonToken.END_ARRAY) {
- copyCurrentStructure(jp);
+ while (p.nextToken() != JsonToken.END_ARRAY) {
+ copyCurrentStructure(p);
}
writeEndArray();
break;
case START_OBJECT:
writeStartObject();
- while (jp.nextToken() != JsonToken.END_OBJECT) {
- copyCurrentStructure(jp);
+ while (p.nextToken() != JsonToken.END_OBJECT) {
+ copyCurrentStructure(p);
}
writeEndObject();
break;
default: // others are simple:
- copyCurrentEvent(jp);
+ copyCurrentEvent(p);
}
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/BaseMapTest.java b/src/test/java/com/fasterxml/jackson/databind/BaseMapTest.java
index 8db5792..9376d77 100644
--- a/src/test/java/com/fasterxml/jackson/databind/BaseMapTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/BaseMapTest.java
@@ -52,6 +52,13 @@
public LongWrapper() { }
public LongWrapper(long value) { l = value; }
}
+
+ protected static class DoubleWrapper {
+ public double d;
+
+ public DoubleWrapper() { }
+ public DoubleWrapper(double value) { d = value; }
+ }
/**
* Simple wrapper around String type, usually to test value
@@ -121,12 +128,37 @@
public enum ABC { A, B, C; }
+ // since 2.8
+ public static class Point {
+ public int x, y;
+
+ protected Point() { } // for deser
+ public Point(int x0, int y0) {
+ x = x0;
+ y = y0;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Point)) {
+ return false;
+ }
+ Point other = (Point) o;
+ return (other.x == x) && (other.y == y);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("[x=%d, y=%d]", x, y);
+ }
+ }
+
/*
/**********************************************************
/* Shared serializers
/**********************************************************
*/
-
+
@SuppressWarnings("serial")
public static class UpperCasingSerializer extends StdScalarSerializer<String>
{
@@ -165,22 +197,25 @@
/**********************************************************
*/
- private final static ObjectMapper SHARED_MAPPER = new ObjectMapper();
+ private static ObjectMapper SHARED_MAPPER;
protected ObjectMapper objectMapper() {
+ if (SHARED_MAPPER == null) {
+ SHARED_MAPPER = new ObjectMapper();
+ }
return SHARED_MAPPER;
}
protected ObjectWriter objectWriter() {
- return SHARED_MAPPER.writer();
+ return objectMapper().writer();
}
protected ObjectReader objectReader() {
- return SHARED_MAPPER.reader();
+ return objectMapper().reader();
}
protected ObjectReader objectReader(Class<?> cls) {
- return SHARED_MAPPER.readerFor(cls);
+ return objectMapper().readerFor(cls);
}
// @since 2.7
diff --git a/src/test/java/com/fasterxml/jackson/databind/BaseTest.java b/src/test/java/com/fasterxml/jackson/databind/BaseTest.java
index 8226035..400cdcd 100644
--- a/src/test/java/com/fasterxml/jackson/databind/BaseTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/BaseTest.java
@@ -290,22 +290,21 @@
}
protected JsonParser createParserUsingReader(JsonFactory f, String input)
- throws IOException, JsonParseException
+ throws IOException
{
return f.createParser(new StringReader(input));
}
protected JsonParser createParserUsingStream(String input, String encoding)
- throws IOException, JsonParseException
+ throws IOException
{
return createParserUsingStream(new JsonFactory(), input, encoding);
}
protected JsonParser createParserUsingStream(JsonFactory f,
- String input, String encoding)
- throws IOException, JsonParseException
+ String input, String encoding)
+ throws IOException
{
-
/* 23-Apr-2008, tatus: UTF-32 is not supported by JDK, have to
* use our own codec too (which is not optimal since there's
* a chance both encoder and decoder might have bugs, but ones
diff --git a/src/test/java/com/fasterxml/jackson/databind/ObjectMapperTest.java b/src/test/java/com/fasterxml/jackson/databind/ObjectMapperTest.java
index 6a10a47..ea9c090 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ObjectMapperTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ObjectMapperTest.java
@@ -299,4 +299,37 @@
assertFalse(copy.isEnabled(JsonParser.Feature.ALLOW_COMMENTS));
assertTrue(mapper.isEnabled(JsonParser.Feature.ALLOW_COMMENTS));
}
+
+ // since 2.8
+ public void testDataOutputViaMapper() throws Exception
+ {
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ ObjectNode input = MAPPER.createObjectNode();
+ input.put("a", 1);
+ DataOutput data = new DataOutputStream(bytes);
+ final String exp = "{\"a\":1}";
+ MAPPER.writeValue(data, input);
+ assertEquals(exp, bytes.toString("UTF-8"));
+
+ // and also via ObjectWriter...
+ bytes.reset();
+ data = new DataOutputStream(bytes);
+ MAPPER.writer().writeValue(data, input);
+ assertEquals(exp, bytes.toString("UTF-8"));
+ }
+
+ // since 2.8
+ @SuppressWarnings("unchecked")
+ public void testDataInputViaMapper() throws Exception
+ {
+ byte[] src = "{\"a\":1}".getBytes("UTF-8");
+ DataInput input = new DataInputStream(new ByteArrayInputStream(src));
+ Map<String,Object> map = (Map<String,Object>) MAPPER.readValue(input, Map.class);
+ assertEquals(Integer.valueOf(1), map.get("a"));
+
+ input = new DataInputStream(new ByteArrayInputStream(src));
+ map = MAPPER.readerFor(Map.class)
+ .readValue(input);
+ assertEquals(Integer.valueOf(1), map.get("a"));
+ }
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/TestFormatSchema.java b/src/test/java/com/fasterxml/jackson/databind/TestFormatSchema.java
index 0809df0..a5050b7 100644
--- a/src/test/java/com/fasterxml/jackson/databind/TestFormatSchema.java
+++ b/src/test/java/com/fasterxml/jackson/databind/TestFormatSchema.java
@@ -123,11 +123,6 @@
public void setCodec(ObjectCodec c) { }
@Override
- protected boolean loadMore() throws IOException {
- return false;
- }
-
- @Override
protected void _closeInput() throws IOException {
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/TestObjectMapperBeanDeserializer.java b/src/test/java/com/fasterxml/jackson/databind/TestObjectMapperBeanDeserializer.java
index 6778274..5d96b07 100644
--- a/src/test/java/com/fasterxml/jackson/databind/TestObjectMapperBeanDeserializer.java
+++ b/src/test/java/com/fasterxml/jackson/databind/TestObjectMapperBeanDeserializer.java
@@ -217,7 +217,8 @@
OtherBean otherResult = MAPPER.readValue("13", OtherBean.class);
fail("Expected an exception, but got result value: "+otherResult.o);
} catch (JsonMappingException e) {
- verifyException(e, "from Integral number", "no single-int-arg constructor/factory method");
+ verifyException(e, "deserialize from Number value",
+ "no int/Integer-argument constructor/factory method");
assertValidLocation(e.getLocation());
}
}
@@ -233,7 +234,8 @@
OtherBean otherResult = MAPPER.readValue(""+value, OtherBean.class);
fail("Expected an exception, but got result value: "+otherResult.o);
} catch (JsonMappingException e) {
- verifyException(e, "from Long integral number", "no single-long-arg constructor/factory method");
+ verifyException(e, "deserialize from Number value",
+ "no long/Long-argument constructor/factory method");
assertValidLocation(e.getLocation());
}
}
@@ -247,7 +249,8 @@
OtherBean otherResult = MAPPER.readValue("13.5", OtherBean.class);
fail("Expected an exception, but got result value: "+otherResult.o);
} catch (JsonMappingException e) {
- verifyException(e, "from Floating-point number", "no one-double/Double-arg constructor/factory method");
+ verifyException(e, "deserialize from Number value",
+ "no double/Double-argument constructor/factory method");
assertValidLocation(e.getLocation());
}
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualWithAnnDeserializer.java b/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualWithAnnDeserializer.java
index d20d37d..e019014 100644
--- a/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualWithAnnDeserializer.java
+++ b/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualWithAnnDeserializer.java
@@ -46,9 +46,9 @@
}
@Override
- public StringValue deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException
+ public StringValue deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
{
- return new StringValue(""+_fieldName+"="+jp.getText());
+ return new StringValue(""+_fieldName+"="+p.getText());
}
@Override
@@ -61,38 +61,10 @@
ann = property.getContextAnnotation(Name.class);
}
String propertyName = (ann == null) ? "UNKNOWN" : ann.value();
- return new MyContextualDeserializer(propertyName);
+ return new AnnotatedContextualDeserializer(propertyName);
}
}
- static class MyContextualDeserializer
- extends JsonDeserializer<StringValue>
- implements ContextualDeserializer
- {
- protected final String _fieldName;
-
- public MyContextualDeserializer() { this(""); }
- public MyContextualDeserializer(String fieldName) {
- _fieldName = fieldName;
- }
-
- @Override
- public StringValue deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException
- {
- return new StringValue(""+_fieldName+"="+jp.getText());
- }
-
- @Override
- public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
- BeanProperty property)
- throws JsonMappingException
- {
- String name = (property == null) ? "NULL" : property.getName();
- return new MyContextualDeserializer(name);
- }
- }
-
-
// ensure that direct associations also work
public void testAnnotatedContextual() throws Exception
{
@@ -103,5 +75,4 @@
assertNotNull(bean);
assertEquals("xyz=a", bean.value.value);
}
-
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/convert/TestArrayConversions.java b/src/test/java/com/fasterxml/jackson/databind/convert/TestArrayConversions.java
index ab7f50a..a4156eb 100644
--- a/src/test/java/com/fasterxml/jackson/databind/convert/TestArrayConversions.java
+++ b/src/test/java/com/fasterxml/jackson/databind/convert/TestArrayConversions.java
@@ -196,8 +196,8 @@
for (int i = 0; i < size; ++i) {
Number n1 = (Number) Array.get(inputArray, i);
Number n2 = (Number) Array.get(outputArray, i);
- double value1 = ((Number) n1).longValue();
- double value2 = ((Number) n2).longValue();
+ double value1 = n1.longValue();
+ double value2 = n2.longValue();
assertEquals("Entry #"+i+"/"+size+" not equal", value1, value2);
}
}
@@ -207,10 +207,9 @@
for (int i = 0; i < size; ++i) {
Number n1 = (Number) Array.get(inputArray, i);
Number n2 = (Number) Array.get(outputArray, i);
- double value1 = ((Number) n1).doubleValue();
- double value2 = ((Number) n2).doubleValue();
+ double value1 = n1.doubleValue();
+ double value2 = n2.doubleValue();
assertEquals("Entry #"+i+"/"+size+" not equal", value1, value2);
- }
+ }
}
-
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/convert/TestBeanConversions.java b/src/test/java/com/fasterxml/jackson/databind/convert/TestBeanConversions.java
index 3baee20..34c161e 100644
--- a/src/test/java/com/fasterxml/jackson/databind/convert/TestBeanConversions.java
+++ b/src/test/java/com/fasterxml/jackson/databind/convert/TestBeanConversions.java
@@ -15,13 +15,13 @@
{
final ObjectMapper MAPPER = new ObjectMapper();
- static class Point {
+ static class PointZ {
public int x, y;
public int z = -13;
- public Point() { }
- public Point(int a, int b, int c)
+ public PointZ() { }
+ public PointZ(int a, int b, int c)
{
x = a;
y = b;
@@ -101,7 +101,7 @@
{
// should have no problems convert between compatible beans...
PointStrings input = new PointStrings("37", "-9");
- Point point = MAPPER.convertValue(input, Point.class);
+ PointZ point = MAPPER.convertValue(input, PointZ.class);
assertEquals(37, point.x);
assertEquals(-9, point.y);
// z not included in input, will be whatever default constructor provides
@@ -124,7 +124,7 @@
try {
MAPPER.readValue("{\"boolProp\":\"foobar\"}", BooleanBean.class);
} catch (JsonMappingException e) {
- verifyException(e, "from String value (\"foobar\")");
+ verifyException(e, "Can not deserialize value of type boolean from String");
}
}
@@ -137,7 +137,7 @@
assertEquals("foo", a2.getData());
}
- // [JACKSON-710]: should work regardless of wrapping...
+ // should work regardless of wrapping...
public void testWrapping() throws Exception
{
ObjectMapper wrappingMapper = new ObjectMapper();
@@ -182,15 +182,15 @@
private void _convertAndVerifyPoint(ObjectMapper m)
{
- final Point input = new Point(1, 2, 3);
- Point output = m.convertValue(input, Point.class);
+ final PointZ input = new PointZ(1, 2, 3);
+ PointZ output = m.convertValue(input, PointZ.class);
assertEquals(1, output.x);
assertEquals(2, output.y);
assertEquals(3, output.z);
}
/**
- * Need to test "shortcuts" introduced by [Issue-11]
+ * Need to test "shortcuts" introduced by [databind#11]
*/
public void testIssue11() throws Exception
{
diff --git a/src/test/java/com/fasterxml/jackson/databind/convert/TestUpdateValue.java b/src/test/java/com/fasterxml/jackson/databind/convert/TestUpdateValue.java
index 5698edd..ef86c5f 100644
--- a/src/test/java/com/fasterxml/jackson/databind/convert/TestUpdateValue.java
+++ b/src/test/java/com/fasterxml/jackson/databind/convert/TestUpdateValue.java
@@ -70,8 +70,10 @@
@Override
public DataA deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
if (p.getCurrentToken() != JsonToken.START_OBJECT) {
- throw ctxt.mappingException("Wrong current token, expected START_OBJECT, got: %s",
+ ctxt.reportWrongTokenException(p, JsonToken.START_OBJECT,
+ "Wrong current token, expected START_OBJECT, got: %s",
p.getCurrentToken());
+ // never gets here
}
/*JsonNode node =*/ p.readValueAsTree();
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/BuilderSimpleTest.java b/src/test/java/com/fasterxml/jackson/databind/creators/BuilderSimpleTest.java
index 16f5bfd..3ae76e7 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/BuilderSimpleTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/creators/BuilderSimpleTest.java
@@ -4,6 +4,7 @@
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.core.JsonProcessingException;
@@ -302,19 +303,46 @@
*/
private final ObjectMapper MAPPER = new ObjectMapper();
-
+
public void testSimple() throws Exception
{
String json = "{\"x\":1,\"y\":2}";
Object o = MAPPER.readValue(json, ValueClassXY.class);
assertNotNull(o);
- assertSame(ValueClassXY.class, o.getClass());
- ValueClassXY value = (ValueClassXY) o;
- // note: ctor adds one to both values
- assertEquals(value._x, 2);
- assertEquals(value._y, 3);
+ assertSame(ValueClassXY.class, o.getClass());
+ ValueClassXY value = (ValueClassXY) o;
+ // note: ctor adds one to both values
+ assertEquals(value._x, 2);
+ assertEquals(value._y, 3);
}
+ // related to [databind#1214]
+ public void testSimpleWithIgnores() throws Exception
+ {
+ // 'z' is unknown, and would fail by default:
+ String json = "{\"x\":1,\"y\":2,\"z\":3}";
+ Object o = null;
+
+ try {
+ o = MAPPER.readValue(json, ValueClassXY.class);
+ fail("Should not pass");
+ } catch (JsonMappingException e) {
+ verifyException(e, "Unrecognized field \"z\"");
+ }
+
+ // but with config overrides should pass
+ ObjectMapper ignorantMapper = new ObjectMapper();
+ ignorantMapper.configOverride(SimpleBuilderXY.class)
+ .setIgnorals(JsonIgnoreProperties.Value.forIgnoreUnknown(true));
+ o = ignorantMapper.readValue(json, ValueClassXY.class);
+ assertNotNull(o);
+ assertSame(ValueClassXY.class, o.getClass());
+ ValueClassXY value = (ValueClassXY) o;
+ // note: ctor adds one to both values
+ assertEquals(value._x, 2);
+ assertEquals(value._y, 3);
+ }
+
public void testMultiAccess() throws Exception
{
String json = "{\"c\":3,\"a\":2,\"b\":-9}";
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/EnumCreatorTest.java b/src/test/java/com/fasterxml/jackson/databind/creators/EnumCreatorTest.java
new file mode 100644
index 0000000..1cf89c2
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/creators/EnumCreatorTest.java
@@ -0,0 +1,310 @@
+package com.fasterxml.jackson.databind.creators;
+
+import java.math.BigDecimal;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonGetter;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.Deserializers;
+import com.fasterxml.jackson.databind.deser.std.EnumDeserializer;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+
+public class EnumCreatorTest extends BaseMapTest
+{
+ protected enum EnumWithCreator {
+ A, B;
+
+ @JsonCreator
+ public static EnumWithCreator fromEnum(String str) {
+ if ("enumA".equals(str)) return A;
+ if ("enumB".equals(str)) return B;
+ return null;
+ }
+ }
+
+ protected enum EnumWithBDCreator {
+ E5, E8;
+
+ @JsonCreator
+ public static EnumWithBDCreator create(BigDecimal bd) {
+ if (bd.longValue() == 5L) return E5;
+ if (bd.longValue() == 8L) return E8;
+ return null;
+ }
+ }
+
+ protected enum TestEnumFromInt
+ {
+ ENUM_A(1), ENUM_B(2), ENUM_C(3);
+
+ private final int id;
+
+ private TestEnumFromInt(int id) {
+ this.id = id;
+ }
+
+ @JsonCreator public static TestEnumFromInt fromId(int id) {
+ for (TestEnumFromInt e: values()) {
+ if (e.id == id) return e;
+ }
+ return null;
+ }
+ }
+
+ static enum EnumWithPropertiesModeJsonCreator {
+ TEST1,
+ TEST2,
+ TEST3;
+
+ @JsonGetter("name")
+ public String getName() {
+ return name();
+ }
+
+ @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
+ public static EnumWithPropertiesModeJsonCreator create(@JsonProperty("name") String name) {
+ return EnumWithPropertiesModeJsonCreator.valueOf(name);
+ }
+ }
+
+ static enum EnumWithDelegateModeJsonCreator {
+ TEST1,
+ TEST2,
+ TEST3;
+
+ @JsonGetter("name")
+ public String getName() {
+ return name();
+ }
+
+ @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
+ public static EnumWithDelegateModeJsonCreator create(JsonNode json) {
+ return EnumWithDelegateModeJsonCreator.valueOf(json.get("name").asText());
+ }
+ }
+
+ // [databind#324]: exception from creator method
+ protected enum TestEnum324
+ {
+ A, B;
+
+ @JsonCreator public static TestEnum324 creator(String arg) {
+ throw new RuntimeException("Foobar!");
+ }
+ }
+
+ // [databind#745]
+ static class DelegatingDeserializers extends Deserializers.Base
+ {
+ @Override
+ public JsonDeserializer<?> findEnumDeserializer(final Class<?> type, final DeserializationConfig config, final BeanDescription beanDesc) throws JsonMappingException {
+ final Collection<AnnotatedMethod> factoryMethods = beanDesc.getFactoryMethods();
+ if (factoryMethods != null) {
+ for (AnnotatedMethod am : factoryMethods) {
+ final JsonCreator creator = am.getAnnotation(JsonCreator.class);
+ if (creator != null) {
+ return EnumDeserializer.deserializerForCreator(config, type, am, null, null);
+ }
+ }
+ }
+ return null;
+ }
+ }
+
+ // [databind#745]
+ static class DelegatingDeserializersModule extends SimpleModule
+ {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void setupModule(final SetupContext context) {
+ context.addDeserializers(new DelegatingDeserializers());
+ }
+ }
+
+ // [databind#929]: support multi-arg enum creator
+ static enum Enum929
+ {
+ A, B, C;
+
+ @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
+ static Enum929 forValues(@JsonProperty("id") int intProp,
+ @JsonProperty("name") String name)
+ {
+ return Enum929.valueOf(name);
+ }
+ }
+
+ static enum MyEnum960
+ {
+ VALUE, BOGUS;
+
+ @JsonCreator
+ public static MyEnum960 getInstance() {
+ return VALUE;
+ }
+ }
+
+ static class MyEnum960Wrapper {
+ public MyEnum960 value;
+ }
+
+ static enum Enum1291 {
+
+ V1("val1"),
+ V2("val2"),
+ V3("val3"),
+ V4("val4"),
+ V5("val5"),
+ V6("val6");
+
+ private final String name;
+
+ Enum1291(String name) {
+ this.name = name;
+ }
+
+ public static Enum1291 fromString(String name) {
+ for (Enum1291 type : Enum1291.values()) {
+ if (type.name.equals(name)) {
+ return type;
+ }
+ }
+ return Enum1291.valueOf(name.toUpperCase());
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+ }
+
+ /*
+ /**********************************************************
+ /* Test methods
+ /**********************************************************
+ */
+
+ protected final ObjectMapper MAPPER = new ObjectMapper();
+
+ public void testCreatorEnums() throws Exception {
+ EnumWithCreator value = MAPPER.readValue("\"enumA\"", EnumWithCreator.class);
+ assertEquals(EnumWithCreator.A, value);
+ }
+
+ public void testCreatorEnumsFromBigDecimal() throws Exception {
+ EnumWithBDCreator value = MAPPER.readValue("\"8.0\"", EnumWithBDCreator.class);
+ assertEquals(EnumWithBDCreator.E8, value);
+ }
+
+ public void testEnumWithCreatorEnumMaps() throws Exception {
+ EnumMap<EnumWithCreator,String> value = MAPPER.readValue("{\"enumA\":\"value\"}",
+ new TypeReference<EnumMap<EnumWithCreator,String>>() {});
+ assertEquals("value", value.get(EnumWithCreator.A));
+ }
+
+ public void testEnumWithCreatorMaps() throws Exception {
+ HashMap<EnumWithCreator,String> value = MAPPER.readValue("{\"enumA\":\"value\"}",
+ new TypeReference<java.util.HashMap<EnumWithCreator,String>>() {});
+ assertEquals("value", value.get(EnumWithCreator.A));
+ }
+
+ public void testEnumWithCreatorEnumSets() throws Exception {
+ EnumSet<EnumWithCreator> value = MAPPER.readValue("[\"enumA\"]",
+ new TypeReference<EnumSet<EnumWithCreator>>() {});
+ assertTrue(value.contains(EnumWithCreator.A));
+ }
+
+ public void testJsonCreatorPropertiesWithEnum() throws Exception
+ {
+ EnumWithPropertiesModeJsonCreator type1 = MAPPER.readValue("{\"name\":\"TEST1\", \"description\":\"TEST\"}", EnumWithPropertiesModeJsonCreator.class);
+ assertSame(EnumWithPropertiesModeJsonCreator.TEST1, type1);
+
+ EnumWithPropertiesModeJsonCreator type2 = MAPPER.readValue("{\"name\":\"TEST3\", \"description\":\"TEST\"}", EnumWithPropertiesModeJsonCreator.class);
+ assertSame(EnumWithPropertiesModeJsonCreator.TEST3, type2);
+
+ }
+
+ public void testJsonCreatorDelagateWithEnum() throws Exception {
+ final ObjectMapper mapper = new ObjectMapper();
+
+ EnumWithDelegateModeJsonCreator type1 = mapper.readValue("{\"name\":\"TEST1\", \"description\":\"TEST\"}", EnumWithDelegateModeJsonCreator.class);
+ assertSame(EnumWithDelegateModeJsonCreator.TEST1, type1);
+
+ EnumWithDelegateModeJsonCreator type2 = mapper.readValue("{\"name\":\"TEST3\", \"description\":\"TEST\"}", EnumWithDelegateModeJsonCreator.class);
+ assertSame(EnumWithDelegateModeJsonCreator.TEST3, type2);
+
+ }
+
+ public void testEnumsFromInts() throws Exception
+ {
+ Object ob = MAPPER.readValue("1 ", TestEnumFromInt.class);
+ assertEquals(TestEnumFromInt.class, ob.getClass());
+ assertSame(TestEnumFromInt.ENUM_A, ob);
+ }
+
+ // [databind#324]
+ public void testExceptionFromCreator() throws Exception
+ {
+ try {
+ /*TestEnum324 e =*/ MAPPER.readValue(quote("xyz"), TestEnum324.class);
+ fail("Should throw exception");
+ } catch (JsonMappingException e) {
+ verifyException(e, "foobar");
+ }
+ }
+
+ // [databind#745]
+ public void testDeserializerForCreatorWithEnumMaps() throws Exception
+ {
+ final ObjectMapper mapper = new ObjectMapper();
+ mapper.registerModule(new DelegatingDeserializersModule());
+ EnumMap<EnumWithCreator,String> value = mapper.readValue("{\"enumA\":\"value\"}",
+ new TypeReference<EnumMap<EnumWithCreator,String>>() {});
+ assertEquals("value", value.get(EnumWithCreator.A));
+ }
+
+ // for [databind#929]
+ public void testMultiArgEnumCreator() throws Exception
+ {
+ Enum929 v = MAPPER.readValue("{\"id\":3,\"name\":\"B\"}", Enum929.class);
+ assertEquals(Enum929.B, v);
+ }
+
+ // for [databind#960]
+ public void testNoArgEnumCreator() throws Exception
+ {
+ MyEnum960 v = MAPPER.readValue("{\"value\":\"bogus\"}", MyEnum960.class);
+ assertEquals(MyEnum960.VALUE, v);
+ }
+
+ // for [databind#1291]
+ public void testEnumCreators1291() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper();
+ String json = mapper.writeValueAsString(Enum1291.V2);
+ Enum1291 result = mapper.readValue(json, Enum1291.class);
+ assertSame(Enum1291.V2, result);
+ }
+
+ // for [databind#1389]
+ public void testMultiArgEnumInCollections() throws Exception
+ {
+ EnumSet<Enum929> valueEnumSet = MAPPER.readValue("[{\"id\":3,\"name\":\"B\"}, {\"id\":3,\"name\":\"A\"}]",
+ new TypeReference<EnumSet<Enum929>>() {});
+ assertEquals(2, valueEnumSet.size());
+ assertTrue(valueEnumSet.contains(Enum929.A));
+ assertTrue(valueEnumSet.contains(Enum929.B));
+ List<Enum929> valueList = MAPPER.readValue("[{\"id\":3,\"name\":\"B\"}, {\"id\":3,\"name\":\"A\"}, {\"id\":3,\"name\":\"B\"}]",
+ new TypeReference<List<Enum929>>() {});
+ assertEquals(3, valueList.size());
+ assertEquals(Enum929.B, valueList.get(2));
+ }
+
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/FailOnNullCreatorTest.java b/src/test/java/com/fasterxml/jackson/databind/creators/FailOnNullCreatorTest.java
new file mode 100644
index 0000000..52416f4
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/creators/FailOnNullCreatorTest.java
@@ -0,0 +1,60 @@
+package com.fasterxml.jackson.databind.creators;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Tests to ensure that deserialization fails when a bean property has a null value
+ * Relates to <a href="https://github.com/FasterXML/jackson-databind/issues/988">issue #988</a>
+ */
+public class FailOnNullCreatorTest extends BaseMapTest
+{
+ static class Person {
+ String name;
+ Integer age;
+
+ @JsonCreator
+ public Person(@JsonProperty(value="name") String name,
+ @JsonProperty(value="age") int age)
+ {
+ this.name = name;
+ this.age = age;
+ }
+ }
+
+ private final ObjectReader POINT_READER = objectMapper().readerFor(Person.class);
+
+ public void testRequiredNonNullParam() throws Exception
+ {
+ Person p;
+ // First: fine if feature is not enabled
+ p = POINT_READER.readValue(aposToQuotes("{}"));
+ assertEquals(null, p.name);
+ assertEquals(Integer.valueOf(0), p.age);
+
+ // Second: fine if feature is enabled but default value is not null
+ ObjectReader r = POINT_READER.with(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES);
+ p = POINT_READER.readValue(aposToQuotes("{'name':'John', 'age': null}"));
+ assertEquals("John", p.name);
+ assertEquals(Integer.valueOf(0), p.age);
+
+ // Third: throws exception if property is missing
+ try {
+ r.readValue(aposToQuotes("{}"));
+ fail("Should not pass third test");
+ } catch (JsonMappingException e) {
+ verifyException(e, "Null value for creator property 'name'");
+ }
+
+ // Fourth: throws exception if property is set to null explicitly
+ try {
+ r.readValue(aposToQuotes("{'age': 5, 'name': null}"));
+ fail("Should not pass fourth test");
+ } catch (JsonMappingException e) {
+ verifyException(e, "Null value for creator property 'name'");
+ }
+ }
+
+
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestConstructFromMap.java b/src/test/java/com/fasterxml/jackson/databind/creators/TestConstructFromMap.java
index 873a556..84e8c7c 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/TestConstructFromMap.java
+++ b/src/test/java/com/fasterxml/jackson/databind/creators/TestConstructFromMap.java
@@ -1,6 +1,5 @@
package com.fasterxml.jackson.databind.creators;
-import java.awt.Point; // just for convenience
import java.math.BigDecimal;
import java.util.*;
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators2.java b/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators2.java
index 1e24e5d..f075bcb 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators2.java
+++ b/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators2.java
@@ -276,7 +276,7 @@
MAPPER.readValue(quote("abc"), IgnoredCtor.class);
fail("Should have caught missing constructor problem");
} catch (JsonMappingException e) {
- verifyException(e, "no single-String constructor/factory method");
+ verifyException(e, "no String-argument constructor/factory method");
}
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorsWithIdentity.java b/src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorsWithIdentity.java
index 1975038..b064424 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorsWithIdentity.java
+++ b/src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorsWithIdentity.java
@@ -25,7 +25,6 @@
}
}
-
public static class Child {
@JsonProperty
Parent parent;
@@ -50,5 +49,6 @@
assertNotNull(parent);
Child child = JSON_MAPPER.readValue(childStr, Child.class);
assertNotNull(child);
+ assertNotNull(child.parent);
}
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestValueInstantiator.java b/src/test/java/com/fasterxml/jackson/databind/creators/TestValueInstantiator.java
index 60771fe..744d3d0 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/TestValueInstantiator.java
+++ b/src/test/java/com/fasterxml/jackson/databind/creators/TestValueInstantiator.java
@@ -12,7 +12,7 @@
import com.fasterxml.jackson.databind.type.TypeFactory;
/**
- * Test related to [JACKSON-580] (allow specifying custom instantiators)
+ * Test custom instantiators.
*/
public class TestValueInstantiator extends BaseMapTest
{
@@ -43,8 +43,12 @@
}
}
- static abstract class InstantiatorBase extends ValueInstantiator
+ static abstract class InstantiatorBase extends ValueInstantiator.Base
{
+ public InstantiatorBase() {
+ super(Object.class);
+ }
+
@Override
public String getValueTypeDesc() {
return "UNKNOWN";
@@ -153,8 +157,10 @@
}
}
- static class MyDelegateBeanInstantiator extends ValueInstantiator
+ static class MyDelegateBeanInstantiator extends ValueInstantiator.Base
{
+ public MyDelegateBeanInstantiator() { super(Object.class); }
+
@Override
public String getValueTypeDesc() { return "xxx"; }
@@ -188,8 +194,10 @@
}
}
- static class MyDelegateListInstantiator extends ValueInstantiator
+ static class MyDelegateListInstantiator extends ValueInstantiator.Base
{
+ public MyDelegateListInstantiator() { super(Object.class); }
+
@Override
public String getValueTypeDesc() { return "xxx"; }
@@ -225,8 +233,10 @@
}
}
- static class MyDelegateMapInstantiator extends ValueInstantiator
+ static class MyDelegateMapInstantiator extends ValueInstantiator.Base
{
+ public MyDelegateMapInstantiator() { super(Object.class); }
+
@Override
public String getValueTypeDesc() { return "xxx"; }
@@ -567,4 +577,30 @@
assertEquals("foo", bean.a);
assertEquals(3, bean.b);
}
+
+ // @since 2.8
+ public void testErrorMessageForMissingCtor() throws Exception
+ {
+ // first fail, check message from JSON Object (no default ctor)
+ try {
+ MAPPER.readValue("{ }", MyBean.class);
+ fail("Should not succeed");
+ } catch (JsonMappingException e) {
+ verifyException(e, "Can not construct instance of");
+ verifyException(e, "missing default constructor");
+ }
+ }
+
+ // @since 2.8
+ public void testErrorMessageForMissingStringCtor() throws Exception
+ {
+ // then from JSON String
+ try {
+ MAPPER.readValue("\"foo\"", MyBean.class);
+ fail("Should not succeed");
+ } catch (JsonMappingException e) {
+ verifyException(e, "Can not construct instance of");
+ verifyException(e, "no String-argument constructor/factory");
+ }
+ }
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestValueUpdate.java b/src/test/java/com/fasterxml/jackson/databind/creators/TestValueUpdate.java
index 3c7feae..228e597 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/TestValueUpdate.java
+++ b/src/test/java/com/fasterxml/jackson/databind/creators/TestValueUpdate.java
@@ -36,7 +36,7 @@
}
}
- // [Issue#318] (and Scala module issue #83]
+ // [databind#318] (and Scala module issue #83]
public void testValueUpdateWithCreator() throws Exception
{
Bean bean = new Bean("abc", "def");
@@ -44,5 +44,4 @@
assertEquals("ghi", bean.getA());
assertEquals("jkl", bean.getB());
}
-
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestEnumDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/deser/EnumDeserializationTest.java
similarity index 70%
rename from src/test/java/com/fasterxml/jackson/databind/deser/TestEnumDeserialization.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/EnumDeserializationTest.java
index 752cd5e..fc9d777 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestEnumDeserialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/EnumDeserializationTest.java
@@ -1,7 +1,6 @@
package com.fasterxml.jackson.databind.deser;
import java.io.IOException;
-import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
@@ -10,13 +9,12 @@
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.deser.std.EnumDeserializer;
+import com.fasterxml.jackson.databind.deser.std.FromStringDeserializer;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
-import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
import com.fasterxml.jackson.databind.module.SimpleModule;
@SuppressWarnings("serial")
-public class TestEnumDeserialization
+public class EnumDeserializationTest
extends BaseMapTest
{
enum TestEnum { JACKSON, RULES, OK; }
@@ -48,28 +46,6 @@
return TestEnum.valueOf(jp.getText().toUpperCase());
}
}
-
- protected enum EnumWithCreator {
- A, B;
-
- @JsonCreator
- public static EnumWithCreator fromEnum(String str) {
- if ("enumA".equals(str)) return A;
- if ("enumB".equals(str)) return B;
- return null;
- }
- }
-
- protected enum EnumWithBDCreator {
- E5, E8;
-
- @JsonCreator
- public static EnumWithBDCreator create(BigDecimal bd) {
- if (bd.longValue() == 5L) return E5;
- if (bd.longValue() == 8L) return E8;
- return null;
- }
- }
protected enum LowerCaseEnum {
A, B, C;
@@ -93,61 +69,6 @@
@JsonProperty Map<TestEnum, String> map;
}
- protected enum TestEnumFor834
- {
- ENUM_A(1), ENUM_B(2), ENUM_C(3);
-
- private final int id;
-
- private TestEnumFor834(int id) {
- this.id = id;
- }
-
- @JsonCreator public static TestEnumFor834 fromId(int id) {
- for (TestEnumFor834 e: values()) {
- if (e.id == id) return e;
- }
- return null;
- }
- }
-
- // [Issue#324]: exception from creator method
- protected enum TestEnum324
- {
- A, B;
-
- @JsonCreator public static TestEnum324 creator(String arg) {
- throw new RuntimeException("Foobar!");
- }
- }
-
- // [databind#745]
- static class DelegatingDeserializers extends Deserializers.Base
- {
- @Override
- public JsonDeserializer<?> findEnumDeserializer(final Class<?> type, final DeserializationConfig config, final BeanDescription beanDesc) throws JsonMappingException {
- final Collection<AnnotatedMethod> factoryMethods = beanDesc.getFactoryMethods();
- if (factoryMethods != null) {
- for (AnnotatedMethod am : factoryMethods) {
- final JsonCreator creator = am.getAnnotation(JsonCreator.class);
- if (creator != null) {
- return EnumDeserializer.deserializerForCreator(config, type, am);
- }
- }
- }
- return null;
- }
- }
-
- // [databind#745]
- static class DelegatingDeserializersModule extends SimpleModule
- {
- @Override
- public void setupModule(final SetupContext context) {
- context.addDeserializers(new DelegatingDeserializers());
- }
- }
-
// [databind#677]
static enum EnumWithPropertyAnno {
@JsonProperty("a")
@@ -173,10 +94,85 @@
return name().toLowerCase();
};
}
+
+ static enum EnumWithDefaultAnno {
+ A, B,
+
+ @JsonEnumDefaultValue
+ OTHER;
+ }
+
+ static enum EnumWithDefaultAnnoAndConstructor {
+ A, B,
+
+ @JsonEnumDefaultValue
+ OTHER;
+
+ @JsonCreator public static EnumWithDefaultAnnoAndConstructor fromId(String value) {
+ for (EnumWithDefaultAnnoAndConstructor e: values()) {
+ if (e.name().toLowerCase().equals(value)) return e;
+ }
+ return null;
+ }
+ }
+
+ // //
+ public enum AnEnum {
+ ZERO,
+ ONE
+ }
+
+ public static class AnEnumDeserializer extends FromStringDeserializer<AnEnum> {
+
+ public AnEnumDeserializer() {
+ super(AnEnum.class);
+ }
+
+ @Override
+ protected AnEnum _deserialize(String value, DeserializationContext ctxt) throws IOException {
+ try {
+ return AnEnum.valueOf(value);
+ } catch (IllegalArgumentException e) {
+ return (AnEnum) ctxt.handleWeirdStringValue(AnEnum.class, value,
+ "Undefined AnEnum code");
+ }
+ }
+ }
+
+ public static class AnEnumKeyDeserializer extends KeyDeserializer {
+
+ @Override
+ public Object deserializeKey(String key, DeserializationContext ctxt) throws IOException {
+ try {
+ return AnEnum.valueOf(key);
+ } catch (IllegalArgumentException e) {
+ return ctxt.handleWeirdKey(AnEnum.class, key, "Undefined AnEnum code");
+ }
+ }
+ }
+
+
+ @JsonDeserialize(using = AnEnumDeserializer.class, keyUsing = AnEnumKeyDeserializer.class)
+ public enum LanguageCodeMixin {
+ }
+
+ public static class EnumModule extends SimpleModule {
+ @Override
+ public void setupModule(SetupContext context) {
+ context.setMixInAnnotations(AnEnum.class, LanguageCodeMixin.class);
+ }
+
+ public static ObjectMapper setupObjectMapper(ObjectMapper mapper) {
+ final EnumModule module = new EnumModule();
+ mapper.registerModule(module);
+ return mapper;
+ }
+ }
+
/*
/**********************************************************
- /* Tests
+ /* Test methods
/**********************************************************
*/
@@ -200,12 +196,10 @@
// and no more content beyond that...
assertFalse(jp.hasCurrentToken());
- /* Then alternative with index (0 means first entry)
- */
+ // Then alternative with index (0 means first entry)
assertEquals(TestEnum.JACKSON, MAPPER.readValue(" 0 ", TestEnum.class));
- /* Then error case: unrecognized value
- */
+ // Then error case: unrecognized value
try {
/*Object result =*/ MAPPER.readValue("\"NO-SUCH-VALUE\"", TestEnum.class);
fail("Expected an exception for bogus enum value...");
@@ -252,16 +246,6 @@
assertEquals(EnumWithSubClass.A, value);
}
- public void testCreatorEnums() throws Exception {
- EnumWithCreator value = MAPPER.readValue("\"enumA\"", EnumWithCreator.class);
- assertEquals(EnumWithCreator.A, value);
- }
-
- public void testCreatorEnumsFromBigDecimal() throws Exception {
- EnumWithBDCreator value = MAPPER.readValue("\"8.0\"", EnumWithBDCreator.class);
- assertEquals(EnumWithBDCreator.E8, value);
- }
-
public void testToStringEnums() throws Exception
{
// can't reuse global one due to reconfig
@@ -295,7 +279,8 @@
value = r.readValue("1");
fail("Expected an error");
} catch (JsonMappingException e) {
- verifyException(e, "Not allowed to deserialize Enum value out of JSON number");
+ verifyException(e, "Can not deserialize");
+ verifyException(e, "not allowed to deserialize Enum value out of number: disable");
}
// and [databind#684]
@@ -303,7 +288,8 @@
value = r.readValue(quote("1"));
fail("Expected an error");
} catch (JsonMappingException e) {
- verifyException(e, "Not allowed to deserialize Enum value out of JSON number");
+ verifyException(e, "Can not deserialize");
+ verifyException(e, "not allowed to deserialize Enum value out of number: disable");
}
}
@@ -341,25 +327,7 @@
assertEquals(Integer.valueOf(13), map.get(EnumWithJsonValue.A));
}
- public void testEnumWithCreatorEnumMaps() throws Exception {
- EnumMap<EnumWithCreator,String> value = MAPPER.readValue("{\"enumA\":\"value\"}",
- new TypeReference<EnumMap<EnumWithCreator,String>>() {});
- assertEquals("value", value.get(EnumWithCreator.A));
- }
-
- public void testEnumWithCreatorMaps() throws Exception {
- java.util.HashMap<EnumWithCreator,String> value = MAPPER.readValue("{\"enumA\":\"value\"}",
- new TypeReference<java.util.HashMap<EnumWithCreator,String>>() {});
- assertEquals("value", value.get(EnumWithCreator.A));
- }
-
- public void testEnumWithCreatorEnumSets() throws Exception {
- EnumSet<EnumWithCreator> value = MAPPER.readValue("[\"enumA\"]",
- new TypeReference<EnumSet<EnumWithCreator>>() {});
- assertTrue(value.contains(EnumWithCreator.A));
- }
-
- // [JACKSON-810], ability to ignore unknown Enum values:
+ // Ability to ignore unknown Enum values:
public void testAllowUnknownEnumValuesReadAsNull() throws Exception
{
@@ -392,17 +360,10 @@
MAPPER.readValue("{\"map\":{\"NO-SUCH-VALUE\":\"val\"}}", ClassWithEnumMapKey.class);
fail("Expected an exception for bogus enum value...");
} catch (JsonMappingException jex) {
- verifyException(jex, "Can not construct Map key");
+ verifyException(jex, "Can not deserialize Map key of type com.fasterxml.jackson.databind.deser");
}
}
- public void testEnumsFromInts() throws Exception
- {
- Object ob = MAPPER.readValue("1 ", TestEnumFor834.class);
- assertEquals(TestEnumFor834.class, ob.getClass());
- assertSame(TestEnumFor834.ENUM_A, ob);
- }
-
// [databind#141]: allow mapping of empty String into null
public void testEnumsWithEmpty() throws Exception
{
@@ -421,18 +382,7 @@
// not sure this is totally safe but...
assertEquals(TestEnum.JACKSON, mapper.readValue(quote("jackson"), TestEnum.class));
}
-
- // [databind#324]
- public void testExceptionFromCreator() throws Exception
- {
- try {
- /*TestEnum324 e =*/ MAPPER.readValue(quote("xyz"), TestEnum324.class);
- fail("Should throw exception");
- } catch (JsonMappingException e) {
- verifyException(e, "foobar");
- }
- }
-
+
// [databind#381]
public void testUnwrappedEnum() throws Exception {
final ObjectMapper mapper = new ObjectMapper();
@@ -445,10 +395,12 @@
final ObjectMapper mapper = new ObjectMapper();
mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
try {
- assertEquals(TestEnum.JACKSON, mapper.readValue("[" + quote("JACKSON") + "]", TestEnum.class));
- fail("Exception was not thrown on deserializing a single array element of type enum");
+ Object v = mapper.readValue("[" + quote("JACKSON") + "]",
+ TestEnum.class);
+ fail("Exception was not thrown on deserializing a single array element of type enum; instead got: "+v);
} catch (JsonMappingException exp) {
//exception as thrown correctly
+ verifyException(exp, "Can not deserialize");
}
}
@@ -464,16 +416,6 @@
assertSame(TestEnum.values()[1], en);
}
- // [databind#745]
- public void testDeserializerForCreatorWithEnumMaps() throws Exception
- {
- final ObjectMapper mapper = new ObjectMapper();
- mapper.registerModule(new DelegatingDeserializersModule());
- EnumMap<EnumWithCreator,String> value = mapper.readValue("{\"enumA\":\"value\"}",
- new TypeReference<EnumMap<EnumWithCreator,String>>() {});
- assertEquals("value", value.get(EnumWithCreator.A));
- }
-
public void testEnumWithJsonPropertyRename() throws Exception
{
String json = MAPPER.writeValueAsString(new EnumWithPropertyAnno[] {
@@ -506,4 +448,65 @@
.without(DeserializationFeature.READ_ENUMS_USING_TO_STRING)
.readValue(quote("A"));
assertSame(Enum1161.A, result);
- }}
+ }
+
+ public void testEnumWithDefaultAnnotation() throws Exception {
+ final ObjectMapper mapper = new ObjectMapper();
+ mapper.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE);
+
+ EnumWithDefaultAnno myEnum = mapper.readValue("\"foo\"", EnumWithDefaultAnno.class);
+ assertSame(EnumWithDefaultAnno.OTHER, myEnum);
+ }
+
+ public void testEnumWithDefaultAnnotationUsingIndexInBound1() throws Exception {
+ final ObjectMapper mapper = new ObjectMapper();
+ mapper.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE);
+
+ EnumWithDefaultAnno myEnum = mapper.readValue("1", EnumWithDefaultAnno.class);
+ assertSame(EnumWithDefaultAnno.B, myEnum);
+ }
+
+ public void testEnumWithDefaultAnnotationUsingIndexInBound2() throws Exception {
+ final ObjectMapper mapper = new ObjectMapper();
+ mapper.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE);
+
+ EnumWithDefaultAnno myEnum = mapper.readValue("2", EnumWithDefaultAnno.class);
+ assertSame(EnumWithDefaultAnno.OTHER, myEnum);
+ }
+
+ public void testEnumWithDefaultAnnotationUsingIndexSameAsLength() throws Exception {
+ final ObjectMapper mapper = new ObjectMapper();
+ mapper.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE);
+
+ EnumWithDefaultAnno myEnum = mapper.readValue("3", EnumWithDefaultAnno.class);
+ assertSame(EnumWithDefaultAnno.OTHER, myEnum);
+ }
+
+ public void testEnumWithDefaultAnnotationUsingIndexOutOfBound() throws Exception {
+ final ObjectMapper mapper = new ObjectMapper();
+ mapper.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE);
+
+ EnumWithDefaultAnno myEnum = mapper.readValue("4", EnumWithDefaultAnno.class);
+ assertSame(EnumWithDefaultAnno.OTHER, myEnum);
+ }
+
+ public void testEnumWithDefaultAnnotationWithConstructor() throws Exception {
+ final ObjectMapper mapper = new ObjectMapper();
+ mapper.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE);
+
+ EnumWithDefaultAnnoAndConstructor myEnum = mapper.readValue("\"foo\"", EnumWithDefaultAnnoAndConstructor.class);
+ assertNull("When using a constructor, the default value annotation shouldn't be used.", myEnum);
+ }
+
+ public void testExceptionFromCustomEnumKeyDeserializer() {
+ ObjectMapper objectMapper = new ObjectMapper();
+ objectMapper.registerModule(new EnumModule());
+ try {
+ objectMapper.readValue("{\"TWO\": \"dumpling\"}",
+ new TypeReference<Map<AnEnum, String>>() {});
+ fail("No exception");
+ } catch (IOException e) {
+ assertTrue(e.getMessage().contains("Undefined AnEnum"));
+ }
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/ExceptionFromCustomEnumKeyDeserializerTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/ExceptionFromCustomEnumKeyDeserializerTest.java
deleted file mode 100644
index 6b8ea10..0000000
--- a/src/test/java/com/fasterxml/jackson/databind/deser/ExceptionFromCustomEnumKeyDeserializerTest.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/******************************************************************************
- * * This data and information is proprietary to, and a valuable trade secret
- * * of, Basis Technology Corp. It is given in confidence by Basis Technology
- * * and may only be used as permitted under the license agreement under which
- * * it has been distributed, and in no other way.
- * *
- * * Copyright (c) 2015 Basis Technology Corporation All rights reserved.
- * *
- * * The technical data and information provided herein are provided with
- * * `limited rights', and the computer software provided herein is provided
- * * with `restricted rights' as those terms are defined in DAR and ASPR
- * * 7-104.9(a).
- ******************************************************************************/
-
-package com.fasterxml.jackson.databind.deser;
-
-import java.io.IOException;
-import java.util.Map;
-
-import org.junit.Test;
-
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.BaseMapTest;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.KeyDeserializer;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.deser.std.FromStringDeserializer;
-import com.fasterxml.jackson.databind.module.SimpleModule;
-
-@SuppressWarnings("serial")
-public class ExceptionFromCustomEnumKeyDeserializerTest
- extends BaseMapTest
-{
- public enum AnEnum {
- ZERO,
- ONE
- }
-
- public static class AnEnumDeserializer extends FromStringDeserializer<AnEnum> {
-
- public AnEnumDeserializer() {
- super(AnEnum.class);
- }
-
- //CHECKSTYLE:OFF
- @Override
- protected AnEnum _deserialize(String value, DeserializationContext ctxt) throws IOException {
- try {
- return AnEnum.valueOf(value);
- } catch (IllegalArgumentException e) {
- throw ctxt.weirdKeyException(AnEnum.class, value, "Undefined AnEnum code");
- }
- }
- }
-
- public static class AnEnumKeyDeserializer extends KeyDeserializer {
-
- @Override
- public Object deserializeKey(String key, DeserializationContext ctxt) throws IOException {
- try {
- return AnEnum.valueOf(key);
- } catch (IllegalArgumentException e) {
- throw ctxt.weirdKeyException(AnEnum.class, key, "Undefined AnEnum code");
- }
- }
- }
-
-
- @JsonDeserialize(using = AnEnumDeserializer.class, keyUsing = AnEnumKeyDeserializer.class)
- public enum LanguageCodeMixin {
- }
-
- public static class EnumModule extends SimpleModule {
- @Override
- public void setupModule(SetupContext context) {
- context.setMixInAnnotations(AnEnum.class, LanguageCodeMixin.class);
- }
-
- public static ObjectMapper setupObjectMapper(ObjectMapper mapper) {
- final EnumModule module = new EnumModule();
- mapper.registerModule(module);
- return mapper;
- }
- }
-
- @Test
- public void testLostMessage() {
- ObjectMapper objectMapper = new ObjectMapper();
- objectMapper.registerModule(new EnumModule());
- try {
- objectMapper.readValue("{\"TWO\": \"dumpling\"}", new TypeReference<Map<AnEnum, String>>() {});
- } catch (IOException e) {
- assertTrue(e.getMessage().contains("Undefined AnEnum"));
- return;
- }
- fail("No exception");
- }
-}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestJDKAtomicTypes.java b/src/test/java/com/fasterxml/jackson/databind/deser/JDKAtomicTypesTest.java
similarity index 93%
rename from src/test/java/com/fasterxml/jackson/databind/deser/TestJDKAtomicTypes.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/JDKAtomicTypesTest.java
index d505256..a3236cf 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestJDKAtomicTypes.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/JDKAtomicTypesTest.java
@@ -7,8 +7,9 @@
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-public class TestJDKAtomicTypes
+public class JDKAtomicTypesTest
extends com.fasterxml.jackson.databind.BaseMapTest
{
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME)
@@ -86,6 +87,13 @@
public LCStringWrapper() { }
}
+ @JsonPropertyOrder({ "a", "b" })
+ static class Issue1256Bean {
+ @JsonSerialize(as=AtomicReference.class)
+ public Object a = new AtomicReference<Object>();
+ public AtomicReference<Object> b = new AtomicReference<Object>();
+ }
+
/*
/**********************************************************
/* Test methods
@@ -251,6 +259,15 @@
assertEquals("foobar", w.value.get());
}
+ public void testEmpty1256() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.setSerializationInclusion(JsonInclude.Include.NON_ABSENT);
+
+ String json = mapper.writeValueAsString(new Issue1256Bean());
+ assertEquals("{}", json);
+ }
+
// [databind#1307]
@SuppressWarnings("unchecked")
public void testNullValueHandling() throws Exception
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/JDKNumberDeserTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/JDKNumberDeserTest.java
new file mode 100644
index 0000000..c224f88
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/JDKNumberDeserTest.java
@@ -0,0 +1,251 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+public class JDKNumberDeserTest extends BaseMapTest
+{
+ /*
+ /**********************************************************************
+ /* Helper classes, beans
+ /**********************************************************************
+ */
+
+ static class MyBeanHolder {
+ public Long id;
+ public MyBeanDefaultValue defaultValue;
+ }
+
+ static class MyBeanDefaultValue
+ {
+ public MyBeanValue value;
+ }
+
+ @JsonDeserialize(using=MyBeanDeserializer.class)
+ static class MyBeanValue {
+ public BigDecimal decimal;
+ public MyBeanValue() { this(null); }
+ public MyBeanValue(BigDecimal d) { this.decimal = d; }
+ }
+
+ /*
+ /**********************************************************************
+ /* Helper classes, serializers/deserializers/resolvers
+ /**********************************************************************
+ */
+
+ static class MyBeanDeserializer extends JsonDeserializer<MyBeanValue>
+ {
+ @Override
+ public MyBeanValue deserialize(JsonParser jp, DeserializationContext ctxt)
+ throws IOException
+ {
+ return new MyBeanValue(jp.getDecimalValue());
+ }
+ }
+
+ /*
+ /**********************************************************************
+ /* Unit tests
+ /**********************************************************************
+ */
+
+ final ObjectMapper MAPPER = new ObjectMapper();
+
+ public void testNaN() throws Exception
+ {
+ Float result = MAPPER.readValue(" \"NaN\"", Float.class);
+ assertEquals(Float.valueOf(Float.NaN), result);
+
+ Double d = MAPPER.readValue(" \"NaN\"", Double.class);
+ assertEquals(Double.valueOf(Double.NaN), d);
+
+ Number num = MAPPER.readValue(" \"NaN\"", Number.class);
+ assertEquals(Double.valueOf(Double.NaN), num);
+ }
+
+ public void testDoubleInf() throws Exception
+ {
+ Double result = MAPPER.readValue(" \""+Double.POSITIVE_INFINITY+"\"", Double.class);
+ assertEquals(Double.valueOf(Double.POSITIVE_INFINITY), result);
+
+ result = MAPPER.readValue(" \""+Double.NEGATIVE_INFINITY+"\"", Double.class);
+ assertEquals(Double.valueOf(Double.NEGATIVE_INFINITY), result);
+ }
+
+ public void testEmptyAsNumber() throws Exception
+ {
+ assertNull(MAPPER.readValue(quote(""), Integer.class));
+ assertNull(MAPPER.readValue(quote(""), Long.class));
+ assertNull(MAPPER.readValue(quote(""), Float.class));
+ assertNull(MAPPER.readValue(quote(""), Double.class));
+ assertNull(MAPPER.readValue(quote(""), BigInteger.class));
+ assertNull(MAPPER.readValue(quote(""), BigDecimal.class));
+ }
+
+ public void testDeserializeDecimalHappyPath() throws Exception {
+ String json = "{\"defaultValue\": { \"value\": 123 } }";
+ MyBeanHolder result = MAPPER.readValue(json, MyBeanHolder.class);
+ assertEquals(BigDecimal.valueOf(123), result.defaultValue.value.decimal);
+ }
+
+ public void testDeserializeDecimalProperException() throws Exception {
+ String json = "{\"defaultValue\": { \"value\": \"123\" } }";
+ try {
+ MAPPER.readValue(json, MyBeanHolder.class);
+ fail("should have raised exception");
+ } catch (JsonProcessingException e) {
+ verifyException(e, "not numeric");
+ }
+ }
+
+ public void testDeserializeDecimalProperExceptionWhenIdSet() throws Exception {
+ String json = "{\"id\": 5, \"defaultValue\": { \"value\": \"123\" } }";
+ try {
+ MyBeanHolder result = MAPPER.readValue(json, MyBeanHolder.class);
+ fail("should have raised exception instead value was set to " + result.defaultValue.value.decimal.toString());
+ } catch (JsonProcessingException e) {
+ verifyException(e, "not numeric");
+ }
+ }
+
+ // And then [databind#852]
+ public void testScientificNotationAsStringForNumber() throws Exception
+ {
+ Object ob = MAPPER.readValue("\"3E-8\"", Number.class);
+ assertEquals(Double.class, ob.getClass());
+ ob = MAPPER.readValue("\"3e-8\"", Number.class);
+ assertEquals(Double.class, ob.getClass());
+ ob = MAPPER.readValue("\"300000000\"", Number.class);
+ assertEquals(Integer.class, ob.getClass());
+ ob = MAPPER.readValue("\"123456789012\"", Number.class);
+ assertEquals(Long.class, ob.getClass());
+ }
+
+ public void testIntAsNumber() throws Exception
+ {
+ /* Even if declared as 'generic' type, should return using most
+ * efficient type... here, Integer
+ */
+ Number result = MAPPER.readValue(" 123 ", Number.class);
+ assertEquals(Integer.valueOf(123), result);
+ }
+
+ public void testLongAsNumber() throws Exception
+ {
+ // And beyond int range, should get long
+ long exp = 1234567890123L;
+ Number result = MAPPER.readValue(String.valueOf(exp), Number.class);
+ assertEquals(Long.valueOf(exp), result);
+ }
+
+ public void testBigIntAsNumber() throws Exception
+ {
+ // and after long, BigInteger
+ BigInteger biggie = new BigInteger("1234567890123456789012345678901234567890");
+ Number result = MAPPER.readValue(biggie.toString(), Number.class);
+ assertEquals(BigInteger.class, biggie.getClass());
+ assertEquals(biggie, result);
+ }
+
+ public void testIntTypeOverride() throws Exception
+ {
+ /* Slight twist; as per [JACKSON-100], can also request binding
+ * to BigInteger even if value would fit in Integer
+ */
+ ObjectReader r = MAPPER.reader(DeserializationFeature.USE_BIG_INTEGER_FOR_INTS);
+
+ BigInteger exp = BigInteger.valueOf(123L);
+
+ // first test as any Number
+ Number result = r.forType(Number.class).readValue(" 123 ");
+ assertEquals(BigInteger.class, result.getClass());
+ assertEquals(exp, result);
+
+ // then as any Object
+ /*Object value =*/ r.forType(Object.class).readValue("123");
+ assertEquals(BigInteger.class, result.getClass());
+ assertEquals(exp, result);
+
+ // and as JsonNode
+ JsonNode node = r.readTree(" 123");
+ assertTrue(node.isBigInteger());
+ assertEquals(123, node.asInt());
+ }
+
+ public void testDoubleAsNumber() throws Exception
+ {
+ Number result = MAPPER.readValue(new StringReader(" 1.0 "), Number.class);
+ assertEquals(Double.valueOf(1.0), result);
+ }
+
+ public void testFpTypeOverrideSimple() throws Exception
+ {
+ ObjectReader r = MAPPER.reader(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
+ BigDecimal dec = new BigDecimal("0.1");
+
+ // First test generic stand-alone Number
+ Number result = r.forType(Number.class).readValue(dec.toString());
+ assertEquals(BigDecimal.class, result.getClass());
+ assertEquals(dec, result);
+
+ // Then plain old Object
+ Object value = r.forType(Object.class).readValue(dec.toString());
+ assertEquals(BigDecimal.class, result.getClass());
+ assertEquals(dec, value);
+
+ JsonNode node = r.readTree(dec.toString());
+ assertTrue(node.isBigDecimal());
+ assertEquals(dec.doubleValue(), node.asDouble());
+ }
+
+ public void testFpTypeOverrideStructured() throws Exception
+ {
+ ObjectReader r = MAPPER.reader(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
+
+ BigDecimal dec = new BigDecimal("-19.37");
+ // List element types
+ @SuppressWarnings("unchecked")
+ List<Object> list = (List<Object>) r.forType(List.class).readValue("[ "+dec.toString()+" ]");
+ assertEquals(1, list.size());
+ Object val = list.get(0);
+ assertEquals(BigDecimal.class, val.getClass());
+ assertEquals(dec, val);
+
+ // and a map
+ Map<?,?> map = r.forType(Map.class).readValue("{ \"a\" : "+dec.toString()+" }");
+ assertEquals(1, map.size());
+ val = map.get("a");
+ assertEquals(BigDecimal.class, val.getClass());
+ assertEquals(dec, val);
+ }
+
+ // [databind#504]
+ public void testForceIntsToLongs() throws Exception
+ {
+ ObjectReader r = MAPPER.reader(DeserializationFeature.USE_LONG_FOR_INTS);
+
+ Object ob = r.forType(Object.class).readValue("42");
+ assertEquals(Long.class, ob.getClass());
+ assertEquals(Long.valueOf(42L), ob);
+
+ Number n = r.forType(Number.class).readValue("42");
+ assertEquals(Long.class, n.getClass());
+ assertEquals(Long.valueOf(42L), n);
+
+ // and one more: should get proper node as well
+ JsonNode node = r.readTree("42");
+ if (!node.isLong()) {
+ fail("Expected LongNode, got: "+node.getClass().getName());
+ }
+ assertEquals(42, node.asInt());
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/JDKScalarsTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/JDKScalarsTest.java
new file mode 100644
index 0000000..2474fde
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/JDKScalarsTest.java
@@ -0,0 +1,988 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.io.*;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import org.junit.Assert;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Unit tests for verifying handling of simple basic non-structured
+ * types; primitives (and/or their wrappers), Strings.
+ */
+public class JDKScalarsTest
+ extends BaseMapTest
+{
+ final static String NAN_STRING = "NaN";
+
+ final static class BooleanBean {
+ boolean _v;
+ void setV(boolean v) { _v = v; }
+ }
+
+ static class BooleanWrapper {
+ public Boolean wrapper;
+ public boolean primitive;
+
+ protected Boolean ctor;
+
+ @JsonCreator
+ public BooleanWrapper(@JsonProperty("ctor") Boolean foo) {
+ ctor = foo;
+ }
+ }
+
+ static class IntBean {
+ int _v;
+ void setV(int v) { _v = v; }
+ }
+
+ final static class DoubleBean {
+ double _v;
+ void setV(double v) { _v = v; }
+ }
+
+ final static class FloatBean {
+ float _v;
+ void setV(float v) { _v = v; }
+ }
+
+ final static class CharacterBean {
+ char _v;
+ void setV(char v) { _v = v; }
+ char getV() { return _v; }
+ }
+
+ final static class CharacterWrapperBean {
+ Character _v;
+ void setV(Character v) { _v = v; }
+ Character getV() { return _v; }
+ }
+
+ /**
+ * Also, let's ensure that it's ok to override methods.
+ */
+ static class IntBean2
+ extends IntBean
+ {
+ @Override
+ void setV(int v2) { super.setV(v2+1); }
+ }
+
+ static class PrimitivesBean
+ {
+ public boolean booleanValue = true;
+ public byte byteValue = 3;
+ public char charValue = 'a';
+ public short shortValue = 37;
+ public int intValue = 1;
+ public long longValue = 100L;
+ public float floatValue = 0.25f;
+ public double doubleValue = -1.0;
+ }
+
+ static class WrappersBean
+ {
+ public Boolean booleanValue;
+ public Byte byteValue;
+ public Character charValue;
+ public Short shortValue;
+ public Integer intValue;
+ public Long longValue;
+ public Float floatValue;
+ public Double doubleValue;
+ }
+
+ private final ObjectMapper MAPPER = new ObjectMapper();
+
+ /*
+ /**********************************************************
+ /* Scalar tests for boolean
+ /**********************************************************
+ */
+
+ public void testBooleanPrimitive() throws Exception
+ {
+ // first, simple case:
+ BooleanBean result = MAPPER.readValue(new StringReader("{\"v\":true}"), BooleanBean.class);
+ assertTrue(result._v);
+ // then [JACKSON-79]:
+ result = MAPPER.readValue(new StringReader("{\"v\":null}"), BooleanBean.class);
+ assertNotNull(result);
+ assertFalse(result._v);
+
+ // should work with arrays too..
+ boolean[] array = MAPPER.readValue(new StringReader("[ null ]"), boolean[].class);
+ assertNotNull(array);
+ assertEquals(1, array.length);
+ assertFalse(array[0]);
+
+ // [Issue#381]
+ final ObjectMapper mapper = new ObjectMapper();
+ mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+ result = mapper.readValue(new StringReader("{\"v\":[true]}"), BooleanBean.class);
+ assertTrue(result._v);
+
+ try {
+ mapper.readValue(new StringReader("[{\"v\":[true,true]}]"), BooleanBean.class);
+ fail("Did not throw exception while reading a value from a multi value array with UNWRAP_SINGLE_VALUE_ARRAY feature enabled");
+ } catch (JsonMappingException exp) {
+ //threw exception as required
+ }
+
+ result = mapper.readValue(new StringReader("{\"v\":[null]}"), BooleanBean.class);
+ assertNotNull(result);
+ assertFalse(result._v);
+
+ result = mapper.readValue(new StringReader("[{\"v\":[null]}]"), BooleanBean.class);
+ assertNotNull(result);
+ assertFalse(result._v);
+
+ array = mapper.readValue(new StringReader("[ [ null ] ]"), boolean[].class);
+ assertNotNull(array);
+ assertEquals(1, array.length);
+ assertFalse(array[0]);
+ }
+
+ /**
+ * Simple unit test to verify that we can map boolean values to
+ * java.lang.Boolean.
+ */
+ public void testBooleanWrapper() throws Exception
+ {
+ Boolean result = MAPPER.readValue(new StringReader("true"), Boolean.class);
+ assertEquals(Boolean.TRUE, result);
+ result = MAPPER.readValue(new StringReader("false"), Boolean.class);
+ assertEquals(Boolean.FALSE, result);
+
+ // should accept ints too, (0 == false, otherwise true)
+ result = MAPPER.readValue(new StringReader("0"), Boolean.class);
+ assertEquals(Boolean.FALSE, result);
+ result = MAPPER.readValue(new StringReader("1"), Boolean.class);
+ assertEquals(Boolean.TRUE, result);
+ }
+
+ // Test for verifying that Long values are coerced to boolean correctly as well
+ public void testLongToBoolean() throws Exception
+ {
+ long value = 1L + Integer.MAX_VALUE;
+ BooleanWrapper b = MAPPER.readValue("{\"primitive\" : "+value+", \"wrapper\":"+value+", \"ctor\":"+value+"}",
+ BooleanWrapper.class);
+ assertEquals(Boolean.TRUE, b.wrapper);
+ assertTrue(b.primitive);
+ assertEquals(Boolean.TRUE, b.ctor);
+ }
+
+ /*
+ /**********************************************************
+ /* Scalar tests for integral types
+ /**********************************************************
+ */
+
+ public void testIntPrimitive() throws Exception
+ {
+ // first, simple case:
+ IntBean result = MAPPER.readValue(new StringReader("{\"v\":3}"), IntBean.class);
+ assertEquals(3, result._v);
+ result = MAPPER.readValue(new StringReader("{\"v\":null}"), IntBean.class);
+ assertNotNull(result);
+ assertEquals(0, result._v);
+
+ // should work with arrays too..
+ int[] array = MAPPER.readValue(new StringReader("[ null ]"), int[].class);
+ assertNotNull(array);
+ assertEquals(1, array.length);
+ assertEquals(0, array[0]);
+
+ // [Issue#381]
+ final ObjectMapper mapper = new ObjectMapper();
+ mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+ try {
+ mapper.readValue(new StringReader("{\"v\":[3]}"), IntBean.class);
+ fail("Did not throw exception when reading a value from a single value array with the UNWRAP_SINGLE_VALUE_ARRAYS feature disabled");
+ } catch (JsonMappingException exp) {
+ //Correctly threw exception
+ }
+
+ mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+
+ result = mapper.readValue(new StringReader("{\"v\":[3]}"), IntBean.class);
+ assertEquals(3, result._v);
+
+ result = mapper.readValue(new StringReader("[{\"v\":[3]}]"), IntBean.class);
+ assertEquals(3, result._v);
+
+ try {
+ mapper.readValue("[{\"v\":[3,3]}]", IntBean.class);
+ fail("Did not throw exception while reading a value from a multi value array with UNWRAP_SINGLE_VALUE_ARRAY feature enabled");
+ } catch (JsonMappingException exp) {
+ //threw exception as required
+ }
+
+ result = mapper.readValue("{\"v\":[null]}", IntBean.class);
+ assertNotNull(result);
+ assertEquals(0, result._v);
+
+ array = mapper.readValue("[ [ null ] ]", int[].class);
+ assertNotNull(array);
+ assertEquals(1, array.length);
+ assertEquals(0, array[0]);
+ }
+
+ public void testByteWrapper() throws Exception
+ {
+ Byte result = MAPPER.readValue(new StringReader(" -42\t"), Byte.class);
+ assertEquals(Byte.valueOf((byte)-42), result);
+
+ // Also: should be able to coerce floats, strings:
+ result = MAPPER.readValue(new StringReader(" \"-12\""), Byte.class);
+ assertEquals(Byte.valueOf((byte)-12), result);
+
+ result = MAPPER.readValue(new StringReader(" 39.07"), Byte.class);
+ assertEquals(Byte.valueOf((byte)39), result);
+ }
+
+ public void testShortWrapper() throws Exception
+ {
+ Short result = MAPPER.readValue(new StringReader("37"), Short.class);
+ assertEquals(Short.valueOf((short)37), result);
+
+ // Also: should be able to coerce floats, strings:
+ result = MAPPER.readValue(new StringReader(" \"-1009\""), Short.class);
+ assertEquals(Short.valueOf((short)-1009), result);
+
+ result = MAPPER.readValue(new StringReader("-12.9"), Short.class);
+ assertEquals(Short.valueOf((short)-12), result);
+ }
+
+ public void testCharacterWrapper() throws Exception
+ {
+ // First: canonical value is 1-char string
+ Character result = MAPPER.readValue(new StringReader("\"a\""), Character.class);
+ assertEquals(Character.valueOf('a'), result);
+
+ // But can also pass in ascii code
+ result = MAPPER.readValue(new StringReader(" "+((int) 'X')), Character.class);
+ assertEquals(Character.valueOf('X'), result);
+
+ final CharacterWrapperBean wrapper = MAPPER.readValue(new StringReader("{\"v\":null}"), CharacterWrapperBean.class);
+ assertNotNull(wrapper);
+ assertNull(wrapper.getV());
+
+ final ObjectMapper mapper = new ObjectMapper();
+ mapper.enable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES);
+ try {
+ mapper.readValue("{\"v\":null}", CharacterBean.class);
+ fail("Attempting to deserialize a 'null' JSON reference into a 'char' property did not throw an exception");
+ } catch (JsonMappingException exp) {
+ //Exception thrown as required
+ }
+
+ mapper.disable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES);
+ final CharacterBean charBean = MAPPER.readValue(new StringReader("{\"v\":null}"), CharacterBean.class);
+ assertNotNull(wrapper);
+ assertEquals('\u0000', charBean.getV());
+ }
+
+ public void testIntWrapper() throws Exception
+ {
+ Integer result = MAPPER.readValue(new StringReader(" -42\t"), Integer.class);
+ assertEquals(Integer.valueOf(-42), result);
+
+ // Also: should be able to coerce floats, strings:
+ result = MAPPER.readValue(new StringReader(" \"-1200\""), Integer.class);
+ assertEquals(Integer.valueOf(-1200), result);
+
+ result = MAPPER.readValue(new StringReader(" 39.07"), Integer.class);
+ assertEquals(Integer.valueOf(39), result);
+ }
+
+ public void testLongWrapper() throws Exception
+ {
+ Long result = MAPPER.readValue(new StringReader("12345678901"), Long.class);
+ assertEquals(Long.valueOf(12345678901L), result);
+
+ // Also: should be able to coerce floats, strings:
+ result = MAPPER.readValue(new StringReader(" \"-9876\""), Long.class);
+ assertEquals(Long.valueOf(-9876), result);
+
+ result = MAPPER.readValue(new StringReader("1918.3"), Long.class);
+ assertEquals(Long.valueOf(1918), result);
+ }
+
+ /**
+ * Beyond simple case, let's also ensure that method overriding works as
+ * expected.
+ */
+ public void testIntWithOverride() throws Exception
+ {
+ IntBean2 result = MAPPER.readValue(new StringReader("{\"v\":8}"), IntBean2.class);
+ assertEquals(9, result._v);
+ }
+
+ /*
+ /**********************************************************
+ /* Scalar tests for floating point types
+ /**********************************************************
+ */
+
+ public void testDoublePrimitive() throws Exception
+ {
+ // first, simple case:
+ // bit tricky with binary fps but...
+ final double value = 0.016;
+ DoubleBean result = MAPPER.readValue(new StringReader("{\"v\":"+value+"}"), DoubleBean.class);
+ assertEquals(value, result._v);
+ // then [JACKSON-79]:
+ result = MAPPER.readValue(new StringReader("{\"v\":null}"), DoubleBean.class);
+ assertNotNull(result);
+ assertEquals(0.0, result._v);
+
+ // should work with arrays too..
+ double[] array = MAPPER.readValue(new StringReader("[ null ]"), double[].class);
+ assertNotNull(array);
+ assertEquals(1, array.length);
+ assertEquals(0.0, array[0]);
+ }
+
+ /* Note: dealing with floating-point values is tricky; not sure if
+ * we can really use equality tests here... JDK does have decent
+ * conversions though, to retain accuracy and round-trippability.
+ * But still...
+ */
+ public void testFloatWrapper() throws Exception
+ {
+ // Also: should be able to coerce floats, strings:
+ String[] STRS = new String[] {
+ "1.0", "0.0", "-0.3", "0.7", "42.012", "-999.0", NAN_STRING
+ };
+
+ for (String str : STRS) {
+ Float exp = Float.valueOf(str);
+ Float result;
+
+ if (NAN_STRING != str) {
+ // First, as regular floating point value
+ result = MAPPER.readValue(new StringReader(str), Float.class);
+ assertEquals(exp, result);
+ }
+
+ // and then as coerced String:
+ result = MAPPER.readValue(new StringReader(" \""+str+"\""), Float.class);
+ assertEquals(exp, result);
+ }
+ }
+
+ public void testDoubleWrapper() throws Exception
+ {
+ // Also: should be able to coerce doubles, strings:
+ String[] STRS = new String[] {
+ "1.0", "0.0", "-0.3", "0.7", "42.012", "-999.0", NAN_STRING
+ };
+
+ for (String str : STRS) {
+ Double exp = Double.valueOf(str);
+ Double result;
+
+ // First, as regular double value
+ if (NAN_STRING != str) {
+ result = MAPPER.readValue(str, Double.class);
+ assertEquals(exp, result);
+ }
+ // and then as coerced String:
+ result = MAPPER.readValue(new StringReader(" \""+str+"\""), Double.class);
+ assertEquals(exp, result);
+ }
+ }
+
+ public void testDoubleAsArray() throws Exception
+ {
+ final ObjectMapper mapper = new ObjectMapper();
+ mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+ final double value = 0.016;
+ try {
+ mapper.readValue(new StringReader("{\"v\":[" + value + "]}"), DoubleBean.class);
+ fail("Did not throw exception when reading a value from a single value array with the UNWRAP_SINGLE_VALUE_ARRAYS feature disabled");
+ } catch (JsonMappingException exp) {
+ //Correctly threw exception
+ }
+
+ mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+
+ DoubleBean result = mapper.readValue(new StringReader("{\"v\":[" + value + "]}"),
+ DoubleBean.class);
+ assertEquals(value, result._v);
+
+ result = mapper.readValue(new StringReader("[{\"v\":[" + value + "]}]"), DoubleBean.class);
+ assertEquals(value, result._v);
+
+ try {
+ mapper.readValue(new StringReader("[{\"v\":[" + value + "," + value + "]}]"), DoubleBean.class);
+ fail("Did not throw exception while reading a value from a multi value array with UNWRAP_SINGLE_VALUE_ARRAY feature enabled");
+ } catch (JsonMappingException exp) {
+ //threw exception as required
+ }
+
+ result = mapper.readValue(new StringReader("{\"v\":[null]}"), DoubleBean.class);
+ assertNotNull(result);
+ assertEquals(0d, result._v);
+
+ double[] array = mapper.readValue(new StringReader("[ [ null ] ]"), double[].class);
+ assertNotNull(array);
+ assertEquals(1, array.length);
+ assertEquals(0d, array[0]);
+ }
+
+ public void testDoublePrimitiveNonNumeric() throws Exception
+ {
+ // first, simple case:
+ // bit tricky with binary fps but...
+ double value = Double.POSITIVE_INFINITY;
+ DoubleBean result = MAPPER.readValue(new StringReader("{\"v\":\""+value+"\"}"), DoubleBean.class);
+ assertEquals(value, result._v);
+
+ // should work with arrays too..
+ double[] array = MAPPER.readValue(new StringReader("[ \"Infinity\" ]"), double[].class);
+ assertNotNull(array);
+ assertEquals(1, array.length);
+ assertEquals(Double.POSITIVE_INFINITY, array[0]);
+ }
+
+ public void testFloatPrimitiveNonNumeric() throws Exception
+ {
+ // bit tricky with binary fps but...
+ float value = Float.POSITIVE_INFINITY;
+ FloatBean result = MAPPER.readValue(new StringReader("{\"v\":\""+value+"\"}"), FloatBean.class);
+ assertEquals(value, result._v);
+
+ // should work with arrays too..
+ float[] array = MAPPER.readValue(new StringReader("[ \"Infinity\" ]"), float[].class);
+ assertNotNull(array);
+ assertEquals(1, array.length);
+ assertEquals(Float.POSITIVE_INFINITY, array[0]);
+ }
+
+ /*
+ /**********************************************************
+ /* Scalar tests, other
+ /**********************************************************
+ */
+
+ public void testEmptyToNullCoercionForPrimitives() throws Exception {
+ _testEmptyToNullCoercion(int.class, Integer.valueOf(0));
+ _testEmptyToNullCoercion(long.class, Long.valueOf(0));
+ _testEmptyToNullCoercion(double.class, Double.valueOf(0.0));
+ _testEmptyToNullCoercion(float.class, Float.valueOf(0.0f));
+ }
+
+ private void _testEmptyToNullCoercion(Class<?> primType, Object emptyValue) throws Exception
+ {
+ final String EMPTY = "\"\"";
+
+ // as per [databind#1095] should only allow coercion from empty String,
+ // if `null` is acceptable
+ ObjectReader intR = MAPPER.readerFor(primType);
+ assertEquals(emptyValue, intR.readValue(EMPTY));
+ try {
+ intR.with(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)
+ .readValue("\"\"");
+ fail("Should not have passed");
+ } catch (JsonMappingException e) {
+ verifyException(e, "Can not map Empty String");
+ }
+ }
+
+ public void testBase64Variants() throws Exception
+ {
+ final byte[] INPUT = "abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz1234567890X".getBytes("UTF-8");
+
+ // default encoding is "MIME, no linefeeds", so:
+ Assert.assertArrayEquals(INPUT, MAPPER.readValue(
+ quote("YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwWA=="),
+ byte[].class));
+ ObjectReader reader = MAPPER.readerFor(byte[].class);
+ Assert.assertArrayEquals(INPUT, (byte[]) reader.with(Base64Variants.MIME_NO_LINEFEEDS).readValue(
+ quote("YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwWA=="
+ )));
+
+ // but others should be slightly different
+ Assert.assertArrayEquals(INPUT, (byte[]) reader.with(Base64Variants.MIME).readValue(
+ quote("YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwYWJjZGVmZ2hpamtsbW5vcHFyc3R1\\ndnd4eXoxMjM0NTY3ODkwWA=="
+ )));
+ Assert.assertArrayEquals(INPUT, (byte[]) reader.with(Base64Variants.MODIFIED_FOR_URL).readValue(
+ quote("YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwWA"
+ )));
+ // PEM mandates 64 char lines:
+ Assert.assertArrayEquals(INPUT, (byte[]) reader.with(Base64Variants.PEM).readValue(
+ quote("YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwYWJjZGVmZ2hpamts\\nbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwWA=="
+ )));
+ }
+ /*
+ /**********************************************************
+ /* Simple non-primitive types
+ /**********************************************************
+ */
+
+ public void testSingleString() throws Exception
+ {
+ String value = "FOO!";
+ String result = MAPPER.readValue(new StringReader("\""+value+"\""), String.class);
+ assertEquals(value, result);
+ }
+
+ public void testSingleStringWrapped() throws Exception
+ {
+ final ObjectMapper mapper = new ObjectMapper();
+ mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+
+ String value = "FOO!";
+ try {
+ mapper.readValue(new StringReader("[\""+value+"\"]"), String.class);
+ fail("Exception not thrown when attempting to unwrap a single value 'String' array into a simple String");
+ } catch (JsonMappingException exp) {
+ //exception thrown correctly
+ }
+
+ mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+
+ try {
+ mapper.readValue(new StringReader("[\""+value+"\",\""+value+"\"]"), String.class);
+ fail("Exception not thrown when attempting to unwrap a single value 'String' array that contained more than one value into a simple String");
+ } catch (JsonMappingException exp) {
+ //exception thrown correctly
+ }
+
+ String result = mapper.readValue(new StringReader("[\""+value+"\"]"), String.class);
+ assertEquals(value, result);
+ }
+
+ public void testBigDecimal() throws Exception
+ {
+ final ObjectMapper mapper = objectMapper();
+ mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+
+ BigDecimal value = new BigDecimal("0.001");
+ BigDecimal result = mapper.readValue(value.toString(), BigDecimal.class);
+ assertEquals(value, result);
+ try {
+ mapper.readValue("[" + value.toString() + "]", BigDecimal.class);
+ fail("Exception was not thrown when attempting to read a single value array of BigDecimal when UNWRAP_SINGLE_VALUE_ARRAYS feature is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+
+ mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+ result = mapper.readValue("[" + value.toString() + "]", BigDecimal.class);
+ assertEquals(value, result);
+
+ try {
+ mapper.readValue("[" + value.toString() + "," + value.toString() + "]", BigDecimal.class);
+ fail("Exception was not thrown when attempting to read a muti value array of BigDecimal when UNWRAP_SINGLE_VALUE_ARRAYS feature is enabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+ }
+
+ public void testBigInteger() throws Exception
+ {
+ final ObjectMapper mapper = objectMapper();
+ mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+
+ BigInteger value = new BigInteger("-1234567890123456789012345567809");
+ BigInteger result = mapper.readValue(new StringReader(value.toString()), BigInteger.class);
+ assertEquals(value, result);
+
+ //Issue#381
+ try {
+ mapper.readValue("[" + value.toString() + "]", BigInteger.class);
+ fail("Exception was not thrown when attempting to read a single value array of BigInteger when UNWRAP_SINGLE_VALUE_ARRAYS feature is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+
+ mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+ result = mapper.readValue("[" + value.toString() + "]", BigInteger.class);
+ assertEquals(value, result);
+
+ try {
+ mapper.readValue("[" + value.toString() + "," + value.toString() + "]", BigInteger.class);
+ fail("Exception was not thrown when attempting to read a muti value array of BigInteger when UNWRAP_SINGLE_VALUE_ARRAYS feature is enabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+ }
+
+ /*
+ /**********************************************************
+ /* Sequence tests
+ /**********************************************************
+ */
+
+ /**
+ * Then a unit test to verify that we can conveniently bind sequence of
+ * space-separate simple values
+ */
+ public void testSequenceOfInts() throws Exception
+ {
+ final int NR_OF_INTS = 100;
+
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < NR_OF_INTS; ++i) {
+ sb.append(" ");
+ sb.append(i);
+ }
+ JsonParser jp = MAPPER.getFactory().createParser(sb.toString());
+ for (int i = 0; i < NR_OF_INTS; ++i) {
+ Integer result = MAPPER.readValue(jp, Integer.class);
+ assertEquals(Integer.valueOf(i), result);
+ }
+ jp.close();
+ }
+
+ /*
+ /**********************************************************
+ /* Single-element as array tests
+ /**********************************************************
+ */
+
+ // [databind#381]
+ public void testSingleElementScalarArrays() throws Exception {
+ final int intTest = 932832;
+ final double doubleTest = 32.3234;
+ final long longTest = 2374237428374293423L;
+ final short shortTest = (short) intTest;
+ final float floatTest = 84.3743f;
+ final byte byteTest = (byte) 43;
+ final char charTest = 'c';
+
+ final ObjectMapper mapper = new ObjectMapper();
+ mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+
+ final int intValue = mapper.readValue(asArray(intTest), Integer.TYPE);
+ assertEquals(intTest, intValue);
+ final Integer integerWrapperValue = mapper.readValue(asArray(Integer.valueOf(intTest)), Integer.class);
+ assertEquals(Integer.valueOf(intTest), integerWrapperValue);
+
+ final double doubleValue = mapper.readValue(asArray(doubleTest), Double.class);
+ assertEquals(doubleTest, doubleValue);
+ final Double doubleWrapperValue = mapper.readValue(asArray(Double.valueOf(doubleTest)), Double.class);
+ assertEquals(Double.valueOf(doubleTest), doubleWrapperValue);
+
+ final long longValue = mapper.readValue(asArray(longTest), Long.TYPE);
+ assertEquals(longTest, longValue);
+ final Long longWrapperValue = mapper.readValue(asArray(Long.valueOf(longTest)), Long.class);
+ assertEquals(Long.valueOf(longTest), longWrapperValue);
+
+ final short shortValue = mapper.readValue(asArray(shortTest), Short.TYPE);
+ assertEquals(shortTest, shortValue);
+ final Short shortWrapperValue = mapper.readValue(asArray(Short.valueOf(shortTest)), Short.class);
+ assertEquals(Short.valueOf(shortTest), shortWrapperValue);
+
+ final float floatValue = mapper.readValue(asArray(floatTest), Float.TYPE);
+ assertEquals(floatTest, floatValue);
+ final Float floatWrapperValue = mapper.readValue(asArray(Float.valueOf(floatTest)), Float.class);
+ assertEquals(Float.valueOf(floatTest), floatWrapperValue);
+
+ final byte byteValue = mapper.readValue(asArray(byteTest), Byte.TYPE);
+ assertEquals(byteTest, byteValue);
+ final Byte byteWrapperValue = mapper.readValue(asArray(Byte.valueOf(byteTest)), Byte.class);
+ assertEquals(Byte.valueOf(byteTest), byteWrapperValue);
+
+ final char charValue = mapper.readValue(asArray(quote(String.valueOf(charTest))), Character.TYPE);
+ assertEquals(charTest, charValue);
+ final Character charWrapperValue = mapper.readValue(asArray(quote(String.valueOf(charTest))), Character.class);
+ assertEquals(Character.valueOf(charTest), charWrapperValue);
+
+ final boolean booleanTrueValue = mapper.readValue(asArray(true), Boolean.TYPE);
+ assertTrue(booleanTrueValue);
+
+ final boolean booleanFalseValue = mapper.readValue(asArray(false), Boolean.TYPE);
+ assertFalse(booleanFalseValue);
+
+ final Boolean booleanWrapperTrueValue = mapper.readValue(asArray(Boolean.valueOf(true)), Boolean.class);
+ assertEquals(Boolean.TRUE, booleanWrapperTrueValue);
+ }
+
+ public void testSingleElementArrayDisabled() throws Exception {
+ final ObjectMapper mapper = new ObjectMapper();
+ mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+ try {
+ mapper.readValue("[42]", Integer.class);
+ fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+ try {
+ mapper.readValue("[42]", Integer.TYPE);
+ fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+
+ try {
+ mapper.readValue("[42.273]", Double.class);
+ fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+ try {
+ mapper.readValue("[42.2723]", Double.TYPE);
+ fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+
+ try {
+ mapper.readValue("[42342342342342]", Long.class);
+ fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+ try {
+ mapper.readValue("[42342342342342342]", Long.TYPE);
+ fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+
+ try {
+ mapper.readValue("[42]", Short.class);
+ fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+ try {
+ mapper.readValue("[42]", Short.TYPE);
+ fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+
+ try {
+ mapper.readValue("[327.2323]", Float.class);
+ fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+ try {
+ mapper.readValue("[82.81902]", Float.TYPE);
+ fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+
+ try {
+ mapper.readValue("[22]", Byte.class);
+ fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+ try {
+ mapper.readValue("[22]", Byte.TYPE);
+ fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+
+ try {
+ mapper.readValue("['d']", Character.class);
+ fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+ try {
+ mapper.readValue("['d']", Character.TYPE);
+ fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+
+ try {
+ mapper.readValue("[true]", Boolean.class);
+ fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+ try {
+ mapper.readValue("[true]", Boolean.TYPE);
+ fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+ }
+
+ public void testMultiValueArrayException() throws IOException {
+ final ObjectMapper mapper = new ObjectMapper();
+ mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+
+ try {
+ mapper.readValue("[42,42]", Integer.class);
+ fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+ try {
+ mapper.readValue("[42,42]", Integer.TYPE);
+ fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+
+ try {
+ mapper.readValue("[42.273,42.273]", Double.class);
+ fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+ try {
+ mapper.readValue("[42.2723,42.273]", Double.TYPE);
+ fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+
+ try {
+ mapper.readValue("[42342342342342,42342342342342]", Long.class);
+ fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+ try {
+ mapper.readValue("[42342342342342342,42342342342342]", Long.TYPE);
+ fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+
+ try {
+ mapper.readValue("[42,42]", Short.class);
+ fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+ try {
+ mapper.readValue("[42,42]", Short.TYPE);
+ fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+
+ try {
+ mapper.readValue("[327.2323,327.2323]", Float.class);
+ fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+ try {
+ mapper.readValue("[82.81902,327.2323]", Float.TYPE);
+ fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+
+ try {
+ mapper.readValue("[22,23]", Byte.class);
+ fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+ try {
+ mapper.readValue("[22,23]", Byte.TYPE);
+ fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+
+ try {
+ mapper.readValue(asArray(quote("c") + "," + quote("d")), Character.class);
+
+ fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+ try {
+ mapper.readValue(asArray(quote("c") + "," + quote("d")), Character.TYPE);
+ fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+
+ try {
+ mapper.readValue("[true,false]", Boolean.class);
+ fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+ try {
+ mapper.readValue("[true,false]", Boolean.TYPE);
+ fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+ } catch (JsonMappingException exp) {
+ //Exception was thrown correctly
+ }
+ }
+
+ private static String asArray(Object value) {
+ final String stringVal = value.toString();
+ return new StringBuilder(stringVal.length() + 2).append("[").append(stringVal).append("]").toString();
+ }
+
+ /*
+ /**********************************************************
+ /* Empty String coercion, handling
+ /**********************************************************
+ */
+
+ // by default, should return nulls, n'est pas?
+ public void testEmptyStringForWrappers() throws IOException
+ {
+ WrappersBean bean;
+
+ // by default, ok to rely on defaults
+ bean = MAPPER.readValue("{\"booleanValue\":\"\"}", WrappersBean.class);
+ assertNull(bean.booleanValue);
+ bean = MAPPER.readValue("{\"byteValue\":\"\"}", WrappersBean.class);
+ assertNull(bean.byteValue);
+
+ // char/Character is different... not sure if this should work or not:
+ bean = MAPPER.readValue("{\"charValue\":\"\"}", WrappersBean.class);
+ assertNull(bean.charValue);
+
+ bean = MAPPER.readValue("{\"shortValue\":\"\"}", WrappersBean.class);
+ assertNull(bean.shortValue);
+ bean = MAPPER.readValue("{\"intValue\":\"\"}", WrappersBean.class);
+ assertNull(bean.intValue);
+ bean = MAPPER.readValue("{\"longValue\":\"\"}", WrappersBean.class);
+ assertNull(bean.longValue);
+ bean = MAPPER.readValue("{\"floatValue\":\"\"}", WrappersBean.class);
+ assertNull(bean.floatValue);
+ bean = MAPPER.readValue("{\"doubleValue\":\"\"}", WrappersBean.class);
+ assertNull(bean.doubleValue);
+ }
+
+ public void testEmptyStringForPrimitives() throws IOException
+ {
+ PrimitivesBean bean;
+ bean = MAPPER.readValue("{\"booleanValue\":\"\"}", PrimitivesBean.class);
+ assertFalse(bean.booleanValue);
+ bean = MAPPER.readValue("{\"byteValue\":\"\"}", PrimitivesBean.class);
+ assertEquals((byte) 0, bean.byteValue);
+ bean = MAPPER.readValue("{\"charValue\":\"\"}", PrimitivesBean.class);
+ assertEquals((char) 0, bean.charValue);
+ bean = MAPPER.readValue("{\"shortValue\":\"\"}", PrimitivesBean.class);
+ assertEquals((short) 0, bean.shortValue);
+ bean = MAPPER.readValue("{\"intValue\":\"\"}", PrimitivesBean.class);
+ assertEquals(0, bean.intValue);
+ bean = MAPPER.readValue("{\"longValue\":\"\"}", PrimitivesBean.class);
+ assertEquals(0L, bean.longValue);
+ bean = MAPPER.readValue("{\"floatValue\":\"\"}", PrimitivesBean.class);
+ assertEquals(0.0f, bean.floatValue);
+ bean = MAPPER.readValue("{\"doubleValue\":\"\"}", PrimitivesBean.class);
+ assertEquals(0.0, bean.doubleValue);
+ }
+}
+
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/JDKStringLikeTypesTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/JDKStringLikeTypesTest.java
new file mode 100644
index 0000000..cef00c1
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/JDKStringLikeTypesTest.java
@@ -0,0 +1,477 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.io.*;
+import java.net.*;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.Currency;
+import java.util.Locale;
+import java.util.UUID;
+import java.util.regex.Pattern;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+import com.fasterxml.jackson.core.Base64Variants;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import com.fasterxml.jackson.databind.exc.InvalidFormatException;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.databind.util.TokenBuffer;
+
+public class JDKStringLikeTypesTest extends BaseMapTest
+{
+ static class ParamClassBean
+ {
+ public String name = "bar";
+ public Class<String> clazz ;
+
+ public ParamClassBean() { }
+ public ParamClassBean(String name) {
+ this.name = name;
+ clazz = String.class;
+ }
+ }
+
+ // [databind#429]
+ static class StackTraceBean {
+ public final static int NUM = 13;
+
+ @JsonProperty("Location")
+ @JsonDeserialize(using=MyStackTraceElementDeserializer.class)
+ protected StackTraceElement location;
+ }
+
+ @SuppressWarnings("serial")
+ static class MyStackTraceElementDeserializer extends StdDeserializer<StackTraceElement>
+ {
+ public MyStackTraceElementDeserializer() { super(StackTraceElement.class); }
+
+ @Override
+ public StackTraceElement deserialize(JsonParser jp,
+ DeserializationContext ctxt) throws IOException, JsonProcessingException {
+ jp.skipChildren();
+ return new StackTraceElement("a", "b", "b", StackTraceBean.NUM);
+ }
+
+ }
+
+ /*
+ /**********************************************************
+ /* Test methods
+ /**********************************************************
+ */
+
+ private final ObjectMapper MAPPER = objectMapper();
+
+ // [databind#239]
+ public void testByteBuffer() throws Exception
+ {
+ byte[] INPUT = new byte[] { 1, 3, 9, -1, 6 };
+ String exp = MAPPER.writeValueAsString(INPUT);
+ ByteBuffer result = MAPPER.readValue(exp, ByteBuffer.class);
+ assertNotNull(result);
+ assertEquals(INPUT.length, result.remaining());
+ for (int i = 0; i < INPUT.length; ++i) {
+ assertEquals(INPUT[i], result.get());
+ }
+ assertEquals(0, result.remaining());
+ }
+
+ public void testCharset() throws Exception
+ {
+ Charset UTF8 = Charset.forName("UTF-8");
+ assertSame(UTF8, MAPPER.readValue(quote("UTF-8"), Charset.class));
+ }
+
+ public void testClass() throws IOException
+ {
+ ObjectMapper mapper = new ObjectMapper();
+ assertSame(String.class, mapper.readValue(quote("java.lang.String"), Class.class));
+
+ // then primitive types
+ assertSame(Boolean.TYPE, mapper.readValue(quote("boolean"), Class.class));
+ assertSame(Byte.TYPE, mapper.readValue(quote("byte"), Class.class));
+ assertSame(Short.TYPE, mapper.readValue(quote("short"), Class.class));
+ assertSame(Character.TYPE, mapper.readValue(quote("char"), Class.class));
+ assertSame(Integer.TYPE, mapper.readValue(quote("int"), Class.class));
+ assertSame(Long.TYPE, mapper.readValue(quote("long"), Class.class));
+ assertSame(Float.TYPE, mapper.readValue(quote("float"), Class.class));
+ assertSame(Double.TYPE, mapper.readValue(quote("double"), Class.class));
+ assertSame(Void.TYPE, mapper.readValue(quote("void"), Class.class));
+ }
+
+ public void testClassWithParams() throws IOException
+ {
+ String json = MAPPER.writeValueAsString(new ParamClassBean("Foobar"));
+
+ ParamClassBean result = MAPPER.readValue(json, ParamClassBean.class);
+ assertEquals("Foobar", result.name);
+ assertSame(String.class, result.clazz);
+ }
+
+ public void testClassAsArray() throws Exception
+ {
+ final ObjectMapper mapper = new ObjectMapper();
+ Class<?> result = mapper
+ .readerFor(Class.class)
+ .with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)
+ .readValue(quote(String.class.getName()));
+ assertEquals(String.class, result);
+
+ try {
+ mapper
+ .readerFor(Class.class)
+ .without(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)
+ .readValue("[" + quote(String.class.getName()) + "]");
+ fail("Did not throw exception when UNWRAP_SINGLE_VALUE_ARRAYS feature was disabled and attempted to read a Class array containing one element");
+ } catch (JsonMappingException e) {
+ verifyException(e, "out of START_ARRAY token");
+ }
+
+ try {
+ mapper
+ .readerFor(Class.class)
+ .with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)
+ .readValue("[" + quote(Object.class.getName()) + "," + quote(Object.class.getName()) +"]");
+ fail("Did not throw exception when UNWRAP_SINGLE_VALUE_ARRAYS feature was enabled and attempted to read a Class array containing two elements");
+ } catch (JsonMappingException e) {
+ verifyException(e, "more than a single value in");
+ }
+ result = mapper
+ .readerFor(Class.class)
+ .with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)
+ .readValue("[" + quote(String.class.getName()) + "]");
+ assertEquals(String.class, result);
+ }
+
+ public void testCurrency() throws IOException
+ {
+ Currency usd = Currency.getInstance("USD");
+ assertEquals(usd, new ObjectMapper().readValue(quote("USD"), Currency.class));
+ }
+
+ public void testFile() throws Exception
+ {
+ // Not portable etc... has to do:
+ File src = new File("/test").getAbsoluteFile();
+ String abs = src.getAbsolutePath();
+
+ // escape backslashes (for portability with windows)
+ String json = MAPPER.writeValueAsString(abs);
+ File result = MAPPER.readValue(json, File.class);
+ assertEquals(abs, result.getAbsolutePath());
+
+ // Then #170
+ final ObjectMapper mapper2 = new ObjectMapper();
+ mapper2.setVisibility(PropertyAccessor.CREATOR, Visibility.NONE);
+
+ result = mapper2.readValue(json, File.class);
+ assertEquals(abs, result.getAbsolutePath());
+ }
+
+ public void testLocale() throws IOException
+ {
+ assertEquals(new Locale("en"), MAPPER.readValue(quote("en"), Locale.class));
+ assertEquals(new Locale("es", "ES"), MAPPER.readValue(quote("es_ES"), Locale.class));
+ assertEquals(new Locale("FI", "fi", "savo"),
+ MAPPER.readValue(quote("fi_FI_savo"), Locale.class));
+ assertEquals(new Locale("en", "US"),
+ MAPPER.readValue(quote("en-US"), Locale.class));
+
+ // [databind#1123]
+ Locale loc = MAPPER.readValue(quote(""), Locale.class);
+ assertSame(Locale.ROOT, loc);
+ }
+
+ public void testCharSequence() throws IOException
+ {
+ CharSequence cs = MAPPER.readValue("\"abc\"", CharSequence.class);
+ assertEquals(String.class, cs.getClass());
+ assertEquals("abc", cs.toString());
+ }
+
+ public void testInetAddress() throws IOException
+ {
+ InetAddress address = MAPPER.readValue(quote("127.0.0.1"), InetAddress.class);
+ assertEquals("127.0.0.1", address.getHostAddress());
+
+ // should we try resolving host names? That requires connectivity...
+ final String HOST = "google.com";
+ address = MAPPER.readValue(quote(HOST), InetAddress.class);
+ assertEquals(HOST, address.getHostName());
+ }
+
+ public void testInetSocketAddress() throws IOException
+ {
+ InetSocketAddress address = MAPPER.readValue(quote("127.0.0.1"), InetSocketAddress.class);
+ assertEquals("127.0.0.1", address.getAddress().getHostAddress());
+
+ InetSocketAddress ip6 = MAPPER.readValue(
+ quote("2001:db8:85a3:8d3:1319:8a2e:370:7348"), InetSocketAddress.class);
+ assertEquals("2001:db8:85a3:8d3:1319:8a2e:370:7348", ip6.getAddress().getHostAddress());
+
+ InetSocketAddress ip6port = MAPPER.readValue(
+ quote("[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443"), InetSocketAddress.class);
+ assertEquals("2001:db8:85a3:8d3:1319:8a2e:370:7348", ip6port.getAddress().getHostAddress());
+ assertEquals(443, ip6port.getPort());
+
+ // should we try resolving host names? That requires connectivity...
+ final String HOST = "www.google.com";
+ address = MAPPER.readValue(quote(HOST), InetSocketAddress.class);
+ assertEquals(HOST, address.getHostName());
+
+ final String HOST_AND_PORT = HOST+":80";
+ address = MAPPER.readValue(quote(HOST_AND_PORT), InetSocketAddress.class);
+ assertEquals(HOST, address.getHostName());
+ assertEquals(80, address.getPort());
+ }
+
+ public void testRegexps() throws IOException
+ {
+ final String PATTERN_STR = "abc:\\s?(\\d+)";
+ Pattern exp = Pattern.compile(PATTERN_STR);
+ /* Ok: easiest way is to just serialize first; problem
+ * is the backslash
+ */
+ String json = MAPPER.writeValueAsString(exp);
+ Pattern result = MAPPER.readValue(json, Pattern.class);
+ assertEquals(exp.pattern(), result.pattern());
+ }
+ public void testStackTraceElement() throws Exception
+ {
+ StackTraceElement elem = null;
+ try {
+ throw new IllegalStateException();
+ } catch (Exception e) {
+ elem = e.getStackTrace()[0];
+ }
+ String json = MAPPER.writeValueAsString(elem);
+ StackTraceElement back = MAPPER.readValue(json, StackTraceElement.class);
+
+ assertEquals("testStackTraceElement", back.getMethodName());
+ assertEquals(elem.getLineNumber(), back.getLineNumber());
+ assertEquals(elem.getClassName(), back.getClassName());
+ assertEquals(elem.isNativeMethod(), back.isNativeMethod());
+ assertTrue(back.getClassName().endsWith("JDKStringLikeTypesTest"));
+ assertFalse(back.isNativeMethod());
+ }
+
+ // [databind#429]
+ public void testStackTraceElementWithCustom() throws Exception
+ {
+ // first, via bean that contains StackTraceElement
+ StackTraceBean bean = MAPPER.readValue(aposToQuotes("{'Location':'foobar'}"),
+ StackTraceBean.class);
+ assertNotNull(bean);
+ assertNotNull(bean.location);
+ assertEquals(StackTraceBean.NUM, bean.location.getLineNumber());
+
+ // and then directly, iff registered
+ ObjectMapper mapper = new ObjectMapper();
+ SimpleModule module = new SimpleModule();
+ module.addDeserializer(StackTraceElement.class, new MyStackTraceElementDeserializer());
+ mapper.registerModule(module);
+
+ StackTraceElement elem = mapper.readValue("123", StackTraceElement.class);
+ assertNotNull(elem);
+ assertEquals(StackTraceBean.NUM, elem.getLineNumber());
+
+ // and finally, even as part of real exception
+
+ IOException ioe = mapper.readValue(aposToQuotes("{'stackTrace':[ 123, 456 ]}"),
+ IOException.class);
+ assertNotNull(ioe);
+ StackTraceElement[] traces = ioe.getStackTrace();
+ assertNotNull(traces);
+ assertEquals(2, traces.length);
+ assertEquals(StackTraceBean.NUM, traces[0].getLineNumber());
+ assertEquals(StackTraceBean.NUM, traces[1].getLineNumber());
+ }
+
+ public void testStringBuilder() throws Exception
+ {
+ StringBuilder sb = MAPPER.readValue(quote("abc"), StringBuilder.class);
+ assertEquals("abc", sb.toString());
+ }
+
+ public void testURI() throws Exception
+ {
+ final ObjectReader reader = MAPPER.readerFor(URI.class);
+ final URI value = new URI("http://foo.com");
+ assertEquals(value, reader.readValue("\""+value.toString()+"\""));
+
+ // Also: empty String should be handled properly
+ URI result = reader.readValue(quote(""));
+ assertNotNull(result);
+ assertEquals(URI.create(""), result);
+
+ // and finally: broken URI should give proper failure
+ try {
+ result = reader.readValue(quote("a b"));
+ fail("Should not accept malformed URI, instead got: "+result);
+ } catch (InvalidFormatException e) {
+ verifyException(e, "not a valid textual representation");
+ }
+ }
+
+ public void testURIAsArray() throws Exception
+ {
+ final ObjectReader reader = MAPPER.readerFor(URI.class);
+ final URI value = new URI("http://foo.com");
+ try {
+ reader.without(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)
+ .readValue("[\""+value.toString()+"\"]");
+ fail("Did not throw exception for single value array when UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+ } catch (JsonMappingException e) {
+ verifyException(e, "out of START_ARRAY token");
+ }
+
+ try {
+ reader.with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)
+ .readValue("[\""+value.toString()+"\",\""+value.toString()+"\"]");
+ fail("Did not throw exception for single value array when there were multiple values");
+ } catch (JsonMappingException e) {
+ verifyException(e, "more than a single value in the array");
+ }
+ assertEquals(value,
+ reader.with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)
+ .readValue("[\""+value.toString()+"\"]"));
+ }
+
+ public void testURL() throws Exception
+ {
+ URL exp = new URL("http://foo.com");
+ assertEquals(exp, MAPPER.readValue("\""+exp.toString()+"\"", URL.class));
+
+ // trivial case; null to null, embedded URL to URL
+ TokenBuffer buf = new TokenBuffer(null, false);
+ buf.writeObject(null);
+ assertNull(MAPPER.readValue(buf.asParser(), URL.class));
+ buf.close();
+
+ // then, URLitself come as is:
+ buf = new TokenBuffer(null, false);
+ buf.writeObject(exp);
+ assertSame(exp, MAPPER.readValue(buf.asParser(), URL.class));
+ buf.close();
+
+ // and finally, invalid URL should be handled appropriately too
+ try {
+ URL result = MAPPER.readValue(quote("a b"), URL.class);
+ fail("Should not accept malformed URI, instead got: "+result);
+ } catch (InvalidFormatException e) {
+ verifyException(e, "not a valid textual representation");
+ }
+ }
+
+ public void testUUID() throws Exception
+ {
+ final ObjectMapper mapper = objectMapper();
+
+ final String NULL_UUID = "00000000-0000-0000-0000-000000000000";
+ // first, couple of generated UUIDs:
+ for (String value : new String[] {
+ "76e6d183-5f68-4afa-b94a-922c1fdb83f8",
+ "540a88d1-e2d8-4fb1-9396-9212280d0a7f",
+ "2c9e441d-1cd0-472d-9bab-69838f877574",
+ "591b2869-146e-41d7-8048-e8131f1fdec5",
+ "82994ac2-7b23-49f2-8cc5-e24cf6ed77be",
+ "00000007-0000-0000-0000-000000000000"
+ }) {
+
+ mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+
+ UUID uuid = UUID.fromString(value);
+ assertEquals(uuid,
+ mapper.readValue(quote(value), UUID.class));
+
+ try {
+ mapper.readValue("[" + quote(value) + "]", UUID.class);
+ fail("Exception was not thrown when UNWRAP_SINGLE_VALUE_ARRAYS is disabled and attempted to read a single value array as a single element");
+ } catch (JsonMappingException exp) {
+ //Exception thrown successfully
+ }
+
+ mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+
+ assertEquals(uuid,
+ mapper.readValue("[" + quote(value) + "]", UUID.class));
+
+ try {
+ mapper.readValue("[" + quote(value) + "," + quote(value) + "]", UUID.class);
+ fail("Exception was not thrown when UNWRAP_SINGLE_VALUE_ARRAYS is enabled and attempted to read a multi value array as a single element");
+ } catch (JsonMappingException exp) {
+ //Exception thrown successfully
+ }
+ }
+ // then use templating; note that these are not exactly valid UUIDs
+ // wrt spec (type bits etc), but JDK UUID should deal ok
+ final String TEMPL = NULL_UUID;
+ final String chars = "123456789abcdefABCDEF";
+
+ for (int i = 0; i < chars.length(); ++i) {
+ String value = TEMPL.replace('0', chars.charAt(i));
+ assertEquals(UUID.fromString(value).toString(),
+ mapper.readValue(quote(value), UUID.class).toString());
+ }
+
+ // also: see if base64 encoding works as expected
+ String base64 = Base64Variants.getDefaultVariant().encode(new byte[16]);
+ assertEquals(UUID.fromString(NULL_UUID),
+ mapper.readValue(quote(base64), UUID.class));
+ }
+
+ public void testUUIDInvalid() throws Exception
+ {
+ // and finally, exception handling too [databind#1000], for invalid cases
+ try {
+ MAPPER.readValue(quote("abcde"), UUID.class);
+ fail("Should fail on invalid UUID string");
+ } catch (InvalidFormatException e) {
+ verifyException(e, "UUID has to be represented by standard");
+ }
+ try {
+ MAPPER.readValue(quote("76e6d183-5f68-4afa-b94a-922c1fdb83fx"), UUID.class);
+ fail("Should fail on invalid UUID string");
+ } catch (InvalidFormatException e) {
+ verifyException(e, "non-hex character 'x'");
+ }
+ // should also test from-bytes version, but that's trickier... leave for now.
+ }
+
+ public void testUUIDAux() throws Exception
+ {
+ // [JACKSON-393] fix:
+ final UUID value = UUID.fromString("76e6d183-5f68-4afa-b94a-922c1fdb83f8");
+
+ // first, null should come as null
+ TokenBuffer buf = new TokenBuffer(null, false);
+ buf.writeObject(null);
+ assertNull(MAPPER.readValue(buf.asParser(), UUID.class));
+ buf.close();
+
+ // then, UUID itself come as is:
+ buf = new TokenBuffer(null, false);
+ buf.writeObject(value);
+ assertSame(value, MAPPER.readValue(buf.asParser(), UUID.class));
+
+ // and finally from byte[]
+ // oh crap; JDK UUID just... sucks. Not even byte[] accessors or constructors? Huh?
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ DataOutputStream out = new DataOutputStream(bytes);
+ out.writeLong(value.getMostSignificantBits());
+ out.writeLong(value.getLeastSignificantBits());
+ byte[] data = bytes.toByteArray();
+ assertEquals(16, data.length);
+
+ buf.writeObject(data);
+
+ UUID value2 = MAPPER.readValue(buf.asParser(), UUID.class);
+
+ assertEquals(value, value2);
+ buf.close();
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/NullHandlingTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/NullHandlingTest.java
new file mode 100644
index 0000000..a860de9
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/NullHandlingTest.java
@@ -0,0 +1,224 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonAnySetter;
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.JDKScalarsTest.PrimitivesBean;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+
+public class NullHandlingTest extends BaseMapTest
+{
+ static class FunnyNullDeserializer extends JsonDeserializer<String>
+ {
+ @Override
+ public String deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
+ return "text";
+ }
+
+ @Override
+ public String getNullValue(DeserializationContext ctxt) { return "funny"; }
+ }
+
+ static class AnySetter{
+
+ private Map<String,String> any = new HashMap<String,String>();
+
+ @JsonAnySetter
+ public void setAny(String name, String value){
+ this.any.put(name,value);
+ }
+
+ public Map<String,String> getAny(){
+ return this.any;
+ }
+ }
+
+ private final ObjectMapper MAPPER = objectMapper();
+
+ /*
+ /**********************************************************
+ /* Test methods
+ /**********************************************************
+ */
+
+ public void testNull() throws Exception
+ {
+ // null doesn't really have a type, fake by assuming Object
+ Object result = MAPPER.readValue(" null", Object.class);
+ assertNull(result);
+ }
+
+ public void testAnySetterNulls() throws Exception {
+ ObjectMapper mapper = new ObjectMapper();
+ SimpleModule module = new SimpleModule("test", Version.unknownVersion());
+ module.addDeserializer(String.class, new FunnyNullDeserializer());
+ mapper.registerModule(module);
+
+ String fieldName = "fieldName";
+ String nullValue = "{\""+fieldName+"\":null}";
+
+ // should get non-default null directly:
+ AnySetter result = mapper.readValue(nullValue, AnySetter.class);
+
+ assertEquals(1, result.getAny().size());
+ assertNotNull(result.getAny().get(fieldName));
+ assertEquals("funny", result.getAny().get(fieldName));
+
+ // as well as via ObjectReader
+ ObjectReader reader = mapper.readerFor(AnySetter.class);
+ result = reader.readValue(nullValue);
+
+ assertEquals(1, result.getAny().size());
+ assertNotNull(result.getAny().get(fieldName));
+ assertEquals("funny", result.getAny().get(fieldName));
+ }
+
+ public void testCustomRootNulls() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper();
+ SimpleModule module = new SimpleModule("test", Version.unknownVersion());
+ module.addDeserializer(String.class, new FunnyNullDeserializer());
+ mapper.registerModule(module);
+
+ // should get non-default null directly:
+ String str = mapper.readValue("null", String.class);
+ assertNotNull(str);
+ assertEquals("funny", str);
+
+ // as well as via ObjectReader
+ ObjectReader reader = mapper.readerFor(String.class);
+ str = reader.readValue("null");
+ assertNotNull(str);
+ assertEquals("funny", str);
+ }
+
+ public void testNullForPrimitives() throws IOException
+ {
+ // by default, ok to rely on defaults
+ PrimitivesBean bean = MAPPER.readValue(
+ "{\"intValue\":null, \"booleanValue\":null, \"doubleValue\":null}",
+ PrimitivesBean.class);
+ assertNotNull(bean);
+ assertEquals(0, bean.intValue);
+ assertEquals(false, bean.booleanValue);
+ assertEquals(0.0, bean.doubleValue);
+
+ bean = MAPPER.readValue("{\"byteValue\":null, \"longValue\":null, \"floatValue\":null}",
+ PrimitivesBean.class);
+ assertNotNull(bean);
+ assertEquals((byte) 0, bean.byteValue);
+ assertEquals(0L, bean.longValue);
+ assertEquals(0.0f, bean.floatValue);
+
+ // but not when enabled
+ final ObjectReader reader = MAPPER
+ .readerFor(PrimitivesBean.class)
+ .with(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES);
+ // boolean
+ try {
+ reader.readValue("{\"booleanValue\":null}");
+ fail("Expected failure for boolean + null");
+ } catch (JsonMappingException e) {
+ verifyException(e, "Can not map JSON null into type boolean");
+ }
+ // byte/char/short/int/long
+ try {
+ reader.readValue("{\"byteValue\":null}");
+ fail("Expected failure for byte + null");
+ } catch (JsonMappingException e) {
+ verifyException(e, "Can not map JSON null into type byte");
+ }
+ try {
+ reader.readValue("{\"charValue\":null}");
+ fail("Expected failure for char + null");
+ } catch (JsonMappingException e) {
+ verifyException(e, "Can not map JSON null into type char");
+ }
+ try {
+ reader.readValue("{\"shortValue\":null}");
+ fail("Expected failure for short + null");
+ } catch (JsonMappingException e) {
+ verifyException(e, "Can not map JSON null into type short");
+ }
+ try {
+ reader.readValue("{\"intValue\":null}");
+ fail("Expected failure for int + null");
+ } catch (JsonMappingException e) {
+ verifyException(e, "Can not map JSON null into type int");
+ }
+ try {
+ reader.readValue("{\"longValue\":null}");
+ fail("Expected failure for long + null");
+ } catch (JsonMappingException e) {
+ verifyException(e, "Can not map JSON null into type long");
+ }
+
+ // float/double
+ try {
+ reader.readValue("{\"floatValue\":null}");
+ fail("Expected failure for float + null");
+ } catch (JsonMappingException e) {
+ verifyException(e, "Can not map JSON null into type float");
+ }
+ try {
+ reader.readValue("{\"doubleValue\":null}");
+ fail("Expected failure for double + null");
+ } catch (JsonMappingException e) {
+ verifyException(e, "Can not map JSON null into type double");
+ }
+ }
+
+ // [databind#407]
+ public void testListOfNulls() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper();
+ SimpleModule module = new SimpleModule("test", Version.unknownVersion());
+ module.addDeserializer(String.class, new FunnyNullDeserializer());
+ mapper.registerModule(module);
+
+ List<String> list = Arrays.asList("funny");
+ JavaType type = mapper.getTypeFactory().constructCollectionType(List.class, String.class);
+
+ // should get non-default null directly:
+ List<?> deser = mapper.readValue("[null]", type);
+ assertNotNull(deser);
+ assertEquals(1, deser.size());
+ assertEquals(list.get(0), deser.get(0));
+
+ // as well as via ObjectReader
+ ObjectReader reader = mapper.readerFor(type);
+ deser = reader.readValue("[null]");
+ assertNotNull(deser);
+ assertEquals(1, deser.size());
+ assertEquals(list.get(0), deser.get(0));
+ }
+
+ // Test for [#407]
+ public void testMapOfNulls() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper();
+ SimpleModule module = new SimpleModule("test", Version.unknownVersion());
+ module.addDeserializer(String.class, new FunnyNullDeserializer());
+ mapper.registerModule(module);
+
+ JavaType type = mapper.getTypeFactory().constructMapType(Map.class, String.class, String.class);
+ // should get non-default null directly:
+ Map<?,?> deser = mapper.readValue("{\"key\":null}", type);
+ assertNotNull(deser);
+ assertEquals(1, deser.size());
+ assertEquals("funny", deser.get("key"));
+
+ // as well as via ObjectReader
+ ObjectReader reader = mapper.readerFor(type);
+ deser = reader.readValue("{\"key\":null}");
+ assertNotNull(deser);
+ assertEquals(1, deser.size());
+ assertEquals("funny", deser.get("key"));
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/NumberDeserTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/NumberDeserTest.java
deleted file mode 100644
index b5bcf9c..0000000
--- a/src/test/java/com/fasterxml/jackson/databind/deser/NumberDeserTest.java
+++ /dev/null
@@ -1,132 +0,0 @@
-package com.fasterxml.jackson.databind.deser;
-
-import java.io.IOException;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-
-import com.fasterxml.jackson.core.*;
-import com.fasterxml.jackson.databind.*;
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-
-public class NumberDeserTest extends BaseMapTest
-{
- /*
- /**********************************************************************
- /* Helper classes, beans
- /**********************************************************************
- */
-
- static class MyBeanHolder {
- public Long id;
- public MyBeanDefaultValue defaultValue;
- }
-
- static class MyBeanDefaultValue
- {
- public MyBeanValue value;
- }
-
- @JsonDeserialize(using=MyBeanDeserializer.class)
- static class MyBeanValue {
- public BigDecimal decimal;
- public MyBeanValue() { this(null); }
- public MyBeanValue(BigDecimal d) { this.decimal = d; }
- }
-
- /*
- /**********************************************************************
- /* Helper classes, serializers/deserializers/resolvers
- /**********************************************************************
- */
-
- static class MyBeanDeserializer extends JsonDeserializer<MyBeanValue>
- {
- @Override
- public MyBeanValue deserialize(JsonParser jp, DeserializationContext ctxt)
- throws IOException
- {
- return new MyBeanValue(jp.getDecimalValue());
- }
- }
-
- /*
- /**********************************************************************
- /* Unit tests
- /**********************************************************************
- */
-
- final ObjectMapper MAPPER = new ObjectMapper();
-
- public void testNaN() throws Exception
- {
- Float result = MAPPER.readValue(" \"NaN\"", Float.class);
- assertEquals(Float.valueOf(Float.NaN), result);
-
- Double d = MAPPER.readValue(" \"NaN\"", Double.class);
- assertEquals(Double.valueOf(Double.NaN), d);
-
- Number num = MAPPER.readValue(" \"NaN\"", Number.class);
- assertEquals(Double.valueOf(Double.NaN), num);
- }
-
- public void testDoubleInf() throws Exception
- {
- Double result = MAPPER.readValue(" \""+Double.POSITIVE_INFINITY+"\"", Double.class);
- assertEquals(Double.valueOf(Double.POSITIVE_INFINITY), result);
-
- result = MAPPER.readValue(" \""+Double.NEGATIVE_INFINITY+"\"", Double.class);
- assertEquals(Double.valueOf(Double.NEGATIVE_INFINITY), result);
- }
-
- // [JACKSON-349]
- public void testEmptyAsNumber() throws Exception
- {
- assertNull(MAPPER.readValue(quote(""), Integer.class));
- assertNull(MAPPER.readValue(quote(""), Long.class));
- assertNull(MAPPER.readValue(quote(""), Float.class));
- assertNull(MAPPER.readValue(quote(""), Double.class));
- assertNull(MAPPER.readValue(quote(""), BigInteger.class));
- assertNull(MAPPER.readValue(quote(""), BigDecimal.class));
- }
-
- // // Tests for [JACKSON-668]
-
- public void testDeserializeDecimalHappyPath() throws Exception {
- String json = "{\"defaultValue\": { \"value\": 123 } }";
- MyBeanHolder result = MAPPER.readValue(json, MyBeanHolder.class);
- assertEquals(BigDecimal.valueOf(123), result.defaultValue.value.decimal);
- }
-
- public void testDeserializeDecimalProperException() throws Exception {
- String json = "{\"defaultValue\": { \"value\": \"123\" } }";
- try {
- MAPPER.readValue(json, MyBeanHolder.class);
- fail("should have raised exception");
- } catch (JsonProcessingException e) {
- verifyException(e, "not numeric");
- }
- }
-
- public void testDeserializeDecimalProperExceptionWhenIdSet() throws Exception {
- String json = "{\"id\": 5, \"defaultValue\": { \"value\": \"123\" } }";
- try {
- MyBeanHolder result = MAPPER.readValue(json, MyBeanHolder.class);
- fail("should have raised exception instead value was set to " + result.defaultValue.value.decimal.toString());
- } catch (JsonProcessingException e) {
- verifyException(e, "not numeric");
- }
- }
-
- // And then [databind#852]
- public void testScientificNotationForString() throws Exception
- {
- Object ob = MAPPER.readValue("\"3E-8\"", Number.class);
- assertEquals(Double.class, ob.getClass());
- ob = MAPPER.readValue("\"3e-8\"", Number.class);
- assertEquals(Double.class, ob.getClass());
- ob = MAPPER.readValue("\"300000000\"", Number.class);
- assertEquals(Integer.class, ob.getClass());
- ob = MAPPER.readValue("\"123456789012\"", Number.class);
- assertEquals(Long.class, ob.getClass());
- }
-}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestAnnotationIgnore.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestAnnotationIgnore.java
index b254a4f..74c656c 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestAnnotationIgnore.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestAnnotationIgnore.java
@@ -47,8 +47,7 @@
public void testSimpleIgnore() throws Exception
{
- SizeClassIgnore result = MAPPER.readValue
- ("{ \"x\":1, \"y\" : 2 }",
+ SizeClassIgnore result = MAPPER.readValue("{ \"x\":1, \"y\" : 2 }",
SizeClassIgnore.class);
// x should be set, y not
assertEquals(1, result._x);
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestAnyProperties.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestAnyProperties.java
index 5b8c104..fb7ef8e 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestAnyProperties.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestAnyProperties.java
@@ -135,6 +135,33 @@
}
}
+ static class JsonAnySetterOnMap {
+ public int id;
+
+ @JsonAnySetter
+ protected HashMap<String, String> other = new HashMap<String, String>();
+
+ @JsonAnyGetter
+ public Map<String, String> any() {
+ return other;
+ }
+
+ }
+
+ static class JsonAnySetterOnNullMap {
+ public int id;
+
+ @JsonAnySetter
+ protected HashMap<String, String> other;
+
+ @JsonAnyGetter
+ public Map<String, String> any() {
+ return other;
+ }
+
+ }
+
+
/*
/**********************************************************
/* Test methods
@@ -224,6 +251,21 @@
assertEquals("xyz", ((Impl) ob).value);
}
+ public void testJsonAnySetterOnMap() throws Exception {
+ JsonAnySetterOnMap result = MAPPER.readValue("{\"id\":2,\"name\":\"Joe\", \"city\":\"New Jersey\"}",
+ JsonAnySetterOnMap.class);
+ assertEquals(2, result.id);
+ assertEquals("Joe", result.other.get("name"));
+ assertEquals("New Jersey", result.other.get("city"));
+ }
+
+ public void testJsonAnySetterOnNullMap() throws Exception {
+ JsonAnySetterOnNullMap result = MAPPER.readValue("{\"id\":2,\"name\":\"Joe\", \"city\":\"New Jersey\"}",
+ JsonAnySetterOnNullMap.class);
+ assertEquals(2, result.id);
+ assertNull(result.other);
+ }
+
/*
/**********************************************************
/* Private helper methods
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestCustomValueInstDefaults.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestCustomValueInstDefaults.java
new file mode 100644
index 0000000..091ac00
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestCustomValueInstDefaults.java
@@ -0,0 +1,418 @@
+package com.fasterxml.jackson.databind.deser;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.impl.PropertyValueBuffer;
+import com.fasterxml.jackson.databind.deser.std.StdValueInstantiator;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+
+/**
+ * Exercises a custom value instantiator with an overridden
+ * {@link ValueInstantiator#createFromObjectWith(DeserializationContext, SettableBeanProperty[], PropertyValueBuffer)}
+ * as well as the {@link PropertyValueBuffer#hasParameter(SettableBeanProperty)}
+ * and {@link PropertyValueBuffer#getParameter(SettableBeanProperty)} methods.
+ */
+@SuppressWarnings("serial")
+public class TestCustomValueInstDefaults extends BaseTest
+{
+ static class Bucket
+ {
+ static final int DEFAULT_A = 111;
+ static final int DEFAULT_B = 222;
+ static final String DEFAULT_C = "defaultC";
+ static final String DEFAULT_D = "defaultD";
+
+ final int a;
+ final int b;
+ final String c;
+ final String d;
+
+ @JsonCreator
+ public Bucket(
+ @JsonProperty("a") int a,
+ @JsonProperty("b") int b,
+ @JsonProperty("c") String c,
+ @JsonProperty("d") String d)
+ {
+ this.a = a;
+ this.b = b;
+ this.c = c;
+ this.d = d;
+ }
+ }
+
+ static class BigBucket
+ {
+ static final int DEFAULT_I = 555;
+ static final String DEFAULT_S = "defaultS";
+
+ final int
+ i01, i02, i03, i04, i05, i06, i07, i08,
+ i09, i10, i11, i12, i13, i14, i15, i16;
+
+ final String
+ s01, s02, s03, s04, s05, s06, s07, s08,
+ s09, s10, s11, s12, s13, s14, s15, s16;
+
+ @JsonCreator
+ public BigBucket(
+ @JsonProperty("i01") int i01,
+ @JsonProperty("i02") int i02,
+ @JsonProperty("i03") int i03,
+ @JsonProperty("i04") int i04,
+ @JsonProperty("i05") int i05,
+ @JsonProperty("i06") int i06,
+ @JsonProperty("i07") int i07,
+ @JsonProperty("i08") int i08,
+ @JsonProperty("i09") int i09,
+ @JsonProperty("i10") int i10,
+ @JsonProperty("i11") int i11,
+ @JsonProperty("i12") int i12,
+ @JsonProperty("i13") int i13,
+ @JsonProperty("i14") int i14,
+ @JsonProperty("i15") int i15,
+ @JsonProperty("i16") int i16,
+ @JsonProperty("s01") String s01,
+ @JsonProperty("s02") String s02,
+ @JsonProperty("s03") String s03,
+ @JsonProperty("s04") String s04,
+ @JsonProperty("s05") String s05,
+ @JsonProperty("s06") String s06,
+ @JsonProperty("s07") String s07,
+ @JsonProperty("s08") String s08,
+ @JsonProperty("s09") String s09,
+ @JsonProperty("s10") String s10,
+ @JsonProperty("s11") String s11,
+ @JsonProperty("s12") String s12,
+ @JsonProperty("s13") String s13,
+ @JsonProperty("s14") String s14,
+ @JsonProperty("s15") String s15,
+ @JsonProperty("s16") String s16,
+ // It is not clear why PropertyValueBuffer uses the BitSet for
+ // exactly 32 params. The primitive int has 32 bits, so
+ // couldn't the int be used there? We add this 33rd dummy
+ // parameter just in case PropertyValueBuffer is refactored to
+ // to so.
+ @JsonProperty("dummy") boolean dummy) {
+ this.i01 = i01; this.i02 = i02; this.i03 = i03; this.i04 = i04;
+ this.i05 = i05; this.i06 = i06; this.i07 = i07; this.i08 = i08;
+ this.i09 = i09; this.i10 = i10; this.i11 = i11; this.i12 = i12;
+ this.i13 = i13; this.i14 = i14; this.i15 = i15; this.i16 = i16;
+ this.s01 = s01; this.s02 = s02; this.s03 = s03; this.s04 = s04;
+ this.s05 = s05; this.s06 = s06; this.s07 = s07; this.s08 = s08;
+ this.s09 = s09; this.s10 = s10; this.s11 = s11; this.s12 = s12;
+ this.s13 = s13; this.s14 = s14; this.s15 = s15; this.s16 = s16;
+ }
+ }
+
+ static class BucketInstantiator extends StdValueInstantiator
+ {
+ BucketInstantiator(StdValueInstantiator src)
+ {
+ super(src);
+ }
+
+ @Override
+ public Object createFromObjectWith(
+ DeserializationContext ctxt,
+ SettableBeanProperty[] props,
+ PropertyValueBuffer buffer) throws JsonMappingException
+ {
+ int a = Bucket.DEFAULT_A;
+ int b = Bucket.DEFAULT_B;
+ String c = Bucket.DEFAULT_C;
+ String d = Bucket.DEFAULT_D;
+ for (SettableBeanProperty prop : props) {
+ if (buffer.hasParameter(prop)) {
+ if (prop.getName().equals("a")) {
+ a = (Integer) buffer.getParameter(prop);
+ } else if (prop.getName().equals("b")) {
+ b = (Integer) buffer.getParameter(prop);
+ } else if (prop.getName().equals("c")) {
+ c = (String) buffer.getParameter(prop);
+ } else if (prop.getName().equals("d")) {
+ d = (String) buffer.getParameter(prop);
+ }
+ }
+ }
+ return new Bucket(a, b, c, d);
+ }
+ }
+
+ static class BigBucketInstantiator extends StdValueInstantiator
+ {
+ BigBucketInstantiator(StdValueInstantiator src)
+ {
+ super(src);
+ }
+
+ @Override
+ public Object createFromObjectWith(
+ DeserializationContext ctxt,
+ SettableBeanProperty[] props,
+ PropertyValueBuffer buffer) throws JsonMappingException
+ {
+ int i01 = BigBucket.DEFAULT_I;
+ int i02 = BigBucket.DEFAULT_I;
+ int i03 = BigBucket.DEFAULT_I;
+ int i04 = BigBucket.DEFAULT_I;
+ int i05 = BigBucket.DEFAULT_I;
+ int i06 = BigBucket.DEFAULT_I;
+ int i07 = BigBucket.DEFAULT_I;
+ int i08 = BigBucket.DEFAULT_I;
+ int i09 = BigBucket.DEFAULT_I;
+ int i10 = BigBucket.DEFAULT_I;
+ int i11 = BigBucket.DEFAULT_I;
+ int i12 = BigBucket.DEFAULT_I;
+ int i13 = BigBucket.DEFAULT_I;
+ int i14 = BigBucket.DEFAULT_I;
+ int i15 = BigBucket.DEFAULT_I;
+ int i16 = BigBucket.DEFAULT_I;
+ String s01 = BigBucket.DEFAULT_S;
+ String s02 = BigBucket.DEFAULT_S;
+ String s03 = BigBucket.DEFAULT_S;
+ String s04 = BigBucket.DEFAULT_S;
+ String s05 = BigBucket.DEFAULT_S;
+ String s06 = BigBucket.DEFAULT_S;
+ String s07 = BigBucket.DEFAULT_S;
+ String s08 = BigBucket.DEFAULT_S;
+ String s09 = BigBucket.DEFAULT_S;
+ String s10 = BigBucket.DEFAULT_S;
+ String s11 = BigBucket.DEFAULT_S;
+ String s12 = BigBucket.DEFAULT_S;
+ String s13 = BigBucket.DEFAULT_S;
+ String s14 = BigBucket.DEFAULT_S;
+ String s15 = BigBucket.DEFAULT_S;
+ String s16 = BigBucket.DEFAULT_S;
+ for (SettableBeanProperty prop : props) {
+ if (buffer.hasParameter(prop)) {
+ String name = prop.getName();
+ Object value = buffer.getParameter(prop);
+ if (name.equals("i01")) i01 = (Integer) value;
+ else if (name.equals("i02")) i02 = (Integer) value;
+ else if (name.equals("i03")) i03 = (Integer) value;
+ else if (name.equals("i04")) i04 = (Integer) value;
+ else if (name.equals("i05")) i05 = (Integer) value;
+ else if (name.equals("i06")) i06 = (Integer) value;
+ else if (name.equals("i07")) i07 = (Integer) value;
+ else if (name.equals("i08")) i08 = (Integer) value;
+ else if (name.equals("i09")) i09 = (Integer) value;
+ else if (name.equals("i10")) i10 = (Integer) value;
+ else if (name.equals("i11")) i11 = (Integer) value;
+ else if (name.equals("i12")) i12 = (Integer) value;
+ else if (name.equals("i13")) i13 = (Integer) value;
+ else if (name.equals("i14")) i14 = (Integer) value;
+ else if (name.equals("i15")) i15 = (Integer) value;
+ else if (name.equals("i16")) i16 = (Integer) value;
+ else if (name.equals("s01")) s01 = (String) value;
+ else if (name.equals("s02")) s02 = (String) value;
+ else if (name.equals("s03")) s03 = (String) value;
+ else if (name.equals("s04")) s04 = (String) value;
+ else if (name.equals("s05")) s05 = (String) value;
+ else if (name.equals("s06")) s06 = (String) value;
+ else if (name.equals("s07")) s07 = (String) value;
+ else if (name.equals("s08")) s08 = (String) value;
+ else if (name.equals("s09")) s09 = (String) value;
+ else if (name.equals("s10")) s10 = (String) value;
+ else if (name.equals("s11")) s11 = (String) value;
+ else if (name.equals("s12")) s12 = (String) value;
+ else if (name.equals("s13")) s13 = (String) value;
+ else if (name.equals("s14")) s14 = (String) value;
+ else if (name.equals("s15")) s15 = (String) value;
+ else if (name.equals("s16")) s16 = (String) value;
+ }
+ }
+ return new BigBucket(
+ i01, i02, i03, i04, i05, i06, i07, i08,
+ i09, i10, i11, i12, i13, i14, i15, i16,
+ s01, s02, s03, s04, s05, s06, s07, s08,
+ s09, s10, s11, s12, s13, s14, s15, s16,
+ false);
+ }
+ }
+
+ static class BucketInstantiators implements ValueInstantiators
+ {
+ @Override
+ public ValueInstantiator findValueInstantiator(
+ DeserializationConfig config,
+ BeanDescription beanDesc,
+ ValueInstantiator defaultInstantiator)
+ {
+ if (defaultInstantiator instanceof StdValueInstantiator) {
+ if (beanDesc.getBeanClass() == Bucket.class) {
+ return new BucketInstantiator(
+ (StdValueInstantiator) defaultInstantiator);
+ }
+ if (beanDesc.getBeanClass() == BigBucket.class) {
+ return new BigBucketInstantiator(
+ (StdValueInstantiator) defaultInstantiator);
+ }
+ }
+ return defaultInstantiator;
+ }
+ }
+
+ static class BucketModule extends SimpleModule
+ {
+ @Override
+ public void setupModule(SetupContext context)
+ {
+ context.addValueInstantiators(new BucketInstantiators());
+ }
+ }
+
+ // When all values are in the source, no defaults should be used.
+ public void testAllPresent() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.registerModule(new BucketModule());
+
+ Bucket allPresent = mapper.readValue(
+ "{\"a\":8,\"b\":9,\"c\":\"y\",\"d\":\"z\"}",
+ Bucket.class);
+
+ assertEquals(8, allPresent.a);
+ assertEquals(9, allPresent.b);
+ assertEquals("y", allPresent.c);
+ assertEquals("z", allPresent.d);
+ }
+
+ // When no values are in the source, all defaults should be used.
+ public void testAllAbsent() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.registerModule(new BucketModule());
+
+ Bucket allAbsent = mapper.readValue(
+ "{}",
+ Bucket.class);
+
+ assertEquals(Bucket.DEFAULT_A, allAbsent.a);
+ assertEquals(Bucket.DEFAULT_B, allAbsent.b);
+ assertEquals(Bucket.DEFAULT_C, allAbsent.c);
+ assertEquals(Bucket.DEFAULT_D, allAbsent.d);
+ }
+
+ // When some values are in the source and some are not, defaults should only
+ // be used for the missing values.
+ public void testMixedPresentAndAbsent() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.registerModule(new BucketModule());
+
+ Bucket aAbsent = mapper.readValue(
+ "{\"b\":9,\"c\":\"y\",\"d\":\"z\"}",
+ Bucket.class);
+
+ assertEquals(Bucket.DEFAULT_A, aAbsent.a);
+ assertEquals(9, aAbsent.b);
+ assertEquals("y", aAbsent.c);
+ assertEquals("z", aAbsent.d);
+
+ Bucket bAbsent = mapper.readValue(
+ "{\"a\":8,\"c\":\"y\",\"d\":\"z\"}",
+ Bucket.class);
+
+ assertEquals(8, bAbsent.a);
+ assertEquals(Bucket.DEFAULT_B, bAbsent.b);
+ assertEquals("y", bAbsent.c);
+ assertEquals("z", bAbsent.d);
+
+ Bucket cAbsent = mapper.readValue(
+ "{\"a\":8,\"b\":9,\"d\":\"z\"}",
+ Bucket.class);
+
+ assertEquals(8, cAbsent.a);
+ assertEquals(9, cAbsent.b);
+ assertEquals(Bucket.DEFAULT_C, cAbsent.c);
+ assertEquals("z", cAbsent.d);
+
+ Bucket dAbsent = mapper.readValue(
+ "{\"a\":8,\"b\":9,\"c\":\"y\"}",
+ Bucket.class);
+
+ assertEquals(8, dAbsent.a);
+ assertEquals(9, dAbsent.b);
+ assertEquals("y", dAbsent.c);
+ assertEquals(Bucket.DEFAULT_D, dAbsent.d);
+ }
+
+ // Ensure that 0 is not mistaken for a missing int value.
+ public void testPresentZeroPrimitive() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.registerModule(new BucketModule());
+
+ Bucket aZeroRestAbsent = mapper.readValue(
+ "{\"a\":0}",
+ Bucket.class);
+
+ assertEquals(0, aZeroRestAbsent.a);
+ assertEquals(Bucket.DEFAULT_B, aZeroRestAbsent.b);
+ assertEquals(Bucket.DEFAULT_C, aZeroRestAbsent.c);
+ assertEquals(Bucket.DEFAULT_D, aZeroRestAbsent.d);
+ }
+
+ // Ensure that null is not mistaken for a missing String value.
+ public void testPresentNullReference() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.registerModule(new BucketModule());
+
+ Bucket cNullRestAbsent = mapper.readValue(
+ "{\"c\":null}",
+ Bucket.class);
+
+ assertEquals(Bucket.DEFAULT_A, cNullRestAbsent.a);
+ assertEquals(Bucket.DEFAULT_B, cNullRestAbsent.b);
+ assertEquals(null, cNullRestAbsent.c);
+ assertEquals(Bucket.DEFAULT_D, cNullRestAbsent.d);
+ }
+
+ // When we have more than 32 creator parameters, the buffer will use a
+ // BitSet instead of a primitive int to keep track of which parameters it
+ // has seen. Ensure that nothing breaks in that case.
+ public void testMoreThan32CreatorParams() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.registerModule(new BucketModule());
+
+ BigBucket big = mapper.readValue(
+ "{\"i03\":0,\"i11\":1,\"s05\":null,\"s08\":\"x\"}",
+ BigBucket.class);
+
+ assertEquals(BigBucket.DEFAULT_I, big.i01);
+ assertEquals(BigBucket.DEFAULT_I, big.i02);
+ assertEquals(0, big.i03);
+ assertEquals(BigBucket.DEFAULT_I, big.i04);
+ assertEquals(BigBucket.DEFAULT_I, big.i05);
+ assertEquals(BigBucket.DEFAULT_I, big.i06);
+ assertEquals(BigBucket.DEFAULT_I, big.i07);
+ assertEquals(BigBucket.DEFAULT_I, big.i08);
+ assertEquals(BigBucket.DEFAULT_I, big.i09);
+ assertEquals(BigBucket.DEFAULT_I, big.i10);
+ assertEquals(1, big.i11);
+ assertEquals(BigBucket.DEFAULT_I, big.i12);
+ assertEquals(BigBucket.DEFAULT_I, big.i13);
+ assertEquals(BigBucket.DEFAULT_I, big.i14);
+ assertEquals(BigBucket.DEFAULT_I, big.i15);
+ assertEquals(BigBucket.DEFAULT_I, big.i16);
+ assertEquals(BigBucket.DEFAULT_S, big.s01);
+ assertEquals(BigBucket.DEFAULT_S, big.s02);
+ assertEquals(BigBucket.DEFAULT_S, big.s03);
+ assertEquals(BigBucket.DEFAULT_S, big.s04);
+ assertEquals(null, big.s05);
+ assertEquals(BigBucket.DEFAULT_S, big.s06);
+ assertEquals(BigBucket.DEFAULT_S, big.s07);
+ assertEquals("x", big.s08);
+ assertEquals(BigBucket.DEFAULT_S, big.s09);
+ assertEquals(BigBucket.DEFAULT_S, big.s10);
+ assertEquals(BigBucket.DEFAULT_S, big.s11);
+ assertEquals(BigBucket.DEFAULT_S, big.s12);
+ assertEquals(BigBucket.DEFAULT_S, big.s13);
+ assertEquals(BigBucket.DEFAULT_S, big.s14);
+ assertEquals(BigBucket.DEFAULT_S, big.s15);
+ assertEquals(BigBucket.DEFAULT_S, big.s16);
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestDateDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestDateDeserialization.java
index ba1ef58..634cf00 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestDateDeserialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestDateDeserialization.java
@@ -516,7 +516,7 @@
MAPPER.readValue(quote("foobar"), Date.class);
fail("Should have failed with an exception");
} catch (InvalidFormatException e) {
- verifyException(e, "Can not construct instance");
+ verifyException(e, "Can not deserialize value of type java.util.Date from String");
assertEquals("foobar", e.getValue());
assertEquals(Date.class, e.getTargetType());
} catch (Exception e) {
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestExceptionDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestExceptionDeserialization.java
index a784100..e1f1877 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestExceptionDeserialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestExceptionDeserialization.java
@@ -44,7 +44,7 @@
{
@JsonCreator MyNoArgException() { }
}
-
+
/*
/**********************************************************
/* Tests
@@ -89,7 +89,7 @@
assertNotNull(exc);
}
- // [JACKSON-794]: try simulating JDK 7 behavior
+ // try simulating JDK 7 behavior
public void testJDK7SuppressionProperty() throws IOException
{
Exception exc = MAPPER.readValue("{\"suppressed\":[]}", IOException.class);
@@ -113,10 +113,34 @@
assertEquals(exp.getStackTrace().length, cloned.getStackTrace().length);
for (int i = 0; i < exp.getStackTrace().length; i ++) {
- assertEquals(exp.getStackTrace()[i], cloned.getStackTrace()[i]);
+ _assertEquality(i, exp.getStackTrace()[i], cloned.getStackTrace()[i]);
}
}
-
+
+ protected void _assertEquality(int ix, StackTraceElement exp, StackTraceElement act)
+ {
+ _assertEquality(ix, "className", exp.getClassName(), act.getClassName());
+ _assertEquality(ix, "methodName", exp.getMethodName(), act.getMethodName());
+ _assertEquality(ix, "fileName", exp.getFileName(), act.getFileName());
+ _assertEquality(ix, "lineNumber", exp.getLineNumber(), act.getLineNumber());
+ }
+
+ protected void _assertEquality(int ix, String prop,
+ Object exp, Object act)
+ {
+ if (exp == null) {
+ if (act == null) {
+ return;
+ }
+ } else {
+ if (exp.equals(act)) {
+ return;
+ }
+ }
+ fail(String.format("StackTraceElement #%d, property '%s' differs: expected %s, actual %s",
+ ix, prop, exp, act));
+ }
+
public void testSingleValueArrayDeserializationException() throws Exception {
final ObjectMapper mapper = new ObjectMapper();
mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
@@ -136,33 +160,4 @@
//Exception thrown correctly
}
}
-
- static class NoSerdeConstructor {
- private String strVal;
- public String getVal() { return strVal; }
- public NoSerdeConstructor( String strVal ) {
- this.strVal = strVal;
- }
- }
-
- // [databind#1368]
- public void testFailingNoSerdeConstructor() throws IOException {
- Exception e = null;
- // cant deserialize due to unexpected constructor
- try {
- MAPPER.readValue( "{ \"val\": \"foo\" }", NoSerdeConstructor.class );
- fail("Should not pass");
- } catch (JsonMappingException e0) {
- verifyException(e0, "No suitable constructor");
- e = e0;
- }
- // but should be able to serialize new exception we got
- String json = MAPPER.writeValueAsString(e);
- JsonNode root = MAPPER.readTree(json);
- String msg = root.path("message").asText();
- String MATCH = "No suitable constructor";
- if (!msg.contains(MATCH)) {
- fail("Exception should contain '"+MATCH+"', does not: '"+msg+"'");
- }
- }
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestIgnoredTypes.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestIgnoredTypes.java
index d610e03..01b5c51 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestIgnoredTypes.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestIgnoredTypes.java
@@ -8,18 +8,13 @@
import com.fasterxml.jackson.databind.module.SimpleModule;
/**
- * Test for [JACKSON-429]
+ * Test for type-based ignoral, both via annotations (<code>JsonIgnoreType</code>)
+ * and "config overrides" (2.8 and above).
*/
public class TestIgnoredTypes extends BaseMapTest
{
- /*
- /**********************************************************
- /* Annotated helper classes
- /**********************************************************
- */
-
@JsonIgnoreType
- class IgnoredType { // note: non-static, can't be deserializer
+ class IgnoredType { // note: non-static, can't be deserialized
public IgnoredType(IgnoredType src) { }
}
@@ -49,17 +44,31 @@
@JsonIgnoreType
static abstract class PersonMixin {
}
-
+
+ static class Wrapper {
+ public int value = 3;
+ public Wrapped wrapped = new Wrapped(7);
+ }
+
+ static class Wrapped {
+ public int x;
+
+ // make default ctor fail
+ public Wrapped() { throw new RuntimeException("Should not be called"); }
+ public Wrapped(int x0) { x = x0; }
+ }
+
/*
/**********************************************************
/* Unit tests
/**********************************************************
*/
-
+
public void testIgnoredType() throws Exception
{
+ final ObjectMapper mapper = objectMapper();
+
// First: should be ok in general, even though couldn't build deserializer (due to non-static inner class):
- ObjectMapper mapper = new ObjectMapper();
NonIgnoredType bean = mapper.readValue("{\"value\":13}", NonIgnoredType.class);
assertNotNull(bean);
assertEquals(13, bean.value);
@@ -90,4 +99,19 @@
String json = mapper.writeValueAsString(persons);
assertEquals("[{\"name\":\"Bob\"}]", json);
}
+
+ public void testIgnoreUsingConfigOverride() throws Exception
+ {
+ final ObjectMapper mapper = objectMapper();
+ mapper.configOverride(Wrapped.class).setIsIgnoredType(true);
+
+ // serialize , first
+ String json = mapper.writeValueAsString(new Wrapper());
+ assertEquals(aposToQuotes("{'value':3}"), json);
+
+ // then deserialize
+ Wrapper result = mapper.readValue(aposToQuotes("{'value':5,'wrapped':false}"),
+ Wrapper.class);
+ assertEquals(5, result.value);
+ }
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestJdkTypes.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestJdkTypes.java
deleted file mode 100644
index cc6ab51..0000000
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestJdkTypes.java
+++ /dev/null
@@ -1,746 +0,0 @@
-package com.fasterxml.jackson.databind.deser;
-
-import java.io.*;
-import java.net.*;
-import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
-import java.util.Currency;
-import java.util.List;
-import java.util.Locale;
-import java.util.regex.Pattern;
-
-import com.fasterxml.jackson.annotation.*;
-import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.*;
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
-import com.fasterxml.jackson.databind.module.SimpleModule;
-
-public class TestJdkTypes extends BaseMapTest
-{
- static class PrimitivesBean
- {
- public boolean booleanValue = true;
- public byte byteValue = 3;
- public char charValue = 'a';
- public short shortValue = 37;
- public int intValue = 1;
- public long longValue = 100L;
- public float floatValue = 0.25f;
- public double doubleValue = -1.0;
- }
-
- static class WrappersBean
- {
- public Boolean booleanValue;
- public Byte byteValue;
- public Character charValue;
- public Short shortValue;
- public Integer intValue;
- public Long longValue;
- public Float floatValue;
- public Double doubleValue;
- }
-
- static class ParamClassBean
- {
- public String name = "bar";
- public Class<String> clazz ;
-
- public ParamClassBean() { }
- public ParamClassBean(String name) {
- this.name = name;
- clazz = String.class;
- }
- }
-
- static class BooleanBean {
- public Boolean wrapper;
- public boolean primitive;
-
- protected Boolean ctor;
-
- @JsonCreator
- public BooleanBean(@JsonProperty("ctor") Boolean foo) {
- ctor = foo;
- }
- }
-
- // [databind#429]
- static class StackTraceBean {
- public final static int NUM = 13;
-
- @JsonProperty("Location")
- @JsonDeserialize(using=MyStackTraceElementDeserializer.class)
- protected StackTraceElement location;
- }
-
- @SuppressWarnings("serial")
- static class MyStackTraceElementDeserializer extends StdDeserializer<StackTraceElement>
- {
- public MyStackTraceElementDeserializer() { super(StackTraceElement.class); }
-
- @Override
- public StackTraceElement deserialize(JsonParser jp,
- DeserializationContext ctxt) throws IOException, JsonProcessingException {
- jp.skipChildren();
- return new StackTraceElement("a", "b", "b", StackTraceBean.NUM);
- }
-
- }
-
- /*
- /**********************************************************
- /* Test methods
- /**********************************************************
- */
-
- private final ObjectMapper MAPPER = objectMapper();
-
- /**
- * Related to issues [JACKSON-155], [#170].
- */
- public void testFile() throws Exception
- {
- // Not portable etc... has to do:
- File src = new File("/test").getAbsoluteFile();
- String abs = src.getAbsolutePath();
-
- // escape backslashes (for portability with windows)
- String json = MAPPER.writeValueAsString(abs);
- File result = MAPPER.readValue(json, File.class);
- assertEquals(abs, result.getAbsolutePath());
-
- // Then #170
- final ObjectMapper mapper2 = new ObjectMapper();
- mapper2.setVisibility(PropertyAccessor.CREATOR, Visibility.NONE);
-
- result = mapper2.readValue(json, File.class);
- assertEquals(abs, result.getAbsolutePath());
- }
-
- public void testRegexps() throws IOException
- {
- final String PATTERN_STR = "abc:\\s?(\\d+)";
- Pattern exp = Pattern.compile(PATTERN_STR);
- /* Ok: easiest way is to just serialize first; problem
- * is the backslash
- */
- String json = MAPPER.writeValueAsString(exp);
- Pattern result = MAPPER.readValue(json, Pattern.class);
- assertEquals(exp.pattern(), result.pattern());
- }
-
- public void testCurrency() throws IOException
- {
- Currency usd = Currency.getInstance("USD");
- assertEquals(usd, new ObjectMapper().readValue(quote("USD"), Currency.class));
- }
-
- public void testLocale() throws IOException
- {
- assertEquals(new Locale("en"), MAPPER.readValue(quote("en"), Locale.class));
- assertEquals(new Locale("es", "ES"), MAPPER.readValue(quote("es_ES"), Locale.class));
- assertEquals(new Locale("FI", "fi", "savo"),
- MAPPER.readValue(quote("fi_FI_savo"), Locale.class));
- assertEquals(new Locale("en", "US"),
- MAPPER.readValue(quote("en-US"), Locale.class));
-
- // [databind#1123]
- Locale loc = MAPPER.readValue(quote(""), Locale.class);
- assertSame(Locale.ROOT, loc);
- }
-
- public void testNullForPrimitives() throws IOException
- {
- // by default, ok to rely on defaults
- PrimitivesBean bean = MAPPER.readValue("{\"intValue\":null, \"booleanValue\":null, \"doubleValue\":null}",
- PrimitivesBean.class);
- assertNotNull(bean);
- assertEquals(0, bean.intValue);
- assertEquals(false, bean.booleanValue);
- assertEquals(0.0, bean.doubleValue);
-
- bean = MAPPER.readValue("{\"byteValue\":null, \"longValue\":null, \"floatValue\":null}",
- PrimitivesBean.class);
- assertNotNull(bean);
- assertEquals((byte) 0, bean.byteValue);
- assertEquals(0L, bean.longValue);
- assertEquals(0.0f, bean.floatValue);
-
- // but not when enabled
- final ObjectMapper mapper2 = new ObjectMapper();
- mapper2.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, true);
-
- // boolean
- try {
- mapper2.readValue("{\"booleanValue\":null}", PrimitivesBean.class);
- fail("Expected failure for boolean + null");
- } catch (JsonMappingException e) {
- verifyException(e, "Can not map JSON null into type boolean");
- }
- // byte/char/short/int/long
- try {
- mapper2.readValue("{\"byteValue\":null}", PrimitivesBean.class);
- fail("Expected failure for byte + null");
- } catch (JsonMappingException e) {
- verifyException(e, "Can not map JSON null into type byte");
- }
- try {
- mapper2.readValue("{\"charValue\":null}", PrimitivesBean.class);
- fail("Expected failure for char + null");
- } catch (JsonMappingException e) {
- verifyException(e, "Can not map JSON null into type char");
- }
- try {
- mapper2.readValue("{\"shortValue\":null}", PrimitivesBean.class);
- fail("Expected failure for short + null");
- } catch (JsonMappingException e) {
- verifyException(e, "Can not map JSON null into type short");
- }
- try {
- mapper2.readValue("{\"intValue\":null}", PrimitivesBean.class);
- fail("Expected failure for int + null");
- } catch (JsonMappingException e) {
- verifyException(e, "Can not map JSON null into type int");
- }
- try {
- mapper2.readValue("{\"longValue\":null}", PrimitivesBean.class);
- fail("Expected failure for long + null");
- } catch (JsonMappingException e) {
- verifyException(e, "Can not map JSON null into type long");
- }
-
- // float/double
- try {
- mapper2.readValue("{\"floatValue\":null}", PrimitivesBean.class);
- fail("Expected failure for float + null");
- } catch (JsonMappingException e) {
- verifyException(e, "Can not map JSON null into type float");
- }
- try {
- mapper2.readValue("{\"doubleValue\":null}", PrimitivesBean.class);
- fail("Expected failure for double + null");
- } catch (JsonMappingException e) {
- verifyException(e, "Can not map JSON null into type double");
- }
- }
-
- /**
- * Test for [JACKSON-483], allow handling of CharSequence
- */
- public void testCharSequence() throws IOException
- {
- CharSequence cs = MAPPER.readValue("\"abc\"", CharSequence.class);
- assertEquals(String.class, cs.getClass());
- assertEquals("abc", cs.toString());
- }
-
- // [JACKSON-484]
- public void testInetAddress() throws IOException
- {
- InetAddress address = MAPPER.readValue(quote("127.0.0.1"), InetAddress.class);
- assertEquals("127.0.0.1", address.getHostAddress());
-
- // should we try resolving host names? That requires connectivity...
- final String HOST = "google.com";
- address = MAPPER.readValue(quote(HOST), InetAddress.class);
- assertEquals(HOST, address.getHostName());
- }
-
- public void testInetSocketAddress() throws IOException
- {
- InetSocketAddress address = MAPPER.readValue(quote("127.0.0.1"), InetSocketAddress.class);
- assertEquals("127.0.0.1", address.getAddress().getHostAddress());
-
- InetSocketAddress ip6 = MAPPER.readValue(
- quote("2001:db8:85a3:8d3:1319:8a2e:370:7348"), InetSocketAddress.class);
- assertEquals("2001:db8:85a3:8d3:1319:8a2e:370:7348", ip6.getAddress().getHostAddress());
-
- InetSocketAddress ip6port = MAPPER.readValue(
- quote("[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443"), InetSocketAddress.class);
- assertEquals("2001:db8:85a3:8d3:1319:8a2e:370:7348", ip6port.getAddress().getHostAddress());
- assertEquals(443, ip6port.getPort());
-
- // should we try resolving host names? That requires connectivity...
- final String HOST = "www.google.com";
- address = MAPPER.readValue(quote(HOST), InetSocketAddress.class);
- assertEquals(HOST, address.getHostName());
-
- final String HOST_AND_PORT = HOST+":80";
- address = MAPPER.readValue(quote(HOST_AND_PORT), InetSocketAddress.class);
- assertEquals(HOST, address.getHostName());
- assertEquals(80, address.getPort());
- }
-
- // [JACKSON-597]
- public void testClass() throws IOException
- {
- ObjectMapper mapper = new ObjectMapper();
- assertSame(String.class, mapper.readValue(quote("java.lang.String"), Class.class));
-
- // then primitive types
- assertSame(Boolean.TYPE, mapper.readValue(quote("boolean"), Class.class));
- assertSame(Byte.TYPE, mapper.readValue(quote("byte"), Class.class));
- assertSame(Short.TYPE, mapper.readValue(quote("short"), Class.class));
- assertSame(Character.TYPE, mapper.readValue(quote("char"), Class.class));
- assertSame(Integer.TYPE, mapper.readValue(quote("int"), Class.class));
- assertSame(Long.TYPE, mapper.readValue(quote("long"), Class.class));
- assertSame(Float.TYPE, mapper.readValue(quote("float"), Class.class));
- assertSame(Double.TYPE, mapper.readValue(quote("double"), Class.class));
- assertSame(Void.TYPE, mapper.readValue(quote("void"), Class.class));
- }
-
- // [JACKSON-605]
- public void testClassWithParams() throws IOException
- {
- String json = MAPPER.writeValueAsString(new ParamClassBean("Foobar"));
-
- ParamClassBean result = MAPPER.readValue(json, ParamClassBean.class);
- assertEquals("Foobar", result.name);
- assertSame(String.class, result.clazz);
- }
-
- // by default, should return nulls, n'est pas?
- public void testEmptyStringForWrappers() throws IOException
- {
- WrappersBean bean;
-
- // by default, ok to rely on defaults
- bean = MAPPER.readValue("{\"booleanValue\":\"\"}", WrappersBean.class);
- assertNull(bean.booleanValue);
- bean = MAPPER.readValue("{\"byteValue\":\"\"}", WrappersBean.class);
- assertNull(bean.byteValue);
-
- // char/Character is different... not sure if this should work or not:
- bean = MAPPER.readValue("{\"charValue\":\"\"}", WrappersBean.class);
- assertNull(bean.charValue);
-
- bean = MAPPER.readValue("{\"shortValue\":\"\"}", WrappersBean.class);
- assertNull(bean.shortValue);
- bean = MAPPER.readValue("{\"intValue\":\"\"}", WrappersBean.class);
- assertNull(bean.intValue);
- bean = MAPPER.readValue("{\"longValue\":\"\"}", WrappersBean.class);
- assertNull(bean.longValue);
- bean = MAPPER.readValue("{\"floatValue\":\"\"}", WrappersBean.class);
- assertNull(bean.floatValue);
- bean = MAPPER.readValue("{\"doubleValue\":\"\"}", WrappersBean.class);
- assertNull(bean.doubleValue);
- }
-
- // for [JACKSON-616]
- // @since 1.9
- public void testEmptyStringForPrimitives() throws IOException
- {
- PrimitivesBean bean;
- bean = MAPPER.readValue("{\"booleanValue\":\"\"}", PrimitivesBean.class);
- assertFalse(bean.booleanValue);
- bean = MAPPER.readValue("{\"byteValue\":\"\"}", PrimitivesBean.class);
- assertEquals((byte) 0, bean.byteValue);
- bean = MAPPER.readValue("{\"charValue\":\"\"}", PrimitivesBean.class);
- assertEquals((char) 0, bean.charValue);
- bean = MAPPER.readValue("{\"shortValue\":\"\"}", PrimitivesBean.class);
- assertEquals((short) 0, bean.shortValue);
- bean = MAPPER.readValue("{\"intValue\":\"\"}", PrimitivesBean.class);
- assertEquals(0, bean.intValue);
- bean = MAPPER.readValue("{\"longValue\":\"\"}", PrimitivesBean.class);
- assertEquals(0L, bean.longValue);
- bean = MAPPER.readValue("{\"floatValue\":\"\"}", PrimitivesBean.class);
- assertEquals(0.0f, bean.floatValue);
- bean = MAPPER.readValue("{\"doubleValue\":\"\"}", PrimitivesBean.class);
- assertEquals(0.0, bean.doubleValue);
- }
-
- // for [JACKSON-652]
- // @since 1.9
- public void testUntypedWithJsonArrays() throws Exception
- {
- // by default we get:
- Object ob = MAPPER.readValue("[1]", Object.class);
- assertTrue(ob instanceof List<?>);
-
- // but can change to produce Object[]:
- MAPPER.configure(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY, true);
- ob = MAPPER.readValue("[1]", Object.class);
- assertEquals(Object[].class, ob.getClass());
- }
-
- // Test for verifying that Long values are coerced to boolean correctly as well
- public void testLongToBoolean() throws Exception
- {
- long value = 1L + Integer.MAX_VALUE;
- BooleanBean b = MAPPER.readValue("{\"primitive\" : "+value+", \"wrapper\":"+value+", \"ctor\":"+value+"}",
- BooleanBean.class);
- assertEquals(Boolean.TRUE, b.wrapper);
- assertTrue(b.primitive);
- assertEquals(Boolean.TRUE, b.ctor);
- }
-
- // [JACKSON-789]
- public void testCharset() throws Exception
- {
- Charset UTF8 = Charset.forName("UTF-8");
- assertSame(UTF8, MAPPER.readValue(quote("UTF-8"), Charset.class));
- }
-
- // [JACKSON-888]
- public void testStackTraceElement() throws Exception
- {
- StackTraceElement elem = null;
- try {
- throw new IllegalStateException();
- } catch (Exception e) {
- elem = e.getStackTrace()[0];
- }
- String json = MAPPER.writeValueAsString(elem);
- StackTraceElement back = MAPPER.readValue(json, StackTraceElement.class);
-
- assertEquals("testStackTraceElement", back.getMethodName());
- assertEquals(elem.getLineNumber(), back.getLineNumber());
- assertEquals(elem.getClassName(), back.getClassName());
- assertEquals(elem.isNativeMethod(), back.isNativeMethod());
- assertTrue(back.getClassName().endsWith("TestJdkTypes"));
- assertFalse(back.isNativeMethod());
- }
-
- // [Issue#239]
- public void testByteBuffer() throws Exception
- {
- byte[] INPUT = new byte[] { 1, 3, 9, -1, 6 };
- String exp = MAPPER.writeValueAsString(INPUT);
- ByteBuffer result = MAPPER.readValue(exp, ByteBuffer.class);
- assertNotNull(result);
- assertEquals(INPUT.length, result.remaining());
- for (int i = 0; i < INPUT.length; ++i) {
- assertEquals(INPUT[i], result.get());
- }
- assertEquals(0, result.remaining());
- }
-
- public void testStringBuilder() throws Exception
- {
- StringBuilder sb = MAPPER.readValue(quote("abc"), StringBuilder.class);
- assertEquals("abc", sb.toString());
- }
-
- // [Issue#429]
- public void testStackTraceElementWithCustom() throws Exception
- {
- // first, via bean that contains StackTraceElement
- StackTraceBean bean = MAPPER.readValue(aposToQuotes("{'Location':'foobar'}"),
- StackTraceBean.class);
- assertNotNull(bean);
- assertNotNull(bean.location);
- assertEquals(StackTraceBean.NUM, bean.location.getLineNumber());
-
- // and then directly, iff registered
- ObjectMapper mapper = new ObjectMapper();
- SimpleModule module = new SimpleModule();
- module.addDeserializer(StackTraceElement.class, new MyStackTraceElementDeserializer());
- mapper.registerModule(module);
-
- StackTraceElement elem = mapper.readValue("123", StackTraceElement.class);
- assertNotNull(elem);
- assertEquals(StackTraceBean.NUM, elem.getLineNumber());
-
- // and finally, even as part of real exception
-
- IOException ioe = mapper.readValue(aposToQuotes("{'stackTrace':[ 123, 456 ]}"),
- IOException.class);
- assertNotNull(ioe);
- StackTraceElement[] traces = ioe.getStackTrace();
- assertNotNull(traces);
- assertEquals(2, traces.length);
- assertEquals(StackTraceBean.NUM, traces[0].getLineNumber());
- assertEquals(StackTraceBean.NUM, traces[1].getLineNumber());
- }
-
- /*
- /**********************************************************
- /* Single-array handling tests
- /**********************************************************
- */
-
- // [Issue#381]
- public void testSingleElementArray() throws Exception {
- final int intTest = 932832;
- final double doubleTest = 32.3234;
- final long longTest = 2374237428374293423L;
- final short shortTest = (short) intTest;
- final float floatTest = 84.3743f;
- final byte byteTest = (byte) 43;
- final char charTest = 'c';
-
- final ObjectMapper mapper = new ObjectMapper();
- mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
-
- final int intValue = mapper.readValue(asArray(intTest), Integer.TYPE);
- assertEquals(intTest, intValue);
- final Integer integerWrapperValue = mapper.readValue(asArray(Integer.valueOf(intTest)), Integer.class);
- assertEquals(Integer.valueOf(intTest), integerWrapperValue);
-
- final double doubleValue = mapper.readValue(asArray(doubleTest), Double.class);
- assertEquals(doubleTest, doubleValue);
- final Double doubleWrapperValue = mapper.readValue(asArray(Double.valueOf(doubleTest)), Double.class);
- assertEquals(Double.valueOf(doubleTest), doubleWrapperValue);
-
- final long longValue = mapper.readValue(asArray(longTest), Long.TYPE);
- assertEquals(longTest, longValue);
- final Long longWrapperValue = mapper.readValue(asArray(Long.valueOf(longTest)), Long.class);
- assertEquals(Long.valueOf(longTest), longWrapperValue);
-
- final short shortValue = mapper.readValue(asArray(shortTest), Short.TYPE);
- assertEquals(shortTest, shortValue);
- final Short shortWrapperValue = mapper.readValue(asArray(Short.valueOf(shortTest)), Short.class);
- assertEquals(Short.valueOf(shortTest), shortWrapperValue);
-
- final float floatValue = mapper.readValue(asArray(floatTest), Float.TYPE);
- assertEquals(floatTest, floatValue);
- final Float floatWrapperValue = mapper.readValue(asArray(Float.valueOf(floatTest)), Float.class);
- assertEquals(Float.valueOf(floatTest), floatWrapperValue);
-
- final byte byteValue = mapper.readValue(asArray(byteTest), Byte.TYPE);
- assertEquals(byteTest, byteValue);
- final Byte byteWrapperValue = mapper.readValue(asArray(Byte.valueOf(byteTest)), Byte.class);
- assertEquals(Byte.valueOf(byteTest), byteWrapperValue);
-
- final char charValue = mapper.readValue(asArray(quote(String.valueOf(charTest))), Character.TYPE);
- assertEquals(charTest, charValue);
- final Character charWrapperValue = mapper.readValue(asArray(quote(String.valueOf(charTest))), Character.class);
- assertEquals(Character.valueOf(charTest), charWrapperValue);
-
- final boolean booleanTrueValue = mapper.readValue(asArray(true), Boolean.TYPE);
- assertTrue(booleanTrueValue);
-
- final boolean booleanFalseValue = mapper.readValue(asArray(false), Boolean.TYPE);
- assertFalse(booleanFalseValue);
-
- final Boolean booleanWrapperTrueValue = mapper.readValue(asArray(Boolean.valueOf(true)), Boolean.class);
- assertEquals(Boolean.TRUE, booleanWrapperTrueValue);
- }
-
- private static String asArray(Object value) {
- final String stringVal = value.toString();
- return new StringBuilder(stringVal.length() + 2).append("[").append(stringVal).append("]").toString();
- }
-
- public void testSingleElementArrayException() throws Exception {
- final ObjectMapper mapper = new ObjectMapper();
- mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
- try {
- mapper.readValue("[42]", Integer.class);
- fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
- try {
- mapper.readValue("[42]", Integer.TYPE);
- fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
-
- try {
- mapper.readValue("[42.273]", Double.class);
- fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
- try {
- mapper.readValue("[42.2723]", Double.TYPE);
- fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
-
- try {
- mapper.readValue("[42342342342342]", Long.class);
- fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
- try {
- mapper.readValue("[42342342342342342]", Long.TYPE);
- fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
-
- try {
- mapper.readValue("[42]", Short.class);
- fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
- try {
- mapper.readValue("[42]", Short.TYPE);
- fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
-
- try {
- mapper.readValue("[327.2323]", Float.class);
- fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
- try {
- mapper.readValue("[82.81902]", Float.TYPE);
- fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
-
- try {
- mapper.readValue("[22]", Byte.class);
- fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
- try {
- mapper.readValue("[22]", Byte.TYPE);
- fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
-
- try {
- mapper.readValue("['d']", Character.class);
- fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
- try {
- mapper.readValue("['d']", Character.TYPE);
- fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
-
- try {
- mapper.readValue("[true]", Boolean.class);
- fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
- try {
- mapper.readValue("[true]", Boolean.TYPE);
- fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
- }
-
- public void testMultiValueArrayException() throws IOException {
- final ObjectMapper mapper = new ObjectMapper();
- mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
-
- try {
- mapper.readValue("[42,42]", Integer.class);
- fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
- try {
- mapper.readValue("[42,42]", Integer.TYPE);
- fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
-
- try {
- mapper.readValue("[42.273,42.273]", Double.class);
- fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
- try {
- mapper.readValue("[42.2723,42.273]", Double.TYPE);
- fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
-
- try {
- mapper.readValue("[42342342342342,42342342342342]", Long.class);
- fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
- try {
- mapper.readValue("[42342342342342342,42342342342342]", Long.TYPE);
- fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
-
- try {
- mapper.readValue("[42,42]", Short.class);
- fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
- try {
- mapper.readValue("[42,42]", Short.TYPE);
- fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
-
- try {
- mapper.readValue("[327.2323,327.2323]", Float.class);
- fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
- try {
- mapper.readValue("[82.81902,327.2323]", Float.TYPE);
- fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
-
- try {
- mapper.readValue("[22,23]", Byte.class);
- fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
- try {
- mapper.readValue("[22,23]", Byte.TYPE);
- fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
-
- try {
- mapper.readValue(asArray(quote("c") + "," + quote("d")), Character.class);
-
- fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
- try {
- mapper.readValue(asArray(quote("c") + "," + quote("d")), Character.TYPE);
- fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
-
- try {
- mapper.readValue("[true,false]", Boolean.class);
- fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
- try {
- mapper.readValue("[true,false]", Boolean.TYPE);
- fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
- }
-}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestNullHandling.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestNullHandling.java
deleted file mode 100644
index e49b3c9..0000000
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestNullHandling.java
+++ /dev/null
@@ -1,139 +0,0 @@
-package com.fasterxml.jackson.databind.deser;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import com.fasterxml.jackson.annotation.JsonAnySetter;
-import com.fasterxml.jackson.core.*;
-import com.fasterxml.jackson.databind.*;
-import com.fasterxml.jackson.databind.module.SimpleModule;
-
-public class TestNullHandling extends BaseMapTest
-{
- static class FunnyNullDeserializer extends JsonDeserializer<String>
- {
- @Override
- public String deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
- return "text";
- }
-
- @Override
- public String getNullValue(DeserializationContext ctxt) { return "funny"; }
- }
-
- static class AnySetter{
-
- private Map<String,String> any = new HashMap<String,String>();
-
- @JsonAnySetter
- public void setAny(String name, String value){
- this.any.put(name,value);
- }
-
- public Map<String,String> getAny(){
- return this.any;
- }
- }
-
- /*
- /**********************************************************
- /* Test methods
- /**********************************************************
- */
-
- public void testAnySetterNulls() throws Exception {
- ObjectMapper mapper = new ObjectMapper();
- SimpleModule module = new SimpleModule("test", Version.unknownVersion());
- module.addDeserializer(String.class, new FunnyNullDeserializer());
- mapper.registerModule(module);
-
- String fieldName = "fieldName";
- String nullValue = "{\""+fieldName+"\":null}";
-
- // should get non-default null directly:
- AnySetter result = mapper.readValue(nullValue, AnySetter.class);
-
- assertEquals(1, result.getAny().size());
- assertNotNull(result.getAny().get(fieldName));
- assertEquals("funny", result.getAny().get(fieldName));
-
- // as well as via ObjectReader
- ObjectReader reader = mapper.readerFor(AnySetter.class);
- result = reader.readValue(nullValue);
-
- assertEquals(1, result.getAny().size());
- assertNotNull(result.getAny().get(fieldName));
- assertEquals("funny", result.getAny().get(fieldName));
- }
-
- // Test for [JACKSON-643]
- public void testCustomRootNulls() throws Exception
- {
- ObjectMapper mapper = new ObjectMapper();
- SimpleModule module = new SimpleModule("test", Version.unknownVersion());
- module.addDeserializer(String.class, new FunnyNullDeserializer());
- mapper.registerModule(module);
-
- // should get non-default null directly:
- String str = mapper.readValue("null", String.class);
- assertNotNull(str);
- assertEquals("funny", str);
-
- // as well as via ObjectReader
- ObjectReader reader = mapper.readerFor(String.class);
- str = reader.readValue("null");
- assertNotNull(str);
- assertEquals("funny", str);
- }
-
- // Test for [#407]
- public void testListOfNulls() throws Exception
- {
- ObjectMapper mapper = new ObjectMapper();
- SimpleModule module = new SimpleModule("test", Version.unknownVersion());
- module.addDeserializer(String.class, new FunnyNullDeserializer());
- mapper.registerModule(module);
-
- List<String> list = Arrays.asList("funny");
- JavaType type = mapper.getTypeFactory().constructCollectionType(List.class, String.class);
-
- // should get non-default null directly:
- List<?> deser = mapper.readValue("[null]", type);
- assertNotNull(deser);
- assertEquals(1, deser.size());
- assertEquals(list.get(0), deser.get(0));
-
- // as well as via ObjectReader
- ObjectReader reader = mapper.readerFor(type);
- deser = reader.readValue("[null]");
- assertNotNull(deser);
- assertEquals(1, deser.size());
- assertEquals(list.get(0), deser.get(0));
- }
-
- // Test for [#407]
- public void testMapOfNulls() throws Exception
- {
- ObjectMapper mapper = new ObjectMapper();
- SimpleModule module = new SimpleModule("test", Version.unknownVersion());
- module.addDeserializer(String.class, new FunnyNullDeserializer());
- mapper.registerModule(module);
-
- JavaType type = mapper.getTypeFactory().constructMapType(Map.class, String.class, String.class);
- // should get non-default null directly:
- Map<?,?> deser = mapper.readValue("{\"key\":null}", type);
- assertNotNull(deser);
- assertEquals(1, deser.size());
- assertEquals("funny", deser.get("key"));
-
- // as well as via ObjectReader
- ObjectReader reader = mapper.readerFor(type);
- deser = reader.readValue("{\"key\":null}");
- assertNotNull(deser);
- assertEquals(1, deser.size());
- assertEquals("funny", deser.get("key"));
- }
-}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestSimpleTypes.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestSimpleTypes.java
deleted file mode 100644
index 80150a2..0000000
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestSimpleTypes.java
+++ /dev/null
@@ -1,784 +0,0 @@
-package com.fasterxml.jackson.databind.deser;
-
-import java.io.*;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.net.URL;
-import java.net.URI;
-import java.util.*;
-
-import org.junit.Assert;
-
-import com.fasterxml.jackson.core.*;
-import com.fasterxml.jackson.databind.*;
-import com.fasterxml.jackson.databind.exc.InvalidFormatException;
-import com.fasterxml.jackson.databind.util.TokenBuffer;
-
-/**
- * Unit tests for verifying handling of simple basic non-structured
- * types; primitives (and/or their wrappers), Strings.
- */
-public class TestSimpleTypes
- extends BaseMapTest
-{
- final static String NAN_STRING = "NaN";
-
- final static class BooleanBean {
- boolean _v;
- void setV(boolean v) { _v = v; }
- }
-
- static class IntBean {
- int _v;
- void setV(int v) { _v = v; }
- }
-
- final static class DoubleBean {
- double _v;
- void setV(double v) { _v = v; }
- }
-
- final static class FloatBean {
- float _v;
- void setV(float v) { _v = v; }
- }
-
- final static class CharacterBean {
- char _v;
- void setV(char v) { _v = v; }
- char getV() { return _v; }
- }
-
- final static class CharacterWrapperBean {
- Character _v;
- void setV(Character v) { _v = v; }
- Character getV() { return _v; }
- }
-
- /**
- * Also, let's ensure that it's ok to override methods.
- */
- static class IntBean2
- extends IntBean
- {
- @Override
- void setV(int v2) { super.setV(v2+1); }
- }
-
- /*
- /**********************************************************
- /* Then tests for primitives
- /**********************************************************
- */
-
- private final ObjectMapper MAPPER = new ObjectMapper();
-
- public void testBooleanPrimitive() throws Exception
- {
- // first, simple case:
- BooleanBean result = MAPPER.readValue(new StringReader("{\"v\":true}"), BooleanBean.class);
- assertTrue(result._v);
- // then [JACKSON-79]:
- result = MAPPER.readValue(new StringReader("{\"v\":null}"), BooleanBean.class);
- assertNotNull(result);
- assertFalse(result._v);
-
- // should work with arrays too..
- boolean[] array = MAPPER.readValue(new StringReader("[ null ]"), boolean[].class);
- assertNotNull(array);
- assertEquals(1, array.length);
- assertFalse(array[0]);
-
- // [Issue#381]
- final ObjectMapper mapper = new ObjectMapper();
- mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
- result = mapper.readValue(new StringReader("{\"v\":[true]}"), BooleanBean.class);
- assertTrue(result._v);
-
- try {
- mapper.readValue(new StringReader("[{\"v\":[true,true]}]"), BooleanBean.class);
- fail("Did not throw exception while reading a value from a multi value array with UNWRAP_SINGLE_VALUE_ARRAY feature enabled");
- } catch (JsonMappingException exp) {
- //threw exception as required
- }
-
- result = mapper.readValue(new StringReader("{\"v\":[null]}"), BooleanBean.class);
- assertNotNull(result);
- assertFalse(result._v);
-
- result = mapper.readValue(new StringReader("[{\"v\":[null]}]"), BooleanBean.class);
- assertNotNull(result);
- assertFalse(result._v);
-
- array = mapper.readValue(new StringReader("[ [ null ] ]"), boolean[].class);
- assertNotNull(array);
- assertEquals(1, array.length);
- assertFalse(array[0]);
- }
-
- public void testIntPrimitive() throws Exception
- {
- // first, simple case:
- IntBean result = MAPPER.readValue(new StringReader("{\"v\":3}"), IntBean.class);
- assertEquals(3, result._v);
- // then [JACKSON-79]:
- result = MAPPER.readValue(new StringReader("{\"v\":null}"), IntBean.class);
- assertNotNull(result);
- assertEquals(0, result._v);
-
- // should work with arrays too..
- int[] array = MAPPER.readValue(new StringReader("[ null ]"), int[].class);
- assertNotNull(array);
- assertEquals(1, array.length);
- assertEquals(0, array[0]);
-
- // [Issue#381]
- final ObjectMapper mapper = new ObjectMapper();
- mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
- try {
- mapper.readValue(new StringReader("{\"v\":[3]}"), IntBean.class);
- fail("Did not throw exception when reading a value from a single value array with the UNWRAP_SINGLE_VALUE_ARRAYS feature disabled");
- } catch (JsonMappingException exp) {
- //Correctly threw exception
- }
-
- mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
-
- result = mapper.readValue(new StringReader("{\"v\":[3]}"), IntBean.class);
- assertEquals(3, result._v);
-
- result = mapper.readValue(new StringReader("[{\"v\":[3]}]"), IntBean.class);
- assertEquals(3, result._v);
-
- try {
- mapper.readValue(new StringReader("[{\"v\":[3,3]}]"), IntBean.class);
- fail("Did not throw exception while reading a value from a multi value array with UNWRAP_SINGLE_VALUE_ARRAY feature enabled");
- } catch (JsonMappingException exp) {
- //threw exception as required
- }
-
- result = mapper.readValue(new StringReader("{\"v\":[null]}"), IntBean.class);
- assertNotNull(result);
- assertEquals(0, result._v);
-
- array = mapper.readValue(new StringReader("[ [ null ] ]"), int[].class);
- assertNotNull(array);
- assertEquals(1, array.length);
- assertEquals(0, array[0]);
- }
-
- public void testDoublePrimitive() throws Exception
- {
- // first, simple case:
- // bit tricky with binary fps but...
- double value = 0.016;
- DoubleBean result = MAPPER.readValue(new StringReader("{\"v\":"+value+"}"), DoubleBean.class);
- assertEquals(value, result._v);
- // then [JACKSON-79]:
- result = MAPPER.readValue(new StringReader("{\"v\":null}"), DoubleBean.class);
- assertNotNull(result);
- assertEquals(0.0, result._v);
-
- // should work with arrays too..
- double[] array = MAPPER.readValue(new StringReader("[ null ]"), double[].class);
- assertNotNull(array);
- assertEquals(1, array.length);
- assertEquals(0.0, array[0]);
-
- // [Issue#381]
- final ObjectMapper mapper = new ObjectMapper();
- mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
- try {
- mapper.readValue(new StringReader("{\"v\":[" + value + "]}"), DoubleBean.class);
- fail("Did not throw exception when reading a value from a single value array with the UNWRAP_SINGLE_VALUE_ARRAYS feature disabled");
- } catch (JsonMappingException exp) {
- //Correctly threw exception
- }
-
- mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
-
- result = mapper.readValue(new StringReader("{\"v\":[" + value + "]}"), DoubleBean.class);
- assertEquals(value, result._v);
-
- result = mapper.readValue(new StringReader("[{\"v\":[" + value + "]}]"), DoubleBean.class);
- assertEquals(value, result._v);
-
- try {
- mapper.readValue(new StringReader("[{\"v\":[" + value + "," + value + "]}]"), DoubleBean.class);
- fail("Did not throw exception while reading a value from a multi value array with UNWRAP_SINGLE_VALUE_ARRAY feature enabled");
- } catch (JsonMappingException exp) {
- //threw exception as required
- }
-
- result = mapper.readValue(new StringReader("{\"v\":[null]}"), DoubleBean.class);
- assertNotNull(result);
- assertEquals(0d, result._v);
-
- array = mapper.readValue(new StringReader("[ [ null ] ]"), double[].class);
- assertNotNull(array);
- assertEquals(1, array.length);
- assertEquals(0d, array[0]);
- }
-
- public void testDoublePrimitiveNonNumeric() throws Exception
- {
- // first, simple case:
- // bit tricky with binary fps but...
- double value = Double.POSITIVE_INFINITY;
- DoubleBean result = MAPPER.readValue(new StringReader("{\"v\":\""+value+"\"}"), DoubleBean.class);
- assertEquals(value, result._v);
-
- // should work with arrays too..
- double[] array = MAPPER.readValue(new StringReader("[ \"Infinity\" ]"), double[].class);
- assertNotNull(array);
- assertEquals(1, array.length);
- assertEquals(Double.POSITIVE_INFINITY, array[0]);
- }
-
- public void testFloatPrimitiveNonNumeric() throws Exception
- {
- // bit tricky with binary fps but...
- float value = Float.POSITIVE_INFINITY;
- FloatBean result = MAPPER.readValue(new StringReader("{\"v\":\""+value+"\"}"), FloatBean.class);
- assertEquals(value, result._v);
-
- // should work with arrays too..
- float[] array = MAPPER.readValue(new StringReader("[ \"Infinity\" ]"), float[].class);
- assertNotNull(array);
- assertEquals(1, array.length);
- assertEquals(Float.POSITIVE_INFINITY, array[0]);
- }
-
- /**
- * Beyond simple case, let's also ensure that method overriding works as
- * expected.
- */
- public void testIntWithOverride() throws Exception
- {
- IntBean2 result = MAPPER.readValue(new StringReader("{\"v\":8}"), IntBean2.class);
- assertEquals(9, result._v);
- }
-
- public void testEmptyToNullCoercionForPrimitives() throws Exception {
- _testEmptyToNullCoercion(int.class, Integer.valueOf(0));
- _testEmptyToNullCoercion(long.class, Long.valueOf(0));
- _testEmptyToNullCoercion(double.class, Double.valueOf(0.0));
- _testEmptyToNullCoercion(float.class, Float.valueOf(0.0f));
- }
-
- private void _testEmptyToNullCoercion(Class<?> primType, Object emptyValue) throws Exception
- {
- final String EMPTY = "\"\"";
-
- // as per [databind#1095] should only allow coercion from empty String,
- // if `null` is acceptable
- ObjectReader intR = MAPPER.readerFor(primType);
- assertEquals(emptyValue, intR.readValue(EMPTY));
- try {
- intR.with(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)
- .readValue("\"\"");
- fail("Should not have passed");
- } catch (JsonMappingException e) {
- verifyException(e, "Can not map Empty String");
- }
- }
-
- /*
- /**********************************************************
- /* Then tests for wrappers
- /**********************************************************
- */
-
- /**
- * Simple unit test to verify that we can map boolean values to
- * java.lang.Boolean.
- */
- public void testBooleanWrapper() throws Exception
- {
- Boolean result = MAPPER.readValue(new StringReader("true"), Boolean.class);
- assertEquals(Boolean.TRUE, result);
- result = MAPPER.readValue(new StringReader("false"), Boolean.class);
- assertEquals(Boolean.FALSE, result);
-
- // [JACKSON-78]: should accept ints too, (0 == false, otherwise true)
- result = MAPPER.readValue(new StringReader("0"), Boolean.class);
- assertEquals(Boolean.FALSE, result);
- result = MAPPER.readValue(new StringReader("1"), Boolean.class);
- assertEquals(Boolean.TRUE, result);
- }
-
- public void testByteWrapper() throws Exception
- {
- Byte result = MAPPER.readValue(new StringReader(" -42\t"), Byte.class);
- assertEquals(Byte.valueOf((byte)-42), result);
-
- // Also: should be able to coerce floats, strings:
- result = MAPPER.readValue(new StringReader(" \"-12\""), Byte.class);
- assertEquals(Byte.valueOf((byte)-12), result);
-
- result = MAPPER.readValue(new StringReader(" 39.07"), Byte.class);
- assertEquals(Byte.valueOf((byte)39), result);
- }
-
- public void testShortWrapper() throws Exception
- {
- Short result = MAPPER.readValue(new StringReader("37"), Short.class);
- assertEquals(Short.valueOf((short)37), result);
-
- // Also: should be able to coerce floats, strings:
- result = MAPPER.readValue(new StringReader(" \"-1009\""), Short.class);
- assertEquals(Short.valueOf((short)-1009), result);
-
- result = MAPPER.readValue(new StringReader("-12.9"), Short.class);
- assertEquals(Short.valueOf((short)-12), result);
- }
-
- public void testCharacterWrapper() throws Exception
- {
- // First: canonical value is 1-char string
- Character result = MAPPER.readValue(new StringReader("\"a\""), Character.class);
- assertEquals(Character.valueOf('a'), result);
-
- // But can also pass in ascii code
- result = MAPPER.readValue(new StringReader(" "+((int) 'X')), Character.class);
- assertEquals(Character.valueOf('X'), result);
-
- final CharacterWrapperBean wrapper = MAPPER.readValue(new StringReader("{\"v\":null}"), CharacterWrapperBean.class);
- assertNotNull(wrapper);
- assertNull(wrapper.getV());
-
- final ObjectMapper mapper = new ObjectMapper();
- mapper.enable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES);
- try {
- mapper.readValue("{\"v\":null}", CharacterBean.class);
- fail("Attempting to deserialize a 'null' JSON reference into a 'char' property did not throw an exception");
- } catch (JsonMappingException exp) {
- //Exception thrown as required
- }
-
- mapper.disable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES);
- final CharacterBean charBean = MAPPER.readValue(new StringReader("{\"v\":null}"), CharacterBean.class);
- assertNotNull(wrapper);
- assertEquals('\u0000', charBean.getV());
- }
-
- public void testIntWrapper() throws Exception
- {
- Integer result = MAPPER.readValue(new StringReader(" -42\t"), Integer.class);
- assertEquals(Integer.valueOf(-42), result);
-
- // Also: should be able to coerce floats, strings:
- result = MAPPER.readValue(new StringReader(" \"-1200\""), Integer.class);
- assertEquals(Integer.valueOf(-1200), result);
-
- result = MAPPER.readValue(new StringReader(" 39.07"), Integer.class);
- assertEquals(Integer.valueOf(39), result);
- }
-
- public void testLongWrapper() throws Exception
- {
- Long result = MAPPER.readValue(new StringReader("12345678901"), Long.class);
- assertEquals(Long.valueOf(12345678901L), result);
-
- // Also: should be able to coerce floats, strings:
- result = MAPPER.readValue(new StringReader(" \"-9876\""), Long.class);
- assertEquals(Long.valueOf(-9876), result);
-
- result = MAPPER.readValue(new StringReader("1918.3"), Long.class);
- assertEquals(Long.valueOf(1918), result);
- }
-
- /* Note: dealing with floating-point values is tricky; not sure if
- * we can really use equality tests here... JDK does have decent
- * conversions though, to retain accuracy and round-trippability.
- * But still...
- */
- public void testFloatWrapper() throws Exception
- {
- // Also: should be able to coerce floats, strings:
- String[] STRS = new String[] {
- "1.0", "0.0", "-0.3", "0.7", "42.012", "-999.0", NAN_STRING
- };
-
- for (String str : STRS) {
- Float exp = Float.valueOf(str);
- Float result;
-
- if (NAN_STRING != str) {
- // First, as regular floating point value
- result = MAPPER.readValue(new StringReader(str), Float.class);
- assertEquals(exp, result);
- }
-
- // and then as coerced String:
- result = MAPPER.readValue(new StringReader(" \""+str+"\""), Float.class);
- assertEquals(exp, result);
- }
- }
-
- public void testDoubleWrapper() throws Exception
- {
- // Also: should be able to coerce doubles, strings:
- String[] STRS = new String[] {
- "1.0", "0.0", "-0.3", "0.7", "42.012", "-999.0", NAN_STRING
- };
-
- for (String str : STRS) {
- Double exp = Double.valueOf(str);
- Double result;
-
- // First, as regular double value
- if (NAN_STRING != str) {
- result = MAPPER.readValue(str, Double.class);
- assertEquals(exp, result);
- }
- // and then as coerced String:
- result = MAPPER.readValue(new StringReader(" \""+str+"\""), Double.class);
- assertEquals(exp, result);
- }
- }
-
- // as per [Issue#42], allow Base64 variant use as well
- public void testBase64Variants() throws Exception
- {
- final byte[] INPUT = "abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz1234567890X".getBytes("UTF-8");
-
- // default encoding is "MIME, no linefeeds", so:
- Assert.assertArrayEquals(INPUT, MAPPER.readValue(
- quote("YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwWA=="),
- byte[].class));
- ObjectReader reader = MAPPER.readerFor(byte[].class);
- Assert.assertArrayEquals(INPUT, (byte[]) reader.with(Base64Variants.MIME_NO_LINEFEEDS).readValue(
- quote("YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwWA=="
- )));
-
- // but others should be slightly different
- Assert.assertArrayEquals(INPUT, (byte[]) reader.with(Base64Variants.MIME).readValue(
- quote("YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwYWJjZGVmZ2hpamtsbW5vcHFyc3R1\\ndnd4eXoxMjM0NTY3ODkwWA=="
- )));
- Assert.assertArrayEquals(INPUT, (byte[]) reader.with(Base64Variants.MODIFIED_FOR_URL).readValue(
- quote("YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwWA"
- )));
- // PEM mandates 64 char lines:
- Assert.assertArrayEquals(INPUT, (byte[]) reader.with(Base64Variants.PEM).readValue(
- quote("YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwYWJjZGVmZ2hpamts\\nbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwWA=="
- )));
- }
- /*
- /**********************************************************
- /* Simple non-primitive types
- /**********************************************************
- */
-
- public void testSingleString() throws Exception
- {
- String value = "FOO!";
- String result = MAPPER.readValue(new StringReader("\""+value+"\""), String.class);
- assertEquals(value, result);
- }
-
- public void testSingleStringWrapped() throws Exception
- {
- final ObjectMapper mapper = new ObjectMapper();
- mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
-
- String value = "FOO!";
- try {
- mapper.readValue(new StringReader("[\""+value+"\"]"), String.class);
- fail("Exception not thrown when attempting to unwrap a single value 'String' array into a simple String");
- } catch (JsonMappingException exp) {
- //exception thrown correctly
- }
-
- mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
-
- try {
- mapper.readValue(new StringReader("[\""+value+"\",\""+value+"\"]"), String.class);
- fail("Exception not thrown when attempting to unwrap a single value 'String' array that contained more than one value into a simple String");
- } catch (JsonMappingException exp) {
- //exception thrown correctly
- }
-
- String result = mapper.readValue(new StringReader("[\""+value+"\"]"), String.class);
- assertEquals(value, result);
- }
-
- public void testNull() throws Exception
- {
- // null doesn't really have a type, fake by assuming Object
- Object result = MAPPER.readValue(" null", Object.class);
- assertNull(result);
- }
-
- public void testClass() throws Exception
- {
- final ObjectMapper mapper = new ObjectMapper();
- mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
-
- Class<?> result = mapper.readValue(quote(String.class.getName()), Class.class);
- assertEquals(String.class, result);
-
- //[Issue#381]
- try {
- mapper.readValue("[" + quote(String.class.getName()) + "]", Class.class);
- fail("Did not throw exception when UNWRAP_SINGLE_VALUE_ARRAYS feature was disabled and attempted to read a Class array containing one element");
- } catch (JsonMappingException exp) {
-
- }
-
- mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
-
- try {
- mapper.readValue("[" + quote(Object.class.getName()) + "," + quote(Object.class.getName()) +"]", Class.class);
- fail("Did not throw exception when UNWRAP_SINGLE_VALUE_ARRAYS feature was enabled and attempted to read a Class array containing two elements");
- } catch (JsonMappingException exp) {
-
- }
- result = mapper.readValue("[" + quote(String.class.getName()) + "]", Class.class);
- assertEquals(String.class, result);
- }
-
- public void testBigDecimal() throws Exception
- {
- final ObjectMapper mapper = objectMapper();
- mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
-
- BigDecimal value = new BigDecimal("0.001");
- BigDecimal result = mapper.readValue(value.toString(), BigDecimal.class);
- assertEquals(value, result);
-
- //Issue#381
- try {
- mapper.readValue("[" + value.toString() + "]", BigDecimal.class);
- fail("Exception was not thrown when attempting to read a single value array of BigDecimal when UNWRAP_SINGLE_VALUE_ARRAYS feature is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
-
- mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
- result = mapper.readValue("[" + value.toString() + "]", BigDecimal.class);
- assertEquals(value, result);
-
- try {
- mapper.readValue("[" + value.toString() + "," + value.toString() + "]", BigDecimal.class);
- fail("Exception was not thrown when attempting to read a muti value array of BigDecimal when UNWRAP_SINGLE_VALUE_ARRAYS feature is enabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
- }
-
- public void testBigInteger() throws Exception
- {
- final ObjectMapper mapper = objectMapper();
- mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
-
- BigInteger value = new BigInteger("-1234567890123456789012345567809");
- BigInteger result = mapper.readValue(new StringReader(value.toString()), BigInteger.class);
- assertEquals(value, result);
-
- //Issue#381
- try {
- mapper.readValue("[" + value.toString() + "]", BigInteger.class);
- fail("Exception was not thrown when attempting to read a single value array of BigInteger when UNWRAP_SINGLE_VALUE_ARRAYS feature is disabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
-
- mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
- result = mapper.readValue("[" + value.toString() + "]", BigInteger.class);
- assertEquals(value, result);
-
- try {
- mapper.readValue("[" + value.toString() + "," + value.toString() + "]", BigInteger.class);
- fail("Exception was not thrown when attempting to read a muti value array of BigInteger when UNWRAP_SINGLE_VALUE_ARRAYS feature is enabled");
- } catch (JsonMappingException exp) {
- //Exception was thrown correctly
- }
- }
-
- public void testUUID() throws Exception
- {
- final ObjectMapper mapper = objectMapper();
-
- final String NULL_UUID = "00000000-0000-0000-0000-000000000000";
- // first, couple of generated UUIDs:
- for (String value : new String[] {
- "76e6d183-5f68-4afa-b94a-922c1fdb83f8",
- "540a88d1-e2d8-4fb1-9396-9212280d0a7f",
- "2c9e441d-1cd0-472d-9bab-69838f877574",
- "591b2869-146e-41d7-8048-e8131f1fdec5",
- "82994ac2-7b23-49f2-8cc5-e24cf6ed77be",
- "00000007-0000-0000-0000-000000000000"
- }) {
-
- mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
-
- UUID uuid = UUID.fromString(value);
- assertEquals(uuid,
- mapper.readValue(quote(value), UUID.class));
-
- try {
- mapper.readValue("[" + quote(value) + "]", UUID.class);
- fail("Exception was not thrown when UNWRAP_SINGLE_VALUE_ARRAYS is disabled and attempted to read a single value array as a single element");
- } catch (JsonMappingException exp) {
- //Exception thrown successfully
- }
-
- mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
-
- assertEquals(uuid,
- mapper.readValue("[" + quote(value) + "]", UUID.class));
-
- try {
- mapper.readValue("[" + quote(value) + "," + quote(value) + "]", UUID.class);
- fail("Exception was not thrown when UNWRAP_SINGLE_VALUE_ARRAYS is enabled and attempted to read a multi value array as a single element");
- } catch (JsonMappingException exp) {
- //Exception thrown successfully
- }
- }
- // then use templating; note that these are not exactly valid UUIDs
- // wrt spec (type bits etc), but JDK UUID should deal ok
- final String TEMPL = NULL_UUID;
- final String chars = "123456789abcdefABCDEF";
-
- for (int i = 0; i < chars.length(); ++i) {
- String value = TEMPL.replace('0', chars.charAt(i));
- assertEquals(UUID.fromString(value).toString(),
- mapper.readValue(quote(value), UUID.class).toString());
- }
-
- // also: see if base64 encoding works as expected
- String base64 = Base64Variants.getDefaultVariant().encode(new byte[16]);
- assertEquals(UUID.fromString(NULL_UUID),
- mapper.readValue(quote(base64), UUID.class));
- }
-
- public void testUUIDInvalid() throws Exception
- {
- // and finally, exception handling too [databind#1000], for invalid cases
- try {
- MAPPER.readValue(quote("abcde"), UUID.class);
- fail("Should fail on invalid UUID string");
- } catch (InvalidFormatException e) {
- verifyException(e, "UUID has to be represented by standard");
- }
- try {
- MAPPER.readValue(quote("76e6d183-5f68-4afa-b94a-922c1fdb83fx"), UUID.class);
- fail("Should fail on invalid UUID string");
- } catch (InvalidFormatException e) {
- verifyException(e, "non-hex character 'x'");
- }
- // should also test from-bytes version, but that's trickier... leave for now.
- }
-
- public void testUUIDAux() throws Exception
- {
- // [JACKSON-393] fix:
- final UUID value = UUID.fromString("76e6d183-5f68-4afa-b94a-922c1fdb83f8");
-
- // first, null should come as null
- TokenBuffer buf = new TokenBuffer(null, false);
- buf.writeObject(null);
- assertNull(MAPPER.readValue(buf.asParser(), UUID.class));
- buf.close();
-
- // then, UUID itself come as is:
- buf = new TokenBuffer(null, false);
- buf.writeObject(value);
- assertSame(value, MAPPER.readValue(buf.asParser(), UUID.class));
-
- // and finally from byte[]
- // oh crap; JDK UUID just... sucks. Not even byte[] accessors or constructors? Huh?
- ByteArrayOutputStream bytes = new ByteArrayOutputStream();
- DataOutputStream out = new DataOutputStream(bytes);
- out.writeLong(value.getMostSignificantBits());
- out.writeLong(value.getLeastSignificantBits());
- byte[] data = bytes.toByteArray();
- assertEquals(16, data.length);
-
- buf.writeObject(data);
-
- UUID value2 = MAPPER.readValue(buf.asParser(), UUID.class);
-
- assertEquals(value, value2);
- buf.close();
- }
-
- public void testURL() throws Exception
- {
- URL value = new URL("http://foo.com");
- assertEquals(value, MAPPER.readValue("\""+value.toString()+"\"", URL.class));
-
- // trivial case; null to null, embedded URL to URL
- TokenBuffer buf = new TokenBuffer(null, false);
- buf.writeObject(null);
- assertNull(MAPPER.readValue(buf.asParser(), URL.class));
- buf.close();
-
- // then, URLitself come as is:
- buf = new TokenBuffer(null, false);
- buf.writeObject(value);
- assertSame(value, MAPPER.readValue(buf.asParser(), URL.class));
- buf.close();
- }
-
- public void testURI() throws Exception
- {
- final ObjectMapper mapper = new ObjectMapper();
-
- URI value = new URI("http://foo.com");
- assertEquals(value, mapper.readValue("\""+value.toString()+"\"", URI.class));
-
- //[Issue#381]
- mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
- try {
- assertEquals(value, mapper.readValue("[\""+value.toString()+"\"]", URI.class));
- fail("Did not throw exception for single value array when UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
- } catch (JsonMappingException exp) {
- //exception thrown successfully
- }
-
- try {
- assertEquals(value, mapper.readValue("[\""+value.toString()+"\",\""+value.toString()+"\"]", URI.class));
- fail("Did not throw exception for single value array when there were multiple values");
- } catch (JsonMappingException exp) {
- //exception thrown successfully
- }
-
- mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
- assertEquals(value, mapper.readValue("[\""+value.toString()+"\"]", URI.class));
-
- // [#398]
- value = mapper.readValue(quote(""), URI.class);
- assertNotNull(value);
- assertEquals(URI.create(""), value);
- }
-
- /*
- /**********************************************************
- /* Sequence tests
- /**********************************************************
- */
-
- /**
- * Then a unit test to verify that we can conveniently bind sequence of
- * space-separate simple values
- */
- public void testSequenceOfInts() throws Exception
- {
- final int NR_OF_INTS = 100;
-
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < NR_OF_INTS; ++i) {
- sb.append(" ");
- sb.append(i);
- }
- JsonParser jp = MAPPER.getFactory().createParser(sb.toString());
- for (int i = 0; i < NR_OF_INTS; ++i) {
- Integer result = MAPPER.readValue(jp, Integer.class);
- assertEquals(Integer.valueOf(i), result);
- }
- jp.close();
- }
-}
-
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestUntypedDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestUntypedDeserialization.java
index d5bb225..9154149 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestUntypedDeserialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestUntypedDeserialization.java
@@ -266,4 +266,16 @@
pojo = r.readValue("{\"a\":[]}");
assertTrue(pojo.value instanceof Map);
}
+
+ public void testUntypedWithJsonArrays() throws Exception
+ {
+ // by default we get:
+ Object ob = MAPPER.readValue("[1]", Object.class);
+ assertTrue(ob instanceof List<?>);
+
+ // but can change to produce Object[]:
+ MAPPER.configure(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY, true);
+ ob = MAPPER.readValue("[1]", Object.class);
+ assertEquals(Object[].class, ob.getClass());
+ }
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestValueAnnotations.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestValueAnnotations.java
index 6603745..e14ad18 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestValueAnnotations.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestValueAnnotations.java
@@ -67,13 +67,13 @@
public RootStringDeserializer() { super(RootString.class); }
@Override
- public RootString deserialize(JsonParser jp, DeserializationContext ctxt)
- throws IOException, JsonProcessingException
+ public RootString deserialize(JsonParser p, DeserializationContext ctxt)
+ throws IOException
{
- if (jp.getCurrentToken() == JsonToken.VALUE_STRING) {
- return new RootStringImpl(jp.getText());
+ if (p.hasToken(JsonToken.VALUE_STRING)) {
+ return new RootStringImpl(p.getText());
}
- throw ctxt.mappingException(_valueClass);
+ return (RootString) ctxt.handleUnexpectedToken(_valueClass, p);
}
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/UntypedNumbersTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/UntypedNumbersTest.java
deleted file mode 100644
index 2de4389..0000000
--- a/src/test/java/com/fasterxml/jackson/databind/deser/UntypedNumbersTest.java
+++ /dev/null
@@ -1,137 +0,0 @@
-package com.fasterxml.jackson.databind.deser;
-
-import java.io.*;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.util.*;
-
-
-import com.fasterxml.jackson.databind.*;
-
-/**
- * Unit tests for verifying handling of non-specific numeric types.
- */
-public class UntypedNumbersTest
- extends BaseMapTest
-{
- private final ObjectMapper MAPPER = objectMapper();
-
- public void testIntAsNumber() throws Exception
- {
- /* Even if declared as 'generic' type, should return using most
- * efficient type... here, Integer
- */
- Number result = MAPPER.readValue(" 123 ", Number.class);
- assertEquals(Integer.valueOf(123), result);
- }
-
- public void testLongAsNumber() throws Exception
- {
- // And beyond int range, should get long
- long exp = 1234567890123L;
- Number result = MAPPER.readValue(String.valueOf(exp), Number.class);
- assertEquals(Long.valueOf(exp), result);
- }
-
- public void testBigIntAsNumber() throws Exception
- {
- // and after long, BigInteger
- BigInteger biggie = new BigInteger("1234567890123456789012345678901234567890");
- Number result = MAPPER.readValue(biggie.toString(), Number.class);
- assertEquals(BigInteger.class, biggie.getClass());
- assertEquals(biggie, result);
- }
-
- public void testIntTypeOverride() throws Exception
- {
- /* Slight twist; as per [JACKSON-100], can also request binding
- * to BigInteger even if value would fit in Integer
- */
- ObjectReader r = MAPPER.reader(DeserializationFeature.USE_BIG_INTEGER_FOR_INTS);
-
- BigInteger exp = BigInteger.valueOf(123L);
-
- // first test as any Number
- Number result = r.forType(Number.class).readValue(" 123 ");
- assertEquals(BigInteger.class, result.getClass());
- assertEquals(exp, result);
-
- // then as any Object
- /*Object value =*/ r.forType(Object.class).readValue("123");
- assertEquals(BigInteger.class, result.getClass());
- assertEquals(exp, result);
-
- // and as JsonNode
- JsonNode node = r.readTree(" 123");
- assertTrue(node.isBigInteger());
- assertEquals(123, node.asInt());
- }
-
- public void testDoubleAsNumber() throws Exception
- {
- Number result = MAPPER.readValue(new StringReader(" 1.0 "), Number.class);
- assertEquals(Double.valueOf(1.0), result);
- }
-
- public void testFpTypeOverrideSimple() throws Exception
- {
- ObjectReader r = MAPPER.reader(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
- BigDecimal dec = new BigDecimal("0.1");
-
- // First test generic stand-alone Number
- Number result = r.forType(Number.class).readValue(dec.toString());
- assertEquals(BigDecimal.class, result.getClass());
- assertEquals(dec, result);
-
- // Then plain old Object
- Object value = r.forType(Object.class).readValue(dec.toString());
- assertEquals(BigDecimal.class, result.getClass());
- assertEquals(dec, value);
-
- JsonNode node = r.readTree(dec.toString());
- assertTrue(node.isBigDecimal());
- assertEquals(dec.doubleValue(), node.asDouble());
- }
-
- public void testFpTypeOverrideStructured() throws Exception
- {
- ObjectReader r = MAPPER.reader(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
-
- BigDecimal dec = new BigDecimal("-19.37");
- // List element types
- @SuppressWarnings("unchecked")
- List<Object> list = (List<Object>) r.forType(List.class).readValue("[ "+dec.toString()+" ]");
- assertEquals(1, list.size());
- Object val = list.get(0);
- assertEquals(BigDecimal.class, val.getClass());
- assertEquals(dec, val);
-
- // and a map
- Map<?,?> map = r.forType(Map.class).readValue("{ \"a\" : "+dec.toString()+" }");
- assertEquals(1, map.size());
- val = map.get("a");
- assertEquals(BigDecimal.class, val.getClass());
- assertEquals(dec, val);
- }
-
- // [databind#504]
- public void testForceIntsToLongs() throws Exception
- {
- ObjectReader r = MAPPER.reader(DeserializationFeature.USE_LONG_FOR_INTS);
-
- Object ob = r.forType(Object.class).readValue("42");
- assertEquals(Long.class, ob.getClass());
- assertEquals(Long.valueOf(42L), ob);
-
- Number n = r.forType(Number.class).readValue("42");
- assertEquals(Long.class, n.getClass());
- assertEquals(Long.valueOf(42L), n);
-
- // and one more: should get proper node as well
- JsonNode node = r.readTree("42");
- if (!node.isLong()) {
- fail("Expected LongNode, got: "+node.getClass().getName());
- }
- assertEquals(42, node.asInt());
- }
-}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ext/TestDOM.java b/src/test/java/com/fasterxml/jackson/databind/ext/TestDOM.java
index 2938d20..0bd5609 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ext/TestDOM.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ext/TestDOM.java
@@ -14,6 +14,8 @@
"<root attr='3'><leaf>Rock & Roll!</leaf><?proc instr?></root>";
final static String SIMPLE_XML_NS =
"<root ns:attr='abc' xmlns:ns='http://foo' />";
+
+ private final ObjectMapper MAPPER = new ObjectMapper();
public void testSerializeSimpleNonNS() throws Exception
{
@@ -21,11 +23,10 @@
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse
(new InputSource(new StringReader(SIMPLE_XML)));
assertNotNull(doc);
- ObjectMapper mapper = new ObjectMapper();
// need to strip xml declaration, if any
- String outputRaw = mapper.writeValueAsString(doc);
+ String outputRaw = MAPPER.writeValueAsString(doc);
// And re-parse as String, since JSON has quotes...
- String output = mapper.readValue(outputRaw, String.class);
+ String output = MAPPER.readValue(outputRaw, String.class);
/* ... and finally, normalize to (close to) canonical XML
* output (single vs double quotes, xml declaration etc)
*/
@@ -34,16 +35,15 @@
public void testDeserializeNonNS() throws Exception
{
- ObjectMapper mapper = new ObjectMapper();
for (int i = 0; i < 2; ++i) {
Document doc;
if (i == 0) {
// First, as Document:
- doc = mapper.readValue(quote(SIMPLE_XML), Document.class);
+ doc = MAPPER.readValue(quote(SIMPLE_XML), Document.class);
} else {
// and then as plain Node (no difference)
- Node node = mapper.readValue(quote(SIMPLE_XML), Node.class);
+ Node node = MAPPER.readValue(quote(SIMPLE_XML), Node.class);
doc = (Document) node;
}
Element root = doc.getDocumentElement();
@@ -66,8 +66,7 @@
public void testDeserializeNS() throws Exception
{
- ObjectMapper mapper = new ObjectMapper();
- Document doc = mapper.readValue(quote(SIMPLE_XML_NS), Document.class);
+ Document doc = MAPPER.readValue(quote(SIMPLE_XML_NS), Document.class);
Element root = doc.getDocumentElement();
assertNotNull(root);
assertEquals("root", root.getTagName());
@@ -82,9 +81,9 @@
}
/*
- **********************************************************
- * Helper methods
- **********************************************************
+ /**********************************************************
+ /* Helper methods
+ /**********************************************************
*/
protected static String normalizeOutput(String output)
diff --git a/src/test/java/com/fasterxml/jackson/databind/ext/TestJdk16Types.java b/src/test/java/com/fasterxml/jackson/databind/ext/TestJava6Types.java
similarity index 92%
rename from src/test/java/com/fasterxml/jackson/databind/ext/TestJdk16Types.java
rename to src/test/java/com/fasterxml/jackson/databind/ext/TestJava6Types.java
index 2d1f6fc..84ee94a 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ext/TestJdk16Types.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ext/TestJava6Types.java
@@ -9,7 +9,7 @@
* Tests to ensure that we can handle 1.6-only types, even if
* registrations are done without direct refs
*/
-public class TestJdk16Types extends com.fasterxml.jackson.databind.BaseMapTest
+public class TestJava6Types extends com.fasterxml.jackson.databind.BaseMapTest
{
// for [databind#216]
public void test16Types() throws Exception
diff --git a/src/test/java/com/fasterxml/jackson/databind/ext/TestJava7Types.java b/src/test/java/com/fasterxml/jackson/databind/ext/TestJava7Types.java
new file mode 100644
index 0000000..459978d
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ext/TestJava7Types.java
@@ -0,0 +1,33 @@
+package com.fasterxml.jackson.databind.ext;
+
+import java.nio.file.FileSystem;
+import java.nio.file.Path;
+
+import com.fasterxml.jackson.databind.*;
+
+import com.google.common.jimfs.Configuration;
+import com.google.common.jimfs.Jimfs;
+
+/**
+ * @since 2.7
+ */
+public class TestJava7Types extends BaseMapTest
+{
+ public void testPathRoundtrip() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper();
+
+ FileSystem fs = Jimfs.newFileSystem(Configuration.unix());
+ Path input = fs.getPath("/tmp", "foo.txt");
+
+ String json = mapper.writeValueAsString(input);
+ assertNotNull(json);
+
+ Path p = mapper.readValue(json, Path.class);
+ assertNotNull(p);
+
+ assertEquals(input.toUri(), p.toUri());
+ assertEquals(input, p);
+ fs.close();
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ext/TestJdk7Types.java b/src/test/java/com/fasterxml/jackson/databind/ext/TestJdk7Types.java
deleted file mode 100644
index 1b15f33..0000000
--- a/src/test/java/com/fasterxml/jackson/databind/ext/TestJdk7Types.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.fasterxml.jackson.databind.ext;
-
-import java.nio.file.Path;
-import java.nio.file.Paths;
-
-import com.fasterxml.jackson.databind.*;
-
-/**
- * @since 2.7
- */
-public class TestJdk7Types extends BaseMapTest
-{
- public void testPathRoundtrip() throws Exception
- {
- ObjectMapper mapper = new ObjectMapper();
-
- // Start with serialization, actually
- Path input = Paths.get("tmp", "foo.txt");
-
- String json = mapper.writeValueAsString(input);
- assertNotNull(json);
-
- Path p = mapper.readValue(json, Path.class);
- assertNotNull(p);
-
- assertEquals(input.toUri(), p.toUri());
- }
-}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ext/TestSOAP.java b/src/test/java/com/fasterxml/jackson/databind/ext/TestSOAP.java
deleted file mode 100644
index eb78c19..0000000
--- a/src/test/java/com/fasterxml/jackson/databind/ext/TestSOAP.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.fasterxml.jackson.databind.ext;
-
-import javax.xml.soap.Detail;
-import javax.xml.soap.SOAPException;
-import javax.xml.soap.SOAPFactory;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-public class TestSOAP extends com.fasterxml.jackson.databind.BaseMapTest {
-
- public void testSerializeSOAP() throws SOAPException, JsonProcessingException {
- ObjectMapper objectMapper = new ObjectMapper();
- SOAPFactory fac = SOAPFactory.newInstance();
- Detail detailElement = fac.createDetail();
- detailElement.setTextContent("test");
- String result = objectMapper.writer().writeValueAsString(detailElement);
- assertNotNull(result);
- }
-}
diff --git a/src/test/java/com/fasterxml/jackson/failing/IgnorePropertyOnDeser1217Test.java b/src/test/java/com/fasterxml/jackson/databind/filter/IgnorePropertyOnDeser1217Test.java
similarity index 67%
rename from src/test/java/com/fasterxml/jackson/failing/IgnorePropertyOnDeser1217Test.java
rename to src/test/java/com/fasterxml/jackson/databind/filter/IgnorePropertyOnDeser1217Test.java
index 7df823e..d42e4f5 100644
--- a/src/test/java/com/fasterxml/jackson/failing/IgnorePropertyOnDeser1217Test.java
+++ b/src/test/java/com/fasterxml/jackson/databind/filter/IgnorePropertyOnDeser1217Test.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.failing;
+package com.fasterxml.jackson.databind.filter;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.BaseMapTest;
@@ -19,6 +19,12 @@
public IgnoreObject obj2;
}
+ /*
+ /****************************************************************
+ /* Unit tests
+ /****************************************************************
+ */
+
private final ObjectMapper MAPPER = new ObjectMapper();
public void testIgnoreOnProperty() throws Exception
@@ -41,4 +47,15 @@
assertEquals(20, result1.obj2.x);
assertEquals(2, result1.obj2.y);
}
+
+ public void testIgnoreViaConfigOverride() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.configOverride(Point.class)
+ .setIgnorals(JsonIgnoreProperties.Value.forIgnoredProperties("y"));
+ Point p = mapper.readValue(aposToQuotes("{'x':1,'y':2}"), Point.class);
+ // bind 'x', but ignore 'y'
+ assertEquals(1, p.x);
+ assertEquals(0, p.y);
+ }
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/filter/IgnorePropsForSerTest.java b/src/test/java/com/fasterxml/jackson/databind/filter/IgnorePropsForSerTest.java
index d4064a6..a8342bd 100644
--- a/src/test/java/com/fasterxml/jackson/databind/filter/IgnorePropsForSerTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/filter/IgnorePropsForSerTest.java
@@ -3,7 +3,6 @@
import java.util.*;
import com.fasterxml.jackson.annotation.*;
-
import com.fasterxml.jackson.databind.*;
public class IgnorePropsForSerTest
@@ -122,4 +121,12 @@
assertEquals("{\"value\":{\"y\":2}}",
MAPPER.writeValueAsString(new WrapperWithPropIgnore2()));
}
+
+ public void testIgnoreViaConfigOverride() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.configOverride(Point.class)
+ .setIgnorals(JsonIgnoreProperties.Value.forIgnoredProperties("x"));
+ assertEquals("{\"y\":3}", mapper.writeValueAsString(new Point(2, 3)));
+ }
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/filter/JsonInclude1327Test.java b/src/test/java/com/fasterxml/jackson/databind/filter/JsonInclude1327Test.java
new file mode 100644
index 0000000..cf11a4d
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/filter/JsonInclude1327Test.java
@@ -0,0 +1,56 @@
+package com.fasterxml.jackson.databind.filter;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+/**
+ * Unit tests for checking that alternative settings for
+ * {@link JsonSerialize#include} annotation property work
+ * as expected.
+ */
+public class JsonInclude1327Test
+ extends BaseMapTest
+{
+ @JsonInclude(JsonInclude.Include.NON_EMPTY)
+ static class Issue1327BeanEmpty {
+ public List<String> myList = new ArrayList<String>();
+ }
+
+ static class Issue1327BeanAlways {
+ @JsonInclude(JsonInclude.Include.ALWAYS)
+ public List<String> myList = new ArrayList<String>();
+ }
+
+ /*
+ /**********************************************************
+ /* Unit tests
+ /**********************************************************
+ */
+
+ // for [databind#1327]
+ public void testClassDefaultsForEmpty() throws Exception {
+ ObjectMapper om = new ObjectMapper();
+ om.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+
+ final String jsonString = om.writeValueAsString(new Issue1327BeanEmpty());
+
+ if (jsonString.contains("myList")) {
+ fail("Should not contain `myList`: "+jsonString);
+ }
+ }
+
+ public void testClassDefaultsForAlways() throws Exception {
+ ObjectMapper om = new ObjectMapper();
+ om.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
+
+ final String jsonString = om.writeValueAsString(new Issue1327BeanAlways());
+
+ if (!jsonString.contains("myList")) {
+ fail("Should contain `myList` with Include.ALWAYS: "+jsonString);
+ }
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/filter/JsonIncludeTest.java b/src/test/java/com/fasterxml/jackson/databind/filter/JsonIncludeTest.java
index e5f44eb..2e51cfd 100644
--- a/src/test/java/com/fasterxml/jackson/databind/filter/JsonIncludeTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/filter/JsonIncludeTest.java
@@ -119,10 +119,12 @@
public NonEmptyDouble(double v) { value = v; }
}
- @JsonInclude(JsonInclude.Include.NON_EMPTY)
- public static class Issues1327Bean {
- public String myString = "stuff";
- public List<String> myList = new ArrayList<String>();
+ @JsonPropertyOrder({"list", "map"})
+ static class EmptyListMapBean
+ {
+ public List<String> list = Collections.emptyList();
+
+ public Map<String,String> map = Collections.emptyMap();
}
/*
@@ -238,15 +240,44 @@
assertEquals("{\"i\":0}", inclMapper.writeValueAsString(zero));
}
- public void testIssue1327() throws Exception {
- ObjectMapper om = new ObjectMapper();
- om.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+ public void testPropConfigOverridesForInclude() throws IOException
+ {
+ // First, with defaults, both included:
+ EmptyListMapBean empty = new EmptyListMapBean();
+ assertEquals(aposToQuotes("{'list':[],'map':{}}"),
+ MAPPER.writeValueAsString(empty));
+ ObjectMapper mapper;
- final Issues1327Bean input = new Issues1327Bean();
- final String jsonString = om.writeValueAsString(input);
+ // and then change inclusion criteria for either
+ mapper = new ObjectMapper();
+ mapper.configOverride(Map.class)
+ .setInclude(JsonInclude.Value.construct(JsonInclude.Include.NON_EMPTY, null));
+ assertEquals(aposToQuotes("{'list':[]}"),
+ mapper.writeValueAsString(empty));
- if (jsonString.contains("myList")) {
- fail("Should not contain `myList`: "+jsonString);
+ mapper = new ObjectMapper();
+ mapper.configOverride(List.class)
+ .setInclude(JsonInclude.Value.construct(JsonInclude.Include.NON_EMPTY, null));
+ assertEquals(aposToQuotes("{'map':{}}"),
+ mapper.writeValueAsString(empty));
+ }
+
+ static class Issue1351Bean
+ {
+ public final String first;
+ public final double second;
+
+ public Issue1351Bean(String first, double second) {
+ this.first = first;
+ this.second = second;
}
}
+
+ public void testIssue1351() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.setSerializationInclusion(JsonInclude.Include.NON_DEFAULT);
+ assertEquals(aposToQuotes("{}"),
+ mapper.writeValueAsString(new Issue1351Bean(null, (double) 0)));
+ }
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/filter/ProblemHandlerTest.java b/src/test/java/com/fasterxml/jackson/databind/filter/ProblemHandlerTest.java
new file mode 100644
index 0000000..f213843
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/filter/ProblemHandlerTest.java
@@ -0,0 +1,306 @@
+package com.fasterxml.jackson.databind.filter;
+
+import java.io.IOException;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler;
+import com.fasterxml.jackson.databind.exc.InvalidTypeIdException;
+import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
+
+/**
+ * Tests to exercise handler methods of {@link DeserializationProblemHandler}.
+ *
+ * @since 2.8
+ */
+public class ProblemHandlerTest extends BaseMapTest
+{
+ /*
+ /**********************************************************
+ /* Test handler types
+ /**********************************************************
+ */
+
+ static class WeirdKeyHandler
+ extends DeserializationProblemHandler
+ {
+ protected final Object key;
+
+ public WeirdKeyHandler(Object key0) {
+ key = key0;
+ }
+
+ @Override
+ public Object handleWeirdKey(DeserializationContext ctxt,
+ Class<?> rawKeyType, String keyValue,
+ String failureMsg)
+ throws IOException
+ {
+ return key;
+ }
+ }
+
+ static class WeirdNumberHandler
+ extends DeserializationProblemHandler
+ {
+ protected final Object value;
+
+ public WeirdNumberHandler(Object v0) {
+ value = v0;
+ }
+
+ @Override
+ public Object handleWeirdNumberValue(DeserializationContext ctxt,
+ Class<?> targetType, Number n,
+ String failureMsg)
+ throws IOException
+ {
+ return value;
+ }
+ }
+
+ static class WeirdStringHandler
+ extends DeserializationProblemHandler
+ {
+ protected final Object value;
+
+ public WeirdStringHandler(Object v0) {
+ value = v0;
+ }
+
+ @Override
+ public Object handleWeirdStringValue(DeserializationContext ctxt,
+ Class<?> targetType, String v,
+ String failureMsg)
+ throws IOException
+ {
+ return value;
+ }
+ }
+
+ static class InstantiationProblemHandler
+ extends DeserializationProblemHandler
+ {
+ protected final Object value;
+
+ public InstantiationProblemHandler(Object v0) {
+ value = v0;
+ }
+
+ @Override
+ public Object handleInstantiationProblem(DeserializationContext ctxt,
+ Class<?> instClass, Object argument, Throwable t)
+ throws IOException
+ {
+ return value;
+ }
+ }
+
+ static class MissingInstantiationHandler
+ extends DeserializationProblemHandler
+ {
+ protected final Object value;
+
+ public MissingInstantiationHandler(Object v0) {
+ value = v0;
+ }
+
+ @Override
+ public Object handleMissingInstantiator(DeserializationContext ctxt,
+ Class<?> instClass, JsonParser p, String msg)
+ throws IOException
+ {
+ p.skipChildren();
+ return value;
+ }
+ }
+
+ static class WeirdTokenHandler
+ extends DeserializationProblemHandler
+ {
+ protected final Object value;
+
+ public WeirdTokenHandler(Object v) {
+ value = v;
+ }
+
+ @Override
+ public Object handleUnexpectedToken(DeserializationContext ctxt,
+ Class<?> targetType, JsonToken t, JsonParser p,
+ String failureMsg)
+ throws IOException
+ {
+ return value;
+ }
+ }
+
+ static class TypeIdHandler
+ extends DeserializationProblemHandler
+ {
+ protected final Class<?> raw;
+
+ public TypeIdHandler(Class<?> r) { raw = r; }
+
+ @Override
+ public JavaType handleUnknownTypeId(DeserializationContext ctxt,
+ JavaType baseType, String subTypeId, TypeIdResolver idResolver,
+ String failureMsg)
+ throws IOException
+ {
+ return ctxt.constructType(raw);
+ }
+ }
+
+ /*
+ /**********************************************************
+ /* Other helper types
+ /**********************************************************
+ */
+
+ static class IntKeyMapWrapper {
+ public Map<Integer,String> stuff;
+ }
+
+ @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
+ static class Base { }
+ static class BaseImpl extends Base {
+ public int a;
+ }
+
+ static class BaseWrapper {
+ public Base value;
+ }
+
+ @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "clazz")
+ static class Base2 { }
+ static class Base2Impl extends Base2 {
+ public int a;
+ }
+
+ static class Base2Wrapper {
+ public Base2 value;
+ }
+
+ enum SingleValuedEnum {
+ A;
+ }
+
+ static class BustedCtor {
+ public final static BustedCtor INST = new BustedCtor(true);
+
+ public BustedCtor() {
+ throw new RuntimeException("Fail!");
+ }
+ private BustedCtor(boolean b) { }
+ }
+
+ static class NoDefaultCtor {
+ public int value;
+
+ public NoDefaultCtor(int v) { value = v; }
+ }
+
+ /*
+ /**********************************************************
+ /* Test methods
+ /**********************************************************
+ */
+
+ private final ObjectMapper MAPPER = new ObjectMapper();
+
+ public void testWeirdKeyHandling() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper()
+ .addHandler(new WeirdKeyHandler(7));
+ IntKeyMapWrapper w = mapper.readValue("{\"stuff\":{\"foo\":\"abc\"}}",
+ IntKeyMapWrapper.class);
+ Map<Integer,String> map = w.stuff;
+ assertEquals(1, map.size());
+ assertEquals("abc", map.values().iterator().next());
+ assertEquals(Integer.valueOf(7), map.keySet().iterator().next());
+ }
+
+ public void testWeirdNumberHandling() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper()
+ .addHandler(new WeirdNumberHandler(SingleValuedEnum.A))
+ ;
+ SingleValuedEnum result = mapper.readValue("3", SingleValuedEnum.class);
+ assertEquals(SingleValuedEnum.A, result);
+ }
+
+ public void testWeirdStringHandling() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper()
+ .addHandler(new WeirdStringHandler(SingleValuedEnum.A))
+ ;
+ SingleValuedEnum result = mapper.readValue("\"B\"", SingleValuedEnum.class);
+ assertEquals(SingleValuedEnum.A, result);
+ }
+
+ public void testInvalidTypeId() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper()
+ .addHandler(new TypeIdHandler(BaseImpl.class));
+ BaseWrapper w = mapper.readValue("{\"value\":{\"type\":\"foo\",\"a\":4}}",
+ BaseWrapper.class);
+ assertNotNull(w);
+ assertEquals(BaseImpl.class, w.value.getClass());
+ }
+
+
+ public void testInvalidClassAsId() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper()
+ .addHandler(new TypeIdHandler(Base2Impl.class));
+ Base2Wrapper w = mapper.readValue("{\"value\":{\"clazz\":\"com.fizz\",\"a\":4}}",
+ Base2Wrapper.class);
+ assertNotNull(w);
+ assertEquals(Base2Impl.class, w.value.getClass());
+ }
+
+ // verify that by default we get special exception type
+ public void testInvalidTypeIdFail() throws Exception
+ {
+ try {
+ MAPPER.readValue("{\"value\":{\"type\":\"foo\",\"a\":4}}",
+ BaseWrapper.class);
+ fail("Should not pass");
+ } catch (InvalidTypeIdException e) {
+ verifyException(e, "Could not resolve type id 'foo'");
+ assertEquals(Base.class, e.getBaseType().getRawClass());
+ assertEquals("foo", e.getTypeId());
+ }
+ }
+
+ public void testInstantiationExceptionHandling() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper()
+ .addHandler(new InstantiationProblemHandler(BustedCtor.INST));
+ BustedCtor w = mapper.readValue("{ }",
+ BustedCtor.class);
+ assertNotNull(w);
+ }
+
+ public void testMissingInstantiatorHandling() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper()
+ .addHandler(new MissingInstantiationHandler(new NoDefaultCtor(13)))
+ ;
+ NoDefaultCtor w = mapper.readValue("{ \"x\" : true }", NoDefaultCtor.class);
+ assertNotNull(w);
+ assertEquals(13, w.value);
+ }
+
+ public void testUnexpectedTokenHandling() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper()
+ .addHandler(new WeirdTokenHandler(Integer.valueOf(13)))
+ ;
+ Integer v = mapper.readValue("true", Integer.class);
+ assertEquals(Integer.valueOf(13), v);
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/filter/TestJsonFilter.java b/src/test/java/com/fasterxml/jackson/databind/filter/TestJsonFilter.java
index 5e81a8c..b607af7 100644
--- a/src/test/java/com/fasterxml/jackson/databind/filter/TestJsonFilter.java
+++ b/src/test/java/com/fasterxml/jackson/databind/filter/TestJsonFilter.java
@@ -2,8 +2,12 @@
import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonStreamContext;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.ser.FilterProvider;
+import com.fasterxml.jackson.databind.ser.PropertyWriter;
import com.fasterxml.jackson.databind.ser.impl.*;
/**
@@ -19,6 +23,46 @@
public String b = "b";
}
+ @JsonFilter("checkSiblingContextFilter")
+ static class CheckSiblingContextBean {
+ public A a = new A();
+ public B b = new B();
+ @JsonFilter("checkSiblingContextFilter")
+ static class A {
+ }
+ @JsonFilter("checkSiblingContextFilter")
+ static class B {
+ public C c = new C();
+ @JsonFilter("checkSiblingContextFilter")
+ static class C {
+ }
+ }
+ }
+
+ static class CheckSiblingContextFilter extends SimpleBeanPropertyFilter {
+ @Override
+ public void serializeAsField(Object bean, JsonGenerator jgen, SerializerProvider prov, PropertyWriter writer) throws Exception {
+ JsonStreamContext sc = jgen.getOutputContext();
+
+ if (writer.getName() != null && writer.getName().equals("c")) {
+ //This assertion is failing as sc.getParent() incorrectly returns 'a'. If you comment out the member 'a'
+ // in the CheckSiblingContextBean, you'll see that the sc.getParent() correctly returns 'b'
+ assertEquals("b", sc.getParent().getCurrentName());
+ }
+ writer.serializeAsField(bean, jgen, prov);
+ }
+ }
+
+ public void testCheckSiblingContextFilter() {
+ FilterProvider prov = new SimpleFilterProvider().addFilter("checkSiblingContextFilter",
+ new CheckSiblingContextFilter());
+
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.setFilterProvider(prov);
+ mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
+ mapper.valueToTree(new CheckSiblingContextBean());
+ }
+
// [Issue#89]
static class Pod
{
diff --git a/src/test/java/com/fasterxml/jackson/databind/filter/TestUnknownPropertyDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/filter/TestUnknownPropertyDeserialization.java
index 1689c87..b362822 100644
--- a/src/test/java/com/fasterxml/jackson/databind/filter/TestUnknownPropertyDeserialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/filter/TestUnknownPropertyDeserialization.java
@@ -42,7 +42,7 @@
* just marks unknown property/ies when encountered, along with
* Json value of the property.
*/
- final static class MyHandler
+ static class MyHandler
extends DeserializationProblemHandler
{
@Override
@@ -256,7 +256,8 @@
public void testPropertyIgnoralWithClass() throws Exception
{
- XYZWrapper2 result = MAPPER.readValue("{\"value\":{\"y\":2,\"x\":1,\"z\":3}}", XYZWrapper2.class);
+ XYZWrapper2 result = MAPPER.readValue("{\"value\":{\"y\":2,\"x\":1,\"z\":3}}",
+ XYZWrapper2.class);
assertEquals(1, result.value.x);
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/format/DateFormatTest.java b/src/test/java/com/fasterxml/jackson/databind/format/DateFormatTest.java
new file mode 100644
index 0000000..19b97f7
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/format/DateFormatTest.java
@@ -0,0 +1,37 @@
+package com.fasterxml.jackson.databind.format;
+
+import java.util.Calendar;
+import java.util.Date;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.databind.*;
+
+public class DateFormatTest extends BaseMapTest
+{
+ protected static class DateWrapper {
+ public Date value;
+
+ public DateWrapper() { }
+ public DateWrapper(long l) { value = new Date(l); }
+ public DateWrapper(Date v) { value = v; }
+ }
+
+ public void testTypeDefaults() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.configOverride(Date.class)
+ .setFormat(JsonFormat.Value.forPattern("yyyy.dd.MM"));
+ // First serialize, should result in this (in UTC):
+ String json = mapper.writeValueAsString(new DateWrapper(0L));
+ assertEquals(aposToQuotes("{'value':'1970.01.01'}"), json);
+
+ // and then read back
+ DateWrapper w = mapper.readValue(aposToQuotes("{'value':'1981.13.3'}"), DateWrapper.class);
+ assertNotNull(w);
+ // arbitrary TimeZone, but good enough to ensure year is right
+ Calendar c = Calendar.getInstance();
+ c.setTime(w.value);
+ assertEquals(1981, c.get(Calendar.YEAR));
+ assertEquals(Calendar.MARCH, c.get(Calendar.MONTH));
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/TestAutoDetect.java b/src/test/java/com/fasterxml/jackson/databind/introspect/TestAutoDetect.java
index b4a59a2..c5817bc 100644
--- a/src/test/java/com/fasterxml/jackson/databind/introspect/TestAutoDetect.java
+++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TestAutoDetect.java
@@ -32,7 +32,6 @@
// then by increasing visibility requirement:
m = new ObjectMapper();
- // note: clumsy code, but needed for Eclipse/JDK1.5 compilation (not for 1.6)
VisibilityChecker<?> vc = m.getVisibilityChecker();
vc = vc.withCreatorVisibility(JsonAutoDetect.Visibility.PUBLIC_ONLY);
m.setVisibility(vc);
@@ -40,7 +39,7 @@
m.readValue("\"abc\"", PrivateBean.class);
fail("Expected exception for missing constructor");
} catch (JsonProcessingException e) {
- verifyException(e, "no single-String constructor/factory");
+ verifyException(e, "no String-argument constructor/factory");
}
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/TestJacksonAnnotationIntrospector.java b/src/test/java/com/fasterxml/jackson/databind/introspect/TestJacksonAnnotationIntrospector.java
index bf56791..d506473 100644
--- a/src/test/java/com/fasterxml/jackson/databind/introspect/TestJacksonAnnotationIntrospector.java
+++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TestJacksonAnnotationIntrospector.java
@@ -137,11 +137,6 @@
private static final long serialVersionUID = 1L;
@Override
- public String findEnumValue(Enum<?> value) {
- return value.name().toLowerCase();
- }
-
- @Override
public String[] findEnumValues(Class<?> enumType, Enum<?>[] enumValues, String[] names) {
// kinda sorta wrong, but for testing's sake...
for (int i = 0, len = enumValues.length; i < len; ++i) {
diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/TestNamingStrategyStd.java b/src/test/java/com/fasterxml/jackson/databind/introspect/TestNamingStrategyStd.java
index 344a067..64f4860 100644
--- a/src/test/java/com/fasterxml/jackson/databind/introspect/TestNamingStrategyStd.java
+++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TestNamingStrategyStd.java
@@ -115,7 +115,7 @@
/**********************************************************
*/
- public static List<Object[]> SNAKE_CASE_NAME_TRANSLATIONS = Arrays.asList(new Object[][] {
+ final static List<Object[]> SNAKE_CASE_NAME_TRANSLATIONS = Arrays.asList(new Object[][] {
{null, null},
{"", ""},
{"a", "a"},
diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/TransientTest.java b/src/test/java/com/fasterxml/jackson/databind/introspect/TransientTest.java
index 484c753..882c399 100644
--- a/src/test/java/com/fasterxml/jackson/databind/introspect/TransientTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TransientTest.java
@@ -2,6 +2,7 @@
import java.beans.Transient;
+import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.databind.*;
@@ -22,6 +23,11 @@
public int getX() { return 42; }
}
+ static class SimplePrunableTransient {
+ public int a = 1;
+ public transient int b = 2;
+ }
+
// for [databind#857]
static class BeanTransient {
@Transient
@@ -30,6 +36,15 @@
public int getY() { return 4; }
}
+ // for [databind#1184]
+ static class OverridableTransient {
+ @JsonProperty
+// @JsonProperty("value") // should override transient here, to force inclusion
+ public transient int tValue;
+
+ public OverridableTransient(int v) { tValue = v; }
+ }
+
/*
/**********************************************************
/* Unit tests
@@ -44,6 +59,8 @@
// default handling: remove transient field but do not propagate
assertEquals(aposToQuotes("{'x':42,'value':3}"),
MAPPER.writeValueAsString(new ClassyTransient()));
+ assertEquals(aposToQuotes("{'a':1}"),
+ MAPPER.writeValueAsString(new SimplePrunableTransient()));
// but may change that
ObjectMapper m = new ObjectMapper()
@@ -58,4 +75,11 @@
assertEquals(aposToQuotes("{'y':4}"),
MAPPER.writeValueAsString(new BeanTransient()));
}
+
+ // for [databind#1184]
+ public void testOverridingTransient() throws Exception
+ {
+ assertEquals(aposToQuotes("{'tValue':38}"),
+ MAPPER.writeValueAsString(new OverridableTransient(38)));
+ }
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsonschema/NewSchemaTest.java b/src/test/java/com/fasterxml/jackson/databind/jsonschema/NewSchemaTest.java
index 57cefec..ea31611 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsonschema/NewSchemaTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsonschema/NewSchemaTest.java
@@ -100,7 +100,8 @@
public void testEnumWithJsonValue() throws Exception
{
final Set<String> values = new TreeSet<String>();
- MAPPER.acceptJsonFormatVisitor(TestEnumWithJsonValue.class, new JsonFormatVisitorWrapper.Base() {
+ MAPPER.acceptJsonFormatVisitor(TestEnumWithJsonValue.class,
+ new JsonFormatVisitorWrapper.Base() {
@Override
public JsonStringFormatVisitor expectStringFormat(JavaType type) {
return new JsonStringFormatVisitor() {
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsonschema/TestGenerateJsonSchema.java b/src/test/java/com/fasterxml/jackson/databind/jsonschema/TestGenerateJsonSchema.java
index ea086d6..7edfd4d 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsonschema/TestGenerateJsonSchema.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsonschema/TestGenerateJsonSchema.java
@@ -180,7 +180,7 @@
public void setObvious(String s) {obvious = s; }
}
- public static FilterProvider secretFilterProvider = new SimpleFilterProvider()
+ final static FilterProvider secretFilterProvider = new SimpleFilterProvider()
.addFilter("filteredBean", SimpleBeanPropertyFilter.filterOutAllExcept(new String[]{"obvious"}));
public void testGeneratingJsonSchemaWithFilters() throws Exception {
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestCustomTypeIdResolver.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestCustomTypeIdResolver.java
index 8fa9bd7..25ebfd5 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestCustomTypeIdResolver.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestCustomTypeIdResolver.java
@@ -5,7 +5,7 @@
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
-
+import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver;
import com.fasterxml.jackson.databind.jsontype.impl.TypeIdResolverBase;
@@ -102,7 +102,71 @@
return "xxx";
}
}
-
+
+ // for [databind#1270]
+ static class Top1270 {
+ @JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
+ include = JsonTypeInfo.As.PROPERTY,
+ property = "type")
+ @JsonTypeIdResolver(Resolver1270.class)
+ public Base1270<?> b;
+ }
+
+ static class Base1270<O extends Poly1Base> {
+ public O options;
+ public String val;
+ }
+
+ static abstract class Poly1Base { }
+
+ static class Poly1 extends Poly1Base {
+ public String val;
+ }
+
+ static class Resolver1270 implements TypeIdResolver {
+ public Resolver1270() { }
+
+ @Override
+ public void init(JavaType baseType) { }
+
+ @Override
+ public String idFromValue(Object value) {
+ if (value.getClass() == Base1270.class) {
+ return "poly1";
+ }
+ return null;
+ }
+
+ @Override
+ public String idFromValueAndType(Object value, Class<?> suggestedType) {
+ return idFromValue(value);
+ }
+
+ @Override
+ public String idFromBaseType() {
+ return null;
+ }
+
+ @Override
+ public JavaType typeFromId(DatabindContext context, String id) {
+ if ("poly1".equals(id)) {
+ return context.getTypeFactory()
+ .constructType(new TypeReference<Base1270<Poly1>>() { });
+ }
+ return null;
+ }
+
+ @Override
+ public String getDescForKnownTypeIds() {
+ return null;
+ }
+
+ @Override
+ public JsonTypeInfo.Id getMechanism() {
+ return JsonTypeInfo.Id.CUSTOM;
+ }
+ }
+
/*
/**********************************************************
/* Unit tests
@@ -110,8 +174,7 @@
*/
private final ObjectMapper MAPPER = objectMapper();
-
- // for [JACKSON-359]
+
public void testCustomTypeIdResolver() throws Exception
{
List<JavaType> types = new ArrayList<JavaType>();
@@ -143,4 +206,25 @@
assertEquals(12, ((ExtBeanImpl) out.value).y);
}
+
+ // for [databind#1270]
+ public void testPolymorphicTypeViaCustom() throws Exception {
+ Base1270<Poly1> req = new Base1270<Poly1>();
+ Poly1 o = new Poly1();
+ o.val = "optionValue";
+ req.options = o;
+ req.val = "some value";
+ Top1270 top = new Top1270();
+ top.b = req;
+ String json = MAPPER.writeValueAsString(top);
+ JsonNode tree = MAPPER.readTree(json);
+ assertNotNull(tree.get("b"));
+ assertNotNull(tree.get("b").get("options"));
+ assertNotNull(tree.get("b").get("options").get("val"));
+
+ // Can we reverse the process? I have some doubts
+ Top1270 itemRead = MAPPER.readValue(json, Top1270.class);
+ assertNotNull(itemRead);
+ assertNotNull(itemRead.b);
+ }
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForArrays.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForArrays.java
index f7ce743..154630d 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForArrays.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForArrays.java
@@ -1,5 +1,10 @@
package com.fasterxml.jackson.databind.jsontype;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
@@ -66,6 +71,24 @@
Object ob = result[0];
assertTrue(ob instanceof JsonNode);
}
+
+ public void testNodeInEmptyArray() throws Exception {
+ Map<String, List<String>> outerMap = new HashMap<String, List<String>>();
+ outerMap.put("inner", new ArrayList<String>());
+ ObjectMapper m = new ObjectMapper().disable(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS);
+ JsonNode tree = m.convertValue(outerMap, JsonNode.class);
+
+ String json = m.writeValueAsString(tree);
+ assertEquals("{}", json);
+
+ JsonNode node = new ObjectMapper().readTree("{\"a\":[]}");
+
+ m.enableDefaultTyping(DefaultTyping.JAVA_LANG_OBJECT);
+ Object[] obs = new Object[] { node };
+ json = m.writeValueAsString(obs);
+ Object[] result = m.readValue(json, Object[].class);
+ assertEquals("{}", result[0].toString());
+ }
// test for [JACKSON-845]
public void testArraysOfArrays() throws Exception
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestGenericListSerialization.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestGenericListSerialization.java
index dd096b4..7982ed2 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestGenericListSerialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestGenericListSerialization.java
@@ -63,7 +63,7 @@
JSONResponse<List<Parent>> out = mapper.readValue(json, 0, json.length, rootType);
- List<Parent> deserializedContent = (List<Parent>) out.getResult();
+ List<Parent> deserializedContent = out.getResult();
assertEquals(2, deserializedContent.size());
assertTrue(deserializedContent.get(0) instanceof Parent);
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestOverlappingTypeIdNames312.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestOverlappingTypeIdNames.java
similarity index 96%
rename from src/test/java/com/fasterxml/jackson/databind/jsontype/TestOverlappingTypeIdNames312.java
rename to src/test/java/com/fasterxml/jackson/databind/jsontype/TestOverlappingTypeIdNames.java
index d922852..3ef352c 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestOverlappingTypeIdNames312.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestOverlappingTypeIdNames.java
@@ -3,7 +3,7 @@
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.databind.*;
-public class TestOverlappingTypeIdNames312 extends BaseMapTest
+public class TestOverlappingTypeIdNames extends BaseMapTest
{
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPolymorphicWithDefaultImpl.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPolymorphicWithDefaultImpl.java
index 20d0ae7..36e6901 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPolymorphicWithDefaultImpl.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPolymorphicWithDefaultImpl.java
@@ -3,10 +3,8 @@
import java.util.*;
import com.fasterxml.jackson.annotation.*;
-import com.fasterxml.jackson.databind.BaseMapTest;
-import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.ObjectReader;
+
+import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.NoClass;
/**
@@ -123,6 +121,14 @@
public Event() {}
}
+ @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY,
+ property = "clazz")
+ abstract static class BaseClass { }
+
+ static class BaseWrapper {
+ public BaseClass value;
+ }
+
/*
/**********************************************************
/* Unit tests, deserialization
@@ -230,6 +236,17 @@
assertEquals("123", r3.application);
}
+ public void testUnknownClassAsSubtype() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false);
+ BaseWrapper w = mapper.readValue(aposToQuotes
+ ("{'value':{'clazz':'com.foobar.Nothing'}}'"),
+ BaseWrapper.class);
+ assertNotNull(w);
+ assertNull(w.value);
+ }
+
/*
/**********************************************************
/* Unit tests, serialization
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestSubtypes.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestSubtypes.java
index 3760ea9..a0ed794 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestSubtypes.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestSubtypes.java
@@ -133,7 +133,17 @@
def = def0;
}
}
-
+
+ // [databind#1311]
+ @JsonTypeInfo(property = "type", use = JsonTypeInfo.Id.NAME, defaultImpl = Factory1311ImplA.class)
+ interface Factory1311 { }
+
+ @JsonTypeName("implA")
+ static class Factory1311ImplA implements Factory1311 { }
+
+ @JsonTypeName("implB")
+ static class Factory1311ImplB implements Factory1311 { }
+
/*
/**********************************************************
/* Unit tests
@@ -141,8 +151,7 @@
*/
private final ObjectMapper MAPPER = new ObjectMapper();
-
- // JACKSON-510
+
public void testPropertyWithSubtypes() throws Exception
{
ObjectMapper mapper = new ObjectMapper();
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TypeRefinementForMapTest.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TypeRefinementForMapTest.java
index c810755..711c77c 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TypeRefinementForMapTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TypeRefinementForMapTest.java
@@ -54,10 +54,11 @@
public static final class TestClass {
@JsonProperty("mapProperty")
@JsonSerialize(keyUsing = CompoundKeySerializer.class)
+ @JsonDeserialize(keyUsing = CompoundKeyDeserializer.class)
private final Map<CompoundKey, String> mapProperty;
@JsonCreator
- private TestClass(@JsonDeserialize(keyUsing = CompoundKeyDeserializer.class) @JsonProperty("mapProperty") Map<CompoundKey, String> mapProperty) {
+ private TestClass(@JsonProperty("mapProperty") Map<CompoundKey, String> mapProperty) {
this.mapProperty = mapProperty;
}
}
diff --git a/src/test/java/com/fasterxml/jackson/failing/FailingSubTypeUnknownClass1098Test.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/UnknownSubClassTest.java
similarity index 86%
rename from src/test/java/com/fasterxml/jackson/failing/FailingSubTypeUnknownClass1098Test.java
rename to src/test/java/com/fasterxml/jackson/databind/jsontype/UnknownSubClassTest.java
index 9ec52a0..ba67098 100644
--- a/src/test/java/com/fasterxml/jackson/failing/FailingSubTypeUnknownClass1098Test.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/UnknownSubClassTest.java
@@ -1,10 +1,10 @@
-package com.fasterxml.jackson.failing;
+package com.fasterxml.jackson.databind.jsontype;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.*;
-public class FailingSubTypeUnknownClass1098Test extends BaseMapTest
+public class UnknownSubClassTest extends BaseMapTest
{
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY,
property = "clazz")
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/ExternalTypeId198Test.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/ExternalTypeId198Test.java
similarity index 60%
rename from src/test/java/com/fasterxml/jackson/databind/jsontype/ExternalTypeId198Test.java
rename to src/test/java/com/fasterxml/jackson/databind/jsontype/ext/ExternalTypeId198Test.java
index effef58..c34793f 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/ExternalTypeId198Test.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/ExternalTypeId198Test.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.jsontype;
+package com.fasterxml.jackson.databind.jsontype.ext;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonSubTypes;
@@ -33,38 +33,38 @@
}
public static class Kick extends Attack {
- @JsonCreator
- public Kick(String side) {
- super(side);
- }
+ @JsonCreator
+ public Kick(String side) {
+ super(side);
+ }
}
public static class Punch extends Attack {
- @JsonCreator
- public Punch(String side) {
- super(side);
- }
+ @JsonCreator
+ public Punch(String side) {
+ super(side);
+ }
}
final ObjectMapper MAPPER = new ObjectMapper();
public void testFails() throws Exception {
- String json = "{ \"name\": \"foo\", \"attack\":\"right\" } }";
+ String json = "{ \"name\": \"foo\", \"attack\":\"right\" } }";
- Character character = MAPPER.readValue(json, Character.class);
+ Character character = MAPPER.readValue(json, Character.class);
- assertNotNull(character);
- assertNotNull(character.attack);
- assertEquals("foo", character.name);
+ assertNotNull(character);
+ assertNotNull(character.attack);
+ assertEquals("foo", character.name);
}
public void testWorks() throws Exception {
- String json = "{ \"name\": \"foo\", \"preferredAttack\": \"KICK\", \"attack\":\"right\" } }";
+ String json = "{ \"name\": \"foo\", \"preferredAttack\": \"KICK\", \"attack\":\"right\" } }";
- Character character = MAPPER.readValue(json, Character.class);
+ Character character = MAPPER.readValue(json, Character.class);
- assertNotNull(character);
- assertNotNull(character.attack);
- assertEquals("foo", character.name);
+ assertNotNull(character);
+ assertNotNull(character.attack);
+ assertEquals("foo", character.name);
}
}
diff --git a/src/test/java/com/fasterxml/jackson/failing/ExternalTypeId999Test.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/ExternalTypeId999Test.java
similarity index 61%
rename from src/test/java/com/fasterxml/jackson/failing/ExternalTypeId999Test.java
rename to src/test/java/com/fasterxml/jackson/databind/jsontype/ext/ExternalTypeId999Test.java
index 1c7404f..a81f251 100644
--- a/src/test/java/com/fasterxml/jackson/failing/ExternalTypeId999Test.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/ExternalTypeId999Test.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.failing;
+package com.fasterxml.jackson.databind.jsontype.ext;
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.core.type.TypeReference;
@@ -18,7 +18,8 @@
{
final String type;
- @JsonTypeInfo(visible = true, use = JsonTypeInfo.Id.NAME,
+ @JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
+ visible = true,
include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(FooPayload.class),
@@ -29,24 +30,26 @@
public Message(@JsonProperty("type") String type,
@JsonProperty("payload") P payload)
{
- if (payload == null) {
- throw new RuntimeException("'payload' is null");
- }
- if (type == null) {
- throw new RuntimeException("'type' is null");
- }
this.type = type;
this.payload = payload;
}
}
+ private final ObjectMapper MAPPER = objectMapper();
public void testExternalTypeId() throws Exception
{
- ObjectMapper objectMapper = new ObjectMapper();
- Message<?> msg = objectMapper.readValue(
- "{ \"type\": \"foo\", \"payload\": {} }",
- new TypeReference<Message<FooPayload>>() { });
+ TypeReference<?> type = new TypeReference<Message<FooPayload>>() { };
+
+ Message<?> msg = MAPPER.readValue(aposToQuotes("{ 'type':'foo', 'payload': {} }"), type);
assertNotNull(msg);
+ assertNotNull(msg.payload);
+ assertEquals("foo", msg.type);
+
+ // and then with different order
+ msg = MAPPER.readValue(aposToQuotes("{'payload': {}, 'type':'foo' }"), type);
+ assertNotNull(msg);
+ assertNotNull(msg.payload);
+ assertEquals("foo", msg.type);
}
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestExternalId.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/ExternalTypeIdTest.java
similarity index 98%
rename from src/test/java/com/fasterxml/jackson/databind/jsontype/TestExternalId.java
rename to src/test/java/com/fasterxml/jackson/databind/jsontype/ext/ExternalTypeIdTest.java
index 7bdcd09..3c54a38 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestExternalId.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/ExternalTypeIdTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.jsontype;
+package com.fasterxml.jackson.databind.jsontype.ext;
import java.math.BigDecimal;
import java.util.*;
@@ -12,7 +12,7 @@
// Tests for External type id, one that exists at same level as typed Object,
// that is, property is not within typed object but a member of its parent.
-public class TestExternalId extends BaseMapTest
+public class ExternalTypeIdTest extends BaseMapTest
{
static class ExternalBean
{
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/ExternalTypeIdTest1288.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/ExternalTypeIdTest1288.java
index 7681899..d756d40 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/ExternalTypeIdTest1288.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/ExternalTypeIdTest1288.java
@@ -504,12 +504,10 @@
+ "\"csc\":666,\"address\":\"10 boulevard de Sebastopol\",\"zip_code\":\"75001\",\"city\":\"Paris\",\"province\":\"Ile-de-France\",\"country_code\":\"FR\",\"description\":\"John Doe personal credit card\"}}";
final String asJson2 = "{\"form_of_payment\":\"INSTRUMENTED_CREDIT_CARD\",\"payment_details\":{\"payment_instrument_id\":\"00000000-0000-0000-0000-000000000000\", \"name\":\"Mr John Doe encrypted credit card\"}}";
final ObjectMapper objectMapper = new ObjectMapper ().setPropertyNamingStrategy (PropertyNamingStrategy.SNAKE_CASE);
-// .disable (DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
ClassesWithoutBuilder.PaymentMean ob1 = objectMapper.readValue (asJson1, ClassesWithoutBuilder.PaymentMean.class);
assertNotNull(ob1);
ClassesWithBuilder.PaymentMean ob2 = objectMapper.readValue (asJson2, ClassesWithBuilder.PaymentMean.class);
assertNotNull(ob2);
-
}
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/TestSubtypesExternalPropertyMissingProperty.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/TestSubtypesExternalPropertyMissingProperty.java
new file mode 100644
index 0000000..ac414b3
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/TestSubtypesExternalPropertyMissingProperty.java
@@ -0,0 +1,259 @@
+package com.fasterxml.jackson.databind.jsontype.ext;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
+import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
+import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
+
+import com.fasterxml.jackson.databind.*;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+// for [databind#1341]
+public class TestSubtypesExternalPropertyMissingProperty
+{
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ /**
+ * Base class - external property for Fruit subclasses.
+ */
+ static class Box {
+ public String type;
+ @JsonTypeInfo(use = Id.NAME, include = As.EXTERNAL_PROPERTY, property = "type")
+ public Fruit fruit;
+
+ public Box() {
+ }
+
+ public Box(String type, Fruit fruit) {
+ this.type = type;
+ this.fruit = fruit;
+ }
+ }
+
+ /**
+ * Base class that requires the property to be present.
+ */
+ static class ReqBox {
+ public String type;
+ @JsonTypeInfo(use = Id.NAME, include = As.EXTERNAL_PROPERTY, property = "type")
+ @JsonProperty(required = true)
+ public Fruit fruit;
+
+ public ReqBox() {
+ }
+
+ public ReqBox(String type, Fruit fruit) {
+ this.type = type;
+ this.fruit = fruit;
+ }
+ }
+
+ @JsonSubTypes({
+ @Type(value = Apple.class, name = "apple"),
+ @Type(value = Orange.class, name = "orange")
+ })
+ static abstract class Fruit {
+ public String name;
+
+ public Fruit() {
+ }
+
+ protected Fruit(String n) {
+ name = n;
+ }
+ }
+
+ static class Apple extends Fruit {
+ public int seedCount;
+
+ public Apple() {
+ }
+
+ public Apple(String name, int b) {
+ super(name);
+ seedCount = b;
+ }
+ }
+
+ static class Orange extends Fruit {
+ public String color;
+
+ public Orange() {
+ }
+
+ public Orange(String name, String c) {
+ super(name);
+ color = c;
+ }
+ }
+
+ private final ObjectMapper MAPPER = new ObjectMapper();
+
+ /*
+ /**********************************************************
+ /* Mock data
+ /**********************************************************
+ */
+
+ private static final Orange orange = new Orange("Orange", "orange");
+ private static final Box orangeBox = new Box("orange", orange);
+ private static final String orangeBoxJson = "{\"type\":\"orange\",\"fruit\":{\"name\":\"Orange\",\"color\":\"orange\"}}";
+ private static final String orangeBoxNullJson = "{\"type\":\"orange\",\"fruit\":null}}";
+ private static final String orangeBoxEmptyJson = "{\"type\":\"orange\",\"fruit\":{}}}";
+ private static final String orangeBoxMissingJson = "{\"type\":\"orange\"}}";
+
+ private static final Apple apple = new Apple("Apple", 16);
+ private static Box appleBox = new Box("apple", apple);
+ private static final String appleBoxJson = "{\"type\":\"apple\",\"fruit\":{\"name\":\"Apple\",\"seedCount\":16}}";
+ private static final String appleBoxNullJson = "{\"type\":\"apple\",\"fruit\":null}";
+ private static final String appleBoxEmptyJson = "{\"type\":\"apple\",\"fruit\":{}}";
+ private static final String appleBoxMissingJson = "{\"type\":\"apple\"}";
+
+ /*
+ /**********************************************************
+ /* Unit tests
+ /**********************************************************
+ */
+
+ /**
+ * Deserialization tests for external type id property present
+ */
+ @Test
+ public void testDeserializationPresent() throws Exception {
+ MAPPER.disable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY);
+ checkOrangeBox();
+ checkAppleBox();
+
+ MAPPER.enable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY);
+ checkOrangeBox();
+ checkAppleBox();
+ }
+
+ /**
+ * Deserialization tests for external type id property null
+ */
+ @Test
+ public void testDeserializationNull() throws Exception {
+ MAPPER.disable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY);
+ checkOrangeBoxNull(orangeBoxNullJson);
+ checkAppleBoxNull(appleBoxNullJson);
+
+ MAPPER.enable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY);
+ checkOrangeBoxNull(orangeBoxNullJson);
+ checkAppleBoxNull(appleBoxNullJson);
+ }
+
+ /**
+ * Deserialization tests for external type id property empty
+ */
+ @Test
+ public void testDeserializationEmpty() throws Exception {
+ MAPPER.disable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY);
+ checkOrangeBoxEmpty(orangeBoxEmptyJson);
+ checkAppleBoxEmpty(appleBoxEmptyJson);
+
+ MAPPER.enable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY);
+ checkOrangeBoxEmpty(orangeBoxEmptyJson);
+ checkAppleBoxEmpty(appleBoxEmptyJson);
+ }
+
+ /**
+ * Deserialization tests for external type id property missing
+ */
+ @Test
+ public void testDeserializationMissing() throws Exception {
+ MAPPER.disable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY);
+ checkOrangeBoxNull(orangeBoxMissingJson);
+ checkAppleBoxNull(appleBoxMissingJson);
+
+ MAPPER.enable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY);
+ checkBoxJsonMappingException(orangeBoxMissingJson);
+ checkBoxJsonMappingException(appleBoxMissingJson);
+ }
+
+ /**
+ * Deserialization tests for external type id required property missing
+ */
+ @Test
+ public void testDeserializationMissingRequired() throws Exception {
+ MAPPER.disable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY);
+ checkReqBoxJsonMappingException(orangeBoxMissingJson);
+ checkReqBoxJsonMappingException(appleBoxMissingJson);
+
+ MAPPER.enable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY);
+ checkReqBoxJsonMappingException(orangeBoxMissingJson);
+ checkReqBoxJsonMappingException(appleBoxMissingJson);
+ }
+
+ private void checkOrangeBox() throws Exception {
+ Box deserOrangeBox = MAPPER.readValue(orangeBoxJson, Box.class);
+ assertEquals(orangeBox.type, deserOrangeBox.type);
+
+ Fruit deserOrange = deserOrangeBox.fruit;
+ assertSame(Orange.class, deserOrange.getClass());
+ assertEquals(orange.name, deserOrange.name);
+ assertEquals(orange.color, ((Orange) deserOrange).color);
+ }
+
+ private void checkAppleBox() throws Exception {
+ Box deserAppleBox = MAPPER.readValue(appleBoxJson, Box.class);
+ assertEquals(appleBox.type, deserAppleBox.type);
+
+ Fruit deserApple = deserAppleBox.fruit;
+ assertSame(Apple.class, deserApple.getClass());
+ assertEquals(apple.name, deserApple.name);
+ assertEquals(apple.seedCount, ((Apple) deserApple).seedCount);
+ }
+
+ private void checkOrangeBoxEmpty(String json) throws Exception {
+ Box deserOrangeBox = MAPPER.readValue(json, Box.class);
+ assertEquals(orangeBox.type, deserOrangeBox.type);
+
+ Fruit deserOrange = deserOrangeBox.fruit;
+ assertSame(Orange.class, deserOrange.getClass());
+ assertNull(deserOrange.name);
+ assertNull(((Orange) deserOrange).color);
+ }
+
+ private void checkAppleBoxEmpty(String json) throws Exception {
+ Box deserAppleBox = MAPPER.readValue(json, Box.class);
+ assertEquals(appleBox.type, deserAppleBox.type);
+
+ Fruit deserApple = deserAppleBox.fruit;
+ assertSame(Apple.class, deserApple.getClass());
+ assertNull(deserApple.name);
+ assertEquals(0, ((Apple) deserApple).seedCount);
+ }
+
+ private void checkOrangeBoxNull(String json) throws Exception {
+ Box deserOrangeBox = MAPPER.readValue(json, Box.class);
+ assertEquals(orangeBox.type, deserOrangeBox.type);
+ assertNull(deserOrangeBox.fruit);
+ }
+
+ private void checkAppleBoxNull(String json) throws Exception {
+ Box deserAppleBox = MAPPER.readValue(json, Box.class);
+ assertEquals(appleBox.type, deserAppleBox.type);
+ assertNull(deserAppleBox.fruit);
+ }
+
+ private void checkBoxJsonMappingException(String json) throws Exception {
+ thrown.expect(JsonMappingException.class);
+ thrown.expectMessage("Missing property 'fruit' for external type id 'type'");
+ MAPPER.readValue(json, Box.class);
+ }
+
+ private void checkReqBoxJsonMappingException(String json) throws Exception {
+ thrown.expect(JsonMappingException.class);
+ thrown.expectMessage("Missing property 'fruit' for external type id 'type'");
+ MAPPER.readValue(json, ReqBox.class);
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/misc/AccessFixTest.java b/src/test/java/com/fasterxml/jackson/databind/misc/AccessFixTest.java
index 6e7eb98..64c4ca5 100644
--- a/src/test/java/com/fasterxml/jackson/databind/misc/AccessFixTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/misc/AccessFixTest.java
@@ -14,7 +14,7 @@
@Override
public void checkPermission(Permission perm) throws SecurityException {
if ("suppressAccessChecks".equals(perm.getName())) {
-throw new SecurityException("Can not force permission: "+perm);
+ throw new SecurityException("Can not force permission: "+perm);
}
}
}
@@ -24,18 +24,18 @@
public void testCauseOfThrowableIgnoral() throws Exception
{
final SecurityManager origSecMan = System.getSecurityManager();
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.disable(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS);
try {
System.setSecurityManager(new CauseBlockingSecurityManager());
- _testCauseOfThrowableIgnoral();
+ _testCauseOfThrowableIgnoral(mapper);
} finally {
System.setSecurityManager(origSecMan);
}
}
- private void _testCauseOfThrowableIgnoral() throws Exception
+ private void _testCauseOfThrowableIgnoral(ObjectMapper mapper) throws Exception
{
- ObjectMapper mapper = new ObjectMapper();
- mapper.disable(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS);
IOException e = mapper.readValue("{}", IOException.class);
assertNotNull(e);
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/misc/CaseInsensitiveDeser.java b/src/test/java/com/fasterxml/jackson/databind/misc/CaseInsensitiveDeserTest.java
similarity index 89%
rename from src/test/java/com/fasterxml/jackson/databind/misc/CaseInsensitiveDeser.java
rename to src/test/java/com/fasterxml/jackson/databind/misc/CaseInsensitiveDeserTest.java
index 20a9fad..bf4c252 100644
--- a/src/test/java/com/fasterxml/jackson/databind/misc/CaseInsensitiveDeser.java
+++ b/src/test/java/com/fasterxml/jackson/databind/misc/CaseInsensitiveDeserTest.java
@@ -3,7 +3,7 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.*;
-public class CaseInsensitiveDeser extends BaseMapTest
+public class CaseInsensitiveDeserTest extends BaseMapTest
{
// [databind#1036]
static class BaseResponse {
@@ -45,7 +45,7 @@
// [databind#566]
public void testCaseInsensitiveDeserialization() throws Exception
{
- final String JSON = "{\"Value1\" : {\"nAme\" : \"fruit\", \"vALUe\" : \"apple\"}, \"valUE2\" : {\"NAME\" : \"color\", \"value\" : \"red\"}}";
+ final String JSON = "{\"Value1\" : {\"nAme\" : \"fruit\", \"vALUe\" : \"apple\"}, \"valUE2\" : {\"NAME\" : \"color\", \"value\" : \"red\"}}";
// first, verify default settings which do not accept improper case
ObjectMapper mapper = new ObjectMapper();
@@ -62,7 +62,7 @@
// Definitely not OK to enable dynamically - the BeanPropertyMap (which is the consumer of this particular feature) gets cached.
mapper = new ObjectMapper();
- mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
+ mapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);
ObjectReader r = mapper.readerFor(Issue476Bean.class);
Issue476Bean result = r.readValue(JSON);
assertEquals(result.value1.name, "fruit");
diff --git a/src/test/java/com/fasterxml/jackson/databind/misc/TestJSONP.java b/src/test/java/com/fasterxml/jackson/databind/misc/TestJSONP.java
index 4aa7ef9..24249ff 100644
--- a/src/test/java/com/fasterxml/jackson/databind/misc/TestJSONP.java
+++ b/src/test/java/com/fasterxml/jackson/databind/misc/TestJSONP.java
@@ -1,8 +1,10 @@
package com.fasterxml.jackson.databind.misc;
-import com.fasterxml.jackson.databind.*;
+import java.util.Arrays;
+import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.util.JSONPObject;
+import com.fasterxml.jackson.databind.util.JSONWrappedObject;
public class TestJSONP
extends BaseMapTest
@@ -18,23 +20,23 @@
this.b = b;
}
}
-
+
+ private final ObjectMapper MAPPER = new ObjectMapper();
+
public void testSimpleScalars() throws Exception
{
- ObjectMapper m = new ObjectMapper();
assertEquals("callback(\"abc\")",
- serializeAsString(m, new JSONPObject("callback", "abc")));
+ MAPPER.writeValueAsString(new JSONPObject("callback", "abc")));
assertEquals("calc(123)",
- serializeAsString(m, new JSONPObject("calc", Integer.valueOf(123))));
+ MAPPER.writeValueAsString(new JSONPObject("calc", Integer.valueOf(123))));
assertEquals("dummy(null)",
- serializeAsString(m, new JSONPObject("dummy", null)));
+ MAPPER.writeValueAsString(new JSONPObject("dummy", null)));
}
public void testSimpleBean() throws Exception
{
- ObjectMapper m = new ObjectMapper();
assertEquals("xxx({\"a\":\"123\",\"b\":\"456\"})",
- serializeAsString(m, new JSONPObject("xxx",
+ MAPPER.writeValueAsString(new JSONPObject("xxx",
new Impl("123", "456"))));
}
@@ -44,10 +46,16 @@
*/
public void testWithType() throws Exception
{
- ObjectMapper m = new ObjectMapper();
Object ob = new Impl("abc", "def");
- JavaType type = m.constructType(Base.class);
+ JavaType type = MAPPER.constructType(Base.class);
assertEquals("do({\"a\":\"abc\"})",
- serializeAsString(m, new JSONPObject("do", ob, type)));
+ MAPPER.writeValueAsString(new JSONPObject("do", ob, type)));
+ }
+
+ public void testGeneralWrapping() throws Exception
+ {
+ JSONWrappedObject input = new JSONWrappedObject("/*Foo*/", "\n// the end",
+ Arrays.asList());
+ assertEquals("/*Foo*/[]\n// the end", MAPPER.writeValueAsString(input));
}
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/module/TestAbstractTypes.java b/src/test/java/com/fasterxml/jackson/databind/module/TestAbstractTypes.java
index 5d126c0..f33daa7 100644
--- a/src/test/java/com/fasterxml/jackson/databind/module/TestAbstractTypes.java
+++ b/src/test/java/com/fasterxml/jackson/databind/module/TestAbstractTypes.java
@@ -9,12 +9,6 @@
public class TestAbstractTypes extends BaseMapTest
{
- /*
- /**********************************************************
- /* Helper classes; simple beans and their handlers
- /**********************************************************
- */
-
static class MyString implements CharSequence
{
protected String value;
@@ -34,10 +28,10 @@
@Override
public CharSequence subSequence(int arg0, int arg1) { return this; }
}
-
+
/*
/**********************************************************
- /* Unit tests
+ /* Test methods
/**********************************************************
*/
diff --git a/src/test/java/com/fasterxml/jackson/databind/module/TestCustomEnumKeyDeserializer.java b/src/test/java/com/fasterxml/jackson/databind/module/TestCustomEnumKeyDeserializer.java
index 1900a17..14757fb 100644
--- a/src/test/java/com/fasterxml/jackson/databind/module/TestCustomEnumKeyDeserializer.java
+++ b/src/test/java/com/fasterxml/jackson/databind/module/TestCustomEnumKeyDeserializer.java
@@ -71,7 +71,7 @@
try {
return TestEnum.lookup(key);
} catch (IllegalArgumentException e) {
- throw ctxt.weirdKeyException(TestEnum.class, key, "Unknown code");
+ return ctxt.handleWeirdKey(TestEnum.class, key, "Unknown code");
}
}
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/NotANumberConversionTest.java b/src/test/java/com/fasterxml/jackson/databind/node/NotANumberConversionTest.java
new file mode 100644
index 0000000..3fcb71d
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/node/NotANumberConversionTest.java
@@ -0,0 +1,41 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.math.BigDecimal;
+
+import com.fasterxml.jackson.databind.*;
+
+public class NotANumberConversionTest extends BaseMapTest
+{
+ private final ObjectMapper m = new ObjectMapper();
+ {
+ m.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
+ }
+
+ public void testBigDecimalWithNaN() throws Exception
+ {
+ JsonNode tree = m.valueToTree(new DoubleWrapper(Double.NaN));
+ assertNotNull(tree);
+ String json = m.writeValueAsString(tree);
+ assertNotNull(json);
+
+ tree = m.valueToTree(new DoubleWrapper(Double.NEGATIVE_INFINITY));
+ assertNotNull(tree);
+ json = m.writeValueAsString(tree);
+ assertNotNull(json);
+
+ tree = m.valueToTree(new DoubleWrapper(Double.POSITIVE_INFINITY));
+ assertNotNull(tree);
+ json = m.writeValueAsString(tree);
+ assertNotNull(json);
+ }
+
+ // for [databind#1315]: no accidental coercion to DoubleNode
+ public void testBigDecimalWithoutNaN() throws Exception
+ {
+ BigDecimal input = new BigDecimal(Double.MIN_VALUE).divide(new BigDecimal(10L));
+ JsonNode tree = m.readTree(input.toString());
+ assertTrue(tree.isBigDecimal());
+ BigDecimal output = tree.decimalValue();
+ assertEquals(input, output);
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TestEndOfInputHandling.java b/src/test/java/com/fasterxml/jackson/databind/node/TestEndOfInputHandling.java
new file mode 100644
index 0000000..a86baa1
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/node/TestEndOfInputHandling.java
@@ -0,0 +1,31 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.*;
+
+public class TestEndOfInputHandling extends BaseMapTest
+{
+ public void testErrorHandling() throws IOException {
+ ObjectMapper mapper = new ObjectMapper();
+
+ String json = "{\"A\":{\"B\":\n";
+ JsonParser parser = mapper.getFactory().createParser(json);
+ parser.setCodec(new ObjectMapper());
+ try {
+ parser.readValueAsTree();
+ } catch(JsonParseException e) {
+ verifyException(e, "Unexpected end-of-input");
+ }
+ parser.close();
+
+ try {
+ mapper.readTree(json);
+ }
+ catch(JsonParseException e) {
+ verifyException(e, "Unexpected end-of-input");
+ }
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/objectid/Objecid1083Test.java b/src/test/java/com/fasterxml/jackson/databind/objectid/Objecid1083Test.java
deleted file mode 100644
index 9bc5850..0000000
--- a/src/test/java/com/fasterxml/jackson/databind/objectid/Objecid1083Test.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.fasterxml.jackson.databind.objectid;
-
-import java.util.*;
-
-import com.fasterxml.jackson.annotation.*;
-
-import com.fasterxml.jackson.databind.*;
-
-public class Objecid1083Test extends BaseMapTest
-{
- public static class JsonRoot {
- public final List<JsonSchema> schemas = new ArrayList<JsonSchema>();
- }
-
- @JsonTypeInfo(
- use = JsonTypeInfo.Id.NAME,
- property = "type",
- defaultImpl = JsonMapSchema.class)
- @JsonSubTypes({
- @JsonSubTypes.Type(value = JsonMapSchema.class, name = "map"),
- @JsonSubTypes.Type(value = JsonJdbcSchema.class, name = "jdbc") })
- public static abstract class JsonSchema {
- public String name;
- }
-
- static class JsonMapSchema extends JsonSchema { }
-
- static class JsonJdbcSchema extends JsonSchema { }
-
- /*
- /*****************************************************
- /* Unit tests, external id deserialization
- /*****************************************************
- */
-
- public void testSimple() throws Exception {
- final ObjectMapper mapper = new ObjectMapper();
- final String json = aposToQuotes("{'schemas': [{\n"
- + " 'name': 'FoodMart'\n"
- + "}]}\n");
- mapper.readValue(json, JsonRoot.class);
- }
-}
diff --git a/src/test/java/com/fasterxml/jackson/databind/objectid/ObjectWithCreator1261Test.java b/src/test/java/com/fasterxml/jackson/databind/objectid/ObjectWithCreator1261Test.java
new file mode 100644
index 0000000..e673d73
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/objectid/ObjectWithCreator1261Test.java
@@ -0,0 +1,107 @@
+package com.fasterxml.jackson.databind.objectid;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.databind.*;
+
+public class ObjectWithCreator1261Test
+ extends BaseMapTest
+{
+ static class Answer
+ {
+ public SortedMap<String, Parent> parents;
+
+ public Answer() {
+ parents = new TreeMap<String, Parent>();
+ }
+ }
+
+ @JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "@id")
+ static class Parent
+ {
+ public Map<String, Child> children;
+
+ @JsonIdentityReference(alwaysAsId = true)
+ public Child favoriteChild;
+
+ public String name;
+
+ protected Parent() { }
+
+ public Parent(String name, boolean ignored) {
+ children = new TreeMap<String, Child>();
+ this.name = name;
+ }
+ }
+
+ @JsonInclude(JsonInclude.Include.NON_NULL)
+ @JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "@id")
+ static class Child
+ {
+ public String name;
+
+ @JsonIdentityReference(alwaysAsId = true)
+ public Parent parent;
+
+ @JsonIdentityReference(alwaysAsId = true)
+ public List<Parent> parentAsList;
+
+ public String someNullProperty;
+
+ protected Child() { }
+
+ @JsonCreator
+ public Child(@JsonProperty("name") String name,
+ @JsonProperty("someNullProperty") String someNullProperty) {
+ this.name = name;
+ this.someNullProperty = someNullProperty;
+ }
+
+ public Child(String n) {
+ name = n;
+ }
+ }
+
+ public void testObjectIds1261() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.enable(SerializationFeature.INDENT_OUTPUT);
+ mapper.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);
+
+ Answer initialAnswer = createInitialAnswer();
+ String initialAnswerString = mapper.writeValueAsString(initialAnswer);
+// System.out.println("Initial answer:\n"+initialAnswerString);
+ JsonNode tree = mapper.readTree(initialAnswerString);
+ Answer deserializedAnswer = mapper.readValue(initialAnswerString,
+ Answer.class);
+ String reserializedAnswerString = mapper
+ .writeValueAsString(deserializedAnswer);
+ JsonNode newTree = mapper.readTree(reserializedAnswerString);
+ if (!tree.equals(newTree)) {
+ fail("Original and recovered Json are different. Recovered = \n"
+ + reserializedAnswerString + "\n");
+ }
+ }
+
+ private Answer createInitialAnswer() {
+ Answer answer = new Answer();
+ String child1Name = "child1";
+ String child2Name = "child2";
+ String parent1Name = "parent1";
+ String parent2Name = "parent2";
+ Parent parent1 = new Parent(parent1Name, false);
+ answer.parents.put(parent1Name, parent1);
+ Child child1 = new Child(child1Name);
+ child1.parent = parent1;
+ child1.parentAsList = Collections.singletonList(parent1);
+ Child child2 = new Child(child2Name);
+ Parent parent2 = new Parent(parent2Name, false);
+ child2.parent = parent2;
+ child2.parentAsList = Collections.singletonList(parent2);
+ parent1.children.put(child1Name, child1);
+ parent1.children.put(child2Name, child2);
+ answer.parents.put(parent2Name, parent2);
+ return answer;
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectId.java b/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectId.java
index f66de3e..a51776c 100644
--- a/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectId.java
+++ b/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectId.java
@@ -102,6 +102,22 @@
public Foo next;
}
+ // for [databind#1083]
+ @JsonTypeInfo(
+ use = JsonTypeInfo.Id.NAME,
+ property = "type",
+ defaultImpl = JsonMapSchema.class)
+ @JsonSubTypes({
+ @JsonSubTypes.Type(value = JsonMapSchema.class, name = "map"),
+ @JsonSubTypes.Type(value = JsonJdbcSchema.class, name = "jdbc") })
+ public static abstract class JsonSchema {
+ public String name;
+ }
+
+ static class JsonMapSchema extends JsonSchema { }
+
+ static class JsonJdbcSchema extends JsonSchema { }
+
/*
/**********************************************************
/* Test methods
@@ -171,5 +187,16 @@
Foo second = (Foo) first.next;
assertNotNull(second.ref);
assertSame(first, second.ref);
- }
+ }
+
+ public static class JsonRoot {
+ public final List<JsonSchema> schemas = new ArrayList<JsonSchema>();
+ }
+
+ public void testWithFieldsInBaseClass1083() throws Exception {
+ final String json = aposToQuotes("{'schemas': [{\n"
+ + " 'name': 'FoodMart'\n"
+ + "}]}\n");
+ MAPPER.readValue(json, JsonRoot.class);
+ }
}
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestObjectIdWithInjectables538.java b/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdWithInjectables538.java
similarity index 92%
rename from src/test/java/com/fasterxml/jackson/failing/TestObjectIdWithInjectables538.java
rename to src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdWithInjectables538.java
index d2a552d..9f94257 100644
--- a/src/test/java/com/fasterxml/jackson/failing/TestObjectIdWithInjectables538.java
+++ b/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdWithInjectables538.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.failing;
+package com.fasterxml.jackson.databind.objectid;
import com.fasterxml.jackson.annotation.*;
@@ -18,6 +18,7 @@
public static class B {
public A a;
+ @JsonCreator
public B(@JacksonInject("i2") String injected) {
}
}
@@ -50,8 +51,8 @@
throw new IllegalStateException("Failed to deserialize from JSON '"+json+"'", e);
}
assertNotNull(output);
-
assertNotNull(output.b);
+ assertSame(output, output.b.a);
}
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/NumberSerTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/NumberSerTest.java
index c0870be..7d573d6 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/NumberSerTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/NumberSerTest.java
@@ -15,6 +15,12 @@
{
private final ObjectMapper MAPPER = objectMapper();
+ static class IntWrapper {
+ public int i;
+
+ public IntWrapper(int value) { i = value; }
+ }
+
static class IntAsString {
@JsonFormat(shape=JsonFormat.Shape.STRING)
@JsonProperty("value")
@@ -72,4 +78,13 @@
assertEquals(aposToQuotes("{'value':'4'}"), MAPPER.writeValueAsString(new LongAsString()));
assertEquals(aposToQuotes("{'value':'-0.5'}"), MAPPER.writeValueAsString(new DoubleAsString()));
}
+
+ public void testConfigOverridesForNumbers() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.configOverride(Integer.TYPE) // for `int`
+ .setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.STRING));
+ assertEquals(aposToQuotes("{'i':'3'}"),
+ mapper.writeValueAsString(new IntWrapper(3)));
+ }
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestFeatures.java b/src/test/java/com/fasterxml/jackson/databind/ser/SerializationFeaturesTest.java
similarity index 91%
rename from src/test/java/com/fasterxml/jackson/databind/ser/TestFeatures.java
rename to src/test/java/com/fasterxml/jackson/databind/ser/SerializationFeaturesTest.java
index 3a6e151..e408561 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestFeatures.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/SerializationFeaturesTest.java
@@ -3,25 +3,21 @@
import java.io.*;
import java.util.*;
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.*;
-import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+import com.fasterxml.jackson.core.JsonGenerator;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
+
/**
- * Unit tests for checking whether JsonSerializerFactory.Feature
- * configuration works
+ * Unit tests for checking handling of some of {@link MapperFeature}s
+ * and {@link SerializationFeature}s for serialization.
*/
-public class TestFeatures
+public class SerializationFeaturesTest
extends BaseMapTest
{
- /*
- /**********************************************************
- /* Helper classes
- /**********************************************************
- */
-
/**
* Class with one explicitly defined getter, one name-based
* auto-detectable getter.
@@ -86,7 +82,19 @@
public StringListBean(Collection<String> v) { values = v; }
}
-
+
+ static class TCls {
+ @JsonProperty("groupname")
+ private String groupname;
+
+ public void setName(String str) {
+ this.groupname = str;
+ }
+ public String getName() {
+ return groupname;
+ }
+ }
+
/*
/**********************************************************
/* Test methods
@@ -193,49 +201,47 @@
assertTrue(mapper.getSerializationConfig().isEnabled(SerializationFeature.FLUSH_AFTER_WRITE_VALUE));
// default is to flush after writeValue()
StringWriter sw = new StringWriter();
- JsonGenerator jgen = mapper.getFactory().createGenerator(sw);
- mapper.writeValue(jgen, Integer.valueOf(13));
+ JsonGenerator g = mapper.getFactory().createGenerator(sw);
+ mapper.writeValue(g, Integer.valueOf(13));
assertEquals("13", sw.toString());
- jgen.close();
+ g.close();
// ditto with ObjectWriter
sw = new StringWriter();
- jgen = mapper.getFactory().createGenerator(sw);
+ g = mapper.getFactory().createGenerator(sw);
ObjectWriter ow = mapper.writer();
- ow.writeValue(jgen, Integer.valueOf(99));
+ ow.writeValue(g, Integer.valueOf(99));
assertEquals("99", sw.toString());
- jgen.close();
+ g.close();
}
- // Test for [JACKSON-401]
public void testFlushingNotAutomatic() throws IOException
{
// but should not occur if configured otherwise
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.FLUSH_AFTER_WRITE_VALUE, false);
StringWriter sw = new StringWriter();
- JsonGenerator jgen = mapper.getFactory().createGenerator(sw);
+ JsonGenerator g = mapper.getFactory().createGenerator(sw);
- mapper.writeValue(jgen, Integer.valueOf(13));
+ mapper.writeValue(g, Integer.valueOf(13));
// no flushing now:
assertEquals("", sw.toString());
// except when actually flushing
- jgen.flush();
+ g.flush();
assertEquals("13", sw.toString());
- jgen.close();
+ g.close();
// Also, same should happen with ObjectWriter
sw = new StringWriter();
- jgen = mapper.getFactory().createGenerator(sw);
+ g = mapper.getFactory().createGenerator(sw);
ObjectWriter ow = mapper.writer();
- ow.writeValue(jgen, Integer.valueOf(99));
+ ow.writeValue(g, Integer.valueOf(99));
assertEquals("", sw.toString());
// except when actually flushing
- jgen.flush();
+ g.flush();
assertEquals("99", sw.toString());
- jgen.close();
+ g.close();
}
- // Test for [JACKSON-805]
public void testSingleElementCollections() throws IOException
{
final ObjectWriter writer = objectWriter().with(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED);
@@ -265,19 +271,6 @@
assertEquals("true", writer.writeValueAsString(new Boolean[] { Boolean.TRUE }));
assertEquals("3", writer.writeValueAsString(new int[] { 3 }));
assertEquals(quote("foo"), writer.writeValueAsString(new String[] { "foo" }));
-
- }
-
- static class TCls {
- @JsonProperty("groupname")
- private String groupname;
-
- public void setName(String str) {
- this.groupname = str;
- }
- public String getName() {
- return groupname;
- }
}
public void testVisibilityFeatures() throws Exception
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestExceptionSerialization.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestExceptionSerialization.java
index f056ab2..ee68983 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestExceptionSerialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestExceptionSerialization.java
@@ -1,7 +1,9 @@
package com.fasterxml.jackson.databind.ser;
+import java.io.IOException;
import java.util.*;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.*;
/**
@@ -10,17 +12,41 @@
public class TestExceptionSerialization
extends BaseMapTest
{
+ @SuppressWarnings("serial")
+ @JsonIgnoreProperties({ "bogus1" })
+ static class ExceptionWithIgnoral extends RuntimeException
+ {
+ public int bogus1 = 3;
+
+ public int bogus2 = 5;
+
+ protected ExceptionWithIgnoral() { }
+ public ExceptionWithIgnoral(String msg) {
+ super(msg);
+ }
+ }
+
+ // [databind#1368]
+ static class NoSerdeConstructor {
+ private String strVal;
+ public String getVal() { return strVal; }
+ public NoSerdeConstructor( String strVal ) {
+ this.strVal = strVal;
+ }
+ }
+
/*
/**********************************************************
/* Tests
/**********************************************************
*/
+ private final ObjectMapper MAPPER = new ObjectMapper();
+
public void testSimple() throws Exception
{
- ObjectMapper mapper = new ObjectMapper();
String TEST = "test exception";
- Map<String,Object> result = writeAndMap(mapper, new Exception(TEST));
+ Map<String,Object> result = writeAndMap(MAPPER, new Exception(TEST));
// JDK 7 has introduced a new property 'suppressed' to Throwable
Object ob = result.get("suppressed");
if (ob != null) {
@@ -39,4 +65,60 @@
fail("Expected a List for exception member 'stackTrace', got: "+traces);
}
}
+
+ // for [databind#877]
+ @SuppressWarnings("unchecked")
+ public void testIgnorals() throws Exception
+ {
+ ExceptionWithIgnoral input = new ExceptionWithIgnoral("foobar");
+ input.initCause(new IOException("surprise!"));
+
+ // First, should ignore anything with class annotations
+ String json = MAPPER
+ .writerWithDefaultPrettyPrinter()
+ .writeValueAsString(input);
+
+ Map<String,Object> result = MAPPER.readValue(json, Map.class);
+ assertEquals("foobar", result.get("message"));
+
+ assertNull(result.get("bogus1"));
+ assertNotNull(result.get("bogus2"));
+
+ // and then also remova second property with config overrides
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.configOverride(ExceptionWithIgnoral.class)
+ .setIgnorals(JsonIgnoreProperties.Value.forIgnoredProperties("bogus2"));
+ String json2 = mapper
+ .writeValueAsString(new ExceptionWithIgnoral("foobar"));
+
+ Map<String,Object> result2 = mapper.readValue(json2, Map.class);
+ assertNull(result2.get("bogus1"));
+ assertNull(result2.get("bogus2"));
+
+ // and try to deserialize as well
+ ExceptionWithIgnoral output = mapper.readValue(json2, ExceptionWithIgnoral.class);
+ assertNotNull(output);
+ assertEquals("foobar", output.getMessage());
+ }
+
+ // [databind#1368]
+ public void testJsonMappingExceptionSerialization() throws IOException {
+ Exception e = null;
+ // cant deserialize due to unexpected constructor
+ try {
+ MAPPER.readValue( "{ \"val\": \"foo\" }", NoSerdeConstructor.class );
+ fail("Should not pass");
+ } catch (JsonMappingException e0) {
+ verifyException(e0, "no suitable constructor");
+ e = e0;
+ }
+ // but should be able to serialize new exception we got
+ String json = MAPPER.writeValueAsString(e);
+ JsonNode root = MAPPER.readTree(json);
+ String msg = root.path("message").asText();
+ String MATCH = "no suitable constructor";
+ if (!msg.contains(MATCH)) {
+ fail("Exception should contain '"+MATCH+"', does not: '"+msg+"'");
+ }
+ }
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/FormatFeaturesTest.java b/src/test/java/com/fasterxml/jackson/databind/struct/FormatFeaturesTest.java
index 3c3f642..87d5922 100644
--- a/src/test/java/com/fasterxml/jackson/databind/struct/FormatFeaturesTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/FormatFeaturesTest.java
@@ -26,7 +26,7 @@
public boolean[] bools = new boolean[] { true };
}
-
+
@JsonPropertyOrder( { "strings", "ints", "bools", "enums" })
static class WrapWriteWithCollections
{
@@ -47,16 +47,23 @@
public String[] values;
}
+ static class StringArrayNotAnnoted {
+ public String[] values;
+
+ protected StringArrayNotAnnoted() { }
+ public StringArrayNotAnnoted(String ... v) { values = v; }
+ }
+
static class IntArrayWrapper {
@JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
public int[] values;
}
+
static class LongArrayWrapper {
@JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
public long[] values;
}
-
static class StringListWrapper {
@JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
public List<String> values;
@@ -82,6 +89,24 @@
public String Name;
}
+ static class CaseInsensitiveRoleWrapper
+ {
+ @JsonFormat(with={ JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES })
+ public Role role;
+ }
+
+ static class SortedKeysMap {
+ @JsonFormat(with = JsonFormat.Feature.WRITE_SORTED_MAP_ENTRIES)
+ public Map<String,Integer> values = new LinkedHashMap<>();
+
+ protected SortedKeysMap() { }
+
+ public SortedKeysMap put(String key, int value) {
+ values.put(key, value);
+ return this;
+ }
+ }
+
/*
/**********************************************************
/* Test methods, writing with single-element unwrapping
@@ -105,6 +130,13 @@
assertEquals(aposToQuotes("{'strings':'a','ints':[1],'bools':[true]}"),
MAPPER.writer().without(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)
.writeValueAsString(new WrapWriteWithArrays()));
+
+ // And then without SerializationFeature but with config override:
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.configOverride(String[].class).setFormat(JsonFormat.Value.empty()
+ .withFeature(JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED));
+ assertEquals(aposToQuotes("{'values':'a'}"),
+ mapper.writeValueAsString(new StringArrayNotAnnoted("a")));
}
public void testWithCollectionTypes() throws Exception
@@ -137,6 +169,15 @@
assertNotNull(result.values);
assertEquals(1, result.values.length);
assertEquals("first", result.values[0]);
+
+ // and then without annotation, but with global override
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.configOverride(String[].class).setFormat(JsonFormat.Value.empty()
+ .withFeature(JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY));
+ StringArrayNotAnnoted result2 = mapper.readValue(json, StringArrayNotAnnoted.class);
+ assertNotNull(result2.values);
+ assertEquals(1, result2.values.length);
+ assertEquals("first", result2.values[0]);
}
public void testSingleIntArrayRead() throws Exception {
@@ -185,10 +226,29 @@
}
public void testSingleEnumSetRead() throws Exception {
- String json = aposToQuotes("{ 'values': 'B' }");
- EnumSetWrapper result = MAPPER.readValue(json, EnumSetWrapper.class);
+ EnumSetWrapper result = MAPPER.readValue(aposToQuotes("{ 'values': 'B' }"),
+ EnumSetWrapper.class);
assertNotNull(result.values);
assertEquals(1, result.values.size());
assertEquals(ABC.B, result.values.iterator().next());
}
+
+ // [databind#1232]: allow per-property case-insensitivity
+ public void testCaseInsensitive() throws Exception {
+ CaseInsensitiveRoleWrapper w = MAPPER.readValue
+ (aposToQuotes("{'role':{'id':'12','name':'Foo'}}"),
+ CaseInsensitiveRoleWrapper.class);
+ assertNotNull(w);
+ assertEquals("12", w.role.ID);
+ assertEquals("Foo", w.role.Name);
+ }
+
+ // [databind#1232]: allow forcing sorting on Map keys
+ public void testOrderedMaps() throws Exception {
+ SortedKeysMap map = new SortedKeysMap()
+ .put("b", 2)
+ .put("a", 1);
+ assertEquals(aposToQuotes("{'values':{'a':1,'b':2}}"),
+ MAPPER.writeValueAsString(map));
+ }
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/TestBackRefsWithPolymorphic.java b/src/test/java/com/fasterxml/jackson/databind/struct/TestBackRefsWithPolymorphic.java
index 6e631b7..33f3eca 100644
--- a/src/test/java/com/fasterxml/jackson/databind/struct/TestBackRefsWithPolymorphic.java
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestBackRefsWithPolymorphic.java
@@ -12,33 +12,6 @@
// Unit test for [JACKSON-890]
public class TestBackRefsWithPolymorphic extends BaseMapTest
{
- private final String CLASS_NAME = getClass().getName();
-
- // NOTE: order is arbitrary, test is fragile... has to work for now
- private final String JSON =
- "{\"@class\":\""+CLASS_NAME+"$PropertySheetImpl\",\"id\":0,\"properties\":{\"p1name\":{\"@class\":"
- +"\"" +CLASS_NAME+ "$StringPropertyImpl\",\"id\":0,\"name\":\"p1name\",\"value\":\"p1value\"},"
- +"\"p2name\":{\"@class\":\""+CLASS_NAME+"$StringPropertyImpl\",\"id\":0,"
- +"\"name\":\"p2name\",\"value\":\"p2value\"}}}";
-
- private final ObjectMapper MAPPER = new ObjectMapper();
-
- public void testDeserialize() throws IOException
- {
- PropertySheet input = MAPPER.readValue(JSON, PropertySheet.class);
- assertEquals(JSON, MAPPER.writeValueAsString(input));
- }
-
- public void testSerialize() throws IOException
- {
- PropertySheet sheet = new PropertySheetImpl();
-
- sheet.addProperty(new StringPropertyImpl("p1name", "p1value"));
- sheet.addProperty(new StringPropertyImpl("p2name", "p2value"));
- String actual = MAPPER.writeValueAsString(sheet);
- assertEquals(JSON, actual);
- }
-
@JsonPropertyOrder(alphabetic=true)
interface Entity
{
@@ -217,4 +190,36 @@
}
static class YetAnotherClass extends StringPropertyImpl { }
+
+ /*
+ /**********************************************************
+ /* Test methods
+ /**********************************************************
+ */
+
+ private final String CLASS_NAME = getClass().getName();
+ // NOTE: order is arbitrary, test is fragile... has to work for now
+ private final String JSON =
+ "{\"@class\":\""+CLASS_NAME+"$PropertySheetImpl\",\"id\":0,\"properties\":{\"p1name\":{\"@class\":"
+ +"\"" +CLASS_NAME+ "$StringPropertyImpl\",\"id\":0,\"name\":\"p1name\",\"value\":\"p1value\"},"
+ +"\"p2name\":{\"@class\":\""+CLASS_NAME+"$StringPropertyImpl\",\"id\":0,"
+ +"\"name\":\"p2name\",\"value\":\"p2value\"}}}";
+
+ private final ObjectMapper MAPPER = new ObjectMapper();
+
+ public void testDeserialize() throws IOException
+ {
+ PropertySheet input = MAPPER.readValue(JSON, PropertySheet.class);
+ assertEquals(JSON, MAPPER.writeValueAsString(input));
+ }
+
+ public void testSerialize() throws IOException
+ {
+ PropertySheet sheet = new PropertySheetImpl();
+
+ sheet.addProperty(new StringPropertyImpl("p1name", "p1value"));
+ sheet.addProperty(new StringPropertyImpl("p2name", "p2value"));
+ String actual = MAPPER.writeValueAsString(sheet);
+ assertEquals(JSON, actual);
+ }
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/TestFormatForCollections.java b/src/test/java/com/fasterxml/jackson/databind/struct/TestFormatForCollections.java
index 344f7e4..bd308b3 100644
--- a/src/test/java/com/fasterxml/jackson/databind/struct/TestFormatForCollections.java
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestFormatForCollections.java
@@ -10,7 +10,7 @@
public class TestFormatForCollections extends BaseMapTest
{
- // [issue#40]: Allow serialization 'as POJO' (resulting in JSON Object)
+ // [databind#40]: Allow serialization 'as POJO' (resulting in JSON Object)
@JsonPropertyOrder({ "size", "value" })
@JsonFormat(shape=Shape.OBJECT)
@JsonIgnoreProperties({ "empty" }) // from 'isEmpty()'
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/TestPOJOAsArray.java b/src/test/java/com/fasterxml/jackson/databind/struct/TestPOJOAsArray.java
index 81cfa35..7e8f091 100644
--- a/src/test/java/com/fasterxml/jackson/databind/struct/TestPOJOAsArray.java
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestPOJOAsArray.java
@@ -12,27 +12,38 @@
public class TestPOJOAsArray extends BaseMapTest
{
- static class Pojo
+ static class PojoAsArrayWrapper
{
@JsonFormat(shape=JsonFormat.Shape.ARRAY)
- public PojoValue value;
+ public PojoAsArray value;
- public Pojo() { }
- public Pojo(String name, int x, int y, boolean c) {
- value = new PojoValue(name, x, y, c);
+ public PojoAsArrayWrapper() { }
+ public PojoAsArrayWrapper(String name, int x, int y, boolean c) {
+ value = new PojoAsArray(name, x, y, c);
}
}
+ @JsonPropertyOrder(alphabetic=true)
+ static class NonAnnotatedXY {
+ public int x, y;
+
+ public NonAnnotatedXY() { }
+ public NonAnnotatedXY(int x0, int y0) {
+ x = x0;
+ y = y0;
+ }
+ }
+
// note: must be serialized/deserialized alphabetically; fields NOT declared in that order
@JsonPropertyOrder(alphabetic=true)
- static class PojoValue
+ static class PojoAsArray
{
public int x, y;
public String name;
public boolean complete;
- public PojoValue() { }
- public PojoValue(String name, int x, int y, boolean c) {
+ public PojoAsArray() { }
+ public PojoAsArray(String name, int x, int y, boolean c) {
this.name = name;
this.x = x;
this.y = y;
@@ -77,7 +88,6 @@
public int y = 2;
}
- // for [JACKSON-805]
@JsonFormat(shape=Shape.ARRAY)
static class SingleBean {
public String name = "foo";
@@ -115,7 +125,6 @@
}
}
-
/*
/*****************************************************
/* Basic tests
@@ -130,7 +139,7 @@
public void testReadSimplePropertyValue() throws Exception
{
String json = "{\"value\":[true,\"Foobar\",42,13]}";
- Pojo p = MAPPER.readValue(json, Pojo.class);
+ PojoAsArrayWrapper p = MAPPER.readValue(json, PojoAsArrayWrapper.class);
assertNotNull(p.value);
assertTrue(p.value.complete);
assertEquals("Foobar", p.value.name);
@@ -156,7 +165,7 @@
*/
public void testWriteSimplePropertyValue() throws Exception
{
- String json = MAPPER.writeValueAsString(new Pojo("Foobar", 42, 13, true));
+ String json = MAPPER.writeValueAsString(new PojoAsArrayWrapper("Foobar", 42, 13, true));
// will have wrapper POJO, then POJO-as-array..
assertEquals("{\"value\":[true,\"Foobar\",42,13]}", json);
}
@@ -215,6 +224,8 @@
ObjectMapper mapper2 = new ObjectMapper();
mapper2.setAnnotationIntrospector(new ForceArraysIntrospector());
assertEquals("[[1,2]]", mapper2.writeValueAsString(new A()));
+
+ // and allow reading back, too
}
public void testWithMaps() throws Exception
@@ -237,4 +248,18 @@
assertEquals(2, value._a);
assertEquals(1, value._b);
}
+
+ public void testWithConfigOverrides() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.configOverride(NonAnnotatedXY.class)
+ .setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.ARRAY));
+ String json = mapper.writeValueAsString(new NonAnnotatedXY(2, 3));
+ assertEquals("[2,3]", json);
+
+ // also, read it back
+ NonAnnotatedXY result = mapper.readValue(json, NonAnnotatedXY.class);
+ assertNotNull(result);
+ assertEquals(3, result.y);
+ }
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/type/TestJavaType.java b/src/test/java/com/fasterxml/jackson/databind/type/TestJavaType.java
index c773e83..91be3f7 100644
--- a/src/test/java/com/fasterxml/jackson/databind/type/TestJavaType.java
+++ b/src/test/java/com/fasterxml/jackson/databind/type/TestJavaType.java
@@ -36,6 +36,9 @@
public Map<String,String> getMap();
}
+ @SuppressWarnings("serial")
+ static class AtomicStringReference extends AtomicReference<String> { }
+
/*
/**********************************************************
/* Test methods
@@ -75,8 +78,11 @@
assertFalse(baseType.isEnumType());
assertFalse(baseType.isInterface());
assertFalse(baseType.isPrimitive());
+ assertFalse(baseType.isReferenceType());
+ assertFalse(baseType.hasContentType());
assertNull(baseType.getContentType());
+ assertNull(baseType.getKeyType());
assertNull(baseType.getValueHandler());
}
@@ -86,8 +92,12 @@
JavaType arrayT = ArrayType.construct(tf.constructType(String.class), null);
assertNotNull(arrayT);
assertTrue(arrayT.isContainerType());
+ assertFalse(arrayT.isReferenceType());
+ assertTrue(arrayT.hasContentType());
assertNotNull(arrayT.toString());
+ assertNotNull(arrayT.getContentType());
+ assertNull(arrayT.getKeyType());
assertTrue(arrayT.equals(arrayT));
assertFalse(arrayT.equals(null));
@@ -97,6 +107,23 @@
assertFalse(arrayT.equals(ArrayType.construct(tf.constructType(Integer.class), null)));
}
+ public void testMapType()
+ {
+ TypeFactory tf = TypeFactory.defaultInstance();
+ JavaType mapT = tf.constructType(HashMap.class);
+ assertTrue(mapT.isContainerType());
+ assertFalse(mapT.isReferenceType());
+ assertTrue(mapT.hasContentType());
+
+ assertNotNull(mapT.toString());
+ assertNotNull(mapT.getContentType());
+ assertNotNull(mapT.getKeyType());
+
+ assertTrue(mapT.equals(mapT));
+ assertFalse(mapT.equals(null));
+ assertFalse(mapT.equals("xyz"));
+ }
+
public void testEnumType()
{
TypeFactory tf = TypeFactory.defaultInstance();
@@ -147,4 +174,27 @@
t = tf.constructType(m.getGenericReturnType());
assertEquals("Ljava/util/concurrent/atomic/AtomicReference<Ljava/lang/String;>;", t.getGenericSignature());
}
+
+ public void testAnchorTypeForRefTypes() throws Exception
+ {
+ TypeFactory tf = TypeFactory.defaultInstance();
+ JavaType t = tf.constructType(AtomicStringReference.class);
+ assertTrue(t.isReferenceType());
+ assertTrue(t.hasContentType());
+ ReferenceType rt = (ReferenceType) t;
+ assertFalse(rt.isAnchorType());
+ assertEquals(AtomicReference.class, rt.getAnchorType().getRawClass());
+ }
+
+ // for [databind#1290]
+ public void testObjectToReferenceSpecialization() throws Exception
+ {
+ TypeFactory tf = TypeFactory.defaultInstance();
+ JavaType base = tf.constructType(Object.class);
+ assertTrue(base.isJavaLangObject());
+
+ JavaType sub = tf.constructSpecializedType(base, AtomicReference.class);
+ assertEquals(AtomicReference.class, sub.getRawClass());
+ assertTrue(sub.isReferenceType());
+ }
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/type/TestTypeBindings.java b/src/test/java/com/fasterxml/jackson/databind/type/TestTypeBindings.java
index f2b9e4a..7af37e8 100644
--- a/src/test/java/com/fasterxml/jackson/databind/type/TestTypeBindings.java
+++ b/src/test/java/com/fasterxml/jackson/databind/type/TestTypeBindings.java
@@ -29,14 +29,17 @@
}
}
}
-
+
+ // for [databind#76]
+ @SuppressWarnings("serial")
+ static class HashTree<K, V> extends HashMap<K, HashTree<K, V>> { }
+
/*
/**********************************************************
/* Test methods
/**********************************************************
*/
- // [JACKSON-677]
public void testInnerType() throws Exception
{
TypeFactory tf = TypeFactory.defaultInstance();
@@ -49,4 +52,12 @@
JavaType vt2 = valueType.getContentType();
assertEquals(Object.class, vt2.getRawClass());
}
+
+ // for [databind#76]
+ public void testRecursiveType()
+ {
+ TypeFactory tf = TypeFactory.defaultInstance();
+ JavaType type = tf.constructType(HashTree.class);
+ assertNotNull(type);
+ }
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/type/TestTypeFactory.java b/src/test/java/com/fasterxml/jackson/databind/type/TestTypeFactory.java
index e5183a0..dd075e5 100644
--- a/src/test/java/com/fasterxml/jackson/databind/type/TestTypeFactory.java
+++ b/src/test/java/com/fasterxml/jackson/databind/type/TestTypeFactory.java
@@ -70,7 +70,11 @@
static class CollectionLike<E> { }
static class MapLike<K,V> { }
-
+
+ static class Wrapper1297<T> {
+ public T content;
+ }
+
/*
/**********************************************************
/* Unit tests
@@ -565,5 +569,14 @@
tf.clearCache();
assertEquals(0, tf._typeCache.size());
}
+
+ // for [databind#1297]
+ public void testRawMapType()
+ {
+ TypeFactory tf = TypeFactory.defaultInstance().withModifier(null); // to get a new copy
+
+ JavaType type = tf.constructParametricType(Wrapper1297.class, Map.class);
+ assertNotNull(type);
+ assertEquals(Wrapper1297.class, type.getRawClass());
+ }
}
-
\ No newline at end of file
diff --git a/src/test/java/com/fasterxml/jackson/databind/util/TestTokenBuffer.java b/src/test/java/com/fasterxml/jackson/databind/util/TestTokenBuffer.java
index 4e14bbf..477301e 100644
--- a/src/test/java/com/fasterxml/jackson/databind/util/TestTokenBuffer.java
+++ b/src/test/java/com/fasterxml/jackson/databind/util/TestTokenBuffer.java
@@ -26,31 +26,46 @@
TokenBuffer buf = new TokenBuffer(null, false); // no ObjectCodec
// First, with empty buffer
- JsonParser jp = buf.asParser();
- assertNull(jp.getCurrentToken());
- assertNull(jp.nextToken());
- jp.close();
+ JsonParser p = buf.asParser();
+ assertNull(p.getCurrentToken());
+ assertNull(p.nextToken());
+ p.close();
// Then with simple text
buf.writeString("abc");
// First, simple text
- jp = buf.asParser();
- assertNull(jp.getCurrentToken());
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
- assertEquals("abc", jp.getText());
- assertNull(jp.nextToken());
- jp.close();
+ p = buf.asParser();
+ assertNull(p.getCurrentToken());
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals("abc", p.getText());
+ assertNull(p.nextToken());
+ p.close();
// Then, let's append at root level
buf.writeNumber(13);
- jp = buf.asParser();
- assertNull(jp.getCurrentToken());
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(13, jp.getIntValue());
- assertNull(jp.nextToken());
- jp.close();
+ p = buf.asParser();
+ assertNull(p.getCurrentToken());
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(13, p.getIntValue());
+ assertNull(p.nextToken());
+ p.close();
+ buf.close();
+ }
+
+ public void testParentContext() throws IOException
+ {
+ TokenBuffer buf = new TokenBuffer(null, false); // no ObjectCodec
+ buf.writeStartObject();
+ buf.writeFieldName("b");
+ buf.writeStartObject();
+ buf.writeFieldName("c");
+ //This assertion succeeds as expected
+ assertEquals("b", buf.getOutputContext().getParent().getCurrentName());
+ buf.writeString("cval");
+ buf.writeEndObject();
+ buf.writeEndObject();
buf.close();
}
@@ -65,15 +80,15 @@
buf.writeEndArray();
assertTrue(buf.getOutputContext().inRoot());
- JsonParser jp = buf.asParser();
- assertNull(jp.getCurrentToken());
- assertTrue(jp.getParsingContext().inRoot());
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- assertTrue(jp.getParsingContext().inArray());
- assertToken(JsonToken.END_ARRAY, jp.nextToken());
- assertTrue(jp.getParsingContext().inRoot());
- assertNull(jp.nextToken());
- jp.close();
+ JsonParser p = buf.asParser();
+ assertNull(p.getCurrentToken());
+ assertTrue(p.getParsingContext().inRoot());
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ assertTrue(p.getParsingContext().inArray());
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ assertTrue(p.getParsingContext().inRoot());
+ assertNull(p.nextToken());
+ p.close();
buf.close();
// Then one with simple contents
@@ -82,14 +97,14 @@
buf.writeBoolean(true);
buf.writeNull();
buf.writeEndArray();
- jp = buf.asParser();
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- assertToken(JsonToken.VALUE_TRUE, jp.nextToken());
- assertTrue(jp.getBooleanValue());
- assertToken(JsonToken.VALUE_NULL, jp.nextToken());
- assertToken(JsonToken.END_ARRAY, jp.nextToken());
- assertNull(jp.nextToken());
- jp.close();
+ p = buf.asParser();
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ assertToken(JsonToken.VALUE_TRUE, p.nextToken());
+ assertTrue(p.getBooleanValue());
+ assertToken(JsonToken.VALUE_NULL, p.nextToken());
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ assertNull(p.nextToken());
+ p.close();
buf.close();
// And finally, with array-in-array
@@ -99,19 +114,19 @@
buf.writeBinary(new byte[3]);
buf.writeEndArray();
buf.writeEndArray();
- jp = buf.asParser();
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
+ p = buf.asParser();
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
// TokenBuffer exposes it as embedded object...
- assertToken(JsonToken.VALUE_EMBEDDED_OBJECT, jp.nextToken());
- Object ob = jp.getEmbeddedObject();
+ assertToken(JsonToken.VALUE_EMBEDDED_OBJECT, p.nextToken());
+ Object ob = p.getEmbeddedObject();
assertNotNull(ob);
assertTrue(ob instanceof byte[]);
assertEquals(3, ((byte[]) ob).length);
- assertToken(JsonToken.END_ARRAY, jp.nextToken());
- assertToken(JsonToken.END_ARRAY, jp.nextToken());
- assertNull(jp.nextToken());
- jp.close();
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ assertNull(p.nextToken());
+ p.close();
buf.close();
}
@@ -126,15 +141,15 @@
buf.writeEndObject();
assertTrue(buf.getOutputContext().inRoot());
- JsonParser jp = buf.asParser();
- assertNull(jp.getCurrentToken());
- assertTrue(jp.getParsingContext().inRoot());
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
- assertTrue(jp.getParsingContext().inObject());
- assertToken(JsonToken.END_OBJECT, jp.nextToken());
- assertTrue(jp.getParsingContext().inRoot());
- assertNull(jp.nextToken());
- jp.close();
+ JsonParser p = buf.asParser();
+ assertNull(p.getCurrentToken());
+ assertTrue(p.getParsingContext().inRoot());
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ assertTrue(p.getParsingContext().inObject());
+ assertToken(JsonToken.END_OBJECT, p.nextToken());
+ assertTrue(p.getParsingContext().inRoot());
+ assertNull(p.nextToken());
+ p.close();
buf.close();
// Then one with simple contents
@@ -143,25 +158,25 @@
buf.writeNumberField("num", 1.25);
buf.writeEndObject();
- jp = buf.asParser();
- assertNull(jp.getCurrentToken());
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
- assertNull(jp.getCurrentName());
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- assertEquals("num", jp.getCurrentName());
+ p = buf.asParser();
+ assertNull(p.getCurrentToken());
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ assertNull(p.getCurrentName());
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("num", p.getCurrentName());
// and override should also work:
- jp.overrideCurrentName("bah");
- assertEquals("bah", jp.getCurrentName());
+ p.overrideCurrentName("bah");
+ assertEquals("bah", p.getCurrentName());
- assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
- assertEquals(1.25, jp.getDoubleValue());
+ assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+ assertEquals(1.25, p.getDoubleValue());
// should still have access to (overridden) name
- assertEquals("bah", jp.getCurrentName());
- assertToken(JsonToken.END_OBJECT, jp.nextToken());
+ assertEquals("bah", p.getCurrentName());
+ assertToken(JsonToken.END_OBJECT, p.nextToken());
// but not any more
- assertNull(jp.getCurrentName());
- assertNull(jp.nextToken());
- jp.close();
+ assertNull(p.getCurrentName());
+ assertNull(p.nextToken());
+ p.close();
buf.close();
}
@@ -172,10 +187,10 @@
public void testWithJSONSampleDoc() throws Exception
{
// First, copy events from known good source (StringReader)
- JsonParser jp = createParserUsingReader(SAMPLE_DOC_JSON_SPEC);
+ JsonParser p = createParserUsingReader(SAMPLE_DOC_JSON_SPEC);
TokenBuffer tb = new TokenBuffer(null, false);
- while (jp.nextToken() != null) {
- tb.copyCurrentEvent(jp);
+ while (p.nextToken() != null) {
+ tb.copyCurrentEvent(p);
}
// And then request verification; first structure only:
@@ -184,7 +199,7 @@
// then content check too:
verifyJsonSpecSampleDoc(tb.asParser(), true);
tb.close();
- jp.close();
+ p.close();
}
public void testAppend() throws IOException
@@ -202,17 +217,17 @@
buf1.append(buf2);
// and verify that we got it all...
- JsonParser jp = buf1.asParser();
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- assertEquals("a", jp.getCurrentName());
- assertToken(JsonToken.VALUE_TRUE, jp.nextToken());
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- assertEquals("b", jp.getCurrentName());
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(13, jp.getIntValue());
- assertToken(JsonToken.END_OBJECT, jp.nextToken());
- jp.close();
+ JsonParser p = buf1.asParser();
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("a", p.getCurrentName());
+ assertToken(JsonToken.VALUE_TRUE, p.nextToken());
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("b", p.getCurrentName());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(13, p.getIntValue());
+ assertToken(JsonToken.END_OBJECT, p.nextToken());
+ p.close();
buf1.close();
buf2.close();
}
@@ -239,11 +254,11 @@
assertEquals(uuid.toString(), out.toString());
// second part: As per [databind#362], should NOT use binary with TokenBuffer
- JsonParser jp = buf.asParser();
- assertEquals(JsonToken.VALUE_STRING, jp.nextToken());
- String str = jp.getText();
+ JsonParser p = buf.asParser();
+ assertEquals(JsonToken.VALUE_STRING, p.nextToken());
+ String str = p.getText();
assertEquals(value, str);
- jp.close();
+ p.close();
}
}
@@ -325,7 +340,7 @@
} else if (ctxt2 == null) {
fail("Context 2 null, context 1 not null: "+ctxt1);
}
- if (!ctxt1.getTypeDesc().equals(ctxt2.getTypeDesc())) {
+ if (!ctxt1.toString().equals(ctxt2.toString())) {
fail("Different output context: token-buffer's = "+ctxt1+", json-generator's: "+ctxt2);
}
@@ -344,6 +359,29 @@
_verifyOutputContext(ctxt1.getParent(), ctxt2.getParent());
}
+ // [databind#1253]
+ public void testParentSiblingContext() throws IOException
+ {
+ TokenBuffer buf = new TokenBuffer(null, false); // no ObjectCodec
+
+ // {"a":{},"b":{"c":"cval"}}
+
+ buf.writeStartObject();
+ buf.writeFieldName("a");
+ buf.writeStartObject();
+ buf.writeEndObject();
+
+ buf.writeFieldName("b");
+ buf.writeStartObject();
+ buf.writeFieldName("c");
+ //This assertion fails (because of 'a')
+ assertEquals("b", buf.getOutputContext().getParent().getCurrentName());
+ buf.writeString("cval");
+ buf.writeEndObject();
+ buf.writeEndObject();
+ buf.close();
+ }
+
/*
/**********************************************************
/* Tests to verify interaction of TokenBuffer and JsonParserSequence
@@ -356,12 +394,12 @@
TokenBuffer buf = new TokenBuffer(null, false);
buf.writeStartArray();
buf.writeString("test");
- JsonParser jp = createParserUsingReader("[ true, null ]");
+ JsonParser p = createParserUsingReader("[ true, null ]");
- JsonParserSequence seq = JsonParserSequence.createFlattened(buf.asParser(), jp);
+ JsonParserSequence seq = JsonParserSequence.createFlattened(false, buf.asParser(), p);
assertEquals(2, seq.containedParsersCount());
- assertFalse(jp.isClosed());
+ assertFalse(p.isClosed());
assertFalse(seq.hasCurrentToken());
assertNull(seq.getCurrentToken());
@@ -389,8 +427,8 @@
assertNull(seq.nextToken());
// also: original parsers should be closed
- assertTrue(jp.isClosed());
- jp.close();
+ assertTrue(p.isClosed());
+ p.close();
buf.close();
seq.close();
}
@@ -411,11 +449,11 @@
TokenBuffer buf4 = new TokenBuffer(null, false);
buf4.writeEndArray();
- JsonParserSequence seq1 = JsonParserSequence.createFlattened(buf1.asParser(), buf2.asParser());
+ JsonParserSequence seq1 = JsonParserSequence.createFlattened(false, buf1.asParser(), buf2.asParser());
assertEquals(2, seq1.containedParsersCount());
- JsonParserSequence seq2 = JsonParserSequence.createFlattened(buf3.asParser(), buf4.asParser());
+ JsonParserSequence seq2 = JsonParserSequence.createFlattened(false, buf3.asParser(), buf4.asParser());
assertEquals(2, seq2.containedParsersCount());
- JsonParserSequence combo = JsonParserSequence.createFlattened(seq1, seq2);
+ JsonParserSequence combo = JsonParserSequence.createFlattened(false, seq1, seq2);
// should flatten it to have 4 underlying parsers
assertEquals(4, combo.containedParsersCount());
diff --git a/src/test/java/com/fasterxml/jackson/failing/AnySetter1035Test.java b/src/test/java/com/fasterxml/jackson/failing/AnySetter1035Test.java
index 48fce29..451cab3 100644
--- a/src/test/java/com/fasterxml/jackson/failing/AnySetter1035Test.java
+++ b/src/test/java/com/fasterxml/jackson/failing/AnySetter1035Test.java
@@ -6,6 +6,10 @@
import com.fasterxml.jackson.databind.*;
+/**
+ * Test for [databind#1035], wherein key type of a `Map` not used with `@JsonAnySetter`
+ * (value type is).
+ */
public class AnySetter1035Test extends BaseMapTest
{
static class MyGeneric<T>
@@ -63,7 +67,9 @@
Map<Integer, Integer> integerGenericMap = new HashMap<Integer, Integer>();
integerGenericMap.put(111, 6);
- MyWrapper deserialized = mapper.readValue("{\"myStringGeneric\":{\"staticallyMappedProperty\":\"Test\",\"testStringKey\":5},\"myIntegerGeneric\":{\"staticallyMappedProperty\":\"Test2\",\"111\":6}}", MyWrapper.class);
+ MyWrapper deserialized = mapper.readValue(aposToQuotes(
+ "{'myStringGeneric':{'staticallyMappedProperty':'Test','testStringKey':5},'myIntegerGeneric':{'staticallyMappedProperty':'Test2','111':6}}"
+ ), MyWrapper.class);
MyGeneric<String> stringGeneric = deserialized.getMyStringGeneric();
MyGeneric<Integer> integerGeneric = deserialized.getMyIntegerGeneric();
diff --git a/src/test/java/com/fasterxml/jackson/failing/BuilderWithIgnored1214Test.java b/src/test/java/com/fasterxml/jackson/failing/BuilderWithIgnored1214Test.java
new file mode 100644
index 0000000..54c6d6a
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/BuilderWithIgnored1214Test.java
@@ -0,0 +1,49 @@
+package com.fasterxml.jackson.failing;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+public class BuilderWithIgnored1214Test extends BaseMapTest
+{
+ @JsonDeserialize(builder = TestObject1214.Builder.class)
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ static class TestObject1214 {
+ final String property1;
+
+ private TestObject1214(Builder builder) {
+ property1 = builder.property1;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public String getProperty1() {
+ return property1;
+ }
+
+ static class Builder {
+
+ private String property1;
+
+ public Builder withProperty1(String p1) {
+ property1 = p1;
+ return this;
+ }
+
+ public TestObject1214 build() {
+ return new TestObject1214(this);
+ }
+ }
+ }
+
+ public void testUnknown1214() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper();
+ TestObject1214 value = mapper.readValue(aposToQuotes
+ ("{'property1':'a', 'property2':'b'}"),
+ TestObject1214.class);
+ assertEquals("a", value.property1);
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/EnumCreatorTest929.java b/src/test/java/com/fasterxml/jackson/failing/EnumCreatorTest929.java
deleted file mode 100644
index 783a5e8..0000000
--- a/src/test/java/com/fasterxml/jackson/failing/EnumCreatorTest929.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.fasterxml.jackson.failing;
-
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.*;
-
-public class EnumCreatorTest929 extends BaseMapTest
-{
- static enum MyEnum
- {
- A, B, C;
-
- @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
- static MyEnum forValues(@JsonProperty("id") int intProp,
- @JsonProperty("name") String name)
- {
- return MyEnum.valueOf(name);
- }
- }
-
- private final ObjectMapper MAPPER = new ObjectMapper();
-
- // for [databind#929]
- public void testMultiArgEnumCreator() throws Exception
- {
- MyEnum v = MAPPER.readValue("{\"id\":3,\"name\":\"B\"}", MyEnum.class);
- assertEquals(MyEnum.B, v);
- }
-}
diff --git a/src/test/java/com/fasterxml/jackson/failing/ExternalId96Test.java b/src/test/java/com/fasterxml/jackson/failing/ExternalTypeId96Test.java
similarity index 97%
rename from src/test/java/com/fasterxml/jackson/failing/ExternalId96Test.java
rename to src/test/java/com/fasterxml/jackson/failing/ExternalTypeId96Test.java
index 0d19357..0e6fd53 100644
--- a/src/test/java/com/fasterxml/jackson/failing/ExternalId96Test.java
+++ b/src/test/java/com/fasterxml/jackson/failing/ExternalTypeId96Test.java
@@ -9,7 +9,7 @@
// Tests for External type id, one that exists at same level as typed Object,
// that is, property is not within typed object but a member of its parent.
-public class ExternalId96Test extends BaseMapTest
+public class ExternalTypeId96Test extends BaseMapTest
{
// for [databind#96]
static class ExternalBeanWithDefault
diff --git a/src/test/java/com/fasterxml/jackson/failing/ObjectIdWithCreator1367Test.java b/src/test/java/com/fasterxml/jackson/failing/ObjectIdWithCreator1367Test.java
new file mode 100644
index 0000000..32afd16
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/ObjectIdWithCreator1367Test.java
@@ -0,0 +1,49 @@
+package com.fasterxml.jackson.failing;
+
+import java.beans.ConstructorProperties;
+
+import com.fasterxml.jackson.annotation.JsonIdentityInfo;
+import com.fasterxml.jackson.annotation.ObjectIdGenerators;
+import com.fasterxml.jackson.databind.*;
+
+public class ObjectIdWithCreator1367Test
+ extends BaseMapTest
+{
+ @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
+// resolver = SimpleObjectIdResolver.class)
+ public static class A {
+ String id;
+ String name;
+
+ public A() { }
+
+ @ConstructorProperties({"id", "name"})
+ public A(String id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public String getId() {
+ return id;
+ }
+ public void setId(String id) {
+ this.id = id;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ }
+
+ public void testObjectIdWithCreator1367() throws Exception
+ {
+ A a = new A("123", "A");
+
+ ObjectMapper om = new ObjectMapper();
+ String json = om.writeValueAsString(a);
+ A deser = om.readValue(json, A.class);
+ assertEquals(a.name, deser.name);
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/ObjectIdWithInjectable639Test.java b/src/test/java/com/fasterxml/jackson/failing/ObjectIdWithInjectable639Test.java
new file mode 100644
index 0000000..30dfcf3
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/ObjectIdWithInjectable639Test.java
@@ -0,0 +1,45 @@
+package com.fasterxml.jackson.failing;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.databind.*;
+
+public class ObjectIdWithInjectable639Test extends BaseMapTest
+{
+ // for [databind#639]
+ @JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class)
+ public static final class Parent2 {
+ @JsonProperty
+ public Child2 child;
+
+ @JsonCreator
+ public Parent2(@JacksonInject("context") String context) {
+ }
+ }
+
+ @JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class)
+ public static final class Child2 {
+ @JsonProperty
+ private final Parent2 parent;
+
+ @JsonCreator
+ public Child2(@JsonProperty("parent") Parent2 parent) {
+ this.parent = parent;
+ }
+ }
+
+ // for [databind#639]
+ public void testObjectIdWithInjectable() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper()
+ .setInjectableValues(new InjectableValues.Std().
+ addValue("context", "Stuff"));
+ Parent2 parent2 = new Parent2("foo");
+ Child2 child2 = new Child2(parent2);
+ parent2.child = child2;
+
+ String json2 = mapper.writeValueAsString(parent2);
+ parent2 = mapper.readValue(json2, Parent2.class);
+ assertNotNull(parent2);
+ assertNotNull(parent2.child);
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/SkipInjectableIntrospection962Test.java b/src/test/java/com/fasterxml/jackson/failing/SkipInjectableIntrospection962Test.java
new file mode 100644
index 0000000..cb37a5c
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/SkipInjectableIntrospection962Test.java
@@ -0,0 +1,55 @@
+package com.fasterxml.jackson.failing;
+
+import com.fasterxml.jackson.annotation.JacksonInject;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.*;
+
+public class SkipInjectableIntrospection962Test extends BaseMapTest
+{
+ static class InjectMe
+ {
+ private String a;
+
+ public InjectMe(boolean dummy) { }
+
+ public void setA(Integer a) {
+ this.a = a.toString();
+ }
+
+ public void setA(InjectMe a) {
+ this.a = String.valueOf(a);
+ }
+
+ public String getA() {
+ return a;
+ }
+ }
+
+ static class Injectee
+ {
+ private String b;
+
+ @JsonCreator
+ public Injectee(@JacksonInject InjectMe injectMe, @JsonProperty("b") String b) {
+ this.b = b;
+ }
+
+ public String getB() {
+ return b;
+ }
+ }
+
+ // 14-Jun-2016, tatu: For some odd reason, this test sometimes fails, other times not...
+ // possibly related to unstable ordering of properties?
+ public void testInjected() throws Exception
+ {
+ InjectMe im = new InjectMe(true);
+ ObjectMapper mapper = new ObjectMapper()
+ .setInjectableValues(new InjectableValues.Std().addValue(InjectMe.class, im));
+ String test = "{\"b\":\"bbb\"}";
+
+ Injectee actual = mapper.readValue(test, Injectee.class);
+ assertEquals("bbb", actual.getB());
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestObjectIdWithInjectables639.java b/src/test/java/com/fasterxml/jackson/failing/TestObjectIdWithInjectables639.java
new file mode 100644
index 0000000..3d85f2c
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/TestObjectIdWithInjectables639.java
@@ -0,0 +1,85 @@
+package com.fasterxml.jackson.failing;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.databind.*;
+
+// This is probably impossible to handle, in general case, since
+// there is a cycle for Parent2/Child2... unless special handling
+// could be made to ensure that
+public class TestObjectIdWithInjectables639 extends BaseMapTest
+{
+ public static final class Context { }
+
+ @JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class)
+ public static final class Parent1 {
+ public Child1 child;
+
+ public Parent1() { }
+ }
+
+ @JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class)
+ public static final class Child1 {
+
+ @JsonProperty
+ private final Parent1 parent;
+
+ @JsonCreator
+ public Child1(@JsonProperty("parent") Parent1 parent) {
+ this.parent = parent;
+ }
+ }
+
+ @JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class)
+ public static final class Parent2 {
+
+ protected final Context context;
+
+ public Child2 child;
+
+ @JsonCreator
+ public Parent2(@JacksonInject Context context) {
+ this.context = context;
+ }
+ }
+
+ @JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class)
+ public static final class Child2 {
+
+ protected final Context context;
+
+ @JsonProperty
+ protected Parent2 parent;
+
+ @JsonCreator
+ public Child2(@JacksonInject Context context,
+ @JsonProperty("parent") Parent2 parent) {
+ this.context = context;
+ this.parent = parent;
+ }
+ }
+
+ public void testObjectIdWithInjectables() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper();
+ Context context = new Context();
+ InjectableValues iv = new InjectableValues.Std().
+ addValue(Context.class, context);
+ mapper.setInjectableValues(iv);
+
+ Parent1 parent1 = new Parent1();
+ Child1 child1 = new Child1(parent1);
+ parent1.child = child1;
+
+ Parent2 parent2 = new Parent2(context);
+ Child2 child2 = new Child2(context, parent2);
+ parent2.child = child2;
+
+ String json = mapper.writeValueAsString(parent1);
+ parent1 = mapper.readValue(json, Parent1.class);
+// System.out.println("This works: " + json);
+
+ json = mapper.writeValueAsString(parent2);
+//System.out.println("This fails: " + json);
+ parent2 = mapper.readValue(json, Parent2.class);
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestObjectIdWithUnwrapping1298.java b/src/test/java/com/fasterxml/jackson/failing/TestObjectIdWithUnwrapping1298.java
new file mode 100644
index 0000000..8c4b97b
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/TestObjectIdWithUnwrapping1298.java
@@ -0,0 +1,83 @@
+package com.fasterxml.jackson.failing;
+
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+// Test case for https://github.com/FasterXML/jackson-databind/issues/1298
+public class TestObjectIdWithUnwrapping1298 extends BaseMapTest
+{
+ private static Long nextId = 1L;
+
+ public static final class ListOfParents{
+ public List<Parent> parents = new ArrayList<>();
+
+ public void addParent( Parent parent) { parents.add(parent);}
+ }
+
+ @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id", scope = Parent.class)
+ public static final class Parent {
+ public Long id;
+
+ @JsonUnwrapped
+ public Child child;
+ public Parent() { this.id = nextId++;}
+ }
+
+ @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id", scope = Child.class)
+ public static final class Child
+ {
+ public Long id;
+
+ public final String name;
+
+ public Child(@JsonProperty("name") String name) {
+ this.name = name;
+ this.id = TestObjectIdWithUnwrapping1298.nextId++;
+ }
+ }
+
+ public void testObjectIdWithRepeatedChild() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.disable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT);
+
+ // Equivalent to Spring _embedded for Bean w/ List property
+ ListOfParents parents = new ListOfParents();
+
+ //Bean with Relationship
+ Parent parent1 = new Parent();
+ Child child1 = new Child("Child1");
+ parent1.child = child1;
+ parents.addParent(parent1);
+
+ // serialize parent1 and parent2
+ String json = mapper
+// .writerWithDefaultPrettyPrinter()
+ .writeValueAsString(parents);
+ System.out.println("This works: " + json);
+
+ // Add parent3 to create ObjectId reference
+ // Bean w/ repeated relationship from parent1, should generate ObjectId
+ Parent parent3 = new Parent();
+ parent3.child = child1;
+ parents.addParent(parent3);
+ StringWriter sw = new StringWriter();
+
+ try {
+ mapper
+// .writerWithDefaultPrettyPrinter()
+ .writeValue(sw, parents);
+ } catch (Exception e) {
+ System.out.println("Failed output so far: " + sw);
+ throw e;
+ }
+
+ System.out.println("Also works: " + sw);
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestSubtypes1311.java b/src/test/java/com/fasterxml/jackson/failing/TestSubtypes1311.java
new file mode 100644
index 0000000..b3cfbc9
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/TestSubtypes1311.java
@@ -0,0 +1,38 @@
+package com.fasterxml.jackson.failing;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.annotation.JsonTypeName;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+// Not sure if this is valid, but for what it's worth, shows
+// the thing wrt [databind#1311]. May be removed if we can't find
+// improvements.
+public class TestSubtypes1311 extends com.fasterxml.jackson.databind.BaseMapTest
+{
+ // [databind#1311]
+ @JsonTypeInfo(property = "type", use = JsonTypeInfo.Id.NAME, defaultImpl = Factory1311ImplA.class)
+ interface Factory1311 { }
+
+ @JsonTypeName("implA")
+ static class Factory1311ImplA implements Factory1311 { }
+
+ @JsonTypeName("implB")
+ static class Factory1311ImplB implements Factory1311 { }
+
+ /*
+ /**********************************************************
+ /* Unit tests
+ /**********************************************************
+ */
+
+ // [databind#1311]
+ public void testSubtypeAssignmentCheck() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.registerSubtypes(Factory1311ImplA.class, Factory1311ImplB.class);
+ Factory1311ImplB result = mapper.readValue("{\"type\":\"implB\"}", Factory1311ImplB.class);
+ assertNotNull(result);
+ assertEquals(Factory1311ImplB.class, result.getClass());
+ }
+}
diff --git a/src/test/java/perf/ManualReadWithTypeResolution.java b/src/test/java/perf/ManualReadWithTypeResolution.java
new file mode 100644
index 0000000..3f3f358
--- /dev/null
+++ b/src/test/java/perf/ManualReadWithTypeResolution.java
@@ -0,0 +1,156 @@
+package perf;
+
+import java.io.*;
+import java.util.*;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Variant that uses hard-coded input but compares cost of generic type
+ * resolution to that of passing raw (type-erased) Class.
+ *
+ * @since 2.8
+ */
+public class ManualReadWithTypeResolution
+{
+ private final String _desc1, _desc2;
+ private final byte[] _input;
+ private final Class<?> _inputType;
+ private final TypeReference<?> _inputTypeRef;
+
+ private final ObjectMapper _mapper;
+ private final int REPS;
+
+ protected int hash;
+ // wait for 3 seconds
+ protected long startMeasure = System.currentTimeMillis() + 3000L;
+ protected int roundsDone = 0;
+ private double[] timeMsecs;
+
+ public static void main(String[] args) throws Exception {
+ new ManualReadWithTypeResolution().doTest();
+ }
+
+ private ManualReadWithTypeResolution() throws IOException {
+ _desc1 = "Raw type";
+ _desc2 = "Generic type";
+ _mapper = new ObjectMapper();
+
+ _input = "[\"value\",\"123\"]".getBytes("UTF-8");
+ _inputType = List.class;
+ _inputTypeRef = new TypeReference<List<String>>() { };
+
+ /*
+ _input = "{\"id\":124}".getBytes("UTF-8");
+ _inputType = Map.class;
+ _inputTypeRef = new TypeReference<Map<String,Object>>() { };
+ */
+
+ REPS = (int) ((double) (15 * 1000 * 1000) / (double) _input.length);
+ }
+
+ // When comparing to simple streaming parsing, uncomment:
+
+ private void doTest() throws Exception
+ {
+
+ System.out.printf("Read %d bytes to bind; will do %d repetitions\n",
+ _input.length, REPS);
+ System.out.print("Warming up");
+
+ int i = 0;
+ final int TYPES = 2;
+
+
+ timeMsecs = new double[TYPES];
+
+ while (true) {
+ Thread.sleep(100L);
+ final int type = (i++ % TYPES);
+
+ String msg;
+ double msesc;
+
+ switch (type) {
+ case 0:
+ msesc = testDeser(REPS, _input, _mapper, _inputType);
+ msg = _desc1;
+ break;
+ case 1:
+ msesc = testDeser(REPS, _input, _mapper, _inputTypeRef);
+ msg = _desc2;
+ break;
+ default:
+ throw new Error();
+ }
+ updateStats(type, (i % 17) == 0, msg, msesc);
+ }
+ }
+
+ protected final double testDeser(int reps, byte[] json, ObjectMapper mapper, Class<?> type)
+ throws IOException
+ {
+ long start = System.nanoTime();
+ Object result = null;
+ while (--reps >= 0) {
+ result = mapper.readValue(json, type);
+ }
+ hash = result.hashCode();
+ return _msecsFromNanos(System.nanoTime() - start);
+ }
+
+ protected final double testDeser(int reps, byte[] json, ObjectMapper mapper, TypeReference<?> type)
+ throws IOException
+ {
+ long start = System.nanoTime();
+ Object result = null;
+ while (--reps >= 0) {
+ result = mapper.readValue(json, type);
+ }
+ hash = result.hashCode();
+ return _msecsFromNanos(System.nanoTime() - start);
+ }
+
+ private void updateStats(int type, boolean doGc, String msg, double msecs)
+ throws Exception
+ {
+ final boolean lf = (type == (timeMsecs.length - 1));
+
+ if (startMeasure == 0L) { // skip first N seconds
+ timeMsecs[type] += msecs;
+ } else {
+ if (lf) {
+ if (System.currentTimeMillis() >= startMeasure) {
+ startMeasure = 0L;
+ System.out.println(" complete!");
+ } else {
+ System.out.print(".");
+ }
+ }
+ return;
+ }
+
+ System.out.printf("Test '%s' [hash: 0x%s] -> %.1f msecs\n", msg, Integer.toHexString(hash), msecs);
+ if (lf) {
+ ++roundsDone;
+ if ((roundsDone % 3) == 0 ) {
+ double den = (double) roundsDone;
+ System.out.printf("Averages after %d rounds (%s/%s): %.1f / %.1f msecs\n",
+ (int) den, _desc1, _desc2,
+ timeMsecs[0] / den, timeMsecs[1] / den);
+ }
+ System.out.println();
+ }
+ if (doGc) {
+ System.out.println("[GC]");
+ Thread.sleep(100L);
+ System.gc();
+ Thread.sleep(100L);
+ }
+ }
+
+ protected final double _msecsFromNanos(long nanos) {
+ return (nanos / 1000000.0);
+ }
+}