Merge branch '2.4'
diff --git a/.travis.yml b/.travis.yml
index c134ca9..9f83625 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,5 +1,6 @@
language: java
jdk:
+ - oraclejdk7
- openjdk6
# Below this line is configuration for deploying to the Sonatype OSS repo
@@ -11,6 +12,7 @@
branches:
only:
- master
+ - "2.4"
- "2.3"
env:
diff --git a/README.md b/README.md
index 64bc22e..d72b95e 100644
--- a/README.md
+++ b/README.md
@@ -27,7 +27,7 @@
</dependency>
```
-Since package also depends on `jackson-core` and `jackson-databind` packages, you will need to download these if not using Maven; and you may also want to add them as Maven dependency to ensure that compatible versions are used.
+Since package also depends on `jackson-core` and `jackson-annotations` packages, you will need to download these if not using Maven; and you may also want to add them as Maven dependency to ensure that compatible versions are used.
If so, also add:
```xml
@@ -144,7 +144,7 @@
// "name" : "Bob", "age" : 13,
// "other" : {
// "type" : "student"
-// {
+// }
// }
```
@@ -376,7 +376,7 @@
# Further reading
-* [Documentation](https://github.com/FasterXML/jackson-databind/wiki/Documentation)
+* [Documentation](https://github.com/FasterXML/jackson-databind/wiki)
Related:
diff --git a/pom.xml b/pom.xml
index 5e8baa1..1a83ed1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,12 +5,12 @@
<parent>
<groupId>com.fasterxml.jackson</groupId>
<artifactId>jackson-parent</artifactId>
- <version>2.4</version>
+ <version>2.5-rc1</version>
</parent>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
- <version>2.4.5-SNAPSHOT</version>
+ <version>2.5.0-SNAPSHOT</version>
<name>jackson-databind</name>
<packaging>bundle</packaging>
<description>General data-binding functionality for Jackson: works on core streaming API</description>
@@ -69,12 +69,12 @@
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
- <version>2.4.0</version>
+ <version>2.5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
- <version>2.4.5-SNAPSHOT</version>
+ <version>2.5.0-SNAPSHOT</version>
</dependency>
<!-- and for testing we need a few libraries
@@ -102,15 +102,10 @@
<build>
<plugins>
- <plugin> <!-- parent uses 2.4.2 -->
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-release-plugin</artifactId>
- <version>2.5</version>
- </plugin>
- <plugin>
+ <plugin>
<groupId>org.apache.maven.plugins</groupId>
+ <version>${version.plugin.surefire}</version>
<artifactId>maven-surefire-plugin</artifactId>
- <version>${surefire.version}</version>
<configuration>
<excludes>
<exclude>com/fasterxml/jackson/failing/*.java</exclude>
@@ -121,12 +116,12 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
- <version>${javadoc.version}</version>
+ <version>${version.plugin.javadoc}</version>
<configuration>
<links>
<link>http://docs.oracle.com/javase/6/docs/api/</link>
- <link>http://fasterxml.github.com/jackson-annotations/javadoc/2.4/</link>
- <link>http://fasterxml.github.com/jackson-core/javadoc/2.4/</link>
+ <link>http://fasterxml.github.com/jackson-annotations/javadoc/2.5/</link>
+ <link>http://fasterxml.github.com/jackson-core/javadoc/2.5/</link>
</links>
</configuration>
</plugin>
diff --git a/release-notes/CREDITS b/release-notes/CREDITS
index 1fc8c1c..c8c22f8 100644
--- a/release-notes/CREDITS
+++ b/release-notes/CREDITS
@@ -157,3 +157,20 @@
Fernando Otero (zeitos@github)
* Contributed fix for #610: Problem with forward reference in hierarchies
(2.4.4)
+
+Lovro Pandžić (lpandzic@github)
+ * Reported #421: @JsonCreator not used in case of multiple creators with parameter names
+ (2.5.0)
+
+Adam Stroud (adstro@github)
+ * Contributed #576: Add fluent API for adding mixins
+ (2.5.0)
+
+David Fleeman (fleebytes@github)
+ * Contributed #528 implementation: Add support for `JsonType.As.EXISTING_PROPERTY`
+ (2.5.0)
+
+Aurélien Leboulanger (herau@github)
+ * Contributed improvement for #597: Improve error messaging for cases where JSON Creator
+ returns null (which is illegal)
+ (2.5.0)
diff --git a/release-notes/VERSION b/release-notes/VERSION
index fa3821d..d19fc58 100644
--- a/release-notes/VERSION
+++ b/release-notes/VERSION
@@ -4,6 +4,56 @@
=== Releases ===
------------------------------------------------------------------------
+2.5.0 (not yet released)
+
+#113: Problem deserializing polymorphic types with @JsonCreator
+#408: External type id does not allow use of 'visible=true'
+#421: @JsonCreator not used in case of multiple creators with parameter names
+ (reported by Lovro P, lpandzic@github)
+#427: Make array and Collection serializers call `JsonGenerator.writeStartArray(int)`
+#521: Keep bundle annotations, prevent problems with recursive annotation types
+ (reported by tea-dragon@github)
+#527: Add support for `@JsonInclude(contents=Include.NON_NULL)` (and others) for Maps
+#528: Add support for `JsonType.As.EXISTING_PROPERTY`
+ (reported by heapifyman@github; implemented by fleebytes@github)
+#539: Problem with post-procesing of "empty bean" serializer; was not calling
+ 'BeanSerializerModifier.modifySerializer()` for empty beans
+ (reported by Fabien R, fabienrenaud@github)
+#540: Support deserializing `[]` as null or empty collection when the java type is a not an object
+ (requested by Fabien R, fabienrenaud@github)
+#543: Problem resolving self-referential recursive types
+ (reported by ahgittin@github)
+#550: Minor optimization: prune introspection of "well-known" JDK types
+#552: Improved handling for ISO-8601 (date) format
+ (contributed by Jerome G, geronimo-iia@github)
+#559: Add `getDateFormat()`, `getPropertyNamingStrategy()` in `ObjectMapper`
+#560: @JsonCreator to deserialize BigInteger to Enum
+ (requested by gisupp@github)
+#565: Add support for handling `Map.Entry`
+#571: Add support in ObjectMapper for custom `ObjectReader`, `ObjectWriter` (sub-classes)
+#572: Override default serialization of Enums
+ (requested by herau@github)
+#576: Add fluent API for adding mixins
+ (contributed by Adam S, adstro@github)
+#597: Improve error messaging for cases where JSON Creator returns null (which
+ is illegal)
+ (contributed by Aurelien L)
+#599: Add a simple mechanism for avoiding multiple registrations of the same module
+#607: Allow (re)config of `JsonParser.Feature`s via `ObjectReader`
+#608: Allow (re)config of `JsonGenerator.Feature`s via `ObjectWriter`
+#614: Add a mechanism for using `@JsonCreator.mode` for resolving possible ambiguity between
+ delegating- and property-based creators
+#616: Add `SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS`
+#623: Add `StdNodeBasedDeserializer`
+#630: Add `KeyDeserializer` for `Class`
+#633: Allow returning null value from IdResolver to make type information optional
+ (requested by Antibrumm@github)
+#634: Add `typeFromId(DatabindContext,String)` in `TypeIdDeserializer`
+- Allow use of `Shape.ARRAY` for Enums, as an alias to 'use index'
+- Start using `JsonGenerator.writeStartArray(int)` to help data formats
+ that benefit from knowing number of elements in arrays (and would otherwise
+ need to buffer values to know length)
+
2.4.5 (not yet released)
#635: Reduce cachability of `Map` deserializers, to avoid problems with per-property config changes
@@ -51,10 +101,8 @@
- Fixed a problem with `acceptJsonFormatVisitor` with Collection/array types that
are marked with `@JsonValue`; could cause NPE in JSON Schema generator module.
-2.4.2 (13-Aug-2014)
+2.4.2 (14-Aug-2014)
-#506: Index is never set for Collection and Array in InvalidFormatException.Reference
- (reported by Fabrice D, fabdouglas@github)
#515: Mixin annotations lost when using a mixin class hierarchy with non-mixin interfaces
(reported by 'stevebread@github')
- Fixed a problem related to [jackson-dataformat-smile#19].
@@ -72,6 +120,9 @@
#491: Temporary work-around for issue #490 (full fix for 2.5 needs to be
in `jackson-annotations`)
+#506: Index is never set for Collection and Array in InvalidFormatException.Reference
+ (reported by Fabrice D, fabdouglas@github)
+- Fixed a problem related to [jackson-dataformat-smile#19].
2.4.1 (17-Jun-2014)
diff --git a/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java b/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java
index 70b32d4..464e4cc 100644
--- a/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java
+++ b/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java
@@ -3,12 +3,11 @@
import java.lang.annotation.Annotation;
import java.util.*;
+import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
-
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.core.Versioned;
-
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
@@ -571,7 +570,12 @@
* field) defines which Bean/Map properties are to be included in
* serialization.
* If no annotation is found, method should return given second
- * argument; otherwise value indicated by the annotation
+ * argument; otherwise value indicated by the annotation.
+ *<p>
+ * Note that meaning of inclusion value depends on whether it is for
+ * a Class or property (field/method/constructor): in former case,
+ * it is the default for all properties; in latter case it is specific
+ * override for annotated property.
*
* @return Enumerated value indicating which properties to include
* in serialization
@@ -581,6 +585,16 @@
}
/**
+ * Method for checking whether content (entries) of a {@link java.util.Map} property
+ * are to be included during serialization or not.
+ *
+ * @since 2.5
+ */
+ public JsonInclude.Include findSerializationInclusionForContent(Annotated a, JsonInclude.Include defValue) {
+ return defValue;
+ }
+
+ /**
* Method for accessing annotated type definition that a
* method/field can have, to be used as the type for serialization
* instead of the runtime type.
@@ -1006,4 +1020,59 @@
public boolean hasCreatorAnnotation(Annotated a) {
return false;
}
+
+ /**
+ * Method for finding indication of creator binding mode for
+ * a creator (something for which {@link #hasCreatorAnnotation} returns
+ * true), for cases where there may be ambiguity (currently: single-argument
+ * creator with implicit but no explicit name for the argument).
+ *
+ * @since 2.5
+ */
+ public JsonCreator.Mode findCreatorBinding(Annotated a) {
+ return null;
+ }
+
+ /*
+ /**********************************************************
+ /* Overridable methods: may be used as low-level extension
+ /* points.
+ /**********************************************************
+ */
+
+ /**
+ * Method that should be used by sub-classes for ALL
+ * annotation access;
+ * overridable so
+ * that sub-classes may, if they choose to, mangle actual access to
+ * block access ("hide" annotations) or perhaps change it.
+ *<p>
+ * Default implementation is simply:
+ *<code>
+ * return annotated.getAnnotation(annoClass);
+ *</code>
+ *
+ * @since 2.5
+ */
+ protected <A extends Annotation> A _findAnnotation(Annotated annotated,
+ Class<A> annoClass) {
+ return annotated.getAnnotation(annoClass);
+ }
+
+ /**
+ * Method that should be used by sub-classes for ALL
+ * annotation existence access;
+ * overridable so that sub-classes may, if they choose to, mangle actual access to
+ * block access ("hide" annotations) or perhaps change value seen.
+ *<p>
+ * Default implementation is simply:
+ *<code>
+ * return annotated.hasAnnotation(annoClass);
+ *</code>
+ *
+ * @since 2.5
+ */
+ protected boolean _hasAnnotation(Annotated annotated, Class<? extends Annotation> annoClass) {
+ return annotated.hasAnnotation(annoClass);
+ }
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/BeanDescription.java b/src/main/java/com/fasterxml/jackson/databind/BeanDescription.java
index 393300b..b2f796f 100644
--- a/src/main/java/com/fasterxml/jackson/databind/BeanDescription.java
+++ b/src/main/java/com/fasterxml/jackson/databind/BeanDescription.java
@@ -99,30 +99,30 @@
/* Basic API for finding properties
/**********************************************************
*/
-
+
/**
* @return Ordered Map with logical property name as key, and
* matching getter method as value.
*/
public abstract List<BeanPropertyDefinition> findProperties();
-
+
/**
* Method for locating all back-reference properties (setters, fields) bean has
*/
public abstract Map<String,AnnotatedMember> findBackReferenceProperties();
public abstract Set<String> getIgnoredPropertyNames();
-
+
/*
/**********************************************************
/* Basic API for finding creator members
/**********************************************************
*/
-
+
public abstract List<AnnotatedConstructor> getConstructors();
-
+
public abstract List<AnnotatedMethod> getFactoryMethods();
-
+
/**
* Method that will locate the no-arg constructor for this class,
* if it has one, and that constructor has not been marked as
@@ -187,6 +187,11 @@
public abstract JsonInclude.Include findSerializationInclusion(JsonInclude.Include defValue);
/**
+ * @since 2.5
+ */
+ public abstract JsonInclude.Include findSerializationInclusionForContent(JsonInclude.Include defValue);
+
+ /**
* Method for checking what is the expected format for POJO, as
* defined by defaults and possible annotations.
* Note that this may be further refined by per-property annotations.
diff --git a/src/main/java/com/fasterxml/jackson/databind/DeserializationConfig.java b/src/main/java/com/fasterxml/jackson/databind/DeserializationConfig.java
index 4b58b3c..5159c1b 100644
--- a/src/main/java/com/fasterxml/jackson/databind/DeserializationConfig.java
+++ b/src/main/java/com/fasterxml/jackson/databind/DeserializationConfig.java
@@ -34,18 +34,17 @@
* "fluent factory" methods.
* Note also that unlike with Jackson 1, these instances can not be
* assigned to {@link ObjectMapper}; in fact, application code should
- * rarely interact directly with these instance (unlike core Jackson code)
+ * rarely interact directly with these instances.
*/
public final class DeserializationConfig
extends MapperConfigBase<DeserializationFeature, DeserializationConfig>
implements java.io.Serializable // since 2.1
{
- // for 2.1.0
- private static final long serialVersionUID = -4227480407273773599L;
+ // since 2.5
+ private static final long serialVersionUID = 1;
/**
- * Set of features enabled; actual type (kind of features)
- * depends on sub-classes.
+ * Set of {@link DeserializationFeature}s enabled.
*/
protected final int _deserFeatures;
@@ -60,7 +59,17 @@
* Factory used for constructing {@link com.fasterxml.jackson.databind.JsonNode} instances.
*/
protected final JsonNodeFactory _nodeFactory;
-
+
+ /**
+ * States of {@link com.fasterxml.jackson.core.JsonParser.Feature}s to enable/disable.
+ */
+ protected final int _parserFeatures;
+
+ /**
+ * Bitflag of {@link com.fasterxml.jackson.core.JsonParser.Feature}s to enable/disable
+ */
+ protected final int _parserFeaturesToChange;
+
/*
/**********************************************************
/* Life-cycle, constructors
@@ -77,8 +86,22 @@
_deserFeatures = collectFeatureDefaults(DeserializationFeature.class);
_nodeFactory = JsonNodeFactory.instance;
_problemHandlers = null;
+ _parserFeatures = 0;
+ _parserFeaturesToChange = 0;
}
+ private DeserializationConfig(DeserializationConfig src,
+ int mapperFeatures, int deserFeatures,
+ int parserFeatures, int parserFeatureMask)
+ {
+ super(src, mapperFeatures);
+ _deserFeatures = deserFeatures;
+ _nodeFactory = src._nodeFactory;
+ _problemHandlers = src._problemHandlers;
+ _parserFeatures = parserFeatures;
+ _parserFeaturesToChange = parserFeatureMask;
+ }
+
/**
* Copy constructor used to create a non-shared instance with given mix-in
* annotation definitions and subtype resolver.
@@ -89,15 +112,8 @@
_deserFeatures = src._deserFeatures;
_nodeFactory = src._nodeFactory;
_problemHandlers = src._problemHandlers;
- }
-
- private DeserializationConfig(DeserializationConfig src,
- int mapperFeatures, int deserFeatures)
- {
- super(src, mapperFeatures);
- _deserFeatures = deserFeatures;
- _nodeFactory = src._nodeFactory;
- _problemHandlers = src._problemHandlers;
+ _parserFeatures = src._parserFeatures;
+ _parserFeaturesToChange = src._parserFeaturesToChange;
}
private DeserializationConfig(DeserializationConfig src, BaseSettings base)
@@ -106,6 +122,8 @@
_deserFeatures = src._deserFeatures;
_nodeFactory = src._nodeFactory;
_problemHandlers = src._problemHandlers;
+ _parserFeatures = src._parserFeatures;
+ _parserFeaturesToChange = src._parserFeaturesToChange;
}
private DeserializationConfig(DeserializationConfig src, JsonNodeFactory f)
@@ -114,6 +132,8 @@
_deserFeatures = src._deserFeatures;
_problemHandlers = src._problemHandlers;
_nodeFactory = f;
+ _parserFeatures = src._parserFeatures;
+ _parserFeaturesToChange = src._parserFeaturesToChange;
}
private DeserializationConfig(DeserializationConfig src,
@@ -123,6 +143,8 @@
_deserFeatures = src._deserFeatures;
_problemHandlers = problemHandlers;
_nodeFactory = src._nodeFactory;
+ _parserFeatures = src._parserFeatures;
+ _parserFeaturesToChange = src._parserFeaturesToChange;
}
private DeserializationConfig(DeserializationConfig src, String rootName)
@@ -131,6 +153,8 @@
_deserFeatures = src._deserFeatures;
_problemHandlers = src._problemHandlers;
_nodeFactory = src._nodeFactory;
+ _parserFeatures = src._parserFeatures;
+ _parserFeaturesToChange = src._parserFeaturesToChange;
}
private DeserializationConfig(DeserializationConfig src, Class<?> view)
@@ -139,6 +163,8 @@
_deserFeatures = src._deserFeatures;
_problemHandlers = src._problemHandlers;
_nodeFactory = src._nodeFactory;
+ _parserFeatures = src._parserFeatures;
+ _parserFeaturesToChange = src._parserFeaturesToChange;
}
/**
@@ -150,6 +176,8 @@
_deserFeatures = src._deserFeatures;
_problemHandlers = src._problemHandlers;
_nodeFactory = src._nodeFactory;
+ _parserFeatures = src._parserFeatures;
+ _parserFeaturesToChange = src._parserFeaturesToChange;
}
/**
@@ -161,6 +189,8 @@
_deserFeatures = src._deserFeatures;
_problemHandlers = src._problemHandlers;
_nodeFactory = src._nodeFactory;
+ _parserFeatures = src._parserFeatures;
+ _parserFeaturesToChange = src._parserFeaturesToChange;
}
// for unit tests only:
@@ -180,7 +210,9 @@
newMapperFlags |= f.getMask();
}
return (newMapperFlags == _mapperFeatures) ? this :
- new DeserializationConfig(this, newMapperFlags, _deserFeatures);
+ new DeserializationConfig(this, newMapperFlags, _deserFeatures,
+ _parserFeatures, _parserFeaturesToChange);
+
}
@Override
@@ -191,7 +223,8 @@
newMapperFlags &= ~f.getMask();
}
return (newMapperFlags == _mapperFeatures) ? this :
- new DeserializationConfig(this, newMapperFlags, _deserFeatures);
+ new DeserializationConfig(this, newMapperFlags, _deserFeatures,
+ _parserFeatures, _parserFeaturesToChange);
}
@Override
@@ -204,7 +237,8 @@
newMapperFlags = _mapperFeatures & ~feature.getMask();
}
return (newMapperFlags == _mapperFeatures) ? this :
- new DeserializationConfig(this, newMapperFlags, _deserFeatures);
+ new DeserializationConfig(this, newMapperFlags, _deserFeatures,
+ _parserFeatures, _parserFeaturesToChange);
}
@Override
@@ -307,7 +341,175 @@
private final DeserializationConfig _withBase(BaseSettings newBase) {
return (_base == newBase) ? this : new DeserializationConfig(this, newBase);
}
+
+ /*
+ /**********************************************************
+ /* Life-cycle, DeserializationFeature-based factory methods
+ /**********************************************************
+ */
+
+ /**
+ * Fluent factory method that will construct and return a new configuration
+ * object instance with specified features enabled.
+ */
+ public DeserializationConfig with(DeserializationFeature feature)
+ {
+ int newDeserFeatures = (_deserFeatures | feature.getMask());
+ return (newDeserFeatures == _deserFeatures) ? this :
+ new DeserializationConfig(this, _mapperFeatures, newDeserFeatures,
+ _parserFeatures, _parserFeaturesToChange);
+ }
+
+ /**
+ * Fluent factory method that will construct and return a new configuration
+ * object instance with specified features enabled.
+ */
+ public DeserializationConfig with(DeserializationFeature first,
+ DeserializationFeature... features)
+ {
+ int newDeserFeatures = _deserFeatures | first.getMask();
+ for (DeserializationFeature f : features) {
+ newDeserFeatures |= f.getMask();
+ }
+ return (newDeserFeatures == _deserFeatures) ? this :
+ new DeserializationConfig(this, _mapperFeatures, newDeserFeatures,
+ _parserFeatures, _parserFeaturesToChange);
+ }
+
+ /**
+ * Fluent factory method that will construct and return a new configuration
+ * object instance with specified features enabled.
+ */
+ public DeserializationConfig withFeatures(DeserializationFeature... features)
+ {
+ int newDeserFeatures = _deserFeatures;
+ for (DeserializationFeature f : features) {
+ newDeserFeatures |= f.getMask();
+ }
+ return (newDeserFeatures == _deserFeatures) ? this :
+ new DeserializationConfig(this, _mapperFeatures, newDeserFeatures,
+ _parserFeatures, _parserFeaturesToChange);
+ }
+ /**
+ * Fluent factory method that will construct and return a new configuration
+ * object instance with specified feature disabled.
+ */
+ public DeserializationConfig without(DeserializationFeature feature)
+ {
+ int newDeserFeatures = _deserFeatures & ~feature.getMask();
+ return (newDeserFeatures == _deserFeatures) ? this :
+ new DeserializationConfig(this, _mapperFeatures, newDeserFeatures,
+ _parserFeatures, _parserFeaturesToChange);
+ }
+
+ /**
+ * Fluent factory method that will construct and return a new configuration
+ * object instance with specified features disabled.
+ */
+ public DeserializationConfig without(DeserializationFeature first,
+ DeserializationFeature... features)
+ {
+ int newDeserFeatures = _deserFeatures & ~first.getMask();
+ for (DeserializationFeature f : features) {
+ newDeserFeatures &= ~f.getMask();
+ }
+ return (newDeserFeatures == _deserFeatures) ? this :
+ new DeserializationConfig(this, _mapperFeatures, newDeserFeatures,
+ _parserFeatures, _parserFeaturesToChange);
+ }
+
+ /**
+ * Fluent factory method that will construct and return a new configuration
+ * object instance with specified features disabled.
+ */
+ public DeserializationConfig withoutFeatures(DeserializationFeature... features)
+ {
+ int newDeserFeatures = _deserFeatures;
+ for (DeserializationFeature f : features) {
+ newDeserFeatures &= ~f.getMask();
+ }
+ return (newDeserFeatures == _deserFeatures) ? this :
+ new DeserializationConfig(this, _mapperFeatures, newDeserFeatures,
+ _parserFeatures, _parserFeaturesToChange);
+ }
+
+ /*
+ /**********************************************************
+ /* Life-cycle, JsonParser.Feature-based factory methods
+ /**********************************************************
+ */
+
+ /**
+ * Fluent factory method that will construct and return a new configuration
+ * object instance with specified features enabled.
+ *
+ * @since 2.5
+ */
+ public DeserializationConfig with(JsonParser.Feature feature)
+ {
+ int newSet = _parserFeatures | feature.getMask();
+ int newMask = _parserFeaturesToChange | feature.getMask();
+ return ((_parserFeatures == newSet) && (_parserFeaturesToChange == newMask)) ? this :
+ new DeserializationConfig(this, _mapperFeatures, _deserFeatures,
+ newSet, newMask);
+ }
+
+ /**
+ * Fluent factory method that will construct and return a new configuration
+ * object instance with specified features enabled.
+ *
+ * @since 2.5
+ */
+ public DeserializationConfig withFeatures(JsonParser.Feature... features)
+ {
+ int newSet = _parserFeatures;
+ int newMask = _parserFeaturesToChange;
+ for (JsonParser.Feature f : features) {
+ int mask = f.getMask();
+ newSet |= mask;
+ newMask |= mask;
+ }
+ return ((_parserFeatures == newSet) && (_parserFeaturesToChange == newMask)) ? this :
+ new DeserializationConfig(this, _mapperFeatures, _deserFeatures,
+ newSet, newMask);
+ }
+
+ /**
+ * Fluent factory method that will construct and return a new configuration
+ * object instance with specified feature disabled.
+ *
+ * @since 2.5
+ */
+ public DeserializationConfig without(JsonParser.Feature feature)
+ {
+ int newSet = _parserFeatures & ~feature.getMask();
+ int newMask = _parserFeaturesToChange | feature.getMask();
+ return ((_parserFeatures == newSet) && (_parserFeaturesToChange == newMask)) ? this :
+ new DeserializationConfig(this, _mapperFeatures, _deserFeatures,
+ newSet, newMask);
+ }
+
+ /**
+ * Fluent factory method that will construct and return a new configuration
+ * object instance with specified features disabled.
+ *
+ * @since 2.5
+ */
+ public DeserializationConfig withoutFeatures(JsonParser.Feature... features)
+ {
+ int newSet = _parserFeatures;
+ int newMask = _parserFeaturesToChange;
+ for (JsonParser.Feature f : features) {
+ int mask = f.getMask();
+ newSet &= ~mask;
+ newMask |= mask;
+ }
+ return ((_parserFeatures == newSet) && (_parserFeaturesToChange == newMask)) ? this :
+ new DeserializationConfig(this, _mapperFeatures, _deserFeatures,
+ newSet, newMask);
+ }
+
/*
/**********************************************************
/* Life-cycle, deserialization-specific factory methods
@@ -351,84 +553,27 @@
(LinkedNode<DeserializationProblemHandler>) null);
}
- /**
- * Fluent factory method that will construct and return a new configuration
- * object instance with specified features enabled.
+ /*
+ /**********************************************************
+ /* JsonParser initialization
+ /**********************************************************
*/
- public DeserializationConfig with(DeserializationFeature feature)
- {
- int newDeserFeatures = (_deserFeatures | feature.getMask());
- return (newDeserFeatures == _deserFeatures) ? this :
- new DeserializationConfig(this, _mapperFeatures, newDeserFeatures);
- }
/**
- * Fluent factory method that will construct and return a new configuration
- * object instance with specified features enabled.
+ * Method called by {@link ObjectMapper} and {@link ObjectReader}
+ * to modify those {@link com.fasterxml.jackson.core.JsonParser.Feature} settings
+ * that have been configured via this config instance.
+ *
+ * @since 2.5
*/
- public DeserializationConfig with(DeserializationFeature first,
- DeserializationFeature... features)
- {
- int newDeserFeatures = _deserFeatures | first.getMask();
- for (DeserializationFeature f : features) {
- newDeserFeatures |= f.getMask();
+ public void initialize(JsonParser p) {
+ if (_parserFeaturesToChange != 0) {
+ int orig = p.getFeatureMask();
+ int newFlags = (orig & ~_parserFeaturesToChange) | _parserFeatures;
+ if (orig != newFlags) {
+ p.setFeatureMask(newFlags);
+ }
}
- return (newDeserFeatures == _deserFeatures) ? this :
- new DeserializationConfig(this, _mapperFeatures, newDeserFeatures);
- }
-
- /**
- * Fluent factory method that will construct and return a new configuration
- * object instance with specified features enabled.
- */
- public DeserializationConfig withFeatures(DeserializationFeature... features)
- {
- int newDeserFeatures = _deserFeatures;
- for (DeserializationFeature f : features) {
- newDeserFeatures |= f.getMask();
- }
- return (newDeserFeatures == _deserFeatures) ? this :
- new DeserializationConfig(this, _mapperFeatures, newDeserFeatures);
- }
-
- /**
- * Fluent factory method that will construct and return a new configuration
- * object instance with specified feature disabled.
- */
- public DeserializationConfig without(DeserializationFeature feature)
- {
- int newDeserFeatures = _deserFeatures & ~feature.getMask();
- return (newDeserFeatures == _deserFeatures) ? this :
- new DeserializationConfig(this, _mapperFeatures, newDeserFeatures);
- }
-
- /**
- * Fluent factory method that will construct and return a new configuration
- * object instance with specified features disabled.
- */
- public DeserializationConfig without(DeserializationFeature first,
- DeserializationFeature... features)
- {
- int newDeserFeatures = _deserFeatures & ~first.getMask();
- for (DeserializationFeature f : features) {
- newDeserFeatures &= ~f.getMask();
- }
- return (newDeserFeatures == _deserFeatures) ? this :
- new DeserializationConfig(this, _mapperFeatures, newDeserFeatures);
- }
-
- /**
- * Fluent factory method that will construct and return a new configuration
- * object instance with specified features disabled.
- */
- public DeserializationConfig withoutFeatures(DeserializationFeature... features)
- {
- int newDeserFeatures = _deserFeatures;
- for (DeserializationFeature f : features) {
- newDeserFeatures &= ~f.getMask();
- }
- return (newDeserFeatures == _deserFeatures) ? this :
- new DeserializationConfig(this, _mapperFeatures, newDeserFeatures);
}
/*
@@ -501,6 +646,14 @@
return (_deserFeatures & f.getMask()) != 0;
}
+ public final boolean isEnabled(JsonParser.Feature f, JsonFactory factory) {
+ int mask = f.getMask();
+ if ((_parserFeaturesToChange & mask) != 0) {
+ return (_parserFeatures & f.getMask()) != 0;
+ }
+ return factory.isEnabled(f);
+ }
+
/**
* "Bulk" access method for checking that all features specified by
* mask are enabled.
diff --git a/src/main/java/com/fasterxml/jackson/databind/DeserializationContext.java b/src/main/java/com/fasterxml/jackson/databind/DeserializationContext.java
index 72d6dd8..866be42 100644
--- a/src/main/java/com/fasterxml/jackson/databind/DeserializationContext.java
+++ b/src/main/java/com/fasterxml/jackson/databind/DeserializationContext.java
@@ -349,7 +349,7 @@
/**
* Method for checking whether we could find a deserializer
* for given type.
- *
+ *
* @param type
* @since 2.3
*/
@@ -385,6 +385,25 @@
}
/**
+ * Variant that will try to locate deserializer for current type, but without
+ * performing any contextualization (unlike {@link #findContextualValueDeserializer})
+ * or checking for need to create a {@link TypeDeserializer} (unlike
+ * {@link #findRootValueDeserializer(JavaType)}.
+ * This method is usually called from within {@link ResolvableDeserializer#resolve},
+ * and expectation is that caller then calls either
+ * {@link #handlePrimaryContextualization(JsonDeserializer, BeanProperty)} or
+ * {@link #handleSecondaryContextualization(JsonDeserializer, BeanProperty)} at a
+ * later point, as necessary.
+ *
+ * @since 2.5
+ */
+ public final JsonDeserializer<Object> findNonContextualValueDeserializer(JavaType type)
+ throws JsonMappingException
+ {
+ return _cache.findValueDeserializer(this, _factory, type);
+ }
+
+ /**
* Method for finding a deserializer for root-level value.
*/
@SuppressWarnings("unchecked")
@@ -563,10 +582,8 @@
BeanProperty prop)
throws JsonMappingException
{
- if (deser != null) {
- if (deser instanceof ContextualDeserializer) {
- deser = ((ContextualDeserializer) deser).createContextual(this, prop);
- }
+ if (deser instanceof ContextualDeserializer) {
+ deser = ((ContextualDeserializer) deser).createContextual(this, prop);
}
return deser;
}
@@ -589,8 +606,9 @@
*/
public JsonDeserializer<?> handleSecondaryContextualization(JsonDeserializer<?> deser,
BeanProperty prop)
- throws JsonMappingException {
- if (deser != null && (deser instanceof ContextualDeserializer)) {
+ throws JsonMappingException
+ {
+ if (deser instanceof ContextualDeserializer) {
deser = ((ContextualDeserializer) deser).createContextual(this, prop);
}
return deser;
diff --git a/src/main/java/com/fasterxml/jackson/databind/DeserializationFeature.java b/src/main/java/com/fasterxml/jackson/databind/DeserializationFeature.java
index 0808671..f048a4d 100644
--- a/src/main/java/com/fasterxml/jackson/databind/DeserializationFeature.java
+++ b/src/main/java/com/fasterxml/jackson/databind/DeserializationFeature.java
@@ -243,12 +243,27 @@
* If disabled, standard POJOs can only be bound from JSON null or
* JSON Object (standard meaning that no custom deserializers or
* constructors are defined; both of which can add support for other
- * kinds of JSON values); if enable, empty JSON String can be taken
+ * kinds of JSON values); if enabled, empty JSON String can be taken
* to be equivalent of JSON null.
*<p>
* Feature is disabled by default.
*/
ACCEPT_EMPTY_STRING_AS_NULL_OBJECT(false),
+
+ /**
+ * Feature that can be enabled to allow empty JSON Array
+ * value (that is, <code>[ ]</code>) to be bound to POJOs as null.
+ * If disabled, standard POJOs can only be bound from JSON null or
+ * JSON Object (standard meaning that no custom deserializers or
+ * constructors are defined; both of which can add support for other
+ * kinds of JSON values); if enabled, empty JSON Array will be taken
+ * to be equivalent of JSON null.
+ *<p>
+ * Feature is disabled by default.
+ *
+ * @since 2.5
+ */
+ ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT(false),
/**
* Feature that allows unknown Enum values to be parsed as null values.
@@ -319,14 +334,21 @@
;
private final boolean _defaultState;
+ private final int _mask;
private DeserializationFeature(boolean defaultState) {
_defaultState = defaultState;
+ _mask = (1 << ordinal());
}
@Override
public boolean enabledByDefault() { return _defaultState; }
@Override
- public int getMask() { return (1 << ordinal()); }
+ public int getMask() { return _mask; }
+
+ /**
+ * @since 2.5
+ */
+ public boolean enabledIn(int flags) { return (flags & _mask) != 0; }
}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/databind/JavaType.java b/src/main/java/com/fasterxml/jackson/databind/JavaType.java
index 3b93479..822130b 100644
--- a/src/main/java/com/fasterxml/jackson/databind/JavaType.java
+++ b/src/main/java/com/fasterxml/jackson/databind/JavaType.java
@@ -3,13 +3,14 @@
import java.lang.reflect.Modifier;
import com.fasterxml.jackson.core.type.ResolvedType;
+import com.fasterxml.jackson.databind.type.TypeFactory;
/**
* Base class for type token classes used both to contain information
* and as keys for deserializers.
*<p>
* Instances can (only) be constructed by
- * <code>com.fasterxml.jackson.databind.TypeFactory</code>.
+ * <code>com.fasterxml.jackson.databind.type.TypeFactory</code>.
*<p>
* Since 2.2 this implements {@link java.lang.reflect.Type} to allow
* it to be pushed through interfaces that only expose that type.
@@ -319,10 +320,40 @@
@Override
public JavaType containedType(int index) { return null; }
-
+
@Override
public String containedTypeName(int index) { return null; }
+ @Override
+ public abstract Class<?> getParameterSource();
+
+ /*
+ /**********************************************************
+ /* Extended API beyond ResolvedType
+ /**********************************************************
+ */
+
+ // NOTE: not defined in Resolved type
+ /**
+ * Convenience method that is functionally same as:
+ *<code>
+ * JavaType t = containedType(index);
+ * if (t == null) {
+ * t = TypeFactory.unknownType();
+ * }
+ *</code>
+ * and typically used to eliminate need for null checks for common case
+ * where we just want to check if containedType is available first; and
+ * if not, use "unknown type" (which translates to <code>java.lang.Object</code>
+ * basically).
+ *
+ * @since 2.5
+ */
+ public JavaType containedTypeOrUnknown(int index) {
+ JavaType t = containedType(index);
+ return (t == null) ? TypeFactory.unknownType() : t;
+ }
+
/*
/**********************************************************
/* Semi-public API, accessing handlers
diff --git a/src/main/java/com/fasterxml/jackson/databind/JsonNode.java b/src/main/java/com/fasterxml/jackson/databind/JsonNode.java
index 3593370..3b37137 100644
--- a/src/main/java/com/fasterxml/jackson/databind/JsonNode.java
+++ b/src/main/java/com/fasterxml/jackson/databind/JsonNode.java
@@ -5,8 +5,7 @@
import java.math.BigInteger;
import java.util.*;
-import com.fasterxml.jackson.core.JsonPointer;
-import com.fasterxml.jackson.core.TreeNode;
+import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.node.JsonNodeType;
import com.fasterxml.jackson.databind.node.MissingNode;
import com.fasterxml.jackson.databind.util.EmptyIterator;
@@ -29,9 +28,17 @@
*<p>
* Actual concrete sub-classes can be found from package
* {@link com.fasterxml.jackson.databind.node}.
+ *<p>
+ * Note that it is possible to "read" from nodes, using
+ * method {@link TreeNode#traverse(ObjectCodec)}, which will result in
+ * a {@link JsonParser} being constructed. This can be used for (relatively)
+ * efficient conversations between different representations; and it is what
+ * core databind uses for methods like {@link ObjectMapper#treeToValue(TreeNode, Class)}
+ * and {@link ObjectMapper#treeAsTokens(TreeNode)}
*/
public abstract class JsonNode
- implements TreeNode, Iterable<JsonNode>
+ implements TreeNode, Iterable<JsonNode>,
+ JsonSerializable // since 2.5; bit tricky if anyone is sub-classing but...
{
/*
/**********************************************************
@@ -66,11 +73,10 @@
/**********************************************************
*/
-// public abstract JsonToken asToken();
-
-// public abstract JsonParser.NumberType numberType();
-
-// public abstract JsonParser traverse();
+// public abstract JsonToken asToken();
+// public abstract JsonToken traverse();
+// public abstract JsonToken traverse(ObjectCodec codec);
+// public abstract JsonParser.NumberType numberType();
@Override
public int size() { return 0; }
diff --git a/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java b/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java
index 7753c88..83c304a 100644
--- a/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java
+++ b/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java
@@ -182,6 +182,8 @@
* explicitly annotated for such use.
*<p>
* Feature is enabled by default, for backwards compatibility reasons.
+ *
+ * @since 2.2
*/
ALLOW_FINAL_FIELDS_AS_MUTATORS(true),
@@ -272,18 +274,51 @@
*
* @since 2.1
*/
- USE_WRAPPER_NAME_AS_PROPERTY_NAME(false)
+ USE_WRAPPER_NAME_AS_PROPERTY_NAME(false),
+
+ /*
+ /******************************************************
+ /* Other features
+ /******************************************************
+ */
+
+ /**
+ * Feature that determines whether multiple registrations of same module
+ * should be ignored or not; if enabled, only the first registration call
+ * results in module being called, and possible duplicate calls are silently
+ * ignored; if disabled, no checking is done and all registration calls are
+ * dispatched to module.
+ *<p>
+ * Definition of "same module" is based on using {@link Module#getTypeId()};
+ * modules with same non-null <code>type id</code> are considered same for
+ * purposes of duplicate registration. This also avoids having to keep track
+ * of actual module instances; only ids will be kept track of (and only if
+ * this feature is enabled).
+ *<p>
+ * Feature is enabled by default.
+ *
+ * @since 2.5
+ */
+ IGNORE_DUPLICATE_MODULE_REGISTRATIONS(true)
+
;
private final boolean _defaultState;
+ private final int _mask;
private MapperFeature(boolean defaultState) {
_defaultState = defaultState;
+ _mask = (1 << ordinal());
}
@Override
public boolean enabledByDefault() { return _defaultState; }
@Override
- public int getMask() { return (1 << ordinal()); }
+ public int getMask() { return _mask; }
+
+ /**
+ * @since 2.5
+ */
+ public boolean enabledIn(int flags) { return (flags & _mask) != 0; }
}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/databind/MappingIterator.java b/src/main/java/com/fasterxml/jackson/databind/MappingIterator.java
index 04e8077..ade5a02 100644
--- a/src/main/java/com/fasterxml/jackson/databind/MappingIterator.java
+++ b/src/main/java/com/fasterxml/jackson/databind/MappingIterator.java
@@ -48,8 +48,7 @@
*/
@Deprecated
protected MappingIterator(JavaType type, JsonParser jp, DeserializationContext ctxt,
- JsonDeserializer<?> deser)
- {
+ JsonDeserializer<?> deser) {
this(type, jp, ctxt, deser, true, null);
}
@@ -107,12 +106,12 @@
try {
return hasNextValue();
} catch (JsonMappingException e) {
- throw new RuntimeJsonMappingException(e.getMessage(), e);
+ return (Boolean) _handleMappingException(e);
} catch (IOException e) {
- throw new RuntimeException(e.getMessage(), e);
+ return (Boolean) _handleIOException(e);
}
}
-
+
@Override
public T next()
{
@@ -132,7 +131,7 @@
@Override
public void close() throws IOException{
- if(_parser != null) {
+ if (_parser != null) {
_parser.close();
}
}
@@ -176,11 +175,11 @@
// caller should always call 'hasNext[Value]' first; but let's ensure:
if (!_hasNextChecked) {
if (!hasNextValue()) {
- throw new NoSuchElementException();
+ return _throwNoSuchElement();
}
}
if (_parser == null) {
- throw new NoSuchElementException();
+ return _throwNoSuchElement();
}
_hasNextChecked = false;
T result;
@@ -198,7 +197,7 @@
/**
* Convenience method for reading all entries accessible via
- * this iterator
+ * this iterator; resulting container will be a {@link java.util.ArrayList}.
*
* @return List of entries read
*
@@ -216,13 +215,27 @@
*
* @since 2.2
*/
- public List<T> readAll(List<T> resultList) throws IOException
+ public <L extends List<? super T>> L readAll(L resultList) throws IOException
{
while (hasNextValue()) {
- resultList.add(nextValue());
+ resultList.add(nextValue());
}
return resultList;
}
+
+ /**
+ * Convenience method for reading all entries accessible via
+ * this iterator
+ *
+ * @since 2.5
+ */
+ public <C extends Collection<? super T>> C readAll(C results) throws IOException
+ {
+ while (hasNextValue()) {
+ results.add(nextValue());
+ }
+ return results;
+ }
/*
/**********************************************************
@@ -263,4 +276,22 @@
public JsonLocation getCurrentLocation() {
return _parser.getCurrentLocation();
}
+
+ /*
+ /**********************************************************
+ /* Helper methods
+ /**********************************************************
+ */
+
+ protected <R> R _throwNoSuchElement() {
+ throw new NoSuchElementException();
+ }
+
+ protected <R> R _handleMappingException(JsonMappingException e) {
+ throw new RuntimeJsonMappingException(e.getMessage(), e);
+ }
+
+ protected <R> R _handleIOException(IOException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/Module.java b/src/main/java/com/fasterxml/jackson/databind/Module.java
index 1b268a5..37ca295 100644
--- a/src/main/java/com/fasterxml/jackson/databind/Module.java
+++ b/src/main/java/com/fasterxml/jackson/databind/Module.java
@@ -28,7 +28,7 @@
*/
/**
- * Method that returns identifier for module; this can be used by Jackson
+ * Method that returns a display that can be used by Jackson
* for informational purposes, as well as in associating extensions with
* module that provides them.
*/
@@ -41,6 +41,23 @@
@Override
public abstract Version version();
+ /**
+ * Method that returns an id that may be used to determine if two {@link Module}
+ * instances are considered to be of same type, for purpose of preventing
+ * multiple registrations of "same type of" module
+ * (see {@link com.fasterxml.jackson.databind.MapperFeature#IGNORE_DUPLICATE_MODULE_REGISTRATIONS})
+ * If `null` is returned, every instance is considered unique.
+ * If non-null value is returned, equality of id Objects is used to check whether
+ * modules should be considered to be "of same type"
+ *<p>
+ * Default implementation returns value of class name ({@link Class#getName}).
+ *
+ * @since 2.5
+ */
+ public Object getTypeId() {
+ return getClass().getName();
+ }
+
/*
/**********************************************************
/* Life-cycle: registration
diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java
index 76e8d62..a63bbd1 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java
@@ -198,11 +198,6 @@
// Quick little shortcut, to avoid having to use global TypeFactory instance...
private final static JavaType JSON_NODE_TYPE = SimpleType.constructUnsafe(JsonNode.class);
- /* !!! 03-Apr-2009, tatu: Should try to avoid direct reference... but not
- * sure what'd be simple and elegant way. So until then:
- */
- protected final static ClassIntrospector DEFAULT_INTROSPECTOR = BasicClassIntrospector.instance;
-
// 16-May-2009, tatu: Ditto ^^^
protected final static AnnotationIntrospector DEFAULT_ANNOTATION_INTROSPECTOR = new JacksonAnnotationIntrospector();
@@ -214,8 +209,10 @@
* Base settings contain defaults used for all {@link ObjectMapper}
* instances.
*/
- protected final static BaseSettings DEFAULT_BASE = new BaseSettings(DEFAULT_INTROSPECTOR,
- DEFAULT_ANNOTATION_INTROSPECTOR, STD_VISIBILITY_CHECKER, null, TypeFactory.defaultInstance(),
+ protected final static BaseSettings DEFAULT_BASE = new BaseSettings(
+ null, // can not share global ClassIntrospector any more (2.5+)
+ DEFAULT_ANNOTATION_INTROSPECTOR,
+ STD_VISIBILITY_CHECKER, null, TypeFactory.defaultInstance(),
null, StdDateFormat.instance, null,
Locale.getDefault(),
// TimeZone.getDefault()
@@ -327,6 +324,22 @@
/*
/**********************************************************
+ /* Module-related
+ /**********************************************************
+ */
+
+ /**
+ * Set of module types (as per {@link Module#getTypeId()} that have been
+ * registered; kept track of iff {@link MapperFeature#IGNORE_DUPLICATE_MODULE_REGISTRATIONS}
+ * is enabled, so that duplicate registration calls can be ignored
+ * (to avoid adding same handlers multiple times, mostly).
+ *
+ * @since 2.5
+ */
+ protected Set<Object> _registeredModuleTypes;
+
+ /*
+ /**********************************************************
/* Caching
/**********************************************************
*/
@@ -339,7 +352,7 @@
/**
* We will use a separate main-level Map for keeping track
- * of root-level deserializers. This is where most succesful
+ * of root-level deserializers. This is where most successful
* cache lookups get resolved.
* Map will contain resolvers for all kinds of types, including
* container types: this is different from the component cache
@@ -374,8 +387,7 @@
* Java Beans (based on method names and Jackson-specific annotations),
* but does not support JAXB annotations.
*/
- public ObjectMapper()
- {
+ public ObjectMapper() {
this(null, null, null);
}
@@ -384,8 +396,7 @@
* for constructing necessary {@link JsonParser}s and/or
* {@link JsonGenerator}s.
*/
- public ObjectMapper(JsonFactory jf)
- {
+ public ObjectMapper(JsonFactory jf) {
this(jf, null, null);
}
@@ -448,9 +459,11 @@
HashMap<ClassKey,Class<?>> mixins = new HashMap<ClassKey,Class<?>>();
_mixInAnnotations = mixins;
- _serializationConfig = new SerializationConfig(DEFAULT_BASE,
+
+ BaseSettings base = DEFAULT_BASE.withClassIntrospector(defaultClassIntrospector());
+ _serializationConfig = new SerializationConfig(base,
_subtypeResolver, mixins);
- _deserializationConfig = new DeserializationConfig(DEFAULT_BASE,
+ _deserializationConfig = new DeserializationConfig(base,
_subtypeResolver, mixins);
// Some overrides we may need
@@ -468,6 +481,22 @@
}
/**
+ * Overridable helper method used to construct default {@link ClassIntrospector}
+ * to use.
+ *
+ * @since 2.5
+ */
+ protected ClassIntrospector defaultClassIntrospector() {
+ return new BasicClassIntrospector();
+ }
+
+ /*
+ /**********************************************************
+ /* Methods sub-classes MUST override
+ /**********************************************************
+ */
+
+ /**
* Method for creating a new {@link ObjectMapper} instance that
* has same initial configuration as this instance. Note that this
* also requires making a copy of the underlying {@link JsonFactory}
@@ -482,15 +511,13 @@
*
* @since 2.1
*/
- public ObjectMapper copy()
- {
+ public ObjectMapper copy() {
_checkInvalidCopy(ObjectMapper.class);
return new ObjectMapper(this);
}
/**
* @since 2.1
- * @param exp
*/
protected void _checkInvalidCopy(Class<?> exp)
{
@@ -499,7 +526,67 @@
+" (version: "+version()+") does not override copy(); it has to");
}
}
+
+ /*
+ /**********************************************************
+ /* Methods sub-classes MUST override if providing custom
+ /* ObjectReader/ObjectWriter implementations
+ /**********************************************************
+ */
+ /**
+ * Factory method sub-classes must override, to produce {@link ObjectReader}
+ * instances of proper sub-type
+ *
+ * @since 2.5
+ */
+ protected ObjectReader _newReader(DeserializationConfig config) {
+ return new ObjectReader(this, config);
+ }
+
+ /**
+ * Factory method sub-classes must override, to produce {@link ObjectReader}
+ * instances of proper sub-type
+ *
+ * @since 2.5
+ */
+ protected ObjectReader _newReader(DeserializationConfig config,
+ JavaType valueType, Object valueToUpdate,
+ FormatSchema schema, InjectableValues injectableValues) {
+ return new ObjectReader(this, config, valueType, valueToUpdate, schema, injectableValues);
+ }
+
+ /**
+ * Factory method sub-classes must override, to produce {@link ObjectWriter}
+ * instances of proper sub-type
+ *
+ * @since 2.5
+ */
+ protected ObjectWriter _newWriter(SerializationConfig config) {
+ return new ObjectWriter(this, config);
+ }
+
+ /**
+ * Factory method sub-classes must override, to produce {@link ObjectWriter}
+ * instances of proper sub-type
+ *
+ * @since 2.5
+ */
+ protected ObjectWriter _newWriter(SerializationConfig config, FormatSchema schema) {
+ return new ObjectWriter(this, config, schema);
+ }
+
+ /**
+ * Factory method sub-classes must override, to produce {@link ObjectWriter}
+ * instances of proper sub-type
+ *
+ * @since 2.5
+ */
+ protected ObjectWriter _newWriter(SerializationConfig config,
+ JavaType rootType, PrettyPrinter pp) {
+ return new ObjectWriter(this, config, rootType, pp);
+ }
+
/*
/**********************************************************
/* Versioned impl
@@ -514,7 +601,7 @@
public Version version() {
return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION;
}
-
+
/*
/**********************************************************
/* Module registration, discovery
@@ -530,6 +617,19 @@
*/
public ObjectMapper registerModule(Module module)
{
+ if (isEnabled(MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS)) {
+ Object typeId = module.getTypeId();
+ if (typeId != null) {
+ if (_registeredModuleTypes == null) {
+ _registeredModuleTypes = new HashSet<Object>();
+ }
+ // try adding; if already had it, should skip
+ if (!_registeredModuleTypes.add(typeId)) {
+ return this;
+ }
+ }
+ }
+
/* Let's ensure we have access to name and version information,
* even if we do not have immediate use for either. This way we know
* that they will be available from beginning
@@ -685,7 +785,7 @@
@Override
public void setMixInAnnotations(Class<?> target, Class<?> mixinSource) {
- mapper.addMixInAnnotations(target, mixinSource);
+ mapper.addMixIn(target, mixinSource);
}
@Override
@@ -874,7 +974,7 @@
/* Configuration: mix-in annotations
/**********************************************************
*/
-
+
/**
* Method to use for defining mix-in annotations to use for augmenting
* annotations that processable (serializable / deserializable)
@@ -886,8 +986,10 @@
* Annotations from source classes (and their supertypes)
* will <b>override</b>
* annotations that target classes (and their super-types) have.
+ *
+ * @since 2.5
*/
- public final void setMixInAnnotations(Map<Class<?>, Class<?>> sourceMixins)
+ public ObjectMapper setMixIns(Map<Class<?>, Class<?>> sourceMixins)
{
_mixInAnnotations.clear();
if (sourceMixins != null && sourceMixins.size() > 0) {
@@ -895,6 +997,7 @@
_mixInAnnotations.put(new ClassKey(en.getKey()), en.getValue());
}
}
+ return this;
}
/**
@@ -906,35 +1009,39 @@
* @param target Class (or interface) whose annotations to effectively override
* @param mixinSource Class (or interface) whose annotations are to
* be "added" to target's annotations, overriding as necessary
- */
- public final void addMixInAnnotations(Class<?> target, Class<?> mixinSource)
- {
- _mixInAnnotations.put(new ClassKey(target), mixinSource);
- }
-
- /**
- * Method to use for adding mix-in annotations to use for augmenting
- * specified class or interface. All annotations from
- * <code>mixinSource</code> are taken to override annotations
- * that <code>target</code> (or its supertypes) has.
*
- * @param target Class (or interface) whose annotations to effectively override
- * @param mixinSource Class (or interface) whose annotations are to
- * be "added" to target's annotations, overriding as necessary
+ * @since 2.5
*/
- public final ObjectMapper addMixIn(Class<?> target, Class<?> mixinSource)
+ public ObjectMapper addMixIn(Class<?> target, Class<?> mixinSource)
{
_mixInAnnotations.put(new ClassKey(target), mixinSource);
return this;
}
- public final Class<?> findMixInClassFor(Class<?> cls) {
+ public Class<?> findMixInClassFor(Class<?> cls) {
return (_mixInAnnotations == null) ? null : _mixInAnnotations.get(new ClassKey(cls));
}
- public final int mixInCount() {
+ public int mixInCount() {
return (_mixInAnnotations == null) ? 0 : _mixInAnnotations.size();
}
+
+
+ /**
+ * @deprecated Since 2.5: replaced by a fluent form of the method; {@link #setMixIns}.
+ */
+ @Deprecated
+ public void setMixInAnnotations(Map<Class<?>, Class<?>> sourceMixins) {
+ setMixIns(sourceMixins);
+ }
+
+ /**
+ * @deprecated Since 2.5: replaced by a fluent form of the method; {@link #addMixIn(Class, Class)}.
+ */
+ @Deprecated
+ public final void addMixInAnnotations(Class<?> target, Class<?> mixinSource) {
+ addMixIn(target, mixinSource);
+ }
/*
/**********************************************************
@@ -1051,6 +1158,14 @@
}
/**
+ * @since 2.5
+ */
+ public PropertyNamingStrategy getPropertyNamingStrategy() {
+ // arbitrary choice but let's do:
+ return _serializationConfig.getPropertyNamingStrategy();
+ }
+
+ /**
* Method for setting defalt POJO property inclusion strategy for serialization.
*/
public ObjectMapper setSerializationInclusion(JsonInclude.Include incl) {
@@ -1088,12 +1203,23 @@
* Method for enabling automatic inclusion of type information, needed
* for proper deserialization of polymorphic types (unless types
* have been annotated with {@link com.fasterxml.jackson.annotation.JsonTypeInfo}).
+ *<P>
+ * NOTE: use of <code>JsonTypeInfo.As#EXTERNAL_PROPERTY</code> <b>NOT SUPPORTED</b>;
+ * and attempts of do so will throw an {@link IllegalArgumentException} to make
+ * this limitation explicit.
*
* @param applicability Defines kinds of types for which additional type information
* is added; see {@link DefaultTyping} for more information.
*/
public ObjectMapper enableDefaultTyping(DefaultTyping applicability, JsonTypeInfo.As includeAs)
{
+ /* 18-Sep-2014, tatu: Let's add explicit check to ensure no one tries to
+ * use "As.EXTERNAL_PROPERTY", since that will not work (with 2.5+)
+ */
+ if (includeAs == JsonTypeInfo.As.EXTERNAL_PROPERTY) {
+ throw new IllegalArgumentException("Can not use includeAs of "+includeAs);
+ }
+
TypeResolverBuilder<?> typer = new DefaultTypeResolverBuilder(applicability);
// we'll always use full class name, when using defaulting
typer = typer.init(JsonTypeInfo.Id.CLASS, null);
@@ -1207,6 +1333,20 @@
/* Configuration, deserialization
/**********************************************************
*/
+
+ /**
+ * Method that can be used to get hold of {@link JsonNodeFactory}
+ * that this mapper will use when directly constructing
+ * root {@link JsonNode} instances for Trees.
+ *<p>
+ * Note: this is just a shortcut for calling
+ *<pre>
+ * getDeserializationConfig().getNodeFactory()
+ *</pre>
+ */
+ public JsonNodeFactory getNodeFactory() {
+ return _deserializationConfig.getNodeFactory();
+ }
/**
* Method for specifying {@link JsonNodeFactory} to use for
@@ -1307,8 +1447,8 @@
* @since 2.4
*/
public ObjectMapper setConfig(SerializationConfig config) {
- _serializationConfig = config;
- return this;
+ _serializationConfig = config;
+ return this;
}
/*
@@ -1353,6 +1493,14 @@
}
/**
+ * @since 2.5
+ */
+ public DateFormat getDateFormat() {
+ // arbitrary choice but let's do:
+ return _serializationConfig.getDateFormat();
+ }
+
+ /**
* Method for configuring {@link HandlerInstantiator} to use for creating
* instances of handlers (such as serializers, deserializers, type and type
* id resolvers), given a class.
@@ -1397,11 +1545,19 @@
/*
/**********************************************************
- /* Configuration, simple features
+ /* Configuration, simple features: MapperFeature
/**********************************************************
*/
/**
+ * Method for checking whether given {@link MapperFeature} is enabled.
+ */
+ public boolean isEnabled(MapperFeature f) {
+ // ok to use either one, should be kept in sync
+ return _serializationConfig.isEnabled(f);
+ }
+
+ /**
* Method for changing state of an on/off mapper feature for
* this mapper instance.
*/
@@ -1412,54 +1568,6 @@
_deserializationConfig.with(f) : _deserializationConfig.without(f);
return this;
}
-
- /**
- * Method for changing state of an on/off serialization feature for
- * this object mapper.
- */
- public ObjectMapper configure(SerializationFeature f, boolean state) {
- _serializationConfig = state ?
- _serializationConfig.with(f) : _serializationConfig.without(f);
- return this;
- }
-
- /**
- * Method for changing state of an on/off deserialization feature for
- * this object mapper.
- */
- public ObjectMapper configure(DeserializationFeature f, boolean state) {
- _deserializationConfig = state ?
- _deserializationConfig.with(f) : _deserializationConfig.without(f);
- return this;
- }
-
- /**
- * Method for changing state of an on/off {@link JsonParser} feature for
- * {@link JsonFactory} instance this object mapper uses.
- *<p>
- * This is method is basically a shortcut method for calling
- * {@link JsonFactory#enable} on the shared
- * {@link JsonFactory} this mapper uses (which is accessible
- * using {@link #getJsonFactory}).
- */
- public ObjectMapper configure(JsonParser.Feature f, boolean state) {
- _jsonFactory.configure(f, state);
- return this;
- }
-
- /**
- * Method for changing state of an on/off {@link JsonGenerator} feature for
- * {@link JsonFactory} instance this object mapper uses.
- *<p>
- * This is method is basically a shortcut method for calling
- * {@link JsonFactory#enable} on the shared
- * {@link JsonFactory} this mapper uses (which is accessible
- * using {@link #getJsonFactory}).
- */
- public ObjectMapper configure(JsonGenerator.Feature f, boolean state) {
- _jsonFactory.configure(f, state);
- return this;
- }
/**
* Method for enabling specified {@link MapperConfig} features.
@@ -1481,6 +1589,92 @@
return this;
}
+ /*
+ /**********************************************************
+ /* Configuration, simple features: SerializationFeature
+ /**********************************************************
+ */
+
+ /**
+ * Method for checking whether given serialization-specific
+ * feature is enabled.
+ */
+ public boolean isEnabled(SerializationFeature f) {
+ return _serializationConfig.isEnabled(f);
+ }
+
+ /**
+ * Method for changing state of an on/off serialization feature for
+ * this object mapper.
+ */
+ public ObjectMapper configure(SerializationFeature f, boolean state) {
+ _serializationConfig = state ?
+ _serializationConfig.with(f) : _serializationConfig.without(f);
+ return this;
+ }
+
+ /**
+ * Method for enabling specified {@link DeserializationConfig} feature.
+ * Modifies and returns this instance; no new object is created.
+ */
+ public ObjectMapper enable(SerializationFeature f) {
+ _serializationConfig = _serializationConfig.with(f);
+ return this;
+ }
+
+ /**
+ * Method for enabling specified {@link DeserializationConfig} features.
+ * Modifies and returns this instance; no new object is created.
+ */
+ public ObjectMapper enable(SerializationFeature first,
+ SerializationFeature... f) {
+ _serializationConfig = _serializationConfig.with(first, f);
+ return this;
+ }
+
+ /**
+ * Method for enabling specified {@link DeserializationConfig} features.
+ * Modifies and returns this instance; no new object is created.
+ */
+ public ObjectMapper disable(SerializationFeature f) {
+ _serializationConfig = _serializationConfig.without(f);
+ return this;
+ }
+
+ /**
+ * Method for enabling specified {@link DeserializationConfig} features.
+ * Modifies and returns this instance; no new object is created.
+ */
+ public ObjectMapper disable(SerializationFeature first,
+ SerializationFeature... f) {
+ _serializationConfig = _serializationConfig.without(first, f);
+ return this;
+ }
+
+ /*
+ /**********************************************************
+ /* Configuration, simple features: DeserializationFeature
+ /**********************************************************
+ */
+
+ /**
+ * Method for checking whether given deserialization-specific
+ * feature is enabled.
+ */
+ public boolean isEnabled(DeserializationFeature f) {
+ return _deserializationConfig.isEnabled(f);
+ }
+
+ /**
+ * Method for changing state of an on/off deserialization feature for
+ * this object mapper.
+ */
+ public ObjectMapper configure(DeserializationFeature f, boolean state) {
+ _deserializationConfig = state ?
+ _deserializationConfig.with(f) : _deserializationConfig.without(f);
+ return this;
+ }
+
/**
* Method for enabling specified {@link DeserializationConfig} features.
* Modifies and returns this instance; no new object is created.
@@ -1519,70 +1713,117 @@
return this;
}
- /**
- * Method for enabling specified {@link DeserializationConfig} feature.
- * Modifies and returns this instance; no new object is created.
+ /*
+ /**********************************************************
+ /* Configuration, simple features: JsonParser.Feature
+ /**********************************************************
*/
- public ObjectMapper enable(SerializationFeature f) {
- _serializationConfig = _serializationConfig.with(f);
+
+ public boolean isEnabled(JsonParser.Feature f) {
+ return _deserializationConfig.isEnabled(f, _jsonFactory);
+ }
+
+ /**
+ * Method for changing state of specified {@link com.fasterxml.jackson.core.JsonParser.Feature}s
+ * for parser instances this object mapper creates.
+ *<p>
+ * Note that this is equivalent to directly calling same method
+ * on {@link #getFactory}.
+ */
+ public ObjectMapper configure(JsonParser.Feature f, boolean state) {
+ _jsonFactory.configure(f, state);
return this;
}
/**
- * Method for enabling specified {@link DeserializationConfig} features.
- * Modifies and returns this instance; no new object is created.
+ * Method for enabling specified {@link com.fasterxml.jackson.core.JsonParser.Feature}s
+ * for parser instances this object mapper creates.
+ *<p>
+ * Note that this is equivalent to directly calling same method on {@link #getFactory}.
+ *
+ * @since 2.5
*/
- public ObjectMapper enable(SerializationFeature first,
- SerializationFeature... f) {
- _serializationConfig = _serializationConfig.with(first, f);
+ public ObjectMapper enable(JsonParser.Feature... features) {
+ for (JsonParser.Feature f : features) {
+ _jsonFactory.enable(f);
+ }
return this;
}
/**
- * Method for enabling specified {@link DeserializationConfig} features.
- * Modifies and returns this instance; no new object is created.
+ * Method for disabling specified {@link com.fasterxml.jackson.core.JsonParser.Feature}s
+ * for parser instances this object mapper creates.
+ *<p>
+ * Note that this is equivalent to directly calling same method on {@link #getFactory}.
+ *
+ * @since 2.5
*/
- public ObjectMapper disable(SerializationFeature f) {
- _serializationConfig = _serializationConfig.without(f);
+ public ObjectMapper disable(JsonParser.Feature... features) {
+ for (JsonParser.Feature f : features) {
+ _jsonFactory.disable(f);
+ }
return this;
}
-
- /**
- * Method for enabling specified {@link DeserializationConfig} features.
- * Modifies and returns this instance; no new object is created.
- */
- public ObjectMapper disable(SerializationFeature first,
- SerializationFeature... f) {
- _serializationConfig = _serializationConfig.without(first, f);
- return this;
- }
-
- /**
- * Method for checking whether given Mapper
- * feature is enabled.
- */
- public boolean isEnabled(MapperFeature f) {
- // ok to use either one, should be kept in sync
- return _serializationConfig.isEnabled(f);
- }
-
- /**
- * Method for checking whether given serialization-specific
- * feature is enabled.
- */
- public boolean isEnabled(SerializationFeature f) {
- return _serializationConfig.isEnabled(f);
- }
- /**
- * Method for checking whether given deserialization-specific
- * feature is enabled.
+ /*
+ /**********************************************************
+ /* Configuration, simple features: JsonGenerator.Feature
+ /**********************************************************
*/
- public boolean isEnabled(DeserializationFeature f) {
- return _deserializationConfig.isEnabled(f);
+
+ public boolean isEnabled(JsonGenerator.Feature f) {
+ return _serializationConfig.isEnabled(f, _jsonFactory);
}
/**
+ * Method for changing state of an on/off {@link JsonGenerator} feature for
+ * generator instances this object mapper creates.
+ *<p>
+ * Note that this is equivalent to directly calling same method
+ * on {@link #getFactory}.
+ */
+ public ObjectMapper configure(JsonGenerator.Feature f, boolean state) {
+ _jsonFactory.configure(f, state);
+ return this;
+ }
+
+ /**
+ * Method for enabling specified {@link com.fasterxml.jackson.core.JsonGenerator.Feature}s
+ * for parser instances this object mapper creates.
+ *<p>
+ * Note that this is equivalent to directly calling same method on {@link #getFactory}.
+ *
+ * @since 2.5
+ */
+ public ObjectMapper enable(JsonGenerator.Feature... features) {
+ for (JsonGenerator.Feature f : features) {
+ _jsonFactory.enable(f);
+ }
+ return this;
+ }
+
+ /**
+ * Method for disabling specified {@link com.fasterxml.jackson.core.JsonGenerator.Feature}s
+ * for parser instances this object mapper creates.
+ *<p>
+ * Note that this is equivalent to directly calling same method on {@link #getFactory}.
+ *
+ * @since 2.5
+ */
+ public ObjectMapper disable(JsonGenerator.Feature... features) {
+ for (JsonGenerator.Feature f : features) {
+ _jsonFactory.disable(f);
+ }
+ return this;
+ }
+
+ /*
+ /**********************************************************
+ /* Configuration, simple features: JsonFactory.Feature
+ /**********************************************************
+ */
+
+ /**
* Convenience method, equivalent to:
*<pre>
* getJsonFactory().isEnabled(f);
@@ -1592,40 +1833,6 @@
return _jsonFactory.isEnabled(f);
}
- /**
- * Convenience method, equivalent to:
- *<pre>
- * getJsonFactory().isEnabled(f);
- *</pre>
- */
- public boolean isEnabled(JsonParser.Feature f) {
- return _jsonFactory.isEnabled(f);
- }
-
- /**
- * Convenience method, equivalent to:
- *<pre>
- * getJsonFactory().isEnabled(f);
- *</pre>
- */
- public boolean isEnabled(JsonGenerator.Feature f) {
- return _jsonFactory.isEnabled(f);
- }
-
- /**
- * Method that can be used to get hold of {@link JsonNodeFactory}
- * that this mapper will use when directly constructing
- * root {@link JsonNode} instances for Trees.
- *<p>
- * Note: this is just a shortcut for calling
- *<pre>
- * getDeserializationConfig().getNodeFactory()
- *</pre>
- */
- public JsonNodeFactory getNodeFactory() {
- return _deserializationConfig.getNodeFactory();
- }
-
/*
/**********************************************************
/* Public API (from ObjectCodec): deserialization
@@ -1968,8 +2175,7 @@
* @param n Root node of the tree that resulting parser will read from
*/
@Override
- public JsonParser treeAsTokens(TreeNode n)
- {
+ public JsonParser treeAsTokens(TreeNode n) {
return new TreeTraversingParser((JsonNode) n, this);
}
@@ -2051,7 +2257,8 @@
*<p>
* NOTE: since this method does NOT throw exceptions, but internal
* processing may, caller usually has little information as to why
- * serialization would fail.
+ * serialization would fail. If you want access to internal {@link Exception},
+ * call {@link #canSerialize(Class, AtomicReference)} instead.
*
* @return True if mapper can find a serializer for instances of
* given class (potentially serializable), false otherwise (not
@@ -2075,8 +2282,15 @@
/**
* Method that can be called to check whether mapper thinks
* it could deserialize an Object of given type.
- * Check is done
- * by checking whether a deserializer can be found for the type.
+ * Check is done by checking whether a registered deserializer can
+ * be found or built for the type; if not (either by no mapping being
+ * found, or through an <code>Exception</code> being thrown, false
+ * is returned.
+ *<p>
+ * <b>NOTE</b>: in case an exception is thrown during course of trying
+ * co construct matching deserializer, it will be effectively swallowed.
+ * If you want access to that exception, call
+ * {@link #canDeserialize(JavaType, AtomicReference)} instead.
*
* @return True if mapper can find a serializer for instances of
* given class (potentially serializable), false otherwise (not
@@ -2381,8 +2595,9 @@
* Convenience method for constructing {@link ObjectWriter}
* with default settings.
*/
- public ObjectWriter writer() {
- return new ObjectWriter(this, getSerializationConfig());
+ @SuppressWarnings("unchecked")
+ public <W extends ObjectWriter> W writer() {
+ return (W) _newWriter(getSerializationConfig());
}
/**
@@ -2390,8 +2605,9 @@
* specified feature enabled (compared to settings that this
* mapper instance has).
*/
- public ObjectWriter writer(SerializationFeature feature) {
- return new ObjectWriter(this, getSerializationConfig().with(feature));
+ @SuppressWarnings("unchecked")
+ public <W extends ObjectWriter> W writer(SerializationFeature feature) {
+ return (W) _newWriter(getSerializationConfig().with(feature));
}
/**
@@ -2399,9 +2615,10 @@
* specified features enabled (compared to settings that this
* mapper instance has).
*/
- public ObjectWriter writer(SerializationFeature first,
+ @SuppressWarnings("unchecked")
+ public <W extends ObjectWriter> W writer(SerializationFeature first,
SerializationFeature... other) {
- return new ObjectWriter(this, getSerializationConfig().with(first, other));
+ return (W) _newWriter(getSerializationConfig().with(first, other));
}
/**
@@ -2409,27 +2626,34 @@
* serialize objects using specified {@link DateFormat}; or, if
* null passed, using timestamp (64-bit number.
*/
- public ObjectWriter writer(DateFormat df) {
- return new ObjectWriter(this, getSerializationConfig().with(df));
+ @SuppressWarnings("unchecked")
+ public <W extends ObjectWriter> W writer(DateFormat df) {
+ return (W) _newWriter(getSerializationConfig().with(df));
}
/**
* Factory method for constructing {@link ObjectWriter} that will
* serialize objects using specified JSON View (filter).
*/
- public ObjectWriter writerWithView(Class<?> serializationView) {
- return new ObjectWriter(this, getSerializationConfig().withView(serializationView));
+ @SuppressWarnings("unchecked")
+ public <W extends ObjectWriter> W writerWithView(Class<?> serializationView) {
+ return (W) _newWriter(getSerializationConfig().withView(serializationView));
}
/**
* Factory method for constructing {@link ObjectWriter} that will
* serialize objects using specified root type, instead of actual
- * runtime type of value. Type must be a super-type of runtime
- * type.
+ * runtime type of value. Type must be a super-type of runtime type.
+ *<p>
+ * Main reason for using this method is performance, as writer is able
+ * to pre-fetch serializer to use before write, and if writer is used
+ * more than once this avoids addition per-value serializer lookups.
+ *
+ * @since 2.5
*/
- public ObjectWriter writerWithType(Class<?> rootType) {
- return new ObjectWriter(this, getSerializationConfig(),
- // 15-Mar-2013, tatu: Important! Indicate that static typing is needed:
+ @SuppressWarnings("unchecked")
+ public <W extends ObjectWriter> W writerFor(Class<?> rootType) {
+ return (W) _newWriter(getSerializationConfig(),
((rootType == null) ? null :_typeFactory.constructType(rootType)),
/*PrettyPrinter*/null);
}
@@ -2438,10 +2662,16 @@
* Factory method for constructing {@link ObjectWriter} that will
* serialize objects using specified root type, instead of actual
* runtime type of value. Type must be a super-type of runtime type.
+ *<p>
+ * Main reason for using this method is performance, as writer is able
+ * to pre-fetch serializer to use before write, and if writer is used
+ * more than once this avoids addition per-value serializer lookups.
+ *
+ * @since 2.5
*/
- public ObjectWriter writerWithType(TypeReference<?> rootType) {
- return new ObjectWriter(this, getSerializationConfig(),
- // 15-Mar-2013, tatu: Important! Indicate that static typing is needed:
+ @SuppressWarnings("unchecked")
+ public <W extends ObjectWriter> W writerFor(TypeReference<?> rootType) {
+ return (W) _newWriter(getSerializationConfig(),
((rootType == null) ? null : _typeFactory.constructType(rootType)),
/*PrettyPrinter*/null);
}
@@ -2450,9 +2680,49 @@
* Factory method for constructing {@link ObjectWriter} that will
* serialize objects using specified root type, instead of actual
* runtime type of value. Type must be a super-type of runtime type.
+ *<p>
+ * Main reason for using this method is performance, as writer is able
+ * to pre-fetch serializer to use before write, and if writer is used
+ * more than once this avoids addition per-value serializer lookups.
+ *
+ * @since 2.5
*/
- public ObjectWriter writerWithType(JavaType rootType) {
- return new ObjectWriter(this, getSerializationConfig(), rootType, /*PrettyPrinter*/null);
+ @SuppressWarnings("unchecked")
+ public <W extends ObjectWriter> W writerFor(JavaType rootType) {
+ return (W) _newWriter(getSerializationConfig(), rootType, /*PrettyPrinter*/null);
+ }
+
+ /**
+ * @deprecated Since 2.5, use {@link #writerFor(Class)} instead
+ */
+ @SuppressWarnings("unchecked")
+ @Deprecated
+ public <W extends ObjectWriter> W writerWithType(Class<?> rootType) {
+ return (W) _newWriter(getSerializationConfig(),
+ // 15-Mar-2013, tatu: Important! Indicate that static typing is needed:
+ ((rootType == null) ? null :_typeFactory.constructType(rootType)),
+ /*PrettyPrinter*/null);
+ }
+
+ /**
+ * @deprecated Since 2.5, use {@link #writerFor(TypeReference)} instead
+ */
+ @SuppressWarnings("unchecked")
+ @Deprecated
+ public <W extends ObjectWriter> W writerWithType(TypeReference<?> rootType) {
+ return (W) _newWriter(getSerializationConfig(),
+ // 15-Mar-2013, tatu: Important! Indicate that static typing is needed:
+ ((rootType == null) ? null : _typeFactory.constructType(rootType)),
+ /*PrettyPrinter*/null);
+ }
+
+ /**
+ * @deprecated Since 2.5, use {@link #writerFor(JavaType)} instead
+ */
+ @SuppressWarnings("unchecked")
+ @Deprecated
+ public <W extends ObjectWriter> W writerWithType(JavaType rootType) {
+ return (W) _newWriter(getSerializationConfig(), rootType, /*PrettyPrinter*/null);
}
/**
@@ -2460,19 +2730,21 @@
* serialize objects using specified pretty printer for indentation
* (or if null, no pretty printer)
*/
- public ObjectWriter writer(PrettyPrinter pp) {
+ @SuppressWarnings("unchecked")
+ public <W extends ObjectWriter> W writer(PrettyPrinter pp) {
if (pp == null) { // need to use a marker to indicate explicit disabling of pp
pp = ObjectWriter.NULL_PRETTY_PRINTER;
}
- return new ObjectWriter(this, getSerializationConfig(), /*root type*/ null, pp);
+ return (W) _newWriter(getSerializationConfig(), /*root type*/ null, pp);
}
/**
* Factory method for constructing {@link ObjectWriter} that will
* serialize objects using the default pretty printer for indentation
*/
- public ObjectWriter writerWithDefaultPrettyPrinter() {
- return new ObjectWriter(this, getSerializationConfig(),
+ @SuppressWarnings("unchecked")
+ public <W extends ObjectWriter> W writerWithDefaultPrettyPrinter() {
+ return (W) _newWriter(getSerializationConfig(),
/*root type*/ null, _defaultPrettyPrinter());
}
@@ -2480,9 +2752,9 @@
* Factory method for constructing {@link ObjectWriter} that will
* serialize objects using specified filter provider.
*/
- public ObjectWriter writer(FilterProvider filterProvider) {
- return new ObjectWriter(this,
- getSerializationConfig().withFilters(filterProvider));
+ @SuppressWarnings("unchecked")
+ public <W extends ObjectWriter> W writer(FilterProvider filterProvider) {
+ return (W) _newWriter(getSerializationConfig().withFilters(filterProvider));
}
/**
@@ -2492,9 +2764,10 @@
*
* @param schema Schema to pass to generator
*/
- public ObjectWriter writer(FormatSchema schema) {
+ @SuppressWarnings("unchecked")
+ public <W extends ObjectWriter> W writer(FormatSchema schema) {
_verifySchemaType(schema);
- return new ObjectWriter(this, getSerializationConfig(), schema);
+ return (W) _newWriter(getSerializationConfig(), schema);
}
/**
@@ -2503,8 +2776,9 @@
*
* @since 2.1
*/
- public ObjectWriter writer(Base64Variant defaultBase64) {
- return new ObjectWriter(this, getSerializationConfig().with(defaultBase64));
+ @SuppressWarnings("unchecked")
+ public <W extends ObjectWriter> W writer(Base64Variant defaultBase64) {
+ return (W) _newWriter(getSerializationConfig().with(defaultBase64));
}
/**
@@ -2513,8 +2787,9 @@
*
* @since 2.3
*/
- public ObjectWriter writer(CharacterEscapes escapes) {
- return writer().with(escapes);
+ @SuppressWarnings("unchecked")
+ public <W extends ObjectWriter> W writer(CharacterEscapes escapes) {
+ return (W) _newWriter(getSerializationConfig()).with(escapes);
}
/**
@@ -2523,8 +2798,9 @@
*
* @since 2.3
*/
- public ObjectWriter writer(ContextAttributes attrs) {
- return new ObjectWriter(this, getSerializationConfig().with(attrs));
+ @SuppressWarnings("unchecked")
+ public <W extends ObjectWriter> W writer(ContextAttributes attrs) {
+ return (W) _newWriter(getSerializationConfig().with(attrs));
}
/*
@@ -2539,9 +2815,9 @@
* default settings. Note that the resulting instance is NOT usable as is,
* without defining expected value type.
*/
- public ObjectReader reader() {
- return new ObjectReader(this, getDeserializationConfig())
- .with(_injectableValues);
+ @SuppressWarnings("unchecked")
+ public <T extends ObjectReader> T reader() {
+ return (T) _newReader(getDeserializationConfig()).with(_injectableValues);
}
/**
@@ -2551,8 +2827,9 @@
* Note that the resulting instance is NOT usable as is,
* without defining expected value type.
*/
- public ObjectReader reader(DeserializationFeature feature) {
- return new ObjectReader(this, getDeserializationConfig().with(feature));
+ @SuppressWarnings("unchecked")
+ public <T extends ObjectReader> T reader(DeserializationFeature feature) {
+ return (T) _newReader(getDeserializationConfig().with(feature));
}
/**
@@ -2562,9 +2839,10 @@
* Note that the resulting instance is NOT usable as is,
* without defining expected value type.
*/
- public ObjectReader reader(DeserializationFeature first,
+ @SuppressWarnings("unchecked")
+ public <T extends ObjectReader> T reader(DeserializationFeature first,
DeserializationFeature... other) {
- return new ObjectReader(this, getDeserializationConfig().with(first, other));
+ return (T) _newReader(getDeserializationConfig().with(first, other));
}
/**
@@ -2577,10 +2855,10 @@
* Runtime type of value object is used for locating deserializer,
* unless overridden by other factory methods of {@link ObjectReader}
*/
- public ObjectReader readerForUpdating(Object valueToUpdate)
- {
+ @SuppressWarnings("unchecked")
+ public <T extends ObjectReader> T readerForUpdating(Object valueToUpdate) {
JavaType t = _typeFactory.constructType(valueToUpdate.getClass());
- return new ObjectReader(this, getDeserializationConfig(), t, valueToUpdate,
+ return (T) _newReader(getDeserializationConfig(), t, valueToUpdate,
null, _injectableValues);
}
@@ -2588,9 +2866,9 @@
* Factory method for constructing {@link ObjectReader} that will
* read or update instances of specified type
*/
- public ObjectReader reader(JavaType type)
- {
- return new ObjectReader(this, getDeserializationConfig(), type, null,
+ @SuppressWarnings("unchecked")
+ public <T extends ObjectReader> T reader(JavaType type) {
+ return (T) _newReader(getDeserializationConfig(), type, null,
null, _injectableValues);
}
@@ -2598,27 +2876,29 @@
* Factory method for constructing {@link ObjectReader} that will
* read or update instances of specified type
*/
- public ObjectReader reader(Class<?> type)
- {
- return reader(_typeFactory.constructType(type));
+ @SuppressWarnings("unchecked")
+ public <T extends ObjectReader> T reader(Class<?> type) {
+ return (T) _newReader(getDeserializationConfig(), _typeFactory.constructType(type), null,
+ null, _injectableValues);
}
/**
* Factory method for constructing {@link ObjectReader} that will
* read or update instances of specified type
*/
- public ObjectReader reader(TypeReference<?> type)
- {
- return reader(_typeFactory.constructType(type));
+ @SuppressWarnings("unchecked")
+ public <T extends ObjectReader> T reader(TypeReference<?> type) {
+ return (T)_newReader(getDeserializationConfig(), _typeFactory.constructType(type), null,
+ null, _injectableValues);
}
/**
* Factory method for constructing {@link ObjectReader} that will
* use specified {@link JsonNodeFactory} for constructing JSON trees.
*/
- public ObjectReader reader(JsonNodeFactory f)
- {
- return new ObjectReader(this, getDeserializationConfig()).with(f);
+ @SuppressWarnings("unchecked")
+ public <T extends ObjectReader> T reader(JsonNodeFactory f) {
+ return (T) _newReader(getDeserializationConfig()).with(f);
}
/**
@@ -2628,9 +2908,10 @@
*
* @param schema Schema to pass to parser
*/
- public ObjectReader reader(FormatSchema schema) {
+ @SuppressWarnings("unchecked")
+ public <T extends ObjectReader> T reader(FormatSchema schema) {
_verifySchemaType(schema);
- return new ObjectReader(this, getDeserializationConfig(), null, null,
+ return (T)_newReader(getDeserializationConfig(), null, null,
schema, _injectableValues);
}
@@ -2640,8 +2921,9 @@
*
* @param injectableValues Injectable values to use
*/
- public ObjectReader reader(InjectableValues injectableValues) {
- return new ObjectReader(this, getDeserializationConfig(), null, null,
+ @SuppressWarnings("unchecked")
+ public <T extends ObjectReader> T reader(InjectableValues injectableValues) {
+ return (T)_newReader(getDeserializationConfig(), null, null,
null, injectableValues);
}
@@ -2649,8 +2931,9 @@
* Factory method for constructing {@link ObjectReader} that will
* deserialize objects using specified JSON View (filter).
*/
- public ObjectReader readerWithView(Class<?> view) {
- return new ObjectReader(this, getDeserializationConfig().withView(view));
+ @SuppressWarnings("unchecked")
+ public <T extends ObjectReader> T readerWithView(Class<?> view) {
+ return (T) _newReader(getDeserializationConfig().withView(view));
}
/**
@@ -2659,8 +2942,9 @@
*
* @since 2.1
*/
- public ObjectReader reader(Base64Variant defaultBase64) {
- return new ObjectReader(this, getDeserializationConfig().with(defaultBase64));
+ @SuppressWarnings("unchecked")
+ public <T extends ObjectReader> T reader(Base64Variant defaultBase64) {
+ return (T) _newReader(getDeserializationConfig().with(defaultBase64));
}
/**
@@ -2669,16 +2953,17 @@
*
* @since 2.3
*/
- public ObjectReader reader(ContextAttributes attrs) {
- return new ObjectReader(this, getDeserializationConfig().with(attrs));
+ @SuppressWarnings("unchecked")
+ public <T extends ObjectReader> T reader(ContextAttributes attrs) {
+ return (T) _newReader(getDeserializationConfig().with(attrs));
}
-
+
/*
/**********************************************************
/* Extended Public API: convenience type conversion
/**********************************************************
*/
-
+
/**
* Convenience method for doing two-step conversion from given value, into
* instance of given value type. This is functionality equivalent to first
@@ -2860,18 +3145,10 @@
* call write functionality
*/
protected final void _configAndWriteValue(JsonGenerator jgen, Object value)
- throws IOException, JsonGenerationException, JsonMappingException
+ throws IOException
{
SerializationConfig cfg = getSerializationConfig();
- // [JACKSON-96]: allow enabling pretty printing for ObjectMapper directly
- if (cfg.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
- jgen.useDefaultPrettyPrinter();
- }
- // [Issue#232]
- if (cfg.isEnabled(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN)) {
- jgen.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
- }
- // [JACKSON-282]: consider Closeable
+ cfg.initialize(jgen); // since 2.5
if (cfg.isEnabled(SerializationFeature.CLOSE_CLOSEABLE) && (value instanceof Closeable)) {
_configAndWriteCloseable(jgen, value, cfg);
return;
@@ -2898,16 +3175,10 @@
}
protected final void _configAndWriteValue(JsonGenerator jgen, Object value, Class<?> viewClass)
- throws IOException, JsonGenerationException, JsonMappingException
+ throws IOException
{
SerializationConfig cfg = getSerializationConfig().withView(viewClass);
- if (cfg.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
- jgen.useDefaultPrettyPrinter();
- }
- // [Issue#232]
- if (cfg.isEnabled(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN)) {
- jgen.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
- }
+ cfg.initialize(jgen); // since 2.5
// [JACKSON-282]: consider Closeable
if (cfg.isEnabled(SerializationFeature.CLOSE_CLOSEABLE) && (value instanceof Closeable)) {
@@ -2991,7 +3262,7 @@
}
}
}
-
+
/*
/**********************************************************
/* Internal methods for deserialization, overridable
@@ -3004,10 +3275,8 @@
* Can be overridden if a custom context is needed.
*/
protected DefaultDeserializationContext createDeserializationContext(JsonParser jp,
- DeserializationConfig cfg)
- {
- return _deserializationContext.createInstance(cfg,
- jp, _injectableValues);
+ DeserializationConfig cfg) {
+ return _deserializationContext.createInstance(cfg, jp, _injectableValues);
}
/**
@@ -3092,57 +3361,58 @@
* content to map (note: Json "null" value is considered content;
* enf-of-stream not)
*/
- protected JsonToken _initForReading(JsonParser jp)
- throws IOException, JsonParseException, JsonMappingException
+ protected JsonToken _initForReading(JsonParser p) throws IOException
{
+ _deserializationConfig.initialize(p); // since 2.5
+
/* First: must point to a token; if not pointing to one, advance.
* This occurs before first read from JsonParser, as well as
* after clearing of current token.
*/
- JsonToken t = jp.getCurrentToken();
+ JsonToken t = p.getCurrentToken();
if (t == null) {
// and then we must get something...
- t = jp.nextToken();
+ t = p.nextToken();
if (t == null) {
/* [JACKSON-546] Throw mapping exception, since it's failure to map,
* not an actual parsing problem
*/
- throw JsonMappingException.from(jp, "No content to map due to end-of-input");
+ throw JsonMappingException.from(p, "No content to map due to end-of-input");
}
}
return t;
}
- protected Object _unwrapAndDeserialize(JsonParser jp, DeserializationContext ctxt,
+ protected Object _unwrapAndDeserialize(JsonParser p, DeserializationContext ctxt,
DeserializationConfig config,
JavaType rootType, JsonDeserializer<Object> deser)
- throws IOException, JsonParseException, JsonMappingException
+ throws IOException
{
String expName = config.getRootName();
if (expName == null) {
PropertyName pname = _rootNames.findRootName(rootType, config);
expName = pname.getSimpleName();
}
- if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
- throw JsonMappingException.from(jp, "Current token not START_OBJECT (needed to unwrap root name '"
- +expName+"'), but "+jp.getCurrentToken());
+ if (p.getCurrentToken() != JsonToken.START_OBJECT) {
+ throw JsonMappingException.from(p, "Current token not START_OBJECT (needed to unwrap root name '"
+ +expName+"'), but "+p.getCurrentToken());
}
- if (jp.nextToken() != JsonToken.FIELD_NAME) {
- throw JsonMappingException.from(jp, "Current token not FIELD_NAME (to contain expected root name '"
- +expName+"'), but "+jp.getCurrentToken());
+ if (p.nextToken() != JsonToken.FIELD_NAME) {
+ throw JsonMappingException.from(p, "Current token not FIELD_NAME (to contain expected root name '"
+ +expName+"'), but "+p.getCurrentToken());
}
- String actualName = jp.getCurrentName();
+ String actualName = p.getCurrentName();
if (!expName.equals(actualName)) {
- throw JsonMappingException.from(jp, "Root name '"+actualName+"' does not match expected ('"
+ throw JsonMappingException.from(p, "Root name '"+actualName+"' does not match expected ('"
+expName+"') for type "+rootType);
}
// ok, then move to value itself....
- jp.nextToken();
- Object result = deser.deserialize(jp, ctxt);
+ p.nextToken();
+ Object result = deser.deserialize(p, ctxt);
// and last, verify that we now get matching END_OBJECT
- if (jp.nextToken() != JsonToken.END_OBJECT) {
- throw JsonMappingException.from(jp, "Current token not END_OBJECT (to match wrapper object with root name '"
- +expName+"'), but "+jp.getCurrentToken());
+ if (p.nextToken() != JsonToken.END_OBJECT) {
+ throw JsonMappingException.from(p, "Current token not END_OBJECT (to match wrapper object with root name '"
+ +expName+"'), but "+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 a73d4b7..2159481 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java
@@ -38,7 +38,7 @@
extends ObjectCodec
implements Versioned, java.io.Serializable // since 2.1
{
- private static final long serialVersionUID = -4251443320039569153L;
+ private static final long serialVersionUID = 1L; // since 2.5
private final static JavaType JSON_NODE_TYPE = SimpleType.constructUnsafe(JsonNode.class);
@@ -156,8 +156,7 @@
/**
* Constructor used by {@link ObjectMapper} for initial instantiation
*/
- protected ObjectReader(ObjectMapper mapper, DeserializationConfig config)
- {
+ protected ObjectReader(ObjectMapper mapper, DeserializationConfig config) {
this(mapper, config, null, null, null, null);
}
@@ -234,7 +233,7 @@
_unwrapRoot = config.useRootWrapping();
_dataFormatReaders = base._dataFormatReaders;
}
-
+
protected ObjectReader(ObjectReader base, JsonFactory f)
{
// may need to override ordering, based on data format capabilities
@@ -266,21 +265,118 @@
/*
/**********************************************************
- /* Life-cycle, fluent factory methods
+ /* Methods sub-classes MUST override, used for constructing
+ /* reader instances, (re)configuring parser instances
/**********************************************************
*/
- public ObjectReader with(DeserializationConfig config) {
- return _with(config);
- }
-
+ /**
+ * Overridable factory method called by various "withXxx()" methods
+ *
+ * @since 2.5
+ */
+ protected ObjectReader _new(ObjectReader base, JsonFactory f) {
+ return new ObjectReader(base, f);
+ }
+
+ /**
+ * Overridable factory method called by various "withXxx()" methods
+ *
+ * @since 2.5
+ */
+ protected ObjectReader _new(ObjectReader base, DeserializationConfig config) {
+ return new ObjectReader(base, config);
+ }
+
+ /**
+ * Overridable factory method called by various "withXxx()" methods
+ *
+ * @since 2.5
+ */
+ protected ObjectReader _new(ObjectReader base, DeserializationConfig config,
+ JavaType valueType, JsonDeserializer<Object> rootDeser, Object valueToUpdate,
+ FormatSchema schema, InjectableValues injectableValues,
+ DataFormatReaders dataFormatReaders) {
+ return new ObjectReader(base, config, valueType, rootDeser, valueToUpdate,
+ schema, injectableValues, dataFormatReaders);
+ }
+
+ /**
+ * Factory method used to create {@link MappingIterator} instances;
+ * either default, or custom subtype.
+ *
+ * @since 2.5
+ */
+ protected <T> MappingIterator<T> _newIterator(JavaType valueType,
+ JsonParser parser, DeserializationContext ctxt,
+ JsonDeserializer<?> deser, boolean parserManaged, Object valueToUpdate)
+ {
+ return new MappingIterator<T>(valueType, parser, ctxt,
+ deser, parserManaged, valueToUpdate);
+ }
+
+ /*
+ /**********************************************************
+ /* Methods sub-classes may choose to override, if customized
+ /* initialization is needed.
+ /**********************************************************
+ */
+
+ /**
+ * NOTE: changed from static to non-static in 2.5; unfortunate but
+ * necessary change to support overridability
+ */
+ protected JsonToken _initForReading(JsonParser p) throws IOException
+ {
+ if (_schema != null) {
+ p.setSchema(_schema);
+ }
+ _config.initialize(p); // since 2.5
+
+ /* First: must point to a token; if not pointing to one, advance.
+ * This occurs before first read from JsonParser, as well as
+ * after clearing of current token.
+ */
+ JsonToken t = p.getCurrentToken();
+ if (t == null) { // and then we must get something...
+ 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");
+ }
+ }
+ return t;
+ }
+
+ /**
+ * Alternative to {@link #_initForReading(JsonParser)} 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
+ */
+ protected void _initForMultiRead(JsonParser p) throws IOException {
+ if (_schema != null) {
+ p.setSchema(_schema);
+ }
+ _config.initialize(p); // since 2.5
+ }
+
+ /*
+ /**********************************************************
+ /* Life-cycle, fluent factory methods for DeserializationFeatures
+ /**********************************************************
+ */
+
/**
* Method for constructing a new reader instance that is configured
* with specified feature enabled.
*/
public ObjectReader with(DeserializationFeature feature) {
return _with(_config.with(feature));
- }
+ }
/**
* Method for constructing a new reader instance that is configured
@@ -299,22 +395,21 @@
public ObjectReader withFeatures(DeserializationFeature... features) {
return _with(_config.withFeatures(features));
}
-
+
/**
* Method for constructing a new reader instance that is configured
* with specified feature disabled.
*/
public ObjectReader without(DeserializationFeature feature) {
return _with(_config.without(feature));
- }
+ }
/**
* Method for constructing a new reader instance that is configured
* with specified features disabled.
*/
public ObjectReader without(DeserializationFeature first,
- DeserializationFeature... other)
- {
+ DeserializationFeature... other) {
return _with(_config.without(first, other));
}
@@ -325,7 +420,55 @@
public ObjectReader withoutFeatures(DeserializationFeature... features) {
return _with(_config.withoutFeatures(features));
}
-
+
+ /*
+ /**********************************************************
+ /* Life-cycle, fluent factory methods for JsonParser.Features
+ /**********************************************************
+ */
+
+ /**
+ * Method for constructing a new reader instance that is configured
+ * with specified feature enabled.
+ */
+ public ObjectReader with(JsonParser.Feature feature) {
+ return _with(_config.with(feature));
+ }
+
+ /**
+ * Method for constructing a new reader instance that is configured
+ * with specified features enabled.
+ */
+ public ObjectReader withFeatures(JsonParser.Feature... features) {
+ return _with(_config.withFeatures(features));
+ }
+
+ /**
+ * Method for constructing a new reader instance that is configured
+ * with specified feature disabled.
+ */
+ public ObjectReader without(JsonParser.Feature feature) {
+ return _with(_config.without(feature));
+ }
+
+ /**
+ * Method for constructing a new reader instance that is configured
+ * with specified features disabled.
+ */
+ public ObjectReader withoutFeatures(JsonParser.Feature... features) {
+ return _with(_config.withoutFeatures(features));
+ }
+
+ /*
+ /**********************************************************
+ /* Life-cycle, fluent factory methods, other
+ /**********************************************************
+ */
+
+ public ObjectReader with(DeserializationConfig config) {
+ return _with(config);
+ }
+
/**
* Method for constructing a new instance with configuration that uses
* passed {@link InjectableValues} to provide injectable values.
@@ -338,7 +481,7 @@
if (_injectableValues == injectableValues) {
return this;
}
- return new ObjectReader(this, _config,
+ return _new(this, _config,
_valueType, _rootDeserializer, _valueToUpdate,
_schema, injectableValues, _dataFormatReaders);
}
@@ -370,7 +513,7 @@
if (f == _parserFactory) {
return this;
}
- ObjectReader r = new ObjectReader(this, f);
+ ObjectReader r = _new(this, f);
// Also, try re-linking, if possible...
if (f.getCodec() == null) {
f.setCodec(r);
@@ -405,7 +548,7 @@
return this;
}
_verifySchemaType(schema);
- return new ObjectReader(this, _config, _valueType, _rootDeserializer, _valueToUpdate,
+ return _new(this, _config, _valueType, _rootDeserializer, _valueToUpdate,
schema, _injectableValues, _dataFormatReaders);
}
@@ -415,8 +558,10 @@
*<p>
* Note that the method does NOT change state of this reader, but
* rather construct and returns a newly configured instance.
+ *
+ * @since 2.5
*/
- public ObjectReader withType(JavaType valueType)
+ public ObjectReader forType(JavaType valueType)
{
if (valueType != null && valueType.equals(_valueType)) {
return this;
@@ -427,7 +572,7 @@
if (det != null) {
det = det.withType(valueType);
}
- return new ObjectReader(this, _config, valueType, rootDeser,
+ return _new(this, _config, valueType, rootDeser,
_valueToUpdate, _schema, _injectableValues, det);
}
@@ -437,31 +582,56 @@
*<p>
* Note that the method does NOT change state of this reader, but
* rather construct and returns a newly configured instance.
+ *
+ * @since 2.5
*/
+ public ObjectReader forType(Class<?> valueType) {
+ return forType(_config.constructType(valueType));
+ }
+
+ /**
+ * Method for constructing a new reader instance that is configured
+ * to data bind into specified type.
+ *<p>
+ * Note that the method does NOT change state of this reader, but
+ * rather construct and returns a newly configured instance.
+ *
+ * @since 2.5
+ */
+ public ObjectReader forType(TypeReference<?> valueTypeRef) {
+ return forType(_config.getTypeFactory().constructType(valueTypeRef.getType()));
+ }
+
+ /**
+ * @deprecated since 2.5 Use {@link #forType(JavaType)} instead
+ */
+ @Deprecated
+ public ObjectReader withType(JavaType valueType) {
+ return forType(valueType);
+ }
+
+ /**
+ * @deprecated since 2.5 Use {@link #forType(Class)} instead
+ */
+ @Deprecated
public ObjectReader withType(Class<?> valueType) {
- return withType(_config.constructType(valueType));
+ return forType(_config.constructType(valueType));
}
/**
- * Method for constructing a new reader instance that is configured
- * to data bind into specified type.
- *<p>
- * Note that the method does NOT change state of this reader, but
- * rather construct and returns a newly configured instance.
+ * @deprecated since 2.5 Use {@link #forType(Class)} instead
*/
+ @Deprecated
public ObjectReader withType(java.lang.reflect.Type valueType) {
- return withType(_config.getTypeFactory().constructType(valueType));
- }
+ return forType(_config.getTypeFactory().constructType(valueType));
+ }
/**
- * Method for constructing a new reader instance that is configured
- * to data bind into specified type.
- *<p>
- * Note that the method does NOT change state of this reader, but
- * rather construct and returns a newly configured instance.
+ * @deprecated since 2.5 Use {@link #forType(TypeReference)} instead
*/
+ @Deprecated
public ObjectReader withType(TypeReference<?> valueTypeRef) {
- return withType(_config.getTypeFactory().constructType(valueTypeRef.getType()));
+ return forType(_config.getTypeFactory().constructType(valueTypeRef.getType()));
}
/**
@@ -489,7 +659,7 @@
} else {
t = _valueType;
}
- return new ObjectReader(this, _config, t, _rootDeserializer, value,
+ return _new(this, _config, t, _rootDeserializer, value,
_schema, _injectableValues, _dataFormatReaders);
}
@@ -542,8 +712,7 @@
*
* @since 2.1
*/
- public ObjectReader withFormatDetection(ObjectReader... readers)
- {
+ public ObjectReader withFormatDetection(ObjectReader... readers) {
return withFormatDetection(new DataFormatReaders(readers));
}
@@ -562,9 +731,8 @@
*
* @since 2.1
*/
- public ObjectReader withFormatDetection(DataFormatReaders readers)
- {
- return new ObjectReader(this, _config, _valueType, _rootDeserializer, _valueToUpdate,
+ public ObjectReader withFormatDetection(DataFormatReaders readers) {
+ return _new(this, _config, _valueType, _rootDeserializer, _valueToUpdate,
_schema, _injectableValues, readers);
}
@@ -572,32 +740,45 @@
* @since 2.3
*/
public ObjectReader with(ContextAttributes attrs) {
- DeserializationConfig newConfig = _config.with(attrs);
- return (newConfig == _config) ? this : new ObjectReader(this, newConfig);
+ return _with(_config.with(attrs));
}
/**
* @since 2.3
*/
public ObjectReader withAttributes(Map<Object,Object> attrs) {
- DeserializationConfig newConfig = _config.withAttributes(attrs);
- return (newConfig == _config) ? this : new ObjectReader(this, newConfig);
+ return _with(_config.withAttributes(attrs));
}
/**
* @since 2.3
*/
public ObjectReader withAttribute(Object key, Object value) {
- DeserializationConfig newConfig = _config.withAttribute(key, value);
- return (newConfig == _config) ? this : new ObjectReader(this, newConfig);
+ return _with( _config.withAttribute(key, value));
}
/**
* @since 2.3
*/
public ObjectReader withoutAttribute(Object key) {
- DeserializationConfig newConfig = _config.withoutAttribute(key);
- return (newConfig == _config) ? this : new ObjectReader(this, newConfig);
+ return _with(_config.withoutAttribute(key));
+ }
+
+ /*
+ /**********************************************************
+ /* Overridable factory methods may override
+ /**********************************************************
+ */
+
+ protected ObjectReader _with(DeserializationConfig newConfig) {
+ if (newConfig == _config) {
+ return this;
+ }
+ ObjectReader r = _new(this, newConfig);
+ if (_dataFormatReaders != null) {
+ r = r.withFormatDetection(_dataFormatReaders.with(newConfig));
+ }
+ return r;
}
/*
@@ -818,24 +999,25 @@
public JsonParser treeAsTokens(TreeNode n) {
return new TreeTraversingParser((JsonNode) n, this);
}
- /**
- * Convenience method that binds content read using given parser, using
- * configuration of this reader, except that content is bound as
- * JSON tree instead of configured root value type.
- *<p>
- * Note: if an object was specified with {@link #withValueToUpdate}, it
- * will be ignored.
- *<p>
- * NOTE: this method never tries to auto-detect format, since actual
- * (data-format specific) parser is given.
- */
- @SuppressWarnings("unchecked")
- @Override
- public <T extends TreeNode> T readTree(JsonParser jp)
- throws IOException, JsonProcessingException
- {
- return (T) _bindAsTree(jp);
- }
+
+ /**
+ * Convenience method that binds content read using given parser, using
+ * configuration of this reader, except that content is bound as
+ * JSON tree instead of configured root value type.
+ *<p>
+ * Note: if an object was specified with {@link #withValueToUpdate}, it
+ * will be ignored.
+ *<p>
+ * NOTE: this method never tries to auto-detect format, since actual
+ * (data-format specific) parser is given.
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T extends TreeNode> T readTree(JsonParser jp)
+ throws IOException, JsonProcessingException
+ {
+ return (T) _bindAsTree(jp);
+ }
@Override
public void writeTree(JsonGenerator jgen, TreeNode rootNode) {
@@ -1047,7 +1229,7 @@
{
DeserializationContext ctxt = createDeserializationContext(jp, _config);
// false -> do not close as caller gave parser instance
- return new MappingIterator<T>(_valueType, jp, ctxt,
+ return _newIterator(_valueType, jp, ctxt,
_findRootDeserializer(ctxt, _valueType),
false, _valueToUpdate);
}
@@ -1091,13 +1273,11 @@
if (_dataFormatReaders != null) {
_reportUndetectableSource(src);
}
- JsonParser jp = _parserFactory.createParser(src);
- if (_schema != null) {
- jp.setSchema(_schema);
- }
- jp.nextToken();
- DeserializationContext ctxt = createDeserializationContext(jp, _config);
- return new MappingIterator<T>(_valueType, jp, ctxt,
+ JsonParser p = _parserFactory.createParser(src);
+ _initForMultiRead(p);
+ p.nextToken();
+ DeserializationContext ctxt = createDeserializationContext(p, _config);
+ return _newIterator(_valueType, p, ctxt,
_findRootDeserializer(ctxt, _valueType), true, _valueToUpdate);
}
@@ -1113,13 +1293,11 @@
if (_dataFormatReaders != null) {
_reportUndetectableSource(json);
}
- JsonParser jp = _parserFactory.createParser(json);
- if (_schema != null) {
- jp.setSchema(_schema);
- }
- jp.nextToken();
- DeserializationContext ctxt = createDeserializationContext(jp, _config);
- return new MappingIterator<T>(_valueType, jp, ctxt,
+ JsonParser p = _parserFactory.createParser(json);
+ _initForMultiRead(p);
+ p.nextToken();
+ DeserializationContext ctxt = createDeserializationContext(p, _config);
+ return _newIterator(_valueType, p, ctxt,
_findRootDeserializer(ctxt, _valueType), true, _valueToUpdate);
}
@@ -1178,8 +1356,7 @@
*/
@Override
- public <T> T treeToValue(TreeNode n, Class<T> valueType)
- throws JsonProcessingException
+ public <T> T treeToValue(TreeNode n, Class<T> valueType) throws JsonProcessingException
{
try {
return readValue(treeAsTokens(n), valueType);
@@ -1191,8 +1368,7 @@
}
@Override
- public void writeValue(JsonGenerator jgen, Object value) throws IOException, JsonProcessingException
- {
+ public void writeValue(JsonGenerator jgen, Object value) throws IOException, JsonProcessingException {
throw new UnsupportedOperationException("Not implemented for ObjectReader");
}
@@ -1205,8 +1381,7 @@
/**
* Actual implementation of value reading+binding operation.
*/
- protected Object _bind(JsonParser jp, Object valueToUpdate)
- throws IOException, JsonParseException, JsonMappingException
+ protected Object _bind(JsonParser jp, Object valueToUpdate) 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)
@@ -1241,12 +1416,8 @@
return result;
}
- protected Object _bindAndClose(JsonParser jp, Object valueToUpdate)
- throws IOException, JsonParseException, JsonMappingException
+ protected Object _bindAndClose(JsonParser jp, Object valueToUpdate) throws IOException
{
- if (_schema != null) {
- jp.setSchema(_schema);
- }
try {
Object result;
JsonToken t = _initForReading(jp);
@@ -1281,8 +1452,17 @@
}
}
- protected JsonNode _bindAsTree(JsonParser jp)
- throws IOException, JsonParseException, JsonMappingException
+ protected JsonNode _bindAndCloseAsTree(JsonParser jp) throws IOException {
+ try {
+ return _bindAsTree(jp);
+ } finally {
+ try {
+ jp.close();
+ } catch (IOException ioe) { }
+ }
+ }
+
+ protected JsonNode _bindAsTree(JsonParser jp) throws IOException
{
JsonNode result;
JsonToken t = _initForReading(jp);
@@ -1302,58 +1482,62 @@
return result;
}
- protected JsonNode _bindAndCloseAsTree(JsonParser jp)
- throws IOException, JsonParseException, JsonMappingException
- {
- if (_schema != null) {
- jp.setSchema(_schema);
- }
- try {
- return _bindAsTree(jp);
- } finally {
- try {
- jp.close();
- } catch (IOException ioe) { }
- }
- }
-
/**
* @since 2.1
*/
- protected <T> MappingIterator<T> _bindAndReadValues(JsonParser p,
- Object valueToUpdate)
- throws IOException, JsonProcessingException
+ protected <T> MappingIterator<T> _bindAndReadValues(JsonParser p, Object valueToUpdate) throws IOException
{
- if (_schema != null) {
- p.setSchema(_schema);
- }
+ _initForMultiRead(p);
p.nextToken();
DeserializationContext ctxt = createDeserializationContext(p, _config);
- return new MappingIterator<T>(_valueType, p, ctxt,
- _findRootDeserializer(ctxt, _valueType),
- true, _valueToUpdate);
- }
-
- protected static JsonToken _initForReading(JsonParser jp)
- throws IOException, JsonParseException, JsonMappingException
- {
- /* First: must point to a token; if not pointing to one, advance.
- * This occurs before first read from JsonParser, as well as
- * after clearing of current token.
- */
- JsonToken t = jp.getCurrentToken();
- if (t == null) { // and then we must get something...
- t = jp.nextToken();
- if (t == null) {
- /* [JACKSON-546] Throw mapping exception, since it's failure to map,
- * not an actual parsing problem
- */
- throw JsonMappingException.from(jp, "No content to map due to end-of-input");
- }
- }
- return t;
+ return _newIterator(_valueType, p, ctxt,
+ _findRootDeserializer(ctxt, _valueType), true, _valueToUpdate);
}
+ protected Object _unwrapAndDeserialize(JsonParser jp, DeserializationContext ctxt,
+ JavaType rootType, JsonDeserializer<Object> deser) throws IOException
+ {
+ String expName = _config.getRootName();
+ if (expName == null) {
+ PropertyName pname = _rootNames.findRootName(rootType, _config);
+ expName = pname.getSimpleName();
+ }
+ if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
+ throw JsonMappingException.from(jp, "Current token not START_OBJECT (needed to unwrap root name '"
+ +expName+"'), but "+jp.getCurrentToken());
+ }
+ if (jp.nextToken() != JsonToken.FIELD_NAME) {
+ throw JsonMappingException.from(jp, "Current token not FIELD_NAME (to contain expected root name '"
+ +expName+"'), but "+jp.getCurrentToken());
+ }
+ String actualName = jp.getCurrentName();
+ if (!expName.equals(actualName)) {
+ throw JsonMappingException.from(jp, "Root name '"+actualName+"' does not match expected ('"
+ +expName+"') for type "+rootType);
+ }
+ // ok, then move to value itself....
+ jp.nextToken();
+ Object result;
+ if (_valueToUpdate == null) {
+ result = deser.deserialize(jp, ctxt);
+ } else {
+ deser.deserialize(jp, ctxt, _valueToUpdate);
+ result = _valueToUpdate;
+ }
+ // and last, verify that we now get matching END_OBJECT
+ if (jp.nextToken() != JsonToken.END_OBJECT) {
+ throw JsonMappingException.from(jp, "Current token not END_OBJECT (to match wrapper object with root name '"
+ +expName+"'), but "+jp.getCurrentToken());
+ }
+ return result;
+ }
+
+ /*
+ /**********************************************************
+ /* Helper methods, locating deserializers etc
+ /**********************************************************
+ */
+
/**
* Method called to locate deserializer for the passed root-level value.
*/
@@ -1389,8 +1573,7 @@
* by configuration. Method also is NOT to throw an exception if
* access fails.
*/
- protected JsonDeserializer<Object> _prefetchRootDeserializer(
- DeserializationConfig config, JavaType valueType)
+ protected JsonDeserializer<Object> _prefetchRootDeserializer(DeserializationConfig config, JavaType valueType)
{
if (valueType == null || !_config.isEnabled(DeserializationFeature.EAGER_DESERIALIZER_FETCH)) {
return null;
@@ -1413,45 +1596,6 @@
}
return deser;
}
-
- protected Object _unwrapAndDeserialize(JsonParser jp, DeserializationContext ctxt,
- JavaType rootType, JsonDeserializer<Object> deser)
- throws IOException, JsonParseException, JsonMappingException
- {
- String expName = _config.getRootName();
- if (expName == null) {
- PropertyName pname = _rootNames.findRootName(rootType, _config);
- expName = pname.getSimpleName();
- }
- if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
- throw JsonMappingException.from(jp, "Current token not START_OBJECT (needed to unwrap root name '"
- +expName+"'), but "+jp.getCurrentToken());
- }
- if (jp.nextToken() != JsonToken.FIELD_NAME) {
- throw JsonMappingException.from(jp, "Current token not FIELD_NAME (to contain expected root name '"
- +expName+"'), but "+jp.getCurrentToken());
- }
- String actualName = jp.getCurrentName();
- if (!expName.equals(actualName)) {
- throw JsonMappingException.from(jp, "Root name '"+actualName+"' does not match expected ('"
- +expName+"') for type "+rootType);
- }
- // ok, then move to value itself....
- jp.nextToken();
- Object result;
- if (_valueToUpdate == null) {
- result = deser.deserialize(jp, ctxt);
- } else {
- deser.deserialize(jp, ctxt, _valueToUpdate);
- result = _valueToUpdate;
- }
- // and last, verify that we now get matching END_OBJECT
- if (jp.nextToken() != JsonToken.END_OBJECT) {
- throw JsonMappingException.from(jp, "Current token not END_OBJECT (to match wrapper object with root name '"
- +expName+"'), but "+jp.getCurrentToken());
- }
- return result;
- }
/*
/**********************************************************
@@ -1556,17 +1700,6 @@
// 04-Jan-2010, tatu: we do actually need the provider too... (for polymorphic deser)
return _context.createInstance(cfg, jp, _injectableValues);
}
-
- protected ObjectReader _with(DeserializationConfig newConfig) {
- if (newConfig == _config) {
- return this;
- }
- if (_dataFormatReaders != null) {
- return new ObjectReader(this, newConfig)
- .withFormatDetection(_dataFormatReaders.with(newConfig));
- }
- return new ObjectReader(this, newConfig);
- }
protected void _reportUndetectableSource(Object src) throws JsonProcessingException
{
diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectWriter.java b/src/main/java/com/fasterxml/jackson/databind/ObjectWriter.java
index e02a18c..35489ba 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ObjectWriter.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ObjectWriter.java
@@ -1,20 +1,16 @@
package com.fasterxml.jackson.databind;
import java.io.*;
-import java.text.DateFormat;
-import java.util.Locale;
-import java.util.Map;
-import java.util.TimeZone;
+import java.text.*;
+import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.io.CharacterEscapes;
import com.fasterxml.jackson.core.io.SegmentedStringWriter;
import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.core.util.ByteArrayBuilder;
-import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
-import com.fasterxml.jackson.core.util.Instantiatable;
-import com.fasterxml.jackson.core.util.MinimalPrettyPrinter;
+import com.fasterxml.jackson.core.util.*;
+
import com.fasterxml.jackson.databind.cfg.ContextAttributes;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider;
@@ -34,7 +30,7 @@
implements Versioned,
java.io.Serializable // since 2.1
{
- private static final long serialVersionUID = -7040667122552707164L;
+ private static final long serialVersionUID = 1; // since 2.5
/**
* We need to keep track of explicit disabling of pretty printing;
@@ -107,17 +103,6 @@
/*
/**********************************************************
- /* Derived settings
- /**********************************************************
- */
-
- /**
- * @since 2.3
- */
- protected final boolean _cfgBigDecimalAsPlain;
-
- /*
- /**********************************************************
/* Life-cycle, constructors
/**********************************************************
*/
@@ -129,8 +114,6 @@
JavaType rootType, PrettyPrinter pp)
{
_config = config;
- _cfgBigDecimalAsPlain = _config.isEnabled(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN);
-
_serializerProvider = mapper._serializerProvider;
_serializerFactory = mapper._serializerFactory;
_generatorFactory = mapper._jsonFactory;
@@ -154,8 +137,6 @@
protected ObjectWriter(ObjectMapper mapper, SerializationConfig config)
{
_config = config;
- _cfgBigDecimalAsPlain = _config.isEnabled(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN);
-
_serializerProvider = mapper._serializerProvider;
_serializerFactory = mapper._serializerFactory;
_generatorFactory = mapper._jsonFactory;
@@ -174,7 +155,6 @@
FormatSchema s)
{
_config = config;
- _cfgBigDecimalAsPlain = _config.isEnabled(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN);
_serializerProvider = mapper._serializerProvider;
_serializerFactory = mapper._serializerFactory;
@@ -195,7 +175,6 @@
PrettyPrinter pp, FormatSchema s, CharacterEscapes escapes)
{
_config = config;
- _cfgBigDecimalAsPlain = _config.isEnabled(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN);
_serializerProvider = base._serializerProvider;
_serializerFactory = base._serializerFactory;
@@ -214,7 +193,6 @@
protected ObjectWriter(ObjectWriter base, SerializationConfig config)
{
_config = config;
- _cfgBigDecimalAsPlain = _config.isEnabled(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN);
_serializerProvider = base._serializerProvider;
_serializerFactory = base._serializerFactory;
@@ -235,7 +213,6 @@
// may need to override ordering, based on data format capabilities
_config = base._config
.with(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, f.requiresPropertyOrdering());
- _cfgBigDecimalAsPlain = base._cfgBigDecimalAsPlain;
_serializerProvider = base._serializerProvider;
_serializerFactory = base._serializerFactory;
@@ -259,7 +236,7 @@
/*
/**********************************************************
- /* Life-cycle, fluent factories
+ /* Life-cycle, fluent factories for SerializationFeature
/**********************************************************
*/
@@ -315,8 +292,52 @@
public ObjectWriter withoutFeatures(SerializationFeature... features) {
SerializationConfig newConfig = _config.withoutFeatures(features);
return (newConfig == _config) ? this : new ObjectWriter(this, newConfig);
- }
-
+ }
+
+ /*
+ /**********************************************************
+ /* Life-cycle, fluent factories for JsonGenerator.Feature
+ /**********************************************************
+ */
+
+ /**
+ * @since 2.5
+ */
+ public ObjectWriter with(JsonGenerator.Feature feature) {
+ SerializationConfig newConfig = _config.with(feature);
+ return (newConfig == _config) ? this : new ObjectWriter(this, newConfig);
+ }
+
+ /**
+ * @since 2.5
+ */
+ public ObjectWriter withFeatures(JsonGenerator.Feature... features) {
+ SerializationConfig newConfig = _config.withFeatures(features);
+ return (newConfig == _config) ? this : new ObjectWriter(this, newConfig);
+ }
+
+ /**
+ * @since 2.5
+ */
+ public ObjectWriter without(JsonGenerator.Feature feature) {
+ SerializationConfig newConfig = _config.without(feature);
+ return (newConfig == _config) ? this : new ObjectWriter(this, newConfig);
+ }
+
+ /**
+ * @since 2.5
+ */
+ public ObjectWriter withoutFeatures(JsonGenerator.Feature... features) {
+ SerializationConfig newConfig = _config.withoutFeatures(features);
+ return (newConfig == _config) ? this : new ObjectWriter(this, newConfig);
+ }
+
+ /*
+ /**********************************************************
+ /* Life-cycle, fluent factories, other
+ /**********************************************************
+ */
+
/**
* Fluent factory method that will construct a new writer instance that will
* use specified date format for serializing dates; or if null passed, one
@@ -383,8 +404,7 @@
* Note that method does NOT change state of this reader, but
* rather construct and returns a newly configured instance.
*/
-
- public ObjectWriter withSchema(FormatSchema schema) {
+ public ObjectWriter with(FormatSchema schema) {
if (_schema == schema) {
return this;
}
@@ -394,14 +414,24 @@
}
/**
+ * @deprecated Since 2.5 use {@link #with(FormatSchema)} instead
+ */
+ @Deprecated
+ public ObjectWriter withSchema(FormatSchema schema) {
+ return with(schema);
+ }
+
+ /**
* Method that will construct a new instance that uses specific type
* as the root type for serialization, instead of runtime dynamic
* type of the root object itself.
*<p>
* Note that method does NOT change state of this reader, but
* rather construct and returns a newly configured instance.
+ *
+ * @since 2.5
*/
- public ObjectWriter withType(JavaType rootType)
+ public ObjectWriter forType(JavaType rootType)
{
JsonSerializer<Object> rootSer;
if (rootType == null || rootType.hasRawClass(Object.class)) {
@@ -420,16 +450,42 @@
* Method that will construct a new instance that uses specific type
* as the root type for serialization, instead of runtime dynamic
* type of the root object itself.
+ *
+ * @since 2.5
*/
- public ObjectWriter withType(Class<?> rootType) {
+ public ObjectWriter forType(Class<?> rootType) {
if (rootType == Object.class) {
- return withType((JavaType) null);
+ return forType((JavaType) null);
}
- return withType(_config.constructType(rootType));
+ return forType(_config.constructType(rootType));
}
+ public ObjectWriter forType(TypeReference<?> rootType) {
+ return forType(_config.getTypeFactory().constructType(rootType.getType()));
+ }
+
+ /**
+ * @deprecated since 2.5 Use {@link #forType(JavaType)} instead
+ */
+ @Deprecated // since 2.5
+ public ObjectWriter withType(JavaType rootType) {
+ return forType(rootType);
+ }
+
+ /**
+ * @deprecated since 2.5 Use {@link #forType(Class)} instead
+ */
+ @Deprecated // since 2.5
+ public ObjectWriter withType(Class<?> rootType) {
+ return forType(rootType);
+ }
+
+ /**
+ * @deprecated since 2.5 Use {@link #forType(TypeReference)} instead
+ */
+ @Deprecated // since 2.5
public ObjectWriter withType(TypeReference<?> rootType) {
- return withType(_config.getTypeFactory().constructType(rootType.getType()));
+ return forType(rootType);
}
/**
@@ -824,7 +880,7 @@
* method is to be called right after serialization has been called
*/
private final void _writeCloseable(JsonGenerator jgen, Object value, SerializationConfig cfg)
- throws IOException, JsonGenerationException, JsonMappingException
+ throws IOException
{
Closeable toClose = (Closeable) value;
try {
@@ -865,7 +921,7 @@
* method is to be called right after serialization has been called
*/
private final void _writeCloseableValue(JsonGenerator jgen, Object value, SerializationConfig cfg)
- throws IOException, JsonGenerationException, JsonMappingException
+ throws IOException
{
Closeable toClose = (Closeable) value;
try {
@@ -914,12 +970,12 @@
*
* @since 2.1
*/
- private void _configureJsonGenerator(JsonGenerator jgen)
+ protected void _configureJsonGenerator(JsonGenerator gen)
{
if (_prettyPrinter != null) {
PrettyPrinter pp = _prettyPrinter;
if (pp == NULL_PRETTY_PRINTER) {
- jgen.setPrettyPrinter(null);
+ gen.setPrettyPrinter(null);
} else {
/* [JACKSON-851]: Better take care of stateful PrettyPrinters...
* like the DefaultPrettyPrinter.
@@ -927,20 +983,18 @@
if (pp instanceof Instantiatable<?>) {
pp = (PrettyPrinter) ((Instantiatable<?>) pp).createInstance();
}
- jgen.setPrettyPrinter(pp);
+ gen.setPrettyPrinter(pp);
}
} else if (_config.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
- jgen.useDefaultPrettyPrinter();
+ gen.useDefaultPrettyPrinter();
}
if (_characterEscapes != null) {
- jgen.setCharacterEscapes(_characterEscapes);
+ gen.setCharacterEscapes(_characterEscapes);
}
// [JACKSON-520]: add support for pass-through schema:
if (_schema != null) {
- jgen.setSchema(_schema);
+ gen.setSchema(_schema);
}
- if (_cfgBigDecimalAsPlain) { // should only set if explicitly set; this should work for now:
- jgen.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
- }
+ _config.initialize(gen); // since 2.5
}
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/SerializationConfig.java b/src/main/java/com/fasterxml/jackson/databind/SerializationConfig.java
index c53a7be..6fad9f0 100644
--- a/src/main/java/com/fasterxml/jackson/databind/SerializationConfig.java
+++ b/src/main/java/com/fasterxml/jackson/databind/SerializationConfig.java
@@ -5,7 +5,7 @@
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
-import com.fasterxml.jackson.core.Base64Variant;
+import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.cfg.BaseSettings;
import com.fasterxml.jackson.databind.cfg.ContextAttributes;
import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
@@ -37,12 +37,11 @@
extends MapperConfigBase<SerializationFeature, SerializationConfig>
implements java.io.Serializable // since 2.1
{
- // Valid as of 2.4
- private static final long serialVersionUID = -1278867172535832879L;
+ // since 2.5
+ private static final long serialVersionUID = 1;
/**
- * Set of features enabled; actual type (kind of features)
- * depends on sub-classes.
+ * Set of {@link SerializationFeature}s enabled.
*/
protected final int _serFeatures;
@@ -59,6 +58,16 @@
* Non-null if explicitly defined; null by default.
*/
protected final FilterProvider _filterProvider;
+
+ /**
+ * States of {@link com.fasterxml.jackson.core.JsonGenerator.Feature}s to enable/disable.
+ */
+ protected final int _generatorFeatures;
+
+ /**
+ * Bitflag of {@link com.fasterxml.jackson.core.JsonGenerator.Feature}s to enable/disable
+ */
+ protected final int _generatorFeaturesToChange;
/*
/**********************************************************
@@ -75,6 +84,8 @@
super(base, str, mixins);
_serFeatures = collectFeatureDefaults(SerializationFeature.class);
_filterProvider = null;
+ _generatorFeatures = 0;
+ _generatorFeaturesToChange = 0;
}
private SerializationConfig(SerializationConfig src, SubtypeResolver str)
@@ -83,14 +94,20 @@
_serFeatures = src._serFeatures;
_serializationInclusion = src._serializationInclusion;
_filterProvider = src._filterProvider;
+ _generatorFeatures = src._generatorFeatures;
+ _generatorFeaturesToChange = src._generatorFeaturesToChange;
}
- private SerializationConfig(SerializationConfig src, int mapperFeatures, int serFeatures)
+ private SerializationConfig(SerializationConfig src,
+ int mapperFeatures, int serFeatures,
+ int generatorFeatures, int generatorFeatureMask)
{
super(src, mapperFeatures);
_serFeatures = serFeatures;
_serializationInclusion = src._serializationInclusion;
_filterProvider = src._filterProvider;
+ _generatorFeatures = generatorFeatures;
+ _generatorFeaturesToChange = generatorFeatureMask;
}
private SerializationConfig(SerializationConfig src, BaseSettings base)
@@ -99,6 +116,8 @@
_serFeatures = src._serFeatures;
_serializationInclusion = src._serializationInclusion;
_filterProvider = src._filterProvider;
+ _generatorFeatures = src._generatorFeatures;
+ _generatorFeaturesToChange = src._generatorFeaturesToChange;
}
private SerializationConfig(SerializationConfig src, FilterProvider filters)
@@ -107,6 +126,8 @@
_serFeatures = src._serFeatures;
_serializationInclusion = src._serializationInclusion;
_filterProvider = filters;
+ _generatorFeatures = src._generatorFeatures;
+ _generatorFeaturesToChange = src._generatorFeaturesToChange;
}
private SerializationConfig(SerializationConfig src, Class<?> view)
@@ -115,6 +136,8 @@
_serFeatures = src._serFeatures;
_serializationInclusion = src._serializationInclusion;
_filterProvider = src._filterProvider;
+ _generatorFeatures = src._generatorFeatures;
+ _generatorFeaturesToChange = src._generatorFeaturesToChange;
}
private SerializationConfig(SerializationConfig src, JsonInclude.Include incl)
@@ -123,6 +146,8 @@
_serFeatures = src._serFeatures;
_serializationInclusion = incl;
_filterProvider = src._filterProvider;
+ _generatorFeatures = src._generatorFeatures;
+ _generatorFeaturesToChange = src._generatorFeaturesToChange;
}
private SerializationConfig(SerializationConfig src, String rootName)
@@ -131,6 +156,8 @@
_serFeatures = src._serFeatures;
_serializationInclusion = src._serializationInclusion;
_filterProvider = src._filterProvider;
+ _generatorFeatures = src._generatorFeatures;
+ _generatorFeaturesToChange = src._generatorFeaturesToChange;
}
/**
@@ -142,6 +169,8 @@
_serFeatures = src._serFeatures;
_serializationInclusion = src._serializationInclusion;
_filterProvider = src._filterProvider;
+ _generatorFeatures = src._generatorFeatures;
+ _generatorFeaturesToChange = src._generatorFeaturesToChange;
}
/**
@@ -153,6 +182,8 @@
_serFeatures = src._serFeatures;
_serializationInclusion = src._serializationInclusion;
_filterProvider = src._filterProvider;
+ _generatorFeatures = src._generatorFeatures;
+ _generatorFeaturesToChange = src._generatorFeaturesToChange;
}
/*
@@ -173,7 +204,8 @@
newMapperFlags |= f.getMask();
}
return (newMapperFlags == _mapperFeatures) ? this
- : new SerializationConfig(this, newMapperFlags, _serFeatures);
+ : new SerializationConfig(this, newMapperFlags, _serFeatures,
+ _generatorFeatures, _generatorFeaturesToChange);
}
/**
@@ -188,7 +220,8 @@
newMapperFlags &= ~f.getMask();
}
return (newMapperFlags == _mapperFeatures) ? this
- : new SerializationConfig(this, newMapperFlags, _serFeatures);
+ : new SerializationConfig(this, newMapperFlags, _serFeatures,
+ _generatorFeatures, _generatorFeaturesToChange);
}
@Override
@@ -200,8 +233,9 @@
} else {
newMapperFlags = _mapperFeatures & ~feature.getMask();
}
- return (newMapperFlags == _mapperFeatures) ? this :
- new SerializationConfig(this, newMapperFlags, _serFeatures);
+ return (newMapperFlags == _mapperFeatures) ? this
+ : new SerializationConfig(this, newMapperFlags, _serFeatures,
+ _generatorFeatures, _generatorFeaturesToChange);
}
@Override
@@ -223,7 +257,7 @@
public SerializationConfig with(ClassIntrospector ci) {
return _withBase(_base.withClassIntrospector(ci));
}
-
+
/**
* In addition to constructing instance with specified date format,
* will enable or disable <code>SerializationFeature.WRITE_DATES_AS_TIMESTAMPS</code>
@@ -240,12 +274,12 @@
}
return cfg;
}
-
+
@Override
public SerializationConfig with(HandlerInstantiator hi) {
return _withBase(_base.withHandlerInstantiator(hi));
}
-
+
@Override
public SerializationConfig with(PropertyNamingStrategy pns) {
return _withBase(_base.withPropertyNamingStrategy(pns));
@@ -277,7 +311,7 @@
public SerializationConfig with(TypeResolverBuilder<?> trb) {
return _withBase(_base.withTypeResolverBuilder(trb));
}
-
+
@Override
public SerializationConfig withView(Class<?> view) {
return (_view == view) ? this : new SerializationConfig(this, view);
@@ -312,17 +346,17 @@
public SerializationConfig with(ContextAttributes attrs) {
return (attrs == _attributes) ? this : new SerializationConfig(this, attrs);
}
-
+
private final SerializationConfig _withBase(BaseSettings newBase) {
return (_base == newBase) ? this : new SerializationConfig(this, newBase);
}
-
+
/*
/**********************************************************
- /* Life-cycle, SerializationConfig specific factory methods
+ /* Factory methods for SerializationFeature
/**********************************************************
*/
-
+
/**
* Fluent factory method that will construct and return a new configuration
* object instance with specified feature enabled.
@@ -331,7 +365,8 @@
{
int newSerFeatures = _serFeatures | feature.getMask();
return (newSerFeatures == _serFeatures) ? this
- : new SerializationConfig(this, _mapperFeatures, newSerFeatures);
+ : new SerializationConfig(this, _mapperFeatures, newSerFeatures,
+ _generatorFeatures, _generatorFeaturesToChange);
}
/**
@@ -345,9 +380,10 @@
newSerFeatures |= f.getMask();
}
return (newSerFeatures == _serFeatures) ? this
- : new SerializationConfig(this, _mapperFeatures, newSerFeatures);
+ : new SerializationConfig(this, _mapperFeatures, newSerFeatures,
+ _generatorFeatures, _generatorFeaturesToChange);
}
-
+
/**
* Fluent factory method that will construct and return a new configuration
* object instance with specified features enabled.
@@ -359,7 +395,8 @@
newSerFeatures |= f.getMask();
}
return (newSerFeatures == _serFeatures) ? this
- : new SerializationConfig(this, _mapperFeatures, newSerFeatures);
+ : new SerializationConfig(this, _mapperFeatures, newSerFeatures,
+ _generatorFeatures, _generatorFeaturesToChange);
}
/**
@@ -370,7 +407,8 @@
{
int newSerFeatures = _serFeatures & ~feature.getMask();
return (newSerFeatures == _serFeatures) ? this
- : new SerializationConfig(this, _mapperFeatures, newSerFeatures);
+ : new SerializationConfig(this, _mapperFeatures, newSerFeatures,
+ _generatorFeatures, _generatorFeaturesToChange);
}
/**
@@ -384,7 +422,8 @@
newSerFeatures &= ~f.getMask();
}
return (newSerFeatures == _serFeatures) ? this
- : new SerializationConfig(this, _mapperFeatures, newSerFeatures);
+ : new SerializationConfig(this, _mapperFeatures, newSerFeatures,
+ _generatorFeatures, _generatorFeaturesToChange);
}
/**
@@ -398,8 +437,90 @@
newSerFeatures &= ~f.getMask();
}
return (newSerFeatures == _serFeatures) ? this
- : new SerializationConfig(this, _mapperFeatures, newSerFeatures);
+ : new SerializationConfig(this, _mapperFeatures, newSerFeatures,
+ _generatorFeatures, _generatorFeaturesToChange);
}
+
+ /*
+ /**********************************************************
+ /* Factory methods for JsonGenerator.Feature
+ /**********************************************************
+ */
+ /**
+ * Fluent factory method that will construct and return a new configuration
+ * object instance with specified feature enabled.
+ *
+ * @since 2.5
+ */
+ public SerializationConfig with(JsonGenerator.Feature feature)
+ {
+ int newSet = _generatorFeatures | feature.getMask();
+ int newMask = _generatorFeaturesToChange | feature.getMask();
+ return ((_generatorFeatures == newSet) && (_generatorFeaturesToChange == newMask)) ? this :
+ new SerializationConfig(this, _mapperFeatures, _serFeatures,
+ newSet, newMask);
+ }
+
+ /**
+ * Fluent factory method that will construct and return a new configuration
+ * object instance with specified features enabled.
+ *
+ * @since 2.5
+ */
+ public SerializationConfig withFeatures(JsonGenerator.Feature... features)
+ {
+ int newSet = _generatorFeatures;
+ int newMask = _generatorFeaturesToChange;
+ for (JsonGenerator.Feature f : features) {
+ int mask = f.getMask();
+ newSet |= mask;
+ newMask |= mask;
+ }
+ return ((_generatorFeatures == newSet) && (_generatorFeaturesToChange == newMask)) ? this :
+ new SerializationConfig(this, _mapperFeatures, _serFeatures,
+ newSet, newMask);
+ }
+
+ /**
+ * Fluent factory method that will construct and return a new configuration
+ * object instance with specified feature disabled.
+ *
+ * @since 2.5
+ */
+ public SerializationConfig without(JsonGenerator.Feature feature)
+ {
+ int newSet = _generatorFeatures & ~feature.getMask();
+ int newMask = _generatorFeaturesToChange | feature.getMask();
+ return ((_generatorFeatures == newSet) && (_generatorFeaturesToChange == newMask)) ? this :
+ new SerializationConfig(this, _mapperFeatures, _serFeatures,
+ newSet, newMask);
+ }
+
+ /**
+ * Fluent factory method that will construct and return a new configuration
+ * object instance with specified features disabled.
+ *
+ * @since 2.5
+ */
+ public SerializationConfig withoutFeatures(JsonGenerator.Feature... features)
+ {
+ int newSet = _generatorFeatures;
+ int newMask = _generatorFeaturesToChange;
+ for (JsonGenerator.Feature f : features) {
+ int mask = f.getMask();
+ newSet &= ~mask;
+ newMask |= mask;
+ }
+ return ((_generatorFeatures == newSet) && (_generatorFeaturesToChange == newMask)) ? this :
+ new SerializationConfig(this, _mapperFeatures, _serFeatures,
+ newSet, newMask);
+ }
+
+ /*
+ /**********************************************************
+ /* Factory methods, other
+ /**********************************************************
+ */
public SerializationConfig withFilters(FilterProvider filterProvider) {
return (filterProvider == _filterProvider) ? this : new SerializationConfig(this, filterProvider);
@@ -408,13 +529,46 @@
public SerializationConfig withSerializationInclusion(JsonInclude.Include incl) {
return (_serializationInclusion == incl) ? this: new SerializationConfig(this, incl);
}
+
+ /*
+ /**********************************************************
+ /* JsonParser initialization
+ /**********************************************************
+ */
+
+ /**
+ * Method called by {@link ObjectMapper} and {@link ObjectWriter}
+ * to modify those {@link com.fasterxml.jackson.core.JsonGenerator.Feature} settings
+ * that have been configured via this config instance.
+ *
+ * @since 2.5
+ */
+ public void initialize(JsonGenerator g)
+ {
+ if (SerializationFeature.INDENT_OUTPUT.enabledIn(_serFeatures)) {
+ g.useDefaultPrettyPrinter();
+ }
+ @SuppressWarnings("deprecation")
+ boolean useBigDec = SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN.enabledIn(_serFeatures);
+ if ((_generatorFeaturesToChange != 0) || useBigDec) {
+ int orig = g.getFeatureMask();
+ int newFlags = (orig & ~_generatorFeaturesToChange) | _generatorFeatures;
+ // although deprecated, needs to be supported for now
+ if (useBigDec) {
+ newFlags |= JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN.getMask();
+ }
+ if (orig != newFlags) {
+ g.setFeatureMask(newFlags);
+ }
+ }
+ }
/*
/**********************************************************
/* MapperConfig implementation/overrides
/**********************************************************
*/
-
+
@Override
public boolean useRootWrapping()
{
@@ -423,13 +577,10 @@
}
return isEnabled(SerializationFeature.WRAP_ROOT_VALUE);
}
-
+
@Override
public AnnotationIntrospector getAnnotationIntrospector()
{
- /* 29-Jul-2009, tatu: it's now possible to disable use of
- * annotations; can be done using "no-op" introspector
- */
if (isEnabled(MapperFeature.USE_ANNOTATIONS)) {
return super.getAnnotationIntrospector();
}
@@ -454,7 +605,7 @@
public BeanDescription introspectDirectClassAnnotations(JavaType type) {
return getClassIntrospector().forDirectClassAnnotations(this, type, this);
}
-
+
@Override
public VisibilityChecker<?> getDefaultVisibilityChecker()
{
@@ -471,7 +622,7 @@
}
return vchecker;
}
-
+
/*
/**********************************************************
/* Configuration: other
@@ -483,6 +634,21 @@
}
/**
+ * Accessor method that first checks if we have any overrides
+ * for feature, and only if not, checks state of passed-in
+ * factory.
+ *
+ * @since 2.5
+ */
+ public final boolean isEnabled(JsonGenerator.Feature f, JsonFactory factory) {
+ int mask = f.getMask();
+ if ((_generatorFeaturesToChange & mask) != 0) {
+ return (_generatorFeatures & f.getMask()) != 0;
+ }
+ return factory.isEnabled(f);
+ }
+
+ /**
* "Bulk" access method for checking that all features specified by
* mask are enabled.
*
@@ -495,7 +661,7 @@
public final int getSerializationFeatures() {
return _serFeatures;
}
-
+
public JsonInclude.Include getSerializationInclusion()
{
if (_serializationInclusion != null) {
@@ -503,7 +669,7 @@
}
return JsonInclude.Include.ALWAYS;
}
-
+
/**
* Method for getting provider used for locating filters given
* id (which is usually provided with filter annotations).
@@ -513,7 +679,7 @@
public FilterProvider getFilterProvider() {
return _filterProvider;
}
-
+
/*
/**********************************************************
/* Introspection methods
@@ -534,9 +700,9 @@
/* Debug support
/**********************************************************
*/
-
- @Override public String toString()
- {
+
+ @Override
+ public String toString() {
return "[SerializationConfig: flags=0x"+Integer.toHexString(_serFeatures)+"]";
}
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/SerializationFeature.java b/src/main/java/com/fasterxml/jackson/databind/SerializationFeature.java
index 8cd056f..1aed5c8 100644
--- a/src/main/java/com/fasterxml/jackson/databind/SerializationFeature.java
+++ b/src/main/java/com/fasterxml/jackson/databind/SerializationFeature.java
@@ -20,7 +20,7 @@
/* Generic output features
/******************************************************
*/
-
+
/**
* Feature that can be enabled to make root value (usually JSON
* Object but can be any type) wrapped within a single property
@@ -51,16 +51,16 @@
* method to use any <code>PrettyPrinter</code> instance.
* This feature will only allow using the default implementation.
*<p>
- * Feature is enabled by default.
+ * Feature is disabled by default.
*/
INDENT_OUTPUT(false),
-
+
/*
/******************************************************
/* Error handling features
/******************************************************
*/
-
+
/**
* Feature that determines what happens when no accessors are
* found for a type (and there are no annotations to indicate
@@ -89,7 +89,7 @@
* @since 2.4
*/
FAIL_ON_SELF_REFERENCES(true),
-
+
/**
* Feature that determines whether Jackson code should catch
* and wrap {@link Exception}s (but never {@link Error}s!)
@@ -128,12 +128,12 @@
/* Output life cycle features
/******************************************************
*/
-
+
/**
* Feature that determines whether <code>close</code> method of
* serialized <b>root level</b> objects (ones for which <code>ObjectMapper</code>'s
* writeValue() (or equivalent) method is called)
- * that implement {@link java.io.Closeable}
+ * that implement {@link java.io.Closeable}
* is called after serialization or not. If enabled, <b>close()</b> will
* be called after serialization completes (whether succesfully, or
* due to an error manifested by an exception being thrown). You can
@@ -160,7 +160,7 @@
* Feature is enabled by default.
*/
FLUSH_AFTER_WRITE_VALUE(true),
-
+
/*
/******************************************************
/* Datatype-specific serialization configuration
@@ -186,11 +186,29 @@
* Note: whether {@link java.util.Map} keys are serialized as Strings
* or not is controlled using {@link #WRITE_DATE_KEYS_AS_TIMESTAMPS}.
*<p>
- * Feature is enabled by default.
+ * Feature is enabled by default, so that date/time are by default
+ * serialized as timestamps.
*/
WRITE_DATES_AS_TIMESTAMPS(true),
/**
+ * Feature that determines whether time values that represents time periods
+ * (durations, periods, ranges) are to be serialized by default using
+ * a numeric (true) or textual (false) representations. Note that numeric
+ * representation may mean either simple number, or an array of numbers,
+ * depending on type.
+ *<p>
+ * Note: whether {@link java.util.Map} keys are serialized as Strings
+ * or not is controlled using {@link #WRITE_DATE_KEYS_AS_TIMESTAMPS}.
+ *<p>
+ * Feature is enabled by default, so that period/duration are by default
+ * serialized as timestamps.
+ *
+ * @since 2.5
+ */
+ WRITE_DURATIONS_AS_TIMESTAMPS(true),
+
+ /**
* Feature that determines whether {@link java.util.Date}s
* (and sub-types) used as {@link java.util.Map} keys are serialized
* as timestamps or not (if not, will be serialized as textual
@@ -239,7 +257,7 @@
* Feature is disabled by default.
*/
WRITE_ENUMS_USING_INDEX(false),
-
+
/**
* Feature that determines whether Map entries with null values are
* to be serialized (true) or not (false).
@@ -262,7 +280,7 @@
* Feature is enabled by default.
*/
WRITE_EMPTY_JSON_ARRAYS(true),
-
+
/**
* Feature added for interoperability, to work with oddities of
* so-called "BadgerFish" convention.
@@ -298,9 +316,13 @@
* support it.
*<p>
* Feature is disabled by default.
+ *
+ * @deprecated Since 2.5: use {@link com.fasterxml.jackson.core.JsonGenerator.Feature#WRITE_BIGDECIMAL_AS_PLAIN} directly
+ * (using {@link ObjectWriter#with(com.fasterxml.jackson.core.JsonGenerator.Feature)}).
*/
+ @Deprecated // since 2.5
WRITE_BIGDECIMAL_AS_PLAIN(false),
-
+
/**
* Feature that controls whether numeric timestamp values are
* to be written using nanosecond timestamps (enabled) or not (disabled);
@@ -313,11 +335,11 @@
* This is the counterpart to {@link SerializationFeature#WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS}.
*<p>
* Feature is enabled by default, to support most accurate time values possible.
- *
+ *
* @since 2.2
*/
- WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS(true),
-
+ WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS(true),
+
/**
* Feature that determines whether {@link java.util.Map} entries are first
* sorted by key before serialization or not: if enabled, additional sorting
@@ -345,7 +367,7 @@
* feature: only consider that if there are actual perceived problems.
*<p>
* Feature is enabled by default.
- *
+ *
* @since 2.1
*/
EAGER_SERIALIZER_FETCH(true),
@@ -358,21 +380,29 @@
*<p>
* Feature is disabled by default; meaning that strict identity is used, not
* <code>equals()</code>
- *
+ *
* @since 2.3
*/
USE_EQUALITY_FOR_OBJECT_ID(false)
;
private final boolean _defaultState;
+ private final int _mask;
private SerializationFeature(boolean defaultState) {
_defaultState = defaultState;
+ _mask = (1 << ordinal());
}
@Override
public boolean enabledByDefault() { return _defaultState; }
+
@Override
- public int getMask() { return (1 << ordinal()); }
+ public int getMask() { return _mask; }
+
+ /**
+ * @since 2.5
+ */
+ public boolean enabledIn(int flags) { return (flags & _mask) != 0; }
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java b/src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java
index 7c41bfa..851a4d7 100644
--- a/src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java
+++ b/src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java
@@ -37,9 +37,6 @@
public abstract class SerializerProvider
extends DatabindContext
{
- @Deprecated // since 2.3, not used by anything it seems
- protected final static JavaType TYPE_OBJECT = TypeFactory.defaultInstance().uncheckedSimpleType(Object.class);
-
/**
* Setting for determining whether mappings for "unknown classes" should be
* cached for faster resolution. Usually this isn't needed, but maybe it
@@ -231,8 +228,7 @@
}
/**
- * Copy-constructor used when making a {@link #copy} of a blueprint
- * object.
+ * Copy-constructor used when making a copy of a blueprint instance.
*
* @since 2.4.4
*/
@@ -459,8 +455,7 @@
* finding any serializer
*/
@SuppressWarnings("unchecked")
- public JsonSerializer<Object> findValueSerializer(Class<?> valueType,
- BeanProperty property)
+ public JsonSerializer<Object> findValueSerializer(Class<?> valueType, BeanProperty property)
throws JsonMappingException
{
// Fast lookup from local lookup thingy works?
@@ -474,11 +469,7 @@
if (ser == null) {
// If neither, must create
ser = _createAndCacheUntypedSerializer(valueType);
- // Not found? Must use the unknown type serializer
- /* Couldn't create? Need to return the fallback serializer, which
- * most likely will report an error: but one question is whether
- * we should cache it?
- */
+ // Not found? Must use the unknown type serializer, which will report error later on
if (ser == null) {
ser = getUnknownTypeSerializer(valueType);
// Should this be added to lookups?
@@ -508,22 +499,14 @@
public JsonSerializer<Object> findValueSerializer(JavaType valueType, BeanProperty property)
throws JsonMappingException
{
- // Fast lookup from local lookup thingy works?
+ // (see comments from above method)
JsonSerializer<Object> ser = _knownSerializers.untypedValueSerializer(valueType);
if (ser == null) {
- // If not, maybe shared map already has it?
ser = _serializerCache.untypedValueSerializer(valueType);
if (ser == null) {
- // If neither, must create
ser = _createAndCacheUntypedSerializer(valueType);
- // Not found? Must use the unknown type serializer
- /* Couldn't create? Need to return the fallback serializer, which
- * most likely will report an error: but one question is whether
- * we should cache it?
- */
if (ser == null) {
ser = getUnknownTypeSerializer(valueType.getRawClass());
- // Should this be added to lookups?
if (CACHE_UNKNOWN_MAPPINGS) {
_serializerCache.addAndResolveNonTypedSerializer(valueType, ser, this);
}
@@ -535,6 +518,62 @@
}
/**
+ * Method variant used when we do NOT want contextualization to happen; it will need
+ * to be handled at a later point, but caller wants to be able to do that
+ * as needed; sometimes to avoid infinite loops
+ *
+ * @since 2.5
+ */
+ public JsonSerializer<Object> findValueSerializer(Class<?> valueType) throws JsonMappingException
+ {
+ // (see comments from above method)
+ JsonSerializer<Object> ser = _knownSerializers.untypedValueSerializer(valueType);
+ if (ser == null) {
+ ser = _serializerCache.untypedValueSerializer(valueType);
+ if (ser == null) {
+ ser = _serializerCache.untypedValueSerializer(_config.constructType(valueType));
+ if (ser == null) {
+ ser = _createAndCacheUntypedSerializer(valueType);
+ if (ser == null) {
+ ser = getUnknownTypeSerializer(valueType);
+ if (CACHE_UNKNOWN_MAPPINGS) {
+ _serializerCache.addAndResolveNonTypedSerializer(valueType, ser, this);
+ }
+ }
+ }
+ }
+ }
+ return ser;
+ }
+
+ /**
+ * Method variant used when we do NOT want contextualization to happen; it will need
+ * to be handled at a later point, but caller wants to be able to do that
+ * as needed; sometimes to avoid infinite loops
+ *
+ * @since 2.5
+ */
+ public JsonSerializer<Object> findValueSerializer(JavaType valueType)
+ throws JsonMappingException
+ {
+ // (see comments from above method)
+ JsonSerializer<Object> ser = _knownSerializers.untypedValueSerializer(valueType);
+ if (ser == null) {
+ ser = _serializerCache.untypedValueSerializer(valueType);
+ if (ser == null) {
+ ser = _createAndCacheUntypedSerializer(valueType);
+ if (ser == null) {
+ ser = getUnknownTypeSerializer(valueType.getRawClass());
+ if (CACHE_UNKNOWN_MAPPINGS) {
+ _serializerCache.addAndResolveNonTypedSerializer(valueType, ser, this);
+ }
+ }
+ }
+ }
+ return ser;
+ }
+
+ /**
* Similar to {@link #findValueSerializer(JavaType, BeanProperty)}, but used
* when finding "primary" property value serializer (one directly handling
* value of the property). Difference has to do with contextual resolution,
@@ -779,6 +818,17 @@
return _unknownTypeSerializer;
}
+ /**
+ * Helper method called to see if given serializer is considered to be
+ * something returned by {@link #getUnknownTypeSerializer}, that is, something
+ * for which no regular serializer was found or constructed.
+ *
+ * @since 2.5
+ */
+ public boolean isUnknownTypeSerializer(JsonSerializer<?> ser) {
+ return (ser == _unknownTypeSerializer) || (ser == null);
+ }
+
/*
/**********************************************************
/* Methods for creating instances based on annotations
@@ -807,19 +857,6 @@
*/
/**
- * @deprecated Since 2.3 (and to be removed from 2.4); use
- * {@link #handlePrimaryContextualization} or {@link #handleSecondaryContextualization}
- * instead
- */
- @Deprecated
- public JsonSerializer<?> handleContextualization(JsonSerializer<?> ser,
- BeanProperty property)
- throws JsonMappingException
- {
- return handleSecondaryContextualization(ser, property);
- }
-
- /**
* Method called for primary property serializers (ones
* directly created to serialize values of a POJO property),
* to handle details of resolving
@@ -1034,19 +1071,26 @@
* @return Serializer if one can be found, null if not.
*/
protected JsonSerializer<Object> _findExplicitUntypedSerializer(Class<?> runtimeType)
- throws JsonMappingException
+ throws JsonMappingException
{
// Fast lookup from local lookup thingy works?
JsonSerializer<Object> ser = _knownSerializers.untypedValueSerializer(runtimeType);
- if (ser != null) {
- return ser;
+ if (ser == null) {
+ // If not, maybe shared map already has it?
+ ser = _serializerCache.untypedValueSerializer(runtimeType);
+ if (ser == null) {
+ ser = _createAndCacheUntypedSerializer(runtimeType);
+ /* 18-Sep-2014, tatu: This is unfortunate patch over related change
+ * that pushes creation of "unknown type" serializer deeper down
+ * in BeanSerializerFactory; as a result, we need to "undo" creation
+ * here.
+ */
+ if (isUnknownTypeSerializer(ser)) {
+ return null;
+ }
+ }
}
- // If not, maybe shared map already has it?
- ser = _serializerCache.untypedValueSerializer(runtimeType);
- if (ser != null) {
- return ser;
- }
- return _createAndCacheUntypedSerializer(runtimeType);
+ return ser;
}
/*
diff --git a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonNaming.java b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonNaming.java
index c5fb85c..7b94610 100644
--- a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonNaming.java
+++ b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonNaming.java
@@ -15,5 +15,11 @@
@com.fasterxml.jackson.annotation.JacksonAnnotation
public @interface JsonNaming
{
- public Class<? extends PropertyNamingStrategy> value();
+ /**
+ * @return Type of {@link PropertyNamingStrategy} to use, if any; default value of
+ * <code>PropertyNamingStrategy.class</code> means "no strategy specified"
+ * (and may also be used for overriding to remove otherwise applicable
+ * naming strategy)
+ */
+ public Class<? extends PropertyNamingStrategy> value() default PropertyNamingStrategy.class;
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/DeserializerFactoryConfig.java b/src/main/java/com/fasterxml/jackson/databind/cfg/DeserializerFactoryConfig.java
index e451098..de0c6fe 100644
--- a/src/main/java/com/fasterxml/jackson/databind/cfg/DeserializerFactoryConfig.java
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/DeserializerFactoryConfig.java
@@ -12,7 +12,7 @@
public class DeserializerFactoryConfig
implements java.io.Serializable // since 2.1
{
- private static final long serialVersionUID = 3683541151102256824L;
+ private static final long serialVersionUID = 1L; // since 2.5
protected final static Deserializers[] NO_DESERIALIZERS = new Deserializers[0];
protected final static BeanDeserializerModifier[] NO_MODIFIERS = new BeanDeserializerModifier[0];
@@ -47,7 +47,6 @@
*/
protected final BeanDeserializerModifier[] _modifiers;
-
/**
* List of objects that may be able to resolve abstract types to
* concrete types. Used by functionality like "mr Bean" to materialize
@@ -63,7 +62,7 @@
* or to support post-constructor functionality.
*/
protected final ValueInstantiators[] _valueInstantiators;
-
+
/**
* Constructor for creating basic configuration with no additional
* handlers.
@@ -106,6 +105,7 @@
return new DeserializerFactoryConfig(all, _additionalKeyDeserializers, _modifiers,
_abstractTypeResolvers, _valueInstantiators);
}
+
/**
* Fluent/factory method used to construct a configuration object that
* has same key deserializer providers as this instance, plus one specified
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 6349e28..6b18329 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/AbstractDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/AbstractDeserializer.java
@@ -19,7 +19,7 @@
extends JsonDeserializer<Object>
implements java.io.Serializable
{
- private static final long serialVersionUID = -3010349050434697698L;
+ private static final long serialVersionUID = 1L;
protected final JavaType _baseType;
@@ -65,8 +65,7 @@
*
* @since 2.3
*/
- public static AbstractDeserializer constructForNonPOJO(BeanDescription beanDesc)
- {
+ public static AbstractDeserializer constructForNonPOJO(BeanDescription beanDesc) {
return new AbstractDeserializer(beanDesc);
}
@@ -147,8 +146,7 @@
/**********************************************************
*/
- protected Object _deserializeIfNatural(JsonParser jp, DeserializationContext ctxt)
- throws IOException, JsonProcessingException
+ protected Object _deserializeIfNatural(JsonParser jp, DeserializationContext ctxt) throws IOException
{
/* As per [JACKSON-417], there is a chance we might be "natural" types
* (String, Boolean, Integer, Double), which do not include any type information...
@@ -156,34 +154,32 @@
* Finally, we may have to consider possibility of custom handlers for
* these values: but for now this should work ok.
*/
- /* 21-Sep-2013, tatu: It may seem odd that I'm not using a switch here.
- * But turns out that a switch on an enum generates an inner class...
- * crazy! So this is to avoid that, simply since new class weighs about 1kB
- * after compression.
- */
- final JsonToken t = jp.getCurrentToken();
- if (t.isScalarValue()) {
- if (t == JsonToken.VALUE_STRING) {
- if (_acceptString) {
- return jp.getText();
- }
- } else if (t == JsonToken.VALUE_NUMBER_INT) {
- if (_acceptInt) {
- return jp.getIntValue();
- }
- } else if (t == JsonToken.VALUE_NUMBER_FLOAT) {
- if (_acceptDouble) {
- return Double.valueOf(jp.getDoubleValue());
- }
- } else if (t == JsonToken.VALUE_TRUE) {
- if (_acceptBoolean) {
- return Boolean.TRUE;
- }
- } else if (t == JsonToken.VALUE_FALSE) {
- if (_acceptBoolean) {
- return Boolean.FALSE;
- }
+ switch (jp.getCurrentTokenId()) {
+ case JsonTokenId.ID_STRING:
+ if (_acceptString) {
+ return jp.getText();
}
+ break;
+ case JsonTokenId.ID_NUMBER_INT:
+ if (_acceptInt) {
+ return jp.getIntValue();
+ }
+ break;
+ case JsonTokenId.ID_NUMBER_FLOAT:
+ if (_acceptDouble) {
+ return Double.valueOf(jp.getDoubleValue());
+ }
+ break;
+ case JsonTokenId.ID_TRUE:
+ if (_acceptBoolean) {
+ return Boolean.TRUE;
+ }
+ break;
+ case JsonTokenId.ID_FALSE:
+ if (_acceptBoolean) {
+ return Boolean.FALSE;
+ }
+ break;
}
return null;
}
@@ -192,8 +188,7 @@
* Method called in cases where it looks like we got an Object Id
* to parse and use as a reference.
*/
- protected Object _deserializeFromObjectId(JsonParser jp, DeserializationContext ctxt)
- throws IOException, JsonProcessingException
+ protected Object _deserializeFromObjectId(JsonParser jp, DeserializationContext ctxt) throws IOException
{
Object id = _objectIdReader.readObjectReference(jp, ctxt);
ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator, _objectIdReader.resolver);
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 b4e305d..8593664 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java
@@ -3,13 +3,16 @@
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicReference;
+import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.core.JsonLocation;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig;
import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
import com.fasterxml.jackson.databind.deser.impl.CreatorCollector;
import com.fasterxml.jackson.databind.deser.std.*;
+import com.fasterxml.jackson.databind.ext.OptionalHandlerFactory;
import com.fasterxml.jackson.databind.introspect.*;
import com.fasterxml.jackson.databind.jsontype.NamedType;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
@@ -39,6 +42,7 @@
private final static Class<?> CLASS_STRING = String.class;
private final static Class<?> CLASS_CHAR_BUFFER = CharSequence.class;
private final static Class<?> CLASS_ITERABLE = Iterable.class;
+ private final static Class<?> CLASS_MAP_ENTRY = Map.Entry.class;
/**
* We need a placeholder for creator properties that don't have name
@@ -58,21 +62,9 @@
_mapFallbacks.put(ConcurrentMap.class.getName(), ConcurrentHashMap.class);
_mapFallbacks.put(SortedMap.class.getName(), TreeMap.class);
- /* 11-Jan-2009, tatu: Let's see if we can still add support for
- * JDK 1.6 interfaces, even if we run on 1.5. Just need to be
- * more careful with typos, since compiler won't notice any
- * problems...
- */
- _mapFallbacks.put("java.util.NavigableMap", TreeMap.class);
- try {
- Class<?> key = java.util.concurrent.ConcurrentNavigableMap.class;
- Class<?> value = java.util.concurrent.ConcurrentSkipListMap.class;
- @SuppressWarnings("unchecked")
- Class<? extends Map<?,?>> mapValue = (Class<? extends Map<?,?>>) value;
- _mapFallbacks.put(key.getName(), mapValue);
- } catch (Throwable e) { // some class loading problems are Errors, others Exceptions
- System.err.println("Problems with (optional) types: "+e);
- }
+ _mapFallbacks.put(java.util.NavigableMap.class.getName(), TreeMap.class);
+ _mapFallbacks.put(java.util.concurrent.ConcurrentNavigableMap.class.getName(),
+ java.util.concurrent.ConcurrentSkipListMap.class);
}
/* We do some defaulting for abstract Collection classes and
@@ -316,17 +308,55 @@
VisibilityChecker<?> vchecker = config.getDefaultVisibilityChecker();
vchecker = intr.findAutoDetectVisibility(beanDesc.getClassInfo(), vchecker);
+ /* 24-Sep-2014, tatu: Tricky part first; need to merge resolved property information
+ * (which has creator parameters sprinkled around) with actual creator
+ * declarations (which are needed to access creator annotation, amongst other things).
+ * Easiest to combine that info first, then pass it to remaining processing.
+ */
+ Map<AnnotatedWithParams,BeanPropertyDefinition[]> creatorDefs = _findCreatorsFromProperties(ctxt,
+ beanDesc);
+
/* Important: first add factory methods; then constructors, so
* latter can override former!
*/
- _addDeserializerFactoryMethods(ctxt, beanDesc, vchecker, intr, creators);
+ _addDeserializerFactoryMethods(ctxt, beanDesc, vchecker, intr, creators, creatorDefs);
// constructors only usable on concrete types:
if (beanDesc.getType().isConcrete()) {
- _addDeserializerConstructors(ctxt, beanDesc, vchecker, intr, creators);
+ _addDeserializerConstructors(ctxt, beanDesc, vchecker, intr, creators, creatorDefs);
}
return creators.constructValueInstantiator(config);
}
+ protected Map<AnnotatedWithParams,BeanPropertyDefinition[]> _findCreatorsFromProperties(DeserializationContext ctxt,
+ BeanDescription beanDesc) throws JsonMappingException
+ {
+ Map<AnnotatedWithParams,BeanPropertyDefinition[]> result = Collections.emptyMap();
+ for (BeanPropertyDefinition propDef : beanDesc.findProperties()) {
+ Iterator<AnnotatedParameter> it = propDef.getConstructorParameters();
+ while (it.hasNext()) {
+ AnnotatedParameter param = it.next();
+ AnnotatedWithParams owner = param.getOwner();
+ BeanPropertyDefinition[] defs = result.get(owner);
+ final int index = param.getIndex();
+
+ if (defs == null) {
+ if (result.isEmpty()) {
+ result = new LinkedHashMap<AnnotatedWithParams,BeanPropertyDefinition[]>();
+ }
+ defs = new BeanPropertyDefinition[owner.getParameterCount()];
+ result.put(owner, defs);
+ } else {
+ if (defs[index] != null) {
+ throw new IllegalStateException("Conflict: parameter #"+index+" of "+owner
+ +" bound to more than one property; "+defs[index]+" vs "+propDef);
+ }
+ }
+ defs[index] = propDef;
+ }
+ }
+ return result;
+ }
+
public ValueInstantiator _valueInstantiatorInstance(DeserializationConfig config,
Annotated annotated, Object instDef)
throws JsonMappingException
@@ -363,131 +393,155 @@
return (ValueInstantiator) ClassUtil.createInstance(instClass,
config.canOverrideAccessModifiers());
}
+
+ @Deprecated // since 2.5.0, removed from 2.6.0
+ protected void _addDeserializerConstructors(DeserializationContext ctxt, BeanDescription beanDesc, VisibilityChecker<?> vchecker,
+ AnnotationIntrospector intr, CreatorCollector creators)
+ throws JsonMappingException
+ {
+ _addDeserializerConstructors(ctxt, beanDesc, vchecker, intr, creators,
+ Collections.<AnnotatedWithParams,BeanPropertyDefinition[]>emptyMap());
+ }
protected void _addDeserializerConstructors
(DeserializationContext ctxt, BeanDescription beanDesc, VisibilityChecker<?> vchecker,
- AnnotationIntrospector intr, CreatorCollector creators)
+ AnnotationIntrospector intr, CreatorCollector creators,
+ Map<AnnotatedWithParams,BeanPropertyDefinition[]> creatorParams)
throws JsonMappingException
{
- /* First things first: the "default constructor" (zero-arg
- * constructor; whether implicit or explicit) is NOT included
- * in list of constructors, so needs to be handled separately.
- */
+ // First things first: the "default constructor" (zero-arg
+ // constructor; whether implicit or explicit) is NOT included
+ // in list of constructors, so needs to be handled separately.
AnnotatedConstructor defaultCtor = beanDesc.findDefaultConstructor();
if (defaultCtor != null) {
if (!creators.hasDefaultCreator() || intr.hasCreatorAnnotation(defaultCtor)) {
creators.setDefaultCreator(defaultCtor);
}
}
-
- PropertyName[] ctorPropNames = null;
- AnnotatedConstructor propertyCtor = null;
- for (BeanPropertyDefinition propDef : beanDesc.findProperties()) {
- if (propDef.getConstructorParameter() != null) {
- AnnotatedParameter param = propDef.getConstructorParameter();
- AnnotatedWithParams owner = param.getOwner();
- if (owner instanceof AnnotatedConstructor) {
- if (propertyCtor == null) {
- propertyCtor = (AnnotatedConstructor) owner;
- ctorPropNames = new PropertyName[propertyCtor.getParameterCount()];
- }
- ctorPropNames[param.getIndex()] = propDef.getFullName();
- }
- }
- }
-
for (AnnotatedConstructor ctor : beanDesc.getConstructors()) {
- int argCount = ctor.getParameterCount();
- boolean isCreator = intr.hasCreatorAnnotation(ctor) || ctor == propertyCtor;
- boolean isVisible = vchecker.isCreatorVisible(ctor);
- // some single-arg constructors (String, number) are auto-detected
+ final boolean isCreator = intr.hasCreatorAnnotation(ctor);
+ BeanPropertyDefinition[] propDefs = creatorParams.get(ctor);
+ final int argCount = ctor.getParameterCount();
+
+ // some single-arg factory methods (String, number) are auto-detected
if (argCount == 1) {
- PropertyName name = (ctor == propertyCtor) ? ctorPropNames[0] : null;
- _handleSingleArgumentConstructor(ctxt, beanDesc, vchecker, intr, creators,
- ctor, isCreator, isVisible, name);
- continue;
- }
- if (!isCreator && !isVisible) {
+ BeanPropertyDefinition argDef = (propDefs == null) ? null : propDefs[0];
+ boolean useProps = _checkIfCreatorPropertyBased(intr, ctor, argDef);
+
+ if (useProps) {
+ CreatorProperty[] properties = new CreatorProperty[1];
+ PropertyName name = (argDef == null) ? null : argDef.getFullName();
+ AnnotatedParameter arg = ctor.getParameter(0);
+ properties[0] = constructCreatorProperty(ctxt, beanDesc, name, 0, arg,
+ intr.findInjectableValueId(arg));
+ creators.addPropertyCreator(ctor, properties);
+ } else {
+ /*boolean added = */ _handleSingleArgumentConstructor(ctxt, beanDesc, vchecker, intr, creators,
+ ctor, isCreator,
+ vchecker.isCreatorVisible(ctor));
+ // one more thing: sever link to creator property, to avoid possible later
+ // problems with "unresolved" constructor property
+ if (argDef != null) {
+ ((POJOPropertyBuilder) argDef).removeConstructors();
+ }
+ }
+ // regardless, fully handled
continue;
}
- // [JACKSON-541] improved handling a bit so:
- // 2 or more args; all params must have name annotations
- // ... or @JacksonInject (or equivalent)
- /* [JACKSON-711] One more possibility; can have 1 or more injectables, and
- * exactly one non-annotated parameter: if so, it's still delegating.
- */
- AnnotatedParameter nonAnnotatedParam = null;
- int namedCount = 0;
- int injectCount = 0;
+ // 2 or more args; all params must have names or be injectable
+ AnnotatedParameter nonAnnotatedParam = null;
CreatorProperty[] properties = new CreatorProperty[argCount];
+ int explicitNameCount = 0;
+ int implicitNameCount = 0;
+ int injectCount = 0;
for (int i = 0; i < argCount; ++i) {
- AnnotatedParameter param = ctor.getParameter(i);
- PropertyName name = null;
- if (ctor == propertyCtor) {
- name = ctorPropNames[i];
- }
- if (name == null) {
- name = _findParamName(param, intr);
- }
+ final AnnotatedParameter param = ctor.getParameter(i);
+ BeanPropertyDefinition propDef = (propDefs == null) ? null : propDefs[i];
Object injectId = intr.findInjectableValueId(param);
- if (name != null && name.hasSimpleName()) {
- ++namedCount;
+ final PropertyName name = (propDef == null) ? null : propDef.getFullName();
+ if (propDef != null && propDef.isExplicitlyNamed()) {
+ ++explicitNameCount;
properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectId);
- } else if (injectId != null) { // injectable
+ continue;
+ }
+ if (injectId != null) {
++injectCount;
properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectId);
- } else {
- NameTransformer unwrapper = intr.findUnwrappingNameTransformer(param);
- if (unwrapper != null) { // [Issue#265]: allow unwrapped properties
- properties[i] = constructCreatorProperty(ctxt, beanDesc,
- UNWRAPPED_CREATOR_PARAM_NAME, i, param, null);
- ++namedCount;
- } else {
- if (nonAnnotatedParam == null) {
- nonAnnotatedParam = param;
- }
+ continue;
+ }
+ NameTransformer unwrapper = intr.findUnwrappingNameTransformer(param);
+ if (unwrapper != null) {
+ properties[i] = constructCreatorProperty(ctxt, beanDesc, UNWRAPPED_CREATOR_PARAM_NAME, i, param, null);
+ ++explicitNameCount;
+ continue;
+ }
+ // One more thing: implicit names are ok iff ctor has creator annotation
+ if (isCreator) {
+ if (name != null && !name.isEmpty()) {
+ ++implicitNameCount;
+ properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectId);
+ continue;
}
}
+ if (nonAnnotatedParam == null) {
+ nonAnnotatedParam = param;
+ }
}
+ final int namedCount = explicitNameCount + implicitNameCount;
// Ok: if named or injectable, we have more work to do
- if (isCreator || namedCount > 0 || injectCount > 0) {
+ if (isCreator || explicitNameCount > 0 || injectCount > 0) {
// simple case; everything covered:
if ((namedCount + injectCount) == argCount) {
creators.addPropertyCreator(ctor, properties);
- } else if ((namedCount == 0) && ((injectCount + 1) == argCount)) {
+ } else if ((explicitNameCount == 0) && ((injectCount + 1) == argCount)) {
// [712] secondary: all but one injectable, one un-annotated (un-named)
creators.addDelegatingCreator(ctor, properties);
- } else { // otherwise, record the incomplete parameter for later error messaging.
- creators.addIncompeteParameter(nonAnnotatedParam);
+ } else { // otherwise, epic fail
+ throw new IllegalArgumentException("Argument #"+nonAnnotatedParam.getIndex()
+ +" of constructor "+ctor+" has no property name annotation; must have name when multiple-parameter constructor annotated as Creator");
}
}
}
}
+ protected boolean _checkIfCreatorPropertyBased(AnnotationIntrospector intr,
+ AnnotatedWithParams creator, BeanPropertyDefinition propDef)
+ {
+ JsonCreator.Mode mode = intr.findCreatorBinding(creator);
+
+ if (mode == JsonCreator.Mode.PROPERTIES) {
+ return true;
+ }
+ if (mode == JsonCreator.Mode.DELEGATING) {
+ return false;
+ }
+ // If explicit name, or inject id, property-based
+ if (((propDef != null) && propDef.isExplicitlyNamed())
+ || (intr.findInjectableValueId(creator.getParameter(0)) != null)) {
+ return true;
+ }
+ if (propDef != null) {
+ // One more thing: if implicit name matches property with a getter
+ // or field, we'll consider it property-based as well
+ String implName = propDef.getName();
+ if (implName != null && !implName.isEmpty()) {
+ if (propDef.couldSerialize()) {
+ return true;
+ }
+ }
+ }
+ // in absence of everything else, default to delegating
+ return false;
+ }
+
protected boolean _handleSingleArgumentConstructor(DeserializationContext ctxt,
BeanDescription beanDesc, VisibilityChecker<?> vchecker,
AnnotationIntrospector intr, CreatorCollector creators,
- AnnotatedConstructor ctor, boolean isCreator, boolean isVisible,
- PropertyName name)
+ AnnotatedConstructor ctor, boolean isCreator, boolean isVisible)
throws JsonMappingException
{
- // note: if we do have parameter name, it'll be "property constructor":
- AnnotatedParameter param = ctor.getParameter(0);
- if (name == null) {
- name = _findParamName(param, intr);
- }
- Object injectId = intr.findInjectableValueId(param);
-
- if ((injectId != null) || (name != null && name.hasSimpleName())) { // property-based
- // We know there's a name and it's only 1 parameter.
- CreatorProperty[] properties = new CreatorProperty[1];
- properties[0] = constructCreatorProperty(ctxt, beanDesc, name, 0, param, injectId);
- creators.addPropertyCreator(ctor, properties);
- return true;
- }
-
// otherwise either 'simple' number, String, or general delegate:
Class<?> type = ctor.getRawParameterType(0);
if (type == String.class) {
@@ -528,15 +582,25 @@
return false;
}
+ @Deprecated // since 2.5, remove from 2.6
+ protected void _addDeserializerFactoryMethods(DeserializationContext ctxt, BeanDescription beanDesc, VisibilityChecker<?> vchecker,
+ AnnotationIntrospector intr, CreatorCollector creators)
+ throws JsonMappingException
+ {
+ _addDeserializerFactoryMethods(ctxt, beanDesc, vchecker, intr, creators,
+ Collections.<AnnotatedWithParams,BeanPropertyDefinition[]>emptyMap());
+ }
+
protected void _addDeserializerFactoryMethods
(DeserializationContext ctxt, BeanDescription beanDesc, VisibilityChecker<?> vchecker,
- AnnotationIntrospector intr, CreatorCollector creators)
+ AnnotationIntrospector intr, CreatorCollector creators,
+ Map<AnnotatedWithParams,BeanPropertyDefinition[]> creatorParams)
throws JsonMappingException
{
final DeserializationConfig config = ctxt.getConfig();
for (AnnotatedMethod factory : beanDesc.getFactoryMethods()) {
- boolean isCreator = intr.hasCreatorAnnotation(factory);
- int argCount = factory.getParameterCount();
+ final boolean isCreator = intr.hasCreatorAnnotation(factory);
+ final int argCount = factory.getParameterCount();
// zero-arg methods must be annotated; if so, are "default creators" [JACKSON-850]
if (argCount == 0) {
if (isCreator) {
@@ -544,60 +608,86 @@
}
continue;
}
+
+ final BeanPropertyDefinition[] propDefs = creatorParams.get(factory);
// some single-arg factory methods (String, number) are auto-detected
if (argCount == 1) {
- AnnotatedParameter param = factory.getParameter(0);
- PropertyName pn = _findParamName(param, intr);
- String name = (pn == null) ? null : pn.getSimpleName();
- Object injectId = intr.findInjectableValueId(param);
-
- if ((injectId == null) && (name == null || name.length() == 0)) { // not property based
- _handleSingleArgumentFactory(config, beanDesc, vchecker, intr, creators,
+ BeanPropertyDefinition argDef = (propDefs == null) ? null : propDefs[0];
+ boolean useProps = _checkIfCreatorPropertyBased(intr, factory, argDef);
+ if (!useProps) { // not property based but delegating
+ /*boolean added=*/ _handleSingleArgumentFactory(config, beanDesc, vchecker, intr, creators,
factory, isCreator);
// otherwise just ignored
continue;
}
// fall through if there's name
} else {
- // more than 2 args, must be @JsonCreator
- if (!intr.hasCreatorAnnotation(factory)) {
+ // more than 2 args, must have @JsonCreator
+ if (!isCreator) {
continue;
}
}
// 1 or more args; all params must have name annotations
AnnotatedParameter nonAnnotatedParam = null;
CreatorProperty[] properties = new CreatorProperty[argCount];
- int namedCount = 0;
- int injectCount = 0;
+ int implicitNameCount = 0;
+ int explicitNameCount = 0;
+ int injectCount = 0;
+
for (int i = 0; i < argCount; ++i) {
- AnnotatedParameter param = factory.getParameter(i);
- PropertyName name = _findParamName(param, intr);
+ final AnnotatedParameter param = factory.getParameter(i);
+ BeanPropertyDefinition propDef = (propDefs == null) ? null : propDefs[i];
Object injectId = intr.findInjectableValueId(param);
- if (name != null && name.hasSimpleName()) {
- ++namedCount;
+ final PropertyName name = (propDef == null) ? null : propDef.getFullName();
+
+ if (propDef != null && propDef.isExplicitlyNamed()) {
+ ++explicitNameCount;
properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectId);
- } else if (injectId != null) {
+ continue;
+ }
+ if (injectId != null) {
++injectCount;
properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectId);
- } else {
- NameTransformer unwrapper = intr.findUnwrappingNameTransformer(param);
- if (unwrapper != null) {
- properties[i] = constructCreatorProperty(ctxt, beanDesc, UNWRAPPED_CREATOR_PARAM_NAME, i, param, null);
- ++namedCount;
- } else {
- if (nonAnnotatedParam == null) {
- nonAnnotatedParam = param;
- }
+ continue;
+ }
+ NameTransformer unwrapper = intr.findUnwrappingNameTransformer(param);
+ if (unwrapper != null) {
+ properties[i] = constructCreatorProperty(ctxt, beanDesc, UNWRAPPED_CREATOR_PARAM_NAME, i, param, null);
+ ++implicitNameCount;
+ continue;
+ }
+ // One more thing: implicit names are ok iff ctor has creator annotation
+ if (isCreator) {
+ if (name != null && !name.isEmpty()) {
+ ++implicitNameCount;
+ properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectId);
+ continue;
}
}
+ /* 25-Sep-2014, tatu: Actually, we may end up "losing" naming due to higher-priority constructor
+ * (see TestCreators#testConstructorCreator() test). And just to avoid running into that problem,
+ * let's add one more work around
+ */
+ /*
+ PropertyName name2 = _findExplicitParamName(param, intr);
+ if (name2 != null && !name2.isEmpty()) {
+ // Hmmh. Ok, fine. So what are we to do with it... ?
+ // For now... skip. May need to revisit this, should this become problematic
+ continue main_loop;
+ }
+ */
+ if (nonAnnotatedParam == null) {
+ nonAnnotatedParam = param;
+ }
}
-
+ final int namedCount = explicitNameCount + implicitNameCount;
+
// Ok: if named or injectable, we have more work to do
- if (isCreator || namedCount > 0 || injectCount > 0) {
+ if (isCreator || explicitNameCount > 0 || injectCount > 0) {
// simple case; everything covered:
if ((namedCount + injectCount) == argCount) {
creators.addPropertyCreator(factory, properties);
- } else if ((namedCount == 0) && ((injectCount + 1) == argCount)) {
+ } else if ((explicitNameCount == 0) && ((injectCount + 1) == argCount)) {
// [712] secondary: all but one injectable, one un-annotated (un-named)
creators.addDelegatingCreator(factory, properties);
} else { // otherwise, epic fail
@@ -646,7 +736,7 @@
}
return true;
}
- if (intr.hasCreatorAnnotation(factory)) {
+ if (isCreator) {
creators.addDelegatingCreator(factory, null);
return true;
}
@@ -708,7 +798,7 @@
}
return prop;
}
-
+
protected PropertyName _findParamName(AnnotatedParameter param, AnnotationIntrospector intr)
{
if (param != null && intr != null) {
@@ -726,6 +816,32 @@
}
return null;
}
+
+ protected PropertyName _findExplicitParamName(AnnotatedParameter param, AnnotationIntrospector intr)
+ {
+ if (param != null && intr != null) {
+ return intr.findNameForDeserialization(param);
+ }
+ return null;
+ }
+
+ protected PropertyName _findImplicitParamName(AnnotatedParameter param, AnnotationIntrospector intr)
+ {
+ String str = intr.findImplicitPropertyName(param);
+ if (str != null && !str.isEmpty()) {
+ return new PropertyName(str);
+ }
+ return null;
+ }
+
+ protected boolean _hasExplicitParamName(AnnotatedParameter param, AnnotationIntrospector intr)
+ {
+ if (param != null && intr != null) {
+ PropertyName n = intr.findNameForDeserialization(param);
+ return (n != null) && n.hasSimpleName();
+ }
+ return false;
+ }
/*
/**********************************************************
@@ -743,7 +859,7 @@
// Very first thing: is deserializer hard-coded for elements?
JsonDeserializer<Object> contentDeser = elemType.getValueHandler();
- // Then optional type info (1.5): if type has been resolved, we may already know type deserializer:
+ // Then optional type info: if type has been resolved, we may already know type deserializer:
TypeDeserializer elemTypeDeser = elemType.getTypeHandler();
// but if not, may still be possible to find:
if (elemTypeDeser == null) {
@@ -772,21 +888,6 @@
return deser;
}
- protected JsonDeserializer<?> _findCustomArrayDeserializer(ArrayType type,
- DeserializationConfig config, BeanDescription beanDesc,
- TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
- throws JsonMappingException
- {
- for (Deserializers d : _factoryConfig.deserializers()) {
- JsonDeserializer<?> deser = d.findArrayDeserializer(type, config,
- beanDesc, elementTypeDeserializer, elementDeserializer);
- if (deser != null) {
- return deser;
- }
- }
- return null;
- }
-
/*
/**********************************************************
/* JsonDeserializerFactory impl: Collection(-like) deserializers
@@ -882,21 +983,6 @@
}
return (CollectionType) config.constructSpecializedType(type, collectionClass);
}
-
- protected JsonDeserializer<?> _findCustomCollectionDeserializer(CollectionType type,
- DeserializationConfig config, BeanDescription beanDesc,
- TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
- throws JsonMappingException
- {
- for (Deserializers d : _factoryConfig.deserializers()) {
- JsonDeserializer<?> deser = d.findCollectionDeserializer(type, config, beanDesc,
- elementTypeDeserializer, elementDeserializer);
- if (deser != null) {
- return deser;
- }
- }
- return null;
- }
// Copied almost verbatim from "createCollectionDeserializer" -- should try to share more code
@Override
@@ -928,27 +1014,12 @@
return deser;
}
- protected JsonDeserializer<?> _findCustomCollectionLikeDeserializer(CollectionLikeType type,
- DeserializationConfig config, BeanDescription beanDesc,
- TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
- throws JsonMappingException
- {
- for (Deserializers d : _factoryConfig.deserializers()) {
- JsonDeserializer<?> deser = d.findCollectionLikeDeserializer(type, config, beanDesc,
- elementTypeDeserializer, elementDeserializer);
- if (deser != null) {
- return deser;
- }
- }
- return null;
- }
-
/*
/**********************************************************
/* JsonDeserializerFactory impl: Map(-like) deserializers
/**********************************************************
*/
-
+
@Override
public JsonDeserializer<?> createMapDeserializer(DeserializationContext ctxt,
MapType type, BeanDescription beanDesc)
@@ -1071,38 +1142,6 @@
return deser;
}
- protected JsonDeserializer<?> _findCustomMapDeserializer(MapType type,
- DeserializationConfig config, BeanDescription beanDesc,
- KeyDeserializer keyDeserializer,
- TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
- throws JsonMappingException
- {
- for (Deserializers d : _factoryConfig.deserializers()) {
- JsonDeserializer<?> deser = d.findMapDeserializer(type, config, beanDesc,
- keyDeserializer, elementTypeDeserializer, elementDeserializer);
- if (deser != null) {
- return deser;
- }
- }
- return null;
- }
-
- protected JsonDeserializer<?> _findCustomMapLikeDeserializer(MapLikeType type,
- DeserializationConfig config, BeanDescription beanDesc,
- KeyDeserializer keyDeserializer,
- TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
- throws JsonMappingException
- {
- for (Deserializers d : _factoryConfig.deserializers()) {
- JsonDeserializer<?> deser = d.findMapLikeDeserializer(type, config, beanDesc,
- keyDeserializer, elementTypeDeserializer, elementDeserializer);
- if (deser != null) {
- return deser;
- }
- }
- return null;
- }
-
/*
/**********************************************************
/* JsonDeserializerFactory impl: Enum deserializers
@@ -1152,19 +1191,6 @@
}
return deser;
}
-
- protected JsonDeserializer<?> _findCustomEnumDeserializer(Class<?> type,
- DeserializationConfig config, BeanDescription beanDesc)
- throws JsonMappingException
- {
- for (Deserializers d : _factoryConfig.deserializers()) {
- JsonDeserializer<?> deser = d.findEnumDeserializer(type, config, beanDesc);
- if (deser != null) {
- return deser;
- }
- }
- return null;
- }
/*
/**********************************************************
@@ -1187,19 +1213,6 @@
}
return JsonNodeDeserializer.getDeserializer(nodeClass);
}
-
- protected JsonDeserializer<?> _findCustomTreeNodeDeserializer(Class<? extends JsonNode> type,
- DeserializationConfig config, BeanDescription beanDesc)
- throws JsonMappingException
- {
- for (Deserializers d : _factoryConfig.deserializers()) {
- JsonDeserializer<?> deser = d.findTreeNodeDeserializer(type, config, beanDesc);
- if (deser != null) {
- return deser;
- }
- }
- return null;
- }
/*
/**********************************************************
@@ -1240,6 +1253,18 @@
return b.buildTypeDeserializer(config, baseType, subtypes);
}
+ /**
+ * Overridable method called after checking all other types.
+ *
+ * @since 2.2
+ */
+ protected JsonDeserializer<?> findOptionalStdDeserializer(DeserializationContext ctxt,
+ JavaType type, BeanDescription beanDesc)
+ throws JsonMappingException
+ {
+ return OptionalHandlerFactory.instance.findDeserializer(type, ctxt.getConfig(), beanDesc);
+ }
+
/*
/**********************************************************
/* JsonDeserializerFactory impl (partial): key deserializers
@@ -1410,11 +1435,31 @@
if (rawType == CLASS_ITERABLE) {
// [Issue#199]: Can and should 'upgrade' to a Collection type:
TypeFactory tf = ctxt.getTypeFactory();
- JavaType elemType = (type.containedTypeCount() > 0) ? type.containedType(0) : TypeFactory.unknownType();
+ JavaType[] tps = tf.findTypeParameters(type, CLASS_ITERABLE);
+ JavaType elemType = (tps == null || tps.length != 1) ? TypeFactory.unknownType() : tps[0];
CollectionType ct = tf.constructCollectionType(Collection.class, elemType);
// Should we re-introspect beanDesc? For now let's not...
return createCollectionDeserializer(ctxt, ct, beanDesc);
}
+ if (rawType == CLASS_MAP_ENTRY) {
+ final DeserializationConfig config = ctxt.getConfig();
+ TypeFactory tf = ctxt.getTypeFactory();
+ JavaType[] tps = tf.findTypeParameters(type, CLASS_MAP_ENTRY);
+ JavaType kt, vt;
+ if (tps == null || tps.length != 2) {
+ kt = vt = TypeFactory.unknownType();
+ } else {
+ kt = tps[0];
+ vt = tps[1];
+ }
+ TypeDeserializer vts = (TypeDeserializer) vt.getTypeHandler();
+ if (vts == null) {
+ vts = findTypeDeserializer(config, vt);
+ }
+ JsonDeserializer<Object> valueDeser = vt.getValueHandler();
+ KeyDeserializer keyDes = (KeyDeserializer) kt.getValueHandler();
+ return new MapEntryDeserializer(type, keyDes, valueDeser, vts);
+ }
String clsName = rawType.getName();
if (rawType.isPrimitive() || clsName.startsWith("java.")) {
// Primitives/wrappers, other Numbers:
@@ -1430,11 +1475,153 @@
if (rawType == TokenBuffer.class) {
return new TokenBufferDeserializer();
}
+ if (AtomicReference.class.isAssignableFrom(rawType)) {
+ // Must find parameterization
+ TypeFactory tf = ctxt.getTypeFactory();
+ JavaType[] params = tf.findTypeParameters(type, AtomicReference.class);
+ JavaType referencedType;
+ if (params == null || params.length < 1) { // untyped (raw)
+ referencedType = TypeFactory.unknownType();
+ } else {
+ referencedType = params[0];
+ }
+ TypeDeserializer vts = findTypeDeserializer(ctxt.getConfig(), referencedType);
+ BeanDescription refdDesc = ctxt.getConfig().introspectClassAnnotations(referencedType);
+ JsonDeserializer<?> deser = findDeserializerFromAnnotation(ctxt, refdDesc.getClassInfo());
+ return new AtomicReferenceDeserializer(referencedType, vts, deser);
+ }
+ JsonDeserializer<?> deser = findOptionalStdDeserializer(ctxt, type, beanDesc);
+ if (deser != null) {
+ return deser;
+ }
return JdkDeserializers.find(rawType, clsName);
}
/*
/**********************************************************
+ /* Helper methods, finding custom deserializers
+ /**********************************************************
+ */
+
+ protected JsonDeserializer<?> _findCustomArrayDeserializer(ArrayType type,
+ DeserializationConfig config, BeanDescription beanDesc,
+ TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
+ throws JsonMappingException
+ {
+ for (Deserializers d : _factoryConfig.deserializers()) {
+ JsonDeserializer<?> deser = d.findArrayDeserializer(type, config,
+ beanDesc, elementTypeDeserializer, elementDeserializer);
+ if (deser != null) {
+ return deser;
+ }
+ }
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ protected JsonDeserializer<Object> _findCustomBeanDeserializer(JavaType type,
+ DeserializationConfig config, BeanDescription beanDesc)
+ throws JsonMappingException
+ {
+ for (Deserializers d : _factoryConfig.deserializers()) {
+ JsonDeserializer<?> deser = d.findBeanDeserializer(type, config, beanDesc);
+ if (deser != null) {
+ return (JsonDeserializer<Object>) deser;
+ }
+ }
+ return null;
+ }
+
+ protected JsonDeserializer<?> _findCustomCollectionDeserializer(CollectionType type,
+ DeserializationConfig config, BeanDescription beanDesc,
+ TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
+ throws JsonMappingException
+ {
+ for (Deserializers d : _factoryConfig.deserializers()) {
+ JsonDeserializer<?> deser = d.findCollectionDeserializer(type, config, beanDesc,
+ elementTypeDeserializer, elementDeserializer);
+ if (deser != null) {
+ return deser;
+ }
+ }
+ return null;
+ }
+
+ protected JsonDeserializer<?> _findCustomCollectionLikeDeserializer(CollectionLikeType type,
+ DeserializationConfig config, BeanDescription beanDesc,
+ TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
+ throws JsonMappingException
+ {
+ for (Deserializers d : _factoryConfig.deserializers()) {
+ JsonDeserializer<?> deser = d.findCollectionLikeDeserializer(type, config, beanDesc,
+ elementTypeDeserializer, elementDeserializer);
+ if (deser != null) {
+ return deser;
+ }
+ }
+ return null;
+ }
+
+ protected JsonDeserializer<?> _findCustomEnumDeserializer(Class<?> type,
+ DeserializationConfig config, BeanDescription beanDesc)
+ throws JsonMappingException
+ {
+ for (Deserializers d : _factoryConfig.deserializers()) {
+ JsonDeserializer<?> deser = d.findEnumDeserializer(type, config, beanDesc);
+ if (deser != null) {
+ return deser;
+ }
+ }
+ return null;
+ }
+
+ protected JsonDeserializer<?> _findCustomMapDeserializer(MapType type,
+ DeserializationConfig config, BeanDescription beanDesc,
+ KeyDeserializer keyDeserializer,
+ TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
+ throws JsonMappingException
+ {
+ for (Deserializers d : _factoryConfig.deserializers()) {
+ JsonDeserializer<?> deser = d.findMapDeserializer(type, config, beanDesc,
+ keyDeserializer, elementTypeDeserializer, elementDeserializer);
+ if (deser != null) {
+ return deser;
+ }
+ }
+ return null;
+ }
+
+ protected JsonDeserializer<?> _findCustomMapLikeDeserializer(MapLikeType type,
+ DeserializationConfig config, BeanDescription beanDesc,
+ KeyDeserializer keyDeserializer,
+ TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
+ throws JsonMappingException
+ {
+ for (Deserializers d : _factoryConfig.deserializers()) {
+ JsonDeserializer<?> deser = d.findMapLikeDeserializer(type, config, beanDesc,
+ keyDeserializer, elementTypeDeserializer, elementDeserializer);
+ if (deser != null) {
+ return deser;
+ }
+ }
+ return null;
+ }
+
+ protected JsonDeserializer<?> _findCustomTreeNodeDeserializer(Class<? extends JsonNode> type,
+ DeserializationConfig config, BeanDescription beanDesc)
+ throws JsonMappingException
+ {
+ for (Deserializers d : _factoryConfig.deserializers()) {
+ JsonDeserializer<?> deser = d.findTreeNodeDeserializer(type, config, beanDesc);
+ if (deser != null) {
+ return deser;
+ }
+ }
+ return null;
+ }
+
+ /*
+ /**********************************************************
/* Helper methods, value/content/key type introspection
/**********************************************************
*/
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 0d9af65..727aca7 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java
@@ -325,8 +325,8 @@
@Override
@SuppressWarnings("resource")
protected Object _deserializeUsingPropertyBased(final JsonParser jp, final DeserializationContext ctxt)
- throws IOException, JsonProcessingException
- {
+ throws IOException
+ {
final PropertyBasedCreator creator = _propertyBasedCreator;
PropertyValueBuffer buffer = creator.startBuilding(jp, ctxt, _objectIdReader);
@@ -351,6 +351,9 @@
wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt);
bean = null; // never gets here
}
+ if (bean == null) {
+ throw ctxt.instantiationException(_beanType.getRawClass(), "JSON Creator returned null");
+ }
// polymorphic?
if (bean.getClass() != _beanType.getRawClass()) {
return handlePolymorphic(jp, ctxt, bean, unknown);
@@ -391,7 +394,7 @@
unknown.writeFieldName(propName);
unknown.copyCurrentStructure(jp);
}
-
+
// We hit END_OBJECT, so:
Object bean;
try {
@@ -509,7 +512,7 @@
tokens.writeEndObject();
_unwrappedPropertyHandler.processUnwrapped(jp, ctxt, bean, tokens);
return bean;
- }
+ }
@SuppressWarnings("resource")
protected Object deserializeWithUnwrapped(JsonParser jp, DeserializationContext ctxt, Object bean)
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 ac65574..7770f03 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java
@@ -29,7 +29,7 @@
implements ContextualDeserializer, ResolvableDeserializer,
java.io.Serializable // since 2.1
{
- private static final long serialVersionUID = 2960120955735322578L;
+ private static final long serialVersionUID = 1;
protected final static PropertyName TEMP_PROPERTY_NAME = new PropertyName("#temporary-name");
@@ -111,7 +111,7 @@
* to use have been successfully resolved.
*/
final protected BeanPropertyMap _beanProperties;
-
+
/**
* List of {@link ValueInjector}s, if any injectable values are
* expected by the bean; otherwise null.
@@ -119,7 +119,7 @@
* and fields, but not ones passed through constructor parameters.
*/
final protected ValueInjector[] _injectables;
-
+
/**
* Fallback setter used for handling any properties that are not
* mapped to regular setters. If setter is not null, it will be
@@ -238,8 +238,7 @@
;
}
- protected BeanDeserializerBase(BeanDeserializerBase src)
- {
+ protected BeanDeserializerBase(BeanDeserializerBase src) {
this(src, src._ignoreAllUnknown);
}
@@ -517,6 +516,7 @@
}
JsonDeserializer<Object> dd = findDeserializer(ctxt, delegateType, property);
if (td != null) {
+ td = td.forProperty(property);
dd = new TypeWrappedDeserializer(td, dd);
}
_delegateDeserializer = dd;
@@ -1031,8 +1031,7 @@
* buffering in some cases, but usually just a simple lookup to ensure
* that ordering is correct.
*/
- protected Object deserializeWithObjectId(JsonParser jp, DeserializationContext ctxt) throws IOException
- {
+ protected Object deserializeWithObjectId(JsonParser jp, DeserializationContext ctxt) throws IOException {
return deserializeFromObject(jp, ctxt);
}
@@ -1069,7 +1068,7 @@
+" (need to add/enable type information?)");
}
throw JsonMappingException.from(jp, "No suitable constructor found for type "
- +_beanType+": can not instantiate from JSON object (need to add/enable type information?)");
+ +_beanType+": can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)");
}
protected abstract Object _deserializeUsingPropertyBased(final JsonParser jp,
@@ -1200,15 +1199,24 @@
wrapInstantiationProblem(e, ctxt);
}
} else if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
- jp.nextToken();
+ JsonToken t = jp.nextToken();
+ if (t == JsonToken.END_ARRAY && ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT)) {
+ return null;
+ }
final Object 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 '" + _valueClass.getName() + "' value but there was more than a single value in the array");
}
return value;
+ } else if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT)) {
+ JsonToken t = jp.nextToken();
+ if (t == JsonToken.END_ARRAY) {
+ return null;
+ }
+ throw ctxt.mappingException(handledType(), JsonToken.START_ARRAY);
}
- throw ctxt.mappingException(getBeanClass());
+ throw ctxt.mappingException(handledType());
}
public Object deserializeFromEmbedded(JsonParser jp, DeserializationContext ctxt) throws IOException
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 24c0ca0..c2e6f72 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBuilder.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBuilder.java
@@ -180,14 +180,6 @@
// For now, won't add, since it is inferred, not explicit...
}
- @Deprecated // since 2.3
- public void addInjectable(String propName, JavaType propType,
- Annotations contextAnnotations, AnnotatedMember member,
- Object valueId)
- {
- addInjectable(new PropertyName(propName), propType, contextAnnotations, member, valueId);
- }
-
public void addInjectable(PropertyName propName, JavaType propType,
Annotations contextAnnotations, AnnotatedMember member,
Object valueId)
@@ -269,41 +261,17 @@
return _properties.values().iterator();
}
- /**
- * @since 2.3
- */
public SettableBeanProperty findProperty(PropertyName propertyName) {
return _properties.get(propertyName.getSimpleName());
}
- @Deprecated // since 2.3
- public SettableBeanProperty findProperty(String propertyName) {
- return _properties.get(propertyName);
- }
-
- /**
- * @since 2.3
- */
public boolean hasProperty(PropertyName propertyName) {
return findProperty(propertyName) != null;
}
-
- @Deprecated // since 2.3
- public boolean hasProperty(String propertyName) {
- return findProperty(propertyName) != null;
- }
- /**
- * @since 2.3
- */
public SettableBeanProperty removeProperty(PropertyName name) {
return _properties.remove(name.getSimpleName());
}
-
- @Deprecated // since 2.3
- public SettableBeanProperty removeProperty(String name) {
- return _properties.remove(name);
- }
public SettableAnyProperty getAnySetter() {
return _anySetter;
@@ -384,8 +352,7 @@
*
* @since 2.0
*/
- public AbstractDeserializer buildAbstract()
- {
+ public AbstractDeserializer buildAbstract() {
return new AbstractDeserializer(this, _beanDesc, _backRefProperties);
}
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 a10cc45..dcb1787 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java
@@ -2,21 +2,18 @@
import java.lang.reflect.Type;
import java.util.*;
-import java.util.concurrent.atomic.AtomicReference;
import com.fasterxml.jackson.annotation.ObjectIdGenerator;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import com.fasterxml.jackson.annotation.ObjectIdResolver;
+
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig;
import com.fasterxml.jackson.databind.deser.impl.*;
-import com.fasterxml.jackson.databind.deser.std.AtomicReferenceDeserializer;
import com.fasterxml.jackson.databind.deser.std.ThrowableDeserializer;
-import com.fasterxml.jackson.databind.ext.OptionalHandlerFactory;
import com.fasterxml.jackson.databind.introspect.*;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
-import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.databind.util.ArrayBuilders;
import com.fasterxml.jackson.databind.util.ClassUtil;
import com.fasterxml.jackson.databind.util.SimpleBeanPropertyDefinition;
@@ -89,28 +86,6 @@
/*
/**********************************************************
- /* Overrides for super-class methods used for finding
- /* custom deserializers
- /**********************************************************
- */
-
- // Note: NOT overriding, superclass has no matching method
- @SuppressWarnings("unchecked")
- protected JsonDeserializer<Object> _findCustomBeanDeserializer(JavaType type,
- DeserializationConfig config, BeanDescription beanDesc)
- throws JsonMappingException
- {
- for (Deserializers d : _factoryConfig.deserializers()) {
- JsonDeserializer<?> deser = d.findBeanDeserializer(type, config, beanDesc);
- if (deser != null) {
- return (JsonDeserializer<Object>) deser;
- }
- }
- return null;
- }
-
- /*
- /**********************************************************
/* DeserializerFactory API implementation
/**********************************************************
*/
@@ -191,40 +166,15 @@
// note: we do NOT check for custom deserializers here, caller has already
// done that
JsonDeserializer<?> deser = findDefaultDeserializer(ctxt, type, beanDesc);
+ // Also: better ensure these are post-processable?
if (deser != null) {
- return deser;
- }
-
- Class<?> cls = type.getRawClass();
- // [JACKSON-283]: AtomicReference is a rather special type...
- if (AtomicReference.class.isAssignableFrom(cls)) {
- // Must find parameterization
- TypeFactory tf = ctxt.getTypeFactory();
- JavaType[] params = tf.findTypeParameters(type, AtomicReference.class);
- JavaType referencedType;
- if (params == null || params.length < 1) { // untyped (raw)
- referencedType = TypeFactory.unknownType();
- } else {
- referencedType = params[0];
+ if (_factoryConfig.hasDeserializerModifiers()) {
+ for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) {
+ deser = mod.modifyDeserializer(ctxt.getConfig(), beanDesc, deser);
+ }
}
- TypeDeserializer valueTypeDeser = findTypeDeserializer(ctxt.getConfig(), referencedType);
- BeanDescription refdDesc = ctxt.getConfig().introspectClassAnnotations(referencedType);
- deser = findDeserializerFromAnnotation(ctxt, refdDesc.getClassInfo());
- return new AtomicReferenceDeserializer(referencedType, valueTypeDeser, deser);
}
- return findOptionalStdDeserializer(ctxt, type, beanDesc);
- }
-
- /**
- * Overridable method called after checking all other types.
- *
- * @since 2.2
- */
- protected JsonDeserializer<?> findOptionalStdDeserializer(DeserializationContext ctxt,
- JavaType type, BeanDescription beanDesc)
- throws JsonMappingException
- {
- return OptionalHandlerFactory.instance.findDeserializer(type, ctxt.getConfig(), beanDesc);
+ return deser;
}
protected JavaType materializeAbstractType(DeserializationContext ctxt,
@@ -487,6 +437,7 @@
{
final SettableBeanProperty[] creatorProps =
builder.getValueInstantiator().getFromObjectArguments(ctxt.getConfig());
+ final boolean isConcrete = !beanDesc.getType().isAbstract();
// Things specified as "ok to ignore"? [JACKSON-77]
AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
@@ -508,7 +459,7 @@
if (anySetter != null) {
builder.setAnySetter(constructAnySetter(ctxt, beanDesc, anySetter));
}
- // NOTE: we do NOT add @JsonIgnore'd properties into blocked ones if there's any setter
+ // 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) {
Collection<String> ignored2 = beanDesc.getIgnoredPropertyNames();
@@ -559,7 +510,9 @@
prop = constructSetterlessProperty(ctxt, beanDesc, propDef);
}
}
- if (propDef.hasConstructorParameter()) {
+ // 25-Sep-2014, tatu: No point in finding constructor parameters for abstract types
+ // (since they are never used anyway)
+ if (isConcrete && propDef.hasConstructorParameter()) {
/* [JACKSON-700] If property is passed via constructor parameter, we must
* handle things in special way. Not sure what is the most optimal way...
* for now, let's just call a (new) method in builder, which does nothing.
@@ -854,7 +807,7 @@
if (typeStr != null) {
throw new IllegalArgumentException("Can not deserialize Class "+type.getName()+" (of type "+typeStr+") as a Bean");
}
- return true;
+ return true;
}
/**
@@ -865,14 +818,12 @@
Class<?> type, Map<Class<?>,Boolean> ignoredTypes)
{
Boolean status = ignoredTypes.get(type);
- if (status == null) {
- BeanDescription desc = config.introspectClassAnnotations(type);
- status = config.getAnnotationIntrospector().isIgnorableType(desc.getClassInfo());
- // We default to 'false', ie. not ignorable
- if (status == null) {
- status = Boolean.FALSE;
- }
+ if (status != null) {
+ return status.booleanValue();
}
- return status;
+ 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();
}
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/DataFormatReaders.java b/src/main/java/com/fasterxml/jackson/databind/deser/DataFormatReaders.java
index 1cf00fe..7bd78fb 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/DataFormatReaders.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/DataFormatReaders.java
@@ -133,7 +133,7 @@
final int len = _readers.length;
ObjectReader[] r = new ObjectReader[len];
for (int i = 0; i < len; ++i) {
- r[i] = _readers[i].withType(type);
+ r[i] = _readers[i].forType(type);
}
return new DataFormatReaders(r, _optimalMatch, _minimalMatch, _maxInputLookahead);
}
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 6bc7579..ef11980 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/DefaultDeserializationContext.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/DefaultDeserializationContext.java
@@ -82,6 +82,7 @@
public ReadableObjectId findObjectId(Object id, ObjectIdGenerator<?> gen, ObjectIdResolver resolverType)
{
final ObjectIdGenerator.IdKey key = gen.key(id);
+
if (_objectIds == null) {
_objectIds = new LinkedHashMap<ObjectIdGenerator.IdKey,ReadableObjectId>();
} else {
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 7692bd0..3d94a21 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/DeserializerCache.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/DeserializerCache.java
@@ -32,15 +32,15 @@
/**
* We will also cache some dynamically constructed deserializers;
* specifically, ones that are expensive to construct.
- * This currently means bean and Enum deserializers; array, List and Map
- * deserializers will not be cached.
+ * This currently means bean and Enum deserializers; starting with
+ * 2.5, container deserializers will also be cached.
*<p>
* Given that we don't expect much concurrency for additions
* (should very quickly converge to zero after startup), let's
- * explicitly define a low concurrency setting.
+ * define a relatively low concurrency setting.
*/
final protected ConcurrentHashMap<JavaType, JsonDeserializer<Object>> _cachedDeserializers
- = new ConcurrentHashMap<JavaType, JsonDeserializer<Object>>(64, 0.75f, 2);
+ = new ConcurrentHashMap<JavaType, JsonDeserializer<Object>>(64, 0.75f, 4);
/**
* During deserializer construction process we may need to keep track of partially
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 bfe5efe..840641e 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/SettableAnyProperty.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/SettableAnyProperty.java
@@ -1,7 +1,6 @@
package com.fasterxml.jackson.databind.deser;
import java.io.IOException;
-import java.lang.reflect.*;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
@@ -27,14 +26,11 @@
* information. Retained to allow contextualization of any properties.
*/
protected final BeanProperty _property;
-
+
/**
- * Physical JDK object used for assigning properties.
- *<p>
- * NOTE: must be marked transient since it is not serializable,
- * in case these are to be serialized
+ * Annotated variant is needed for JDK serialization only
*/
- protected final transient Method _setter;
+ final protected AnnotatedMethod _setter;
protected final JavaType _type;
@@ -48,30 +44,12 @@
/**********************************************************
*/
- @Deprecated // since 2.3
public SettableAnyProperty(BeanProperty property, AnnotatedMethod setter, JavaType type,
- JsonDeserializer<Object> valueDeser) {
- this(property, setter, type, valueDeser, null);
- }
-
- public SettableAnyProperty(BeanProperty property, AnnotatedMethod setter, JavaType type,
- JsonDeserializer<Object> valueDeser, TypeDeserializer typeDeser)
- {
- this(property, setter.getAnnotated(), type, valueDeser, typeDeser);
- }
-
- @Deprecated // since 2.3
- public SettableAnyProperty(BeanProperty property, Method rawSetter, JavaType type,
- JsonDeserializer<Object> valueDeser) {
- this(property, rawSetter, type, valueDeser, null);
- }
-
- public SettableAnyProperty(BeanProperty property, Method rawSetter, JavaType type,
JsonDeserializer<Object> valueDeser, TypeDeserializer typeDeser)
{
_property = property;
+ _setter = setter;
_type = type;
- _setter = rawSetter;
_valueDeserializer = valueDeser;
_valueTypeDeserializer = typeDeser;
}
@@ -80,6 +58,18 @@
return new SettableAnyProperty(_property, _setter, _type,
deser, _valueTypeDeserializer);
}
+
+ /**
+ * Constructor used for JDK Serialization when reading persisted object
+ */
+ protected SettableAnyProperty(SettableAnyProperty src)
+ {
+ _property = src._property;
+ _setter = src._setter;
+ _type = src._type;
+ _valueDeserializer = src._valueDeserializer;
+ _valueTypeDeserializer = src._valueTypeDeserializer;
+ }
/*
/**********************************************************
@@ -87,13 +77,16 @@
/**********************************************************
*/
- // TODO (2.3): handle restoring of reference to any-setter method
-
-/*
+ /**
+ * Need to define this to verify that we retain actual Method reference
+ */
Object readResolve() {
- return new SettableAnyProperty(this, _annotated.getAnnotated());
+ // sanity check...
+ if (_setter == null || _setter.getAnnotated() == null) {
+ throw new IllegalArgumentException("Missing method (broken JDK (de)serialization?)");
+ }
+ return this;
}
- */
/*
/**********************************************************
@@ -148,7 +141,8 @@
public void set(Object instance, String propName, Object value) throws IOException
{
try {
- _setter.invoke(instance, propName, value);
+ // note: can not use 'setValue()' due to taking 2 args
+ _setter.getAnnotated().invoke(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 7b4ee60..be9879f 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/SettableBeanProperty.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/SettableBeanProperty.java
@@ -145,14 +145,6 @@
contextAnnotations, propDef.getMetadata());
}
- @Deprecated // since 2.2
- protected SettableBeanProperty(String propName, JavaType type, PropertyName wrapper,
- TypeDeserializer typeDeser, Annotations contextAnnotations)
- {
- this(new PropertyName(propName), type, wrapper, typeDeser, contextAnnotations,
- PropertyMetadata.STD_OPTIONAL);
- }
-
@Deprecated // since 2.3
protected SettableBeanProperty(String propName, JavaType type, PropertyName wrapper,
TypeDeserializer typeDeser, Annotations contextAnnotations,
@@ -509,7 +501,7 @@
* @since 2.0
*/
public abstract Object setAndReturn(Object instance, Object value)
- throws IOException;
+ throws IOException;
/**
* This method is needed by some specialized bean deserializers,
@@ -524,8 +516,7 @@
* this method should also not be called directly unless you really know
* what you are doing (and probably not even then).
*/
- public final Object deserialize(JsonParser jp, DeserializationContext ctxt)
- throws IOException, JsonProcessingException
+ public final Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException
{
JsonToken t = jp.getCurrentToken();
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 f0b7ee5..91edf2b 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
@@ -97,7 +97,7 @@
throws IOException, JsonProcessingException
{
// Let's delegate just in case we got a JSON Object (could error out, alternatively?)
- if (jp.getCurrentToken() != JsonToken.START_ARRAY) {
+ if (!jp.isExpectedStartArrayToken()) {
return finishBuild(ctxt, _deserializeFromNonArray(jp, ctxt));
}
if (!_vanillaProcessing) {
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 b8540e3..5aa1cac 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
@@ -90,7 +90,7 @@
throws IOException, JsonProcessingException
{
// Let's delegate just in case we got a JSON Object (could error out, alternatively?)
- if (jp.getCurrentToken() != JsonToken.START_ARRAY) {
+ if (!jp.isExpectedStartArrayToken()) {
return _deserializeFromNonArray(jp, ctxt);
}
if (!_vanillaProcessing) {
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 e47934e..5dd3d6c 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
@@ -55,7 +55,7 @@
*/
public boolean handleTypePropertyValue(JsonParser jp, DeserializationContext ctxt,
String propName, Object bean)
- throws IOException, JsonProcessingException
+ throws IOException
{
Integer I = _nameToPropertyIndex.get(propName);
if (I == null) {
@@ -89,8 +89,7 @@
* @return True, if the given property was properly handled
*/
public boolean handlePropertyValue(JsonParser jp, DeserializationContext ctxt,
- String propName, Object bean)
- throws IOException, JsonProcessingException
+ String propName, Object bean) throws IOException
{
Integer I = _nameToPropertyIndex.get(propName);
if (I == null) {
@@ -125,7 +124,7 @@
@SuppressWarnings("resource")
public Object complete(JsonParser jp, DeserializationContext ctxt, Object bean)
- throws IOException, JsonProcessingException
+ throws IOException
{
for (int i = 0, len = _properties.length; i < len; ++i) {
String typeId = _typeIds[i];
@@ -170,7 +169,7 @@
*/
public Object complete(JsonParser jp, DeserializationContext ctxt,
PropertyValueBuffer buffer, PropertyBasedCreator creator)
- throws IOException, JsonProcessingException
+ throws IOException
{
// first things first: deserialize all data buffered:
final int len = _properties.length;
@@ -214,8 +213,7 @@
@SuppressWarnings("resource")
protected final Object _deserialize(JsonParser jp, DeserializationContext ctxt,
- int index, String typeId)
- throws IOException, JsonProcessingException
+ int index, String typeId) throws IOException
{
TokenBuffer merged = new TokenBuffer(jp);
merged.writeStartArray();
@@ -233,8 +231,7 @@
@SuppressWarnings("resource")
protected final void _deserializeAndSet(JsonParser jp, DeserializationContext ctxt,
- Object bean, int index, String typeId)
- throws IOException, JsonProcessingException
+ Object bean, int index, String typeId) throws IOException
{
/* Ok: time to mix type id, value; and we will actually use "wrapper-array"
* style to ensure we can handle all kinds of JSON constructs.
@@ -246,7 +243,6 @@
p2.nextToken();
merged.copyCurrentStructure(p2);
merged.writeEndArray();
-
// needs to point to START_OBJECT (or whatever first token is)
p2 = merged.asParser(jp);
p2.nextToken();
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 e6e24ce..dbdea50 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
@@ -58,13 +58,13 @@
/**
* Constructor used for JDK Serialization when reading persisted object
*/
- protected FieldProperty(FieldProperty src, Field f)
+ protected FieldProperty(FieldProperty src)
{
super(src);
_annotated = src._annotated;
+ Field f = _annotated.getAnnotated();
if (f == null) {
- throw new IllegalArgumentException("No Field passed for property '"+src.getName()
- +"' (class "+src.getDeclaringClass().getName()+")");
+ throw new IllegalArgumentException("Missing field (broken JDK (de)serialization?)");
}
_field = f;
}
@@ -144,6 +144,6 @@
*/
Object readResolve() {
- return new FieldProperty(this, _annotated.getAnnotated());
+ return new FieldProperty(this);
}
}
\ No newline at end of file
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 8e5c243..76616ab 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
@@ -4,14 +4,10 @@
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.core.JsonToken;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JsonDeserializer;
-import com.fasterxml.jackson.databind.PropertyName;
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
-import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
+import com.fasterxml.jackson.databind.introspect.*;
import com.fasterxml.jackson.databind.util.ClassUtil;
/**
@@ -31,10 +27,17 @@
protected final SettableBeanProperty _delegate;
/**
- * Single-arg constructor we use for value instantiation.
+ * Constructor used when deserializing this property.
+ * Transient since there is no need to persist; only needed during
+ * construction of objects.
*/
- protected final Constructor<?> _creator;
+ final protected transient Constructor<?> _creator;
+ /**
+ * Serializable version of single-arg constructor we use for value instantiation.
+ */
+ protected AnnotatedConstructor _annotated;
+
public InnerClassProperty(SettableBeanProperty delegate,
Constructor<?> ctor)
{
@@ -43,6 +46,21 @@
_creator = ctor;
}
+ /**
+ * Constructor used with JDK Serialization; needed to handle transient
+ * Constructor, wrap/unwrap in/out-of Annotated variant.
+ */
+ protected InnerClassProperty(InnerClassProperty src, AnnotatedConstructor ann)
+ {
+ super(src);
+ _delegate = src._delegate;
+ _annotated = ann;
+ _creator = (_annotated == null) ? null : _annotated.getAnnotated();
+ if (_creator == null) {
+ throw new IllegalArgumentException("Missing constructor (broken JDK (de)serialization?)");
+ }
+ }
+
protected InnerClassProperty(InnerClassProperty src, JsonDeserializer<?> deser)
{
super(src, deser);
@@ -82,9 +100,8 @@
*/
@Override
- public void deserializeAndSet(JsonParser jp, DeserializationContext ctxt,
- Object bean)
- throws IOException, JsonProcessingException
+ public void deserializeAndSet(JsonParser jp, DeserializationContext ctxt, Object bean)
+ throws IOException
{
JsonToken t = jp.getCurrentToken();
Object value;
@@ -107,21 +124,37 @@
@Override
public Object deserializeSetAndReturn(JsonParser jp,
DeserializationContext ctxt, Object instance)
- throws IOException, JsonProcessingException
+ throws IOException
{
return setAndReturn(instance, deserialize(jp, ctxt));
}
@Override
- public final void set(Object instance, Object value) throws IOException
- {
+ public final void set(Object instance, Object value) throws IOException {
_delegate.set(instance, value);
}
@Override
- public Object setAndReturn(Object instance, Object value)
- throws IOException
- {
- return _delegate.setAndReturn(instance, value);
+ public Object setAndReturn(Object instance, Object value) throws IOException {
+ return _delegate.setAndReturn(instance, value);
+ }
+
+ /*
+ /**********************************************************
+ /* JDK serialization handling
+ /**********************************************************
+ */
+
+ // When reading things back,
+ Object readResolve() {
+ return new InnerClassProperty(this, _annotated);
+ }
+
+ Object writeReplace() {
+ // need to construct a fake instance to support serialization
+ if (_annotated != null) {
+ return this;
+ }
+ return new InnerClassProperty(this, new AnnotatedConstructor(_creator, null, null));
}
}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/ObjectIdReader.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ObjectIdReader.java
index 16dcc4c..bad83ba 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/ObjectIdReader.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ObjectIdReader.java
@@ -5,8 +5,9 @@
import com.fasterxml.jackson.annotation.ObjectIdGenerator;
import com.fasterxml.jackson.annotation.ObjectIdResolver;
import com.fasterxml.jackson.annotation.SimpleObjectIdResolver;
+
import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonProcessingException;
+
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
@@ -29,9 +30,6 @@
*/
public final ObjectIdGenerator<?> generator;
- /**
- *
- */
public final ObjectIdResolver resolver;
/**
@@ -66,13 +64,6 @@
this(t,propName, gen, deser, idProp, new SimpleObjectIdResolver());
}
- @Deprecated // since 2.3
- protected ObjectIdReader(JavaType t, String propName, ObjectIdGenerator<?> gen,
- JsonDeserializer<?> deser, SettableBeanProperty idProp)
- {
- this(t, new PropertyName(propName), gen, deser, idProp);
- }
-
/**
* Factory method called by {@link com.fasterxml.jackson.databind.ser.std.BeanSerializerBase}
* with the initial information based on standard settings for the type
@@ -92,14 +83,6 @@
{
return construct(idType, propName, generator, deser, idProp, new SimpleObjectIdResolver());
}
-
- @Deprecated // since 2.3
- public static ObjectIdReader construct(JavaType idType, String propName,
- ObjectIdGenerator<?> generator, JsonDeserializer<?> deser,
- SettableBeanProperty idProp)
- {
- return construct(idType, new PropertyName(propName), generator, deser, idProp);
- }
/*
/**********************************************************
@@ -121,9 +104,7 @@
*
* @since 2.3
*/
- public Object readObjectReference(JsonParser jp, DeserializationContext ctxt)
- throws IOException, JsonProcessingException
- {
+ public Object readObjectReference(JsonParser jp, DeserializationContext ctxt) throws IOException {
return _deserializer.deserialize(jp, ctxt);
}
}
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 3dcec66..ac04b96 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
@@ -4,11 +4,7 @@
import java.lang.annotation.Annotation;
import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JsonDeserializer;
-import com.fasterxml.jackson.databind.JsonMappingException;
-import com.fasterxml.jackson.databind.PropertyName;
+import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
import com.fasterxml.jackson.databind.deser.UnresolvedForwardReference;
import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId.Referring;
@@ -16,8 +12,9 @@
import com.fasterxml.jackson.databind.introspect.ObjectIdInfo;
public class ObjectIdReferenceProperty extends SettableBeanProperty {
- private static final long serialVersionUID = 8465266677345565407L;
- private SettableBeanProperty _forward;
+ private static final long serialVersionUID = 1L;
+
+ private final SettableBeanProperty _forward;
public ObjectIdReferenceProperty(SettableBeanProperty forward, ObjectIdInfo objectIdInfo)
{
@@ -61,20 +58,18 @@
}
@Override
- public void deserializeAndSet(JsonParser jp, DeserializationContext ctxt, Object instance)
- throws IOException, JsonProcessingException
- {
+ public void deserializeAndSet(JsonParser jp, DeserializationContext ctxt, Object instance) throws IOException {
deserializeSetAndReturn(jp, ctxt, instance);
}
@Override
public Object deserializeSetAndReturn(JsonParser jp, DeserializationContext ctxt, Object instance)
- throws IOException, JsonProcessingException
+ throws IOException
{
- boolean usingIdentityInfo = (_objectIdInfo != null) || (_valueDeserializer.getObjectIdReader() != null);
try {
return setAndReturn(instance, deserialize(jp, ctxt));
} catch (UnresolvedForwardReference reference) {
+ boolean usingIdentityInfo = (_objectIdInfo != null) || (_valueDeserializer.getObjectIdReader() != null);
if (!usingIdentityInfo) {
throw JsonMappingException.from(jp, "Unresolved forward reference but no identity info.", reference);
}
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 d899d44..7e06b60 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
@@ -108,8 +108,7 @@
/**
* Helper method called to handle Object Id value collected earlier, if any
*/
- public Object handleIdValue(final DeserializationContext ctxt, Object bean)
- throws IOException
+ public Object handleIdValue(final DeserializationContext ctxt, Object bean) throws IOException
{
if (_objectIdReader != null) {
if (_idValue != null) {
@@ -122,6 +121,8 @@
}
} else {
// TODO: is this an error case?
+ throw ctxt.mappingException("No _idValue when handleIdValue called, on instance of "
+ +bean.getClass().getName());
}
}
return bean;
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 1d127fe..7b250db 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
@@ -99,7 +99,7 @@
// For [#501] fix we need to implement this but:
if (_valueTypeDeserializer != null) {
- throw new JsonMappingException("Problem deserializing 'setterless' property: no way to handle typed deser with setterless yet");
+ throw new JsonMappingException("Problem deserializing 'setterless' property (\""+getName()+"\"): no way to handle typed deser with setterless yet");
// return _valueDeserializer.deserializeWithType(jp, ctxt, _valueTypeDeserializer);
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/TypeWrappedDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/TypeWrappedDeserializer.java
index 1a29481..744bcaa 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/TypeWrappedDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/TypeWrappedDeserializer.java
@@ -3,9 +3,7 @@
import java.io.IOException;
import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
/**
@@ -18,15 +16,19 @@
*/
public final class TypeWrappedDeserializer
extends JsonDeserializer<Object>
+ implements java.io.Serializable // since 2.5
{
- final TypeDeserializer _typeDeserializer;
- final JsonDeserializer<Object> _deserializer;
+ private static final long serialVersionUID = 1L;
- public TypeWrappedDeserializer(TypeDeserializer typeDeser, JsonDeserializer<Object> deser)
+ final protected TypeDeserializer _typeDeserializer;
+ final protected JsonDeserializer<Object> _deserializer;
+
+ @SuppressWarnings("unchecked")
+ public TypeWrappedDeserializer(TypeDeserializer typeDeser, JsonDeserializer<?> deser)
{
super();
_typeDeserializer = typeDeser;
- _deserializer = deser;
+ _deserializer = (JsonDeserializer<Object>) deser;
}
@Override
@@ -35,16 +37,14 @@
}
@Override
- public Object deserialize(JsonParser jp, DeserializationContext ctxt)
- throws IOException, JsonProcessingException
+ public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException
{
return _deserializer.deserializeWithType(jp, ctxt, _typeDeserializer);
}
@Override
public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
- TypeDeserializer typeDeserializer)
- throws IOException, JsonProcessingException
+ TypeDeserializer typeDeserializer) throws IOException
{
// should never happen? (if it can, could call on that object)
throw new IllegalStateException("Type-wrapped deserializer's deserializeWithType should never get called");
@@ -52,8 +52,7 @@
@Override
public Object deserialize(JsonParser jp, DeserializationContext ctxt,
- Object intoValue)
- throws IOException, JsonProcessingException
+ Object intoValue) throws IOException
{
/* 01-Mar-2013, tatu: Hmmh. Tough call as to what to do... need
* to delegate, but will this work reliably? Let's just hope so:
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 f61817d..6d619f1 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
@@ -27,7 +27,7 @@
extends ContainerDeserializerBase<Collection<Object>>
implements ContextualDeserializer
{
- private static final long serialVersionUID = 3917273725180652224L;
+ private static final long serialVersionUID = -1L; // since 2.5
// // Configuration
@@ -117,7 +117,13 @@
(JsonDeserializer<Object>) vd, vtd,
_valueInstantiator, (JsonDeserializer<Object>) dd);
}
-
+
+ // Important: do NOT cache if polymorphic values
+ @Override // since 2.5
+ public boolean isCachable() {
+ return (_valueTypeDeserializer == null);
+ }
+
/*
/**********************************************************
/* Validation, post-processing (ResolvableDeserializer)
@@ -187,7 +193,7 @@
@SuppressWarnings("unchecked")
@Override
public Collection<Object> deserialize(JsonParser jp, DeserializationContext ctxt)
- throws IOException, JsonProcessingException
+ throws IOException
{
if (_delegateDeserializer != null) {
return (Collection<Object>) _valueInstantiator.createUsingDelegate(ctxt,
@@ -209,7 +215,7 @@
@Override
public Collection<Object> deserialize(JsonParser jp, DeserializationContext ctxt,
Collection<Object> result)
- throws IOException, JsonProcessingException
+ throws IOException
{
// Ok: must point to START_ARRAY (or equivalent)
if (!jp.isExpectedStartArrayToken()) {
@@ -255,7 +261,7 @@
@Override
public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
TypeDeserializer typeDeserializer)
- throws IOException, JsonProcessingException
+ throws IOException
{
// In future could check current token... for now this should be enough:
return typeDeserializer.deserializeTypedFromArray(jp, ctxt);
@@ -268,7 +274,7 @@
*/
protected final Collection<Object> handleNonArray(JsonParser jp, DeserializationContext ctxt,
Collection<Object> result)
- throws IOException, JsonProcessingException
+ throws IOException
{
// [JACKSON-526]: implicit arrays from single values?
if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)) {
@@ -359,7 +365,7 @@
private final CollectionReferringAccumulator _parent;
public final List<Object> next = new ArrayList<Object>();
- private CollectionReferring(CollectionReferringAccumulator parent,
+ CollectionReferring(CollectionReferringAccumulator parent,
UnresolvedForwardReference reference, Class<?> contentType)
{
super(reference, contentType);
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/ContainerDeserializerBase.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/ContainerDeserializerBase.java
index 6f86a26..fea065b 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/ContainerDeserializerBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/ContainerDeserializerBase.java
@@ -1,7 +1,11 @@
package com.fasterxml.jackson.databind.deser.std;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
/**
@@ -57,4 +61,30 @@
* Accesor for deserializer use for deserializing content values.
*/
public abstract JsonDeserializer<Object> getContentDeserializer();
+
+ /*
+ /**********************************************************
+ /* Shared methods for sub-classes
+ /**********************************************************
+ */
+
+ /**
+ * Helper method called by various Map(-like) deserializers.
+ */
+ protected void wrapAndThrow(Throwable t, Object ref, String key) throws IOException
+ {
+ // to handle StackOverflow:
+ while (t instanceof InvocationTargetException && t.getCause() != null) {
+ t = t.getCause();
+ }
+ // Errors and "plain" IOExceptions to be passed as is
+ if (t instanceof Error) {
+ throw (Error) t;
+ }
+ // ... except for mapping exceptions
+ if (t instanceof IOException && !(t instanceof JsonMappingException)) {
+ throw (IOException) t;
+ }
+ throw JsonMappingException.wrapWithPath(t, ref, key);
+ }
}
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 e401f5e..14881ba 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
@@ -4,9 +4,10 @@
import java.lang.reflect.Method;
import com.fasterxml.jackson.core.*;
-
import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
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.EnumResolver;
@@ -17,7 +18,7 @@
public class EnumDeserializer
extends StdScalarDeserializer<Enum<?>>
{
- private static final long serialVersionUID = -5893263645879532318L;
+ private static final long serialVersionUID = 1L;
protected final EnumResolver<?> _resolver;
@@ -39,16 +40,6 @@
{
// note: caller has verified there's just one arg; but we must verify its type
Class<?> paramClass = factory.getRawParameterType(0);
- if (paramClass == String.class) {
- paramClass = null;
- } else if (paramClass == Integer.TYPE || paramClass == Integer.class) {
- paramClass = Integer.class;
- } else if (paramClass == Long.TYPE || paramClass == Long.class) {
- paramClass = Long.class;
- } else {
- throw new IllegalArgumentException("Parameter #0 type for factory method ("+factory
- +") not suitable, must be java.lang.String or int/Integer/long/Long");
- }
if (config.canOverrideAccessModifiers()) {
ClassUtil.checkAndFixAccess(factory.getMember());
}
@@ -158,48 +149,71 @@
* for locating Enum values by String id.
*/
protected static class FactoryBasedDeserializer
- extends StdScalarDeserializer<Object>
+ extends StdDeserializer<Object>
+ implements ContextualDeserializer
{
- private static final long serialVersionUID = -7775129435872564122L;
+ private static final long serialVersionUID = 1;
- protected final Class<?> _enumClass;
// 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(Enum.class);
- _enumClass = cls;
+ 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 Object deserialize(JsonParser jp, DeserializationContext ctxt)
- throws IOException, JsonProcessingException
+ public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
+ BeanProperty property)
+ throws JsonMappingException
{
- // couple of accepted types...
+ if ((_deser == null) && (_inputType != String.class)) {
+ return new FactoryBasedDeserializer(this,
+ ctxt.findContextualValueDeserializer(ctxt.constructType(_inputType), property));
+ }
+ return this;
+ }
+
+ @Override
+ public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException
+ {
Object value;
- if (_inputType == null) {
- value = jp.getText();
- } else if (_inputType == Integer.class) {
- value = Integer.valueOf(jp.getValueAsInt());
- } else if (_inputType == Long.class) {
- value = Long.valueOf(jp.getValueAsLong());
+ if (_deser != null) {
+ value = _deser.deserialize(jp, ctxt);
} else {
- throw ctxt.mappingException(_enumClass);
+ value = jp.getValueAsString();
}
try {
- return _factory.invoke(_enumClass, value);
+ return _factory.invoke(_valueClass, value);
} catch (Exception e) {
Throwable t = ClassUtil.getRootCause(e);
if (t instanceof IOException) {
throw (IOException) t;
}
- throw ctxt.instantiationException(_enumClass, t);
+ throw ctxt.instantiationException(_valueClass, t);
}
}
+
+ @Override
+ public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException {
+ if (_deser == null) { // String never has type info
+ return deserialize(jp, ctxt);
+ }
+ return typeDeserializer.deserializeTypedFromAny(jp, 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 552af52..d25eeb4 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
@@ -4,10 +4,8 @@
import java.util.*;
import com.fasterxml.jackson.core.*;
-
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
-import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
/**
@@ -15,21 +13,19 @@
* <p>
* Note: casting within this class is all messed up -- just could not figure out a way
* to properly deal with recursive definition of "EnumMap<K extends Enum<K>, V>
- *
- * @author tsaloranta
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public class EnumMapDeserializer
- extends StdDeserializer<EnumMap<?,?>>
+ extends ContainerDeserializerBase<EnumMap<?,?>>
implements ContextualDeserializer
{
- private static final long serialVersionUID = 4564890642370311174L;
+ private static final long serialVersionUID = 1;
protected final JavaType _mapType;
protected final Class<?> _enumClass;
- protected JsonDeserializer<Enum<?>> _keyDeserializer;
+ protected KeyDeserializer _keyDeserializer;
protected JsonDeserializer<Object> _valueDeserializer;
@@ -45,17 +41,17 @@
/**********************************************************
*/
- public EnumMapDeserializer(JavaType mapType, JsonDeserializer<?> keyDeserializer, JsonDeserializer<?> valueDeser, TypeDeserializer valueTypeDeser)
+ public EnumMapDeserializer(JavaType mapType, KeyDeserializer keyDeserializer, JsonDeserializer<?> valueDeser, TypeDeserializer valueTypeDeser)
{
- super(EnumMap.class);
+ super(mapType);
_mapType = mapType;
_enumClass = mapType.getKeyType().getRawClass();
- _keyDeserializer = (JsonDeserializer<Enum<?>>) keyDeserializer;
+ _keyDeserializer = keyDeserializer;
_valueDeserializer = (JsonDeserializer<Object>) valueDeser;
_valueTypeDeserializer = valueTypeDeser;
}
- public EnumMapDeserializer withResolved(JsonDeserializer<?> keyDeserializer, JsonDeserializer<?> valueDeserializer, TypeDeserializer valueTypeDeser)
+ public EnumMapDeserializer withResolved(KeyDeserializer keyDeserializer, JsonDeserializer<?> valueDeserializer, TypeDeserializer valueTypeDeser)
{
if ((keyDeserializer == _keyDeserializer) && (valueDeserializer == _valueDeserializer) && (valueTypeDeser == _valueTypeDeserializer)) {
return this;
@@ -73,9 +69,9 @@
// note: instead of finding key deserializer, with enums we actually
// work with regular deserializers (less code duplication; but not
// quite as clean as it ought to be)
- JsonDeserializer<?> kd = _keyDeserializer;
+ KeyDeserializer kd = _keyDeserializer;
if (kd == null) {
- kd = ctxt.findContextualValueDeserializer(_mapType.getKeyType(), property);
+ kd = ctxt.findKeyDeserializer(_mapType.getKeyType(), property);
}
JsonDeserializer<?> vd = _valueDeserializer;
if (vd == null) {
@@ -95,39 +91,56 @@
* let's cache instances by default.
*/
@Override
- public boolean isCachable() { return true; }
-
+ public boolean isCachable() {
+ // Important: do NOT cache if polymorphic values
+ return (_valueTypeDeserializer == null);
+ }
+
+ /*
+ /**********************************************************
+ /* ContainerDeserializerBase API
+ /**********************************************************
+ */
+
+ @Override
+ public JavaType getContentType() {
+ return _mapType.getContentType();
+ }
+
+ @Override
+ public JsonDeserializer<Object> getContentDeserializer() {
+ return _valueDeserializer;
+ }
+
/*
/**********************************************************
/* Actual deserialization
/**********************************************************
*/
-
+
@Override
- public EnumMap<?,?> deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException
+ public EnumMap<?,?> deserialize(JsonParser jp, DeserializationContext ctxt)
+ throws IOException
{
// Ok: must point to START_OBJECT
if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
- throw ctxt.mappingException(EnumMap.class);
+ return _deserializeFromEmpty(jp, ctxt);
}
EnumMap result = constructMap();
final JsonDeserializer<Object> valueDes = _valueDeserializer;
final TypeDeserializer typeDeser = _valueTypeDeserializer;
- while ((jp.nextToken()) != JsonToken.END_OBJECT) {
- Enum<?> key = _keyDeserializer.deserialize(jp, ctxt);
+ while ((jp.nextToken()) == JsonToken.FIELD_NAME) {
+ String keyName = jp.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)) {
- String value = null;
- try { // bit ugly, but will have to do; works with usual scalars
- if (jp.hasCurrentToken()) {
- value = jp.getText();
- }
- } catch (Exception e) { }
- throw ctxt.weirdStringException(value, _enumClass, "value not one of declared Enum instance names");
+ throw ctxt.weirdStringException(keyName, _enumClass, "value not one of declared Enum instance names for "
+ +_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 then.
+ * just skip the entry then. But we must skip the value as well, if so.
*/
jp.nextToken();
jp.skipChildren();
@@ -139,13 +152,18 @@
* not handle them (and maybe fail or return bogus data)
*/
Object value;
-
- if (t == JsonToken.VALUE_NULL) {
- value = valueDes.getNullValue();
- } else if (typeDeser == null) {
- value = valueDes.deserialize(jp, ctxt);
- } else {
- value = valueDes.deserializeWithType(jp, ctxt, typeDeser);
+
+ try {
+ if (t == JsonToken.VALUE_NULL) {
+ value = valueDes.getNullValue();
+ } else if (typeDeser == null) {
+ value = valueDes.deserialize(jp, ctxt);
+ } else {
+ value = valueDes.deserializeWithType(jp, ctxt, typeDeser);
+ }
+ } catch (Exception e) {
+ wrapAndThrow(e, result, keyName);
+ return null;
}
result.put(key, value);
}
@@ -153,14 +171,15 @@
}
@Override
- public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer)
+ public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer)
throws IOException, JsonProcessingException
{
// In future could check current token... for now this should be enough:
return typeDeserializer.deserializeTypedFromObject(jp, ctxt);
}
- private EnumMap<?,?> constructMap() {
+ protected EnumMap<?,?> constructMap() {
return new EnumMap(_enumClass);
}
}
+
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 a28e035..3c7aae5 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
@@ -19,7 +19,7 @@
extends StdDeserializer<EnumSet<?>>
implements ContextualDeserializer
{
- private static final long serialVersionUID = 3479455075597887177L;
+ private static final long serialVersionUID = 1L; // since 2.5
protected final JavaType _enumType;
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 f676bf5..3f83a47 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
@@ -132,7 +132,7 @@
public BaseNodeDeserializer(Class<T> vc) {
super(vc);
}
-
+
@Override
public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
TypeDeserializer typeDeserializer)
@@ -144,16 +144,23 @@
return typeDeserializer.deserializeTypedFromAny(jp, ctxt);
}
+ /* 07-Nov-2014, tatu: When investigating [databind#604], realized that it makes
+ * sense to also mark this is cachable, since lookup not exactly free, and
+ * since it's not uncommon to "read anything"
+ */
+ @Override
+ public boolean isCachable() { return true; }
+
/*
/**********************************************************
/* Overridable methods
/**********************************************************
*/
-
+
protected void _reportProblem(JsonParser jp, String msg) throws JsonMappingException {
throw new JsonMappingException(msg, jp.getTokenLocation());
}
-
+
/**
*
* @deprecated Since 2.3, use the overloaded variant
@@ -193,16 +200,15 @@
// Backwards-compatibility; call in case it's overloaded
_handleDuplicateField(fieldName, objectNode, oldValue, newValue);
}
-
+
/*
/**********************************************************
/* Helper methods
/**********************************************************
*/
-
+
protected final ObjectNode deserializeObject(JsonParser jp, DeserializationContext ctxt,
- final JsonNodeFactory nodeFactory)
- throws IOException, JsonProcessingException
+ final JsonNodeFactory nodeFactory) throws IOException
{
ObjectNode node = nodeFactory.objectNode();
JsonToken t = jp.getCurrentToken();
@@ -246,10 +252,9 @@
}
return node;
}
-
+
protected final ArrayNode deserializeArray(JsonParser jp, DeserializationContext ctxt,
- final JsonNodeFactory nodeFactory)
- throws IOException, JsonProcessingException
+ final JsonNodeFactory nodeFactory) throws IOException
{
ArrayNode node = nodeFactory.arrayNode();
while (true) {
@@ -287,10 +292,9 @@
}
}
}
-
+
protected final JsonNode deserializeAny(JsonParser jp, DeserializationContext ctxt,
- final JsonNodeFactory nodeFactory)
- throws IOException
+ final JsonNodeFactory nodeFactory) throws IOException
{
switch (jp.getCurrentTokenId()) {
case JsonTokenId.ID_START_OBJECT:
@@ -326,8 +330,7 @@
}
protected final JsonNode _fromInt(JsonParser jp, DeserializationContext ctxt,
- JsonNodeFactory nodeFactory)
- throws IOException
+ JsonNodeFactory nodeFactory) throws IOException
{
JsonParser.NumberType nt = jp.getNumberType();
if (nt == JsonParser.NumberType.BIG_INTEGER
@@ -341,8 +344,7 @@
}
protected final JsonNode _fromFloat(JsonParser jp, DeserializationContext ctxt,
- final JsonNodeFactory nodeFactory)
- throws IOException
+ final JsonNodeFactory nodeFactory) throws IOException
{
JsonParser.NumberType nt = jp.getNumberType();
if (nt == JsonParser.NumberType.BIG_DECIMAL
@@ -353,8 +355,7 @@
}
protected final JsonNode _fromEmbedded(JsonParser jp, DeserializationContext ctxt,
- JsonNodeFactory nodeFactory)
- throws IOException
+ JsonNodeFactory nodeFactory) throws IOException
{
// [JACKSON-796]
Object ob = jp.getEmbeddedObject();
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 e6a9b1e..4b1916a 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
@@ -1,7 +1,6 @@
package com.fasterxml.jackson.databind.deser.std;
import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
import java.util.*;
import com.fasterxml.jackson.core.*;
@@ -28,7 +27,7 @@
extends ContainerDeserializerBase<Map<Object,Object>>
implements ContextualDeserializer, ResolvableDeserializer
{
- private static final long serialVersionUID = -3378654289961736240L;
+ private static final long serialVersionUID = 1L;
// // Configuration: typing, deserializers
@@ -259,7 +258,7 @@
}
return withResolved(kd, vtd, vd, ignored);
}
-
+
/*
/**********************************************************
/* ContainerDeserializerBase API
@@ -322,7 +321,8 @@
if (t == JsonToken.VALUE_STRING) {
return (Map<Object,Object>) _valueInstantiator.createFromString(ctxt, jp.getText());
}
- throw ctxt.mappingException(getMapClass());
+ // slightly redundant (since String was passed above), but
+ return _deserializeFromEmpty(jp, ctxt);
}
final Map<Object,Object> result = (Map<Object,Object>) _valueInstantiator.createUsingDefault(ctxt);
if (_standardStringKey) {
@@ -482,7 +482,7 @@
}
}
}
-
+
@SuppressWarnings("unchecked")
public Map<Object,Object> _deserializeUsingCreator(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
@@ -556,24 +556,6 @@
protected void wrapAndThrow(Throwable t, Object ref) throws IOException {
wrapAndThrow(t, ref, null);
}
-
- // note: copied from BeanDeserializer; should try to share somehow...
- protected void wrapAndThrow(Throwable t, Object ref, String key) throws IOException
- {
- // to handle StackOverflow:
- while (t instanceof InvocationTargetException && t.getCause() != null) {
- t = t.getCause();
- }
- // Errors and "plain" IOExceptions to be passed as is
- if (t instanceof Error) {
- throw (Error) t;
- }
- // ... except for mapping exceptions
- if (t instanceof IOException && !(t instanceof JsonMappingException)) {
- throw (IOException) t;
- }
- throw JsonMappingException.wrapWithPath(t, ref, key);
- }
private void handleUnresolvedReference(JsonParser jp, MapReferringAccumulator accumulator,
Object key, UnresolvedForwardReference reference)
@@ -644,13 +626,13 @@
* object associated with {@link #_id} comes before the values in
* {@link _next}.
*/
- private final static class MapReferring extends Referring {
+ final static class MapReferring extends Referring {
private final MapReferringAccumulator _parent;
public final Map<Object, Object> next = new LinkedHashMap<Object, Object>();
public final Object key;
- protected MapReferring(MapReferringAccumulator parent, UnresolvedForwardReference ref,
+ MapReferring(MapReferringAccumulator parent, UnresolvedForwardReference ref,
Class<?> valueType, Object key)
{
super(ref, valueType);
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
new file mode 100644
index 0000000..99b9d9c
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/MapEntryDeserializer.java
@@ -0,0 +1,246 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import java.io.IOException;
+import java.util.*;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
+import com.fasterxml.jackson.databind.deser.*;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+
+/**
+ * Basic serializer that can take JSON "Object" structure and
+ * construct a {@link java.util.Map} instance, with typed contents.
+ *<p>
+ * Note: for untyped content (one indicated by passing Object.class
+ * as the type), {@link UntypedObjectDeserializer} is used instead.
+ * It can also construct {@link java.util.Map}s, but not with specific
+ * POJO types, only other containers and primitives/wrappers.
+ */
+@JacksonStdImpl
+public class MapEntryDeserializer
+ extends ContainerDeserializerBase<Map.Entry<Object,Object>>
+ implements ContextualDeserializer
+{
+ private static final long serialVersionUID = 1;
+
+ // // Configuration: typing, deserializers
+
+ protected final JavaType _type;
+
+ /**
+ * Key deserializer to use; either passed via constructor
+ * (when indicated by annotations), or resolved when
+ * {@link #createContextual} is called;
+ */
+ protected final KeyDeserializer _keyDeserializer;
+
+ /**
+ * Value deserializer.
+ */
+ protected final JsonDeserializer<Object> _valueDeserializer;
+
+ /**
+ * If value instances have polymorphic type information, this
+ * is the type deserializer that can handle it
+ */
+ protected final TypeDeserializer _valueTypeDeserializer;
+
+ /*
+ /**********************************************************
+ /* Life-cycle
+ /**********************************************************
+ */
+
+ public MapEntryDeserializer(JavaType type,
+ KeyDeserializer keyDeser, JsonDeserializer<Object> valueDeser,
+ TypeDeserializer valueTypeDeser)
+ {
+ super(type);
+ if (type.containedTypeCount() != 2) { // sanity check
+ throw new IllegalArgumentException("Missing generic type information for "+type);
+ }
+ _type = type;
+ _keyDeserializer = keyDeser;
+ _valueDeserializer = valueDeser;
+ _valueTypeDeserializer = valueTypeDeser;
+ }
+
+ /**
+ * Copy-constructor that can be used by sub-classes to allow
+ * copy-on-write styling copying of settings of an existing instance.
+ */
+ protected MapEntryDeserializer(MapEntryDeserializer src)
+ {
+ super(src._type);
+ _type = src._type;
+ _keyDeserializer = src._keyDeserializer;
+ _valueDeserializer = src._valueDeserializer;
+ _valueTypeDeserializer = src._valueTypeDeserializer;
+ }
+
+ protected MapEntryDeserializer(MapEntryDeserializer src,
+ KeyDeserializer keyDeser, JsonDeserializer<Object> valueDeser,
+ TypeDeserializer valueTypeDeser)
+ {
+ super(src._type);
+ _type = src._type;
+ _keyDeserializer = keyDeser;
+ _valueDeserializer = valueDeser;
+ _valueTypeDeserializer = valueTypeDeser;
+ }
+
+ /**
+ * Fluent factory method used to create a copy with slightly
+ * different settings. When sub-classing, MUST be overridden.
+ */
+ @SuppressWarnings("unchecked")
+ protected MapEntryDeserializer withResolved(KeyDeserializer keyDeser,
+ TypeDeserializer valueTypeDeser, JsonDeserializer<?> valueDeser)
+ {
+
+ if ((_keyDeserializer == keyDeser) && (_valueDeserializer == valueDeser)
+ && (_valueTypeDeserializer == valueTypeDeser)) {
+ return this;
+ }
+ return new MapEntryDeserializer(this,
+ keyDeser, (JsonDeserializer<Object>) valueDeser, valueTypeDeser);
+ }
+
+ /*
+ /**********************************************************
+ /* Validation, post-processing (ResolvableDeserializer)
+ /**********************************************************
+ */
+
+ /**
+ * Method called to finalize setup of this deserializer,
+ * when it is known for which property deserializer is needed for.
+ */
+ @Override
+ public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
+ BeanProperty property) throws JsonMappingException
+ {
+ KeyDeserializer kd = _keyDeserializer;
+ if (kd == null) {
+ kd = ctxt.findKeyDeserializer(_type.containedType(0), property);
+ } else {
+ if (kd instanceof ContextualKeyDeserializer) {
+ kd = ((ContextualKeyDeserializer) kd).createContextual(ctxt, property);
+ }
+ }
+ JsonDeserializer<?> vd = _valueDeserializer;
+ vd = findConvertingContentDeserializer(ctxt, property, vd);
+ if (vd == null) {
+ vd = ctxt.findContextualValueDeserializer(_type.containedType(1), property);
+ } else { // if directly assigned, probably not yet contextual, so:
+ vd = ctxt.handleSecondaryContextualization(vd, property);
+ }
+ TypeDeserializer vtd = _valueTypeDeserializer;
+ if (vtd != null) {
+ vtd = vtd.forProperty(property);
+ }
+ return withResolved(kd, vtd, vd);
+ }
+
+ /*
+ /**********************************************************
+ /* ContainerDeserializerBase API
+ /**********************************************************
+ */
+
+ @Override
+ public JavaType getContentType() {
+ return _type.containedType(1);
+ }
+
+ @Override
+ public JsonDeserializer<Object> getContentDeserializer() {
+ return _valueDeserializer;
+ }
+
+ /*
+ /**********************************************************
+ /* JsonDeserializer API
+ /**********************************************************
+ */
+
+ @Override
+ public Map.Entry<Object,Object> deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException
+ {
+ // Ok: must point to START_OBJECT, FIELD_NAME or END_OBJECT
+ JsonToken t = jp.getCurrentToken();
+ if (t != JsonToken.START_OBJECT && t != JsonToken.FIELD_NAME && t != JsonToken.END_OBJECT) {
+ // [JACKSON-620] (empty) String may be ok however:
+ // slightly redundant (since String was passed above), but
+ return _deserializeFromEmpty(jp, ctxt);
+ }
+ if (t == JsonToken.START_OBJECT) {
+ t = jp.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");
+ }
+ throw ctxt.mappingException(handledType(), t);
+ }
+
+ final KeyDeserializer keyDes = _keyDeserializer;
+ final JsonDeserializer<Object> valueDes = _valueDeserializer;
+ final TypeDeserializer typeDeser = _valueTypeDeserializer;
+
+ final String keyStr = jp.getCurrentName();
+ Object key = keyDes.deserializeKey(keyStr, ctxt);
+ Object value = null;
+ // And then the value...
+ t = jp.nextToken();
+ try {
+ // Note: must handle null explicitly here; value deserializers won't
+ if (t == JsonToken.VALUE_NULL) {
+ value = valueDes.getNullValue();
+ } else if (typeDeser == null) {
+ value = valueDes.deserialize(jp, ctxt);
+ } else {
+ value = valueDes.deserializeWithType(jp, ctxt, typeDeser);
+ }
+ } catch (Exception e) {
+ wrapAndThrow(e, Map.Entry.class, keyStr);
+ }
+
+ // Close, but also verify that we reached the END_OBJECT
+ t = jp.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()+"')");
+ }
+ // how would this occur?
+ throw ctxt.mappingException("Problem binding JSON into Map.Entry: unexpected content after JSON Object entry: "+t);
+ }
+ return new AbstractMap.SimpleEntry<Object,Object>(key, value);
+ }
+
+ @Override
+ public Map.Entry<Object,Object> deserialize(JsonParser jp, 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,
+ TypeDeserializer typeDeserializer)
+ throws IOException, JsonProcessingException
+ {
+ // In future could check current token... for now this should be enough:
+ return typeDeserializer.deserializeTypedFromObject(jp, ctxt);
+ }
+
+ /*
+ /**********************************************************
+ /* Other public accessors
+ /**********************************************************
+ */
+
+ @Override public JavaType getValueType() { return _type; }
+}
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 f7f4d3f..c644e5f 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
@@ -148,8 +148,8 @@
{
private static final long serialVersionUID = 1L;
- private final static BooleanDeserializer primitiveInstance = new BooleanDeserializer(Boolean.class, Boolean.FALSE);
- private final static BooleanDeserializer wrapperInstance = new BooleanDeserializer(Boolean.TYPE, null);
+ final static BooleanDeserializer primitiveInstance = new BooleanDeserializer(Boolean.class, Boolean.FALSE);
+ final static BooleanDeserializer wrapperInstance = new BooleanDeserializer(Boolean.TYPE, null);
public BooleanDeserializer(Class<Boolean> cls, Boolean nvl)
{
@@ -180,8 +180,8 @@
{
private static final long serialVersionUID = 1L;
- private final static ByteDeserializer primitiveInstance = new ByteDeserializer(Byte.TYPE, (byte) 0);
- private final static ByteDeserializer wrapperInstance = new ByteDeserializer(Byte.class, null);
+ final static ByteDeserializer primitiveInstance = new ByteDeserializer(Byte.TYPE, (byte) 0);
+ final static ByteDeserializer wrapperInstance = new ByteDeserializer(Byte.class, null);
public ByteDeserializer(Class<Byte> cls, Byte nvl)
{
@@ -202,8 +202,8 @@
{
private static final long serialVersionUID = 1L;
- private final static ShortDeserializer primitiveInstance = new ShortDeserializer(Short.class, Short.valueOf((short)0));
- private final static ShortDeserializer wrapperInstance = new ShortDeserializer(Short.TYPE, null);
+ final static ShortDeserializer primitiveInstance = new ShortDeserializer(Short.class, Short.valueOf((short)0));
+ final static ShortDeserializer wrapperInstance = new ShortDeserializer(Short.TYPE, null);
public ShortDeserializer(Class<Short> cls, Short nvl)
{
@@ -224,8 +224,8 @@
{
private static final long serialVersionUID = 1L;
- private final static CharacterDeserializer primitiveInstance = new CharacterDeserializer(Character.class, '\0');
- private final static CharacterDeserializer wrapperInstance = new CharacterDeserializer(Character.TYPE, null);
+ final static CharacterDeserializer primitiveInstance = new CharacterDeserializer(Character.class, '\0');
+ final static CharacterDeserializer wrapperInstance = new CharacterDeserializer(Character.TYPE, null);
public CharacterDeserializer(Class<Character> cls, Character nvl)
{
@@ -274,8 +274,8 @@
{
private static final long serialVersionUID = 1L;
- private final static IntegerDeserializer primitiveInstance = new IntegerDeserializer(Integer.class, 0);
- private final static IntegerDeserializer wrapperInstance = new IntegerDeserializer(Integer.TYPE, null);
+ final static IntegerDeserializer primitiveInstance = new IntegerDeserializer(Integer.class, 0);
+ final static IntegerDeserializer wrapperInstance = new IntegerDeserializer(Integer.TYPE, null);
public IntegerDeserializer(Class<Integer> cls, Integer nvl)
{
@@ -306,8 +306,8 @@
{
private static final long serialVersionUID = 1L;
- private final static LongDeserializer primitiveInstance = new LongDeserializer(Long.class, Long.valueOf(0L));
- private final static LongDeserializer wrapperInstance = new LongDeserializer(Long.TYPE, null);
+ final static LongDeserializer primitiveInstance = new LongDeserializer(Long.class, Long.valueOf(0L));
+ final static LongDeserializer wrapperInstance = new LongDeserializer(Long.TYPE, null);
public LongDeserializer(Class<Long> cls, Long nvl)
{
@@ -328,8 +328,8 @@
{
private static final long serialVersionUID = 1L;
- private final static FloatDeserializer primitiveInstance = new FloatDeserializer(Float.class, 0.f);
- private final static FloatDeserializer wrapperInstance = new FloatDeserializer(Float.TYPE, null);
+ final static FloatDeserializer primitiveInstance = new FloatDeserializer(Float.class, 0.f);
+ final static FloatDeserializer wrapperInstance = new FloatDeserializer(Float.TYPE, null);
public FloatDeserializer(Class<Float> cls, Float nvl)
{
@@ -353,8 +353,8 @@
{
private static final long serialVersionUID = 1L;
- private final static DoubleDeserializer primitiveInstance = new DoubleDeserializer(Double.class, 0.d);
- private final static DoubleDeserializer wrapperInstance = new DoubleDeserializer(Double.TYPE, null);
+ final static DoubleDeserializer primitiveInstance = new DoubleDeserializer(Double.class, 0.d);
+ final static DoubleDeserializer wrapperInstance = new DoubleDeserializer(Double.TYPE, null);
public DoubleDeserializer(Class<Double> cls, Double nvl)
{
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 45174c6..d7ff64e 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
@@ -100,6 +100,12 @@
}
return withDeserializer(elemTypeDeser, deser);
}
+
+ @Override // since 2.5
+ public boolean isCachable() {
+ // Important: do NOT cache if polymorphic values
+ return (_elementTypeDeserializer == null);
+ }
/*
/**********************************************************
@@ -157,7 +163,7 @@
chunk[ix++] = value;
}
} catch (Exception e) {
- throw JsonMappingException.wrapWithPath(e, chunk, ix);
+ throw JsonMappingException.wrapWithPath(e, chunk, buffer.bufferedSize() + ix);
}
Object[] result;
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 ea5ac60..7ecdd90 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
@@ -165,7 +165,7 @@
chunk[ix++] = value;
}
} catch (Exception e) {
- throw JsonMappingException.wrapWithPath(e, chunk, ix);
+ throw JsonMappingException.wrapWithPath(e, chunk, builder.bufferedSize() + ix);
}
return builder.completeAndClearBuffer(chunk, ix);
}
@@ -245,7 +245,7 @@
chunk[ix++] = value;
}
} catch (Exception e) {
- throw JsonMappingException.wrapWithPath(e, chunk, ix);
+ throw JsonMappingException.wrapWithPath(e, chunk, builder.bufferedSize() + ix);
}
return builder.completeAndClearBuffer(chunk, ix);
}
@@ -308,7 +308,7 @@
chunk[ix++] = value;
}
} catch (Exception e) {
- throw JsonMappingException.wrapWithPath(e, chunk, ix);
+ throw JsonMappingException.wrapWithPath(e, chunk, builder.bufferedSize() + ix);
}
return builder.completeAndClearBuffer(chunk, ix);
}
@@ -362,7 +362,7 @@
chunk[ix++] = value;
}
} catch (Exception e) {
- throw JsonMappingException.wrapWithPath(e, chunk, ix);
+ throw JsonMappingException.wrapWithPath(e, chunk, builder.bufferedSize() + ix);
}
return builder.completeAndClearBuffer(chunk, ix);
}
@@ -415,7 +415,7 @@
chunk[ix++] = value;
}
} catch (Exception e) {
- throw JsonMappingException.wrapWithPath(e, chunk, ix);
+ throw JsonMappingException.wrapWithPath(e, chunk, builder.bufferedSize() + ix);
}
return builder.completeAndClearBuffer(chunk, ix);
}
@@ -467,7 +467,7 @@
chunk[ix++] = value;
}
} catch (Exception e) {
- throw JsonMappingException.wrapWithPath(e, chunk, ix);
+ throw JsonMappingException.wrapWithPath(e, chunk, builder.bufferedSize() + ix);
}
return builder.completeAndClearBuffer(chunk, ix);
}
@@ -518,7 +518,7 @@
chunk[ix++] = value;
}
} catch (Exception e) {
- throw JsonMappingException.wrapWithPath(e, chunk, ix);
+ throw JsonMappingException.wrapWithPath(e, chunk, builder.bufferedSize() + ix);
}
return builder.completeAndClearBuffer(chunk, ix);
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDelegatingDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDelegatingDeserializer.java
index 1e30aea..da4e048 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDelegatingDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDelegatingDeserializer.java
@@ -20,10 +20,16 @@
*<p>
* Note that although types (delegate, target) may be related, they must not be same; trying
* to do this will result in an exception.
+ *<p>
+ * Since 2.5 There is {@link StdNodeBasedDeserializer} that is a simplified version
+ * for cases where intermediate type is {@link JsonNode}
*
* @param <T> Target type to convert to, from delegate type
*
* @since 2.1
+ *
+ * @see StdNodeBasedDeserializer
+ * @see Converter
*/
public class StdDelegatingDeserializer<T>
extends StdDeserializer<T>
@@ -32,17 +38,17 @@
private static final long serialVersionUID = 1L;
protected final Converter<Object,T> _converter;
-
+
/**
* Fully resolved delegate type, with generic information if any available.
*/
protected final JavaType _delegateType;
-
+
/**
* Underlying serializer for type <code>T<.code>.
*/
protected final JsonDeserializer<Object> _delegateDeserializer;
-
+
/*
/**********************************************************
/* Life-cycle
@@ -57,7 +63,7 @@
_delegateType = null;
_delegateDeserializer = null;
}
-
+
@SuppressWarnings("unchecked")
public StdDelegatingDeserializer(Converter<Object,T> converter,
JavaType delegateType, JsonDeserializer<?> delegateDeserializer)
@@ -69,6 +75,17 @@
}
/**
+ * @since 2.5
+ */
+ protected StdDelegatingDeserializer(StdDelegatingDeserializer<T> src)
+ {
+ super(src);
+ _converter = src._converter;
+ _delegateType = src._delegateType;
+ _delegateDeserializer = src._delegateDeserializer;
+ }
+
+ /**
* Method used for creating resolved contextual instances. Must be
* overridden when sub-classing.
*/
@@ -95,7 +112,7 @@
((ResolvableDeserializer) _delegateDeserializer).resolve(ctxt);
}
}
-
+
@Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
throws JsonMappingException
@@ -129,7 +146,7 @@
public Class<?> handledType() {
return _delegateDeserializer.handledType();
}
-
+
/*
/**********************************************************
/* Serialization
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 e4eeddc..9b54d2a 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
@@ -39,6 +39,16 @@
_valueClass = (valueType == null) ? null : valueType.getRawClass();
}
+ /**
+ * Copy-constructor for sub-classes to use, most often when creating
+ * new instances for {@link com.fasterxml.jackson.databind.deser.ContextualDeserializer}.
+ *
+ * @since 2.5
+ */
+ protected StdDeserializer(StdDeserializer<?> src) {
+ _valueClass = src._valueClass;
+ }
+
/*
/**********************************************************
/* Accessors
@@ -135,7 +145,7 @@
}
throw ctxt.weirdStringException(text, _valueClass, "only \"true\" or \"false\" recognized");
}
- // Issue#381
+ // [databind#381]
if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
jp.nextToken();
final boolean parsed = _parseBooleanPrimitive(jp, ctxt);
@@ -800,6 +810,35 @@
}
/**
+ * Helper method that may be used to support fallback for Empty String / Empty Array
+ * non-standard representations; usually for things serialized as JSON Objects.
+ *
+ * @since 2.5
+ */
+ protected T _deserializeFromEmpty(JsonParser jp, DeserializationContext ctxt)
+ throws IOException
+ {
+ JsonToken t = jp.getCurrentToken();
+ if (t == JsonToken.START_ARRAY) {
+ if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT)) {
+ t = jp.nextToken();
+ if (t == JsonToken.END_ARRAY) {
+ return null;
+ }
+ throw ctxt.mappingException(handledType(), JsonToken.START_ARRAY);
+ }
+ } else if (t == JsonToken.VALUE_STRING) {
+ if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) {
+ String str = jp.getText().trim();
+ if (str.isEmpty()) {
+ return null;
+ }
+ }
+ }
+ throw ctxt.mappingException(handledType());
+ }
+
+ /**
* Helper method called to determine if we are seeing String value of
* "null", and, further, that it should be coerced to null just like
* null token.
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 b174bef..a374552 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,6 +3,8 @@
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
+import java.net.URI;
+import java.net.URL;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
@@ -41,6 +43,9 @@
public final static int TYPE_DATE = 10;
public final static int TYPE_CALENDAR = 11;
public final static int TYPE_UUID = 12;
+ public final static int TYPE_URI = 13;
+ public final static int TYPE_URL = 14;
+ public final static int TYPE_CLASS = 15;
final protected int _kind;
final protected Class<?> _keyClass;
@@ -90,6 +95,12 @@
kind = TYPE_FLOAT;
} else if (raw == Double.class) {
kind = TYPE_DOUBLE;
+ } else if (raw == URI.class) {
+ kind = TYPE_URI;
+ } else if (raw == URL.class) {
+ kind = TYPE_URL;
+ } else if (raw == Class.class) {
+ kind = TYPE_CLASS;
} else if (raw == Locale.class) {
FromStringDeserializer<?> deser = FromStringDeserializer.findDeserializer(Locale.class);
return new StdKeyDeserializer(TYPE_LOCALE, raw, deser);
@@ -162,9 +173,7 @@
return _parseLong(key);
case TYPE_FLOAT:
- /* 22-Jan-2009, tatu: Bounds/range checks would be tricky
- * here, so let's not bother even trying...
- */
+ // Bounds/range checks would be tricky here, so let's not bother even trying...
return Float.valueOf((float) _parseDouble(key));
case TYPE_DOUBLE:
return _parseDouble(key);
@@ -182,6 +191,16 @@
return (date == null) ? null : ctxt.constructCalendar(date);
case TYPE_UUID:
return UUID.fromString(key);
+ case TYPE_URI:
+ return URI.create(key);
+ case TYPE_URL:
+ return new URL(key);
+ case TYPE_CLASS:
+ try {
+ return ctxt.findClass(key);
+ } catch (Exception e) {
+ throw ctxt.weirdKeyException(_keyClass, key, "unable to parse key as Class");
+ }
}
return null;
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdKeyDeserializers.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdKeyDeserializers.java
index 95a560e..32e2a4a 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdKeyDeserializers.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdKeyDeserializers.java
@@ -28,7 +28,7 @@
public class StdKeyDeserializers
implements KeyDeserializers, java.io.Serializable
{
- private static final long serialVersionUID = 923268084968181479L;
+ private static final long serialVersionUID = 1L;
public static KeyDeserializer constructEnumKeyDeserializer(EnumResolver<?> enumResolver) {
return new StdKeyDeserializer.EnumKD(enumResolver, null);
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdNodeBasedDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdNodeBasedDeserializer.java
new file mode 100644
index 0000000..b48340c
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdNodeBasedDeserializer.java
@@ -0,0 +1,87 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.ResolvableDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+
+/**
+ * Convenience deserializer that may be used to deserialize values given an
+ * intermediate tree representation ({@link JsonNode}).
+ * Note that this is a slightly simplified alternative to {@link StdDelegatingDeserializer}).
+ *
+ * @param <T> Target type of this deserializer; that is, type of values that
+ * input data is deserialized into.
+ *
+ * @since 2.5
+ */
+public abstract class StdNodeBasedDeserializer<T>
+ extends StdDeserializer<T>
+ implements ResolvableDeserializer
+{
+ private static final long serialVersionUID = 1L;
+
+ protected JsonDeserializer<Object> _treeDeserializer;
+
+ /*
+ /**********************************************************
+ /* Life-cycle
+ /**********************************************************
+ */
+
+ protected StdNodeBasedDeserializer(JavaType targetType) {
+ super(targetType);
+ }
+
+ protected StdNodeBasedDeserializer(Class<T> targetType) {
+ super(targetType);
+ }
+
+ /**
+ * "Copy-constructor" used when creating a modified copies, most often
+ * if sub-class implements {@link com.fasterxml.jackson.databind.deser.ContextualDeserializer}.
+ */
+ protected StdNodeBasedDeserializer(StdNodeBasedDeserializer<?> src) {
+ super(src);
+ _treeDeserializer = src._treeDeserializer;
+ }
+
+ @Override
+ public void resolve(DeserializationContext ctxt) throws JsonMappingException {
+ _treeDeserializer = ctxt.findRootValueDeserializer(ctxt.constructType(JsonNode.class));
+ }
+
+ /*
+ /**********************************************************
+ /* Abstract methods for sub-classes
+ /**********************************************************
+ */
+
+ public abstract T convert(JsonNode root, DeserializationContext ctxt) throws IOException;
+
+ /*
+ /**********************************************************
+ /* JsonDeserializer impl
+ /**********************************************************
+ */
+
+ @Override
+ public T deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
+ JsonNode n = (JsonNode) _treeDeserializer.deserialize(jp, ctxt);
+ return convert(n, ctxt);
+ }
+
+ @Override
+ public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
+ TypeDeserializer td)
+ throws IOException, JsonProcessingException
+ {
+ /* 19-Nov-2014, tatu: Quite likely we'd have some issues but... let's
+ * try, just in case.
+ */
+ JsonNode n = (JsonNode) _treeDeserializer.deserializeWithType(jp, ctxt, td);
+ return convert(n, ctxt);
+ }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdScalarDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdScalarDeserializer.java
index 8594912..889bd62 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdScalarDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdScalarDeserializer.java
@@ -17,6 +17,9 @@
protected StdScalarDeserializer(Class<?> vc) { super(vc); }
protected StdScalarDeserializer(JavaType valueType) { super(valueType); }
+
+ // since 2.5
+ protected StdScalarDeserializer(StdScalarDeserializer<?> src) { super(src); }
@Override
public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException {
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 387a43d..439c256 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
@@ -2,7 +2,6 @@
import java.io.IOException;
-import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.deser.*;
@@ -204,8 +203,7 @@
*/
@Override
- public Object createUsingDefault(DeserializationContext ctxt)
- throws IOException, JsonProcessingException
+ public Object createUsingDefault(DeserializationContext ctxt) throws IOException
{
if (_defaultCreator == null) { // sanity-check; caller should check
throw new IllegalStateException("No default constructor for "+getValueTypeDesc());
@@ -220,8 +218,7 @@
}
@Override
- public Object createFromObjectWith(DeserializationContext ctxt, Object[] args)
- throws IOException, JsonProcessingException
+ 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());
@@ -236,8 +233,7 @@
}
@Override
- public Object createUsingDelegate(DeserializationContext ctxt, Object delegate)
- throws IOException, JsonProcessingException
+ public Object createUsingDelegate(DeserializationContext ctxt, Object delegate) throws IOException
{
if (_delegateCreator == null) { // sanity-check; caller should check
throw new IllegalStateException("No delegate constructor for "+getValueTypeDesc());
@@ -274,8 +270,7 @@
*/
@Override
- public Object createFromString(DeserializationContext ctxt, String value)
- throws IOException, JsonProcessingException
+ public Object createFromString(DeserializationContext ctxt, String value) throws IOException
{
if (_fromStringCreator != null) {
try {
@@ -290,8 +285,7 @@
}
@Override
- public Object createFromInt(DeserializationContext ctxt, int value)
- throws IOException, JsonProcessingException
+ public Object createFromInt(DeserializationContext ctxt, int value) throws IOException
{
try {
// First: "native" int methods work best:
@@ -312,8 +306,7 @@
}
@Override
- public Object createFromLong(DeserializationContext ctxt, long value)
- throws IOException, JsonProcessingException
+ public Object createFromLong(DeserializationContext ctxt, long value) throws IOException
{
try {
if (_fromLongCreator != null) {
@@ -329,8 +322,7 @@
}
@Override
- public Object createFromDouble(DeserializationContext ctxt, double value)
- throws IOException, JsonProcessingException
+ public Object createFromDouble(DeserializationContext ctxt, double value) throws IOException
{
try {
if (_fromDoubleCreator != null) {
@@ -346,8 +338,7 @@
}
@Override
- public Object createFromBoolean(DeserializationContext ctxt, boolean value)
- throws IOException, JsonProcessingException
+ public Object createFromBoolean(DeserializationContext ctxt, boolean value) throws IOException
{
try {
if (_fromBooleanCreator != null) {
@@ -387,7 +378,7 @@
public AnnotatedParameter getIncompleteParameter() {
return _incompleteParameter;
}
-
+
/*
/**********************************************************
/* Internal methods
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 a4e08e4..142eda3 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
@@ -19,7 +19,7 @@
extends StdDeserializer<String[]>
implements ContextualDeserializer
{
- private static final long serialVersionUID = -7589512013334920693L;
+ private static final long serialVersionUID = 1L;
public final static StringArrayDeserializer instance = new StringArrayDeserializer();
@@ -74,7 +74,7 @@
chunk[ix++] = value;
}
} catch (Exception e) {
- throw JsonMappingException.wrapWithPath(e, chunk, ix);
+ throw JsonMappingException.wrapWithPath(e, chunk, buffer.bufferedSize() + ix);
}
String[] result = buffer.completeAndClearBuffer(chunk, ix, String.class);
ctxt.returnObjectBuffer(buffer);
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 97c3ed8..e6a5a0d 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
@@ -82,6 +82,11 @@
_valueInstantiator, delegateDeser, valueDeser);
}
+ @Override // since 2.5
+ public boolean isCachable() {
+ return true;
+ }
+
/*
/**********************************************************
/* Validation, post-processing
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 fdf0a10..e04e09d 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
@@ -112,7 +112,9 @@
protected JsonDeserializer<Object> _findCustomDeser(DeserializationContext ctxt, JavaType type)
throws JsonMappingException
{
- JsonDeserializer<?> deser = ctxt.findRootValueDeserializer(type);
+ // Since we are calling from `resolve`, we should NOT try to contextualize yet;
+ // contextualization will only occur at a later point
+ JsonDeserializer<?> deser = ctxt.findNonContextualValueDeserializer(type);
if (ClassUtil.isJacksonStdImpl(deser)) {
return null;
}
@@ -150,6 +152,13 @@
/**********************************************************
*/
+ /* 07-Nov-2014, tatu: When investigating [databind#604], realized that it makes
+ * sense to also mark this is cachable, since lookup not exactly free, and
+ * since it's not uncommon to "read anything"
+ */
+ @Override
+ public boolean isCachable() { return true; }
+
@Override
public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException
{
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 3512003..2d6441c 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/Annotated.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/Annotated.java
@@ -85,5 +85,15 @@
* not exposed to developers since instances are mutable.
*/
protected abstract AnnotationMap getAllAnnotations();
-}
+ // Also: ensure we can use #equals, #hashCode
+
+ @Override
+ public abstract boolean equals(Object o);
+
+ @Override
+ public abstract int hashCode();
+
+ @Override
+ public abstract String toString();
+}
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 fa379b1..9b9d1c2 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java
@@ -908,13 +908,13 @@
if (anns != null) {
List<Annotation[]> bundles = null;
for (Annotation ann : anns) { // first: direct annotations
- if (_isAnnotationBundle(ann)) {
+ // note: we will NOT filter out non-Jackson anns any more
+ boolean wasNotPresent = result.addIfNotPresent(ann);
+ if (wasNotPresent && _isAnnotationBundle(ann)) {
if (bundles == null) {
bundles = new LinkedList<Annotation[]>();
}
bundles.add(ann.annotationType().getDeclaredAnnotations());
- } else { // note: we will NOT filter out non-Jackson anns any more
- result.addIfNotPresent(ann);
}
}
if (bundles != null) { // and secondarily handle bundles, if any found: precedence important
@@ -930,13 +930,13 @@
if (anns != null) {
List<Annotation[]> bundles = null;
for (Annotation ann : anns) { // first: direct annotations
- if (_isAnnotationBundle(ann)) {
+ // note: we will NOT filter out non-Jackson anns any more
+ boolean wasNotPresent = target.addIfNotPresent(ann);
+ if (wasNotPresent && _isAnnotationBundle(ann)) {
if (bundles == null) {
bundles = new LinkedList<Annotation[]>();
}
bundles.add(ann.annotationType().getDeclaredAnnotations());
- } else { // note: we will NOT filter out non-Jackson anns any more
- target.addIfNotPresent(ann);
}
}
if (bundles != null) { // and secondarily handle bundles, if any found: precedence important
@@ -952,13 +952,13 @@
if (anns != null) {
List<Annotation[]> bundles = null;
for (Annotation ann : anns) { // first: direct annotations
- if (_isAnnotationBundle(ann)) {
+ // note: we will NOT filter out non-Jackson anns any more
+ boolean wasModified = target.addOrOverride(ann);
+ if (wasModified && _isAnnotationBundle(ann)) {
if (bundles == null) {
bundles = new LinkedList<Annotation[]>();
}
bundles.add(ann.annotationType().getDeclaredAnnotations());
- } else { // note: no filtering by jackson-annotations
- target.addOrOverride(ann);
}
}
if (bundles != null) { // and then bundles, if any: important for precedence
@@ -1013,8 +1013,7 @@
_addAnnotationsIfNotPresent(target, src.getDeclaredAnnotations());
}
- private final boolean _isAnnotationBundle(Annotation ann)
- {
+ private final boolean _isAnnotationBundle(Annotation ann) {
return (_annotationIntrospector != null) && _annotationIntrospector.isAnnotationBundle(ann);
}
@@ -1025,8 +1024,19 @@
*/
@Override
- public String toString()
- {
+ public String toString() {
return "[AnnotedClass "+_class.getName()+"]";
}
+
+ @Override
+ public int hashCode() {
+ return _class.getName().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (o == null || o.getClass() != getClass()) return false;
+ return ((AnnotatedClass) o)._class == _class;
+ }
}
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 d94c091..1829cb3 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedConstructor.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedConstructor.java
@@ -169,6 +169,18 @@
return "[constructor for "+getName()+", annotations: "+_annotations+"]";
}
+ @Override
+ public int hashCode() {
+ return _constructor.getName().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (o == null || o.getClass() != getClass()) return false;
+ return ((AnnotatedConstructor) o)._constructor == _constructor;
+ }
+
/*
/**********************************************************
/* JDK serialization handling
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedField.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedField.java
index a0ce14a..ebc48cb 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedField.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedField.java
@@ -73,8 +73,7 @@
public String getName() { return _field.getName(); }
@Override
- public <A extends Annotation> A getAnnotation(Class<A> acls)
- {
+ public <A extends Annotation> A getAnnotation(Class<A> acls) {
return (_annotations == null) ? null : _annotations.get(acls);
}
@@ -135,8 +134,19 @@
public int getAnnotationCount() { return _annotations.size(); }
@Override
- public String toString()
- {
+ public int hashCode() {
+ return _field.getName().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (o == null || o.getClass() != getClass()) return false;
+ return ((AnnotatedField) o)._field == _field;
+ }
+
+ @Override
+ public String toString() {
return "[field "+getFullName()+"]";
}
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 871a65c..d5114d8 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMember.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMember.java
@@ -11,8 +11,6 @@
* a class; fields, methods and constructors. This is a superset
* of things that can represent logical properties as it contains
* constructors in addition to fields and methods.
- *
- * @author tatu
*/
public abstract class AnnotatedMember
extends Annotated
@@ -51,8 +49,8 @@
* annotation masking or overriding an annotation 'real' constructor
* has.
*/
- public final void addOrOverride(Annotation a) {
- _annotations.add(a);
+ public final boolean addOrOverride(Annotation a) {
+ return _annotations.add(a);
}
/**
@@ -60,8 +58,8 @@
* annotation if and only if it is not yet present in the
* annotation map we have.
*/
- public final void addIfNotPresent(Annotation a) {
- _annotations.addIfNotPresent(a);
+ public final boolean addIfNotPresent(Annotation a) {
+ return _annotations.addIfNotPresent(a);
}
/**
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 3fc2838..bf5afd2 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethod.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethod.java
@@ -138,8 +138,7 @@
public Method getMember() { return _method; }
@Override
- public void setValue(Object pojo, Object value)
- throws IllegalArgumentException
+ public void setValue(Object pojo, Object value) throws IllegalArgumentException
{
try {
_method.invoke(pojo, value);
@@ -235,11 +234,22 @@
*/
@Override
- public String toString()
- {
+ public String toString() {
return "[method "+getFullName()+"]";
}
+ @Override
+ public int hashCode() {
+ return _method.getName().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (o == null || o.getClass() != getClass()) return false;
+ return ((AnnotatedMethod) o)._method == _method;
+ }
+
/*
/**********************************************************
/* JDK serialization handling
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedParameter.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedParameter.java
index 4c2bfcb..718d0f5 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedParameter.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedParameter.java
@@ -5,7 +5,6 @@
import java.lang.reflect.Member;
import java.lang.reflect.Type;
-
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.type.TypeFactory;
@@ -175,8 +174,20 @@
*/
@Override
- public String toString()
- {
+ public int hashCode() {
+ return _owner.hashCode() + _index;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (o == null || o.getClass() != getClass()) return false;
+ AnnotatedParameter other = (AnnotatedParameter) o;
+ return other._owner.equals(_owner) && (other._index == _index);
+ }
+
+ @Override
+ public String toString() {
return "[parameter #"+getIndex()+", annotations: "+_annotations+"]";
}
}
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 39d4fcd..22cecc2 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair.java
@@ -5,6 +5,7 @@
import java.util.Collection;
import java.util.List;
+import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.Version;
@@ -315,16 +316,15 @@
public JsonInclude.Include findSerializationInclusion(Annotated a,
JsonInclude.Include defValue)
{
- /* This is bit trickier: need to combine results in a meaningful
- * way. Seems like it should be a disjoint; that is, most
- * restrictive value should be returned.
- * For enumerations, comparison is done by indexes, which
- * works: largest value is the last one, which is the most
- * restrictive value as well.
- */
- /* 09-Mar-2010, tatu: Actually, as per [JACKSON-256], it is probably better to just
- * use strict overriding. Simpler, easier to understand.
- */
+ // note: call secondary first, to give lower priority
+ defValue = _secondary.findSerializationInclusion(a, defValue);
+ defValue = _primary.findSerializationInclusion(a, defValue);
+ return defValue;
+ }
+
+ @Override
+ public JsonInclude.Include findSerializationInclusionForContent(Annotated a, JsonInclude.Include defValue)
+ {
// note: call secondary first, to give lower priority
defValue = _secondary.findSerializationInclusion(a, defValue);
defValue = _primary.findSerializationInclusion(a, defValue);
@@ -333,7 +333,7 @@
@Override
public Class<?> findSerializationType(Annotated a) {
- Class<?> r = _primary.findSerializationType(a);
+ Class<?> r = _primary.findSerializationType(a);
return (r == null) ? _secondary.findSerializationType(a) : r;
}
@@ -599,6 +599,15 @@
return _primary.hasCreatorAnnotation(a) || _secondary.hasCreatorAnnotation(a);
}
+ @Override
+ public JsonCreator.Mode findCreatorBinding(Annotated a) {
+ JsonCreator.Mode mode = _primary.findCreatorBinding(a);
+ if (mode != null) {
+ return mode;
+ }
+ return _secondary.findCreatorBinding(a);
+ }
+
protected boolean _isExplicitClassOrOb(Object maybeCls, Class<?> implicit) {
if (maybeCls == null) {
return false;
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationMap.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationMap.java
index 1b350fe..0b514ff 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationMap.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationMap.java
@@ -71,18 +71,20 @@
* Method called to add specified annotation in the Map, but
* only if it didn't yet exist.
*/
- public void addIfNotPresent(Annotation ann)
+ public boolean addIfNotPresent(Annotation ann)
{
if (_annotations == null || !_annotations.containsKey(ann.annotationType())) {
_add(ann);
+ return true;
}
+ return false;
}
/**
* Method called to add specified annotation in the Map.
*/
- public void add(Annotation ann) {
- _add(ann);
+ public boolean add(Annotation ann) {
+ return _add(ann);
}
@Override
@@ -99,11 +101,12 @@
/**********************************************************
*/
- protected final void _add(Annotation ann) {
+ protected final boolean _add(Annotation ann) {
if (_annotations == null) {
_annotations = new HashMap<Class<? extends Annotation>,Annotation>();
}
- _annotations.put(ann.annotationType(), ann);
+ Annotation previous = _annotations.put(ann.annotationType(), ann);
+ return (previous != null) && previous.equals(ann);
}
}
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 f8745dd..3936ab3 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/BasicBeanDescription.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/BasicBeanDescription.java
@@ -175,7 +175,7 @@
@Override
public ObjectIdInfo getObjectIdInfo() { return _objectIdInfo; }
-
+
@Override
public List<BeanPropertyDefinition> findProperties() {
return _properties;
@@ -193,7 +193,7 @@
}
return _ignoredPropertyNames;
}
-
+
@Override
public boolean hasKnownClassAnnotations() {
return _classInfo.hasAnnotations();
@@ -330,13 +330,20 @@
* and per-class annotation (highest priority).
*/
@Override
- public JsonInclude.Include findSerializationInclusion(JsonInclude.Include defValue)
- {
+ public JsonInclude.Include findSerializationInclusion(JsonInclude.Include defValue) {
if (_annotationIntrospector == null) {
return defValue;
}
return _annotationIntrospector.findSerializationInclusion(_classInfo, defValue);
}
+
+ @Override
+ public JsonInclude.Include findSerializationInclusionForContent(JsonInclude.Include defValue) {
+ if (_annotationIntrospector == null) {
+ return defValue;
+ }
+ return _annotationIntrospector.findSerializationInclusionForContent(_classInfo, defValue);
+ }
/**
* Method used to locate the method of introspected class that
@@ -363,7 +370,20 @@
public Map<String,AnnotatedMember> findBackReferenceProperties()
{
HashMap<String,AnnotatedMember> result = null;
+// boolean hasIgnored = (_ignoredPropertyNames != null);
+
for (BeanPropertyDefinition property : _properties) {
+ /* 23-Sep-2014, tatu: As per [Databind#426], we _should_ try to avoid
+ * calling accessor, as it triggers exception from seeming conflict.
+ * But the problem is that _ignoredPropertyNames here only contains
+ * ones ignored on per-property annotations, but NOT class annotations...
+ * so commented out part does not work, alas
+ */
+ /*
+ if (hasIgnored && _ignoredPropertyNames.contains(property.getName())) {
+ continue;
+ }
+ */
AnnotatedMember am = property.getMutator();
if (am == null) {
continue;
@@ -457,7 +477,7 @@
/* Also: must be a recognized factory method, meaning:
* (a) marked with @JsonCreator annotation, or
- * (a) "valueOf" (at this point, need not be public)
+ * (b) "valueOf" (at this point, need not be public)
*/
if (_annotationIntrospector.hasCreatorAnnotation(am)) {
return true;
@@ -479,7 +499,7 @@
}
/**
- * @deprecated Since 2.4, use {@link #findCreatorParameterNames()} instead.
+ * @deprecated Since 2.4, use <code>findCreatorParameterNames()</code> instead.
*/
@Deprecated
public List<String> findCreatorPropertyNames()
@@ -496,16 +516,9 @@
}
/**
- * Method for getting ordered list of named Creator properties.
- * Returns an empty list is none found. If multiple Creator
- * methods are defined, order between properties from different
- * methods is undefined; however, properties for each such
- * Creator are ordered properly relative to each other.
- * For the usual case of just a single Creator, named properties are
- * thus properly ordered.
- *
- * @since 2.4
+ * @deprecated Since 2.5, does not seem to be used at all.
*/
+ @Deprecated
public List<PropertyName> findCreatorParameterNames()
{
for (int i = 0; i < 2; ++i) {
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/BasicClassIntrospector.java b/src/main/java/com/fasterxml/jackson/databind/introspect/BasicClassIntrospector.java
index dbab578..d908a09 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/BasicClassIntrospector.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/BasicClassIntrospector.java
@@ -1,5 +1,8 @@
package com.fasterxml.jackson.databind.introspect;
+import java.util.Collection;
+import java.util.Map;
+
import com.fasterxml.jackson.databind.AnnotationIntrospector;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.JavaType;
@@ -7,6 +10,7 @@
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.type.SimpleType;
+import com.fasterxml.jackson.databind.util.LRUMap;
public class BasicClassIntrospector
extends ClassIntrospector
@@ -19,6 +23,8 @@
* This is strictly performance optimization to reduce what is
* usually one-time cost, but seems useful for some cases considering
* simplicity.
+ *
+ * @since 2.4
*/
protected final static BasicBeanDescription STRING_DESC;
@@ -48,9 +54,21 @@
/**********************************************************
*/
+ @Deprecated // since 2.5: construct instance directly
public final static BasicClassIntrospector instance = new BasicClassIntrospector();
- public BasicClassIntrospector() { }
+ /**
+ * Looks like 'forClassAnnotations()' gets called so frequently that we
+ * should consider caching to avoid some of the lookups.
+ *
+ * @since 2.5
+ */
+ protected final LRUMap<JavaType,BasicBeanDescription> _cachedFCA;
+
+ public BasicClassIntrospector() {
+ // a small cache should go a long way here
+ _cachedFCA = new LRUMap<JavaType,BasicBeanDescription>(16, 64);
+ }
/*
/**********************************************************
@@ -62,11 +80,18 @@
public BasicBeanDescription forSerialization(SerializationConfig cfg,
JavaType type, MixInResolver r)
{
- // minor optimization: for JDK types do minimal introspection
- BasicBeanDescription desc = _findCachedDesc(type);
+ // minor optimization: for some JDK types do minimal introspection
+ BasicBeanDescription desc = _findStdTypeDesc(type);
if (desc == null) {
- desc = BasicBeanDescription.forSerialization(collectProperties(cfg,
- type, r, true, "set"));
+ // As per [Databind#550], skip full introspection for some of standard
+ // structured types as well
+ desc = _findStdJdkCollectionDesc(cfg, type, r);
+ if (desc == null) {
+ desc = BasicBeanDescription.forSerialization(collectProperties(cfg,
+ type, r, true, "set"));
+ }
+ // Also: this is a superset of "forClassAnnotations", so may optimize by optional add:
+ _cachedFCA.putIfAbsent(type, desc);
}
return desc;
}
@@ -75,11 +100,18 @@
public BasicBeanDescription forDeserialization(DeserializationConfig cfg,
JavaType type, MixInResolver r)
{
- // minor optimization: for JDK types do minimal introspection
- BasicBeanDescription desc = _findCachedDesc(type);
+ // minor optimization: for some JDK types do minimal introspection
+ BasicBeanDescription desc = _findStdTypeDesc(type);
if (desc == null) {
- desc = BasicBeanDescription.forDeserialization(collectProperties(cfg,
- type, r, false, "set"));
+ // As per [Databind#550], skip full introspection for some of standard
+ // structured types as well
+ desc = _findStdJdkCollectionDesc(cfg, type, r);
+ if (desc == null) {
+ desc = BasicBeanDescription.forDeserialization(collectProperties(cfg,
+ type, r, false, "set"));
+ }
+ // Also: this is a superset of "forClassAnnotations", so may optimize by optional add:
+ _cachedFCA.putIfAbsent(type, desc);
}
return desc;
}
@@ -88,20 +120,31 @@
public BasicBeanDescription forDeserializationWithBuilder(DeserializationConfig cfg,
JavaType type, MixInResolver r)
{
- // no caching for Builders (no standard JDK builder types):
- return BasicBeanDescription.forDeserialization(collectPropertiesWithBuilder(cfg,
- type, r, false));
+ // no std JDK types with Builders, so:
+
+ BasicBeanDescription desc = BasicBeanDescription.forDeserialization(collectPropertiesWithBuilder(cfg,
+ type, r, false));
+ // this is still a superset of "forClassAnnotations", so may optimize by optional add:
+ _cachedFCA.putIfAbsent(type, desc);
+ return desc;
}
@Override
public BasicBeanDescription forCreation(DeserializationConfig cfg,
JavaType type, MixInResolver r)
{
- BasicBeanDescription desc = _findCachedDesc(type);
+ BasicBeanDescription desc = _findStdTypeDesc(type);
if (desc == null) {
- desc = BasicBeanDescription.forDeserialization(
+
+ // As per [Databind#550], skip full introspection for some of standard
+ // structured types as well
+ desc = _findStdJdkCollectionDesc(cfg, type, r);
+ if (desc == null) {
+ desc = BasicBeanDescription.forDeserialization(
collectProperties(cfg, type, r, false, "set"));
+ }
}
+ // should this be cached for FCA?
return desc;
}
@@ -109,21 +152,33 @@
public BasicBeanDescription forClassAnnotations(MapperConfig<?> cfg,
JavaType type, MixInResolver r)
{
- boolean useAnnotations = cfg.isAnnotationProcessingEnabled();
- AnnotatedClass ac = AnnotatedClass.construct(type.getRawClass(),
- (useAnnotations ? cfg.getAnnotationIntrospector() : null), r);
- return BasicBeanDescription.forOtherUse(cfg, type, ac);
+ BasicBeanDescription desc = _findStdTypeDesc(type);
+ if (desc == null) {
+ desc = _cachedFCA.get(type);
+ if (desc == null) {
+ boolean useAnnotations = cfg.isAnnotationProcessingEnabled();
+ AnnotatedClass ac = AnnotatedClass.construct(type.getRawClass(),
+ (useAnnotations ? cfg.getAnnotationIntrospector() : null), r);
+ desc = BasicBeanDescription.forOtherUse(cfg, type, ac);
+ _cachedFCA.put(type, desc);
+ }
+ }
+ return desc;
}
@Override
public BasicBeanDescription forDirectClassAnnotations(MapperConfig<?> cfg,
JavaType type, MixInResolver r)
{
- boolean useAnnotations = cfg.isAnnotationProcessingEnabled();
- AnnotationIntrospector ai = cfg.getAnnotationIntrospector();
- AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(type.getRawClass(),
- (useAnnotations ? ai : null), r);
- return BasicBeanDescription.forOtherUse(cfg, type, ac);
+ BasicBeanDescription desc = _findStdTypeDesc(type);
+ if (desc == null) {
+ boolean useAnnotations = cfg.isAnnotationProcessingEnabled();
+ AnnotationIntrospector ai = cfg.getAnnotationIntrospector();
+ AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(type.getRawClass(),
+ (useAnnotations ? ai : null), r);
+ desc = BasicBeanDescription.forOtherUse(cfg, type, ac);
+ }
+ return desc;
}
/*
@@ -167,20 +222,62 @@
* Method called to see if type is one of core JDK types
* that we have cached for efficiency.
*/
- protected BasicBeanDescription _findCachedDesc(JavaType type)
+ protected BasicBeanDescription _findStdTypeDesc(JavaType type)
{
Class<?> cls = type.getRawClass();
- if (cls == String.class) {
- return STRING_DESC;
+ if (cls.isPrimitive()) {
+ if (cls == Boolean.TYPE) {
+ return BOOLEAN_DESC;
+ }
+ if (cls == Integer.TYPE) {
+ return INT_DESC;
+ }
+ if (cls == Long.TYPE) {
+ return LONG_DESC;
+ }
+ } else {
+ if (cls == String.class) {
+ return STRING_DESC;
+ }
}
- if (cls == Boolean.TYPE) {
- return BOOLEAN_DESC;
+ return null;
+ }
+
+ /**
+ * Helper method used to decide whether we can omit introspection
+ * for members (methods, fields, constructors); we may do so for
+ * a limited number of container types JDK provides.
+ */
+ protected boolean _isStdJDKCollection(JavaType type)
+ {
+ if (!type.isContainerType() || type.isArrayType()) {
+ return false;
}
- if (cls == Integer.TYPE) {
- return INT_DESC;
+ Class<?> raw = type.getRawClass();
+ Package pkg = raw.getPackage();
+ if (pkg != null) {
+ String pkgName = pkg.getName();
+ if (pkgName.startsWith("java.lang")
+ || pkgName.startsWith("java.util")) {
+ /* 23-Sep-2014, tatu: Should we be conservative here (minimal number
+ * of matches), or ambitious? Let's do latter for now.
+ */
+ if (Collection.class.isAssignableFrom(raw)
+ || Map.class.isAssignableFrom(raw)) {
+ return true;
+ }
+ }
}
- if (cls == Long.TYPE) {
- return LONG_DESC;
+ return false;
+ }
+
+ protected BasicBeanDescription _findStdJdkCollectionDesc(MapperConfig<?> cfg,
+ JavaType type, MixInResolver r)
+ {
+ if (_isStdJDKCollection(type)) {
+ AnnotatedClass ac = AnnotatedClass.construct(type.getRawClass(),
+ (cfg.isAnnotationProcessingEnabled() ? cfg.getAnnotationIntrospector() : null), r);
+ return BasicBeanDescription.forOtherUse(cfg, type, ac);
}
return null;
}
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 809a97c..dce8b3c 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/BeanPropertyDefinition.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/BeanPropertyDefinition.java
@@ -1,9 +1,9 @@
package com.fasterxml.jackson.databind.introspect;
-import com.fasterxml.jackson.databind.AnnotationIntrospector;
-import com.fasterxml.jackson.databind.BeanProperty;
-import com.fasterxml.jackson.databind.PropertyMetadata;
-import com.fasterxml.jackson.databind.PropertyName;
+import java.util.Iterator;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.util.EmptyIterator;
import com.fasterxml.jackson.databind.util.Named;
/**
@@ -135,6 +135,16 @@
public abstract AnnotatedParameter getConstructorParameter();
/**
+ * Additional method that may be called instead of {@link #getConstructorParameter()}
+ * to get access to all constructor parameters, not just the highest priority one.
+ *
+ * @since 2.5
+ */
+ public Iterator<AnnotatedParameter> getConstructorParameters() {
+ return EmptyIterator.instance();
+ }
+
+ /**
* Method used to find accessor (getter, field to access) to use for accessing
* value of the property.
* Null if no such member exists.
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 6998126..d43f59f 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java
@@ -69,7 +69,7 @@
@Override
public PropertyName findRootName(AnnotatedClass ac)
{
- JsonRootName ann = ac.getAnnotation(JsonRootName.class);
+ JsonRootName ann = _findAnnotation(ac, JsonRootName.class);
if (ann == null) {
return null;
}
@@ -82,19 +82,19 @@
@Override
public String[] findPropertiesToIgnore(Annotated ac) {
- JsonIgnoreProperties ignore = ac.getAnnotation(JsonIgnoreProperties.class);
+ JsonIgnoreProperties ignore = _findAnnotation(ac, JsonIgnoreProperties.class);
return (ignore == null) ? null : ignore.value();
}
@Override
public Boolean findIgnoreUnknownProperties(AnnotatedClass ac) {
- JsonIgnoreProperties ignore = ac.getAnnotation(JsonIgnoreProperties.class);
+ JsonIgnoreProperties ignore = _findAnnotation(ac, JsonIgnoreProperties.class);
return (ignore == null) ? null : ignore.ignoreUnknown();
}
@Override
public Boolean isIgnorableType(AnnotatedClass ac) {
- JsonIgnoreType ignore = ac.getAnnotation(JsonIgnoreType.class);
+ JsonIgnoreType ignore = _findAnnotation(ac, JsonIgnoreType.class);
return (ignore == null) ? null : ignore.value();
}
@@ -114,7 +114,7 @@
protected final Object _findFilterId(Annotated a)
{
- JsonFilter ann = a.getAnnotation(JsonFilter.class);
+ JsonFilter ann = _findAnnotation(a, JsonFilter.class);
if (ann != null) {
String id = ann.value();
// Empty String is same as not having annotation, to allow overrides
@@ -128,7 +128,7 @@
@Override
public Object findNamingStrategy(AnnotatedClass ac)
{
- JsonNaming ann = ac.getAnnotation(JsonNaming.class);
+ JsonNaming ann = _findAnnotation(ac, JsonNaming.class);
return (ann == null) ? null : ann.value();
}
@@ -142,7 +142,7 @@
public VisibilityChecker<?> findAutoDetectVisibility(AnnotatedClass ac,
VisibilityChecker<?> checker)
{
- JsonAutoDetect ann = ac.getAnnotation(JsonAutoDetect.class);
+ JsonAutoDetect ann = _findAnnotation(ac, JsonAutoDetect.class);
return (ann == null) ? checker : checker.with(ann);
}
@@ -155,11 +155,11 @@
@Override
public ReferenceProperty findReferenceType(AnnotatedMember member)
{
- JsonManagedReference ref1 = member.getAnnotation(JsonManagedReference.class);
+ JsonManagedReference ref1 = _findAnnotation(member, JsonManagedReference.class);
if (ref1 != null) {
return AnnotationIntrospector.ReferenceProperty.managed(ref1.value());
}
- JsonBackReference ref2 = member.getAnnotation(JsonBackReference.class);
+ JsonBackReference ref2 = _findAnnotation(member, JsonBackReference.class);
if (ref2 != null) {
return AnnotationIntrospector.ReferenceProperty.back(ref2.value());
}
@@ -169,7 +169,7 @@
@Override
public NameTransformer findUnwrappingNameTransformer(AnnotatedMember member)
{
- JsonUnwrapped ann = member.getAnnotation(JsonUnwrapped.class);
+ JsonUnwrapped ann = _findAnnotation(member, JsonUnwrapped.class);
// if not enabled, just means annotation is not enabled; not necessarily
// that unwrapping should not be done (relevant when using chained introspectors)
if (ann == null || !ann.enabled()) {
@@ -188,7 +188,7 @@
@Override
public Boolean hasRequiredMarker(AnnotatedMember m)
{
- JsonProperty ann = m.getAnnotation(JsonProperty.class);
+ JsonProperty ann = _findAnnotation(m, JsonProperty.class);
if (ann != null) {
return ann.required();
}
@@ -198,7 +198,7 @@
@Override
public Object findInjectableValueId(AnnotatedMember m)
{
- JacksonInject ann = m.getAnnotation(JacksonInject.class);
+ JacksonInject ann = _findAnnotation(m, JacksonInject.class);
if (ann == null) {
return null;
}
@@ -261,7 +261,7 @@
@Override
public List<NamedType> findSubtypes(Annotated a)
{
- JsonSubTypes t = a.getAnnotation(JsonSubTypes.class);
+ JsonSubTypes t = _findAnnotation(a, JsonSubTypes.class);
if (t == null) return null;
JsonSubTypes.Type[] types = t.value();
ArrayList<NamedType> result = new ArrayList<NamedType>(types.length);
@@ -274,7 +274,7 @@
@Override
public String findTypeName(AnnotatedClass ac)
{
- JsonTypeName tn = ac.getAnnotation(JsonTypeName.class);
+ JsonTypeName tn = _findAnnotation(ac, JsonTypeName.class);
return (tn == null) ? null : tn.value();
}
@@ -287,7 +287,7 @@
@Override
public Object findSerializer(Annotated a)
{
- JsonSerialize ann = a.getAnnotation(JsonSerialize.class);
+ JsonSerialize ann = _findAnnotation(a, JsonSerialize.class);
if (ann != null) {
Class<? extends JsonSerializer<?>> serClass = ann.using();
if (serClass != JsonSerializer.None.class) {
@@ -299,7 +299,7 @@
* if we need to get raw indicator from other sources need to add
* separate accessor within {@link AnnotationIntrospector} interface.
*/
- JsonRawValue annRaw = a.getAnnotation(JsonRawValue.class);
+ JsonRawValue annRaw = _findAnnotation(a, JsonRawValue.class);
if ((annRaw != null) && annRaw.value()) {
// let's construct instance with nominal type:
Class<?> cls = a.getRawType();
@@ -311,7 +311,7 @@
@Override
public Class<? extends JsonSerializer<?>> findKeySerializer(Annotated a)
{
- JsonSerialize ann = a.getAnnotation(JsonSerialize.class);
+ JsonSerialize ann = _findAnnotation(a, JsonSerialize.class);
if (ann != null) {
Class<? extends JsonSerializer<?>> serClass = ann.keyUsing();
if (serClass != JsonSerializer.None.class) {
@@ -324,7 +324,7 @@
@Override
public Class<? extends JsonSerializer<?>> findContentSerializer(Annotated a)
{
- JsonSerialize ann = a.getAnnotation(JsonSerialize.class);
+ JsonSerialize ann = _findAnnotation(a, JsonSerialize.class);
if (ann != null) {
Class<? extends JsonSerializer<?>> serClass = ann.contentUsing();
if (serClass != JsonSerializer.None.class) {
@@ -337,7 +337,7 @@
@Override
public Object findNullSerializer(Annotated a)
{
- JsonSerialize ann = a.getAnnotation(JsonSerialize.class);
+ JsonSerialize ann = _findAnnotation(a, JsonSerialize.class);
if (ann != null) {
Class<? extends JsonSerializer<?>> serClass = ann.nullsUsing();
if (serClass != JsonSerializer.None.class) {
@@ -350,11 +350,11 @@
@Override
public JsonInclude.Include findSerializationInclusion(Annotated a, JsonInclude.Include defValue)
{
- JsonInclude inc = a.getAnnotation(JsonInclude.class);
+ JsonInclude inc = _findAnnotation(a, JsonInclude.class);
if (inc != null) {
return inc.value();
}
- JsonSerialize ann = a.getAnnotation(JsonSerialize.class);
+ JsonSerialize ann = _findAnnotation(a, JsonSerialize.class);
if (ann != null) {
@SuppressWarnings("deprecation")
JsonSerialize.Inclusion i2 = ann.include();
@@ -375,60 +375,67 @@
}
@Override
+ public JsonInclude.Include findSerializationInclusionForContent(Annotated a, JsonInclude.Include defValue)
+ {
+ JsonInclude inc = _findAnnotation(a, JsonInclude.class);
+ return (inc == null) ? defValue : inc.content();
+ }
+
+ @Override
public Class<?> findSerializationType(Annotated am)
{
- JsonSerialize ann = am.getAnnotation(JsonSerialize.class);
+ JsonSerialize ann = _findAnnotation(am, JsonSerialize.class);
return (ann == null) ? null : _classIfExplicit(ann.as());
}
@Override
public Class<?> findSerializationKeyType(Annotated am, JavaType baseType)
{
- JsonSerialize ann = am.getAnnotation(JsonSerialize.class);
+ JsonSerialize ann = _findAnnotation(am, JsonSerialize.class);
return (ann == null) ? null : _classIfExplicit(ann.keyAs());
}
@Override
public Class<?> findSerializationContentType(Annotated am, JavaType baseType)
{
- JsonSerialize ann = am.getAnnotation(JsonSerialize.class);
+ JsonSerialize ann = _findAnnotation(am, JsonSerialize.class);
return (ann == null) ? null : _classIfExplicit(ann.contentAs());
}
@Override
public JsonSerialize.Typing findSerializationTyping(Annotated a)
{
- JsonSerialize ann = a.getAnnotation(JsonSerialize.class);
+ JsonSerialize ann = _findAnnotation(a, JsonSerialize.class);
return (ann == null) ? null : ann.typing();
}
@Override
public Object findSerializationConverter(Annotated a) {
- JsonSerialize ann = a.getAnnotation(JsonSerialize.class);
+ JsonSerialize ann = _findAnnotation(a, JsonSerialize.class);
return (ann == null) ? null : _classIfExplicit(ann.converter(), Converter.None.class);
}
@Override
public Object findSerializationContentConverter(AnnotatedMember a) {
- JsonSerialize ann = a.getAnnotation(JsonSerialize.class);
+ JsonSerialize ann = _findAnnotation(a, JsonSerialize.class);
return (ann == null) ? null : _classIfExplicit(ann.contentConverter(), Converter.None.class);
}
@Override
public Class<?>[] findViews(Annotated a)
{
- JsonView ann = a.getAnnotation(JsonView.class);
+ JsonView ann = _findAnnotation(a, JsonView.class);
return (ann == null) ? null : ann.value();
}
@Override
public Boolean isTypeId(AnnotatedMember member) {
- return member.hasAnnotation(JsonTypeId.class);
+ return _hasAnnotation(member, JsonTypeId.class);
}
@Override
public ObjectIdInfo findObjectIdInfo(Annotated ann) {
- JsonIdentityInfo info = ann.getAnnotation(JsonIdentityInfo.class);
+ JsonIdentityInfo info = _findAnnotation(ann, JsonIdentityInfo.class);
if (info == null || info.generator() == ObjectIdGenerators.None.class) {
return null;
}
@@ -439,7 +446,7 @@
@Override
public ObjectIdInfo findObjectReferenceInfo(Annotated ann, ObjectIdInfo objectIdInfo) {
- JsonIdentityReference ref = ann.getAnnotation(JsonIdentityReference.class);
+ JsonIdentityReference ref = _findAnnotation(ann, JsonIdentityReference.class);
if (ref != null) {
objectIdInfo = objectIdInfo.withAlwaysAsId(ref.alwaysAsId());
}
@@ -447,22 +454,22 @@
}
@Override
- public JsonFormat.Value findFormat(Annotated annotated) {
- JsonFormat ann = annotated.getAnnotation(JsonFormat.class);
- return (ann == null) ? null : new JsonFormat.Value(ann);
+ public JsonFormat.Value findFormat(Annotated ann) {
+ JsonFormat f = _findAnnotation(ann, JsonFormat.class);
+ return (f == null) ? null : new JsonFormat.Value(f);
}
@Override
- public String findPropertyDescription(Annotated annotated) {
- JsonPropertyDescription desc = annotated.getAnnotation(JsonPropertyDescription.class);
+ public String findPropertyDescription(Annotated ann) {
+ JsonPropertyDescription desc = _findAnnotation(ann, JsonPropertyDescription.class);
return (desc == null) ? null : desc.value();
}
@Override
- public Integer findPropertyIndex(Annotated annotated) {
- JsonProperty ann = annotated.getAnnotation(JsonProperty.class);
- if (ann != null) {
- int ix = ann.index();
+ public Integer findPropertyIndex(Annotated ann) {
+ JsonProperty prop = _findAnnotation(ann, JsonProperty.class);
+ if (prop != null) {
+ int ix = prop.index();
if (ix != JsonProperty.INDEX_UNKNOWN) {
return Integer.valueOf(ix);
}
@@ -485,7 +492,7 @@
@Override
public String[] findSerializationPropertyOrder(AnnotatedClass ac) {
- JsonPropertyOrder order = ac.getAnnotation(JsonPropertyOrder.class);
+ JsonPropertyOrder order = _findAnnotation(ac, JsonPropertyOrder.class);
return (order == null) ? null : order.value();
}
@@ -501,7 +508,7 @@
}
private final Boolean _findSortAlpha(Annotated ann) {
- JsonPropertyOrder order = ann.getAnnotation(JsonPropertyOrder.class);
+ JsonPropertyOrder order = _findAnnotation(ann, JsonPropertyOrder.class);
return (order == null) ? null : order.alphabetic();
}
@@ -516,14 +523,14 @@
{
String name = null;
- JsonGetter jg = a.getAnnotation(JsonGetter.class);
+ JsonGetter jg = _findAnnotation(a, JsonGetter.class);
if (jg != null) {
name = jg.value();
} else {
- JsonProperty pann = a.getAnnotation(JsonProperty.class);
+ JsonProperty pann = _findAnnotation(a, JsonProperty.class);
if (pann != null) {
name = pann.value();
- } else if (a.hasAnnotation(JsonSerialize.class) || a.hasAnnotation(JsonView.class)) {
+ } else if (_hasAnnotation(a, JsonSerialize.class) || _hasAnnotation(a, JsonView.class)) {
name = "";
} else {
return null;
@@ -537,7 +544,7 @@
@Override
public boolean hasAsValueAnnotation(AnnotatedMethod am) {
- JsonValue ann = am.getAnnotation(JsonValue.class);
+ JsonValue ann = _findAnnotation(am, JsonValue.class);
// value of 'false' means disabled...
return (ann != null && ann.value());
}
@@ -551,7 +558,7 @@
@Override
public Class<? extends JsonDeserializer<?>> findDeserializer(Annotated a)
{
- JsonDeserialize ann = a.getAnnotation(JsonDeserialize.class);
+ JsonDeserialize ann = _findAnnotation(a, JsonDeserialize.class);
if (ann != null) {
Class<? extends JsonDeserializer<?>> deserClass = ann.using();
if (deserClass != JsonDeserializer.None.class) {
@@ -564,7 +571,7 @@
@Override
public Class<? extends KeyDeserializer> findKeyDeserializer(Annotated a)
{
- JsonDeserialize ann = a.getAnnotation(JsonDeserialize.class);
+ JsonDeserialize ann = _findAnnotation(a, JsonDeserialize.class);
if (ann != null) {
Class<? extends KeyDeserializer> deserClass = ann.keyUsing();
if (deserClass != KeyDeserializer.None.class) {
@@ -577,7 +584,7 @@
@Override
public Class<? extends JsonDeserializer<?>> findContentDeserializer(Annotated a)
{
- JsonDeserialize ann = a.getAnnotation(JsonDeserialize.class);
+ JsonDeserialize ann = _findAnnotation(a, JsonDeserialize.class);
if (ann != null) {
Class<? extends JsonDeserializer<?>> deserClass = ann.contentUsing();
if (deserClass != JsonDeserializer.None.class) {
@@ -589,34 +596,34 @@
@Override
public Class<?> findDeserializationType(Annotated am, JavaType baseType) {
- JsonDeserialize ann = am.getAnnotation(JsonDeserialize.class);
+ JsonDeserialize ann = _findAnnotation(am, JsonDeserialize.class);
return (ann == null) ? null : _classIfExplicit(ann.as());
}
@Override
public Class<?> findDeserializationKeyType(Annotated am, JavaType baseKeyType) {
- JsonDeserialize ann = am.getAnnotation(JsonDeserialize.class);
+ JsonDeserialize ann = _findAnnotation(am, JsonDeserialize.class);
return (ann == null) ? null : _classIfExplicit(ann.keyAs());
}
@Override
public Class<?> findDeserializationContentType(Annotated am, JavaType baseContentType)
{
- JsonDeserialize ann = am.getAnnotation(JsonDeserialize.class);
+ JsonDeserialize ann = _findAnnotation(am, JsonDeserialize.class);
return (ann == null) ? null : _classIfExplicit(ann.contentAs());
}
@Override
public Object findDeserializationConverter(Annotated a)
{
- JsonDeserialize ann = a.getAnnotation(JsonDeserialize.class);
+ JsonDeserialize ann = _findAnnotation(a, JsonDeserialize.class);
return (ann == null) ? null : _classIfExplicit(ann.converter(), Converter.None.class);
}
@Override
public Object findDeserializationContentConverter(AnnotatedMember a)
{
- JsonDeserialize ann = a.getAnnotation(JsonDeserialize.class);
+ JsonDeserialize ann = _findAnnotation(a, JsonDeserialize.class);
return (ann == null) ? null : _classIfExplicit(ann.contentConverter(), Converter.None.class);
}
@@ -629,7 +636,7 @@
@Override
public Object findValueInstantiator(AnnotatedClass ac)
{
- JsonValueInstantiator ann = ac.getAnnotation(JsonValueInstantiator.class);
+ JsonValueInstantiator ann = _findAnnotation(ac, JsonValueInstantiator.class);
// no 'null' marker yet, so:
return (ann == null) ? null : ann.value();
}
@@ -637,14 +644,14 @@
@Override
public Class<?> findPOJOBuilder(AnnotatedClass ac)
{
- JsonDeserialize ann = ac.getAnnotation(JsonDeserialize.class);
+ JsonDeserialize ann = _findAnnotation(ac, JsonDeserialize.class);
return (ann == null) ? null : _classIfExplicit(ann.builder());
}
@Override
public JsonPOJOBuilder.Value findPOJOBuilderConfig(AnnotatedClass ac)
{
- JsonPOJOBuilder ann = ac.getAnnotation(JsonPOJOBuilder.class);
+ JsonPOJOBuilder ann = _findAnnotation(ac, JsonPOJOBuilder.class);
return (ann == null) ? null : new JsonPOJOBuilder.Value(ann);
}
@@ -661,22 +668,22 @@
// @JsonSetter has precedence over @JsonProperty, being more specific
// @JsonDeserialize implies that there is a property, but no name
- JsonSetter js = a.getAnnotation(JsonSetter.class);
+ JsonSetter js = _findAnnotation(a, JsonSetter.class);
if (js != null) {
name = js.value();
} else {
- JsonProperty pann = a.getAnnotation(JsonProperty.class);
+ JsonProperty pann = _findAnnotation(a, JsonProperty.class);
if (pann != null) {
name = pann.value();
/* 22-Apr-2014, tatu: Should figure out a better way to do this, but
* it's actually bit tricky to do it more efficiently (meta-annotations
* add more lookups; AnnotationMap costs etc)
*/
- } else if (a.hasAnnotation(JsonDeserialize.class)
- || a.hasAnnotation(JsonView.class)
- || a.hasAnnotation(JsonUnwrapped.class) // [#442]
- || a.hasAnnotation(JsonBackReference.class)
- || a.hasAnnotation(JsonManagedReference.class)) {
+ } else if (_hasAnnotation(a, JsonDeserialize.class)
+ || _hasAnnotation(a, JsonView.class)
+ || _hasAnnotation(a, JsonUnwrapped.class) // [#442]
+ || _hasAnnotation(a, JsonBackReference.class)
+ || _hasAnnotation(a, JsonManagedReference.class)) {
name = "";
} else {
return null;
@@ -695,7 +702,7 @@
* if needs to be ignored (and if so, is handled prior
* to this method getting called)
*/
- return am.hasAnnotation(JsonAnySetter.class);
+ return _hasAnnotation(am, JsonAnySetter.class);
}
@Override
@@ -704,7 +711,7 @@
/* No dedicated disabling; regular @JsonIgnore used
* if needs to be ignored (handled separately
*/
- return am.hasAnnotation(JsonAnyGetter.class);
+ return _hasAnnotation(am, JsonAnyGetter.class);
}
@Override
@@ -714,7 +721,13 @@
* if needs to be ignored (and if so, is handled prior
* to this method getting called)
*/
- return a.hasAnnotation(JsonCreator.class);
+ return _hasAnnotation(a, JsonCreator.class);
+ }
+
+ @Override
+ public JsonCreator.Mode findCreatorBinding(Annotated a) {
+ JsonCreator ann = _findAnnotation(a, JsonCreator.class);
+ return (ann == null) ? null : ann.mode();
}
/*
@@ -725,7 +738,7 @@
protected boolean _isIgnorable(Annotated a)
{
- JsonIgnore ann = a.getAnnotation(JsonIgnore.class);
+ JsonIgnore ann = _findAnnotation(a, JsonIgnore.class);
return (ann != null && ann.value());
}
@@ -745,13 +758,14 @@
* Helper method called to construct and initialize instance of {@link TypeResolverBuilder}
* if given annotated element indicates one is needed.
*/
+ @SuppressWarnings("deprecation")
protected TypeResolverBuilder<?> _findTypeResolver(MapperConfig<?> config,
Annotated ann, JavaType baseType)
{
// First: maybe we have explicit type resolver?
TypeResolverBuilder<?> b;
- JsonTypeInfo info = ann.getAnnotation(JsonTypeInfo.class);
- JsonTypeResolver resAnn = ann.getAnnotation(JsonTypeResolver.class);
+ JsonTypeInfo info = _findAnnotation(ann, JsonTypeInfo.class);
+ JsonTypeResolver resAnn = _findAnnotation(ann, JsonTypeResolver.class);
if (resAnn != null) {
if (info == null) {
@@ -773,7 +787,7 @@
b = _constructStdTypeResolverBuilder();
}
// Does it define a custom type id resolver?
- JsonTypeIdResolver idResInfo = ann.getAnnotation(JsonTypeIdResolver.class);
+ JsonTypeIdResolver idResInfo = _findAnnotation(ann, JsonTypeIdResolver.class);
TypeIdResolver idRes = (idResInfo == null) ? null
: config.typeIdResolverInstance(ann, idResInfo.value());
if (idRes != null) { // [JACKSON-359]
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/NopAnnotationIntrospector.java b/src/main/java/com/fasterxml/jackson/databind/introspect/NopAnnotationIntrospector.java
index bd7881e..f42f9c0 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/NopAnnotationIntrospector.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/NopAnnotationIntrospector.java
@@ -26,7 +26,7 @@
@Override
public Version version() {
return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION;
- }
+ }
};
@Override
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 4fc330d..f7c0028 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java
@@ -467,11 +467,15 @@
if (!expl) {
if (impl.isEmpty()) {
/* Important: if neither implicit nor explicit name, can not make use
- * of this creator paramter -- may or may not be a problem, verified
+ * of this creator parameter -- may or may not be a problem, verified
* at a later point.
*/
return;
}
+ // Also: if this occurs, there MUST be explicit annotation on creator itself
+ if (!_annotationIntrospector.hasCreatorAnnotation(param.getOwner())) {
+ return;
+ }
pn = new PropertyName(impl);
}
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 54db8a8..e67c6ca 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertyBuilder.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertyBuilder.java
@@ -3,6 +3,7 @@
import java.util.*;
import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.util.EmptyIterator;
/**
* Helper class used for aggregating information about a single
@@ -53,13 +54,6 @@
_annotationIntrospector = annotationIntrospector;
_forSerialization = forSerialization;
}
-
- @Deprecated // since 2.3
- public POJOPropertyBuilder(String simpleInternalName,
- AnnotationIntrospector annotationIntrospector, boolean forSerialization)
- {
- this(new PropertyName(simpleInternalName), annotationIntrospector, forSerialization);
- }
public POJOPropertyBuilder(POJOPropertyBuilder src, PropertyName newName)
{
@@ -79,12 +73,6 @@
/**********************************************************
*/
- @Deprecated // since 2.3
- @Override
- public POJOPropertyBuilder withName(String newName) {
- return withSimpleName(newName);
- }
-
@Override
public POJOPropertyBuilder withName(PropertyName newName) {
return new POJOPropertyBuilder(this, newName);
@@ -359,6 +347,14 @@
} while (curr != null);
return _ctorParameters.value;
}
+
+ @Override
+ public Iterator<AnnotatedParameter> getConstructorParameters() {
+ if (_ctorParameters == null) {
+ return EmptyIterator.instance();
+ }
+ return new MemberIterator<AnnotatedParameter>(_ctorParameters);
+ }
@Override
public AnnotatedMember getAccessor()
@@ -665,6 +661,15 @@
}
/**
+ * Mutator that will simply drop any constructor parameters property may have.
+ *
+ * @since 2.5
+ */
+ public void removeConstructors() {
+ _ctorParameters = null;
+ }
+
+ /**
* Method called to trim unnecessary entries, such as implicit
* getter if there is an explict one available. This is important
* for later stages, to avoid unnecessary conflicts.
@@ -981,6 +986,38 @@
private interface WithMember<T> {
public T withMember(AnnotatedMember member);
}
+
+ /**
+ * @since 2.5
+ */
+ protected static class MemberIterator<T extends AnnotatedMember>
+ implements Iterator<T>
+ {
+ private Linked<T> next;
+
+ public MemberIterator(Linked<T> first) {
+ next = first;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return (next != null);
+ }
+
+ @Override
+ public T next() {
+ if (next == null) throw new NoSuchElementException();
+ T result = next.value;
+ next = next.next;
+ return result;
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ }
/**
* Node used for creating simple linked lists to efficiently store small sets
@@ -1063,7 +1100,7 @@
* Method called to append given node(s) at the end of this
* node chain.
*/
- private Linked<T> append(Linked<T> appendable) {
+ protected Linked<T> append(Linked<T> appendable) {
if (next == null) {
return withNext(appendable);
}
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 3f03e27..0a55496 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/VisibilityChecker.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/VisibilityChecker.java
@@ -16,8 +16,6 @@
*<p>
* Note on type declaration: funky recursive type is necessary to
* support builder/fluent pattern.
- *
- * @author tatu
*/
public interface VisibilityChecker<T extends VisibilityChecker<T>>
{
@@ -163,7 +161,7 @@
implements VisibilityChecker<Std>,
java.io.Serializable
{
- private static final long serialVersionUID = -7073939237187922755L;
+ private static final long serialVersionUID = 1;
/**
* This is the canonical base instance, configured with default
@@ -208,7 +206,7 @@
}
/**
- * Costructor that will assign given visibility value for all
+ * Constructor that will assign given visibility value for all
* properties.
*
* @param v level to use for all property types
@@ -238,158 +236,158 @@
/********************************************************
*/
- @Override
- public Std with(JsonAutoDetect ann)
- {
- 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());
- }
- return curr;
- }
-
- @Override
- public Std with(Visibility v)
- {
- if (v == Visibility.DEFAULT) {
- return DEFAULT;
+ @Override
+ public Std with(JsonAutoDetect ann)
+ {
+ 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());
+ }
+ return curr;
}
- return new Std(v);
- }
- @Override
- public Std withVisibility(PropertyAccessor method, Visibility v)
- {
- switch (method) {
- case GETTER:
- return withGetterVisibility(v);
- case SETTER:
- return withSetterVisibility(v);
- case CREATOR:
- return withCreatorVisibility(v);
- case FIELD:
- return withFieldVisibility(v);
- case IS_GETTER:
- return withIsGetterVisibility(v);
+ @Override
+ public Std with(Visibility v)
+ {
+ if (v == Visibility.DEFAULT) {
+ return DEFAULT;
+ }
+ return new Std(v);
+ }
+
+ @Override
+ public Std withVisibility(PropertyAccessor method, Visibility v)
+ {
+ switch (method) {
+ case GETTER:
+ return withGetterVisibility(v);
+ case SETTER:
+ return withSetterVisibility(v);
+ case CREATOR:
+ return withCreatorVisibility(v);
+ case FIELD:
+ return withFieldVisibility(v);
+ case IS_GETTER:
+ return withIsGetterVisibility(v);
case ALL:
return with(v);
- //case NONE:
- default:
- // break;
- return this;
- }
- }
+ //case NONE:
+ default:
+ // break;
+ return this;
+ }
+ }
- @Override
- public Std withGetterVisibility(Visibility v) {
- if (v == Visibility.DEFAULT) v = DEFAULT._getterMinLevel;
+ @Override
+ public Std withGetterVisibility(Visibility v) {
+ if (v == Visibility.DEFAULT) v = DEFAULT._getterMinLevel;
if (_getterMinLevel == v) return this;
- return new Std(v, _isGetterMinLevel, _setterMinLevel, _creatorMinLevel, _fieldMinLevel);
- }
+ return new Std(v, _isGetterMinLevel, _setterMinLevel, _creatorMinLevel, _fieldMinLevel);
+ }
- @Override
+ @Override
public Std withIsGetterVisibility(Visibility v) {
if (v == Visibility.DEFAULT) v = DEFAULT._isGetterMinLevel;
if (_isGetterMinLevel == v) return this;
return new Std(_getterMinLevel, v, _setterMinLevel, _creatorMinLevel, _fieldMinLevel);
}
+
+ @Override
+ public Std withSetterVisibility(Visibility v) {
+ if (v == Visibility.DEFAULT) v = DEFAULT._setterMinLevel;
+ if (_setterMinLevel == v) return this;
+ return new Std(_getterMinLevel, _isGetterMinLevel, v, _creatorMinLevel, _fieldMinLevel);
+ }
+
+ @Override
+ public Std withCreatorVisibility(Visibility v) {
+ if (v == Visibility.DEFAULT) v = DEFAULT._creatorMinLevel;
+ if (_creatorMinLevel == v) return this;
+ return new Std(_getterMinLevel, _isGetterMinLevel, _setterMinLevel, v, _fieldMinLevel);
+ }
+
+ @Override
+ public Std withFieldVisibility(Visibility v) {
+ if (v == Visibility.DEFAULT) v = DEFAULT._fieldMinLevel;
+ if (_fieldMinLevel == v) return this;
+ return new Std(_getterMinLevel, _isGetterMinLevel, _setterMinLevel, _creatorMinLevel, v);
+ }
- @Override
- public Std withSetterVisibility(Visibility v) {
- if (v == Visibility.DEFAULT) v = DEFAULT._setterMinLevel;
- if (_setterMinLevel == v) return this;
- return new Std(_getterMinLevel, _isGetterMinLevel, v, _creatorMinLevel, _fieldMinLevel);
- }
+ /*
+ /********************************************************
+ /* Public API impl
+ /********************************************************
+ */
- @Override
- public Std withCreatorVisibility(Visibility v) {
- if (v == Visibility.DEFAULT) v = DEFAULT._creatorMinLevel;
- if (_creatorMinLevel == v) return this;
- return new Std(_getterMinLevel, _isGetterMinLevel, _setterMinLevel, v, _fieldMinLevel);
- }
+ @Override
+ public boolean isCreatorVisible(Member m) {
+ return _creatorMinLevel.isVisible(m);
+ }
+
+ @Override
+ public boolean isCreatorVisible(AnnotatedMember m) {
+ return isCreatorVisible(m.getMember());
+ }
- @Override
- public Std withFieldVisibility(Visibility v) {
- if (v == Visibility.DEFAULT) v = DEFAULT._fieldMinLevel;
- if (_fieldMinLevel == v) return this;
- return new Std(_getterMinLevel, _isGetterMinLevel, _setterMinLevel, _creatorMinLevel, v);
- }
-
- /*
- /********************************************************
- /* Public API impl
- /********************************************************
- */
-
- @Override
- public boolean isCreatorVisible(Member m) {
- return _creatorMinLevel.isVisible(m);
- }
-
- @Override
- public boolean isCreatorVisible(AnnotatedMember m) {
- return isCreatorVisible(m.getMember());
- }
+ @Override
+ public boolean isFieldVisible(Field f) {
+ return _fieldMinLevel.isVisible(f);
+ }
+
+ @Override
+ public boolean isFieldVisible(AnnotatedField f) {
+ return isFieldVisible(f.getAnnotated());
+ }
+
+ @Override
+ public boolean isGetterVisible(Method m) {
+ return _getterMinLevel.isVisible(m);
+ }
- @Override
- public boolean isFieldVisible(Field f) {
- return _fieldMinLevel.isVisible(f);
- }
+ @Override
+ public boolean isGetterVisible(AnnotatedMethod m) {
+ return isGetterVisible(m.getAnnotated());
+ }
- @Override
- public boolean isFieldVisible(AnnotatedField f) {
- return isFieldVisible(f.getAnnotated());
- }
+ @Override
+ public boolean isIsGetterVisible(Method m) {
+ return _isGetterMinLevel.isVisible(m);
+ }
- @Override
- public boolean isGetterVisible(Method m) {
- return _getterMinLevel.isVisible(m);
- }
-
- @Override
- public boolean isGetterVisible(AnnotatedMethod m) {
- return isGetterVisible(m.getAnnotated());
- }
-
- @Override
- public boolean isIsGetterVisible(Method m) {
- return _isGetterMinLevel.isVisible(m);
- }
-
- @Override
- public boolean isIsGetterVisible(AnnotatedMethod m) {
- return isIsGetterVisible(m.getAnnotated());
- }
-
- @Override
- public boolean isSetterVisible(Method m) {
- return _setterMinLevel.isVisible(m);
- }
+ @Override
+ public boolean isIsGetterVisible(AnnotatedMethod m) {
+ return isIsGetterVisible(m.getAnnotated());
+ }
- @Override
- public boolean isSetterVisible(AnnotatedMethod m) {
- return isSetterVisible(m.getAnnotated());
- }
+ @Override
+ public boolean isSetterVisible(Method m) {
+ return _setterMinLevel.isVisible(m);
+ }
+
+ @Override
+ public boolean isSetterVisible(AnnotatedMethod m) {
+ return isSetterVisible(m.getAnnotated());
+ }
- /*
- /********************************************************
- /* Standard methods
- /********************************************************
- */
-
- @Override
- public String toString() {
- return new StringBuilder("[Visibility:")
- .append(" getter: ").append(_getterMinLevel)
- .append(", isGetter: ").append(_isGetterMinLevel)
- .append(", setter: ").append(_setterMinLevel)
- .append(", creator: ").append(_creatorMinLevel)
- .append(", field: ").append(_fieldMinLevel)
- .append("]").toString();
- }
+ /*
+ /********************************************************
+ /* Standard methods
+ /********************************************************
+ */
+
+ @Override
+ public String toString() {
+ return new StringBuilder("[Visibility:")
+ .append(" getter: ").append(_getterMinLevel)
+ .append(", isGetter: ").append(_isGetterMinLevel)
+ .append(", setter: ").append(_setterMinLevel)
+ .append(", creator: ").append(_creatorMinLevel)
+ .append(", field: ").append(_fieldMinLevel)
+ .append("]").toString();
+ }
}
}
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 357c1ef..fc3ed16 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeIdResolver.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeIdResolver.java
@@ -1,6 +1,7 @@
package com.fasterxml.jackson.databind.jsontype;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.databind.DatabindContext;
import com.fasterxml.jackson.databind.JavaType;
/**
@@ -63,11 +64,19 @@
* available for some reason.
*/
public String idFromBaseType();
-
+
+ /**
+ * @deprecated since 2.5; call {@link #typeFromId(DatabindContext, String)} instead
+ */
+ @Deprecated // since 2.4
+ 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}
*/
- public JavaType typeFromId(String id);
+ public JavaType typeFromId(DatabindContext context, String id);
/*
/**********************************************************
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 fb6d026..9a06915 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
@@ -21,7 +21,7 @@
extends TypeDeserializerBase
implements java.io.Serializable
{
- private static final long serialVersionUID = 5345570420394408290L;
+ private static final long serialVersionUID = 1L;
public AsArrayTypeDeserializer(JavaType bt, TypeIdResolver idRes,
String typePropertyName, boolean typeIdVisible, Class<?> defaultImpl)
@@ -93,7 +93,12 @@
String typeId = _locateTypeId(jp, ctxt);
JsonDeserializer<Object> deser = _findDeserializer(ctxt, typeId);
// Minor complication: we may need to merge type id in?
- if (_typeIdVisible && jp.getCurrentToken() == JsonToken.START_OBJECT) {
+ if (_typeIdVisible
+ // 06-Oct-2014, tatu: To fix [databind#408], must distinguish between
+ // internal and external properties
+ // TODO: but does it need to be injected in external case? Why not?
+ && !_usesExternalId()
+ && jp.getCurrentToken() == JsonToken.START_OBJECT) {
// but what if there's nowhere to add it in? Error? Or skip? For now, skip.
TokenBuffer tb = new TokenBuffer(null, false);
tb.writeStartObject(); // recreate START_OBJECT
@@ -114,7 +119,7 @@
protected final String _locateTypeId(JsonParser jp, DeserializationContext ctxt) throws IOException
{
if (!jp.isExpectedStartArrayToken()) {
- // [JACKSON-712] Need to allow even more customized handling, if something unexpected seen...
+ // 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();
@@ -133,4 +138,11 @@
}
throw ctxt.wrongTokenException(jp, JsonToken.VALUE_STRING, "need JSON String that contains type id (for subtype of "+baseTypeName()+")");
}
+
+ /**
+ * @since 2.5
+ */
+ protected boolean _usesExternalId() {
+ return false;
+ }
}
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
new file mode 100644
index 0000000..1311916
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsExistingPropertyTypeSerializer.java
@@ -0,0 +1,69 @@
+package com.fasterxml.jackson.databind.jsontype.impl;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.BeanProperty;
+import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
+
+/**
+ * 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)
+ {
+ super(idRes, property, propName);
+ }
+
+ @Override
+ public AsExistingPropertyTypeSerializer forProperty(BeanProperty prop) {
+ return (_property == prop) ? this : new AsExistingPropertyTypeSerializer(this._idResolver, prop, this._typePropertyName);
+ }
+
+ @Override
+ public As getTypeInclusion() { return As.EXISTING_PROPERTY; }
+
+ @Override
+ public void writeTypePrefixForObject(Object value, JsonGenerator jgen) throws IOException
+ {
+ final String typeId = idFromValue(value);
+ if (jgen.canWriteTypeId()) {
+ jgen.writeTypeId(typeId);
+ jgen.writeStartObject();
+ } else {
+ jgen.writeStartObject();
+ }
+ }
+
+ @Override
+ public void writeTypePrefixForObject(Object value, JsonGenerator jgen, Class<?> type) throws IOException
+ {
+ final String typeId = idFromValueAndType(value, type);
+ if (jgen.canWriteTypeId()) {
+ jgen.writeTypeId(typeId);
+ jgen.writeStartObject();
+ } else {
+ jgen.writeStartObject();
+ }
+ }
+
+ @Override
+ public void writeCustomTypePrefixForObject(Object value, JsonGenerator jgen, String typeId) throws IOException
+ {
+ if (jgen.canWriteTypeId()) {
+ jgen.writeTypeId(typeId);
+ jgen.writeStartObject();
+ } else {
+ jgen.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 4352564..3bd4539 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
@@ -39,4 +39,10 @@
@Override
public As getTypeInclusion() { return As.EXTERNAL_PROPERTY; }
+
+ // yes, very important distinction...
+ @Override
+ protected boolean _usesExternalId() {
+ return true;
+ }
}
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 ffbddc8..484cb0b 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,25 @@
{
private static final long serialVersionUID = 1L;
+ protected final As _inclusion;
+
public AsPropertyTypeDeserializer(JavaType bt, TypeIdResolver idRes,
String typePropertyName, boolean typeIdVisible, Class<?> defaultImpl)
{
+ this(bt, idRes, typePropertyName, typeIdVisible, defaultImpl, As.PROPERTY);
+ }
+
+ public AsPropertyTypeDeserializer(JavaType bt, TypeIdResolver idRes,
+ String typePropertyName, boolean typeIdVisible, Class<?> defaultImpl,
+ As inclusion)
+ {
super(bt, idRes, typePropertyName, typeIdVisible, defaultImpl);
+ _inclusion = inclusion;
}
public AsPropertyTypeDeserializer(AsPropertyTypeDeserializer src, BeanProperty property) {
super(src, property);
+ _inclusion = src._inclusion;
}
@Override
@@ -40,7 +51,7 @@
}
@Override
- public As getTypeInclusion() { return As.PROPERTY; }
+ public As getTypeInclusion() { return _inclusion; }
/**
* This is the trickiest thing to handle, since property we are looking
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 b299e7f..fe6753d 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
@@ -66,7 +66,6 @@
return this;
}
- @SuppressWarnings("incomplete-switch")
@Override
public TypeSerializer buildTypeSerializer(SerializationConfig config,
JavaType baseType, Collection<NamedType> subtypes)
@@ -84,11 +83,19 @@
case EXTERNAL_PROPERTY:
return new AsExternalTypeSerializer(idRes, null,
_typeProperty);
+ case EXISTING_PROPERTY:
+ // as per [#528]
+ return new AsExistingPropertyTypeSerializer(idRes, null, _typeProperty);
}
throw new IllegalStateException("Do not know how to construct standard type serializer for inclusion type: "+_includeAs);
}
- @SuppressWarnings("incomplete-switch")
+ // as per [#368]
+ // removed when fix [#528]
+ //private IllegalArgumentException _noExisting() {
+ // return new IllegalArgumentException("Inclusion type "+_includeAs+" not yet supported");
+ //}
+
@Override
public TypeDeserializer buildTypeDeserializer(DeserializationConfig config,
JavaType baseType, Collection<NamedType> subtypes)
@@ -103,8 +110,9 @@
return new AsArrayTypeDeserializer(baseType, idRes,
_typeProperty, _typeIdVisible, _defaultImpl);
case PROPERTY:
+ case EXISTING_PROPERTY: // as per [#528] same class as PROPERTY
return new AsPropertyTypeDeserializer(baseType, idRes,
- _typeProperty, _typeIdVisible, _defaultImpl);
+ _typeProperty, _typeIdVisible, _defaultImpl, _includeAs);
case WRAPPER_OBJECT:
return new AsWrapperTypeDeserializer(baseType, idRes,
_typeProperty, _typeIdVisible, _defaultImpl);
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 47b6092..4a4922e 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
@@ -154,12 +154,7 @@
* for base class, not via interface. Later on we can add this
* to the interface, assuming deprecation at base class helps.
*/
- JavaType type;
- if (_idResolver instanceof TypeIdResolverBase) {
- type = ((TypeIdResolverBase) _idResolver).typeFromId(ctxt, typeId);
- } else {
- type = _idResolver.typeFromId(typeId);
- }
+ JavaType type = _idResolver.typeFromId(ctxt, typeId);
if (type == null) {
// As per [JACKSON-614], use the default impl if no type id available:
deser = _findDefaultImplDeserializer(ctxt);
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 296b19f..771283a 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
@@ -54,7 +54,9 @@
*/
@Deprecated
@Override
- public abstract JavaType typeFromId(String id);
+ public JavaType typeFromId(String id) {
+ return typeFromId(null, id);
+ }
/**
* New method, replacement for {@link #typeFromId(String)}, which is given
@@ -65,6 +67,7 @@
*
* @since 2.3
*/
+ @Override
public JavaType typeFromId(DatabindContext context, String id) {
return typeFromId(id);
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeSerializerBase.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeSerializerBase.java
index 38ac022..5397946 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeSerializerBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeSerializerBase.java
@@ -35,9 +35,7 @@
protected String idFromValue(Object value) {
String id = _idResolver.idFromValue(value);
if (id == null) {
- String typeDesc = (value == null) ? "NULL" : value.getClass().getName();
- throw new IllegalArgumentException("Can not resolve type id for "
- +typeDesc+" (using "+_idResolver.getClass().getName()+")");
+ handleMissingId(value);
}
return id;
}
@@ -45,10 +43,17 @@
protected String idFromValueAndType(Object value, Class<?> type) {
String id = _idResolver.idFromValueAndType(value, type);
if (id == null) {
- String typeDesc = (value == null) ? "NULL" : value.getClass().getName();
- throw new IllegalArgumentException("Can not resolve type id for "
- +typeDesc+" (using "+_idResolver.getClass().getName()+")");
+ handleMissingId(value);
}
return id;
}
+
+ // As per [databind#633], maybe better just not do anything...
+ protected void handleMissingId(Object value) {
+ /*
+ String typeDesc = (value == null) ? "NULL" : value.getClass().getName();
+ throw new IllegalArgumentException("Can not resolve type id for "
+ +typeDesc+" (using "+_idResolver.getClass().getName()+")");
+ */
+ }
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/module/SimpleKeyDeserializers.java b/src/main/java/com/fasterxml/jackson/databind/module/SimpleKeyDeserializers.java
index 9e660c7..531141d 100644
--- a/src/main/java/com/fasterxml/jackson/databind/module/SimpleKeyDeserializers.java
+++ b/src/main/java/com/fasterxml/jackson/databind/module/SimpleKeyDeserializers.java
@@ -22,7 +22,7 @@
public class SimpleKeyDeserializers
implements KeyDeserializers, java.io.Serializable // since 2.1
{
- private static final long serialVersionUID = -6786398737835438187L;
+ private static final long serialVersionUID = 1L;
protected HashMap<ClassKey,KeyDeserializer> _classMappings = null;
diff --git a/src/main/java/com/fasterxml/jackson/databind/module/SimpleModule.java b/src/main/java/com/fasterxml/jackson/databind/module/SimpleModule.java
index 6a33afb..f6485b8 100644
--- a/src/main/java/com/fasterxml/jackson/databind/module/SimpleModule.java
+++ b/src/main/java/com/fasterxml/jackson/databind/module/SimpleModule.java
@@ -6,7 +6,6 @@
import java.util.Map;
import com.fasterxml.jackson.core.Version;
-
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.deser.ValueInstantiator;
@@ -30,8 +29,7 @@
extends Module
implements java.io.Serializable
{
- // at 2.4.0:
- private static final long serialVersionUID = -8905749147637667249L;
+ private static final long serialVersionUID = 1L; // 2.5.0
protected final String _name;
protected final Version _version;
@@ -94,8 +92,11 @@
* use actual name and version number information.
*/
public SimpleModule() {
- // when passing 'this', can not chain constructors...
- _name = "SimpleModule-"+System.identityHashCode(this);
+ // can't chain when making reference to 'this'
+ // note: generate different name for direct instantiation, sub-classing
+ _name = (getClass() == SimpleModule.class) ?
+ "SimpleModule-"+System.identityHashCode(this)
+ : getClass().getName();
_version = Version.unknownVersion();
}
@@ -162,6 +163,19 @@
_serializers = new SimpleSerializers(serializers);
}
}
+
+ /**
+ * Since instances are likely to be custom, implementation returns
+ * <code>null</code> if (but only if!) this class is directly instantiated;
+ * but class name (default impl) for sub-classes.
+ */
+ @Override
+ public Object getTypeId() {
+ if (getClass() == SimpleModule.class) {
+ return null;
+ }
+ return super.getTypeId();
+ }
/*
/**********************************************************
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 9704471..e32efba 100644
--- a/src/main/java/com/fasterxml/jackson/databind/node/ArrayNode.java
+++ b/src/main/java/com/fasterxml/jackson/databind/node/ArrayNode.java
@@ -28,7 +28,7 @@
protected JsonNode _at(JsonPointer ptr) {
return get(ptr.getMatchingIndex());
}
-
+
// note: co-variant to allow caller-side type safety
@SuppressWarnings("unchecked")
@Override
@@ -96,14 +96,13 @@
@Override
public void serialize(JsonGenerator jg, SerializerProvider provider) throws IOException, JsonProcessingException
{
- jg.writeStartArray();
- for (JsonNode n : _children) {
- /* 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)n).serialize(jg, provider);
+ final List<JsonNode> c = _children;
+ final int size = c.size();
+ jg.writeStartArray(size);
+ for (int i = 0; i < size; ++i) { // we'll typically have array list
+ // Can we trust that all nodes will always extend BaseJsonNode? Or if not,
+ // at least implement JsonSerializable? Let's start with former, change if must
+ ((BaseJsonNode) c.get(i)).serialize(jg, provider);
}
jg.writeEndArray();
}
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 4436dab..6a1d1c9 100644
--- a/src/main/java/com/fasterxml/jackson/databind/node/JsonNodeFactory.java
+++ b/src/main/java/com/fasterxml/jackson/databind/node/JsonNodeFactory.java
@@ -165,7 +165,7 @@
public ValueNode numberNode(Integer value) {
return (value == null) ? nullNode() : IntNode.valueOf(value.intValue());
}
-
+
/**
* Factory method for getting an instance of JSON numeric value
* that expresses given 64-bit integer value
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 976ce85..03ec5d8 100644
--- a/src/main/java/com/fasterxml/jackson/databind/node/ObjectNode.java
+++ b/src/main/java/com/fasterxml/jackson/databind/node/ObjectNode.java
@@ -599,6 +599,10 @@
/**
* Method for setting value of a field to specified numeric value.
+ * The underlying {@link JsonNode} that will be added is constructed
+ * using {@link JsonNodeFactory#numberNode(int)}, and may be
+ * "smaller" (like {@link ShortNode}) in cases where value fits within
+ * range of a smaller integral numeric value.
*
* @return This node (to allow chaining)
*/
@@ -619,6 +623,10 @@
/**
* Method for setting value of a field to specified numeric value.
+ * The underlying {@link JsonNode} that will be added is constructed
+ * using {@link JsonNodeFactory#numberNode(long)}, and may be
+ * "smaller" (like {@link IntNode}) in cases where value fits within
+ * range of a smaller integral numeric value.
*
* @return This node (to allow chaining)
*/
@@ -627,8 +635,14 @@
}
/**
- * Alternative method that we need to avoid bumping into NPE issues
- * with auto-unboxing.
+ * Method for setting value of a field to specified numeric value.
+ * The underlying {@link JsonNode} that will be added is constructed
+ * using {@link JsonNodeFactory#numberNode(Long)}, and may be
+ * "smaller" (like {@link IntNode}) in cases where value fits within
+ * range of a smaller integral numeric value.
+ * <p>
+ * Note that this is alternative to {@link #put(String, long)} needed to avoid
+ * bumping into NPE issues with auto-unboxing.
*
* @return This node (to allow chaining)
*/
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 bfa8518..3bfa7b2 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/AnyGetterWriter.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/AnyGetterWriter.java
@@ -60,7 +60,8 @@
throw new JsonMappingException("Value returned by 'any-getter' ("
+_accessor.getName()+"()) not java.util.Map but "+value.getClass().getName());
}
- _serializer.serializeFilteredFields((Map<?,?>) value, jgen, provider, filter);
+ // 19-Oct-2014, tatu: Should we try to support @JsonInclude options here?
+ _serializer.serializeFilteredFields((Map<?,?>) value, jgen, provider, filter, null);
}
// Note: NOT part of ResolvableSerializer...
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 ba10386..cdf3eec 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,8 @@
import java.util.*;
import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.cfg.SerializerFactoryConfig;
@@ -20,10 +22,7 @@
import com.fasterxml.jackson.databind.ser.impl.*;
import com.fasterxml.jackson.databind.ser.std.*;
import com.fasterxml.jackson.databind.type.*;
-import com.fasterxml.jackson.databind.util.ClassUtil;
-import com.fasterxml.jackson.databind.util.Converter;
-import com.fasterxml.jackson.databind.util.EnumValues;
-import com.fasterxml.jackson.databind.util.TokenBuffer;
+import com.fasterxml.jackson.databind.util.*;
/**
* Factory class that can provide serializers for standard JDK classes,
@@ -78,9 +77,8 @@
_concrete.put(Boolean.class.getName(), new BooleanSerializer(false));
// Other numbers, more complicated
- final JsonSerializer<?> ns = NumberSerializer.instance;
- _concrete.put(BigInteger.class.getName(), ns);
- _concrete.put(BigDecimal.class.getName(), ns);
+ _concrete.put(BigInteger.class.getName(), new NumberSerializer(BigInteger.class));
+ _concrete.put(BigDecimal.class.getName(),new NumberSerializer(BigDecimal.class));
// Other discrete non-container types:
// First, Date/Time zoo:
@@ -368,6 +366,17 @@
if (java.util.Date.class.isAssignableFrom(raw)) {
return DateSerializer.instance;
}
+ if (Map.Entry.class.isAssignableFrom(raw)) {
+ JavaType kt, vt;
+ JavaType[] params = prov.getTypeFactory().findTypeParameters(type, Map.Entry.class);
+ if (params == null || params.length != 2) { // assume that if we don't get 2, they are wrong...
+ kt = vt = TypeFactory.unknownType();
+ } else {
+ kt = params[0];
+ vt = params[1];
+ }
+ return buildMapEntrySerializer(prov.getConfig(), type, beanDesc, staticTyping, kt, vt);
+ }
if (ByteBuffer.class.isAssignableFrom(raw)) {
return new ByteBufferSerializer();
}
@@ -429,12 +438,17 @@
{
Class<?> type = javaType.getRawClass();
- // These need to be in decreasing order of specificity...
if (Iterator.class.isAssignableFrom(type)) {
- return buildIteratorSerializer(config, javaType, beanDesc, staticTyping);
+ JavaType[] params = config.getTypeFactory().findTypeParameters(javaType, Iterator.class);
+ JavaType vt = (params == null || params.length != 1) ?
+ TypeFactory.unknownType() : params[0];
+ return buildIteratorSerializer(config, javaType, beanDesc, staticTyping, vt);
}
if (Iterable.class.isAssignableFrom(type)) {
- return buildIterableSerializer(config, javaType, beanDesc, staticTyping);
+ JavaType[] params = config.getTypeFactory().findTypeParameters(javaType, Iterable.class);
+ JavaType vt = (params == null || params.length != 1) ?
+ TypeFactory.unknownType() : params[0];
+ return buildIterableSerializer(config, javaType, beanDesc, staticTyping, vt);
}
if (CharSequence.class.isAssignableFrom(type)) {
return ToStringSerializer.instance;
@@ -723,9 +737,15 @@
} else {
*/
Object filterId = findFilterId(config, beanDesc);
- ser = MapSerializer.construct(config.getAnnotationIntrospector().findPropertiesToIgnore(beanDesc.getClassInfo()),
+ MapSerializer mapSer = MapSerializer.construct(config.getAnnotationIntrospector().findPropertiesToIgnore(beanDesc.getClassInfo()),
type, staticTyping, elementTypeSerializer,
keySerializer, elementValueSerializer, filterId);
+ Object suppressableValue = findSuppressableContentValue(config,
+ type.getContentType(), beanDesc);
+ if (suppressableValue != null) {
+ mapSer = mapSer.withContentInclusion(suppressableValue);
+ }
+ ser = mapSer;
}
// [Issue#120]: Allow post-processing
if (_factoryConfig.hasSerializerModifiers()) {
@@ -736,6 +756,30 @@
return ser;
}
+ /**
+ * @since 2.5
+ */
+ protected Object findSuppressableContentValue(SerializationConfig config,
+ JavaType contentType, BeanDescription beanDesc)
+ throws JsonMappingException
+ {
+ JsonInclude.Include incl = beanDesc.findSerializationInclusionForContent(null);
+
+ if (incl != null) {
+ switch (incl) {
+ case NON_DEFAULT:
+ // 19-Oct-2014, tatu: Not sure what this'd mean; so take it to mean "NON_EMPTY"...
+ incl = JsonInclude.Include.NON_EMPTY;
+ break;
+ default:
+ // all other modes actually good as is, unless we'll find better ways
+ break;
+ }
+ return incl;
+ }
+ return null;
+ }
+
/*
/**********************************************************
/* Factory methods, for Arrays
@@ -792,39 +836,67 @@
/**********************************************************
*/
+ /**
+ * @since 2.5
+ */
protected JsonSerializer<?> buildIteratorSerializer(SerializationConfig config,
- JavaType type, BeanDescription beanDesc,
- boolean staticTyping)
+ JavaType type, BeanDescription beanDesc, boolean staticTyping,
+ JavaType valueType)
throws JsonMappingException
{
- // if there's generic type, it'll be the first contained type
- JavaType valueType = type.containedType(0);
- if (valueType == null) {
- valueType = TypeFactory.unknownType();
- }
- TypeSerializer vts = createTypeSerializer(config, valueType);
- return new IteratorSerializer(valueType, staticTyping, vts, null);
+ return new IteratorSerializer(valueType, staticTyping, createTypeSerializer(config, valueType), null);
}
+ @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
+ */
+ protected JsonSerializer<?> buildIterableSerializer(SerializationConfig config,
+ JavaType type, BeanDescription beanDesc, boolean staticTyping,
+ JavaType valueType)
+ throws JsonMappingException
+ {
+ return new IterableSerializer(valueType, staticTyping, createTypeSerializer(config, valueType), null);
+ }
+
+ @Deprecated // since 2.5
protected JsonSerializer<?> buildIterableSerializer(SerializationConfig config,
JavaType type, BeanDescription beanDesc,
boolean staticTyping)
throws JsonMappingException
{
- // if there's generic type, it'll be the first contained type
- JavaType valueType = type.containedType(0);
- if (valueType == null) {
- valueType = TypeFactory.unknownType();
- }
- TypeSerializer vts = createTypeSerializer(config, valueType);
- return new IterableSerializer(valueType, staticTyping, vts, null);
+ 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
+ */
+ protected JsonSerializer<?> buildMapEntrySerializer(SerializationConfig config,
+ JavaType type, BeanDescription beanDesc, boolean staticTyping,
+ JavaType keyType, JavaType valueType)
+ throws JsonMappingException
+ {
+ return new MapEntrySerializer(valueType, keyType, valueType,
+ staticTyping, createTypeSerializer(config, valueType), null);
+ }
+
protected JsonSerializer<?> buildEnumSerializer(SerializationConfig config,
JavaType type, BeanDescription beanDesc)
throws JsonMappingException
{
- /* As per [Issue#24], may want to use alternate shape, serialize as JSON Object.
+ /* As per [databind#24], may want to use alternate shape, serialize as JSON Object.
* Challenge here is that EnumSerializer does not know how to produce
* POJO style serialization, so we must handle that special case separately;
* otherwise pass it to EnumSerializer.
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 1e50a26..1811c7d 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyWriter.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyWriter.java
@@ -6,6 +6,7 @@
import java.lang.reflect.Type;
import java.util.HashMap;
+import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.SerializableString;
import com.fasterxml.jackson.core.io.SerializedString;
@@ -37,8 +38,8 @@
/**
* Marker object used to indicate "do not serialize if empty"
*/
- public final static Object MARKER_FOR_EMPTY = new Object();
-
+ public final static Object MARKER_FOR_EMPTY = JsonInclude.Include.NON_EMPTY;
+
/*
/**********************************************************
/* Settings for accessing property value to serialize
@@ -320,12 +321,14 @@
@Override public PropertyName getWrapperName() { return _wrapperName; }
@Override public boolean isRequired() { return _metadata.isRequired(); }
@Override public PropertyMetadata getMetadata() { return _metadata; }
-
+
+ // Note: also part of 'PropertyWriter'
@Override
public <A extends Annotation> A getAnnotation(Class<A> acls) {
return _member.getAnnotation(acls);
}
+ // Note: also part of 'PropertyWriter'
@Override
public <A extends Annotation> A getContextAnnotation(Class<A> acls) {
return _contextAnnotations.get(acls);
@@ -434,20 +437,6 @@
public Class<?>[] getViews() { return _includeInViews; }
- /**
- *<p>
- * NOTE: due to introspection, this is a <b>slow</b> method to call
- * and should never be called during actual serialization or filtering
- * of the property. Rather it is needed for traversal needed for things
- * like constructing JSON Schema instances.
- *
- * @since 2.1
- *
- * @deprecated since 2.2, use {@link #isRequired()} instead.
- */
- @Deprecated
- protected boolean isRequired(AnnotationIntrospector intr) { return _metadata.isRequired(); }
-
/*
/**********************************************************
/* PropertyWriter methods (serialization)
@@ -687,15 +676,6 @@
}
/**
- * @deprecated Since 2.3 Use overloaded variants
- */
- @Deprecated
- protected void _handleSelfReference(Object bean, JsonSerializer<?> ser)
- throws JsonMappingException {
- _handleSelfReference(bean, null, null, ser);
- }
-
- /**
* 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
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 605f8b7..1eb949d 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializer.java
@@ -113,8 +113,8 @@
/* Can not:
*
* - have Object Id (may be allowed in future)
- * - have any getter
- *
+ * - have "any getter"
+ * - have per-property filters
*/
if ((_objectIdWriter == null)
&& (_anyGetterWriter == null)
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 2f10c9f..baf8462 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java
@@ -223,6 +223,12 @@
// Finally: maybe we can still deal with it as an implementation of some basic JDK interface?
if (ser == null) {
ser = findSerializerByAddonType(config, type, beanDesc, staticTyping);
+ // 18-Sep-2014, tatu: Actually, as per [jackson-databind#539], need to get
+ // 'unknown' serializer assigned earlier, here, so that it gets properly
+ // post-processed
+ if (ser == null) {
+ ser = prov.getUnknownTypeSerializer(beanDesc.getBeanClass());
+ }
}
}
}
@@ -339,7 +345,7 @@
final SerializationConfig config = prov.getConfig();
BeanSerializerBuilder builder = constructBeanSerializerBuilder(beanDesc);
builder.setConfig(config);
-
+
// First: any detectable (auto-detect, annotations) properties to serialize?
List<BeanPropertyWriter> props = findBeanProperties(prov, beanDesc, builder);
if (props == null) {
@@ -351,10 +357,10 @@
props = mod.changeProperties(config, beanDesc, props);
}
}
-
+
// Any properties to suppress?
props = filterBeanProperties(config, beanDesc, props);
-
+
// [JACKSON-440] Need to allow reordering of properties to serialize
if (_factoryConfig.hasSerializerModifiers()) {
for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {
@@ -403,14 +409,10 @@
JsonSerializer<Object> ser = (JsonSerializer<Object>) builder.build();
- /* However, after all modifications: no properties, no serializer
- * (note; as per [JACKSON-670], check was moved later on from an earlier location)
- */
if (ser == null) {
- /* 27-Nov-2009, tatu: Except that as per [JACKSON-201], we are
- * ok with that as long as it has a recognized class annotation
- * (which may come from a mix-in too)
- */
+ // If we get this far, there were no properties found, so no regular BeanSerializer
+ // would be constructed. But, couple of exceptions.
+ // First: if there are known annotations, just create 'empty bean' serializer
if (beanDesc.hasKnownClassAnnotations()) {
return builder.createDummy();
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/ContainerSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/ContainerSerializer.java
index a72700c..c3e3fe0 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/ContainerSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/ContainerSerializer.java
@@ -23,6 +23,13 @@
protected ContainerSerializer(Class<T> t) {
super(t);
}
+
+ /**
+ * @since 2.5
+ */
+ protected ContainerSerializer(JavaType fullType) {
+ super(fullType);
+ }
/**
* Alternate constructor that is (alas!) needed to work
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 d13728a..17ad71c 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/DefaultSerializerProvider.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/DefaultSerializerProvider.java
@@ -330,11 +330,6 @@
findValueSerializer(javaType, null).acceptJsonFormatVisitor(visitor, javaType);
}
- @Deprecated // since 2.3; use the overloaded variant
- public boolean hasSerializerFor(Class<?> cls) {
- return hasSerializerFor(cls, null);
- }
-
/**
* Method that can be called to see if this serializer provider
* can find a serializer for an instance of given class.
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 00b6adc..623a020 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/PropertyBuilder.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/PropertyBuilder.java
@@ -48,23 +48,6 @@
}
/**
- * @deprecated Since 2.3, use variant that takes {@link SerializerProvider} as
- * first argument -- to be removed from 2.4
- */
- @Deprecated
- protected final BeanPropertyWriter buildWriter(BeanPropertyDefinition propDef,
- JavaType declaredType, JsonSerializer<?> ser,
- TypeSerializer typeSer, TypeSerializer contentTypeSer,
- AnnotatedMember am, boolean defaultUseStaticTyping)
- {
- /* We will only retain this method until 2.4; left for now to explicitly
- * cause compilation/linking issue iff anyone has overridden the method
- * (hopefully not)
- */
- throw new IllegalStateException();
- }
-
- /**
* @param contentTypeSer Optional explicit type information serializer
* to use for contained values (only used for properties that are
* of container type)
@@ -90,7 +73,7 @@
}
JavaType ct = serializationType.getContentType();
/* 03-Sep-2010, tatu: This is somehow related to [JACKSON-356], but I don't completely
- * yet understand how pieces fit together. Still, better be explicit than rely on
+ * yet understand how pieces fit together. Still, better to be explicit than rely on
* NPE to indicate an issue...
*/
if (ct == null) {
@@ -140,13 +123,12 @@
am, _beanDesc.getClassAnnotations(), declaredType,
ser, typeSer, serializationType, suppressNulls, valueToSuppress);
- // 14-Oct-2013, tatu: And how about custom null serializer?
+ // How about custom null serializer?
Object serDef = _annotationIntrospector.findNullSerializer(am);
if (serDef != null) {
bpw.assignNullSerializer(prov.serializerInstance(am, serDef));
}
-
- // [JACKSON-132]: Unwrapping
+ // And then, handling of unwrapping
NameTransformer unwrapper = _annotationIntrospector.findUnwrappingNameTransformer(am);
if (unwrapper != null) {
bpw = bpw.unwrappingWriter(unwrapper);
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/PropertyWriter.java b/src/main/java/com/fasterxml/jackson/databind/ser/PropertyWriter.java
index 6e1d363..1091aa8 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/PropertyWriter.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/PropertyWriter.java
@@ -1,7 +1,8 @@
package com.fasterxml.jackson.databind.ser;
-import com.fasterxml.jackson.core.JsonGenerator;
+import java.lang.annotation.Annotation;
+import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor;
import com.fasterxml.jackson.databind.node.ObjectNode;
@@ -25,7 +26,52 @@
public abstract String getName();
public abstract PropertyName getFullName();
+
+ /**
+ * Convenience method for accessing annotation that may be associated
+ * either directly on property, or, if not, via enclosing class (context).
+ * This allows adding baseline contextual annotations, for example, by adding
+ * an annotation for a given class and making that apply to all properties
+ * unless overridden by per-property annotations.
+ *<p>
+ * This method is functionally equivalent to:
+ *<pre>
+ * MyAnnotation ann = propWriter.getAnnotation(MyAnnotation.class);
+ * if (ann == null) {
+ * ann = propWriter.getContextAnnotation(MyAnnotation.class);
+ * }
+ *</pre>
+ * that is, tries to find a property annotation first, but if one is not
+ * found, tries to find context-annotation (from enclosing class) of
+ * same type.
+ *
+ * @since 2.5
+ */
+ public <A extends Annotation> A findAnnotation(Class<A> acls) {
+ A ann = getAnnotation(acls);
+ if (ann == null) {
+ ann = getContextAnnotation(acls);
+ }
+ return ann;
+ }
+ /**
+ * Method for accessing annotations directly declared for property that this
+ * writer is associated with.
+ *
+ * @since 2.5
+ */
+ public abstract <A extends Annotation> A getAnnotation(Class<A> acls);
+
+ /**
+ * Method for accessing annotations declared in context of the property that this
+ * writer is associated with; usually this means annotations on enclosing class
+ * for property.
+ *
+ * @since 2.5
+ */
+ public abstract <A extends Annotation> A getContextAnnotation(Class<A> acls);
+
/*
/**********************************************************
/* Serialization methods, regular output
@@ -35,7 +81,7 @@
/**
* The main serialization method called by filter when property is to be written normally.
*/
- public abstract void serializeAsField(Object pojo, JsonGenerator jgen, SerializerProvider prov)
+ public abstract void serializeAsField(Object value, JsonGenerator jgen, SerializerProvider provider)
throws Exception;
/**
@@ -43,7 +89,7 @@
* filtered, but the underlying data format requires a placeholder of some kind.
* This is usually the case for tabular (positional) data formats such as CSV.
*/
- public abstract void serializeAsOmittedField(Object pojo, JsonGenerator jgen, SerializerProvider prov)
+ public abstract void serializeAsOmittedField(Object value, JsonGenerator jgen, SerializerProvider provider)
throws Exception;
/*
@@ -62,7 +108,7 @@
* data format; so it is typically NOT called for fully tabular formats such as CSV,
* where logical output is still as form of POJOs.
*/
- public abstract void serializeAsElement(Object pojo, JsonGenerator jgen, SerializerProvider prov)
+ public abstract void serializeAsElement(Object value, JsonGenerator jgen, SerializerProvider provider)
throws Exception;
/**
@@ -70,7 +116,7 @@
* but then value is to be omitted. This requires output of a placeholder value
* of some sort; often similar to {@link #serializeAsOmittedField}.
*/
- public abstract void serializeAsPlaceholder(Object pojo, JsonGenerator jgen, SerializerProvider prov)
+ public abstract void serializeAsPlaceholder(Object value, JsonGenerator jgen, SerializerProvider provider)
throws Exception;
/*
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/IndexedListSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/IndexedListSerializer.java
index 86a9246..417db79 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/IndexedListSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/IndexedListSerializer.java
@@ -1,12 +1,10 @@
package com.fasterxml.jackson.databind.ser.impl;
import java.io.IOException;
-import java.util.LinkedList;
-import java.util.List;
+import java.util.*;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonGenerator;
-
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
@@ -60,6 +58,19 @@
public ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
return new IndexedListSerializer(_elementType, _staticTyping, vts, _property, _elementSerializer);
}
+
+ @Override
+ public final void serialize(List<?> value, JsonGenerator jgen, SerializerProvider provider) throws IOException
+ {
+ final int len = value.size();
+ if ((len == 1) && provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) {
+ serializeContents(value, jgen, provider);
+ return;
+ }
+ jgen.writeStartArray(len);
+ serializeContents(value, jgen, provider);
+ jgen.writeEndArray();
+ }
@Override
public void serializeContents(List<?> value, JsonGenerator jgen, SerializerProvider provider)
@@ -101,14 +112,13 @@
}
}
} catch (Exception e) {
- // [JACKSON-55] Need to add reference information
wrapAndThrow(provider, e, value, i);
}
}
public void serializeContentsUsing(List<?> value, JsonGenerator jgen, SerializerProvider provider,
JsonSerializer<Object> ser)
- throws IOException, JsonGenerationException
+ throws IOException
{
final int len = value.size();
if (len == 0) {
@@ -133,7 +143,7 @@
}
public void serializeTypedContents(List<?> value, JsonGenerator jgen, SerializerProvider provider)
- throws IOException, JsonGenerationException
+ throws IOException
{
final int len = value.size();
if (len == 0) {
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/IndexedStringListSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/IndexedStringListSerializer.java
index 00f11df..1c4d784 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/IndexedStringListSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/IndexedStringListSerializer.java
@@ -112,7 +112,7 @@
return;
}
- jgen.writeStartArray();
+ jgen.writeStartArray(len);
if (_serializer == null) {
serializeContents(value, jgen, provider, len);
} else {
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/IteratorSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/IteratorSerializer.java
index 1999212..20e86b3 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/IteratorSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/IteratorSerializer.java
@@ -3,7 +3,6 @@
import java.io.IOException;
import java.util.Iterator;
-import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
@@ -50,8 +49,20 @@
}
@Override
+ public final void serialize(Iterator<?> value, JsonGenerator jgen, SerializerProvider provider) throws IOException
+ {
+ if (provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED) && hasSingleElement(value)) {
+ serializeContents(value, jgen, provider);
+ return;
+ }
+ jgen.writeStartArray();
+ serializeContents(value, jgen, provider);
+ jgen.writeEndArray();
+ }
+
+ @Override
public void serializeContents(Iterator<?> value, JsonGenerator jgen, SerializerProvider provider)
- throws IOException, JsonGenerationException
+ throws IOException
{
if (value.hasNext()) {
final TypeSerializer typeSer = _valueTypeSerializer;
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
new file mode 100644
index 0000000..cfbbfe5
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/MapEntrySerializer.java
@@ -0,0 +1,324 @@
+package com.fasterxml.jackson.databind.ser.impl;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import com.fasterxml.jackson.core.JsonGenerationException;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.ser.ContainerSerializer;
+import com.fasterxml.jackson.databind.ser.ContextualSerializer;
+
+/**
+ * @since 2.5
+ */
+@JacksonStdImpl
+public class MapEntrySerializer
+ extends ContainerSerializer<Map.Entry<?,?>>
+ implements ContextualSerializer
+{
+ /**
+ * Map-valued property being serialized with this instance
+ */
+ protected final BeanProperty _property;
+
+ /**
+ * Whether static types should be used for serialization of values
+ * or not (if not, dynamic runtime type is used)
+ */
+ protected final boolean _valueTypeIsStatic;
+
+ protected final JavaType _entryType, _keyType, _valueType;
+
+ /**
+ * Key serializer to use, if it can be statically determined
+ */
+ protected JsonSerializer<Object> _keySerializer;
+
+ /**
+ * Value serializer to use, if it can be statically determined
+ */
+ protected JsonSerializer<Object> _valueSerializer;
+
+ /**
+ * Type identifier serializer used for values, if any.
+ */
+ protected final TypeSerializer _valueTypeSerializer;
+
+ /**
+ * If value type can not be statically determined, mapping from
+ * runtime value types to serializers are stored in this object.
+ */
+ protected PropertySerializerMap _dynamicValueSerializers;
+
+ /*
+ /**********************************************************
+ /* Construction, initialization
+ /**********************************************************
+ */
+
+ public MapEntrySerializer(JavaType type, JavaType keyType, JavaType valueType,
+ boolean staticTyping, TypeSerializer vts,
+ BeanProperty property)
+ {
+ super(type);
+ _entryType = type;
+ _keyType = keyType;
+ _valueType = valueType;
+ _valueTypeIsStatic = staticTyping;
+ _valueTypeSerializer = vts;
+ _property = property;
+ _dynamicValueSerializers = PropertySerializerMap.emptyMap();
+ }
+
+ @SuppressWarnings("unchecked")
+ protected MapEntrySerializer(MapEntrySerializer src, BeanProperty property,
+ TypeSerializer vts,
+ JsonSerializer<?> keySer, JsonSerializer<?> valueSer)
+ {
+ super(Map.class, false);
+ _entryType = src._entryType;
+ _keyType = src._keyType;
+ _valueType = src._valueType;
+ _valueTypeIsStatic = src._valueTypeIsStatic;
+ _valueTypeSerializer = src._valueTypeSerializer;
+ _keySerializer = (JsonSerializer<Object>) keySer;
+ _valueSerializer = (JsonSerializer<Object>) valueSer;
+ _dynamicValueSerializers = src._dynamicValueSerializers;
+ _property = src._property;
+ }
+
+ @Override
+ public ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
+ return new MapEntrySerializer(this, _property, vts, _keySerializer, _valueSerializer);
+ }
+
+ public MapEntrySerializer withResolved(BeanProperty property,
+ JsonSerializer<?> keySerializer, JsonSerializer<?> valueSerializer) {
+ return new MapEntrySerializer(this, property, _valueTypeSerializer, keySerializer, valueSerializer);
+ }
+
+ @Override
+ public JsonSerializer<?> createContextual(SerializerProvider provider,
+ BeanProperty property) throws JsonMappingException
+ {
+ JsonSerializer<?> ser = null;
+ JsonSerializer<?> keySer = null;
+ final AnnotationIntrospector intr = provider.getAnnotationIntrospector();
+ final AnnotatedMember propertyAcc = (property == null) ? null : property.getMember();
+
+ // First: if we have a property, may have property-annotation overrides
+ if (propertyAcc != null && intr != null) {
+ Object serDef = intr.findKeySerializer(propertyAcc);
+ if (serDef != null) {
+ keySer = provider.serializerInstance(propertyAcc, serDef);
+ }
+ serDef = intr.findContentSerializer(propertyAcc);
+ if (serDef != null) {
+ ser = provider.serializerInstance(propertyAcc, serDef);
+ }
+ }
+ if (ser == null) {
+ ser = _valueSerializer;
+ }
+ // [Issue#124]: May have a content converter
+ ser = findConvertingContentSerializer(provider, 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.
+ // 20-Aug-2013, tatu: Need to avoid trying to access serializer for java.lang.Object tho
+ if ((_valueTypeIsStatic && _valueType.getRawClass() != Object.class)
+ || hasContentTypeAnnotation(provider, property)) {
+ ser = provider.findValueSerializer(_valueType, property);
+ }
+ } else {
+ ser = provider.handleSecondaryContextualization(ser, property);
+ }
+ if (keySer == null) {
+ keySer = _keySerializer;
+ }
+ if (keySer == null) {
+ keySer = provider.findKeySerializer(_keyType, property);
+ } else {
+ keySer = provider.handleSecondaryContextualization(keySer, property);
+ }
+ MapEntrySerializer mser = withResolved(property, keySer, ser);
+ // but note: no filtering, ignored entries or sorting (unlike Maps)
+ return mser;
+ }
+
+ /*
+ /**********************************************************
+ /* Accessors
+ /**********************************************************
+ */
+
+ @Override
+ public JavaType getContentType() {
+ return _valueType;
+ }
+
+ @Override
+ public JsonSerializer<?> getContentSerializer() {
+ return _valueSerializer;
+ }
+
+ @Override
+ public boolean hasSingleElement(Map.Entry<?,?> value) {
+ return true;
+ }
+
+ @Override
+ public boolean isEmpty(Entry<?, ?> value) {
+ return (value == null);
+ }
+
+ /*
+ /**********************************************************
+ /* Serialization methods
+ /**********************************************************
+ */
+
+ @Override
+ public void serialize(Map.Entry<?, ?> value, JsonGenerator jgen, SerializerProvider provider)
+ throws IOException
+ {
+ jgen.writeStartObject();
+ if (_valueSerializer != null) {
+ serializeUsing(value, jgen, provider, _valueSerializer);
+ } else {
+ serializeDynamic(value, jgen, provider);
+ }
+ jgen.writeEndObject();
+ }
+
+ @Override
+ public void serializeWithType(Map.Entry<?, ?> value, JsonGenerator jgen, SerializerProvider provider,
+ TypeSerializer typeSer) throws IOException
+ {
+ typeSer.writeTypePrefixForObject(value, jgen);
+ if (_valueSerializer != null) {
+ serializeUsing(value, jgen, provider, _valueSerializer);
+ } else {
+ serializeDynamic(value, jgen, provider);
+ }
+ typeSer.writeTypeSuffixForObject(value, jgen);
+ }
+
+ protected void serializeDynamic(Map.Entry<?, ?> value, JsonGenerator jgen, SerializerProvider provider)
+ throws IOException
+ {
+ final JsonSerializer<Object> keySerializer = _keySerializer;
+ final boolean skipNulls = !provider.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES);
+ final TypeSerializer vts = _valueTypeSerializer;
+
+ PropertySerializerMap serializers = _dynamicValueSerializers;
+
+ Object valueElem = value.getValue();
+ Object keyElem = value.getKey();
+ if (keyElem == null) {
+ provider.findNullKeySerializer(_keyType, _property).serialize(null, jgen, provider);
+ } else {
+ // [JACKSON-314] skip entries with null values?
+ if (skipNulls && valueElem == null) return;
+ keySerializer.serialize(keyElem, jgen, provider);
+ }
+ // And then value
+ if (valueElem == null) {
+ provider.defaultSerializeNull(jgen);
+ } else {
+ Class<?> cc = valueElem.getClass();
+ JsonSerializer<Object> ser = serializers.serializerFor(cc);
+ if (ser == null) {
+ if (_valueType.hasGenericTypes()) {
+ ser = _findAndAddDynamic(serializers,
+ provider.constructSpecializedType(_valueType, cc), provider);
+ } else {
+ ser = _findAndAddDynamic(serializers, cc, provider);
+ }
+ serializers = _dynamicValueSerializers;
+ }
+ try {
+ if (vts == null) {
+ ser.serialize(valueElem, jgen, provider);
+ } else {
+ ser.serializeWithType(valueElem, jgen, provider, vts);
+ }
+ } catch (Exception e) {
+ // [JACKSON-55] Need to add reference information
+ String keyDesc = ""+keyElem;
+ wrapAndThrow(provider, e, value, keyDesc);
+ }
+ }
+ }
+
+ /**
+ * Method called to serialize fields, when the value type is statically known,
+ * so that value serializer is passed and does not need to be fetched from
+ * provider.
+ */
+ protected void serializeUsing(Map.Entry<?, ?> value, JsonGenerator jgen, SerializerProvider provider,
+ JsonSerializer<Object> ser)
+ throws IOException, JsonGenerationException
+ {
+ final JsonSerializer<Object> keySerializer = _keySerializer;
+ final TypeSerializer vts = _valueTypeSerializer;
+ final boolean skipNulls = !provider.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES);
+
+ Object valueElem = value.getValue();
+ Object keyElem = value.getKey();
+ if (keyElem == null) {
+ provider.findNullKeySerializer(_keyType, _property).serialize(null, jgen, provider);
+ } else {
+ // [JACKSON-314] also may need to skip entries with null values
+ if (skipNulls && valueElem == null) return;
+ keySerializer.serialize(keyElem, jgen, provider);
+ }
+ if (valueElem == null) {
+ provider.defaultSerializeNull(jgen);
+ } else {
+ try {
+ if (vts == null) {
+ ser.serialize(valueElem, jgen, provider);
+ } else {
+ ser.serializeWithType(valueElem, jgen, provider, vts);
+ }
+ } catch (Exception e) {
+ // [JACKSON-55] Need to add reference information
+ String keyDesc = ""+keyElem;
+ wrapAndThrow(provider, e, value, keyDesc);
+ }
+ }
+ }
+
+ /*
+ /**********************************************************
+ /* Internal helper methods
+ /**********************************************************
+ */
+
+ protected final JsonSerializer<Object> _findAndAddDynamic(PropertySerializerMap map,
+ Class<?> type, SerializerProvider provider) throws JsonMappingException
+ {
+ PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSecondarySerializer(type, provider, _property);
+ if (map != result.map) {
+ _dynamicValueSerializers = result.map;
+ }
+ return result.serializer;
+ }
+
+ protected final JsonSerializer<Object> _findAndAddDynamic(PropertySerializerMap map,
+ JavaType type, SerializerProvider provider) throws JsonMappingException
+ {
+ PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSecondarySerializer(type, provider, _property);
+ if (map != result.map) {
+ _dynamicValueSerializers = result.map;
+ }
+ return result.serializer;
+ }
+
+}
\ No newline at end of file
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 f0f7f48..7dd804f 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
@@ -5,7 +5,6 @@
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonGenerator;
-
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
@@ -146,6 +145,20 @@
*/
@Override
+ public final void serialize(String[] value, JsonGenerator jgen, SerializerProvider provider)
+ throws IOException, JsonGenerationException
+ {
+ final int len = value.length;
+ if ((len == 1) && provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) {
+ serializeContents(value, jgen, provider);
+ return;
+ }
+ jgen.writeStartArray(len);
+ serializeContents(value, jgen, provider);
+ jgen.writeEndArray();
+ }
+
+ @Override
public void serializeContents(String[] value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonGenerationException
{
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringCollectionSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringCollectionSerializer.java
index ae40648..110c9fa 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringCollectionSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringCollectionSerializer.java
@@ -116,11 +116,12 @@
throws IOException, JsonGenerationException
{
// [JACKSON-805]
- if ((value.size() == 1) && provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) {
+ final int len = value.size();
+ if ((len == 1) && provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) {
_serializeUnwrapped(value, jgen, provider);
return;
}
- jgen.writeStartArray();
+ jgen.writeStartArray(len);
if (_serializer == null) {
serializeContents(value, jgen, provider);
} else {
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 1f68d91..877066a 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
@@ -42,12 +42,13 @@
super(src._handledType, false);
_property = property;
}
-
+
+ // NOTE: as of 2.5, sub-classes SHOULD override (in 2.4 and before, was final),
+ // at least if they can provide access to actual size of value and use `writeStartArray()`
+ // variant that passes size of array to output, which is helpful with some data formats
@Override
- public final void serialize(T value, JsonGenerator jgen, SerializerProvider provider)
- throws IOException, JsonGenerationException
+ public void serialize(T value, JsonGenerator jgen, SerializerProvider provider) throws IOException
{
- // [JACKSON-805]
if (provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)
&& hasSingleElement(value)) {
serializeContents(value, jgen, provider);
@@ -57,7 +58,7 @@
serializeContents(value, jgen, provider);
jgen.writeEndArray();
}
-
+
@Override
public final void serializeWithType(T value, JsonGenerator jgen, SerializerProvider provider,
TypeSerializer typeSer)
@@ -68,7 +69,7 @@
serializeContents(value, jgen, provider);
typeSer.writeTypeSuffixForArray(value, jgen);
}
-
+
protected abstract void serializeContents(T value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonGenerationException;
}
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 ada854c..3558fb8 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
@@ -1,7 +1,6 @@
package com.fasterxml.jackson.databind.ser.std;
import java.io.IOException;
-import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import com.fasterxml.jackson.core.*;
@@ -15,7 +14,6 @@
import com.fasterxml.jackson.databind.ser.ContainerSerializer;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap;
-import com.fasterxml.jackson.databind.type.TypeFactory;
/**
* Base class for serializers that will output contents as JSON
@@ -168,12 +166,13 @@
/* Serialization
/**********************************************************
*/
-
+
+ // NOTE: as of 2.5, sub-classes SHOULD override (in 2.4 and before, was final),
+ // at least if they can provide access to actual size of value and use `writeStartArray()`
+ // variant that passes size of array to output, which is helpful with some data formats
@Override
- public final void serialize(T value, JsonGenerator jgen, SerializerProvider provider)
- throws IOException, JsonGenerationException
+ public void serialize(T value, JsonGenerator jgen, SerializerProvider provider) throws IOException
{
- // [JACKSON-805]
if (provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)
&& hasSingleElement(value)) {
serializeContents(value, jgen, provider);
@@ -184,11 +183,9 @@
jgen.writeEndArray();
}
- // Note: was 'final' modifier in 2.2 and before; no real need to be, removed
@Override
public void serializeWithType(T value, JsonGenerator jgen, SerializerProvider provider,
- TypeSerializer typeSer)
- throws IOException, JsonGenerationException
+ TypeSerializer typeSer) throws IOException
{
// note: let's NOT consider [JACKSON-805] here; gets too complicated, and probably just won't work
typeSer.writeTypePrefixForArray(value, jgen);
@@ -197,7 +194,7 @@
}
protected abstract void serializeContents(T value, JsonGenerator jgen, SerializerProvider provider)
- throws IOException, JsonGenerationException;
+ throws IOException;
@SuppressWarnings("deprecation")
@Override
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 c136a2f..eac13f4 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
@@ -369,19 +369,49 @@
return null;
}
+ @SuppressWarnings("incomplete-switch")
@Override
public JsonSerializer<?> createContextual(SerializerProvider provider,
BeanProperty property)
throws JsonMappingException
{
- ObjectIdWriter oiw = _objectIdWriter;
- String[] ignorals = null;
- Object newFilterId = null;
final AnnotationIntrospector intr = provider.getAnnotationIntrospector();
final AnnotatedMember accessor = (property == null || intr == null)
? null : property.getMember();
+ final SerializationConfig config = provider.getConfig();
- // First: may have an override for Object Id:
+ // Let's start with one big transmutation: Enums that are annotated
+ // to serialize as Objects may want to revert
+ 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);
+ }
+ }
+ }
+ }
+ }
+
+ ObjectIdWriter oiw = _objectIdWriter;
+ String[] ignorals = null;
+ Object newFilterId = null;
+
+ // Then we may have an override for Object Id
if (accessor != null) {
ignorals = intr.findPropertiesToIgnore(accessor);
ObjectIdInfo objectIdInfo = intr.findObjectIdInfo(accessor);
@@ -467,21 +497,11 @@
if (newFilterId != null) {
contextual = contextual.withFilterId(newFilterId);
}
-
- // One more thing: are we asked to serialize POJO as array?
- JsonFormat.Shape shape = null;
- if (accessor != null) {
- JsonFormat.Value format = intr.findFormat((Annotated) accessor);
-
- if (format != null) {
- shape = format.getShape();
- }
- }
if (shape == null) {
shape = _serializationShape;
}
if (shape == JsonFormat.Shape.ARRAY) {
- contextual = contextual.asArraySerializer();
+ return contextual.asArraySerializer();
}
return contextual;
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/CollectionSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/CollectionSerializer.java
index e99a9ea..bdbcbc3 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/CollectionSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/CollectionSerializer.java
@@ -8,6 +8,7 @@
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.ser.ContainerSerializer;
@@ -72,16 +73,28 @@
it.next();
return !it.hasNext();
}
-
+
/*
/**********************************************************
/* Actual serialization
/**********************************************************
*/
+
+ @Override
+ public final void serialize(Collection<?> value, JsonGenerator jgen, SerializerProvider provider) throws IOException
+ {
+ final int len = value.size();
+ if ((len == 1) && provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) {
+ serializeContents(value, jgen, provider);
+ return;
+ }
+ jgen.writeStartArray(len);
+ serializeContents(value, jgen, provider);
+ jgen.writeEndArray();
+ }
@Override
- public void serializeContents(Collection<?> value, JsonGenerator jgen, SerializerProvider provider)
- throws IOException, JsonGenerationException
+ public void serializeContents(Collection<?> value, JsonGenerator jgen, SerializerProvider provider) throws IOException
{
if (_elementSerializer != null) {
serializeContentsUsing(value, jgen, provider, _elementSerializer);
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 ff42872..2825a0a 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
@@ -108,7 +108,7 @@
//todo: (ryan) add a format for the date in the schema?
return createSchemaNode(_asTimestamp(provider) ? "number" : "string", true);
}
-
+
@Override
public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException
{
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumMapSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumMapSerializer.java
index da48945..cb447f5 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumMapSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumMapSerializer.java
@@ -27,6 +27,7 @@
* and if not, it can only be gotten from actual instance.
*
* @deprecated Since 2.4.4; standard {@link MapSerializer} works better.
+ * (to be removed from 2.6)
*/
@JacksonStdImpl
@Deprecated
@@ -293,7 +294,7 @@
}
}
- @SuppressWarnings({ "unchecked", "deprecation" })
+ @SuppressWarnings({ "unchecked" })
@Override
public JsonNode getSchema(SerializerProvider provider, Type typeHint)
throws JsonMappingException
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 47c24e6..ddfddfd 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
@@ -7,7 +7,6 @@
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonFormat.Shape;
import com.fasterxml.jackson.core.*;
-
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.introspect.Annotated;
@@ -75,11 +74,12 @@
*
* @since 2.1
*/
- public static EnumSerializer construct(Class<Enum<?>> enumClass, SerializationConfig config,
+ @SuppressWarnings("unchecked")
+ public static EnumSerializer construct(Class<?> enumClass, SerializationConfig config,
BeanDescription beanDesc, JsonFormat.Value format)
{
// [JACKSON-212]: If toString() is to be used instead, leave EnumValues null
- EnumValues v = EnumValues.construct(config, enumClass);
+ EnumValues v = EnumValues.construct(config, (Class<Enum<?>>) enumClass);
Boolean serializeAsIndex = _isShapeWrittenUsingIndex(enumClass, format, true);
return new EnumSerializer(v, serializeAsIndex);
}
@@ -120,7 +120,7 @@
@Override
public final void serialize(Enum<?> en, JsonGenerator jgen, SerializerProvider provider)
- throws IOException, JsonGenerationException
+ throws IOException
{
// [JACKSON-684]: serialize as index?
if (_serializeAsIndex(provider)) {
@@ -205,7 +205,8 @@
if (shape == Shape.STRING) {
return Boolean.FALSE;
}
- if (shape.isNumeric()) {
+ // 01-Oct-2014, tatu: For convenience, consider "as-array" to also mean 'yes, use index')
+ if (shape.isNumeric() || (shape == Shape.ARRAY)) {
return Boolean.TRUE;
}
throw new IllegalArgumentException("Unsupported serialization shape ("+shape+") for Enum "+enumClass.getName()
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSetSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSetSerializer.java
index e1edbf6..cefea7a 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSetSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSetSerializer.java
@@ -1,10 +1,9 @@
package com.fasterxml.jackson.databind.ser.std;
import java.io.IOException;
-import java.util.EnumSet;
+import java.util.*;
import com.fasterxml.jackson.core.*;
-
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
@@ -43,6 +42,19 @@
public boolean hasSingleElement(EnumSet<? extends Enum<?>> value) {
return value.size() == 1;
}
+
+ @Override
+ public final void serialize(EnumSet<? extends Enum<?>> value, JsonGenerator jgen, SerializerProvider provider) throws IOException
+ {
+ final int len = value.size();
+ if ((len == 1) && provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) {
+ serializeContents(value, jgen, provider);
+ return;
+ }
+ jgen.writeStartArray(len);
+ serializeContents(value, jgen, provider);
+ jgen.writeEndArray();
+ }
@Override
public void serializeContents(EnumSet<? extends Enum<?>> value, JsonGenerator jgen, SerializerProvider provider)
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/IterableSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/IterableSerializer.java
index a7cb0b5..470ede4 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/IterableSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/IterableSerializer.java
@@ -1,14 +1,11 @@
package com.fasterxml.jackson.databind.ser.std;
import java.io.IOException;
-import java.util.Iterator;
+import java.util.*;
import com.fasterxml.jackson.core.*;
-
import com.fasterxml.jackson.databind.BeanProperty;
-import com.fasterxml.jackson.databind.JavaType;
-import com.fasterxml.jackson.databind.JsonSerializer;
-import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.ser.ContainerSerializer;
@@ -60,6 +57,18 @@
}
return false;
}
+
+ @Override
+ public final void serialize(Iterable<?> value, JsonGenerator jgen, SerializerProvider provider) throws IOException
+ {
+ if (provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED) && hasSingleElement(value)) {
+ serializeContents(value, jgen, provider);
+ return;
+ }
+ jgen.writeStartArray();
+ serializeContents(value, jgen, provider);
+ jgen.writeEndArray();
+ }
@Override
public void serializeContents(Iterable<?> value, JsonGenerator jgen, SerializerProvider provider)
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 8821964..57761f2 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
@@ -125,7 +125,7 @@
* to serializer factory at this point...
*/
// 05-Sep-2013, tatu: I _think_ this can be considered a primary property...
- ser = provider.findPrimaryPropertySerializer(t, _property);
+ ser = provider.findPrimaryPropertySerializer(t, property);
/* 09-Dec-2010, tatu: Turns out we must add special handling for
* cases where "native" (aka "natural") type is being serialized,
* using standard serializer
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/MapProperty.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/MapProperty.java
index a0c8cc6..89e8f41 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/MapProperty.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/MapProperty.java
@@ -1,8 +1,10 @@
package com.fasterxml.jackson.databind.ser.std;
import java.io.IOException;
+import java.lang.annotation.Annotation;
import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.PropertyName;
@@ -20,26 +22,48 @@
*/
public class MapProperty extends PropertyWriter
{
- protected TypeSerializer _typeSerializer;
-
- protected Object _key, _value;
+ protected final TypeSerializer _typeSerializer;
+
+ protected final BeanProperty _property;
+
+ protected Object _key;
protected JsonSerializer<Object> _keySerializer, _valueSerializer;
- public MapProperty(TypeSerializer typeSer)
+ /**
+ * @deprecated since 2.4
+ */
+ @Deprecated // since 2.4
+ public MapProperty(TypeSerializer typeSer) {
+ this(typeSer, null);
+ }
+
+ public MapProperty(TypeSerializer typeSer, BeanProperty prop)
{
_typeSerializer = typeSer;
+ _property = prop;
+ }
+
+ /**
+ * Deprecated method with wrong signature; value should not be assigned
+ * to property, should be passed via proper call-through methods.
+ *
+ * @deprecated Since 2.5, remove in 2.6
+ */
+ @Deprecated // since 2.5
+ public void reset(Object key, Object value,
+ JsonSerializer<Object> keySer, JsonSerializer<Object> valueSer) {
+ reset(key, keySer, valueSer);
}
/**
* Initialization method that needs to be called before passing
* property to filter.
*/
- public void reset(Object key, Object value,
+ public void reset(Object key,
JsonSerializer<Object> keySer, JsonSerializer<Object> valueSer)
{
_key = key;
- _value = value;
_keySerializer = keySer;
_valueSerializer = valueSer;
}
@@ -58,19 +82,29 @@
}
@Override
- public void serializeAsField(Object pojo, JsonGenerator jgen,
+ public <A extends Annotation> A getAnnotation(Class<A> acls) {
+ return (_property == null) ? null : _property.getAnnotation(acls);
+ }
+
+ @Override
+ public <A extends Annotation> A getContextAnnotation(Class<A> acls) {
+ return (_property == null) ? null : _property.getContextAnnotation(acls);
+ }
+
+ @Override
+ public void serializeAsField(Object value, JsonGenerator jgen,
SerializerProvider provider) throws IOException
{
_keySerializer.serialize(_key, jgen, provider);
if (_typeSerializer == null) {
- _valueSerializer.serialize(_value, jgen, provider);
+ _valueSerializer.serialize(value, jgen, provider);
} else {
- _valueSerializer.serializeWithType(_value, jgen, provider, _typeSerializer);
+ _valueSerializer.serializeWithType(value, jgen, provider, _typeSerializer);
}
}
@Override
- public void serializeAsOmittedField(Object pojo, JsonGenerator jgen,
+ public void serializeAsOmittedField(Object value, JsonGenerator jgen,
SerializerProvider provider) throws Exception
{
if (!jgen.canOmitFields()) {
@@ -79,18 +113,18 @@
}
@Override
- public void serializeAsElement(Object pojo, JsonGenerator jgen,
+ public void serializeAsElement(Object value, JsonGenerator jgen,
SerializerProvider provider) throws Exception
{
if (_typeSerializer == null) {
- _valueSerializer.serialize(_value, jgen, provider);
+ _valueSerializer.serialize(value, jgen, provider);
} else {
- _valueSerializer.serializeWithType(_value, jgen, provider, _typeSerializer);
+ _valueSerializer.serializeWithType(value, jgen, provider, _typeSerializer);
}
}
@Override
- public void serializeAsPlaceholder(Object pojo, JsonGenerator jgen,
+ public void serializeAsPlaceholder(Object value, JsonGenerator jgen,
SerializerProvider provider) throws Exception
{
jgen.writeNull();
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 7d6844d..e08f000 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,6 +4,7 @@
import java.lang.reflect.Type;
import java.util.*;
+import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
@@ -17,6 +18,7 @@
import com.fasterxml.jackson.databind.ser.PropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap;
import com.fasterxml.jackson.databind.type.TypeFactory;
+import com.fasterxml.jackson.databind.util.ArrayBuilders;
/**
* Standard serializer implementation for serializing {link java.util.Map} types.
@@ -93,12 +95,24 @@
*/
protected final boolean _sortKeys;
+ /**
+ * Value that indicates suppression mechanism to use; either one of
+ * values of {@link com.fasterxml.jackson.annotation.JsonInclude.Include}, or actual object to compare
+ * against ("default value")
+ *
+ * @since 2.5
+ */
+ protected final Object _suppressableValue;
+
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
+ /**
+ * @since 2.5
+ */
@SuppressWarnings("unchecked")
protected MapSerializer(HashSet<String> ignoredEntries,
JavaType keyType, JavaType valueType, boolean valueTypeIsStatic,
@@ -117,8 +131,18 @@
_property = null;
_filterId = null;
_sortKeys = false;
+ _suppressableValue = null;
}
+ /**
+ * @since 2.5
+ */
+ protected void _ensureOverride() {
+ if (getClass() != MapSerializer.class) {
+ throw new IllegalStateException("Missing override in class "+getClass().getName());
+ }
+ }
+
@SuppressWarnings("unchecked")
protected MapSerializer(MapSerializer src, BeanProperty property,
JsonSerializer<?> keySerializer, JsonSerializer<?> valueSerializer,
@@ -136,9 +160,19 @@
_property = property;
_filterId = src._filterId;
_sortKeys = src._sortKeys;
+ _suppressableValue = src._suppressableValue;
}
- protected MapSerializer(MapSerializer src, TypeSerializer vts)
+ @Deprecated // since 2.5
+ protected MapSerializer(MapSerializer src, TypeSerializer vts) {
+ this(src, vts, src._suppressableValue);
+ }
+
+ /**
+ * @since 2.5
+ */
+ protected MapSerializer(MapSerializer src, TypeSerializer vts,
+ Object suppressableValue)
{
super(Map.class, false);
_ignoredEntries = src._ignoredEntries;
@@ -152,6 +186,7 @@
_property = src._property;
_filterId = src._filterId;
_sortKeys = src._sortKeys;
+ _suppressableValue = suppressableValue;
}
protected MapSerializer(MapSerializer src, Object filterId, boolean sortKeys)
@@ -168,17 +203,16 @@
_property = src._property;
_filterId = filterId;
_sortKeys = sortKeys;
- }
-
- @Override
- public MapSerializer _withValueTypeSerializer(TypeSerializer vts) {
- return new MapSerializer(this, vts);
+ _suppressableValue = src._suppressableValue;
}
- @Deprecated // since 2.3
- public MapSerializer withResolved(BeanProperty property,
- JsonSerializer<?> keySerializer, JsonSerializer<?> valueSerializer, HashSet<String> ignored) {
- return withResolved(property, keySerializer, valueSerializer, ignored, _sortKeys);
+ @Override
+ public MapSerializer _withValueTypeSerializer(TypeSerializer vts) {
+ if (_valueTypeSerializer == vts) {
+ return this;
+ }
+ _ensureOverride();
+ return new MapSerializer(this, vts, null);
}
/**
@@ -188,32 +222,39 @@
JsonSerializer<?> keySerializer, JsonSerializer<?> valueSerializer,
HashSet<String> ignored, boolean sortKeys)
{
+ _ensureOverride();
MapSerializer ser = new MapSerializer(this, property, keySerializer, valueSerializer, ignored);
if (sortKeys != ser._sortKeys) {
ser = new MapSerializer(ser, _filterId, sortKeys);
}
return ser;
}
-
+
/**
* @since 2.3
*/
public MapSerializer withFilterId(Object filterId) {
- return (_filterId == filterId) ? this : new MapSerializer(this, filterId, _sortKeys);
+ if (_filterId == filterId) {
+ return this;
+ }
+ _ensureOverride();
+ return new MapSerializer(this, filterId, _sortKeys);
}
/**
- * @deprecated Since 2.3 use the method that takes `filterId`
+ * Mutant factory for constructing an instance with different inclusion strategy
+ * for content (Map values).
+ *
+ * @since 2.5
*/
- @Deprecated
- public static MapSerializer construct(String[] ignoredList, JavaType mapType,
- boolean staticValueType, TypeSerializer vts,
- JsonSerializer<Object> keySerializer, JsonSerializer<Object> valueSerializer)
- {
- return construct(ignoredList, mapType, staticValueType, vts,
- keySerializer, valueSerializer, null);
- }
-
+ public MapSerializer withContentInclusion(Object suppressableValue) {
+ if (suppressableValue == _suppressableValue) {
+ return this;
+ }
+ _ensureOverride();
+ return new MapSerializer(this, _valueTypeSerializer, suppressableValue);
+ }
+
/**
* @since 2.3
*/
@@ -222,7 +263,9 @@
JsonSerializer<Object> keySerializer, JsonSerializer<Object> valueSerializer,
Object filterId)
{
- HashSet<String> ignoredEntries = toSet(ignoredList);
+ HashSet<String> ignoredEntries = (ignoredList == null || ignoredList.length == 0)
+ ? null : ArrayBuilders.arrayToSet(ignoredList);
+
JavaType keyType, valueType;
if (mapType == null) {
@@ -248,17 +291,6 @@
return ser;
}
- private static HashSet<String> toSet(String[] ignoredEntries) {
- if (ignoredEntries == null || ignoredEntries.length == 0) {
- return null;
- }
- HashSet<String> result = new HashSet<String>(ignoredEntries.length);
- for (String prop : ignoredEntries) {
- result.add(prop);
- }
- return result;
- }
-
/*
/**********************************************************
/* Post-processing (contextualization)
@@ -278,6 +310,7 @@
JsonSerializer<?> keySer = null;
final AnnotationIntrospector intr = provider.getAnnotationIntrospector();
final AnnotatedMember propertyAcc = (property == null) ? null : property.getMember();
+ Object suppressableValue = _suppressableValue;
// First: if we have a property, may have property-annotation overrides
if (propertyAcc != null && intr != null) {
@@ -289,6 +322,10 @@
if (serDef != null) {
ser = provider.serializerInstance(propertyAcc, serDef);
}
+ JsonInclude.Include incl = intr.findSerializationInclusionForContent(propertyAcc, null);
+ if (incl != null) {
+ suppressableValue = incl;
+ }
}
if (ser == null) {
ser = _valueSerializer;
@@ -328,6 +365,9 @@
sortKeys = (b != null) && b.booleanValue();
}
MapSerializer mser = withResolved(property, keySer, ser, ignored, sortKeys);
+ if (suppressableValue != _suppressableValue) {
+ mser = mser.withContentInclusion(suppressableValue);
+ }
// [Issue#307]: allow filtering
if (property != null) {
@@ -390,23 +430,30 @@
/* JsonSerializer implementation
/**********************************************************
*/
-
+
@Override
public void serialize(Map<?,?> value, JsonGenerator jgen, SerializerProvider provider)
- throws IOException, JsonGenerationException
+ throws IOException
{
jgen.writeStartObject();
if (!value.isEmpty()) {
- if (_filterId != null) {
- serializeFilteredFields(value, jgen, provider,
- findPropertyFilter(provider, _filterId, value));
- jgen.writeEndObject();
- return;
+ Object suppressableValue = _suppressableValue;
+ if (suppressableValue == null) {
+ if (!provider.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES)) {
+ suppressableValue = JsonInclude.Include.NON_NULL;
+ }
+ } else if (suppressableValue == JsonInclude.Include.ALWAYS) {
+ suppressableValue = null;
}
if (_sortKeys || provider.isEnabled(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)) {
value = _orderEntries(value);
}
- if (_valueSerializer != null) {
+ if (_filterId != null) {
+ serializeFilteredFields(value, jgen, provider,
+ findPropertyFilter(provider, _filterId, value), suppressableValue);
+ } else if (suppressableValue != null) {
+ serializeOptionalFields(value, jgen, provider, suppressableValue);
+ } else if (_valueSerializer != null) {
serializeFieldsUsing(value, jgen, provider, _valueSerializer);
} else {
serializeFields(value, jgen, provider);
@@ -418,14 +465,27 @@
@Override
public void serializeWithType(Map<?,?> value, JsonGenerator jgen, SerializerProvider provider,
TypeSerializer typeSer)
- throws IOException, JsonGenerationException
+ throws IOException
{
typeSer.writeTypePrefixForObject(value, jgen);
if (!value.isEmpty()) {
+ Object suppressableValue = _suppressableValue;
+ if (suppressableValue == null) {
+ if (!provider.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES)) {
+ suppressableValue = JsonInclude.Include.NON_NULL;
+ }
+ } else if (suppressableValue == JsonInclude.Include.ALWAYS) {
+ suppressableValue = null;
+ }
if (_sortKeys || provider.isEnabled(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)) {
value = _orderEntries(value);
}
- if (_valueSerializer != null) {
+ if (_filterId != null) {
+ serializeFilteredFields(value, jgen, provider,
+ findPropertyFilter(provider, _filterId, value), suppressableValue);
+ } else if (suppressableValue != null) {
+ serializeOptionalFields(value, jgen, provider, suppressableValue);
+ } else if (_valueSerializer != null) {
serializeFieldsUsing(value, jgen, provider, _valueSerializer);
} else {
serializeFields(value, jgen, provider);
@@ -441,20 +501,19 @@
*/
/**
- * Method called to serialize fields, when the value type is not statically known.
+ * Method called to serialize fields, when the value type is not statically known;
+ * but we know that no value suppression is needed (which simplifies processing a bit)
*/
public void serializeFields(Map<?,?> value, JsonGenerator jgen, SerializerProvider provider)
- throws IOException, JsonGenerationException
+ throws IOException
{
// If value type needs polymorphic type handling, some more work needed:
if (_valueTypeSerializer != null) {
- serializeTypedFields(value, jgen, provider);
+ serializeTypedFields(value, jgen, provider, null);
return;
}
final JsonSerializer<Object> keySerializer = _keySerializer;
-
final HashSet<String> ignored = _ignoredEntries;
- final boolean skipNulls = !provider.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES);
PropertySerializerMap serializers = _dynamicValueSerializers;
@@ -465,8 +524,6 @@
if (keyElem == null) {
provider.findNullKeySerializer(_keyType, _property).serialize(null, jgen, provider);
} else {
- // [JACKSON-314] skip entries with null values?
- if (skipNulls && valueElem == null) continue;
// One twist: is entry ignorable? If so, skip
if (ignored != null && ignored.contains(keyElem)) continue;
keySerializer.serialize(keyElem, jgen, provider);
@@ -490,7 +547,7 @@
try {
serializer.serialize(valueElem, jgen, provider);
} catch (Exception e) {
- // [JACKSON-55] Need to add reference information
+ // Add reference information
String keyDesc = ""+keyElem;
wrapAndThrow(provider, e, value, keyDesc);
}
@@ -498,6 +555,69 @@
}
}
+ public void serializeOptionalFields(Map<?,?> value, JsonGenerator jgen, SerializerProvider provider,
+ Object suppressableValue)
+ throws IOException
+ {
+ // If value type needs polymorphic type handling, some more work needed:
+ if (_valueTypeSerializer != null) {
+ serializeTypedFields(value, jgen, provider, suppressableValue);
+ return;
+ }
+ final HashSet<String> ignored = _ignoredEntries;
+ PropertySerializerMap serializers = _dynamicValueSerializers;
+
+ for (Map.Entry<?,?> entry : value.entrySet()) {
+ // First find key serializer
+ final Object keyElem = entry.getKey();
+ JsonSerializer<Object> keySerializer;
+ if (keyElem == null) {
+ keySerializer = provider.findNullKeySerializer(_keyType, _property);
+ } else {
+ if (ignored != null && ignored.contains(keyElem)) continue;
+ keySerializer = _keySerializer;
+ }
+
+ // Then value serializer
+ final Object valueElem = entry.getValue();
+ JsonSerializer<Object> valueSer;
+ if (valueElem == null) {
+ if (suppressableValue != null) { // all suppressions include null-suppression
+ continue;
+ }
+ valueSer = provider.getDefaultNullValueSerializer();
+ } else {
+ valueSer = _valueSerializer;
+ if (valueSer == null) {
+ Class<?> cc = valueElem.getClass();
+ valueSer = serializers.serializerFor(cc);
+ if (valueSer == null) {
+ if (_valueType.hasGenericTypes()) {
+ valueSer = _findAndAddDynamic(serializers,
+ provider.constructSpecializedType(_valueType, cc), provider);
+ } else {
+ valueSer = _findAndAddDynamic(serializers, cc, provider);
+ }
+ serializers = _dynamicValueSerializers;
+ }
+ }
+ // also may need to skip non-empty values:
+ if ((suppressableValue == JsonInclude.Include.NON_EMPTY)
+ && valueSer.isEmpty(valueElem)) {
+ continue;
+ }
+ }
+ // and then serialize, if all went well
+ try {
+ keySerializer.serialize(keyElem, jgen, provider);
+ valueSer.serialize(valueElem, jgen, provider);
+ } catch (Exception e) {
+ String keyDesc = ""+keyElem;
+ wrapAndThrow(provider, e, value, keyDesc);
+ }
+ }
+ }
+
/**
* Method called to serialize fields, when the value type is statically known,
* so that value serializer is passed and does not need to be fetched from
@@ -505,24 +625,22 @@
*/
protected void serializeFieldsUsing(Map<?,?> value, JsonGenerator jgen, SerializerProvider provider,
JsonSerializer<Object> ser)
- throws IOException, JsonGenerationException
+ throws IOException
{
final JsonSerializer<Object> keySerializer = _keySerializer;
final HashSet<String> ignored = _ignoredEntries;
final TypeSerializer typeSer = _valueTypeSerializer;
- final boolean skipNulls = !provider.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES);
for (Map.Entry<?,?> entry : value.entrySet()) {
- Object valueElem = entry.getValue();
Object keyElem = entry.getKey();
+ if (ignored != null && ignored.contains(keyElem)) continue;
+
if (keyElem == null) {
provider.findNullKeySerializer(_keyType, _property).serialize(null, jgen, provider);
} else {
- // [JACKSON-314] also may need to skip entries with null values
- if (skipNulls && valueElem == null) continue;
- if (ignored != null && ignored.contains(keyElem)) continue;
keySerializer.serialize(keyElem, jgen, provider);
}
+ final Object valueElem = entry.getValue();
if (valueElem == null) {
provider.defaultSerializeNull(jgen);
} else {
@@ -533,7 +651,6 @@
ser.serializeWithType(valueElem, jgen, provider, typeSer);
}
} catch (Exception e) {
- // [JACKSON-55] Need to add reference information
String keyDesc = ""+keyElem;
wrapAndThrow(provider, e, value, keyDesc);
}
@@ -544,40 +661,112 @@
/**
* Helper method used when we have a JSON Filter to use for potentially
* filtering out Map entries.
- *<p>
- * NOTE: initially only called externally, by <code>AnyGetterWriter</code>
*
- * @since 2.3
+ * @since 2.5
*/
public void serializeFilteredFields(Map<?,?> value, JsonGenerator jgen, SerializerProvider provider,
- PropertyFilter filter)
- throws IOException, JsonGenerationException
+ PropertyFilter filter,
+ Object suppressableValue) // since 2.5
+ throws IOException
{
final HashSet<String> ignored = _ignoredEntries;
- final boolean skipNulls = !provider.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES);
PropertySerializerMap serializers = _dynamicValueSerializers;
- final MapProperty prop = new MapProperty(_valueTypeSerializer);
+ final MapProperty prop = new MapProperty(_valueTypeSerializer, _property);
for (Map.Entry<?,?> entry : value.entrySet()) {
- // First, serialize key
+ // First, serialize key; unless ignorable by key
final Object keyElem = entry.getKey();
- final Object valueElem = entry.getValue();
- JsonSerializer<Object> keySer;
+ if (ignored != null && ignored.contains(keyElem)) continue;
+
+ JsonSerializer<Object> keySerializer;
if (keyElem == null) {
- keySer = provider.findNullKeySerializer(_keyType, _property);
+ keySerializer = provider.findNullKeySerializer(_keyType, _property);
} else {
- // [JACKSON-314] skip entries with null values?
- if (skipNulls && valueElem == null) continue;
- // One twist: is entry ignorable? If so, skip
- if (ignored != null && ignored.contains(keyElem)) continue;
- keySer = _keySerializer;
+ keySerializer = _keySerializer;
}
+ // or by value; nulls often suppressed
+ final Object valueElem = entry.getValue();
+
JsonSerializer<Object> valueSer;
// And then value
if (valueElem == null) {
+ if (suppressableValue != null) { // all suppressions include null-suppression
+ continue;
+ }
valueSer = provider.getDefaultNullValueSerializer();
} else {
+ valueSer = _valueSerializer;
+ if (valueSer == null) {
+ Class<?> cc = valueElem.getClass();
+ valueSer = serializers.serializerFor(cc);
+ if (valueSer == null) {
+ if (_valueType.hasGenericTypes()) {
+ valueSer = _findAndAddDynamic(serializers,
+ provider.constructSpecializedType(_valueType, cc), provider);
+ } else {
+ valueSer = _findAndAddDynamic(serializers, cc, provider);
+ }
+ serializers = _dynamicValueSerializers;
+ }
+ }
+ // also may need to skip non-empty values:
+ if ((suppressableValue == JsonInclude.Include.NON_EMPTY)
+ && valueSer.isEmpty(valueElem)) {
+ continue;
+ }
+ }
+ // and with that, ask filter to handle it
+ prop.reset(keyElem, keySerializer, valueSer);
+ try {
+ filter.serializeAsField(valueElem, jgen, provider, prop);
+ } catch (Exception e) {
+ String keyDesc = ""+keyElem;
+ wrapAndThrow(provider, e, value, keyDesc);
+ }
+ }
+ }
+
+ @Deprecated // since 2.5
+ public void serializeFilteredFields(Map<?,?> value, JsonGenerator gen, SerializerProvider provider,
+ PropertyFilter filter) throws IOException {
+ serializeFilteredFields(value, gen, provider, filter,
+ provider.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES) ? null : JsonInclude.Include.NON_NULL);
+ }
+
+ /**
+ * @since 2.5
+ */
+ protected void serializeTypedFields(Map<?,?> value, JsonGenerator gen, SerializerProvider provider,
+ Object suppressableValue) // since 2.5
+ throws IOException
+ {
+ final HashSet<String> ignored = _ignoredEntries;
+ PropertySerializerMap serializers = _dynamicValueSerializers;
+
+ for (Map.Entry<?,?> entry : value.entrySet()) {
+ Object keyElem = entry.getKey();
+ JsonSerializer<Object> keySerializer;
+ if (keyElem == null) {
+ keySerializer = provider.findNullKeySerializer(_keyType, _property);
+ } else {
+ // One twist: is entry ignorable? If so, skip
+ if (ignored != null && ignored.contains(keyElem)) continue;
+ keySerializer = _keySerializer;
+ }
+ final Object valueElem = entry.getValue();
+
+ // And then value
+ JsonSerializer<Object> valueSer;
+ if (valueElem == null) {
+ if (suppressableValue != null) { // all suppression include null suppression
+ continue;
+ }
+ valueSer = provider.getDefaultNullValueSerializer();
+ keySerializer.serialize(keyElem, gen, provider);
+ provider.defaultSerializeNull(gen);
+ } else {
+ valueSer = _valueSerializer;
Class<?> cc = valueElem.getClass();
valueSer = serializers.serializerFor(cc);
if (valueSer == null) {
@@ -589,69 +778,35 @@
}
serializers = _dynamicValueSerializers;
}
+ // also may need to skip non-empty values:
+ if ((suppressableValue == JsonInclude.Include.NON_EMPTY)
+ && valueSer.isEmpty(valueElem)) {
+ continue;
+ }
}
- prop.reset(keyElem, valueElem, keySer, valueSer);
+ keySerializer.serialize(keyElem, gen, provider);
try {
- filter.serializeAsField(value, jgen, provider, prop);
+ valueSer.serializeWithType(valueElem, gen, provider, _valueTypeSerializer);
} catch (Exception e) {
- // [JACKSON-55] Need to add reference information
String keyDesc = ""+keyElem;
wrapAndThrow(provider, e, value, keyDesc);
}
}
}
-
- protected void serializeTypedFields(Map<?,?> value, JsonGenerator jgen, SerializerProvider provider)
- throws IOException, JsonGenerationException
- {
- final JsonSerializer<Object> keySerializer = _keySerializer;
- JsonSerializer<Object> prevValueSerializer = null;
- Class<?> prevValueClass = null;
- final HashSet<String> ignored = _ignoredEntries;
- final boolean skipNulls = !provider.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES);
-
- for (Map.Entry<?,?> entry : value.entrySet()) {
- Object valueElem = entry.getValue();
- // First, serialize key
- Object keyElem = entry.getKey();
- if (keyElem == null) {
- provider.findNullKeySerializer(_keyType, _property).serialize(null, jgen, provider);
- } else {
- // [JACKSON-314] also may need to skip entries with null values
- if (skipNulls && valueElem == null) continue;
- // One twist: is entry ignorable? If so, skip
- if (ignored != null && ignored.contains(keyElem)) continue;
- keySerializer.serialize(keyElem, jgen, provider);
- }
-
- // And then value
- if (valueElem == null) {
- provider.defaultSerializeNull(jgen);
- } else {
- Class<?> cc = valueElem.getClass();
- JsonSerializer<Object> currSerializer;
- if (cc == prevValueClass) {
- currSerializer = prevValueSerializer;
- } else {
- if (_valueType.hasGenericTypes()) {
- currSerializer = provider.findValueSerializer(provider.constructSpecializedType(_valueType, cc), _property);
- } else {
- currSerializer = provider.findValueSerializer(cc, _property);
- }
- prevValueSerializer = currSerializer;
- prevValueClass = cc;
- }
- try {
- currSerializer.serializeWithType(valueElem, jgen, provider, _valueTypeSerializer);
- } catch (Exception e) {
- // [JACKSON-55] Need to add reference information
- String keyDesc = ""+keyElem;
- wrapAndThrow(provider, e, value, keyDesc);
- }
- }
- }
+
+ @Deprecated // since 2.5
+ protected void serializeTypedFields(Map<?,?> value, JsonGenerator gen, SerializerProvider provider)
+ throws IOException {
+ serializeTypedFields(value, gen, provider,
+ provider.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES) ? null : JsonInclude.Include.NON_NULL);
}
+ /*
+ /**********************************************************
+ /* Schema related functionality
+ /**********************************************************
+ */
+
@Override
public JsonNode getSchema(SerializerProvider provider, Type typeHint)
{
@@ -660,7 +815,7 @@
// there's no way to statically determine the keys, so the "Entries" can't be determined.
return o;
}
-
+
@Override
public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
throws JsonMappingException
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializer.java
index 9310270..c4b44bf 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializer.java
@@ -7,10 +7,10 @@
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
-
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonIntegerFormatVisitor;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonNumberFormatVisitor;
/**
@@ -19,16 +19,35 @@
* like {@link BigInteger} and {@link BigDecimal}.
*/
@JacksonStdImpl
-public final class NumberSerializer
+public class NumberSerializer
extends StdScalarSerializer<Number>
{
- public final static NumberSerializer instance = new NumberSerializer();
+ /**
+ * Static instance that is only to be used for {@link java.lang.Number}.
+ */
+ public final static NumberSerializer instance = new NumberSerializer(Number.class);
- public NumberSerializer() { super(Number.class); }
+ protected final boolean _isInt;
+
+ @Deprecated // since 2.5
+ public NumberSerializer() {
+ super(Number.class);
+ _isInt = false;
+ }
+
+ /**
+ * @since 2.5
+ */
+ public NumberSerializer(Class<? extends Number> rawType) {
+ super(rawType, false);
+ // since this will NOT be constructed for Integer or Long, only case is:
+ _isInt = (rawType == BigInteger.class);
+ }
@Override
public void serialize(Number value, JsonGenerator jgen, SerializerProvider provider) throws IOException
{
+ // should mostly come in as one of these two:
if (value instanceof BigDecimal) {
jgen.writeNumber((BigDecimal) value);
} else if (value instanceof BigInteger) {
@@ -55,17 +74,25 @@
@Override
public JsonNode getSchema(SerializerProvider provider, Type typeHint) {
- return createSchemaNode("number", true);
+ return createSchemaNode(_isInt ? "integer" : "number", true);
}
-
+
@Override
public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException
{
- // Hmmh. What should it be? Ideally should probably indicate BIG_DECIMAL
- // to ensure no information is lost? But probably won't work that well...
- JsonNumberFormatVisitor v2 = visitor.expectNumberFormat(typeHint);
- if (v2 != null) {
- v2.numberType(JsonParser.NumberType.BIG_DECIMAL);
+ if (_isInt) {
+ JsonIntegerFormatVisitor v2 = visitor.expectIntegerFormat(typeHint);
+ if (v2 != null) {
+ v2.numberType(JsonParser.NumberType.BIG_INTEGER);
+ }
+ } else {
+ JsonNumberFormatVisitor v2 = visitor.expectNumberFormat(typeHint);
+ if (v2 != null) {
+ Class<?> h = handledType();
+ if (h == BigDecimal.class) {
+ v2.numberType(JsonParser.NumberType.BIG_DECIMAL);
+ } // otherwise it's for Number... anything we could do there?
+ }
}
}
}
\ No newline at end of file
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 5452a8a..bebc346 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
@@ -187,6 +187,20 @@
/* Actual serialization
/**********************************************************
*/
+
+ @Override
+ public final void serialize(Object[] value, JsonGenerator jgen, SerializerProvider provider)
+ throws IOException, JsonGenerationException
+ {
+ final int len = value.length;
+ if ((len == 1) && provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) {
+ serializeContents(value, jgen, provider);
+ return;
+ }
+ jgen.writeStartArray(len);
+ serializeContents(value, jgen, provider);
+ jgen.writeEndArray();
+ }
@Override
public void serializeContents(Object[] value, JsonGenerator jgen, SerializerProvider provider)
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 81a1b14..19f7929 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
@@ -5,7 +5,6 @@
import java.util.HashMap;
import com.fasterxml.jackson.core.*;
-
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonArrayFormatVisitor;
@@ -118,6 +117,19 @@
public boolean hasSingleElement(boolean[] value) {
return (value.length == 1);
}
+
+ @Override
+ public final void serialize(boolean[] value, JsonGenerator jgen, SerializerProvider provider) throws IOException
+ {
+ final int len = value.length;
+ if ((len == 1) && provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) {
+ serializeContents(value, jgen, provider);
+ return;
+ }
+ jgen.writeStartArray(len);
+ serializeContents(value, jgen, provider);
+ jgen.writeEndArray();
+ }
@Override
public void serializeContents(boolean[] value, JsonGenerator jgen, SerializerProvider provider)
@@ -244,6 +256,19 @@
public boolean hasSingleElement(short[] value) {
return (value.length == 1);
}
+
+ @Override
+ public final void serialize(short[] value, JsonGenerator jgen, SerializerProvider provider) throws IOException
+ {
+ final int len = value.length;
+ if ((len == 1) && provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) {
+ serializeContents(value, jgen, provider);
+ return;
+ }
+ jgen.writeStartArray(len);
+ serializeContents(value, jgen, provider);
+ jgen.writeEndArray();
+ }
@SuppressWarnings("cast")
@Override
@@ -307,7 +332,7 @@
{
// [JACKSON-289] allows serializing as 'sparse' char array too:
if (provider.isEnabled(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS)) {
- jgen.writeStartArray();
+ jgen.writeStartArray(value.length);
_writeArrayContents(jgen, value);
jgen.writeEndArray();
} else {
@@ -401,8 +426,21 @@
}
@Override
+ public final void serialize(int[] value, JsonGenerator jgen, SerializerProvider provider) throws IOException
+ {
+ final int len = value.length;
+ if ((len == 1) && provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) {
+ serializeContents(value, jgen, provider);
+ return;
+ }
+ jgen.writeStartArray(len);
+ serializeContents(value, jgen, provider);
+ jgen.writeEndArray();
+ }
+
+ @Override
public void serializeContents(int[] value, JsonGenerator jgen, SerializerProvider provider)
- throws IOException, JsonGenerationException
+ throws IOException
{
for (int i = 0, len = value.length; i < len; ++i) {
jgen.writeNumber(value[i]);
@@ -463,10 +501,23 @@
public boolean hasSingleElement(long[] value) {
return (value.length == 1);
}
+
+ @Override
+ public final void serialize(long[] value, JsonGenerator jgen, SerializerProvider provider) throws IOException
+ {
+ final int len = value.length;
+ if ((len == 1) && provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) {
+ serializeContents(value, jgen, provider);
+ return;
+ }
+ jgen.writeStartArray(len);
+ serializeContents(value, jgen, provider);
+ jgen.writeEndArray();
+ }
@Override
public void serializeContents(long[] value, JsonGenerator jgen, SerializerProvider provider)
- throws IOException, JsonGenerationException
+ throws IOException
{
if (_valueTypeSerializer != null) {
for (int i = 0, len = value.length; i < len; ++i) {
@@ -541,6 +592,19 @@
public boolean hasSingleElement(float[] value) {
return (value.length == 1);
}
+
+ @Override
+ public final void serialize(float[] value, JsonGenerator jgen, SerializerProvider provider) throws IOException
+ {
+ final int len = value.length;
+ if ((len == 1) && provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) {
+ serializeContents(value, jgen, provider);
+ return;
+ }
+ jgen.writeStartArray(len);
+ serializeContents(value, jgen, provider);
+ jgen.writeEndArray();
+ }
@Override
public void serializeContents(float[] value, JsonGenerator jgen, SerializerProvider provider)
@@ -613,10 +677,22 @@
public boolean hasSingleElement(double[] value) {
return (value.length == 1);
}
+
+ @Override
+ public final void serialize(double[] value, JsonGenerator jgen, SerializerProvider provider) throws IOException
+ {
+ final int len = value.length;
+ if ((len == 1) && provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) {
+ serializeContents(value, jgen, provider);
+ return;
+ }
+ jgen.writeStartArray(len);
+ serializeContents(value, jgen, provider);
+ jgen.writeEndArray();
+ }
@Override
- public void serializeContents(double[] value, JsonGenerator jgen, SerializerProvider provider)
- throws IOException, JsonGenerationException
+ public void serializeContents(double[] value, JsonGenerator jgen, SerializerProvider provider) throws IOException
{
for (int i = 0, len = value.length; i < len; ++i) {
jgen.writeNumber(value[i]);
@@ -630,7 +706,7 @@
@Override
public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
- throws JsonMappingException
+ throws JsonMappingException
{
if (visitor != null) {
JsonArrayFormatVisitor v2 = visitor.expectArrayFormat(typeHint);
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdDelegatingSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdDelegatingSerializer.java
index 3a541bc..b2b65c3 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdDelegatingSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdDelegatingSerializer.java
@@ -21,7 +21,7 @@
*<p>
* Note that although types may be related, they must not be same; trying
* to do this will result in an exception.
- *
+ *
* @since 2.1
*/
public class StdDelegatingSerializer
@@ -107,25 +107,21 @@
public JsonSerializer<?> createContextual(SerializerProvider provider, BeanProperty property)
throws JsonMappingException
{
- // First: if already got serializer to delegate to, contextualize it:
- if (_delegateSerializer != null) {
- if (_delegateSerializer instanceof ContextualSerializer) {
- JsonSerializer<?> ser = provider.handleSecondaryContextualization(_delegateSerializer, property);
- if (ser == _delegateSerializer) {
- return this;
- }
- return withDelegate(_converter, _delegateType, ser);
- }
- return this;
- }
- // Otherwise, need to locate serializer to delegate to. For that we need type information...
+ JsonSerializer<?> delSer = _delegateSerializer;
JavaType delegateType = _delegateType;
- if (delegateType == null) {
- delegateType = _converter.getOutputType(provider.getTypeFactory());
+
+ if (delSer == null) {
+ // Otherwise, need to locate serializer to delegate to. For that we need type information...
+ if (delegateType == null) {
+ delegateType = _converter.getOutputType(provider.getTypeFactory());
+ }
+ delSer = provider.findValueSerializer(delegateType);
}
- // and then find the thing...
- return withDelegate(_converter, delegateType,
- provider.findValueSerializer(delegateType, property));
+ if (delSer instanceof ContextualSerializer) {
+ delSer = provider.handleSecondaryContextualization(delSer, property);
+ }
+ return (delSer == _delegateSerializer) ? this
+ : withDelegate(_converter, delegateType, delSer);
}
/*
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 d485016..b946a24 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
@@ -243,6 +243,11 @@
BeanProperty prop, JsonSerializer<?> existingSerializer)
throws JsonMappingException
{
+ /* 19-Oct-2014, tatu: As per [databind#357], need to avoid infinite loop
+ * when applying contextual content converter; this is not ideal way,
+ * but should work for most cases.
+ */
+
final AnnotationIntrospector intr = provider.getAnnotationIntrospector();
if (intr != null && prop != null) {
Object convDef = intr.findSerializationContentConverter(prop.getMember());
@@ -250,7 +255,7 @@
Converter<Object,Object> conv = provider.converterInstance(prop.getMember(), convDef);
JavaType delegateType = conv.getOutputType(provider.getTypeFactory());
if (existingSerializer == null) {
- existingSerializer = provider.findValueSerializer(delegateType, prop);
+ existingSerializer = provider.findValueSerializer(delegateType);
}
return new StdDelegatingSerializer(conv, delegateType, existingSerializer);
}
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 f7a54a2..5e0544b 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
@@ -37,19 +37,27 @@
*/
public ToStringSerializer() { super(Object.class); }
+ /**
+ * Sometimes it may actually make sense to retain actual handled type, so...
+ *
+ * @since 2.5
+ */
+ public ToStringSerializer(Class<?> handledType) {
+ super(handledType, false);
+ }
+
@Override
public boolean isEmpty(Object value) {
if (value == null) {
return true;
}
String str = value.toString();
- // would use String.isEmpty(), but that's JDK 1.6
- return (str == null) || (str.length() == 0);
+ return str.isEmpty();
}
@Override
public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider)
- throws IOException, JsonGenerationException
+ throws IOException
{
jgen.writeString(value.toString());
}
@@ -68,7 +76,7 @@
@Override
public void serializeWithType(Object value, JsonGenerator jgen, SerializerProvider provider,
TypeSerializer typeSer)
- throws IOException, JsonGenerationException
+ throws IOException
{
typeSer.writeTypePrefixForScalar(value, jgen);
serialize(value, jgen, provider);
@@ -76,15 +84,12 @@
}
@Override
- public JsonNode getSchema(SerializerProvider provider, Type typeHint)
- throws JsonMappingException
- {
+ public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException {
return createSchemaNode("string", true);
}
@Override
- public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
- throws JsonMappingException
+ public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException
{
if (visitor != null) {
visitor.expectStringFormat(typeHint);
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 673e704..b40f6eb 100644
--- a/src/main/java/com/fasterxml/jackson/databind/type/ArrayType.java
+++ b/src/main/java/com/fasterxml/jackson/databind/type/ArrayType.java
@@ -200,6 +200,15 @@
if (index == 0) return "E";
return null;
}
+
+ /**
+ * No parameterization for array types themselves; element type
+ * may obviously have parameterization.
+ */
+ @Override
+ public Class<?> getParameterSource() {
+ return null;
+ }
/*
/**********************************************************
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 963b082..9e696ee 100644
--- a/src/main/java/com/fasterxml/jackson/databind/type/CollectionLikeType.java
+++ b/src/main/java/com/fasterxml/jackson/databind/type/CollectionLikeType.java
@@ -133,6 +133,13 @@
return null;
}
+ // TODO: should allow construction of instances that do refer
+ // to parameterization, since it is NOT Collection
+ @Override
+ public Class<?> getParameterSource() {
+ return null;
+ }
+
@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 530a1e2..f9e5e6d 100644
--- a/src/main/java/com/fasterxml/jackson/databind/type/CollectionType.java
+++ b/src/main/java/com/fasterxml/jackson/databind/type/CollectionType.java
@@ -88,6 +88,17 @@
return new CollectionType(_class, _elementType.withStaticTyping(),
_valueHandler, _typeHandler, true);
}
+
+ /*
+ /**********************************************************
+ /* Overridden accessors
+ /**********************************************************
+ */
+
+ @Override
+ public Class<?> getParameterSource() {
+ return java.util.Collection.class;
+ }
/*
/**********************************************************
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 fc39074..f0708f8 100644
--- a/src/main/java/com/fasterxml/jackson/databind/type/MapLikeType.java
+++ b/src/main/java/com/fasterxml/jackson/databind/type/MapLikeType.java
@@ -179,6 +179,13 @@
return null;
}
+ // TODO: should allow construction of instances that do refer
+ // to parameterization, since it is NOT Map
+ @Override
+ public Class<?> getParameterSource() {
+ return null;
+ }
+
@Override
public StringBuilder getErasedSignature(StringBuilder sb) {
return _classSignature(_class, sb, true);
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/MapType.java b/src/main/java/com/fasterxml/jackson/databind/type/MapType.java
index 2b14896..81859ad 100644
--- a/src/main/java/com/fasterxml/jackson/databind/type/MapType.java
+++ b/src/main/java/com/fasterxml/jackson/databind/type/MapType.java
@@ -108,6 +108,17 @@
/*
/**********************************************************
+ /* Overridden accessors
+ /**********************************************************
+ */
+
+ @Override
+ public Class<?> getParameterSource() {
+ return java.util.Map.class;
+ }
+
+ /*
+ /**********************************************************
/* Extended API
/**********************************************************
*/
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 b7c89b2..b19b633 100644
--- a/src/main/java/com/fasterxml/jackson/databind/type/SimpleType.java
+++ b/src/main/java/com/fasterxml/jackson/databind/type/SimpleType.java
@@ -16,6 +16,14 @@
private static final long serialVersionUID = -800374828948534376L;
/**
+ * In case there are resolved type parameters, this field stores reference
+ * to that type. It must be {@link #getRawClass()} or its supertype.
+ *
+ * @since 2.5
+ */
+ protected final Class<?> _typeParametersFor;
+
+ /**
* Generic type arguments for this type.
*/
protected final JavaType[] _typeParameters;
@@ -33,12 +41,30 @@
*/
protected SimpleType(Class<?> cls) {
- this(cls, null, null, null, null, false);
+ this(cls, null, null, null, null, false, null);
}
+ /**
+ * @deprecated Since 2.5 use variant that takes one more argument
+ */
+ @Deprecated
protected SimpleType(Class<?> cls, String[] typeNames, JavaType[] typeParams,
Object valueHandler, Object typeHandler, boolean asStatic)
{
+ this(cls, typeNames, typeParams, valueHandler, typeHandler, asStatic, null);
+ }
+
+ /**
+ *
+ * @param parametersFrom Interface or abstract class implemented by this type,
+ * and for which type parameters apply. It may be <code>cls</code> itself,
+ * but more commonly it is one of its supertypes.
+ */
+ protected SimpleType(Class<?> cls,
+ String[] typeNames, JavaType[] typeParams,
+ Object valueHandler, Object typeHandler, boolean asStatic,
+ Class<?> parametersFrom)
+ {
super(cls, 0, valueHandler, typeHandler, asStatic);
if (typeNames == null || typeNames.length == 0) {
_typeNames = null;
@@ -47,6 +73,7 @@
_typeNames = typeNames;
_typeParameters = typeParams;
}
+ _typeParametersFor = parametersFrom;
}
/**
@@ -56,7 +83,7 @@
* not in same package
*/
public static SimpleType constructUnsafe(Class<?> raw) {
- return new SimpleType(raw, null, null, null, null, false);
+ return new SimpleType(raw, null, null, null, null, false, null);
}
@Override
@@ -64,7 +91,7 @@
{
// Should we check that there is a sub-class relationship?
return new SimpleType(subclass, _typeNames, _typeParameters, _valueHandler, _typeHandler,
- _asStatic);
+ _asStatic, _typeParametersFor);
}
@Override
@@ -102,7 +129,7 @@
@Override
public SimpleType withTypeHandler(Object h)
{
- return new SimpleType(_class, _typeNames, _typeParameters, _valueHandler, h, _asStatic);
+ return new SimpleType(_class, _typeNames, _typeParameters, _valueHandler, h, _asStatic, _typeParametersFor);
}
@Override
@@ -116,7 +143,7 @@
if (h == _valueHandler) {
return this;
}
- return new SimpleType(_class, _typeNames, _typeParameters, h, _typeHandler, _asStatic);
+ return new SimpleType(_class, _typeNames, _typeParameters, h, _typeHandler, _asStatic, _typeParametersFor);
}
@Override
@@ -128,7 +155,7 @@
@Override
public SimpleType withStaticTyping() {
return _asStatic ? this : new SimpleType(_class,
- _typeNames, _typeParameters, _valueHandler, _typeHandler, _asStatic);
+ _typeNames, _typeParameters, _valueHandler, _typeHandler, _asStatic, _typeParametersFor);
}
@Override
@@ -183,6 +210,11 @@
}
return _typeNames[index];
}
+
+ @Override
+ public Class<?> getParameterSource() {
+ return _typeParametersFor;
+ }
@Override
public StringBuilder getErasedSignature(StringBuilder sb) {
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 13504dc..d32e074 100644
--- a/src/main/java/com/fasterxml/jackson/databind/type/TypeFactory.java
+++ b/src/main/java/com/fasterxml/jackson/databind/type/TypeFactory.java
@@ -255,8 +255,7 @@
* case actually fully works; and former mostly works. In future may need to
* rewrite former part, which requires changes to JavaType as well.
*/
- Class<?> raw = type.getRawClass();
- if (raw == expType) {
+ if (expType == type.getParameterSource()) {
// Direct type info; good since we can return it as is
int count = type.containedTypeCount();
if (count == 0) return null;
@@ -272,13 +271,14 @@
* if/when there are problems; current handling is an improvement over earlier
* code.
*/
+ Class<?> raw = type.getRawClass();
return findTypeParameters(raw, expType, new TypeBindings(this, type));
}
public JavaType[] findTypeParameters(Class<?> clz, Class<?> expType) {
return findTypeParameters(clz, expType, new TypeBindings(this, clz));
}
-
+
public JavaType[] findTypeParameters(Class<?> clz, Class<?> expType, TypeBindings bindings)
{
// First: find full inheritance chain
@@ -383,8 +383,7 @@
// simple class?
if (type instanceof Class<?>) {
- Class<?> cls = (Class<?>) type;
- resultType = _fromClass(cls, context);
+ resultType = _fromClass((Class<?>) type, context);
}
// But if not, need to start resolving.
else if (type instanceof ParameterizedType) {
@@ -525,21 +524,29 @@
/**
* Method for constructing a type instance with specified parameterization.
+ *
+ * @deprecated Since 2.5, use variant that takes one more argument
*/
- public JavaType constructSimpleType(Class<?> rawType, JavaType[] parameterTypes)
+ @Deprecated
+ public JavaType constructSimpleType(Class<?> rawType, JavaType[] parameterTypes) {
+ return constructSimpleType(rawType, rawType, parameterTypes);
+ }
+
+ public JavaType constructSimpleType(Class<?> rawType, Class<?> parameterTarget,
+ JavaType[] parameterTypes)
{
// Quick sanity check: must match numbers of types with expected...
- TypeVariable<?>[] typeVars = rawType.getTypeParameters();
+ TypeVariable<?>[] typeVars = parameterTarget.getTypeParameters();
if (typeVars.length != parameterTypes.length) {
throw new IllegalArgumentException("Parameter type mismatch for "+rawType.getName()
- +": expected "+typeVars.length+" parameters, was given "+parameterTypes.length);
+ +" (and target "+parameterTarget.getName()+"): expected "+typeVars.length
+ +" parameters, was given "+parameterTypes.length);
}
String[] names = new String[typeVars.length];
for (int i = 0, len = typeVars.length; i < len; ++i) {
names[i] = typeVars[i].getName();
}
- JavaType resultType = new SimpleType(rawType, names, parameterTypes, null, null, false);
- return resultType;
+ return new SimpleType(rawType, names, parameterTypes, null, null, false, parameterTarget);
}
/**
@@ -564,15 +571,31 @@
*<p>
* NOTE: type modifiers are NOT called on constructed type itself; but are called
* for contained types.
+ *
+ * @param parametrized Type-erased type of instance being constructed
+ * @param parametersFor class or interface for which type parameters are applied; either
+ * <code>parametrized</code> or one of its supertypes
+ * @param parameterClasses Type parameters to apply
+ *
+ * @since 2.5
*/
- public JavaType constructParametricType(Class<?> parametrized, Class<?>... parameterClasses)
+ public JavaType constructParametrizedType(Class<?> parametrized, Class<?> parametersFor,
+ Class<?>... parameterClasses)
{
int len = parameterClasses.length;
JavaType[] pt = new JavaType[len];
for (int i = 0; i < len; ++i) {
pt[i] = _fromClass(parameterClasses[i], null);
}
- return constructParametricType(parametrized, pt);
+ return constructParametrizedType(parametrized, parametersFor, pt);
+ }
+
+ /**
+ * @deprecated Since 2.5, use {@link #constructParametrizedType} instead.
+ */
+ @Deprecated
+ public JavaType constructParametricType(Class<?> parametrized, Class<?>... parameterClasses) {
+ return constructParametrizedType(parametrized, parametrized, parameterClasses);
}
/**
@@ -587,8 +610,17 @@
*<p>
* NOTE: type modifiers are NOT called on constructed type itself; but are called
* for contained types.
+ *
+ *
+ * @param parametrized Actual full type
+ * @param parametersFor class or interface for which type parameters are applied; either
+ * <code>parametrized</code> or one of its supertypes
+ * @param parameterTypes Type parameters to apply
+ *
+ * @since 2.5
*/
- public JavaType constructParametricType(Class<?> parametrized, JavaType... parameterTypes)
+ public JavaType constructParametrizedType(Class<?> parametrized, Class<?> parametersFor,
+ JavaType... parameterTypes)
{
JavaType resultType;
@@ -612,11 +644,19 @@
}
resultType = constructCollectionType((Class<Collection<?>>)parametrized, parameterTypes[0]);
} else {
- resultType = constructSimpleType(parametrized, parameterTypes);
+ resultType = constructSimpleType(parametrized, parametersFor, parameterTypes);
}
return resultType;
}
+ /**
+ * @deprecated Since 2.5, use {@link #constructParametrizedType} instead.
+ */
+ @Deprecated
+ public JavaType constructParametricType(Class<?> parametrized, JavaType... parameterTypes) {
+ return constructParametrizedType(parametrized, parametrized, parameterTypes);
+ }
+
/*
/**********************************************************
/* Direct factory methods for "raw" variants, used when
@@ -733,7 +773,20 @@
} else if (Collection.class.isAssignableFrom(clz)) {
result = _collectionType(clz);
} else {
- result = new SimpleType(clz);
+ // 29-Sep-2014, tatu: We may want to pre-resolve well-known generic types
+ if (Map.Entry.class.isAssignableFrom(clz)) {
+ JavaType[] pts = this.findTypeParameters(clz, Map.Entry.class);
+ JavaType kt, vt;
+ if (pts == null || pts.length != 2) {
+ kt = vt = unknownType();
+ } else {
+ kt = pts[0];
+ vt = pts[1];
+ }
+ result = constructSimpleType(clz, Map.Entry.class, new JavaType[] { kt, vt });
+ } else {
+ result = new SimpleType(clz);
+ }
}
_typeCache.put(key, result); // cache object syncs
return result;
@@ -771,8 +824,9 @@
if (paramTypes.size() == 0) {
return new SimpleType(clz);
}
+ // Hmmh. Does this actually occur?
JavaType[] pt = paramTypes.toArray(new JavaType[paramTypes.size()]);
- return constructSimpleType(clz, pt);
+ return constructSimpleType(clz, clz, pt);
}
/**
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/ISO8601Utils.java b/src/main/java/com/fasterxml/jackson/databind/util/ISO8601Utils.java
index 5fce78e..1192450 100644
--- a/src/main/java/com/fasterxml/jackson/databind/util/ISO8601Utils.java
+++ b/src/main/java/com/fasterxml/jackson/databind/util/ISO8601Utils.java
@@ -5,11 +5,15 @@
import java.text.ParseException;
/**
- * Utilities methods for manipulating dates in iso8601 format. This is much much faster and GC friendly than
- * using SimpleDateFormat so highly suitable if you (un)serialize lots of date objects.
+ * Utilities methods for manipulating dates in iso8601 format. This is much much faster and GC friendly than using SimpleDateFormat so
+ * highly suitable if you (un)serialize lots of date objects.
+ *
+ * Supported parse format: [yyyy-MM-dd|yyyyMMdd][T(hh:mm[:ss[.sss]]|hhmm[ss[.sss]])]?[Z|[+-]hh:mm]]
+ *
+ * @see <a href="http://www.w3.org/TR/NOTE-datetime">this specification</a>
*/
-public class ISO8601Utils {
-
+public class ISO8601Utils
+{
/**
* ID to represent the 'GMT' string
*/
@@ -25,21 +29,23 @@
/* Static factories
/**********************************************************
*/
-
+
/**
* Accessor for static GMT timezone instance.
*/
- public static TimeZone timeZoneGMT() { return TIMEZONE_GMT; }
+ public static TimeZone timeZoneGMT() {
+ return TIMEZONE_GMT;
+ }
/*
/**********************************************************
/* Formatting
/**********************************************************
*/
-
+
/**
* Format a date into 'yyyy-MM-ddThh:mm:ssZ' (GMT timezone, no milliseconds precision)
- *
+ *
* @param date the date to format
* @return the date formatted as 'yyyy-MM-ddThh:mm:ssZ'
*/
@@ -49,8 +55,8 @@
/**
* Format a date into 'yyyy-MM-ddThh:mm:ss[.sss]Z' (GMT timezone)
- *
- * @param date the date to format
+ *
+ * @param date the date to format
* @param millis true to include millis precision otherwise false
* @return the date formatted as 'yyyy-MM-ddThh:mm:ss[.sss]Z'
*/
@@ -60,10 +66,10 @@
/**
* Format date into yyyy-MM-ddThh:mm:ss[.sss][Z|[+-]hh:mm]
- *
- * @param date the date to format
+ *
+ * @param date the date to format
* @param millis true to include millis precision otherwise false
- * @param tz timezone to use for the formatting (GMT will produce 'Z')
+ * @param tz timezone to use for the formatting (GMT will produce 'Z')
* @return the date formatted as yyyy-MM-ddThh:mm:ss[.sss][Z|[+-]hh:mm]
*/
public static String format(Date date, boolean millis, TimeZone tz) {
@@ -114,48 +120,68 @@
*/
/**
- * Parse a date from ISO-8601 formatted string. It expects a format yyyy-MM-ddThh:mm:ss[.sss][Z|[+-]hh:mm]
- *
+ * Parse a date from ISO-8601 formatted string. It expects a format
+ * [yyyy-MM-dd|yyyyMMdd][T(hh:mm[:ss[.sss]]|hhmm[ss[.sss]])]?[Z|[+-]hh:mm]]
+ *
* @param date ISO string to parse in the appropriate format.
* @param pos The position to start parsing from, updated to where parsing stopped.
* @return the parsed date
* @throws ParseException if the date is not in the appropriate format
*/
- public static Date parse(String date, ParsePosition pos) throws ParseException
- {
+ public static Date parse(String date, ParsePosition pos) throws ParseException {
Exception fail = null;
try {
int offset = pos.getIndex();
// extract year
int year = parseInt(date, offset, offset += 4);
- checkOffset(date, offset, '-');
+ if (checkOffset(date, offset, '-')) {
+ offset += 1;
+ }
// extract month
- int month = parseInt(date, offset += 1, offset += 2);
- checkOffset(date, offset, '-');
+ int month = parseInt(date, offset, offset += 2);
+ if (checkOffset(date, offset, '-')) {
+ offset += 1;
+ }
// extract day
- int day = parseInt(date, offset += 1, offset += 2);
- checkOffset(date, offset, 'T');
-
- // extract hours, minutes, seconds and milliseconds
- int hour = parseInt(date, offset += 1, offset += 2);
- checkOffset(date, offset, ':');
-
- int minutes = parseInt(date, offset += 1, offset += 2);
- checkOffset(date, offset, ':');
-
- int seconds = parseInt(date, offset += 1, offset += 2);
- // milliseconds can be optional in the format
+ int day = parseInt(date, offset, offset += 2);
+ // default time value
+ int hour = 0;
+ int minutes = 0;
+ int seconds = 0;
int milliseconds = 0; // always use 0 otherwise returned date will include millis of current time
- if (date.charAt(offset) == '.') {
- checkOffset(date, offset, '.');
- milliseconds = parseInt(date, offset += 1, offset += 3);
+ if (checkOffset(date, offset, 'T')) {
+
+ // extract hours, minutes, seconds and milliseconds
+ hour = parseInt(date, offset += 1, offset += 2);
+ if (checkOffset(date, offset, ':')) {
+ offset += 1;
+ }
+
+ minutes = parseInt(date, offset, offset += 2);
+ if (checkOffset(date, offset, ':')) {
+ offset += 1;
+ }
+ // second and milliseconds can be optional
+ if (date.length() > offset) {
+ char c = date.charAt(offset);
+ if (c != 'Z' && c != '+' && c != '-') {
+ seconds = parseInt(date, offset, offset += 2);
+ // milliseconds can be optional in the format
+ if (checkOffset(date, offset, '.')) {
+ milliseconds = parseInt(date, offset += 1, offset += 3);
+ }
+ }
+ }
}
// extract timezone
String timezoneId;
+ if (date.length() <= offset) {
+ throw new IllegalArgumentException("No time zone indicator");
+ }
char timezoneIndicator = date.charAt(offset);
if (timezoneIndicator == '+' || timezoneIndicator == '-') {
String timezoneOffset = date.substring(offset);
@@ -185,8 +211,8 @@
pos.setIndex(offset);
return calendar.getTime();
- //If we get a ParseException it'll already have the right message/offset.
- //Other exception types can convert here.
+ // If we get a ParseException it'll already have the right message/offset.
+ // Other exception types can convert here.
} catch (IndexOutOfBoundsException e) {
fail = e;
} catch (NumberFormatException e) {
@@ -194,32 +220,28 @@
} catch (IllegalArgumentException e) {
fail = e;
}
- String input = (date == null) ? null : ('"'+date+"'");
- throw new ParseException("Failed to parse date ["+input
- +"]: "+fail.getMessage(), pos.getIndex());
+ String input = (date == null) ? null : ('"' + date + "'");
+ throw new ParseException("Failed to parse date [" + input + "]: " + fail.getMessage(), pos.getIndex());
}
/**
- * Check if the expected character exist at the given offset of the
- *
- * @param value the string to check at the specified offset
- * @param offset the offset to look for the expected character
+ * Check if the expected character exist at the given offset in the value.
+ *
+ * @param value the string to check at the specified offset
+ * @param offset the offset to look for the expected character
* @param expected the expected character
- * @throws IndexOutOfBoundsException if the expected character is not found
+ * @return true if the expected character exist at the given offset
*/
- private static void checkOffset(String value, int offset, char expected) throws ParseException {
- char found = value.charAt(offset);
- if (found != expected) {
- throw new ParseException("Expected '" + expected + "' character but found '" + found + "'", offset);
- }
+ private static boolean checkOffset(String value, int offset, char expected) {
+ return (offset < value.length()) && (value.charAt(offset) == expected);
}
/**
* Parse an integer located between 2 given offsets in a string
- *
- * @param value the string to parse
+ *
+ * @param value the string to parse
* @param beginIndex the start index for the integer in the string
- * @param endIndex the end index for the integer in the string
+ * @param endIndex the end index for the integer in the string
* @return the int
* @throws NumberFormatException if the value is not a number
*/
@@ -251,9 +273,9 @@
/**
* Zero pad a number to a specified length
- *
+ *
* @param buffer buffer to use for padding
- * @param value the integer value to pad if necessary.
+ * @param value the integer value to pad if necessary.
* @param length the length of the string we should zero pad
*/
private static void padInt(StringBuilder buffer, int value, int length) {
@@ -264,4 +286,3 @@
buffer.append(strValue);
}
}
-
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/LRUMap.java b/src/main/java/com/fasterxml/jackson/databind/util/LRUMap.java
index 8f37121..e67a765 100644
--- a/src/main/java/com/fasterxml/jackson/databind/util/LRUMap.java
+++ b/src/main/java/com/fasterxml/jackson/databind/util/LRUMap.java
@@ -13,7 +13,12 @@
* NOTE: since version 2.4.2, this is <b>NOT</b> an LRU-based at all; reason
* being that it is not possible to use JDK components that do LRU _AND_ perform
* well wrt synchronization on multi-core systems. So we choose efficient synchronization
- * over potentially more effecient handling of entries.
+ * over potentially more efficient handling of entries.
+ *<p>
+ * And yes, there are efficient LRU implementations such as
+ * <a href="https://code.google.com/p/concurrentlinkedhashmap/">concurrentlinkedhashmap</a>;
+ * but at this point we really try to keep external deps to minimum. But perhaps
+ * a shaded variant may be used one day.
*/
public class LRUMap<K,V>
implements java.io.Serializable
@@ -43,6 +48,22 @@
return _map.put(key, value);
}
+ /**
+ * @since 2.5
+ */
+ public V putIfAbsent(K key, V value) {
+ // not 100% optimal semantically, but better from correctness (never exceeds
+ // defined maximum) and close enough all in all:
+ if (_map.size() >= _maxEntries) {
+ synchronized (this) {
+ if (_map.size() >= _maxEntries) {
+ clear();
+ }
+ }
+ }
+ return _map.putIfAbsent(key, value);
+ }
+
// NOTE: key is of type Object only to retain binary backwards-compatibility
public V get(Object key) { return _map.get(key); }
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/PrimitiveArrayBuilder.java b/src/main/java/com/fasterxml/jackson/databind/util/PrimitiveArrayBuilder.java
index 468f392..69c902f 100644
--- a/src/main/java/com/fasterxml/jackson/databind/util/PrimitiveArrayBuilder.java
+++ b/src/main/java/com/fasterxml/jackson/databind/util/PrimitiveArrayBuilder.java
@@ -50,6 +50,8 @@
/**********************************************************
*/
+ public int bufferedSize() { return _bufferedEntryCount; }
+
public T resetAndStart()
{
_reset();
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/SimpleBeanPropertyDefinition.java b/src/main/java/com/fasterxml/jackson/databind/util/SimpleBeanPropertyDefinition.java
index 666efbf..2ff3c0d 100644
--- a/src/main/java/com/fasterxml/jackson/databind/util/SimpleBeanPropertyDefinition.java
+++ b/src/main/java/com/fasterxml/jackson/databind/util/SimpleBeanPropertyDefinition.java
@@ -1,5 +1,8 @@
package com.fasterxml.jackson.databind.util;
+import java.util.Collections;
+import java.util.Iterator;
+
import com.fasterxml.jackson.databind.AnnotationIntrospector;
import com.fasterxml.jackson.databind.PropertyMetadata;
import com.fasterxml.jackson.databind.PropertyName;
@@ -178,6 +181,15 @@
return (_member instanceof AnnotatedParameter) ? (AnnotatedParameter) _member : null;
}
+ @Override
+ public Iterator<AnnotatedParameter> getConstructorParameters() {
+ AnnotatedParameter param = getConstructorParameter();
+ if (param == null) {
+ return EmptyIterator.instance();
+ }
+ return Collections.singleton(param).iterator();
+ }
+
/**
* Method used to find accessor (getter, field to access) to use for accessing
* value of the property.
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 58d3e08..2dbb7df 100644
--- a/src/main/java/com/fasterxml/jackson/databind/util/TokenBuffer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/util/TokenBuffer.java
@@ -1129,14 +1129,6 @@
/**********************************************************
*/
- @Deprecated // since 2.3
- protected Parser(Segment firstSeg, ObjectCodec codec) {
- this(firstSeg, codec, false, false);
- }
-
- /**
- * @since 2.3
- */
public Parser(Segment firstSeg, ObjectCodec codec,
boolean hasNativeTypeIds,
boolean hasNativeObjectIds)
diff --git a/src/test/java/com/fasterxml/jackson/databind/BaseMapTest.java b/src/test/java/com/fasterxml/jackson/databind/BaseMapTest.java
index 65c51ce..b7fc64a 100644
--- a/src/test/java/com/fasterxml/jackson/databind/BaseMapTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/BaseMapTest.java
@@ -7,11 +7,8 @@
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
-
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.test.BaseTest;
-
public abstract class BaseMapTest
extends BaseTest
{
@@ -213,9 +210,8 @@
{
return readAndMapFromString(SHARED_MAPPER, input, cls);
}
-
- protected <T> T readAndMapFromString(ObjectMapper m, String input, Class<T> cls)
- throws IOException
+
+ protected <T> T readAndMapFromString(ObjectMapper m, String input, Class<T> cls) throws IOException
{
return (T) m.readValue("\""+input+"\"", cls);
}
@@ -225,7 +221,7 @@
/* Helper methods, other
/**********************************************************
*/
-
+
protected TimeZone getUTCTimeZone() {
return TimeZone.getTimeZone("GMT");
}
@@ -238,7 +234,7 @@
}
}
- protected String aposToQuotes(String json) {
+ protected static String aposToQuotes(String json) {
return json.replace("'", "\"");
}
}
diff --git a/src/test/java/com/fasterxml/jackson/test/BaseTest.java b/src/test/java/com/fasterxml/jackson/databind/BaseTest.java
similarity index 99%
rename from src/test/java/com/fasterxml/jackson/test/BaseTest.java
rename to src/test/java/com/fasterxml/jackson/databind/BaseTest.java
index 0a0d833..8226035 100644
--- a/src/test/java/com/fasterxml/jackson/test/BaseTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/BaseTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.test;
+package com.fasterxml.jackson.databind;
import java.io.*;
import java.util.Arrays;
diff --git a/src/test/java/com/fasterxml/jackson/test/BrokenStringReader.java b/src/test/java/com/fasterxml/jackson/databind/BrokenStringReader.java
similarity index 92%
rename from src/test/java/com/fasterxml/jackson/test/BrokenStringReader.java
rename to src/test/java/com/fasterxml/jackson/databind/BrokenStringReader.java
index 0022290..a4d1a92 100644
--- a/src/test/java/com/fasterxml/jackson/test/BrokenStringReader.java
+++ b/src/test/java/com/fasterxml/jackson/databind/BrokenStringReader.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.test;
+package com.fasterxml.jackson.databind;
import java.io.*;
diff --git a/src/test/java/com/fasterxml/jackson/test/BrokenStringWriter.java b/src/test/java/com/fasterxml/jackson/databind/BrokenStringWriter.java
similarity index 93%
rename from src/test/java/com/fasterxml/jackson/test/BrokenStringWriter.java
rename to src/test/java/com/fasterxml/jackson/databind/BrokenStringWriter.java
index 8bf4248..93bd68a 100644
--- a/src/test/java/com/fasterxml/jackson/test/BrokenStringWriter.java
+++ b/src/test/java/com/fasterxml/jackson/databind/BrokenStringWriter.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.test;
+package com.fasterxml.jackson.databind;
import java.io.*;
diff --git a/src/test/java/com/fasterxml/jackson/databind/TestFormatSchema.java b/src/test/java/com/fasterxml/jackson/databind/TestFormatSchema.java
index a5e6665..0809df0 100644
--- a/src/test/java/com/fasterxml/jackson/databind/TestFormatSchema.java
+++ b/src/test/java/com/fasterxml/jackson/databind/TestFormatSchema.java
@@ -250,7 +250,7 @@
StringReader r = new StringReader("{}");
// bit ugly, but can't think of cleaner simple way to check this...
try {
- mapper.reader(s).withType(Object.class).readValue(r);
+ mapper.reader(s).forType(Object.class).readValue(r);
fail("Excpected exception");
} catch (SchemaException e) {
assertSame(s, e._schema);
diff --git a/src/test/java/com/fasterxml/jackson/databind/TestHandlerInstantiation.java b/src/test/java/com/fasterxml/jackson/databind/TestHandlerInstantiation.java
index d694c5a..afb3ebf 100644
--- a/src/test/java/com/fasterxml/jackson/databind/TestHandlerInstantiation.java
+++ b/src/test/java/com/fasterxml/jackson/databind/TestHandlerInstantiation.java
@@ -6,7 +6,6 @@
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
-
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
@@ -20,6 +19,7 @@
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
+import com.fasterxml.jackson.databind.jsontype.impl.TypeIdResolverBase;
import com.fasterxml.jackson.databind.type.TypeFactory;
public class TestHandlerInstantiation extends BaseMapTest
@@ -111,7 +111,7 @@
}
// copied from "TestCustomTypeIdResolver"
- static class CustomIdResolver implements TypeIdResolver
+ static class CustomIdResolver extends TypeIdResolverBase
{
static List<JavaType> initTypes;
@@ -144,7 +144,7 @@
}
}
@Override
- public JavaType typeFromId(String id)
+ public JavaType typeFromId(DatabindContext context, String id)
{
if (id.equals(_id)) {
return TypeFactory.defaultInstance().constructType(TypeIdBean.class);
diff --git a/src/test/java/com/fasterxml/jackson/databind/TestJDKSerialization.java b/src/test/java/com/fasterxml/jackson/databind/TestJDKSerialization.java
index a2541e4..5c438a0 100644
--- a/src/test/java/com/fasterxml/jackson/databind/TestJDKSerialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/TestJDKSerialization.java
@@ -16,7 +16,7 @@
{
static class MyPojo {
public int x;
- private int y;
+ protected int y;
public MyPojo() { }
public MyPojo(int x0, int y0) {
diff --git a/src/test/java/com/fasterxml/jackson/databind/TestObjectMapper.java b/src/test/java/com/fasterxml/jackson/databind/TestObjectMapper.java
index 6a6ab67..e21809f 100644
--- a/src/test/java/com/fasterxml/jackson/databind/TestObjectMapper.java
+++ b/src/test/java/com/fasterxml/jackson/databind/TestObjectMapper.java
@@ -1,10 +1,10 @@
package com.fasterxml.jackson.databind;
import java.io.*;
+import java.util.*;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
-
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.DefaultDeserializationContext;
import com.fasterxml.jackson.databind.node.*;
@@ -108,14 +108,23 @@
public void testProviderConfig() throws Exception
{
ObjectMapper m = new ObjectMapper();
+ final String JSON = "{ \"x\" : 3 }";
assertEquals(0, m._deserializationContext._cache.cachedDeserializersCount());
// and then should get one constructed for:
- Bean bean = m.readValue("{ \"x\" : 3 }", Bean.class);
+ Bean bean = m.readValue(JSON, Bean.class);
assertNotNull(bean);
assertEquals(1, m._deserializationContext._cache.cachedDeserializersCount());
m._deserializationContext._cache.flushCachedDeserializers();
assertEquals(0, m._deserializationContext._cache.cachedDeserializersCount());
+
+ // 07-Nov-2014, tatu: As per [databind#604] verify that Maps also get cached
+ m = new ObjectMapper();
+ List<?> stuff = m.readValue("[ ]", List.class);
+ assertNotNull(stuff);
+ // may look odd, but due to "Untyped" deserializer thing, we actually have
+ // 3 deserializers (List<?>, Map<?,?>, Object)
+ assertEquals(3, m._deserializationContext._cache.cachedDeserializersCount());
}
// [Issue#28]: ObjectMapper.copy()
@@ -152,7 +161,7 @@
assertEquals(0, m.getDeserializationConfig().mixInCount());
assertEquals(0, m2.getDeserializationConfig().mixInCount());
- m.addMixInAnnotations(String.class, Integer.class);
+ m.addMixIn(String.class, Integer.class);
assertEquals(1, m.getSerializationConfig().mixInCount());
assertEquals(0, m2.getSerializationConfig().mixInCount());
assertEquals(1, m.getDeserializationConfig().mixInCount());
diff --git a/src/test/java/com/fasterxml/jackson/databind/TestObjectMapperBeanDeserializer.java b/src/test/java/com/fasterxml/jackson/databind/TestObjectMapperBeanDeserializer.java
index 1263c2f..6778274 100644
--- a/src/test/java/com/fasterxml/jackson/databind/TestObjectMapperBeanDeserializer.java
+++ b/src/test/java/com/fasterxml/jackson/databind/TestObjectMapperBeanDeserializer.java
@@ -10,7 +10,6 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
-import com.fasterxml.jackson.test.BaseTest;
/**
* Unit tests for verifying deserialization of Beans.
diff --git a/src/test/java/com/fasterxml/jackson/databind/TestObjectMapperBeanSerializer.java b/src/test/java/com/fasterxml/jackson/databind/TestObjectMapperBeanSerializer.java
index 2c03177..fa0d802 100644
--- a/src/test/java/com/fasterxml/jackson/databind/TestObjectMapperBeanSerializer.java
+++ b/src/test/java/com/fasterxml/jackson/databind/TestObjectMapperBeanSerializer.java
@@ -8,7 +8,6 @@
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.test.BaseTest;
/**
* This unit test suite tries to verify that the "Native" java type
@@ -137,8 +136,7 @@
THIS_IS_AN_ENUM_VALUE_3,
}
- @SuppressWarnings("unused")
- private static class FixtureObjectBase
+ static class FixtureObjectBase
{
public static final String VALUE_STRING = "foobar";
public static final EFixtureEnum VALUE_ENUM = EFixtureEnum.THIS_IS_AN_ENUM_VALUE_2;
@@ -227,11 +225,9 @@
}
}
- @SuppressWarnings("unused")
- private static class FixtureObject extends FixtureObjectBase
+ static class FixtureObject extends FixtureObjectBase
{
- public Exception getTestError()
- {
+ public Exception getTestError() {
return new Exception(VALUE_ERRTXT);
}
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/TestParserUsingMapper.java b/src/test/java/com/fasterxml/jackson/databind/TestParserUsingMapper.java
index 8f8ebf3..1b8f6b1 100644
--- a/src/test/java/com/fasterxml/jackson/databind/TestParserUsingMapper.java
+++ b/src/test/java/com/fasterxml/jackson/databind/TestParserUsingMapper.java
@@ -8,7 +8,7 @@
import com.fasterxml.jackson.core.io.SerializedString;
import com.fasterxml.jackson.databind.ObjectMapper;
-public class TestParserUsingMapper extends com.fasterxml.jackson.test.BaseTest
+public class TestParserUsingMapper extends BaseMapTest
{
final static int TWO_BYTE_ESCAPED = 0x111;
final static int THREE_BYTE_ESCAPED = 0x1111;
@@ -169,6 +169,6 @@
{
ObjectMapper mapper = new ObjectMapper();
mapper.configure(JsonGenerator.Feature.ESCAPE_NON_ASCII, true);
- assertEquals(quote("\\u0101"), mapper.writeValueAsString(String.valueOf((char) 257)));
+ mapper.writeValueAsString(String.valueOf((char) 257));
}
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/TestReadValues.java b/src/test/java/com/fasterxml/jackson/databind/TestReadValues.java
index 54da2b2..5ed7db5 100644
--- a/src/test/java/com/fasterxml/jackson/databind/TestReadValues.java
+++ b/src/test/java/com/fasterxml/jackson/databind/TestReadValues.java
@@ -17,8 +17,16 @@
static class Bean {
public int a;
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || o.getClass() != getClass()) return false;
+ Bean other = (Bean) o;
+ return other.a == this.a;
+ }
+ @Override public int hashCode() { return a; }
}
-
+
/*
/**********************************************************
/* Unit tests; root-level value sequences via Mapper
@@ -30,9 +38,10 @@
public void testRootBeans() throws Exception
{
final String JSON = "{\"a\":3}{\"a\":27} ";
- Iterator<Bean> it = MAPPER.reader(Bean.class).readValues(JSON);
- assertNotNull(((MappingIterator<?>) it).getCurrentLocation());
+ MappingIterator<Bean> it = MAPPER.reader(Bean.class).readValues(JSON);
+
+ assertNotNull(it.getCurrentLocation());
assertTrue(it.hasNext());
Bean b = it.next();
assertEquals(3, b.a);
@@ -40,6 +49,19 @@
b = it.next();
assertEquals(27, b.a);
assertFalse(it.hasNext());
+ it.close();
+
+ // Also, test 'readAll()'
+ it = MAPPER.reader(Bean.class).readValues(JSON);
+ List<Bean> all = it.readAll();
+ assertEquals(2, all.size());
+ it.close();
+
+ it = MAPPER.reader(Bean.class).readValues("{\"a\":3}{\"a\":3}");
+ Set<Bean> set = it.readAll(new HashSet<Bean>());
+ assertEquals(HashSet.class, set.getClass());
+ assertEquals(1, set.size());
+ assertEquals(3, set.iterator().next().a);
}
public void testRootMaps() throws Exception
@@ -198,7 +220,7 @@
+"{\"boy\": \"howdy\", \"huh\": \"what\"}]";
final MappingIterator<Map<String, Object>> iterator = MAPPER
.reader()
- .withType(new TypeReference<Map<String, Object>>(){})
+ .forType(new TypeReference<Map<String, Object>>(){})
.readValues(JSON);
Map<String,Object> map;
diff --git a/src/test/java/com/fasterxml/jackson/databind/TestVersions.java b/src/test/java/com/fasterxml/jackson/databind/TestVersions.java
index 5b5df44..182e9c6 100644
--- a/src/test/java/com/fasterxml/jackson/databind/TestVersions.java
+++ b/src/test/java/com/fasterxml/jackson/databind/TestVersions.java
@@ -11,7 +11,7 @@
* Tests to ensure that we get proper Version information via
* things defined as Versioned.
*/
-public class TestVersions extends com.fasterxml.jackson.test.BaseTest
+public class TestVersions extends BaseMapTest
{
public void testMapperVersions()
{
diff --git a/src/test/java/com/fasterxml/jackson/databind/access/TestSerAnyGetter.java b/src/test/java/com/fasterxml/jackson/databind/access/TestSerAnyGetter.java
index 66b5b86..4ca471b 100644
--- a/src/test/java/com/fasterxml/jackson/databind/access/TestSerAnyGetter.java
+++ b/src/test/java/com/fasterxml/jackson/databind/access/TestSerAnyGetter.java
@@ -34,7 +34,7 @@
}
}
- private static class PrivateThing
+ static class PrivateThing
{
@JsonAnyGetter
public Map<?,?> getProperties()
diff --git a/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextAttributeWithDeser.java b/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextAttributeWithDeser.java
index cdfb75f..adf8a55 100644
--- a/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextAttributeWithDeser.java
+++ b/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextAttributeWithDeser.java
@@ -72,9 +72,9 @@
// as above, should not carry on state
TestPOJO pojo2 = MAPPER.reader(TestPOJO.class)
- .withAttribute(KEY, Integer.valueOf(3))
+ .withAttribute(KEY, Integer.valueOf(5))
.readValue(INPUT);
- assertEquals("x/3", pojo2.value);
+ assertEquals("x/5", pojo2.value);
}
public void testHierarchic() throws Exception
diff --git a/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualKeyTypes.java b/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualKeyTypes.java
index 3d56412..6705e2c 100644
--- a/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualKeyTypes.java
+++ b/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualKeyTypes.java
@@ -96,7 +96,7 @@
mapper.registerModule(module);
Map<String,Object> input = new HashMap<String,Object>();
input.put("a", Integer.valueOf(3));
- String json = mapper.writerWithType(TypeFactory.defaultInstance().constructMapType(HashMap.class, String.class, Object.class))
+ String json = mapper.writerFor(TypeFactory.defaultInstance().constructMapType(HashMap.class, String.class, Object.class))
.writeValueAsString(input);
assertEquals("{\"prefix:a\":3}", json);
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/SingleArgCreatorTest.java b/src/test/java/com/fasterxml/jackson/databind/creators/SingleArgCreatorTest.java
index 0e4ce69..e2a9f00 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/SingleArgCreatorTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/creators/SingleArgCreatorTest.java
@@ -1,15 +1,16 @@
package com.fasterxml.jackson.databind.creators;
import com.fasterxml.jackson.annotation.*;
-
import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
+import com.fasterxml.jackson.databind.introspect.AnnotatedParameter;
+import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
public class SingleArgCreatorTest extends BaseMapTest
{
- // [Issue#430]: single arg BUT named; should not delegate
+ // [databind#430]: single arg BUT named; should not delegate
static class SingleNamedStringBean {
-
final String _ss;
@JsonCreator
@@ -20,6 +21,63 @@
public String getSs() { return _ss; }
}
+ // For [databind#614]
+ static class SingleNamedButStillDelegating {
+ protected final String value;
+
+ @JsonCreator(mode=JsonCreator.Mode.DELEGATING)
+ public SingleNamedButStillDelegating(@JsonProperty("foobar") String v){
+ value = v;
+ }
+
+ public String getFoobar() { return "x"; }
+ }
+
+ // [databind#557]
+
+ static class StringyBean
+ {
+ public final String value;
+
+ private StringyBean(String value) { this.value = value; }
+
+ public String getValue() {
+ return value;
+ }
+ }
+
+ static class StringyBeanWithProps
+ {
+ public final String value;
+
+ @JsonCreator
+ private StringyBeanWithProps(String v) { value = v; }
+
+ public String getValue() {
+ return value;
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static class MyParamIntrospector extends JacksonAnnotationIntrospector
+ {
+ private final String name;
+
+ public MyParamIntrospector(String n) { name = n; }
+
+ @Override
+ public String findImplicitPropertyName(AnnotatedMember param) {
+ if (param instanceof AnnotatedParameter) {
+ AnnotatedParameter ap = (AnnotatedParameter) param;
+ switch (ap.getIndex()) {
+ case 0: return name;
+ }
+ return "param"+ap.getIndex();
+ }
+ return super.findImplicitPropertyName(param);
+ }
+ }
+
/*
/**********************************************************
/* Test methods
@@ -34,4 +92,30 @@
SingleNamedStringBean.class);
assertEquals("foobar", bean._ss);
}
+
+ public void testSingleStringArgWithImplicitName() throws Exception
+ {
+ final ObjectMapper mapper = new ObjectMapper();
+ mapper.setAnnotationIntrospector(new MyParamIntrospector("value"));
+ StringyBean bean = mapper.readValue(quote("foobar"), StringyBean.class);
+ assertEquals("foobar", bean.getValue());
+ }
+
+ // [databind#714]
+ public void testSingleImplicitlyNamedNotDelegating() throws Exception
+ {
+ final ObjectMapper mapper = new ObjectMapper();
+ mapper.setAnnotationIntrospector(new MyParamIntrospector("value"));
+ StringyBeanWithProps bean = mapper.readValue("{\"value\":\"x\"}", StringyBeanWithProps.class);
+ assertEquals("x", bean.getValue());
+ }
+
+ // [databind#714]
+ public void testSingleExplicitlyNamedButDelegating() throws Exception
+ {
+ SingleNamedButStillDelegating bean = MAPPER.readValue(quote("xyz"),
+ SingleNamedButStillDelegating.class);
+ assertEquals("xyz", bean.value);
+ }
}
+
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestBuilderSimple.java b/src/test/java/com/fasterxml/jackson/databind/creators/TestBuilderSimple.java
index 6575426..78c60ec 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/TestBuilderSimple.java
+++ b/src/test/java/com/fasterxml/jackson/databind/creators/TestBuilderSimple.java
@@ -22,18 +22,17 @@
}
}
- @SuppressWarnings("hiding")
static class SimpleBuilderXY
{
public int x, y;
- public SimpleBuilderXY withX(int x) {
- this.x = x;
+ public SimpleBuilderXY withX(int x0) {
+ this.x = x0;
return this;
}
- public SimpleBuilderXY withY(int y) {
- this.y = y;
+ public SimpleBuilderXY withY(int y0) {
+ this.y = y0;
return this;
}
@@ -56,22 +55,21 @@
}
}
- @SuppressWarnings("hiding")
static class BuildABC
{
public int a; // to be used as is
private int b, c;
@JsonProperty("b")
- public BuildABC assignB(int b) {
- this.b = b;
+ public BuildABC assignB(int b0) {
+ this.b = b0;
return this;
}
// Also ok NOT to return 'this'
@JsonSetter("c")
- public void c(int c) {
- this.c = c;
+ public void c(int c0) {
+ this.c = c0;
}
public ValueClassABC build() {
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorNullValue.java b/src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorNullValue.java
index 34e084d..0117eef 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorNullValue.java
+++ b/src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorNullValue.java
@@ -1,13 +1,13 @@
package com.fasterxml.jackson.databind.creators;
+import java.io.IOException;
+import java.util.UUID;
+
import com.fasterxml.jackson.annotation.*;
-
import com.fasterxml.jackson.core.*;
-
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.*;
-// Mostly for [JACSON-774]
public class TestCreatorNullValue extends BaseMapTest
{
protected static class Container {
@@ -19,13 +19,13 @@
}
}
- private static interface Contained<T> {}
+ protected static interface Contained<T> {}
- private static class NullContained implements Contained<Object> {}
+ protected static class NullContained implements Contained<Object> {}
- private static final NullContained NULL_CONTAINED = new NullContained();
+ protected static final NullContained NULL_CONTAINED = new NullContained();
- private static class ContainedDeserializer extends JsonDeserializer<Contained<?>> {
+ protected static class ContainedDeserializer extends JsonDeserializer<Contained<?>> {
@Override
public Contained<?> deserialize(JsonParser jp, DeserializationContext ctxt) throws JsonProcessingException {
return null;
@@ -37,7 +37,7 @@
}
}
- private static class ContainerDeserializerResolver extends Deserializers.Base {
+ protected static class ContainerDeserializerResolver extends Deserializers.Base {
@Override
public JsonDeserializer<?> findBeanDeserializer(JavaType type,
DeserializationConfig config, BeanDescription beanDesc)
@@ -45,13 +45,12 @@
{
if (!Contained.class.isAssignableFrom(type.getRawClass())) {
return null;
- } else {
- return new ContainedDeserializer();
}
+ return new ContainedDeserializer();
}
}
- private static class TestModule extends Module
+ protected static class TestModule extends Module
{
@Override
public String getModuleName() {
@@ -69,6 +68,26 @@
}
}
+ // [databind#597]
+ static class JsonEntity {
+ protected final String type;
+ protected final UUID id;
+
+ private JsonEntity(String type, UUID id) {
+ this.type = type;
+ this.id = id;
+ }
+
+ @JsonCreator
+ public static JsonEntity create(@JsonProperty("type") String type, @JsonProperty("id") UUID id) {
+ if (type != null && !type.contains(" ") && (id != null)) {
+ return new JsonEntity(type, id);
+ }
+
+ return null;
+ }
+ }
+
/*
/**********************************************************
/* Unit tests
@@ -81,4 +100,16 @@
Container container = mapper.readValue("{}", Container.class);
assertEquals(NULL_CONTAINED, container.contained);
}
+
+ // [databind#597]: ensure that a useful exception is thrown
+ public void testCreatorReturningNull() throws IOException {
+ ObjectMapper objectMapper = new ObjectMapper();
+ String json = "{ \"type\" : \" \", \"id\" : \"000c0ffb-a0d6-4d2e-a379-4aeaaf283599\" }";
+ try {
+ objectMapper.readValue(json, JsonEntity.class);
+ fail("Should not have succeeded");
+ } catch (JsonMappingException e) {
+ verifyException(e, "JSON creator returned null");
+ }
+ }
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorWithNamingStrategy556.java b/src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorWithNamingStrategy556.java
new file mode 100644
index 0000000..0911fcf
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorWithNamingStrategy556.java
@@ -0,0 +1,82 @@
+package com.fasterxml.jackson.databind.creators;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
+import com.fasterxml.jackson.databind.introspect.AnnotatedParameter;
+import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
+
+public class TestCreatorWithNamingStrategy556
+ extends BaseMapTest
+{
+ static class RenamingCtorBean
+ {
+ protected String myName;
+ protected int myAge;
+
+ @JsonCreator
+ public RenamingCtorBean(int myAge, String myName)
+ {
+ this.myName = myName;
+ this.myAge = myAge;
+ }
+ }
+
+ // Try the same with factory, too
+ static class RenamedFactoryBean
+ {
+ protected String myName;
+ protected int myAge;
+
+ private RenamedFactoryBean(int a, String n, boolean foo) {
+ myAge = a;
+ myName = n;
+ }
+
+ @JsonCreator
+ public static RenamedFactoryBean create(int age, String name) {
+ return new RenamedFactoryBean(age, name, true);
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static class MyParamIntrospector extends JacksonAnnotationIntrospector
+ {
+ @Override
+ public String findImplicitPropertyName(AnnotatedMember param) {
+ if (param instanceof AnnotatedParameter) {
+ AnnotatedParameter ap = (AnnotatedParameter) param;
+ switch (ap.getIndex()) {
+ case 0: return "myAge";
+ case 1: return "myName";
+ default:
+ return "param"+ap.getIndex();
+ }
+ }
+ return super.findImplicitPropertyName(param);
+ }
+ }
+
+ private final ObjectMapper MAPPER = new ObjectMapper()
+ .setPropertyNamingStrategy(PropertyNamingStrategy.PASCAL_CASE_TO_CAMEL_CASE)
+ ;
+ {
+ MAPPER.setAnnotationIntrospector(new MyParamIntrospector());
+ }
+
+ private final static String CTOR_JSON = aposToQuotes("{ 'MyAge' : 42, 'MyName' : 'NotMyRealName' }");
+
+ public void testRenameViaCtor() throws Exception
+ {
+ RenamingCtorBean bean = MAPPER.readValue(CTOR_JSON, RenamingCtorBean.class);
+ assertEquals(42, bean.myAge);
+ assertEquals("NotMyRealName", bean.myName);
+ }
+
+ public void testRenameViaFactory() throws Exception
+ {
+ RenamedFactoryBean bean = MAPPER.readValue(CTOR_JSON, RenamedFactoryBean.class);
+ assertEquals(42, bean.myAge);
+ assertEquals("NotMyRealName", bean.myName);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestCreatorWithPolymorphic113.java b/src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorWithPolymorphic113.java
similarity index 96%
rename from src/test/java/com/fasterxml/jackson/failing/TestCreatorWithPolymorphic113.java
rename to src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorWithPolymorphic113.java
index b7f161c..eb39a57 100644
--- a/src/test/java/com/fasterxml/jackson/failing/TestCreatorWithPolymorphic113.java
+++ b/src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorWithPolymorphic113.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.failing;
+package com.fasterxml.jackson.databind.creators;
import com.fasterxml.jackson.annotation.*;
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators.java b/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators.java
index 0a9a371..73f54a67 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators.java
+++ b/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators.java
@@ -436,7 +436,7 @@
public void testFactoryCreatorWithMixin() throws Exception
{
ObjectMapper m = new ObjectMapper();
- m.addMixInAnnotations(CreatorBean.class, MixIn.class);
+ m.addMixIn(CreatorBean.class, MixIn.class);
CreatorBean bean = m.readValue
("{ \"a\" : \"xyz\", \"x\" : 12 }", CreatorBean.class);
assertEquals(11, bean.x);
@@ -446,7 +446,7 @@
public void testFactoryCreatorWithRenamingMixin() throws Exception
{
ObjectMapper m = new ObjectMapper();
- m.addMixInAnnotations(FactoryBean.class, FactoryBeanMixIn.class);
+ m.addMixIn(FactoryBean.class, FactoryBeanMixIn.class);
// override changes property name from "f" to "mixed"
FactoryBean bean = m.readValue("{ \"mixed\" : 20.5 }", FactoryBean.class);
assertEquals(20.5, bean.d);
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 a20fcb9..9bca3bc 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators2.java
+++ b/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators2.java
@@ -105,13 +105,14 @@
// For [JACKSON-541]: should not need @JsonCreator if SerializationFeature.AUTO_DETECT_CREATORS is on.
static class AutoDetectConstructorBean
{
- protected final String foo;
- protected final String bar;
+ protected final String foo;
+ protected final String bar;
- public AutoDetectConstructorBean(@JsonProperty("bar") String bar, @JsonProperty("foo") String foo){
- this.bar = bar;
- this.foo = foo;
- }
+ public AutoDetectConstructorBean(@JsonProperty("bar") String bar,
+ @JsonProperty("foo") String foo){
+ this.bar = bar;
+ this.foo = foo;
+ }
}
static class BustedCtor {
@@ -267,7 +268,8 @@
}
public void testCreatorMultipleArgumentWithoutAnnotation() throws Exception {
- AutoDetectConstructorBean value = MAPPER.readValue("{\"bar\":\"bar\",\"foo\":\"foo\"}", AutoDetectConstructorBean.class);
+ AutoDetectConstructorBean value = MAPPER.readValue("{\"bar\":\"bar\",\"foo\":\"foo\"}",
+ AutoDetectConstructorBean.class);
assertEquals("bar", value.bar);
assertEquals("foo", value.foo);
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators3.java b/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators3.java
deleted file mode 100644
index c116a56..0000000
--- a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators3.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package com.fasterxml.jackson.databind.creators;
-
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.*;
-
-public class TestCreators3 extends BaseMapTest
-{
- static class MultiCtor
- {
- protected String _a, _b;
-
- @JsonCreator
- static MultiCtor factory(@JsonProperty("a") String a, @JsonProperty("b") String b) {
- return new MultiCtor(a, b, Boolean.TRUE);
- }
-
- private MultiCtor() { }
-
- private MultiCtor(String a, String b, Object c) {
- if (c == null) {
- throw new RuntimeException("Wrong factory!");
- }
- _a = a;
- _b = b;
- }
-
- }
-
- /*
- /**********************************************************
- /* Test methods
- /**********************************************************
- */
-
- private final ObjectMapper MAPPER = new ObjectMapper();
-
- // [Issue#421]
- public void testMultiCtor421() throws Exception
- {
- MultiCtor bean = MAPPER.readValue(aposToQuotes("{'a':'123','b':'foo'}"), MultiCtor.class);
- assertNotNull(bean);
- assertEquals("123", bean._a);
- assertEquals("foo", bean._b);
- }
-}
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators421.java b/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators421.java
new file mode 100644
index 0000000..27de087
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators421.java
@@ -0,0 +1,67 @@
+package com.fasterxml.jackson.databind.creators;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
+import com.fasterxml.jackson.databind.introspect.AnnotatedParameter;
+import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
+
+public class TestCreators421 extends BaseMapTest
+{
+ static class MultiCtor
+ {
+ protected String _a, _b;
+
+ private MultiCtor() { }
+ private MultiCtor(String a, String b, Boolean c) {
+ if (c == null) {
+ throw new RuntimeException("Wrong factory!");
+ }
+ _a = a;
+ _b = b;
+ }
+
+ @JsonCreator
+ static MultiCtor factory(@JsonProperty("a") String a, @JsonProperty("b") String b) {
+ return new MultiCtor(a, b, Boolean.TRUE);
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static class MyParamIntrospector extends JacksonAnnotationIntrospector
+ {
+ @Override
+ public String findImplicitPropertyName(AnnotatedMember param) {
+ if (param instanceof AnnotatedParameter) {
+ AnnotatedParameter ap = (AnnotatedParameter) param;
+ switch (ap.getIndex()) {
+ case 0: return "a";
+ case 1: return "b";
+ case 2: return "c";
+ default:
+ return "param"+ap.getIndex();
+ }
+ }
+ return super.findImplicitPropertyName(param);
+ }
+ }
+
+ /*
+ /**********************************************************
+ /* Test methods
+ /**********************************************************
+ */
+
+ // [Issue#421]
+ public void testMultiCtor421() throws Exception
+ {
+ final ObjectMapper mapper = new ObjectMapper();
+ mapper.setAnnotationIntrospector(new MyParamIntrospector());
+
+ MultiCtor bean = mapper.readValue(aposToQuotes("{'a':'123','b':'foo'}"), MultiCtor.class);
+ assertNotNull(bean);
+ assertEquals("123", bean._a);
+ assertEquals("foo", bean._b);
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators541.java b/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators541.java
index 3c9b7d9..e0f1214 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators541.java
+++ b/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators541.java
@@ -13,9 +13,9 @@
static final class Foo {
@JsonProperty("foo")
- private Map<Integer, Bar> foo;
+ protected Map<Integer, Bar> foo;
@JsonProperty("anumber")
- private long anumber;
+ protected long anumber;
public Foo() {
anumber = 0;
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorsDelegating.java b/src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorsDelegating.java
index ffa1602..0989f8b 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorsDelegating.java
+++ b/src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorsDelegating.java
@@ -2,8 +2,10 @@
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JacksonInject;
+
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
+
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.util.TokenBuffer;
@@ -67,7 +69,7 @@
return new Value592(buffer, false);
}
}
-
+
/*
/**********************************************************
/* Unit tests
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestPolymorphicCreators.java b/src/test/java/com/fasterxml/jackson/databind/creators/TestPolymorphicCreators.java
index 59818d3..59b9da0 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/TestPolymorphicCreators.java
+++ b/src/test/java/com/fasterxml/jackson/databind/creators/TestPolymorphicCreators.java
@@ -11,12 +11,6 @@
public class TestPolymorphicCreators
extends BaseMapTest
{
- /*
- /**********************************************************
- /* Helper beans
- /**********************************************************
- */
-
static class Animal
{
// All animals have names, for our demo purposes...
@@ -58,16 +52,16 @@
abstract static class AbstractRoot
{
- private final String opt;
+ protected final String opt;
- private AbstractRoot(String opt) {
+ protected AbstractRoot(String opt) {
this.opt = opt;
}
@JsonCreator
public static final AbstractRoot make(@JsonProperty("which") int which,
@JsonProperty("opt") String opt) {
- if(1 == which) {
+ if (1 == which) {
return new One(opt);
}
throw new RuntimeException("cannot instantiate " + which);
@@ -76,12 +70,12 @@
abstract public int getWhich();
public final String getOpt() {
- return opt;
+ return opt;
}
}
static final class One extends AbstractRoot {
- private One(String opt) {
+ protected One(String opt) {
super(opt);
}
@@ -135,7 +129,7 @@
public void testManualPolymorphicWithNumbered() throws Exception
{
- final ObjectWriter w = MAPPER.writerWithType(AbstractRoot.class);
+ final ObjectWriter w = MAPPER.writerFor(AbstractRoot.class);
final ObjectReader r = MAPPER.reader(AbstractRoot.class);
AbstractRoot input = AbstractRoot.make(1, "oh hai!");
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestPolymorphicDelegating.java b/src/test/java/com/fasterxml/jackson/databind/creators/TestPolymorphicDelegating.java
new file mode 100644
index 0000000..0eba60b
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/creators/TestPolymorphicDelegating.java
@@ -0,0 +1,53 @@
+package com.fasterxml.jackson.databind.creators;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.databind.*;
+
+public class TestPolymorphicDelegating extends BaseMapTest
+{
+
+ // For [databind#580]
+
+ @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
+ static abstract class Issue580Base {
+ }
+
+ static class Issue580Impl extends Issue580Base {
+ public int id = 3;
+
+ public Issue580Impl() { }
+ public Issue580Impl(int id) { this.id = id; }
+ }
+
+ static class Issue580Bean {
+ public Issue580Base value;
+
+ @JsonCreator
+ public Issue580Bean(Issue580Base v) {
+ value = v;
+ }
+
+ @JsonValue
+ public Issue580Base value() {
+ return value;
+ }
+ }
+
+ /*
+ /**********************************************************
+ /* Unit tests
+ /**********************************************************
+ */
+
+ public void testAbstractDelegateWithCreator() throws Exception
+ {
+ Issue580Bean input = new Issue580Bean(new Issue580Impl(13));
+ ObjectMapper mapper = new ObjectMapper();
+ String json = mapper.writeValueAsString(input);
+
+ Issue580Bean result = mapper.readValue(json, Issue580Bean.class);
+ assertNotNull(result);
+ assertNotNull(result.value);
+ assertEquals(13, ((Issue580Impl) result.value).id);
+ }
+}
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 5c0e0dd..54ce1b2 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/TestValueUpdate.java
+++ b/src/test/java/com/fasterxml/jackson/databind/creators/TestValueUpdate.java
@@ -2,10 +2,10 @@
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.test.BaseTest;
-public class TestValueUpdate extends BaseTest
+import com.fasterxml.jackson.databind.*;
+
+public class TestValueUpdate extends BaseMapTest
{
static class Bean
{
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestBasicAnnotations.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestBasicAnnotations.java
index a4ff293..481c9b7 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestBasicAnnotations.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestBasicAnnotations.java
@@ -43,7 +43,7 @@
static class Issue442Bean {
@JsonUnwrapped
- private IntWrapper w = new IntWrapper(13);
+ protected IntWrapper w = new IntWrapper(13);
}
final static class SizeClassSetter2
@@ -85,7 +85,7 @@
}
static class BeanWithDeserialize {
- @JsonDeserialize private int a;
+ @JsonDeserialize protected int a;
}
/*
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestBeanDeserializer.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestBeanDeserializer.java
index 34cdfd6..65f6a5f 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestBeanDeserializer.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestBeanDeserializer.java
@@ -223,6 +223,8 @@
/********************************************************
*/
+ private final ObjectMapper MAPPER = new ObjectMapper();
+
public void testPropertyRemoval() throws Exception
{
ObjectMapper mapper = new ObjectMapper();
@@ -258,22 +260,43 @@
public void testPOJOFromEmptyString() throws Exception
{
// first, verify default settings which do not accept empty String:
- ObjectMapper mapper = new ObjectMapper();
+ assertFalse(MAPPER.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT));
try {
- mapper.readValue(quote(""), Bean.class);
+ MAPPER.readValue(quote(""), Bean.class);
fail("Should not accept Empty String for POJO");
} catch (JsonProcessingException e) {
verifyException(e, "from String value");
assertValidLocation(e.getLocation());
}
-
- // should be ok to enable dynamically:
- mapper = new ObjectMapper();
- mapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
- Bean result = mapper.readValue(quote(""), Bean.class);
+ // should be ok to enable dynamically
+ ObjectReader r = MAPPER.reader(Bean.class)
+ .with(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
+ Bean result = r.readValue(quote(""));
assertNull(result);
}
+ // [Databind#540]
+ public void testPOJOFromEmptyArray() throws Exception
+ {
+ final String JSON = " [\n]";
+ assertFalse(MAPPER.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT));
+ // first, verify default settings which do not accept empty Array
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ mapper.readValue(JSON, Bean.class);
+ fail("Should not accept Empty Array for POJO by default");
+ } catch (JsonProcessingException e) {
+ verifyException(e, "START_ARRAY token");
+ assertValidLocation(e.getLocation());
+ }
+
+ // should be ok to enable dynamically:
+ ObjectReader r = MAPPER.reader(Bean.class)
+ .with(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT);
+ Bean result = r.readValue(JSON);
+ assertNull(result);
+ }
+
// [Issue#120]
public void testModifyArrayDeserializer() throws Exception
{
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestCollectionDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestCollectionDeserialization.java
index 5075fbc..1af7787 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestCollectionDeserialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestCollectionDeserialization.java
@@ -155,7 +155,7 @@
public void testFromEmptyString() throws Exception
{
ObjectReader r = MAPPER.reader(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
- List<?> result = r.withType(List.class).readValue(quote(""));
+ List<?> result = r.forType(List.class).readValue(quote(""));
assertNull(result);
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestCustomDeserializers.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestCustomDeserializers.java
index c7dcc62..3b1066f 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestCustomDeserializers.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestCustomDeserializers.java
@@ -15,6 +15,7 @@
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.deser.std.StdDelegatingDeserializer;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import com.fasterxml.jackson.databind.deser.std.StdNodeBasedDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.util.StdConverter;
@@ -303,6 +304,26 @@
assertEquals(7, imm.y);
}
+ // [databind#623]
+ public void testJsonNodeDelegating() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper();
+ SimpleModule module = new SimpleModule("test", Version.unknownVersion());
+ module.addDeserializer(Immutable.class,
+ new StdNodeBasedDeserializer<Immutable>(Immutable.class) {
+ @Override
+ public Immutable convert(JsonNode root, DeserializationContext ctxt) throws IOException {
+ int x = root.path("x").asInt();
+ int y = root.path("y").asInt();
+ return new Immutable(x, y);
+ }
+ });
+ mapper.registerModule(module);
+ Immutable imm = mapper.readValue("{\"x\":-10,\"y\":3}", Immutable.class);
+ assertEquals(-10, imm.x);
+ assertEquals(3, imm.y);
+ }
+
public void testIssue882() throws Exception
{
Model original = new Model(Collections.singletonMap(new CustomKey(123), "test"));
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestCyclicTypes.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestCyclicTypes.java
index f5a596e..48a49ec 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestCyclicTypes.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestCyclicTypes.java
@@ -35,7 +35,7 @@
}
static class LinkB {
- private LinkA a;
+ protected LinkA a;
public void setA(LinkA a) { this.a = a; }
public LinkA getA() { return a; }
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestEnumDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestEnumDeserialization.java
index 5c39333..c0b9097 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestEnumDeserialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestEnumDeserialization.java
@@ -1,13 +1,13 @@
package com.fasterxml.jackson.databind.deser;
import java.io.IOException;
+import java.math.BigDecimal;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import com.fasterxml.jackson.annotation.*;
-
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
@@ -48,7 +48,7 @@
return TestEnum.valueOf(jp.getText().toUpperCase());
}
}
-
+
protected enum EnumWithCreator {
A, B;
@@ -59,6 +59,17 @@
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;
@@ -194,11 +205,15 @@
}
// [JACKSON-193]
- public void testCreatorEnums() throws Exception
- {
+ 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);
+ }
// [JACKSON-212]
public void testToStringEnums() throws Exception
@@ -302,14 +317,14 @@
{
// can not use shared mapper when changing configs...
ObjectReader reader = MAPPER.reader(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL);
- assertNull(reader.withType(TestEnum.class).readValue("\"NO-SUCH-VALUE\""));
- assertNull(reader.withType(TestEnum.class).readValue(" 4343 "));
+ assertNull(reader.forType(TestEnum.class).readValue("\"NO-SUCH-VALUE\""));
+ assertNull(reader.forType(TestEnum.class).readValue(" 4343 "));
}
public void testAllowUnknownEnumValuesForEnumSets() throws Exception
{
ObjectReader reader = MAPPER.reader(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL);
- EnumSet<TestEnum> result = reader.withType(new TypeReference<EnumSet<TestEnum>>() { })
+ EnumSet<TestEnum> result = reader.forType(new TypeReference<EnumSet<TestEnum>>() { })
.readValue("[\"NO-SUCH-VALUE\"]");
assertEquals(0, result.size());
}
@@ -317,7 +332,7 @@
public void testAllowUnknownEnumValuesAsMapKeysReadAsNull() throws Exception
{
ObjectReader reader = MAPPER.reader(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL);
- ClassWithEnumMapKey result = reader.withType(ClassWithEnumMapKey.class)
+ ClassWithEnumMapKey result = reader.forType(ClassWithEnumMapKey.class)
.readValue("{\"map\":{\"NO-SUCH-VALUE\":\"val\"}}");
assertTrue(result.map.containsKey(null));
}
@@ -336,8 +351,9 @@
// [JACKSON-834]
public void testEnumsFromInts() throws Exception
{
- TestEnumFor834 res = MAPPER.readValue("1 ", TestEnumFor834.class);
- assertSame(TestEnumFor834.ENUM_A, res);
+ Object ob = MAPPER.readValue("1 ", TestEnumFor834.class);
+ assertEquals(TestEnumFor834.class, ob.getClass());
+ assertSame(TestEnumFor834.ENUM_A, ob);
}
// [Issue#141]: allow mapping of empty String into null
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestExceptionHandling.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestExceptionHandling.java
index 6902fed..6f23517 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestExceptionHandling.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestExceptionHandling.java
@@ -5,7 +5,6 @@
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
-import com.fasterxml.jackson.test.BrokenStringReader;
/**
* Unit test for verifying that exceptions are properly handled (caught,
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestInjectables.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestInjectables.java
index b9af767..f7c27ff 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestInjectables.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestInjectables.java
@@ -58,14 +58,14 @@
static class IssueGH471Bean {
- private final Object constructorInjected;
- private final String constructorValue;
+ protected final Object constructorInjected;
+ protected final String constructorValue;
- @JacksonInject("field_injected") private Object fieldInjected;
- @JsonProperty("field_value") private String fieldValue;
+ @JacksonInject("field_injected") protected Object fieldInjected;
+ @JsonProperty("field_value") protected String fieldValue;
- private Object methodInjected;
- private String methodValue;
+ protected Object methodInjected;
+ protected String methodValue;
public int x;
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestJdkTypes.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestJdkTypes.java
index ad3305d..84aef92 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestJdkTypes.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestJdkTypes.java
@@ -76,7 +76,7 @@
@JsonProperty("Location")
@JsonDeserialize(using=MyStackTraceElementDeserializer.class)
- private StackTraceElement location;
+ protected StackTraceElement location;
}
@SuppressWarnings("serial")
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestMapDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestMapDeserialization.java
index 13a9fd1..615cd62 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestMapDeserialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestMapDeserialization.java
@@ -9,7 +9,6 @@
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.core.*;
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.StdDeserializer;
@@ -18,13 +17,7 @@
public class TestMapDeserialization
extends BaseMapTest
{
- /*
- /**********************************************************
- /* Test classes, enums
- /**********************************************************
- */
-
- enum Key {
+ static enum Key {
KEY1, KEY2, WHATEVER;
}
@@ -84,7 +77,7 @@
ONE, TWO;
}
-
+ static class ClassStringMap extends HashMap<Class<?>,String> { }
/*
/**********************************************************
@@ -302,6 +295,31 @@
assertNull(result.get(""));
}
+ // [Databind#540]
+ public void testMapFromEmptyArray() throws Exception
+ {
+ final String JSON = " [\n]";
+ assertFalse(MAPPER.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT));
+ // first, verify default settings which do not accept empty Array
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ mapper.readValue(JSON, Map.class);
+ fail("Should not accept Empty Array for Map by default");
+ } catch (JsonProcessingException e) {
+ verifyException(e, "START_ARRAY token");
+ }
+ // should be ok to enable dynamically:
+ ObjectReader r = MAPPER.reader(Map.class)
+ .with(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT);
+
+ Map<?,?> result = r.readValue(JSON);
+ assertNull(result);
+
+ EnumMap<?,?> result2 = r.forType(new TypeReference<EnumMap<Key,String>>() { })
+ .readValue(JSON);
+ assertNull(result2);
+ }
+
/*
/**********************************************************
/* Test methods, maps with enums
@@ -457,6 +475,14 @@
key = map.keySet().iterator().next();
assertEquals("foo", key.value);
}
+
+ public void testClassKeyMap() throws Exception {
+ ClassStringMap map = MAPPER.readValue(aposToQuotes("{'java.lang.String':'foo'}"),
+ ClassStringMap.class);
+ assertNotNull(map);
+ assertEquals(1, map.size());
+ assertEquals("foo", map.get(String.class));
+ }
/*
/**********************************************************
@@ -468,7 +494,7 @@
* Simple test to ensure that @JsonDeserialize.using is
* recognized
*/
- public void testMapWithDeserializer() throws IOException
+ public void testMapWithDeserializer() throws Exception
{
CustomMap result = MAPPER.readValue(quote("xyz"), CustomMap.class);
assertEquals(1, result.size());
@@ -477,6 +503,47 @@
/*
/**********************************************************
+ /* Test methods, annotated Map.Entry
+ /**********************************************************
+ */
+
+ public void testMapEntrySimpleTypes() throws Exception
+ {
+ List<Map.Entry<String,Long>> stuff = MAPPER.readValue(aposToQuotes("[{'a':15},{'b':42}]"),
+ new TypeReference<List<Map.Entry<String,Long>>>() { });
+ assertNotNull(stuff);
+ assertEquals(2, stuff.size());
+ assertNotNull(stuff.get(1));
+ assertEquals("b", stuff.get(1).getKey());
+ assertEquals(Long.valueOf(42), stuff.get(1).getValue());
+ }
+
+ public void testMapEntryWithStringBean() throws Exception
+ {
+ List<Map.Entry<Integer,StringWrapper>> stuff = MAPPER.readValue(aposToQuotes("[{'28':'Foo'},{'13':'Bar'}]"),
+ new TypeReference<List<Map.Entry<Integer,StringWrapper>>>() { });
+ assertNotNull(stuff);
+ assertEquals(2, stuff.size());
+ assertNotNull(stuff.get(1));
+ assertEquals(Integer.valueOf(13), stuff.get(1).getKey());
+
+ StringWrapper sw = stuff.get(1).getValue();
+ assertEquals("Bar", sw.str);
+ }
+
+ public void testMapEntryFail() throws Exception
+ {
+ try {
+ /*List<Map.Entry<Integer,StringWrapper>> stuff =*/ MAPPER.readValue(aposToQuotes("[{'28':'Foo','13':'Bar'}]"),
+ new TypeReference<List<Map.Entry<Integer,StringWrapper>>>() { });
+ fail("Should not have passed");
+ } catch (Exception e) {
+ verifyException(e, "more than one entry in JSON");
+ }
+ }
+
+ /*
+ /**********************************************************
/* Error tests
/**********************************************************
*/
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestNumbers.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestNumbers.java
index cce9005..7e02e64 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestNumbers.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestNumbers.java
@@ -11,8 +11,7 @@
/**
* Tests related to [JACKSON-139]
*/
-public class TestNumbers
- extends BaseMapTest
+public class TestNumbers extends BaseMapTest
{
/*
/**********************************************************************
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestObjectReader.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestObjectReader.java
new file mode 100644
index 0000000..30f781e
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestObjectReader.java
@@ -0,0 +1,32 @@
+package com.fasterxml.jackson.databind.deser;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.*;
+
+public class TestObjectReader extends BaseMapTest
+{
+ final ObjectMapper MAPPER = new ObjectMapper();
+
+ public void testParserFeatures() throws Exception
+ {
+ final String JSON = "[ /* foo */ 7 ]";
+ // default won't accept comments, let's change that:
+ ObjectReader reader = MAPPER.reader(int[].class)
+ .with(JsonParser.Feature.ALLOW_COMMENTS);
+
+ int[] value = reader.readValue(JSON);
+ assertNotNull(value);
+ assertEquals(1, value.length);
+ assertEquals(7, value[0]);
+
+ // but also can go back
+ try {
+ reader.without(JsonParser.Feature.ALLOW_COMMENTS).readValue(JSON);
+ fail("Should not have passed");
+ } catch (JsonProcessingException e) {
+ verifyException(e, "foo");
+ }
+ }
+
+}
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 706a1bc..4a69cc5 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestValueAnnotations.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestValueAnnotations.java
@@ -7,7 +7,6 @@
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
-import com.fasterxml.jackson.test.BaseTest;
/**
* This unit test suite tests use of "value" Annotations;
@@ -15,7 +14,7 @@
* deserialization.
*/
public class TestValueAnnotations
- extends BaseTest
+ extends BaseMapTest
{
/*
/**********************************************************
diff --git a/src/test/java/com/fasterxml/jackson/databind/filter/TestMapFiltering.java b/src/test/java/com/fasterxml/jackson/databind/filter/TestMapFiltering.java
index 3f87cb7..f966a72 100644
--- a/src/test/java/com/fasterxml/jackson/databind/filter/TestMapFiltering.java
+++ b/src/test/java/com/fasterxml/jackson/databind/filter/TestMapFiltering.java
@@ -1,21 +1,38 @@
package com.fasterxml.jackson.databind.filter;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
import java.util.*;
import com.fasterxml.jackson.annotation.JsonFilter;
+import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.ser.FilterProvider;
+import com.fasterxml.jackson.databind.ser.PropertyFilter;
+import com.fasterxml.jackson.databind.ser.PropertyWriter;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
public class TestMapFiltering extends BaseMapTest
{
+ @Target({ElementType.FIELD})
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface CustomOffset
+ {
+ public int value();
+ }
+
@SuppressWarnings("serial")
@JsonFilter("filterForMaps")
static class FilteredBean extends LinkedHashMap<String,Integer> { }
static class MapBean {
@JsonFilter("filterX")
+ @CustomOffset(1)
public Map<String,Integer> values;
public MapBean() {
@@ -25,7 +42,46 @@
values.put("c", 9);
}
}
-
+
+ static class MyMapFilter implements PropertyFilter
+ {
+ @Override
+ public void serializeAsField(Object value, JsonGenerator jgen,
+ SerializerProvider provider, PropertyWriter writer)
+ throws Exception
+ {
+ String name = writer.getName();
+ if (!"a".equals(name)) {
+ return;
+ }
+ CustomOffset n = writer.findAnnotation(CustomOffset.class);
+ int offset = (n == null) ? 0 : n.value();
+ Integer I = offset + ((Integer) value).intValue();
+
+ writer.serializeAsField(I, jgen, provider);
+ }
+
+ @Override
+ public void serializeAsElement(Object elementValue, JsonGenerator jgen,
+ SerializerProvider prov, PropertyWriter writer)
+ throws Exception {
+ // not needed for testing
+ }
+
+ @Override
+ public void depositSchemaProperty(PropertyWriter writer,
+ ObjectNode propertiesNode, SerializerProvider provider)
+ throws JsonMappingException {
+
+ }
+
+ @Override
+ public void depositSchemaProperty(PropertyWriter writer,
+ JsonObjectFormatVisitor objectVisitor,
+ SerializerProvider provider) throws JsonMappingException {
+ }
+ }
+
/*
/**********************************************************
/* Unit tests
@@ -53,4 +109,13 @@
assertEquals(aposToQuotes("{'b':3}"), json);
}
+ // [Issue#522]
+ public void testMapFilteringWithAnnotations() throws Exception
+ {
+ FilterProvider prov = new SimpleFilterProvider().addFilter("filterX",
+ new MyMapFilter());
+ String json = MAPPER.writer(prov).writeValueAsString(new MapBean());
+ // a=1 should become a=2
+ assertEquals(aposToQuotes("{'values':{'a':2}}"), json);
+ }
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/interop/TestCglibUsage.java b/src/test/java/com/fasterxml/jackson/databind/interop/TestCglibUsage.java
index f890325..3a0620b 100644
--- a/src/test/java/com/fasterxml/jackson/databind/interop/TestCglibUsage.java
+++ b/src/test/java/com/fasterxml/jackson/databind/interop/TestCglibUsage.java
@@ -1,7 +1,5 @@
package com.fasterxml.jackson.databind.interop;
-
-import java.io.*;
import java.lang.reflect.Method;
import java.util.*;
@@ -10,20 +8,12 @@
import net.sf.cglib.proxy.MethodProxy;
import com.fasterxml.jackson.databind.*;
-import com.fasterxml.jackson.test.BaseTest;
/**
* Unit test for checking that we can serialize CGLib generated proxies.
*/
-public class TestCglibUsage
- extends BaseTest
+public class TestCglibUsage extends BaseMapTest
{
- /*
- /**********************************************************
- /* Helper classes
- /**********************************************************
- */
-
interface BeanInterface {
public int getX();
}
@@ -56,20 +46,5 @@
assertEquals(1, result.size());
assertEquals(Integer.valueOf(13), result.get("x"));
}
-
- /*
- /**********************************************************
- /* Helper methods
- /**********************************************************
- */
-
- @SuppressWarnings("unchecked")
- private Map<String,Object> writeAndMap(ObjectMapper m, Object value)
- throws IOException
- {
- StringWriter sw = new StringWriter();
- m.writeValue(sw, value);
- return (Map<String,Object>) m.readValue(sw.toString(), Object.class);
- }
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/interop/TestFormatDetection.java b/src/test/java/com/fasterxml/jackson/databind/interop/TestFormatDetection.java
index b6234f2..18345a6 100644
--- a/src/test/java/com/fasterxml/jackson/databind/interop/TestFormatDetection.java
+++ b/src/test/java/com/fasterxml/jackson/databind/interop/TestFormatDetection.java
@@ -26,7 +26,7 @@
public void testSimpleWithJSON() throws Exception
{
- ObjectReader detecting = READER.withType(POJO.class);
+ ObjectReader detecting = READER.forType(POJO.class);
detecting = detecting.withFormatDetection(detecting);
POJO pojo = detecting.readValue(utf8Bytes("{\"x\":1}"));
assertNotNull(pojo);
@@ -35,7 +35,7 @@
public void testInvalid() throws Exception
{
- ObjectReader detecting = READER.withType(POJO.class);
+ ObjectReader detecting = READER.forType(POJO.class);
detecting = detecting.withFormatDetection(detecting);
try {
detecting.readValue(utf8Bytes("<POJO><x>1</x></POJO>"));
diff --git a/src/test/java/com/fasterxml/jackson/databind/interop/TestHibernate.java b/src/test/java/com/fasterxml/jackson/databind/interop/TestHibernate.java
index 67bdbdb..48dc573 100644
--- a/src/test/java/com/fasterxml/jackson/databind/interop/TestHibernate.java
+++ b/src/test/java/com/fasterxml/jackson/databind/interop/TestHibernate.java
@@ -1,7 +1,5 @@
package com.fasterxml.jackson.databind.interop;
-
-import java.io.*;
import java.lang.reflect.Method;
import java.util.*;
@@ -10,20 +8,13 @@
import org.hibernate.repackage.cglib.proxy.MethodProxy;
import com.fasterxml.jackson.databind.*;
-import com.fasterxml.jackson.test.BaseTest;
/**
* Basic tests covering Hibernate-compatibility features.
*/
public class TestHibernate
- extends BaseTest
+ extends BaseMapTest
{
- /*
- /**********************************************************
- /* Helper classes
- /**********************************************************
- */
-
interface BeanInterfaceHib {
public int getX();
}
@@ -66,20 +57,5 @@
assertEquals(1, result.size());
assertEquals(Integer.valueOf(13), result.get("x"));
}
-
- /*
- /**********************************************************
- /* Helper methods
- /**********************************************************
- */
-
- @SuppressWarnings("unchecked")
- private Map<String,Object> writeAndMap(ObjectMapper m, Object value)
- throws IOException
- {
- StringWriter sw = new StringWriter();
- m.writeValue(sw, value);
- return (Map<String,Object>) m.readValue(sw.toString(), Object.class);
- }
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/TestAnnotionBundles.java b/src/test/java/com/fasterxml/jackson/databind/introspect/TestAnnotionBundles.java
index ddb7736..804afa8 100644
--- a/src/test/java/com/fasterxml/jackson/databind/introspect/TestAnnotionBundles.java
+++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TestAnnotionBundles.java
@@ -3,9 +3,13 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.PropertyName;
/* Tests mostly for [JACKSON-754]: ability to create "annotation bundles"
*/
@@ -51,7 +55,47 @@
public class Bean92 {
@Bundle92
protected String id = "abc";
- }
+ }
+
+ @HolderB
+ @JacksonAnnotationsInside
+ @Retention(RetentionPolicy.RUNTIME)
+ static @interface HolderA {}
+
+ @HolderA
+ @JacksonAnnotationsInside
+ @Retention(RetentionPolicy.RUNTIME)
+ static @interface HolderB {}
+
+ static class RecursiveHolder {
+ @HolderA public int unimportant = 42;
+ }
+
+ @JsonProperty
+ @JacksonAnnotationsInside
+ @Retention(RetentionPolicy.RUNTIME)
+ static @interface InformativeHolder {
+ // doesn't really contribute to the test, but would be impossible without this feature
+ boolean important() default true;
+ }
+
+ static class InformingHolder {
+ @InformativeHolder public int unimportant = 42;
+ }
+
+ @SuppressWarnings("serial")
+ static class BundleAnnotationIntrospector extends JacksonAnnotationIntrospector {
+ @Override
+ public PropertyName findNameForSerialization(Annotated a)
+ {
+ InformativeHolder informativeHolder = a.getAnnotation(InformativeHolder.class);
+ if ((informativeHolder != null) && informativeHolder.important()) {
+ return new PropertyName("important");
+ }
+ return super.findNameForSerialization(a);
+ }
+ }
+
/*
/**********************************************************
/* Test methods
@@ -59,7 +103,18 @@
*/
private final ObjectMapper MAPPER = new ObjectMapper();
-
+
+ public void testKeepAnnotationBundle() throws Exception
+ {
+ MAPPER.setAnnotationIntrospector(new BundleAnnotationIntrospector());
+ assertEquals("{\"important\":42}", MAPPER.writeValueAsString(new InformingHolder()));
+ }
+
+ public void testRecursiveBundles() throws Exception
+ {
+ assertEquals("{\"unimportant\":42}", MAPPER.writeValueAsString(new RecursiveHolder()));
+ }
+
public void testBundledIgnore() throws Exception
{
assertEquals("{\"foobar\":13}", MAPPER.writeValueAsString(new Bean()));
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestAutoDetect.java b/src/test/java/com/fasterxml/jackson/databind/introspect/TestAutoDetect.java
similarity index 87%
rename from src/test/java/com/fasterxml/jackson/databind/deser/TestAutoDetect.java
rename to src/test/java/com/fasterxml/jackson/databind/introspect/TestAutoDetect.java
index 32c3ed1..b291be5 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestAutoDetect.java
+++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TestAutoDetect.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.deser;
+package com.fasterxml.jackson.databind.introspect;
import com.fasterxml.jackson.annotation.*;
@@ -9,12 +9,6 @@
public class TestAutoDetect
extends BaseMapTest
{
- /*
- /********************************************************
- /* Helper beans
- /********************************************************
- */
-
static class PrivateBean {
String a;
diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/TestBuilderMethods.java b/src/test/java/com/fasterxml/jackson/databind/introspect/TestBuilderMethods.java
index 946aeb3..390f5f3 100644
--- a/src/test/java/com/fasterxml/jackson/databind/introspect/TestBuilderMethods.java
+++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TestBuilderMethods.java
@@ -13,9 +13,8 @@
{
public int x;
- @SuppressWarnings("hiding")
- public SimpleBuilder withX(int x) {
- this.x = x;
+ public SimpleBuilder withX(int x0) {
+ this.x = x0;
return this;
}
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/TestInferredMutators.java b/src/test/java/com/fasterxml/jackson/databind/introspect/TestInferredMutators.java
index 0960c79..0707790 100644
--- a/src/test/java/com/fasterxml/jackson/databind/introspect/TestInferredMutators.java
+++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TestInferredMutators.java
@@ -8,13 +8,13 @@
public class TestInferredMutators extends BaseMapTest
{
public static class Point {
- private int x;
+ protected int x;
public int getX() { return x; }
}
public static class FixedPoint {
- private final int x;
+ protected final int x;
public FixedPoint() { x = 0; }
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 8a436a2..0647ebb 100644
--- a/src/test/java/com/fasterxml/jackson/databind/introspect/TestJacksonAnnotationIntrospector.java
+++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TestJacksonAnnotationIntrospector.java
@@ -29,11 +29,11 @@
public static class JacksonExample
{
- private String attributeProperty;
- private String elementProperty;
- private List<String> wrappedElementProperty;
- private EnumExample enumProperty;
- private QName qname;
+ protected String attributeProperty;
+ protected String elementProperty;
+ protected List<String> wrappedElementProperty;
+ protected EnumExample enumProperty;
+ protected QName qname;
@JsonSerialize(using=QNameSerializer.class)
public QName getQname()
diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/TestMixinMerging.java b/src/test/java/com/fasterxml/jackson/databind/introspect/TestMixinMerging.java
index a17e2c1..2595500 100644
--- a/src/test/java/com/fasterxml/jackson/databind/introspect/TestMixinMerging.java
+++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TestMixinMerging.java
@@ -12,10 +12,12 @@
}
static class ContactImpl implements Contact {
+ @Override
public String getCity() { return "Seattle"; }
}
static class ContactMixin implements Contact {
+ @Override
@JsonProperty
public String getCity() { return null; }
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/TestNameConflicts.java b/src/test/java/com/fasterxml/jackson/databind/introspect/TestNameConflicts.java
index d4fa78e..ae8db66 100644
--- a/src/test/java/com/fasterxml/jackson/databind/introspect/TestNameConflicts.java
+++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TestNameConflicts.java
@@ -5,6 +5,26 @@
public class TestNameConflicts extends BaseMapTest
{
+ @JsonAutoDetect
+ (fieldVisibility= JsonAutoDetect.Visibility.NONE,getterVisibility= JsonAutoDetect.Visibility.NONE, setterVisibility= JsonAutoDetect.Visibility.NONE, isGetterVisibility= JsonAutoDetect.Visibility.NONE)
+ static class CoreBean158 {
+ protected String bar = "x";
+
+ @JsonProperty
+ public String getBar() {
+ return bar;
+ }
+
+ @JsonProperty
+ public void setBar(String bar) {
+ this.bar = bar;
+ }
+
+ public void setBar(java.io.Serializable bar) {
+ this.bar = bar.toString();
+ }
+ }
+
static class Bean193
{
@JsonProperty("val1")
@@ -64,6 +84,8 @@
/**********************************************************
*/
+ private final ObjectMapper MAPPER = objectMapper();
+
// [Issue#193]
public void testIssue193() throws Exception
{
@@ -74,7 +96,7 @@
// [Issue#327]
public void testNonConflict() throws Exception
{
- String json = objectMapper().writeValueAsString(new BogusConflictBean());
+ String json = MAPPER.writeValueAsString(new BogusConflictBean());
assertEquals(aposToQuotes("{'prop1':2,'prop2':1}"), json);
}
@@ -83,4 +105,22 @@
String json = objectWriter().writeValueAsString(new MultipleTheoreticalGetters());
assertEquals(aposToQuotes("{'a':3}"), json);
}
+
+ // for [jackson-core#158]
+ public void testOverrideName() throws Exception
+ {
+ final ObjectMapper mapper = objectMapper();
+ String json = mapper.writeValueAsString(new CoreBean158());
+ assertEquals(aposToQuotes("{'bar':'x'}"), json);
+
+ // and back
+ CoreBean158 result = null;
+ try {
+ result = mapper.readValue(aposToQuotes("{'bar':'y'}"), CoreBean158.class);
+ } catch (Exception e) {
+ fail("Unexpected failure when reading CoreBean158: "+e);
+ }
+ assertNotNull(result);
+ assertEquals("y", result.bar);
+ }
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/TestPropertyConflicts.java b/src/test/java/com/fasterxml/jackson/databind/introspect/TestPropertyConflicts.java
index d8cec1d..16942b8 100644
--- a/src/test/java/com/fasterxml/jackson/databind/introspect/TestPropertyConflicts.java
+++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TestPropertyConflicts.java
@@ -22,6 +22,7 @@
// [Issue#238]
protected static class Getters1A
{
+ @JsonProperty
protected int value = 3;
public int getValue() { return value+1; }
@@ -34,6 +35,7 @@
{
public boolean isValue() { return false; }
+ @JsonProperty
protected int value = 3;
public int getValue() { return value+1; }
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 98b3d91..0e8de56 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestCustomTypeIdResolver.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestCustomTypeIdResolver.java
@@ -6,11 +6,9 @@
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
-import com.fasterxml.jackson.databind.BaseMapTest;
-import com.fasterxml.jackson.databind.JavaType;
-import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver;
-import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
+import com.fasterxml.jackson.databind.jsontype.impl.TypeIdResolverBase;
import com.fasterxml.jackson.databind.type.TypeFactory;
public class TestCustomTypeIdResolver extends BaseMapTest
@@ -63,7 +61,7 @@
}
}
- static class CustomResolverBase implements TypeIdResolver
+ static class CustomResolverBase extends TypeIdResolverBase
{
protected final Class<?> superType;
protected final Class<?> subType;
@@ -91,7 +89,7 @@
public void init(JavaType baseType) { }
@Override
- public JavaType typeFromId(String id)
+ public JavaType typeFromId(DatabindContext context, String id)
{
if ("*".equals(id)) {
return TypeFactory.defaultInstance().constructType(subType);
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForObject.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForObject.java
index 80224ae..71d5d4e 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForObject.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForObject.java
@@ -32,14 +32,14 @@
* Another enum type, but this time forcing sub-classing
*/
enum ComplexChoice {
- MAYBE(true), PROBABLY_NOT(false);
+ MAYBE(true), PROBABLY_NOT(false);
- private boolean state;
+ private boolean state;
- private ComplexChoice(boolean b) { state = b; }
+ private ComplexChoice(boolean b) { state = b; }
@Override
- public String toString() { return String.valueOf(state); }
+ public String toString() { return String.valueOf(state); }
}
// [JACKSON-311]
@@ -113,6 +113,22 @@
assertEquals("abc", ((StringBean) result[0]).name);
}
+ // with 2.5, another test to check that "as-property" is valid option
+ public void testBeanAsObjectUsingAsProperty() throws Exception
+ {
+ ObjectMapper m = new ObjectMapper();
+ m.enableDefaultTypingAsProperty(ObjectMapper.DefaultTyping.NON_FINAL,
+ ".hype");
+ // note: need to wrap, to get declared as Object
+ String json = m.writeValueAsString(new StringBean("abc"));
+
+ // Ok: serialization seems to work as expected. Now deserialize:
+ Object result = m.readValue(json, Object.class);
+ assertNotNull(result);
+ assertEquals(StringBean.class, result.getClass());
+ assertEquals("abc", ((StringBean) result).name);
+ }
+
/**
* Unit test that verifies that an abstract bean is stored with type information
* if default type information is enabled for non-concrete types.
@@ -331,6 +347,18 @@
String json = mapper.writeValueAsString(new BeanHolder(new StringBean("punny")));
assertEquals("{\"bean\":{\"*CLASS*\":\"com.fasterxml.jackson.databind.jsontype.TestDefaultForObject$StringBean\",\"name\":\"punny\"}}", json);
}
+
+ public void testNoGoWithExternalProperty() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT,
+ JsonTypeInfo.As.EXTERNAL_PROPERTY);
+ fail("Should not have passed");
+ } catch (IllegalArgumentException e) {
+ verifyException(e, "Can not use includeAs of EXTERNAL_PROPERTY");
+ }
+ }
/*
/**********************************************************
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 365b466..dd096b4 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestGenericListSerialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestGenericListSerialization.java
@@ -5,7 +5,6 @@
import com.fasterxml.jackson.annotation.*;
-import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
@@ -50,14 +49,6 @@
public void testSubTypesFor356() throws Exception
{
ObjectMapper mapper = new ObjectMapper();
- /* 06-Sep-2010, tatus: This was not fixed for 1.6; and to keep junit test
- * suite green, let's not run it for versions prior to 1.7...
- */
- Version v = mapper.version();
- if (v.getMajorVersion() == 1 && v.getMinorVersion() == 6) {
- System.err.println("Note: skipping test for Jackson 1.6");
- return;
- }
JSONResponse<List<Parent>> input = new JSONResponse<List<Parent>>();
@@ -68,10 +59,7 @@
mapper.configure(MapperFeature.USE_STATIC_TYPING, true);
JavaType rootType = TypeFactory.defaultInstance().constructType(new TypeReference<JSONResponse<List<Parent>>>() { });
- byte[] json = mapper.writerWithType(rootType).writeValueAsBytes(input);
-// byte[] json = mapper.writeValueAsBytes(input);
-
-// System.out.println("After Serialization: " + new String(json));
+ byte[] json = mapper.writerFor(rootType).writeValueAsBytes(input);
JSONResponse<List<Parent>> out = mapper.readValue(json, 0, json.length, rootType);
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestNoTypeInfo.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestNoTypeInfo.java
index 2f59cfb..3678179 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestNoTypeInfo.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestNoTypeInfo.java
@@ -9,10 +9,10 @@
{
@JsonTypeInfo(use=JsonTypeInfo.Id.NONE)
@JsonDeserialize(as=NoType.class)
- private static interface NoTypeInterface {
+ static interface NoTypeInterface {
}
- private final static class NoType implements NoTypeInterface {
+ final static class NoType implements NoTypeInterface {
public int a = 3;
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPropertyTypeInfo.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPropertyTypeInfo.java
index 706bdd2..484f6cc 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPropertyTypeInfo.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPropertyTypeInfo.java
@@ -41,7 +41,7 @@
static class MethodWrapperBean
{
- private Object value;
+ protected Object value;
@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.WRAPPER_ARRAY)
public Object getValue() { return value; }
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestSubtypesExistingProperty.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestSubtypesExistingProperty.java
new file mode 100644
index 0000000..0bb7275
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestSubtypesExistingProperty.java
@@ -0,0 +1,403 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import java.util.*;
+
+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.core.type.TypeReference;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class TestSubtypesExistingProperty extends BaseMapTest {
+
+ /**
+ * Polymorphic base class - existing property as simple property on subclasses
+ */
+ @JsonTypeInfo(use = Id.NAME, include = As.EXISTING_PROPERTY, property = "type",
+ visible=true)
+ @JsonSubTypes({
+ @Type(value = Apple.class, name = "apple") ,
+ @Type(value = Orange.class, name = "orange")
+ })
+ static abstract class Fruit {
+ public String name;
+ protected Fruit(String n) { name = n; }
+ }
+
+ @JsonTypeName("apple")
+ static class Apple extends Fruit
+ {
+ public int seedCount;
+ public String type;
+
+ private Apple() { super(null);; }
+ public Apple(String name, int b) {
+ super(name);
+ seedCount = b;
+ type = "apple";
+ }
+ }
+
+ @JsonTypeName("orange")
+ static class Orange extends Fruit
+ {
+ public String color;
+ public String type;
+
+ private Orange() { super(null); }
+ public Orange(String name, String c) {
+ super(name);
+ color = c;
+ type = "orange";
+ }
+ }
+
+ static class FruitWrapper {
+ public Fruit fruit;
+ public FruitWrapper() {}
+ public FruitWrapper(Fruit f) { fruit = f; }
+ }
+
+ /**
+ * Polymorphic base class - existing property forced by abstract method
+ */
+ @JsonTypeInfo(use = Id.NAME, include = As.EXISTING_PROPERTY, property = "type")
+ @JsonSubTypes({
+ @Type(value = Dog.class, name = "doggie") ,
+ @Type(value = Cat.class, name = "kitty")
+ })
+ static abstract class Animal {
+ public String name;
+
+ protected Animal(String n) { name = n; }
+
+ public abstract String getType();
+ }
+
+ @JsonTypeName("doggie")
+ static class Dog extends Animal
+ {
+ public int boneCount;
+
+ private Dog() { super(null); }
+ public Dog(String name, int b) {
+ super(name);
+ boneCount = b;
+ }
+
+ @Override
+ public String getType() {
+ return "doggie";
+ }
+ }
+
+ @JsonTypeName("kitty")
+ static class Cat extends Animal
+ {
+ public String furColor;
+
+ private Cat() { super(null); }
+ public Cat(String name, String c) {
+ super(name);
+ furColor = c;
+ }
+
+ @Override
+ public String getType() {
+ return "kitty";
+ }
+ }
+
+ static class AnimalWrapper {
+ public Animal animal;
+ public AnimalWrapper() {}
+ public AnimalWrapper(Animal a) { animal = a; }
+ }
+
+
+ /**
+ * Polymorphic base class - existing property NOT forced by abstract method on base class
+ */
+ @JsonTypeInfo(use = Id.NAME, include = As.EXISTING_PROPERTY, property = "type")
+ @JsonSubTypes({
+ @Type(value = Accord.class, name = "accord") ,
+ @Type(value = Camry.class, name = "camry")
+ })
+ static abstract class Car {
+ public String name;
+ protected Car(String n) { name = n; }
+ }
+
+ @JsonTypeName("accord")
+ static class Accord extends Car
+ {
+ public int speakerCount;
+
+ private Accord() { super(null); }
+ public Accord(String name, int b) {
+ super(name);
+ speakerCount = b;
+ }
+
+ public String getType() {
+ return "accord";
+ }
+ }
+
+ @JsonTypeName("camry")
+ static class Camry extends Car
+ {
+ public String exteriorColor;
+
+ private Camry() { super(null); }
+ public Camry(String name, String c) {
+ super(name);
+ exteriorColor = c;
+ }
+
+ public String getType() {
+ return "camry";
+ }
+ }
+
+ static class CarWrapper {
+ public Car car;
+ public CarWrapper() {}
+ public CarWrapper(Car c) { car = c; }
+ }
+
+ private final ObjectMapper MAPPER = new ObjectMapper();
+
+ /*
+ /**********************************************************
+ /* Mock data
+ /**********************************************************
+ */
+
+ private static final Orange mandarin = new Orange("Mandarin Orange", "orange");
+ private static final String mandarinJson = "{\"name\":\"Mandarin Orange\",\"color\":\"orange\",\"type\":\"orange\"}";
+ private static final Apple pinguo = new Apple("Apple-A-Day", 16);
+ private static final String pinguoJson = "{\"name\":\"Apple-A-Day\",\"seedCount\":16,\"type\":\"apple\"}";
+ private static final FruitWrapper pinguoWrapper = new FruitWrapper(pinguo);
+ private static final String pinguoWrapperJson = "{\"fruit\":" + pinguoJson + "}";
+ private static final List<Fruit> fruitList = Arrays.asList(pinguo, mandarin);
+ private static final String fruitListJson = "[" + pinguoJson + "," + mandarinJson + "]";
+
+ private static final Cat beelzebub = new Cat("Beelzebub", "tabby");
+ private static final String beelzebubJson = "{\"name\":\"Beelzebub\",\"furColor\":\"tabby\",\"type\":\"kitty\"}";
+ private static final Dog rover = new Dog("Rover", 42);
+ private static final String roverJson = "{\"name\":\"Rover\",\"boneCount\":42,\"type\":\"doggie\"}";
+ private static final AnimalWrapper beelzebubWrapper = new AnimalWrapper(beelzebub);
+ private static final String beelzebubWrapperJson = "{\"animal\":" + beelzebubJson + "}";
+ private static final List<Animal> animalList = Arrays.asList(beelzebub, rover);
+ private static final String animalListJson = "[" + beelzebubJson + "," + roverJson + "]";
+
+ private static final Camry camry = new Camry("Sweet Ride", "candy-apple-red");
+ private static final String camryJson = "{\"name\":\"Sweet Ride\",\"exteriorColor\":\"candy-apple-red\",\"type\":\"camry\"}";
+ private static final Accord accord = new Accord("Road Rage", 6);
+ private static final String accordJson = "{\"name\":\"Road Rage\",\"speakerCount\":6,\"type\":\"accord\"}";
+ private static final CarWrapper camryWrapper = new CarWrapper(camry);
+ private static final String camryWrapperJson = "{\"car\":" + camryJson + "}";
+ private static final List<Car> carList = Arrays.asList(camry, accord);
+ private static final String carListJson = "[" + camryJson + "," + accordJson + "]";
+
+ /*
+ /**********************************************************
+ /* Unit tests
+ /**********************************************************
+ */
+
+ /**
+ * Fruits - serialization tests for simple property on sub-classes
+ */
+ public void testExistingPropertySerializationFruits() throws Exception
+ {
+ Map<String,Object> result = writeAndMap(MAPPER, pinguo);
+ assertEquals(3, result.size());
+ assertEquals(pinguo.name, result.get("name"));
+ assertEquals(pinguo.seedCount, result.get("seedCount"));
+ assertEquals(pinguo.type, result.get("type"));
+
+ result = writeAndMap(MAPPER, mandarin);
+ assertEquals(3, result.size());
+ assertEquals(mandarin.name, result.get("name"));
+ assertEquals(mandarin.color, result.get("color"));
+ assertEquals(mandarin.type, result.get("type"));
+
+ String pinguoSerialized = MAPPER.writeValueAsString(pinguo);
+ assertEquals(pinguoSerialized, pinguoJson);
+
+ String mandarinSerialized = MAPPER.writeValueAsString(mandarin);
+ assertEquals(mandarinSerialized, mandarinJson);
+
+ String fruitWrapperSerialized = MAPPER.writeValueAsString(pinguoWrapper);
+ assertEquals(fruitWrapperSerialized, pinguoWrapperJson);
+
+ String fruitListSerialized = MAPPER.writeValueAsString(fruitList);
+ assertEquals(fruitListSerialized, fruitListJson);
+ }
+
+ /**
+ * Fruits - deserialization tests for simple property on sub-classes
+ */
+ public void testSimpleClassAsExistingPropertyDeserializationFruits() throws Exception
+ {
+ Fruit pinguoDeserialized = MAPPER.readValue(pinguoJson, Fruit.class);
+ assertTrue(pinguoDeserialized instanceof Apple);
+ assertSame(pinguoDeserialized.getClass(), Apple.class);
+ assertEquals(pinguo.name, pinguoDeserialized.name);
+ assertEquals(pinguo.seedCount, ((Apple) pinguoDeserialized).seedCount);
+ assertEquals(pinguo.type, ((Apple) pinguoDeserialized).type);
+
+ FruitWrapper pinguoWrapperDeserialized = MAPPER.readValue(pinguoWrapperJson, FruitWrapper.class);
+ Fruit pinguoExtracted = pinguoWrapperDeserialized.fruit;
+ assertTrue(pinguoExtracted instanceof Apple);
+ assertSame(pinguoExtracted.getClass(), Apple.class);
+ assertEquals(pinguo.name, pinguoExtracted.name);
+ assertEquals(pinguo.seedCount, ((Apple) pinguoExtracted).seedCount);
+ assertEquals(pinguo.type, ((Apple) pinguoExtracted).type);
+
+ Fruit[] fruits = MAPPER.readValue(fruitListJson, Fruit[].class);
+ assertEquals(2, fruits.length);
+ assertEquals(Apple.class, fruits[0].getClass());
+ assertEquals("apple", ((Apple) fruits[0]).type);
+ assertEquals(Orange.class, fruits[1].getClass());
+ assertEquals("orange", ((Orange) fruits[1]).type);
+
+ List<Fruit> f2 = MAPPER.readValue(fruitListJson,
+ new TypeReference<List<Fruit>>() { });
+ assertNotNull(f2);
+ assertTrue(f2.size() == 2);
+ assertEquals(Apple.class, f2.get(0).getClass());
+ assertEquals(Orange.class, f2.get(1).getClass());
+ }
+
+ /**
+ * Animals - serialization tests for abstract method in base class
+ */
+ public void testExistingPropertySerializationAnimals() throws Exception
+ {
+ Map<String,Object> result = writeAndMap(MAPPER, beelzebub);
+ assertEquals(3, result.size());
+ assertEquals(beelzebub.name, result.get("name"));
+ assertEquals(beelzebub.furColor, result.get("furColor"));
+ assertEquals(beelzebub.getType(), result.get("type"));
+
+ result = writeAndMap(MAPPER, rover);
+ assertEquals(3, result.size());
+ assertEquals(rover.name, result.get("name"));
+ assertEquals(rover.boneCount, result.get("boneCount"));
+ assertEquals(rover.getType(), result.get("type"));
+
+ String beelzebubSerialized = MAPPER.writeValueAsString(beelzebub);
+ assertEquals(beelzebubSerialized, beelzebubJson);
+
+ String roverSerialized = MAPPER.writeValueAsString(rover);
+ assertEquals(roverSerialized, roverJson);
+
+ String animalWrapperSerialized = MAPPER.writeValueAsString(beelzebubWrapper);
+ assertEquals(animalWrapperSerialized, beelzebubWrapperJson);
+
+ String animalListSerialized = MAPPER.writeValueAsString(animalList);
+ assertEquals(animalListSerialized, animalListJson);
+ }
+
+ /**
+ * Animals - deserialization tests for abstract method in base class
+ */
+ public void testSimpleClassAsExistingPropertyDeserializationAnimals() throws Exception
+ {
+ Animal beelzebubDeserialized = MAPPER.readValue(beelzebubJson, Animal.class);
+ assertTrue(beelzebubDeserialized instanceof Cat);
+ assertSame(beelzebubDeserialized.getClass(), Cat.class);
+ assertEquals(beelzebub.name, beelzebubDeserialized.name);
+ assertEquals(beelzebub.furColor, ((Cat) beelzebubDeserialized).furColor);
+ assertEquals(beelzebub.getType(), beelzebubDeserialized.getType());
+
+ AnimalWrapper beelzebubWrapperDeserialized = MAPPER.readValue(beelzebubWrapperJson, AnimalWrapper.class);
+ Animal beelzebubExtracted = beelzebubWrapperDeserialized.animal;
+ assertTrue(beelzebubExtracted instanceof Cat);
+ assertSame(beelzebubExtracted.getClass(), Cat.class);
+ assertEquals(beelzebub.name, beelzebubExtracted.name);
+ assertEquals(beelzebub.furColor, ((Cat) beelzebubExtracted).furColor);
+ assertEquals(beelzebub.getType(), beelzebubExtracted.getType());
+
+ @SuppressWarnings("unchecked")
+ List<Animal> animalListDeserialized = MAPPER.readValue(animalListJson, List.class);
+ assertNotNull(animalListDeserialized);
+ assertTrue(animalListDeserialized.size() == 2);
+ Animal cat = MAPPER.convertValue(animalListDeserialized.get(0), Animal.class);
+ assertTrue(cat instanceof Cat);
+ assertSame(cat.getClass(), Cat.class);
+ Animal dog = MAPPER.convertValue(animalListDeserialized.get(1), Animal.class);
+ assertTrue(dog instanceof Dog);
+ assertSame(dog.getClass(), Dog.class);
+ }
+
+ /**
+ * Cars - serialization tests for no abstract method or type variable in base class
+ */
+ public void testExistingPropertySerializationCars() throws Exception
+ {
+ Map<String,Object> result = writeAndMap(MAPPER, camry);
+ assertEquals(3, result.size());
+ assertEquals(camry.name, result.get("name"));
+ assertEquals(camry.exteriorColor, result.get("exteriorColor"));
+ assertEquals(camry.getType(), result.get("type"));
+
+ result = writeAndMap(MAPPER, accord);
+ assertEquals(3, result.size());
+ assertEquals(accord.name, result.get("name"));
+ assertEquals(accord.speakerCount, result.get("speakerCount"));
+ assertEquals(accord.getType(), result.get("type"));
+
+ String camrySerialized = MAPPER.writeValueAsString(camry);
+ assertEquals(camrySerialized, camryJson);
+
+ String accordSerialized = MAPPER.writeValueAsString(accord);
+ assertEquals(accordSerialized, accordJson);
+
+ String carWrapperSerialized = MAPPER.writeValueAsString(camryWrapper);
+ assertEquals(carWrapperSerialized, camryWrapperJson);
+
+ String carListSerialized = MAPPER.writeValueAsString(carList);
+ assertEquals(carListSerialized, carListJson);
+ }
+
+ /**
+ * Cars - deserialization tests for no abstract method or type variable in base class
+ */
+ public void testSimpleClassAsExistingPropertyDeserializationCars() throws Exception
+ {
+ Car camryDeserialized = MAPPER.readValue(camryJson, Camry.class);
+ assertTrue(camryDeserialized instanceof Camry);
+ assertSame(camryDeserialized.getClass(), Camry.class);
+ assertEquals(camry.name, camryDeserialized.name);
+ assertEquals(camry.exteriorColor, ((Camry) camryDeserialized).exteriorColor);
+ assertEquals(camry.getType(), ((Camry) camryDeserialized).getType());
+
+ CarWrapper camryWrapperDeserialized = MAPPER.readValue(camryWrapperJson, CarWrapper.class);
+ Car camryExtracted = camryWrapperDeserialized.car;
+ assertTrue(camryExtracted instanceof Camry);
+ assertSame(camryExtracted.getClass(), Camry.class);
+ assertEquals(camry.name, camryExtracted.name);
+ assertEquals(camry.exteriorColor, ((Camry) camryExtracted).exteriorColor);
+ assertEquals(camry.getType(), ((Camry) camryExtracted).getType());
+
+ @SuppressWarnings("unchecked")
+ List<Car> carListDeserialized = MAPPER.readValue(carListJson, List.class);
+ assertNotNull(carListDeserialized);
+ assertTrue(carListDeserialized.size() == 2);
+ Car result = MAPPER.convertValue(carListDeserialized.get(0), Car.class);
+ assertTrue(result instanceof Camry);
+ assertSame(result.getClass(), Camry.class);
+
+ result = MAPPER.convertValue(carListDeserialized.get(1), Car.class);
+ assertTrue(result instanceof Accord);
+ assertSame(result.getClass(), Accord.class);
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedArrayDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedArrayDeserialization.java
index f9812bb..dfd562e 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedArrayDeserialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedArrayDeserialization.java
@@ -104,7 +104,7 @@
{
ObjectMapper m = new ObjectMapper();
// use class name, WRAPPER_OBJECT
- m.addMixInAnnotations(long[].class, WrapperMixIn.class);
+ m.addMixIn(long[].class, WrapperMixIn.class);
String JSON = "{\""+long[].class.getName()+"\":[5, 6, 7]}";
long[] value = m.readValue(JSON, long[].class);
assertNotNull(value);
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedArraySerialization.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedArraySerialization.java
index aed0f81..a7ddf02 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedArraySerialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedArraySerialization.java
@@ -125,7 +125,7 @@
public void testIntArray() throws Exception
{
ObjectMapper m = new ObjectMapper();
- m.addMixInAnnotations(int[].class, WrapperMixIn.class);
+ m.addMixIn(int[].class, WrapperMixIn.class);
int[] input = new int[] { 1, 2, 3 };
String clsName = int[].class.getName();
assertEquals("{\""+clsName+"\":[1,2,3]}", serializeAsString(m, input));
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedContainerSerialization.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedContainerSerialization.java
index b2ee863..92cc507 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedContainerSerialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedContainerSerialization.java
@@ -125,8 +125,8 @@
{
ArrayList<Animal> animals = new ArrayList<Animal>();
animals.add(new Dog("Spot"));
- JavaType rootType = TypeFactory.defaultInstance().constructParametricType(Iterator.class, Animal.class);
- String json = mapper.writerWithType(rootType).writeValueAsString(animals.iterator());
+ JavaType rootType = TypeFactory.defaultInstance().constructParametrizedType(Iterator.class, Iterator.class, Animal.class);
+ String json = mapper.writerFor(rootType).writeValueAsString(animals.iterator());
if (json.indexOf("\"object-type\":\"doggy\"") < 0) {
fail("No polymorphic type retained, should be; JSON = '"+json+"'");
}
@@ -139,7 +139,7 @@
l2.add(new Issue508A());
l.add(l2);
TypeReference<?> typeRef = new TypeReference<List<List<Issue508A>>>() {};
- String json = mapper.writerWithType(typeRef).writeValueAsString(l);
+ String json = mapper.writerFor(typeRef).writeValueAsString(l);
List<?> output = mapper.readValue(json, typeRef);
assertEquals(1, output.size());
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedDeserialization.java
index 4ba8381..c1ea52c 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedDeserialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedDeserialization.java
@@ -127,7 +127,7 @@
public void testTypeAsWrapper() throws Exception
{
ObjectMapper m = new ObjectMapper();
- m.addMixInAnnotations(Animal.class, TypeWithWrapper.class);
+ m.addMixIn(Animal.class, TypeWithWrapper.class);
String JSON = "{\".TestTypedDeserialization$Dog\" : "
+asJSONObjectValueString(m, "name", "Scooby", "boneCount", "6")+" }";
Animal a = m.readValue(JSON, Animal.class);
@@ -142,7 +142,7 @@
public void testTypeAsArray() throws Exception
{
ObjectMapper m = new ObjectMapper();
- m.addMixInAnnotations(Animal.class, TypeWithArray.class);
+ m.addMixIn(Animal.class, TypeWithArray.class);
// hmmh. Not good idea to rely on exact output, order may change. But...
String JSON = "[\""+Dog.class.getName()+"\", "
+asJSONObjectValueString(m, "name", "Martti", "boneCount", "11")+" ]";
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedSerialization.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedSerialization.java
index cd857f9..7b04c45 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedSerialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedSerialization.java
@@ -102,7 +102,7 @@
public void testTypeAsWrapper() throws Exception
{
ObjectMapper m = new ObjectMapper();
- m.addMixInAnnotations(Animal.class, TypeWithWrapper.class);
+ m.addMixIn(Animal.class, TypeWithWrapper.class);
Map<String,Object> result = writeAndMap(m, new Cat("Venla", "black"));
// should get a wrapper; keyed by minimal class name ("Cat" here)
assertEquals(1, result.size());
@@ -120,7 +120,7 @@
public void testTypeAsArray() throws Exception
{
ObjectMapper m = new ObjectMapper();
- m.addMixInAnnotations(Animal.class, TypeWithArray.class);
+ m.addMixIn(Animal.class, TypeWithArray.class);
// hmmh. Not good idea to rely on exact output, order may change. But...
Map<String,Object> result = writeAndMap(m, new AnimalWrapper(new Dog("Amadeus", 7)));
// First level, wrapper
@@ -197,7 +197,7 @@
List<Super> list = new ArrayList<Super>();
list.add(new A());
map.put(1L, list);
- String json = mapper.writerWithType(new TypeReference<Map<Long, Collection<Super>>>() {}).writeValueAsString(map);
+ String json = mapper.writerFor(new TypeReference<Map<Long, Collection<Super>>>() {}).writeValueAsString(map);
assertTrue("JSON does not contain '@class': "+json, json.contains("@class"));
}
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestVisibleTypeId.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestVisibleTypeId.java
index 644d187..3b9003e 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestVisibleTypeId.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestVisibleTypeId.java
@@ -1,10 +1,8 @@
package com.fasterxml.jackson.databind.jsontype;
-import com.fasterxml.jackson.annotation.JsonPropertyOrder;
-import com.fasterxml.jackson.annotation.JsonSubTypes;
-import com.fasterxml.jackson.annotation.JsonTypeId;
-import com.fasterxml.jackson.annotation.JsonTypeInfo;
-import com.fasterxml.jackson.annotation.JsonTypeName;
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
+import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
import com.fasterxml.jackson.databind.*;
/**
@@ -48,13 +46,6 @@
public void setType(String t) { type = t; }
}
- // as external id, bit trickier
- static class ExternalIdWrapper {
- @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.EXTERNAL_PROPERTY,
- property="type", visible=true)
- public ExternalIdBean bean = new ExternalIdBean();
- }
-
@JsonTypeName("ExternalType")
static class ExternalIdBean {
public int a = 2;
@@ -133,6 +124,32 @@
public int age = 41;
}
+
+ // [databind#408]
+ static class ExternalBeanWithId
+ {
+ protected String _type;
+
+ @JsonTypeInfo(use=Id.NAME, include=As.EXTERNAL_PROPERTY, property="type", visible=true)
+ public ValueBean bean;
+
+ public ExternalBeanWithId() { }
+ public ExternalBeanWithId(int v) {
+ bean = new ValueBean(v);
+ }
+
+ public void setType(String t) {
+ _type = t;
+ }
+ }
+
+ @JsonTypeName("vbean")
+ static class ValueBean {
+ public int value;
+
+ public ValueBean() { }
+ public ValueBean(int v) { value = v; }
+ }
/*
/**********************************************************
@@ -177,17 +194,6 @@
assertEquals("ObjectType", result.type);
}
- public void testVisibleWithExternalId() throws Exception
- {
- String json = MAPPER.writeValueAsString(new ExternalIdWrapper());
- // but then expect to read it back
- ExternalIdWrapper result = MAPPER.readValue(json, ExternalIdWrapper.class);
- assertEquals("ExternalType", result.bean.type);
- assertEquals(2, result.bean.a);
- }
-
- // [JACKSON-762]
-
public void testTypeIdFromProperty() throws Exception
{
assertEquals("{\"type\":\"SomeType\",\"a\":3}",
@@ -224,6 +230,21 @@
assertTrue(result instanceof I263Impl);
assertEquals(19, ((I263Impl) result).age);
}
+
+ // [databind#408]
+ /* NOTE: Handling changed between 2.4 and 2.5; earlier, type id was 'injected'
+ * inside POJO; but with 2.5 this was fixed so it would remain outside, similar
+ * to how JSON structure is.
+ */
+ public void testVisibleTypeId408() throws Exception
+ {
+ String json = MAPPER.writeValueAsString(new ExternalBeanWithId(3));
+ ExternalBeanWithId result = MAPPER.readValue(json, ExternalBeanWithId.class);
+ assertNotNull(result);
+ assertNotNull(result.bean);
+ assertEquals(3, result.bean.value);
+ assertEquals("vbean", result._type);
+ }
/*
/**********************************************************
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestWithGenerics.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestWithGenerics.java
index 4d82810..b3529f0 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestWithGenerics.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestWithGenerics.java
@@ -63,8 +63,7 @@
public MyParam(T v) { value = v; }
}
- private static class SomeObject {
- @SuppressWarnings("unused")
+ static class SomeObject {
public String someValue = UUID.randomUUID().toString();
}
@@ -157,7 +156,7 @@
{
Dog dog = new Dog("Fluffy", 3);
ContainerWithGetter<Animal> c2 = new ContainerWithGetter<Animal>(dog);
- String json = MAPPER.writerWithType(MAPPER.getTypeFactory().constructParametricType(ContainerWithGetter.class, Animal.class)).writeValueAsString(c2);
+ String json = MAPPER.writerFor(MAPPER.getTypeFactory().constructParametrizedType(ContainerWithGetter.class, ContainerWithGetter.class, Animal.class)).writeValueAsString(c2);
if (json.indexOf("\"object-type\":\"doggy\"") < 0) {
fail("polymorphic type not kept, result == "+json+"; should contain 'object-type':'...'");
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForClass.java b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForClass.java
index 6bfedad..d36132f 100644
--- a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForClass.java
+++ b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForClass.java
@@ -52,7 +52,7 @@
* use field
*/
m = new ObjectMapper();
- m.addMixInAnnotations(LeafClass.class, MixIn.class);
+ m.addMixIn(LeafClass.class, MixIn.class);
result = m.readValue("{\"a\":\"value\"}", LeafClass.class);
assertEquals("value", result.a);
}
@@ -63,7 +63,7 @@
public void testClassMixInsMidLevel() throws IOException
{
ObjectMapper m = new ObjectMapper();
- m.addMixInAnnotations(BaseClass.class, MixIn.class);
+ m.addMixIn(BaseClass.class, MixIn.class);
{
BaseClass result = m.readValue("{\"a\":\"value\"}", BaseClass.class);
assertEquals("value", result.a);
@@ -82,7 +82,7 @@
public void testClassMixInsForObjectClass() throws IOException
{
ObjectMapper m = new ObjectMapper();
- m.addMixInAnnotations(Object.class, MixIn.class);
+ m.addMixIn(Object.class, MixIn.class);
// will be seen for BaseClass
{
BaseClass result = m.readValue("{\"a\":\"\"}", BaseClass.class);
diff --git a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForCreators.java b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForCreators.java
index df1eef5..5118711 100644
--- a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForCreators.java
+++ b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForCreators.java
@@ -86,7 +86,7 @@
public void testForConstructor() throws IOException
{
ObjectMapper m = new ObjectMapper();
- m.addMixInAnnotations(BaseClassWithPrivateCtor.class, MixInForPrivate.class);
+ m.addMixIn(BaseClassWithPrivateCtor.class, MixInForPrivate.class);
BaseClassWithPrivateCtor result = m.readValue("\"?\"", BaseClassWithPrivateCtor.class);
assertEquals("?...", result._a);
}
@@ -102,7 +102,7 @@
// Then with simple mix-in: should change to use the factory method
m = new ObjectMapper();
- m.addMixInAnnotations(BaseClass.class, MixIn.class);
+ m.addMixIn(BaseClass.class, MixIn.class);
result = m.readValue("\"string\"", BaseClass.class);
assertEquals("stringX", result._a);
}
@@ -110,7 +110,7 @@
public void testFactoryMixIn() throws IOException
{
ObjectMapper m = new ObjectMapper();
- m.addMixInAnnotations(StringWrapper.class, StringWrapperMixIn.class);
+ m.addMixIn(StringWrapper.class, StringWrapperMixIn.class);
StringWrapper result = m.readValue("\"a\"", StringWrapper.class);
assertEquals("a", result._value);
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForMethods.java b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForMethods.java
index 2fcd1d0..0014fea 100644
--- a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForMethods.java
+++ b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForMethods.java
@@ -45,7 +45,7 @@
public void testWithAnySetter() throws IOException
{
ObjectMapper m = new ObjectMapper();
- m.addMixInAnnotations(BaseClass.class, MixIn.class);
+ m.addMixIn(BaseClass.class, MixIn.class);
BaseClass result = m.readValue("{ \"a\" : 3, \"b\" : true }", BaseClass.class);
assertNotNull(result);
assertEquals(2, result.values.size());
diff --git a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinInheritance.java b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinInheritance.java
index 6aa8871..6e4f774 100644
--- a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinInheritance.java
+++ b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinInheritance.java
@@ -52,7 +52,7 @@
public void testMixinFieldInheritance() throws IOException
{
ObjectMapper mapper = new ObjectMapper();
- mapper.addMixInAnnotations(Beano.class, BeanoMixinSub.class);
+ mapper.addMixIn(Beano.class, BeanoMixinSub.class);
Map<String,Object> result;
result = writeAndMap(mapper, new Beano());
assertEquals(2, result.size());
@@ -63,7 +63,7 @@
public void testMixinMethodInheritance() throws IOException
{
ObjectMapper mapper = new ObjectMapper();
- mapper.addMixInAnnotations(Beano2.class, BeanoMixinSub2.class);
+ mapper.addMixIn(Beano2.class, BeanoMixinSub2.class);
Map<String,Object> result;
result = writeAndMap(mapper, new Beano2());
assertEquals(2, result.size());
diff --git a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerForClass.java b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerForClass.java
index 2dfaa36..9fd211f 100644
--- a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerForClass.java
+++ b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerForClass.java
@@ -79,7 +79,7 @@
// then with top-level override
mapper = new ObjectMapper();
- mapper.addMixInAnnotations(LeafClass.class, MixIn.class);
+ mapper.addMixIn(LeafClass.class, MixIn.class);
result = writeAndMap(mapper, new LeafClass("abc"));
assertEquals(2, result.size());
assertEquals("abc", result.get("a"));
@@ -87,7 +87,7 @@
// mid-level override; should not have any effect
mapper = new ObjectMapper();
- mapper.addMixInAnnotations(BaseClass.class, MixIn.class);
+ mapper.addMixIn(BaseClass.class, MixIn.class);
result = writeAndMap(mapper, new LeafClass("abc"));
assertEquals(1, result.size());
assertEquals("abc", result.get("a"));
@@ -108,7 +108,7 @@
// then with working mid-level override, which effectively suppresses 'a'
mapper = new ObjectMapper();
- mapper.addMixInAnnotations(BaseClass.class, MixInAutoDetect.class);
+ mapper.addMixIn(BaseClass.class, MixInAutoDetect.class);
result = writeAndMap(mapper, bean);
assertEquals(1, result.size());
assertEquals("c2", result.get("c"));
@@ -118,7 +118,7 @@
result = writeAndMap(mapper2, bean);
assertEquals(2, result.size());
ObjectMapper mapper3 = mapper2.copy();
- mapper3.addMixInAnnotations(BaseClass.class, MixInAutoDetect.class);
+ mapper3.addMixIn(BaseClass.class, MixInAutoDetect.class);
result = writeAndMap(mapper3, bean);
assertEquals(1, result.size());
assertEquals("c2", result.get("c"));
diff --git a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerForFields.java b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerForFields.java
index 08eed33..2f7bf12 100644
--- a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerForFields.java
+++ b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerForFields.java
@@ -69,7 +69,7 @@
// and then with simple mix-in
mapper = new ObjectMapper();
- mapper.addMixInAnnotations(BaseClass.class, MixIn.class);
+ mapper.addMixIn(BaseClass.class, MixIn.class);
result = writeAndMap(mapper, bean);
assertEquals(2, result.size());
assertEquals("1", result.get("a"));
@@ -83,7 +83,7 @@
HashMap<Class<?>,Class<?>> mixins = new HashMap<Class<?>,Class<?>>();
mixins.put(SubClass.class, MixIn.class);
mixins.put(BaseClass.class, MixIn2.class);
- mapper.setMixInAnnotations(mixins);
+ mapper.setMixIns(mixins);
Map<String,Object> result;
result = writeAndMap(mapper, new SubClass("1", "2"));
diff --git a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerForMethods.java b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerForMethods.java
index a20867f..ad46ba9 100644
--- a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerForMethods.java
+++ b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerForMethods.java
@@ -115,7 +115,7 @@
// then with leaf-level mix-in
mapper = new ObjectMapper();
- mapper.addMixInAnnotations(BaseClass.class, MixIn.class);
+ mapper.addMixIn(BaseClass.class, MixIn.class);
result = writeAndMap(mapper, bean);
assertEquals(2, result.size());
assertEquals("b2", result.get("b2"));
@@ -133,7 +133,7 @@
Map<String,Object> result;
LeafClass bean = new LeafClass("XXX", "b2");
- mapper.addMixInAnnotations(BaseClass.class, MixIn.class);
+ mapper.addMixIn(BaseClass.class, MixIn.class);
result = writeAndMap(mapper, bean);
assertEquals(1, result.size());
assertEquals("XXX", result.get("a"));
@@ -146,7 +146,7 @@
public void testIntermediateMixin2() throws IOException
{
ObjectMapper mapper = new ObjectMapper();
- mapper.addMixInAnnotations(EmptyBean.class, MixInForSimple.class);
+ mapper.addMixIn(EmptyBean.class, MixInForSimple.class);
Map<String,Object> result = writeAndMap(mapper, new SimpleBean());
assertEquals(1, result.size());
assertEquals(Integer.valueOf(42), result.get("x"));
@@ -160,7 +160,7 @@
public void testObjectMixin() throws IOException
{
ObjectMapper mapper = new ObjectMapper();
- mapper.addMixInAnnotations(Object.class, ObjectMixIn.class);
+ mapper.addMixIn(Object.class, ObjectMixIn.class);
// First, with our bean...
Map<String,Object> result = writeAndMap(mapper, new BaseClass("a", "b"));
diff --git a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerWithViews.java b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerWithViews.java
index 03a8089..62b4310 100644
--- a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerWithViews.java
+++ b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerWithViews.java
@@ -176,7 +176,7 @@
// Property SerializationConfig.SerializationFeature.DEFAULT_VIEW_INCLUSION set to false
mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, Boolean.FALSE);
- mapper.addMixInAnnotations(A.class, AMixInAnnotation.class);
+ mapper.addMixIn(A.class, AMixInAnnotation.class);
String json = mapper.writerWithView(AView.class).writeValueAsString(a);
assertTrue(json.indexOf("\"name\"") > 0);
@@ -199,7 +199,7 @@
sourceMixins.put( SimpleTestData.class, TestDataJAXBMixin.class );
sourceMixins.put( ComplexTestData.class, TestComplexDataJAXBMixin.class );
- objectMapper.setMixInAnnotations(sourceMixins);
+ objectMapper.setMixIns(sourceMixins);
return objectMapper;
}
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/module/TestDuplicateRegistration.java b/src/test/java/com/fasterxml/jackson/databind/module/TestDuplicateRegistration.java
new file mode 100644
index 0000000..171b867
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/module/TestDuplicateRegistration.java
@@ -0,0 +1,56 @@
+package com.fasterxml.jackson.databind.module;
+
+import com.fasterxml.jackson.core.Version;
+import com.fasterxml.jackson.databind.*;
+
+public class TestDuplicateRegistration extends BaseMapTest
+{
+ static class MyModule extends Module {
+ public int regCount;
+
+ public MyModule() {
+ super();
+ }
+
+ @Override
+ public String getModuleName() {
+ return "TestModule";
+ }
+
+ @Override
+ public Version version() {
+ return Version.unknownVersion();
+ }
+
+ @Override
+ public void setupModule(SetupContext context) {
+ ++regCount;
+ }
+ }
+
+ public void testDuplicateRegistration() throws Exception
+ {
+ // by default, duplicate registration should be prevented
+ ObjectMapper mapper = new ObjectMapper();
+ assertTrue(mapper.isEnabled(MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS));
+ MyModule module = new MyModule();
+ mapper.registerModule(module);
+ mapper.registerModule(module);
+ mapper.registerModule(module);
+ assertEquals(1, module.regCount);
+
+ // but may be allowed by changing setting
+ mapper.disable(MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS);
+ mapper.registerModule(module);
+ assertEquals(2, module.regCount);
+
+ // and ditto for a new instance
+ ObjectMapper mapper2 = new ObjectMapper();
+ mapper2.disable(MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS);
+ MyModule module2 = new MyModule();
+ mapper.registerModule(module2);
+ mapper.registerModule(module2);
+ mapper.registerModule(module2);
+ assertEquals(3, module2.regCount);
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/module/TestTypeModifierNameResolution.java b/src/test/java/com/fasterxml/jackson/databind/module/TestTypeModifierNameResolution.java
index e19a2ae..cdd89ea 100644
--- a/src/test/java/com/fasterxml/jackson/databind/module/TestTypeModifierNameResolution.java
+++ b/src/test/java/com/fasterxml/jackson/databind/module/TestTypeModifierNameResolution.java
@@ -1,17 +1,16 @@
package com.fasterxml.jackson.databind.module;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
-import com.fasterxml.jackson.databind.JavaType;
-import com.fasterxml.jackson.databind.ObjectMapper;
+
+import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.type.TypeBindings;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.databind.type.TypeModifier;
-import com.fasterxml.jackson.test.BaseTest;
import java.lang.reflect.Type;
-public class TestTypeModifierNameResolution extends BaseTest {
-
+public class TestTypeModifierNameResolution extends BaseMapTest
+{
interface MyType {
String getData();
void setData(String data);
@@ -49,7 +48,7 @@
{
ObjectMapper mapper = new ObjectMapper();
mapper.setTypeFactory(mapper.getTypeFactory().withModifier(new CustomTypeModifier()));
- mapper.addMixInAnnotations(MyType.class, Mixin.class);
+ mapper.addMixIn(MyType.class, Mixin.class);
MyType obj = new MyTypeImpl();
obj.setData("something");
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TestConversions.java b/src/test/java/com/fasterxml/jackson/databind/node/TestConversions.java
index 12e4a58..21efe48 100644
--- a/src/test/java/com/fasterxml/jackson/databind/node/TestConversions.java
+++ b/src/test/java/com/fasterxml/jackson/databind/node/TestConversions.java
@@ -126,7 +126,7 @@
{
String JSON = "{\"leaf\":{\"value\":13}}";
ObjectMapper mapper = new ObjectMapper();
- mapper.addMixInAnnotations(Leaf.class, LeafMixIn.class);
+ mapper.addMixIn(Leaf.class, LeafMixIn.class);
JsonNode root = mapper.readTree(JSON);
// Ok, try converting to bean using two mechanisms
Root r1 = mapper.treeToValue(root, Root.class);
@@ -206,7 +206,7 @@
public void testBigDecimalAsPlainStringTreeConversion() throws Exception
{
ObjectMapper mapper = new ObjectMapper();
- mapper.enable(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN);
+ mapper.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
Map<String, Object> map = new HashMap<String, Object>();
String PI_STR = "3.00000000";
map.put("pi", new BigDecimal(PI_STR));
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TestNumberNodes.java b/src/test/java/com/fasterxml/jackson/databind/node/TestNumberNodes.java
index 8026914..ccd1007 100644
--- a/src/test/java/com/fasterxml/jackson/databind/node/TestNumberNodes.java
+++ b/src/test/java/com/fasterxml/jackson/databind/node/TestNumberNodes.java
@@ -3,9 +3,9 @@
import java.math.BigDecimal;
import java.math.BigInteger;
+import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
-
import com.fasterxml.jackson.databind.*;
/**
@@ -261,7 +261,7 @@
{
ObjectMapper mapper = new ObjectMapper()
.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)
- .enable(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN);
+ .enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
final String INPUT = "{\"x\":1e2}";
final JsonNode node = mapper.readTree(INPUT);
String result = mapper.writeValueAsString(node);
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TestTreeMapperSerializer.java b/src/test/java/com/fasterxml/jackson/databind/node/TestTreeMapperSerializer.java
index bd22dda..6114ed6 100644
--- a/src/test/java/com/fasterxml/jackson/databind/node/TestTreeMapperSerializer.java
+++ b/src/test/java/com/fasterxml/jackson/databind/node/TestTreeMapperSerializer.java
@@ -6,14 +6,13 @@
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
-import com.fasterxml.jackson.test.BaseTest;
/**
* This unit test suite tries to verify that the trees ObjectMapper
* constructs can be serialized properly.
*/
public class TestTreeMapperSerializer
- extends BaseTest
+ extends BaseMapTest
{
final static String FIELD1 = "first";
final static String FIELD2 = "Second?";
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestAnnotationInheritance.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestAnnotationInheritance.java
index 12e449c..199a279 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestAnnotationInheritance.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestAnnotationInheritance.java
@@ -1,6 +1,5 @@
package com.fasterxml.jackson.databind.ser;
-import java.io.*;
import java.util.*;
import com.fasterxml.jackson.annotation.*;
@@ -12,7 +11,7 @@
* bean serialization.
*/
public class TestAnnotationInheritance
- extends com.fasterxml.jackson.test.BaseTest
+ extends BaseMapTest
{
/*
/**********************************************************
@@ -92,19 +91,4 @@
assertEquals(Integer.valueOf(1), result.get("width"));
assertEquals(Integer.valueOf(2), result.get("length"));
}
-
- /*
- //////////////////////////////////////////////
- // Helper methods
- //////////////////////////////////////////////
- */
-
- @SuppressWarnings("unchecked")
- private Map<String,Object> writeAndMap(ObjectMapper m, Object value)
- throws IOException
- {
- StringWriter sw = new StringWriter();
- m.writeValue(sw, value);
- return (Map<String,Object>) m.readValue(sw.toString(), Object.class);
- }
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestAnyGetter.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestAnyGetter.java
index bcf4e8a..1b855da 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestAnyGetter.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestAnyGetter.java
@@ -8,15 +8,8 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
-public class TestAnyGetter
- extends BaseMapTest
+public class TestAnyGetter extends BaseMapTest
{
- /*
- /**********************************************************
- /* Helper bean classes
- /**********************************************************
- */
-
static class Bean
{
final static Map<String,Boolean> extra = new HashMap<String,Boolean>();
@@ -40,17 +33,32 @@
}
}
+ static class MapAsAny
+ {
+ protected Map<String,Object> stuff = new LinkedHashMap<String,Object>();
+
+ @JsonAnyGetter
+ public Map<String,Object> any() {
+ return stuff;
+ }
+
+ public void add(String key, Object value) {
+ stuff.put(key, value);
+ }
+ }
+
/*
/**********************************************************
/* Test cases
/**********************************************************
*/
+ private final ObjectMapper MAPPER = new ObjectMapper();
+
public void testSimpleJsonValue() throws Exception
{
- ObjectMapper m = new ObjectMapper();
- String json = serializeAsString(m, new Bean());
- Map<?,?> map = m.readValue(json, Map.class);
+ String json = MAPPER.writeValueAsString(new Bean());
+ Map<?,?> map = MAPPER.readValue(json, Map.class);
assertEquals(2, map.size());
assertEquals(Integer.valueOf(3), map.get("x"));
assertEquals(Boolean.TRUE, map.get("a"));
@@ -73,4 +81,13 @@
json = serializeAsString(m, new AnyOnlyBean());
assertEquals("{\"a\":3}", json);
}
+
+ // Trying to repro [databind#577]
+ public void testAnyWithNull() throws Exception
+ {
+ MapAsAny input = new MapAsAny();
+ input.add("bar", null);
+ assertEquals(aposToQuotes("{'bar':null}"),
+ MAPPER.writeValueAsString(input));
+ }
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestBeanSerializer.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestBeanSerializer.java
index 8263a95..c903328 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestBeanSerializer.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestBeanSerializer.java
@@ -5,7 +5,6 @@
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.core.*;
-
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
import com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder;
@@ -26,17 +25,11 @@
@SuppressWarnings("serial")
public class TestBeanSerializer extends BaseMapTest
{
- /*
- /********************************************************
- /* Helper types
- /********************************************************
- */
-
- static class ModuleImpl extends SimpleModule
+ static class SerializerModifierModule extends SimpleModule
{
protected BeanSerializerModifier modifier;
- public ModuleImpl(BeanSerializerModifier modifier)
+ public SerializerModifierModule(BeanSerializerModifier modifier)
{
super("test", Version.unknownVersion());
this.modifier = modifier;
@@ -178,6 +171,22 @@
}
}
+ // [Issue#539]: use post-modifier
+ static class EmptyBeanModifier539 extends BeanSerializerModifier
+ {
+ @Override
+ public List<BeanPropertyWriter> changeProperties(SerializationConfig config,
+ BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties)
+ {
+ return beanProperties;
+ }
+
+ @Override
+ public JsonSerializer<?> modifySerializer(SerializationConfig config,
+ BeanDescription beanDesc, JsonSerializer<?> serializer) {
+ return new BogusBeanSerializer(42);
+ }
+ }
// [Issue#120], arrays, collections, maps
static class ArraySerializerModifier extends BeanSerializerModifier {
@@ -251,7 +260,7 @@
public void testPropertyRemoval() throws Exception
{
ObjectMapper mapper = new ObjectMapper();
- mapper.registerModule(new ModuleImpl(new RemovingModifier("a")));
+ mapper.registerModule(new SerializerModifierModule(new RemovingModifier("a")));
Bean bean = new Bean();
assertEquals("{\"b\":\"b\"}", mapper.writeValueAsString(bean));
}
@@ -259,7 +268,7 @@
public void testPropertyReorder() throws Exception
{
ObjectMapper mapper = new ObjectMapper();
- mapper.registerModule(new ModuleImpl(new ReorderingModifier()));
+ mapper.registerModule(new SerializerModifierModule(new ReorderingModifier()));
Bean bean = new Bean();
assertEquals("{\"a\":\"a\",\"b\":\"b\"}", mapper.writeValueAsString(bean));
}
@@ -267,14 +276,14 @@
public void testBuilderReplacement() throws Exception
{
ObjectMapper mapper = new ObjectMapper();
- mapper.registerModule(new ModuleImpl(new BuilderModifier(new BogusBeanSerializer(17))));
+ mapper.registerModule(new SerializerModifierModule(new BuilderModifier(new BogusBeanSerializer(17))));
Bean bean = new Bean();
assertEquals("17", mapper.writeValueAsString(bean));
}
public void testSerializerReplacement() throws Exception
{
ObjectMapper mapper = new ObjectMapper();
- mapper.registerModule(new ModuleImpl(new ReplacingModifier(new BogusBeanSerializer(123))));
+ mapper.registerModule(new SerializerModifierModule(new ReplacingModifier(new BogusBeanSerializer(123))));
Bean bean = new Bean();
assertEquals("123", mapper.writeValueAsString(bean));
}
@@ -295,6 +304,22 @@
assertEquals("{\"bogus\":\"foo\"}", json);
}
+ // [Issue#539]
+ public void testEmptyBean539() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.registerModule(new SimpleModule("test", Version.unknownVersion()) {
+ @Override
+ public void setupModule(SetupContext context)
+ {
+ super.setupModule(context);
+ context.addBeanSerializerModifier(new EmptyBeanModifier539());
+ }
+ });
+ String json = mapper.writeValueAsString(new EmptyBean());
+ assertEquals("42", json);
+ }
+
// [Issue#121]
public void testModifyArraySerializer() throws Exception
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestCustomSerializers.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestCustomSerializers.java
index 2f54f5e..5efe029 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestCustomSerializers.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestCustomSerializers.java
@@ -110,7 +110,7 @@
public void testCustomization() throws Exception
{
ObjectMapper objectMapper = new ObjectMapper();
- objectMapper.addMixInAnnotations(Element.class, ElementMixin.class);
+ objectMapper.addMixIn(Element.class, ElementMixin.class);
Element element = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument().createElement("el");
StringWriter sw = new StringWriter();
objectMapper.writeValue(sw, element);
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestEmptyClass.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestEmptyClass.java
index 6aa1857..ed6f291 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestEmptyClass.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestEmptyClass.java
@@ -73,7 +73,7 @@
// Including class annotation through mix-ins
ObjectMapper m2 = new ObjectMapper();
- m2.addMixInAnnotations(Empty.class, EmptyWithAnno.class);
+ m2.addMixIn(Empty.class, EmptyWithAnno.class);
assertEquals("{}", m2.writeValueAsString(new Empty()));
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestEnumSerialization.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestEnumSerialization.java
index 552db83..d8e491e 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestEnumSerialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestEnumSerialization.java
@@ -7,9 +7,7 @@
import com.fasterxml.jackson.annotation.JsonFormat.Shape;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonValue;
-
import com.fasterxml.jackson.core.*;
-
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
@@ -136,8 +134,8 @@
public OK text = OK.V1;
}
- @JsonFormat(shape=JsonFormat.Shape.ARRAY) // not supported as of now
- static enum BrokenPoNum
+ @JsonFormat(shape=JsonFormat.Shape.ARRAY) // alias for 'number', as of 2.5
+ static enum PoAsArray
{
A, B;
}
@@ -152,6 +150,19 @@
jgen.writeString(value.name().toLowerCase());
}
}
+
+ // for [databind#572]
+ static class PoOverrideAsString
+ {
+ @JsonFormat(shape=Shape.STRING)
+ public PoNUM value = PoNUM.B;
+ }
+
+ static class PoOverrideAsNumber
+ {
+ @JsonFormat(shape=Shape.NUMBER)
+ public PoNUM value = PoNUM.B;
+ }
/*
/**********************************************************
@@ -201,7 +212,7 @@
{
// can't share, as new mix-ins are added
ObjectMapper m = new ObjectMapper();
- m.addMixInAnnotations(TestEnum.class, ToStringMixin.class);
+ m.addMixIn(TestEnum.class, ToStringMixin.class);
assertEquals("\"b\"", m.writeValueAsString(TestEnum.B));
}
@@ -279,15 +290,11 @@
public void testEnumAsIndexViaAnnotations() throws Exception {
assertEquals("{\"text\":0}", mapper.writeValueAsString(new PoNUMContainer()));
}
-
+
+ // As of 2.5, use of Shape.ARRAY is legal alias for "write as number"
public void testEnumAsObjectBroken() throws Exception
{
- try {
- String json = mapper.writeValueAsString(BrokenPoNum.A);
- fail("Should not have succeeded, produced: "+json);
- } catch (JsonMappingException e) {
- verifyException(e, "Unsupported serialization shape");
- }
+ assertEquals("0", mapper.writeValueAsString(PoAsArray.A));
}
// [Issue#227]
@@ -300,6 +307,15 @@
m.registerModule(module);
assertEquals(quote("b"), m.writeValueAsString(TestEnum.B));
}
+
+ // [databind#572]
+ public void testOverrideEnumAsString() throws Exception {
+ assertEquals("{\"value\":\"B\"}", mapper.writeValueAsString(new PoOverrideAsString()));
+ }
+
+ public void testOverrideEnumAsNumber() throws Exception {
+ assertEquals("{\"value\":1}", mapper.writeValueAsString(new PoOverrideAsNumber()));
+ }
}
// [JACKSON-757], non-inner enum
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestExceptionHandling.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestExceptionHandling.java
index a063521..cc1aec7 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestExceptionHandling.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestExceptionHandling.java
@@ -6,8 +6,6 @@
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.module.SimpleModule;
-import com.fasterxml.jackson.test.BaseTest;
-import com.fasterxml.jackson.test.BrokenStringWriter;
/**
* Unit test for verifying that exceptions are properly handled (caught,
@@ -15,7 +13,7 @@
* with Object serialization.
*/
public class TestExceptionHandling
- extends BaseTest
+ extends BaseMapTest
{
/*
/**********************************************************
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestFeatures.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestFeatures.java
index e2a8cdd..272edd4 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestFeatures.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestFeatures.java
@@ -168,7 +168,7 @@
// also: let's ensure that ObjectWriter won't interfere with it
bean = new CloseableBean();
- m.writerWithType(CloseableBean.class).writeValueAsString(bean);
+ m.writerFor(CloseableBean.class).writeValueAsString(bean);
assertTrue(bean.wasClosed);
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestGenericTypes.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestGenericTypes.java
index a28f9dd..450773f 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestGenericTypes.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestGenericTypes.java
@@ -45,15 +45,15 @@
public Person1(String name) { this.name = name; }
public String getName() {
- return name;
+ return name;
}
public Key<Account> getAccount() {
- return account;
+ return account;
}
public Long getId() {
- return id;
+ return id;
}
public void setAccount(Key<Account> account) {
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestJdkTypes.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestJdkTypes.java
index 7986544..aa2329c 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestJdkTypes.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestJdkTypes.java
@@ -9,6 +9,7 @@
import java.util.*;
import java.util.regex.Pattern;
+import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.*;
/**
@@ -36,11 +37,13 @@
public void testBigDecimalAsPlainString()
throws Exception
{
- MAPPER.enable(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN);
+ final ObjectMapper mapper = new ObjectMapper();
+
+ mapper.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
Map<String, Object> map = new HashMap<String, Object>();
String PI_STR = "3.00000000";
map.put("pi", new BigDecimal(PI_STR));
- String str = MAPPER.writeValueAsString(map);
+ String str = mapper.writeValueAsString(map);
assertEquals("{\"pi\":3.00000000}", str);
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonRawValue.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonRawValue.java
index 04b9850..6235901 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonRawValue.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonRawValue.java
@@ -21,9 +21,9 @@
@JsonPropertyOrder(alphabetic=true)
final static class ClassGetter<T>
{
- private final T _value;
+ protected final T _value;
- private ClassGetter(T value) { _value = value;}
+ protected ClassGetter(T value) { _value = value;}
public T getNonRaw() { return _value; }
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonSerialize.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonSerialize.java
index 9b63ec6..5af31f8 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonSerialize.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonSerialize.java
@@ -102,8 +102,8 @@
}
static class Bar294{
- @JsonProperty private String id;
- @JsonProperty private String name;
+ @JsonProperty protected String id;
+ @JsonProperty protected String name;
public Bar294() { }
public Bar294(String id) {
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestKeySerializers.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestKeySerializers.java
index 2e8e25e..ff51f04 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestKeySerializers.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestKeySerializers.java
@@ -4,8 +4,10 @@
import java.util.*;
import com.fasterxml.jackson.core.JsonGenerator;
+
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.module.SimpleModule;
public class TestKeySerializers extends BaseMapTest
{
@@ -34,6 +36,25 @@
}
}
+ enum ABC {
+ A, B, C
+ }
+
+ static class ABCSerializer extends JsonSerializer<ABC> {
+ @Override
+ public void serialize(ABC value, JsonGenerator jgen,
+ SerializerProvider provider) throws IOException {
+ jgen.writeFieldName("xxx"+value);
+ }
+ }
+
+ static class ABCMapWrapper {
+ public Map<ABC,String> stuff = new HashMap<ABC,String>();
+ public ABCMapWrapper() {
+ stuff.put(ABC.B, "bar");
+ }
+ }
+
/*
/**********************************************************
/* Unit tests
@@ -61,4 +82,16 @@
final String value2 = mapper.writeValueAsString(new KarlBean());
assertEquals("{\"map\":{\"Karl\":1}}", value2);
}
+
+ // Test custom key serializer for enum
+ public void testCustomForEnum() throws IOException
+ {
+ final ObjectMapper mapper = new ObjectMapper();
+ SimpleModule mod = new SimpleModule("test");
+ mod.addKeySerializer(ABC.class, new ABCSerializer());
+ mapper.registerModule(mod);
+
+ String json = mapper.writeValueAsString(new ABCMapWrapper());
+ assertEquals("{\"stuff\":{\"xxxB\":\"bar\"}}", json);
+ }
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestMapSerialization.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestMapSerialization.java
index 624e96c..a9c36e4 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestMapSerialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestMapSerialization.java
@@ -4,6 +4,7 @@
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
+import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
@@ -13,12 +14,6 @@
public class TestMapSerialization
extends BaseMapTest
{
- /*
- /**********************************************************
- /* Helper classes
- /**********************************************************
- */
-
/**
* Class needed for testing [JACKSON-220]
*/
@@ -69,17 +64,70 @@
}
}
+ // [Databind#565]: Support ser/deser of Map.Entry
+ static class StringIntMapEntry implements Map.Entry<String,Integer> {
+ public final String k;
+ public final Integer v;
+ public StringIntMapEntry(String k, Integer v) {
+ this.k = k;
+ this.v = v;
+ }
+
+ @Override
+ public String getKey() {
+ return k;
+ }
+
+ @Override
+ public Integer getValue() {
+ return v;
+ }
+
+ @Override
+ public Integer setValue(Integer value) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ // [databind#527]
+ static class NoNullValuesMapContainer {
+ @JsonInclude(content=JsonInclude.Include.NON_NULL)
+ public Map<String,String> stuff = new LinkedHashMap<String,String>();
+
+ public NoNullValuesMapContainer add(String key, String value) {
+ stuff.put(key, value);
+ return this;
+ }
+ }
+
+ // [databind#527]
+ @JsonInclude(content=JsonInclude.Include.NON_NULL)
+ static class NoNullsStringMap extends LinkedHashMap<String,String> {
+ public NoNullsStringMap add(String key, String value) {
+ put(key, value);
+ return this;
+ }
+ }
+
+ @JsonInclude(content=JsonInclude.Include.NON_EMPTY)
+ static class NoEmptyStringsMap extends LinkedHashMap<String,String> {
+ public NoEmptyStringsMap add(String key, String value) {
+ put(key, value);
+ return this;
+ }
+ }
+
/*
/**********************************************************
/* Test methods
/**********************************************************
*/
- final ObjectMapper MAPPER = objectMapper();
+ final private ObjectMapper MAPPER = objectMapper();
public void testUsingObjectWriter() throws IOException
{
- ObjectWriter w = MAPPER.writerWithType(Object.class);
+ ObjectWriter w = MAPPER.writerFor(Object.class);
Map<String,Object> map = new LinkedHashMap<String,Object>();
map.put("a", 1);
String json = w.writeValueAsString(map);
@@ -150,11 +198,53 @@
assertEquals("{\"a\":6,\"b\":3}", m.writer(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS).writeValueAsString(map));
}
- // [#335[
+ // [Databind#335]
public void testOrderByKeyViaProperty() throws IOException
{
MapOrderingBean input = new MapOrderingBean("c", "b", "a");
String json = MAPPER.writeValueAsString(input);
assertEquals(aposToQuotes("{'map':{'a':3,'b':2,'c':1}}"), json);
}
+
+ // [Databind#565]
+ public void testEnumMapEntry() throws IOException
+ {
+ StringIntMapEntry input = new StringIntMapEntry("answer", 42);
+ String json = MAPPER.writeValueAsString(input);
+ assertEquals(aposToQuotes("{'answer':42}"), json);
+
+ StringIntMapEntry[] array = new StringIntMapEntry[] { input };
+ json = MAPPER.writeValueAsString(array);
+ assertEquals(aposToQuotes("[{'answer':42}]"), json);
+ }
+
+ // [databind#527]
+ public void testNonNullValueMap() throws IOException
+ {
+ String json = MAPPER.writeValueAsString(new NoNullsStringMap()
+ .add("a", "foo")
+ .add("b", null)
+ .add("c", "bar"));
+ assertEquals(aposToQuotes("{'a':'foo','c':'bar'}"), json);
+ }
+
+ // [databind#527]
+ public void testNonEmptyValueMap() throws IOException
+ {
+ String json = MAPPER.writeValueAsString(new NoEmptyStringsMap()
+ .add("a", "foo")
+ .add("b", "bar")
+ .add("c", ""));
+ assertEquals(aposToQuotes("{'a':'foo','b':'bar'}"), json);
+ }
+
+ // [databind#527]
+ public void testNonNullValueMapViaProp() throws IOException
+ {
+ String json = MAPPER.writeValueAsString(new NoNullValuesMapContainer()
+ .add("a", "foo")
+ .add("b", null)
+ .add("c", "bar"));
+ assertEquals(aposToQuotes("{'stuff':{'a':'foo','c':'bar'}}"), json);
+ }
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestNumbers.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestNumbers.java
index c4d32f2..09d8b3c 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestNumbers.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestNumbers.java
@@ -14,27 +14,27 @@
{
private final ObjectMapper MAPPER = objectMapper();
- private static class IntAsString {
+ static class IntAsString {
@JsonFormat(shape=JsonFormat.Shape.STRING)
public int value = 3;
}
- private static class LongAsString {
+ static class LongAsString {
@JsonFormat(shape=JsonFormat.Shape.STRING)
public long value = 4;
}
-
- private static class DoubleAsString {
+
+ static class DoubleAsString {
@JsonFormat(shape=JsonFormat.Shape.STRING)
public double value = -0.5;
}
-
+
/*
/**********************************************************
/* Test methods
/**********************************************************
*/
-
+
public void testDouble() throws Exception
{
double[] values = new double[] {
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestObjectWriter.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestObjectWriter.java
index 9883921..bef8fe4 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestObjectWriter.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestObjectWriter.java
@@ -2,9 +2,9 @@
import java.util.*;
-
-import com.fasterxml.jackson.core.PrettyPrinter;
+import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* Unit tests for checking features added to {@link ObjectWriter}, such
@@ -13,10 +13,11 @@
public class TestObjectWriter
extends BaseMapTest
{
+ final ObjectMapper MAPPER = new ObjectMapper();
+
public void testPrettyPrinter() throws Exception
{
- ObjectMapper mapper = new ObjectMapper();
- ObjectWriter writer = mapper.writer();
+ ObjectWriter writer = MAPPER.writer();
HashMap<String, Integer> data = new HashMap<String,Integer>();
data.put("a", 1);
@@ -37,9 +38,30 @@
public void testPrefetch() throws Exception
{
- ObjectWriter writer = objectWriter();
+ ObjectWriter writer = MAPPER.writer();
assertFalse(writer.hasPrefetchedSerializer());
- writer = objectWriter().withType(String.class);
+ writer = writer.forType(String.class);
assertTrue(writer.hasPrefetchedSerializer());
}
-}
+
+ public void testObjectWriterFeatures() throws Exception
+ {
+ ObjectWriter writer = MAPPER.writer()
+ .without(JsonGenerator.Feature.QUOTE_FIELD_NAMES);
+ Map<String,Integer> map = new HashMap<String,Integer>();
+ map.put("a", 1);
+ assertEquals("{a:1}", writer.writeValueAsString(map));
+ // but can also reconfigure
+ assertEquals("{\"a\":1}", writer.with(JsonGenerator.Feature.QUOTE_FIELD_NAMES)
+ .writeValueAsString(map));
+ }
+
+ public void testObjectWriterWithNode() throws Exception
+ {
+ ObjectNode stuff = MAPPER.createObjectNode();
+ stuff.put("a", 5);
+ ObjectWriter writer = MAPPER.writerFor(JsonNode.class);
+ String json = writer.writeValueAsString(stuff);
+ assertEquals("{\"a\":5}", json);
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestRootType.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestRootType.java
index ac06a86..9665bad 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestRootType.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestRootType.java
@@ -90,7 +90,7 @@
assertEquals(Boolean.TRUE, result.get("b2"));
// and then using specified typed writer
- ObjectWriter w = mapper.writerWithType(BaseType.class);
+ ObjectWriter w = mapper.writerFor(BaseType.class);
String json = w.writeValueAsString(bean);
result = (Map<String,Object>)mapper.readValue(json, Map.class);
assertEquals(2, result.size());
@@ -104,7 +104,7 @@
SubType bean = new SubType();
// let's constrain by interface:
- ObjectWriter w = mapper.writerWithType(BaseInterface.class);
+ ObjectWriter w = mapper.writerFor(BaseInterface.class);
String json = w.writeValueAsString(bean);
@SuppressWarnings("unchecked")
Map<String,Object> result = mapper.readValue(json, Map.class);
@@ -118,7 +118,7 @@
// must force static typing, otherwise won't matter a lot
mapper.configure(MapperFeature.USE_STATIC_TYPING, true);
SubType[] ob = new SubType[] { new SubType() };
- String json = mapper.writerWithType(BaseInterface[].class).writeValueAsString(ob);
+ String json = mapper.writerFor(BaseInterface[].class).writeValueAsString(ob);
// should propagate interface type through due to root declaration; static typing
assertEquals("[{\"b\":3}]", json);
}
@@ -133,7 +133,7 @@
SubType bean = new SubType();
// and then let's try using incompatible type
- ObjectWriter w = mapper.writerWithType(HashMap.class);
+ ObjectWriter w = mapper.writerFor(HashMap.class);
try {
w.writeValueAsString(bean);
fail("Should have failed due to incompatible type");
@@ -153,12 +153,12 @@
final String EXP = "[{\"beanClass\":\"TestRootType$TestClass398\",\"property\":\"aa\"}]";
// First simplest way:
- String json = mapper.writerWithType(collectionType).writeValueAsString(typedList);
+ String json = mapper.writerFor(collectionType).writeValueAsString(typedList);
assertEquals(EXP, json);
StringWriter out = new StringWriter();
JsonFactory f = new JsonFactory();
- mapper.writerWithType(collectionType).writeValue(f.createGenerator(out), typedList);
+ mapper.writerFor(collectionType).writeValue(f.createGenerator(out), typedList);
assertEquals(EXP, out.toString());
}
@@ -179,8 +179,8 @@
public void testIssue456WrapperPart() throws Exception
{
ObjectMapper mapper = objectMapper();
- assertEquals("123", mapper.writerWithType(Integer.TYPE).writeValueAsString(Integer.valueOf(123)));
- assertEquals("456", mapper.writerWithType(Long.TYPE).writeValueAsString(Long.valueOf(456L)));
+ assertEquals("123", mapper.writerFor(Integer.TYPE).writeValueAsString(Integer.valueOf(123)));
+ assertEquals("456", mapper.writerFor(Long.TYPE).writeValueAsString(Long.valueOf(456L)));
}
// [JACKSON-630] also, allow annotation to define root name
@@ -197,7 +197,7 @@
cmd.uuid = "1234";
cmd.type = 1;
- ObjectWriter writer = WRAP_ROOT_MAPPER.writerWithType(TestCommandParent.class);
+ ObjectWriter writer = WRAP_ROOT_MAPPER.writerFor(TestCommandParent.class);
String json = writer.writeValueAsString(cmd);
assertEquals(json, "{\"TestCommandParent\":{\"uuid\":\"1234\",\"type\":1}}");
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestSerializationOrder.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestSerializationOrder.java
index f3802f6..6cf1b41 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestSerializationOrder.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestSerializationOrder.java
@@ -116,7 +116,7 @@
public void testOrderWithMixins() throws Exception
{
ObjectMapper m = new ObjectMapper();
- m.addMixInAnnotations(BeanWithOrder.class, OrderMixIn.class);
+ m.addMixIn(BeanWithOrder.class, OrderMixIn.class);
assertEquals("{\"b\":2,\"a\":1,\"c\":3,\"d\":4}",
serializeAsString(m, new BeanWithOrder(1, 2, 3, 4)));
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestTypedRootValueSerialization.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestTypedRootValueSerialization.java
index f442e26..ba43470 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestTypedRootValueSerialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestTypedRootValueSerialization.java
@@ -26,7 +26,7 @@
public void testTypedSerialization() throws Exception
{
ObjectMapper mapper = new ObjectMapper();
- String singleJson = mapper.writerWithType(Issue822Interface.class).writeValueAsString(new Issue822Impl());
+ String singleJson = mapper.writerFor(Issue822Interface.class).writeValueAsString(new Issue822Impl());
// start with specific value case:
assertEquals("{\"a\":3}", singleJson);
}
@@ -37,7 +37,7 @@
ObjectMapper mapper = new ObjectMapper();
// Work-around when real solution not yet implemented:
// mapper.enable(MapperFeature.USE_STATIC_TYPING);
- assertEquals("[{\"a\":3}]", mapper.writerWithType(Issue822Interface[].class).writeValueAsString(
+ assertEquals("[{\"a\":3}]", mapper.writerFor(Issue822Interface[].class).writeValueAsString(
new Issue822Interface[] { new Issue822Impl() }));
}
@@ -50,7 +50,7 @@
List<Issue822Interface> list = new ArrayList<Issue822Interface>();
list.add(new Issue822Impl());
- String listJson = mapper.writerWithType(new TypeReference<List<Issue822Interface>>(){})
+ String listJson = mapper.writerFor(new TypeReference<List<Issue822Interface>>(){})
.writeValueAsString(list);
assertEquals("[{\"a\":3}]", listJson);
}
@@ -60,7 +60,7 @@
ObjectMapper mapper = new ObjectMapper();
Map<String,Issue822Interface> map = new HashMap<String,Issue822Interface>();
map.put("a", new Issue822Impl());
- String listJson = mapper.writerWithType(new TypeReference<Map<String,Issue822Interface>>(){})
+ String listJson = mapper.writerFor(new TypeReference<Map<String,Issue822Interface>>(){})
.writeValueAsString(map);
assertEquals("{\"a\":{\"a\":3}}", listJson);
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestForwardReference.java b/src/test/java/com/fasterxml/jackson/databind/struct/TestForwardReference.java
similarity index 79%
rename from src/test/java/com/fasterxml/jackson/databind/deser/TestForwardReference.java
rename to src/test/java/com/fasterxml/jackson/databind/struct/TestForwardReference.java
index e4f0af5..87a5ce1 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestForwardReference.java
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestForwardReference.java
@@ -1,21 +1,14 @@
-package com.fasterxml.jackson.databind.deser;
+package com.fasterxml.jackson.databind.struct;
-import com.fasterxml.jackson.annotation.JsonIdentityInfo;
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.annotation.JsonSubTypes;
-import com.fasterxml.jackson.annotation.JsonTypeInfo;
-import com.fasterxml.jackson.annotation.ObjectIdGenerators;
-import com.fasterxml.jackson.core.JsonParseException;
+import java.io.IOException;
+
+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.SerializationFeature;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
/**
* Test for testing forward reference handling
*/
@@ -58,7 +51,7 @@
@JsonSubTypes({
@JsonSubTypes.Type(value = ForwardReferenceClassOne.class, name = "One"),
@JsonSubTypes.Type(value = ForwardReferenceClassTwo.class, name = "Two")})
- private static abstract class ForwardReferenceClass
+ static abstract class ForwardReferenceClass
{
public String id;
public void setId(String id) {
@@ -67,7 +60,7 @@
}
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY)
- private static class YetAnotherClass
+ static class YetAnotherClass
{
public YetAnotherClass() {}
public ForwardReferenceClass frc;
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/TestObjectIdDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/struct/TestObjectIdDeserialization.java
index 047a80b..d3cbdce 100644
--- a/src/test/java/com/fasterxml/jackson/databind/struct/TestObjectIdDeserialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestObjectIdDeserialization.java
@@ -109,7 +109,7 @@
static class ValueNodeExt
{
public int value;
- private int customId;
+ protected int customId;
public IdWrapperExt next;
public ValueNodeExt() { this(0); }
@@ -126,7 +126,7 @@
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class)
static class AnySetterObjectId {
- private Map<String, AnySetterObjectId> values = new HashMap<String, AnySetterObjectId>();
+ protected Map<String, AnySetterObjectId> values = new HashMap<String, AnySetterObjectId>();
@JsonAnySetter
public void anySet(String field, AnySetterObjectId value) {
@@ -180,8 +180,6 @@
return new PoolResolver(pool);
}
}
-
- private final ObjectMapper mapper = new ObjectMapper();
/*
/*****************************************************
@@ -189,6 +187,8 @@
/*****************************************************
*/
+ private final ObjectMapper mapper = new ObjectMapper();
+
private final static String EXP_SIMPLE_INT_CLASS = "{\"id\":1,\"value\":13,\"next\":1}";
public void testSimpleDeserializationClass() throws Exception
@@ -330,8 +330,7 @@
}
}
- public void testKeepCollectionOrdering()
- throws Exception
+ public void testKeepCollectionOrdering() throws Exception
{
String json = "{\"employees\":[2,1,"
+ "{\"id\":1,\"name\":\"First\",\"manager\":null,\"reports\":[2]},"
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/TestObjectIdWithPolymorphic.java b/src/test/java/com/fasterxml/jackson/databind/struct/TestObjectIdWithPolymorphic.java
index b0cb34c..5ba30dd 100644
--- a/src/test/java/com/fasterxml/jackson/databind/struct/TestObjectIdWithPolymorphic.java
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestObjectIdWithPolymorphic.java
@@ -18,9 +18,8 @@
static abstract class Base
{
public int value;
-
public Base next;
-
+
public Base() { this(0); }
public Base(int v) {
value = v;
@@ -45,7 +44,7 @@
public int id;
public Base811 owner;
- private Base811() {}
+ protected Base811() {}
public Base811(Process owner) {
this.owner = owner;
if (owner == null) {
@@ -70,8 +69,8 @@
super(owner);
this.parent = parent;
}
- private Activity() {
- super();
+ protected Activity() {
+ super();
}
}
@@ -80,7 +79,7 @@
public Scope(Process owner, Activity parent) {
super(owner, parent);
}
- private Scope() {
+ protected Scope() {
super();
}
}
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 33d2f54..c4fb52f 100644
--- a/src/test/java/com/fasterxml/jackson/databind/struct/TestPOJOAsArray.java
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestPOJOAsArray.java
@@ -4,9 +4,7 @@
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.annotation.JsonFormat.Shape;
-import com.fasterxml.jackson.databind.BaseMapTest;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
@@ -164,6 +162,15 @@
String json = mapper.writeValueAsString(new SingleBean());
assertEquals("\"foo\"", json);
}
+
+ public void testBeanAsArrayUnwrapped() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
+ SingleBean result = mapper.readValue("[\"foobar\"]", SingleBean.class);
+ assertNotNull(result);
+ assertEquals("foobar", result.name);
+ }
/*
/*****************************************************
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/TestPOJOAsArrayWithBuilder.java b/src/test/java/com/fasterxml/jackson/databind/struct/TestPOJOAsArrayWithBuilder.java
index 5dc72cb..2f572a4 100644
--- a/src/test/java/com/fasterxml/jackson/databind/struct/TestPOJOAsArrayWithBuilder.java
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestPOJOAsArrayWithBuilder.java
@@ -29,15 +29,13 @@
{
public int x, y;
- @SuppressWarnings("hiding")
- public SimpleBuilderXY withX(int x) {
- this.x = x;
+ public SimpleBuilderXY withX(int x0) {
+ this.x = x0;
return this;
}
- @SuppressWarnings("hiding")
- public SimpleBuilderXY withY(int y) {
- this.y = y;
+ public SimpleBuilderXY withY(int y0) {
+ this.y = y0;
return this;
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestParentChildReferences.java b/src/test/java/com/fasterxml/jackson/databind/struct/TestParentChildReferences.java
similarity index 96%
rename from src/test/java/com/fasterxml/jackson/databind/deser/TestParentChildReferences.java
rename to src/test/java/com/fasterxml/jackson/databind/struct/TestParentChildReferences.java
index 8c87415..1a6a264 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestParentChildReferences.java
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestParentChildReferences.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.deser;
+package com.fasterxml.jackson.databind.struct;
import java.util.*;
@@ -41,8 +41,8 @@
static class SimpleTreeNode2
{
public String name;
- private SimpleTreeNode2 parent;
- private SimpleTreeNode2 child;
+ protected SimpleTreeNode2 parent;
+ protected SimpleTreeNode2 child;
public SimpleTreeNode2() { this(null); }
public SimpleTreeNode2(String n) { name = n; }
@@ -141,7 +141,7 @@
public static class Parent {
@JsonManagedReference
- private final List<Child> children = new ArrayList<Child>();
+ protected final List<Child> children = new ArrayList<Child>();
public List<Child> getChildren() { return children; }
@@ -149,8 +149,8 @@
}
public static class Child {
- private Parent parent;
- private final String value; // So that the bean is not empty of properties
+ protected Parent parent;
+ protected final String value; // So that the bean is not empty of properties
public Child(@JsonProperty("value") String value) { this.value = value; }
@@ -160,9 +160,7 @@
public Parent getParent() { return parent; }
public void setParent(Parent parent) { this.parent = parent; }
- }
-
- // [JACKSON-368]
+ }
@JsonTypeInfo(use=Id.NAME)
@JsonSubTypes({@JsonSubTypes.Type(ConcreteNode.class)})
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 7a3bfa2..aec92c5 100644
--- a/src/test/java/com/fasterxml/jackson/databind/type/TestJavaType.java
+++ b/src/test/java/com/fasterxml/jackson/databind/type/TestJavaType.java
@@ -2,6 +2,7 @@
import java.util.*;
+import com.fasterxml.jackson.databind.BaseMapTest;
import com.fasterxml.jackson.databind.JavaType;
/**
@@ -9,7 +10,7 @@
* some degree
*/
public class TestJavaType
- extends com.fasterxml.jackson.test.BaseTest
+ extends BaseMapTest
{
static class BaseType { }
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 1aaac42..3a15673 100644
--- a/src/test/java/com/fasterxml/jackson/databind/type/TestTypeBindings.java
+++ b/src/test/java/com/fasterxml/jackson/databind/type/TestTypeBindings.java
@@ -2,6 +2,7 @@
import java.util.*;
+import com.fasterxml.jackson.databind.BaseMapTest;
import com.fasterxml.jackson.databind.JavaType;
/**
@@ -9,7 +10,7 @@
* implemented by {@link TypeBindings} class.
*/
public class TestTypeBindings
- extends com.fasterxml.jackson.test.BaseTest
+ extends BaseMapTest
{
static class AbstractType<A,B> { }
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 62dbb9f..e861cb7 100644
--- a/src/test/java/com/fasterxml/jackson/databind/type/TestTypeFactory.java
+++ b/src/test/java/com/fasterxml/jackson/databind/type/TestTypeFactory.java
@@ -5,21 +5,15 @@
import java.util.concurrent.atomic.AtomicReference;
import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.JavaType;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.type.CollectionType;
-import com.fasterxml.jackson.databind.type.HierarchicType;
-import com.fasterxml.jackson.databind.type.MapType;
-import com.fasterxml.jackson.databind.type.SimpleType;
-import com.fasterxml.jackson.databind.type.TypeFactory;
-import com.fasterxml.jackson.test.BaseTest;
+
+import com.fasterxml.jackson.databind.*;
/**
* Simple tests to verify that the {@link TypeFactory} constructs
* type information as expected.
*/
public class TestTypeFactory
- extends BaseTest
+ extends BaseMapTest
{
/*
/**********************************************************
@@ -198,7 +192,7 @@
{
TypeFactory tf = TypeFactory.defaultInstance();
// first, simple class based
- JavaType t = tf.constructParametricType(ArrayList.class, String.class); // ArrayList<String>
+ JavaType t = tf.constructParametrizedType(ArrayList.class, Collection.class, String.class); // ArrayList<String>
assertEquals(CollectionType.class, t.getClass());
JavaType strC = tf.constructType(String.class);
assertEquals(1, t.containedTypeCount());
@@ -206,7 +200,7 @@
assertNull(t.containedType(1));
// Then using JavaType
- JavaType t2 = tf.constructParametricType(Map.class, strC, t); // Map<String,ArrayList<String>>
+ JavaType t2 = tf.constructParametrizedType(Map.class, Map.class, strC, t); // Map<String,ArrayList<String>>
// should actually produce a MapType
assertEquals(MapType.class, t2.getClass());
assertEquals(2, t2.containedTypeCount());
@@ -215,7 +209,8 @@
assertNull(t2.containedType(2));
// and then custom generic type as well
- JavaType custom = tf.constructParametricType(SingleArgGeneric.class, String.class);
+ JavaType custom = tf.constructParametrizedType(SingleArgGeneric.class, SingleArgGeneric.class,
+ String.class);
assertEquals(SimpleType.class, custom.getClass());
assertEquals(1, custom.containedTypeCount());
assertEquals(strC, custom.containedType(0));
@@ -226,14 +221,14 @@
// And finally, ensure that we can't create invalid combinations
try {
// Maps must take 2 type parameters, not just one
- tf.constructParametricType(Map.class, strC);
+ tf.constructParametrizedType(Map.class, Map.class, strC);
} catch (IllegalArgumentException e) {
verifyException(e, "Need exactly 2 parameter types for Map types");
}
try {
// Type only accepts one type param
- tf.constructParametricType(SingleArgGeneric.class, strC, strC);
+ tf.constructParametrizedType(SingleArgGeneric.class, SingleArgGeneric.class, strC, strC);
} catch (IllegalArgumentException e) {
verifyException(e, "expected 1 parameters, was given 2");
}
@@ -316,17 +311,6 @@
assertNull(sup2.getSuperType());
}
- public void testAtomicArrayRefParameterDetection()
- {
- TypeFactory tf = TypeFactory.defaultInstance();
- JavaType type = tf.constructType(new TypeReference<AtomicReference<long[]>>() { });
- HierarchicType sub = tf._findSuperTypeChain(type.getRawClass(), AtomicReference.class);
- assertNotNull(sub);
- assertEquals(0, _countSupers(sub));
- assertTrue(AtomicReference.class.isAssignableFrom(type.getRawClass()));
- assertNull(sub.getSuperType());
- }
-
private int _countSupers(HierarchicType t)
{
int depth = 0;
@@ -335,7 +319,7 @@
}
return depth;
}
-
+
/*
/**********************************************************
/* Unit tests: map/collection type parameter resolution
@@ -431,7 +415,31 @@
assertEquals(1, list.size());
assertEquals("...", list.get(0));
}
-
+
+ public void testSneakySelfRefs() throws Exception
+ {
+ ObjectMapper mapper = new ObjectMapper();
+ String json = mapper.writeValueAsString(new SneakyBean2());
+ assertEquals("{\"foobar\":null}", json);
+ }
+
+ /*
+ /**********************************************************
+ /* Unit tests: handling of specific JDK types
+ /**********************************************************
+ */
+
+ public void testAtomicArrayRefParameterDetection()
+ {
+ TypeFactory tf = TypeFactory.defaultInstance();
+ JavaType type = tf.constructType(new TypeReference<AtomicReference<long[]>>() { });
+ HierarchicType sub = tf._findSuperTypeChain(type.getRawClass(), AtomicReference.class);
+ assertNotNull(sub);
+ assertEquals(0, _countSupers(sub));
+ assertTrue(AtomicReference.class.isAssignableFrom(type.getRawClass()));
+ assertNull(sub.getSuperType());
+ }
+
public void testAtomicArrayRefParameters()
{
TypeFactory tf = TypeFactory.defaultInstance();
@@ -442,13 +450,19 @@
assertEquals(tf.constructType(long[].class), params[0]);
}
- public void testSneakySelfRefs() throws Exception
+ static abstract class StringIntMapEntry implements Map.Entry<String,Integer> { }
+
+ public void testMapEntryResolution()
{
- ObjectMapper mapper = new ObjectMapper();
- String json = mapper.writeValueAsString(new SneakyBean2());
- assertEquals("{\"foobar\":null}", json);
+ TypeFactory tf = TypeFactory.defaultInstance();
+ JavaType t = tf.constructType(StringIntMapEntry.class);
+ assertTrue(t.hasGenericTypes());
+ assertEquals(2, t.containedTypeCount());
+ assertEquals(String.class, t.containedType(0).getRawClass());
+ assertEquals(Integer.class, t.containedType(1).getRawClass());
+ // NOTE: no key/content types, at least not as of 2.5
}
-
+
/*
/**********************************************************
/* Unit tests: construction of "raw" types
diff --git a/src/test/java/com/fasterxml/jackson/databind/type/TestTypeResolution.java b/src/test/java/com/fasterxml/jackson/databind/type/TestTypeResolution.java
index 30be92e..b9dda04 100644
--- a/src/test/java/com/fasterxml/jackson/databind/type/TestTypeResolution.java
+++ b/src/test/java/com/fasterxml/jackson/databind/type/TestTypeResolution.java
@@ -3,14 +3,14 @@
import java.util.*;
import com.fasterxml.jackson.core.type.TypeReference;
-
+import com.fasterxml.jackson.databind.BaseMapTest;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.type.CollectionType;
import com.fasterxml.jackson.databind.type.MapType;
import com.fasterxml.jackson.databind.type.TypeFactory;
@SuppressWarnings("serial")
-public class TestTypeResolution extends com.fasterxml.jackson.test.BaseTest
+public class TestTypeResolution extends BaseMapTest
{
public static class LongValuedMap<K> extends HashMap<K, Long> { }
diff --git a/src/test/java/com/fasterxml/jackson/databind/util/ISO8601UtilsTest.java b/src/test/java/com/fasterxml/jackson/databind/util/ISO8601UtilsTest.java
index 21b9046..9c97807 100644
--- a/src/test/java/com/fasterxml/jackson/databind/util/ISO8601UtilsTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/util/ISO8601UtilsTest.java
@@ -1,28 +1,39 @@
package com.fasterxml.jackson.databind.util;
-import java.util.*;
+import java.text.ParseException;
import java.text.ParsePosition;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.TimeZone;
import com.fasterxml.jackson.databind.BaseMapTest;
-import com.fasterxml.jackson.databind.util.ISO8601Utils;
/**
* @see ISO8601Utils
*/
-public class ISO8601UtilsTest extends BaseMapTest
-{
+public class ISO8601UtilsTest extends BaseMapTest {
private Date date;
+ private Date dateWithoutTime;
private Date dateZeroMillis;
+ private Date dateZeroSecondAndMillis;
@Override
- public void setUp()
- {
+ public void setUp() {
Calendar cal = new GregorianCalendar(2007, 8 - 1, 13, 19, 51, 23);
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
cal.set(Calendar.MILLISECOND, 789);
date = cal.getTime();
cal.set(Calendar.MILLISECOND, 0);
dateZeroMillis = cal.getTime();
+ cal.set(Calendar.SECOND, 0);
+ dateZeroSecondAndMillis = cal.getTime();
+
+ cal = new GregorianCalendar(2007, 8 - 1, 13, 0, 0, 0);
+ cal.set(Calendar.MILLISECOND, 0);
+ cal.setTimeZone(TimeZone.getTimeZone("GMT"));
+ dateWithoutTime = cal.getTime();
+
}
public void testFormat() {
@@ -58,4 +69,85 @@
assertEquals(date, d);
}
+ public void testParseShortDate() throws java.text.ParseException {
+ Date d = ISO8601Utils.parse("20070813T19:51:23.789Z", new ParsePosition(0));
+ assertEquals(date, d);
+
+ d = ISO8601Utils.parse("20070813T19:51:23Z", new ParsePosition(0));
+ assertEquals(dateZeroMillis, d);
+
+ d = ISO8601Utils.parse("20070813T21:51:23.789+02:00", new ParsePosition(0));
+ assertEquals(date, d);
+ }
+
+ public void testParseShortTime() throws java.text.ParseException {
+ Date d = ISO8601Utils.parse("2007-08-13T195123.789Z", new ParsePosition(0));
+ assertEquals(date, d);
+
+ d = ISO8601Utils.parse("2007-08-13T195123Z", new ParsePosition(0));
+ assertEquals(dateZeroMillis, d);
+
+ d = ISO8601Utils.parse("2007-08-13T215123.789+02:00", new ParsePosition(0));
+ assertEquals(date, d);
+ }
+
+ public void testParseShortDateTime() throws java.text.ParseException {
+ Date d = ISO8601Utils.parse("20070813T195123.789Z", new ParsePosition(0));
+ assertEquals(date, d);
+
+ d = ISO8601Utils.parse("20070813T195123Z", new ParsePosition(0));
+ assertEquals(dateZeroMillis, d);
+
+ d = ISO8601Utils.parse("20070813T215123.789+02:00", new ParsePosition(0));
+ assertEquals(date, d);
+ }
+
+ public void testParseWithoutTime() throws ParseException {
+ Date d = ISO8601Utils.parse("2007-08-13Z", new ParsePosition(0));
+ assertEquals(dateWithoutTime, d);
+
+ d = ISO8601Utils.parse("20070813Z", new ParsePosition(0));
+ assertEquals(dateWithoutTime, d);
+
+ d = ISO8601Utils.parse("2007-08-13+00:00", new ParsePosition(0));
+ assertEquals(dateWithoutTime, d);
+
+ d = ISO8601Utils.parse("20070813+00:00", new ParsePosition(0));
+ assertEquals(dateWithoutTime, d);
+ }
+
+ public void testParseWithoutTimeAndTimeZoneMustFail() {
+ try {
+ ISO8601Utils.parse("2007-08-13", new ParsePosition(0));
+ fail();
+ } catch (ParseException p) {
+ }
+ try {
+ ISO8601Utils.parse("20070813", new ParsePosition(0));
+ fail();
+ } catch (ParseException p) {
+ }
+ try {
+ ISO8601Utils.parse("2007-08-13", new ParsePosition(0));
+ fail();
+ } catch (ParseException p) {
+ }
+ try {
+ ISO8601Utils.parse("20070813", new ParsePosition(0));
+ fail();
+ } catch (ParseException p) {
+ }
+ }
+
+
+ public void testParseOptional() throws java.text.ParseException {
+ Date d = ISO8601Utils.parse("2007-08-13T19:51Z", new ParsePosition(0));
+ assertEquals(dateZeroSecondAndMillis, d);
+
+ d = ISO8601Utils.parse("2007-08-13T1951Z", new ParsePosition(0));
+ assertEquals(dateZeroSecondAndMillis, d);
+
+ d = ISO8601Utils.parse("2007-08-13T21:51+02:00", new ParsePosition(0));
+ assertEquals(dateZeroSecondAndMillis, d);
+ }
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/util/TestObjectBuffer.java b/src/test/java/com/fasterxml/jackson/databind/util/TestObjectBuffer.java
index e709136..bdaba8f 100644
--- a/src/test/java/com/fasterxml/jackson/databind/util/TestObjectBuffer.java
+++ b/src/test/java/com/fasterxml/jackson/databind/util/TestObjectBuffer.java
@@ -2,10 +2,10 @@
import java.util.*;
-import com.fasterxml.jackson.test.BaseTest;
+import com.fasterxml.jackson.databind.BaseMapTest;
public class TestObjectBuffer
- extends BaseTest
+ extends BaseMapTest
{
/**
* First a test that treats results as plain old Object[]
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 1e9b566..c0b201f 100644
--- a/src/test/java/com/fasterxml/jackson/databind/util/TestTokenBuffer.java
+++ b/src/test/java/com/fasterxml/jackson/databind/util/TestTokenBuffer.java
@@ -5,9 +5,10 @@
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.util.JsonParserSequence;
+import com.fasterxml.jackson.databind.BaseMapTest;
import com.fasterxml.jackson.databind.ObjectMapper;
-public class TestTokenBuffer extends com.fasterxml.jackson.test.BaseTest
+public class TestTokenBuffer extends BaseMapTest
{
/*
/**********************************************************
diff --git a/src/test/java/com/fasterxml/jackson/databind/views/TestViewDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/views/TestViewDeserialization.java
index e5e4edc..f1eb816 100644
--- a/src/test/java/com/fasterxml/jackson/databind/views/TestViewDeserialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/views/TestViewDeserialization.java
@@ -59,7 +59,7 @@
// but with different views, different contents
bean = mapper.readerWithView(ViewAA.class)
- .withType(Bean.class)
+ .forType(Bean.class)
.readValue("{\"a\":3, \"aa\":\"foo\", \"b\": 9 }");
// should include 'a' and 'aa' (as per view)
assertEquals(3, bean.a);
@@ -68,7 +68,7 @@
assertEquals(0, bean.b);
bean = mapper.readerWithView(ViewA.class)
- .withType(Bean.class)
+ .forType(Bean.class)
.readValue("{\"a\":1, \"aa\":\"x\", \"b\": 3 }");
assertEquals(1, bean.a);
assertNull(bean.aa);
@@ -95,7 +95,7 @@
// but with, say, AA, will not get 'b'
bean = myMapper.readerWithView(ViewAA.class)
- .withType(DefaultsBean.class)
+ .forType(DefaultsBean.class)
.readValue("{\"a\":1, \"b\": 2 }");
// 'a' not there any more
assertEquals(0, bean.a);
diff --git a/src/test/java/com/fasterxml/jackson/failing/JSOGDeserialize622Test.java b/src/test/java/com/fasterxml/jackson/failing/JSOGDeserialize622Test.java
new file mode 100644
index 0000000..1c19570
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/JSOGDeserialize622Test.java
@@ -0,0 +1,139 @@
+package com.fasterxml.jackson.failing;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.annotation.JsonIdentityInfo;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.ObjectIdGenerator;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Unit test(s) for [databind#622], supporting non-scalar-Object-ids,
+ * to support things like JSOG.
+ */
+public class JSOGDeserialize622Test extends BaseMapTest
+{
+ /** the key of the property that holds the ref */
+ public static final String REF_KEY = "@ref";
+
+ /**
+ * JSON input
+ */
+ private static final String EXP_EXAMPLE_JSOG = aposToQuotes(
+ "{'@id':'1','foo':66,'next':{'"+REF_KEY+"':'1'}}");
+
+ private final ObjectMapper mapper = new ObjectMapper();
+
+ /**
+ * Customer IdGenerator
+ */
+ static class JSOGGenerator extends ObjectIdGenerator<JSOGRef> {
+
+ private static final long serialVersionUID = 1L;
+ protected transient int _nextValue;
+ protected final Class<?> _scope;
+
+ protected JSOGGenerator() { this(null, -1); }
+
+ protected JSOGGenerator(Class<?> scope, int nextValue) {
+ _scope = scope;
+ _nextValue = nextValue;
+ }
+
+ @Override
+ public Class<?> getScope() {
+ return _scope;
+ }
+
+ @Override
+ public boolean canUseFor(ObjectIdGenerator<?> gen) {
+ return (gen.getClass() == getClass()) && (gen.getScope() == _scope);
+ }
+
+ @Override
+ public ObjectIdGenerator<JSOGRef> forScope(Class<?> scope) {
+ return (_scope == scope) ? this : new JSOGGenerator(scope, _nextValue);
+ }
+
+ @Override
+ public ObjectIdGenerator<JSOGRef> newForSerialization(Object context) {
+ return new JSOGGenerator(_scope, 1);
+ }
+
+ @Override
+ public com.fasterxml.jackson.annotation.ObjectIdGenerator.IdKey key(Object key) {
+ return new IdKey(getClass(), _scope, key);
+ }
+
+ @Override
+ public JSOGRef generateId(Object forPojo) {
+ int id = _nextValue;
+ ++_nextValue;
+ return new JSOGRef(id);
+ }
+ }
+
+ /**
+ * The reference deserializer
+ */
+ static class JSOGRefDeserializer extends JsonDeserializer<JSOGRef>
+ {
+ @Override
+ public JSOGRef deserialize(JsonParser jp, DeserializationContext ctx) throws IOException, JsonProcessingException {
+ JsonNode node = jp.readValueAsTree();
+ return node.isTextual()
+ ? new JSOGRef(node.asInt()) : new JSOGRef(node.get(REF_KEY).asInt());
+ }
+ }
+
+ /**
+ * The reference object
+ */
+ @JsonDeserialize(using=JSOGRefDeserializer.class)
+ static class JSOGRef
+ {
+ @JsonProperty(REF_KEY)
+ public int ref;
+
+ public JSOGRef() { }
+
+ public JSOGRef(int val) {
+ ref = val;
+ }
+ }
+
+
+ /**
+ * Example class using JSOGGenerator
+ */
+ @JsonIdentityInfo(generator=JSOGGenerator.class)
+ public static class IdentifiableExampleJSOG {
+ public int foo;
+ public IdentifiableExampleJSOG next;
+ }
+
+ /*
+ /**********************************************************************
+ /* Test methods
+ /**********************************************************************
+ */
+
+ // for [databind#622]
+ public void testStructJSOGRef() throws Exception {
+
+ // Because the value ({@ref:1}) is not scalar, parser thinks it is not an id
+ // and tries to deserialize as normal a new IdentifiableExampleJSOG
+ // then complains about unrecognized field "@ref"
+ IdentifiableExampleJSOG result = mapper.readValue(EXP_EXAMPLE_JSOG,
+ IdentifiableExampleJSOG.class);
+
+ assertEquals(66, result.foo);
+ assertSame(result, result.next);
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestConvertingSerializer357.java b/src/test/java/com/fasterxml/jackson/failing/TestConvertingSerializer357.java
index d72721e..46aec41 100644
--- a/src/test/java/com/fasterxml/jackson/failing/TestConvertingSerializer357.java
+++ b/src/test/java/com/fasterxml/jackson/failing/TestConvertingSerializer357.java
@@ -9,16 +9,16 @@
extends com.fasterxml.jackson.databind.BaseMapTest
{
// [Issue#357]
- static class A { }
+ static class Value { }
- static class B {
- @JsonSerialize(contentConverter = AToStringConverter.class)
- public List<A> list = Arrays.asList(new A());
+ static class ListWrapper {
+ @JsonSerialize(contentConverter = ValueToStringListConverter.class)
+ public List<Value> list = Arrays.asList(new Value());
}
- static class AToStringConverter extends StdConverter<A, List<String>> {
+ static class ValueToStringListConverter extends StdConverter<Value, List<String>> {
@Override
- public List<String> convert(A value) {
+ public List<String> convert(Value value) {
return Arrays.asList("Hello world!");
}
}
@@ -31,7 +31,7 @@
// [Issue#357]
public void testConverterForList357() throws Exception {
- String json = objectWriter().writeValueAsString(new B());
+ String json = objectWriter().writeValueAsString(new ListWrapper());
assertEquals("{\"list\":[[\"Hello world!\"]]}", json);
}
}
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestExternalTypeId.java b/src/test/java/com/fasterxml/jackson/failing/TestExternalTypeId222.java
similarity index 96%
rename from src/test/java/com/fasterxml/jackson/failing/TestExternalTypeId.java
rename to src/test/java/com/fasterxml/jackson/failing/TestExternalTypeId222.java
index ab68534..bf20e56 100644
--- a/src/test/java/com/fasterxml/jackson/failing/TestExternalTypeId.java
+++ b/src/test/java/com/fasterxml/jackson/failing/TestExternalTypeId222.java
@@ -6,7 +6,7 @@
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.databind.*;
-public class TestExternalTypeId extends BaseMapTest
+public class TestExternalTypeId222 extends BaseMapTest
{
@SuppressWarnings("unused")
public void testTypes() throws IOException {
@@ -69,7 +69,7 @@
public final int p;
@JsonCreator
- private Pojo(@JsonProperty("p") int p) {
+ Pojo(@JsonProperty("p") int p) {
this.p = p;
}
}
@@ -105,6 +105,4 @@
String json = mapper.writeValueAsString(input);
assertEquals("{\"value\":{\"x\":13},\"type\":\"foo\"}", json);
}
-
-
}
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestJavaType76.java b/src/test/java/com/fasterxml/jackson/failing/TestJavaType76.java
index 836882e..d37e0a0 100644
--- a/src/test/java/com/fasterxml/jackson/failing/TestJavaType76.java
+++ b/src/test/java/com/fasterxml/jackson/failing/TestJavaType76.java
@@ -2,14 +2,13 @@
import java.util.*;
-import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.type.TypeFactory;
/**
* Failing test related to [Issue#76]
*/
-public class TestJavaType76
- extends com.fasterxml.jackson.test.BaseTest
+public class TestJavaType76 extends BaseMapTest
{
@SuppressWarnings("serial")
static class HashTree<K, V> extends HashMap<K, HashTree<K, V>> { }
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestLocalType609.java b/src/test/java/com/fasterxml/jackson/failing/TestLocalType609.java
new file mode 100644
index 0000000..ca7f511
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/TestLocalType609.java
@@ -0,0 +1,36 @@
+package com.fasterxml.jackson.failing;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Failing test related to [databind#609]
+ */
+public class TestLocalType609 extends BaseMapTest
+{
+ static class EntityContainer {
+ RuleForm entity;
+
+ @SuppressWarnings("unchecked")
+ public <T extends RuleForm> T getEntity() { return (T) entity; }
+ public <T extends RuleForm> void setEntity(T e) { entity = e; }
+ }
+
+ static class RuleForm {
+ public int value;
+
+ public RuleForm() { }
+ public RuleForm(int v) { value = v; }
+ }
+
+ public void testLocalPartialType609() throws Exception {
+ ObjectMapper mapper = new ObjectMapper();
+
+ EntityContainer input = new EntityContainer();
+ input.entity = new RuleForm(12);
+ String json = mapper.writeValueAsString(input);
+
+ EntityContainer output = mapper.readValue(json, EntityContainer.class);
+ assertEquals(12, output.getEntity().value);
+ }
+}
+
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestMapJsonValueKey47.java b/src/test/java/com/fasterxml/jackson/failing/TestMapJsonValueKey47.java
index fe25643..210aba0 100644
--- a/src/test/java/com/fasterxml/jackson/failing/TestMapJsonValueKey47.java
+++ b/src/test/java/com/fasterxml/jackson/failing/TestMapJsonValueKey47.java
@@ -1,7 +1,6 @@
package com.fasterxml.jackson.failing;
import java.util.HashMap;
-import java.util.Map;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
@@ -31,10 +30,13 @@
}
}
+ @SuppressWarnings("serial")
+ static class WatMap extends HashMap<Wat,Boolean> { }
+
public void testMapJsonValueKey()
throws Exception
{
- Map<Wat, Boolean> input = new HashMap<Wat, Boolean>();
+ WatMap input = new WatMap();
input.put(new Wat("3"), true);
ObjectMapper mapper = new ObjectMapper();
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestMapSerialization588.java b/src/test/java/com/fasterxml/jackson/failing/TestMapSerialization588.java
new file mode 100644
index 0000000..19136e4
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/TestMapSerialization588.java
@@ -0,0 +1,40 @@
+package com.fasterxml.jackson.failing;
+
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.*;
+
+public class TestMapSerialization588 extends BaseMapTest
+{
+ static class NoEmptiesMapContainer {
+ @JsonInclude(value=JsonInclude.Include.NON_EMPTY,
+ content=JsonInclude.Include.NON_EMPTY)
+ public Map<String,String> stuff = new LinkedHashMap<String,String>();
+
+ public NoEmptiesMapContainer add(String key, String value) {
+ stuff.put(key, value);
+ return this;
+ }
+ }
+
+ /*
+ /**********************************************************
+ /* Test methods
+ /**********************************************************
+ */
+
+ final private ObjectMapper MAPPER = objectMapper();
+
+ // [databind#588]
+ public void testNonNullValueMapViaProp() throws IOException
+ {
+ String json = MAPPER.writeValueAsString(new NoEmptiesMapContainer()
+ .add("a", null)
+ .add("b", ""));
+ assertEquals(aposToQuotes("{}"), json);
+ }
+
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestMultipleExternalIds.java b/src/test/java/com/fasterxml/jackson/failing/TestMultipleExternalIds291.java
similarity index 96%
rename from src/test/java/com/fasterxml/jackson/failing/TestMultipleExternalIds.java
rename to src/test/java/com/fasterxml/jackson/failing/TestMultipleExternalIds291.java
index f89643a..7b942d8 100644
--- a/src/test/java/com/fasterxml/jackson/failing/TestMultipleExternalIds.java
+++ b/src/test/java/com/fasterxml/jackson/failing/TestMultipleExternalIds291.java
@@ -5,7 +5,7 @@
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
import com.fasterxml.jackson.databind.*;
-public class TestMultipleExternalIds extends BaseMapTest
+public class TestMultipleExternalIds291 extends BaseMapTest
{
// For [Issue#291]
interface F1 {}
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestObjectIdWithInjectables538.java b/src/test/java/com/fasterxml/jackson/failing/TestObjectIdWithInjectables538.java
new file mode 100644
index 0000000..d2a552d
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/TestObjectIdWithInjectables538.java
@@ -0,0 +1,57 @@
+package com.fasterxml.jackson.failing;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+public class TestObjectIdWithInjectables538 extends BaseMapTest
+{
+ @JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class)
+ public static class A {
+ public B b;
+
+ public A(@JacksonInject("i1") String injected) {
+ }
+ }
+
+ @JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class)
+ public static class B {
+ public A a;
+
+ public B(@JacksonInject("i2") String injected) {
+ }
+ }
+
+ /*
+ /*****************************************************
+ /* Test methods
+ /*****************************************************
+ */
+
+ private final ObjectMapper MAPPER = new ObjectMapper();
+
+ public void testWithInjectables538() throws Exception
+ {
+ A a = new A("a");
+ B b = new B("b");
+ a.b = b;
+ b.a = a;
+
+ String json = MAPPER.writeValueAsString(a);
+
+ InjectableValues.Std inject = new InjectableValues.Std();
+ inject.addValue("i1", "e1");
+ inject.addValue("i2", "e2");
+ A output = null;
+
+ try {
+ output = MAPPER.reader(inject).forType(A.class).readValue(json);
+ } catch (Exception e) {
+ throw new IllegalStateException("Failed to deserialize from JSON '"+json+"'", e);
+ }
+ assertNotNull(output);
+
+ assertNotNull(output.b);
+ }
+}
+
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestPOJOAsArray.java b/src/test/java/com/fasterxml/jackson/failing/TestPOJOAsArray.java
deleted file mode 100644
index 31fde8d..0000000
--- a/src/test/java/com/fasterxml/jackson/failing/TestPOJOAsArray.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.fasterxml.jackson.failing;
-
-import com.fasterxml.jackson.annotation.JsonFormat;
-import com.fasterxml.jackson.annotation.JsonFormat.Shape;
-
-import com.fasterxml.jackson.databind.BaseMapTest;
-import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-public class TestPOJOAsArray extends BaseMapTest
-{
- // for [JACKSON-805]
- @JsonFormat(shape=Shape.ARRAY)
- static class SingleBean {
- public String name = "foo";
- }
-
-
- // for [JACKSON-805]
- public void testBeanAsArrayUnwrapped() throws Exception
- {
- ObjectMapper mapper = new ObjectMapper();
- mapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
- SingleBean result = mapper.readValue(quote("foobar"), SingleBean.class);
- assertNotNull(result);
- assertEquals("foobar", result.name);
- }
-
-}
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestPolymorphicDeserialization.java b/src/test/java/com/fasterxml/jackson/failing/TestPolymorphicDeserialization.java
index 77081ef..f37b6ad 100644
--- a/src/test/java/com/fasterxml/jackson/failing/TestPolymorphicDeserialization.java
+++ b/src/test/java/com/fasterxml/jackson/failing/TestPolymorphicDeserialization.java
@@ -15,7 +15,7 @@
* deserializer comes to different conclusion (using default implementation class),
* resulting in a <code>ClassCastException</code>.
* Whether this is wrong, and if so, can we fix it, is unknown at this point
- * (2.3.0-SNAPSHOT): quite possibly this can not be changed.
+ * (2.3): quite possibly this can not be changed.
*/
public class TestPolymorphicDeserialization extends BaseMapTest
{
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestScalaLikeImplicitProperties.java b/src/test/java/com/fasterxml/jackson/failing/TestScalaLikeImplicitProperties.java
index cd3ea37..a0cf4c2 100644
--- a/src/test/java/com/fasterxml/jackson/failing/TestScalaLikeImplicitProperties.java
+++ b/src/test/java/com/fasterxml/jackson/failing/TestScalaLikeImplicitProperties.java
@@ -31,19 +31,38 @@
String name = null;
if (member instanceof AnnotatedField) {
name = member.getName();
- }
- if (name != null) {
- if (name.endsWith("‿")) {
+ if (name.endsWith("‿")) {
return name.substring(0, name.length()-1);
}
+ } else if (member instanceof AnnotatedMethod) {
+ name = member.getName();
+ if (name.endsWith("_⁀")) {
+ return name.substring(0, name.length()-2);
+ }
+ if (!name.startsWith("get") && !name.startsWith("set")) {
+ return name;
+ }
+ } else if (member instanceof AnnotatedParameter) {
+ // A placeholder for legitimate property name detection
+ // such as what the JDK8 module provides
+ return "prop";
}
return null;
}
+
+ @Override
+ public boolean hasCreatorAnnotation(Annotated a) {
+ // A placeholder for legitmate creator detection.
+ // In Scala, all primary constructors should be creators,
+ // but I can't obtain a reference to the AnnotatedClass from the
+ // AnnotatedConstructor, so it's simulated here.
+ return (a instanceof AnnotatedConstructor);
+ }
}
static class ValProperty
{
- public final String prop‿;
+ private final String prop‿;
public String prop() { return prop‿; }
public ValProperty(String prop) {
@@ -54,7 +73,7 @@
static class ValWithBeanProperty
{
- public final String prop‿;
+ private final String prop‿;
public String prop() { return prop‿; }
public String getProp() { return prop‿; }
@@ -66,7 +85,7 @@
static class VarProperty
{
- public String prop‿;
+ private String prop‿;
public String prop() { return prop‿; }
public void prop_⁀(String p) { prop‿ = p; }
@@ -78,7 +97,7 @@
static class VarWithBeanProperty
{
- public String prop‿;
+ private String prop‿;
public String prop() { return prop‿; }
public void prop_⁀(String p) { prop‿ = p; }
public String getProp() { return prop‿; }
@@ -105,8 +124,6 @@
{
ObjectMapper m = manglingMapper();
- // TODO: Activate whatever handler implements the property detection style
-
assertEquals("{\"prop\":\"val\"}", m.writeValueAsString(new ValProperty("val")));
}
@@ -115,8 +132,6 @@
{
ObjectMapper m = manglingMapper();
- // TODO: Activate whatever handler implements the property detection style
-
assertEquals("{\"prop\":\"val\"}", m.writeValueAsString(new ValWithBeanProperty("val")));
}
@@ -125,8 +140,6 @@
{
ObjectMapper m = manglingMapper();
- // TODO: Activate whatever handler implements the property detection style
-
assertEquals("{\"prop\":\"var\"}", m.writeValueAsString(new VarProperty("var")));
VarProperty result = m.readValue("{\"prop\":\"read\"}", VarProperty.class);
assertEquals("read", result.prop());
@@ -137,8 +150,6 @@
{
ObjectMapper m = manglingMapper();
- // TODO: Activate whatever handler implements the property detection style
-
assertEquals("{\"prop\":\"var\"}", m.writeValueAsString(new VarWithBeanProperty("var")));
VarWithBeanProperty result = m.readValue("{\"prop\":\"read\"}", VarWithBeanProperty.class);
assertEquals("read", result.prop());
@@ -149,8 +160,6 @@
{
ObjectMapper m = manglingMapper();
- // TODO: Activate whatever handler implements the property detection style
-
assertEquals("{\"prop\":\"get/set\"}", m.writeValueAsString(new GetterSetterProperty()));
GetterSetterProperty result = m.readValue("{\"prop\":\"read\"}", GetterSetterProperty.class);
assertEquals("read", result.prop());
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestSetterlessProperties501.java b/src/test/java/com/fasterxml/jackson/failing/TestSetterlessProperties501.java
index 2d822a6..8363e75 100644
--- a/src/test/java/com/fasterxml/jackson/failing/TestSetterlessProperties501.java
+++ b/src/test/java/com/fasterxml/jackson/failing/TestSetterlessProperties501.java
@@ -6,12 +6,6 @@
import com.fasterxml.jackson.databind.*;
-/**
- * Unit tests for verifying that feature requested
- * via [JACKSON-88] ("setterless collections") work as
- * expected, similar to how Collections and Maps work
- * with JAXB.
- */
public class TestSetterlessProperties501
extends BaseMapTest
{
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestTypeWithJsonValue466.java b/src/test/java/com/fasterxml/jackson/failing/TestTypeWithJsonValue466.java
index 3cda760..fa90f2a 100644
--- a/src/test/java/com/fasterxml/jackson/failing/TestTypeWithJsonValue466.java
+++ b/src/test/java/com/fasterxml/jackson/failing/TestTypeWithJsonValue466.java
@@ -1,6 +1,7 @@
package com.fasterxml.jackson.failing;
import java.io.*;
+import java.math.BigDecimal;
import java.util.*;
import com.fasterxml.jackson.annotation.*;
@@ -12,17 +13,17 @@
// The following is required for the testDecimalMetadata test case. That case fails.
@JsonTypeName(value = "decimalValue")
public static class DecimalValue {
- private java.math.BigDecimal value;
- public DecimalValue(){ this.value = java.math.BigDecimal.valueOf( 1234.4321 ); }
+ private BigDecimal value;
+ public DecimalValue() { value = new BigDecimal("111.1"); }
@JsonValue
- public java.math.BigDecimal getValue(){ return value; }
+ public BigDecimal getValue(){ return value; }
}
@JsonPropertyOrder({"key","value"})
public static class DecimalEntry {
- public DecimalEntry(){}
- public String getKey(){ return "num"; }
+ public DecimalEntry() {}
+ public String getKey() { return "num"; }
@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.EXTERNAL_PROPERTY)
public DecimalValue getValue(){
@@ -41,10 +42,10 @@
@JsonTypeName(value = "doubleValue")
public static class DoubleValue {
private Double value;
- public DoubleValue(){ this.value = 1234.4321; }
+ public DoubleValue() { value = 1234.25; }
@JsonValue
- public Double getValue(){ return value; }
+ public Double getValue() { return value; }
}
@JsonPropertyOrder({"key","value"})
@@ -67,14 +68,14 @@
public void testDoubleMetadata() throws IOException {
DoubleMetadata doub = new DoubleMetadata();
- String expected = "{\"metadata\":[{\"key\":\"num\",\"value\":1234.4321,\"@type\":\"doubleValue\"}]}";
+ String expected = "{\"metadata\":[{\"key\":\"num\",\"value\":1234.25,\"@type\":\"doubleValue\"}]}";
String json = MAPPER.writeValueAsString(doub);
assertEquals("Serialized json not equivalent", expected, json);
}
public void testDecimalMetadata() throws IOException{
DecimalMetadata dec = new DecimalMetadata();
- String expected = "{\"metadata\":[{\"key\":\"num\",\"value\":1234.4321,\"@type\":\"decimalValue\"}]}";
+ String expected = "{\"metadata\":[{\"key\":\"num\",\"value\":111.1,\"@type\":\"decimalValue\"}]}";
String json = MAPPER.writeValueAsString(dec);
assertEquals("Serialized json not equivalent", expected, json);
}
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestUnknownProperty426.java b/src/test/java/com/fasterxml/jackson/failing/TestUnknownProperty426.java
index 53468d7..d9083ff 100644
--- a/src/test/java/com/fasterxml/jackson/failing/TestUnknownProperty426.java
+++ b/src/test/java/com/fasterxml/jackson/failing/TestUnknownProperty426.java
@@ -10,8 +10,8 @@
public class TestUnknownProperty426 extends BaseMapTest
{
// For [Issue#426]
- @JsonIgnoreProperties({ "userId"})
- public class User {
+ @JsonIgnoreProperties({ "userId" })
+ static class User {
Integer userId;
void setUserId(String id) {
@@ -37,8 +37,8 @@
public void testIssue426() throws Exception
{
- String jsonString = "{id: 9, firstName: \"Mike\" }";
- User result = MAPPER.reader( User.class ).readValue(jsonString);
+ final String JSON = aposToQuotes("{'id': 9, 'firstName': 'Mike' }");
+ User result = MAPPER.reader(User.class).readValue(JSON);
assertNotNull(result);
}
}
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestUnwrappedWithCreator.java b/src/test/java/com/fasterxml/jackson/failing/TestUnwrappedWithCreator.java
index be93b16..1392992 100644
--- a/src/test/java/com/fasterxml/jackson/failing/TestUnwrappedWithCreator.java
+++ b/src/test/java/com/fasterxml/jackson/failing/TestUnwrappedWithCreator.java
@@ -7,13 +7,12 @@
public class TestUnwrappedWithCreator extends BaseMapTest
{
static class JAddress {
- private String address;
- private String city;
- private String state;
+ protected String address;
+ protected String city;
+ protected String state;
@JsonCreator
- public JAddress(
- @JsonProperty("address") String address,
+ public JAddress( @JsonProperty("address") String address,
@JsonProperty("city") String city,
@JsonProperty("state") String state
){
@@ -28,16 +27,14 @@
}
static class JPerson {
- private String _name;
- private JAddress _address;
- private String _alias;
+ protected String _name;
+ protected JAddress _address;
+ protected String _alias;
@JsonCreator
- public JPerson(
- @JsonProperty("name") String name,
+ public JPerson(@JsonProperty("name") String name,
@JsonUnwrapped JAddress address,
- @JsonProperty("alias") String alias
- ) {
+ @JsonProperty("alias") String alias) {
_name = name;
_address = address;
_alias = alias;
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestVisibleExternalId.java b/src/test/java/com/fasterxml/jackson/failing/TestVisibleExternalId.java
deleted file mode 100644
index 1e00054..0000000
--- a/src/test/java/com/fasterxml/jackson/failing/TestVisibleExternalId.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.fasterxml.jackson.failing;
-
-import com.fasterxml.jackson.annotation.*;
-import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
-import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
-
-import com.fasterxml.jackson.databind.BaseMapTest;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-public class TestVisibleExternalId extends BaseMapTest
-{
- // [Issue#408]
- static class ExternalBeanWithId
- {
- @JsonTypeInfo(use=Id.NAME, include=As.EXTERNAL_PROPERTY, property="type", visible=true)
- public ValueBean bean;
-
- public ExternalBeanWithId() { }
- public ExternalBeanWithId(int v) {
- bean = new ValueBean(v);
- }
- }
-
- @JsonTypeName("vbean")
- static class ValueBean {
- public int value;
-
- public ValueBean() { }
- public ValueBean(int v) { value = v; }
- }
-
- private final ObjectMapper MAPPER = objectMapper();
-
- // [Issue#408]
- public void testVisibleTypeId() throws Exception
- {
- String json = MAPPER.writeValueAsString(new ExternalBeanWithId(3));
- ExternalBeanWithId result = MAPPER.readValue(json, ExternalBeanWithId.class);
- assertNotNull(result);
- assertNotNull(result.bean);
- assertEquals(3, result.bean.value);
- }
-}
diff --git a/src/test/java/perf/ObjectReaderTestBase.java b/src/test/java/perf/ObjectReaderTestBase.java
index 7a1def3..d973b2e 100644
--- a/src/test/java/perf/ObjectReaderTestBase.java
+++ b/src/test/java/perf/ObjectReaderTestBase.java
@@ -70,9 +70,9 @@
System.out.print("Warming up");
final ObjectReader jsonReader = mapper1.reader()
- .withType(inputClass1);
+ .forType(inputClass1);
final ObjectReader arrayReader = mapper2.reader()
- .withType(inputClass2);
+ .forType(inputClass2);
int i = 0;
final int TYPES = 2;
@@ -112,10 +112,10 @@
final ObjectReader jsonReader = mapper1.reader()
.with(DeserializationFeature.EAGER_DESERIALIZER_FETCH)
- .withType(inputClass1);
+ .forType(inputClass1);
final ObjectReader arrayReader = mapper2.reader()
.with(DeserializationFeature.EAGER_DESERIALIZER_FETCH)
- .withType(inputClass2);
+ .forType(inputClass2);
int i = 0;
final int TYPES = 2;
diff --git a/src/test/java/perf/ObjectWriterTestBase.java b/src/test/java/perf/ObjectWriterTestBase.java
index f05a7ba..c7e0878 100644
--- a/src/test/java/perf/ObjectWriterTestBase.java
+++ b/src/test/java/perf/ObjectWriterTestBase.java
@@ -27,8 +27,8 @@
}
final ObjectWriter writer0 = mapper.writer().with(SerializationFeature.EAGER_SERIALIZER_FETCH);
- final ObjectWriter writer1 = writer0.withType(inputClass1);
- final ObjectWriter writer2 = writer0.withType(inputClass2);
+ final ObjectWriter writer1 = writer0.forType(inputClass1);
+ final ObjectWriter writer2 = writer0.forType(inputClass2);
int i = 0;
int roundsDone = 0;