Merge branch '2.4'
Conflicts:
pom.xml
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..35e439f 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
@@ -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 0aa11a1..7533273 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.4-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.4-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 c161eee..a52f66b 100644
--- a/release-notes/CREDITS
+++ b/release-notes/CREDITS
@@ -146,8 +146,20 @@
Ian Barfield: (tea-dragon@github)
* Reported #580: delegate deserializers choke on a (single) abstract/polymorphic parameter
- (2.4.4)
+ (2.4.4)
Eugene Lukash
* Reported #592: Wrong `TokenBuffer` delegate deserialization using `@JsonCreator`
- (2.4.4)
+ (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)
diff --git a/release-notes/VERSION b/release-notes/VERSION
index 0cdb8fc..b7f0526 100644
--- a/release-notes/VERSION
+++ b/release-notes/VERSION
@@ -4,6 +4,41 @@
=== 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)
+#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)
+- 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.4 (not yet released)
(jackson-core)#158: Setter confusion on assignable types
@@ -37,10 +72,12 @@
- Fixed a problem with `acceptJsonFormatVisitor` with Collection/array types that
are marked with `@JsonValue`; could cause NPE in JSON Schema generator module.
+<<<<<<< HEAD
+2.4.2 (14-Aug-2014)
+=======
2.4.2 (13-Aug-2014)
+>>>>>>> 2.4
-#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].
@@ -58,6 +95,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..b793afb 100644
--- a/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java
+++ b/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java
@@ -5,10 +5,8 @@
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 +569,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 +584,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 +1019,47 @@
public boolean hasCreatorAnnotation(Annotated a) {
return false;
}
+
+ /*
+ /**********************************************************
+ /* 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..27998f0 100644
--- a/src/main/java/com/fasterxml/jackson/databind/BeanDescription.java
+++ b/src/main/java/com/fasterxml/jackson/databind/BeanDescription.java
@@ -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/DeserializationFeature.java b/src/main/java/com/fasterxml/jackson/databind/DeserializationFeature.java
index 0808671..d36a890 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.
diff --git a/src/main/java/com/fasterxml/jackson/databind/JavaType.java b/src/main/java/com/fasterxml/jackson/databind/JavaType.java
index 3b93479..110d3c3 100644
--- a/src/main/java/com/fasterxml/jackson/databind/JavaType.java
+++ b/src/main/java/com/fasterxml/jackson/databind/JavaType.java
@@ -3,6 +3,7 @@
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
@@ -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/MapperFeature.java b/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java
index 7753c88..6a19b10 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),
diff --git a/src/main/java/com/fasterxml/jackson/databind/MappingIterator.java b/src/main/java/com/fasterxml/jackson/databind/MappingIterator.java
index 04e8077..ff3f394 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 _handleMappingException(e);
} catch (IOException e) {
- throw new RuntimeException(e.getMessage(), e);
+ return _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/ObjectMapper.java b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java
index 76e8d62..51e6af0 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()
@@ -339,7 +336,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 +371,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 +380,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 +443,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 +465,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 +495,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 +510,60 @@
+" (version: "+version()+") does not override copy(); it has to");
}
}
+
+ /**
+ * 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 +578,7 @@
public Version version() {
return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION;
}
-
+
/*
/**********************************************************
/* Module registration, discovery
@@ -874,7 +938,7 @@
/* Configuration: mix-in annotations
/**********************************************************
*/
-
+
/**
* Method to use for defining mix-in annotations to use for augmenting
* annotations that processable (serializable / deserializable)
@@ -886,8 +950,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 +961,7 @@
_mixInAnnotations.put(new ClassKey(en.getKey()), en.getValue());
}
}
+ return this;
}
/**
@@ -906,35 +973,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 +1122,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 +1167,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);
@@ -1353,6 +1443,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.
@@ -1968,8 +2066,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 +2148,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 +2173,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 +2486,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 +2496,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 +2506,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,16 +2517,18 @@
* 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));
}
/**
@@ -2427,8 +2537,9 @@
* runtime type of value. Type must be a super-type of runtime
* type.
*/
- public ObjectWriter writerWithType(Class<?> rootType) {
- return new ObjectWriter(this, getSerializationConfig(),
+ @SuppressWarnings("unchecked")
+ 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);
@@ -2439,8 +2550,9 @@
* serialize objects using specified root type, instead of actual
* runtime type of value. Type must be a super-type of runtime type.
*/
- public ObjectWriter writerWithType(TypeReference<?> rootType) {
- return new ObjectWriter(this, getSerializationConfig(),
+ @SuppressWarnings("unchecked")
+ 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);
@@ -2451,8 +2563,9 @@
* serialize objects using specified root type, instead of actual
* runtime type of value. Type must be a super-type of runtime type.
*/
- public ObjectWriter writerWithType(JavaType rootType) {
- return new ObjectWriter(this, getSerializationConfig(), rootType, /*PrettyPrinter*/null);
+ @SuppressWarnings("unchecked")
+ public <W extends ObjectWriter> W writerWithType(JavaType rootType) {
+ return (W) _newWriter(getSerializationConfig(), rootType, /*PrettyPrinter*/null);
}
/**
@@ -2460,19 +2573,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 +2595,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 +2607,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 +2619,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 +2630,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 +2641,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 +2658,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 +2670,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 +2682,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 +2698,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 +2709,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 +2719,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 +2751,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 +2764,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 +2774,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 +2785,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 +2796,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
@@ -2991,7 +3119,7 @@
}
}
}
-
+
/*
/**********************************************************
/* Internal methods for deserialization, overridable
diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java
index a73d4b7..024a6cd 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java
@@ -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,6 +265,104 @@
/*
/**********************************************************
+ /* Methods sub-classes MUST override, used for constructing
+ /* reader instances, (re)configuring parser instances
+ /**********************************************************
+ */
+
+ /**
+ * 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);
+ }
+ /* 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);
+ }
+ }
+
+ /*
+ /**********************************************************
/* Life-cycle, fluent factory methods
/**********************************************************
*/
@@ -280,7 +377,7 @@
*/
public ObjectReader with(DeserializationFeature feature) {
return _with(_config.with(feature));
- }
+ }
/**
* Method for constructing a new reader instance that is configured
@@ -299,22 +396,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));
}
@@ -338,7 +434,7 @@
if (_injectableValues == injectableValues) {
return this;
}
- return new ObjectReader(this, _config,
+ return _new(this, _config,
_valueType, _rootDeserializer, _valueToUpdate,
_schema, injectableValues, _dataFormatReaders);
}
@@ -370,7 +466,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 +501,7 @@
return this;
}
_verifySchemaType(schema);
- return new ObjectReader(this, _config, _valueType, _rootDeserializer, _valueToUpdate,
+ return _new(this, _config, _valueType, _rootDeserializer, _valueToUpdate,
schema, _injectableValues, _dataFormatReaders);
}
@@ -427,7 +523,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);
}
@@ -489,7 +585,7 @@
} else {
t = _valueType;
}
- return new ObjectReader(this, _config, t, _rootDeserializer, value,
+ return _new(this, _config, t, _rootDeserializer, value,
_schema, _injectableValues, _dataFormatReaders);
}
@@ -542,8 +638,7 @@
*
* @since 2.1
*/
- public ObjectReader withFormatDetection(ObjectReader... readers)
- {
+ public ObjectReader withFormatDetection(ObjectReader... readers) {
return withFormatDetection(new DataFormatReaders(readers));
}
@@ -562,9 +657,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 +666,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 that sub-classes MUST 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 +925,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 +1155,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 +1199,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 +1219,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 +1282,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 +1294,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 +1307,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 +1342,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 +1378,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 +1408,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 +1499,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 +1522,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 +1626,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/SerializerProvider.java b/src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java
index c433836..b820b5d 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
@@ -459,8 +456,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 +470,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 +500,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 +519,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 +819,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 +858,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 +1072,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/deser/AbstractDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/AbstractDeserializer.java
index b31ccb1..555b3ad 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/AbstractDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/AbstractDeserializer.java
@@ -65,8 +65,7 @@
*
* @since 2.3
*/
- public static AbstractDeserializer constructForNonPOJO(BeanDescription beanDesc)
- {
+ public static AbstractDeserializer constructForNonPOJO(BeanDescription beanDesc) {
return new AbstractDeserializer(beanDesc);
}
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..efbb895 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java
@@ -3,6 +3,7 @@
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicReference;
import com.fasterxml.jackson.core.JsonLocation;
import com.fasterxml.jackson.databind.*;
@@ -10,6 +11,7 @@
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 +41,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
@@ -316,17 +319,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,104 +404,108 @@
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 propDef = (propDefs == null) ? null : propDefs[0];
+ boolean hasExplicitName = (propDef != null) && propDef.isExplicitlyNamed();
+ Object injectId = intr.findInjectableValueId(ctor.getParameter(0));
+
+ if (hasExplicitName || (injectId != null)) {
+ CreatorProperty[] properties = new CreatorProperty[1];
+ PropertyName name = (propDef == null) ? null : propDef.getFullName();
+ properties[0] = constructCreatorProperty(ctxt, beanDesc, name, 0, ctor.getParameter(0), injectId);
+ creators.addPropertyCreator(ctor, properties);
+ } else {
+ /*boolean added = */ _handleSingleArgumentConstructor(ctxt, beanDesc, vchecker, intr, creators,
+ ctor, isCreator,
+ vchecker.isCreatorVisible(ctor));
+ }
+ // 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");
}
}
}
@@ -469,25 +514,9 @@
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 +557,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 +583,87 @@
}
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 propDef = (propDefs == null) ? null : propDefs[0];
+ boolean hasExplicitName = (propDef != null) && propDef.isExplicitlyNamed();
+ final Object injectId = intr.findInjectableValueId(factory.getParameter(0));
+ if ((injectId == null) && !hasExplicitName) { // not property based
+ /*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 +712,7 @@
}
return true;
}
- if (intr.hasCreatorAnnotation(factory)) {
+ if (isCreator) {
creators.addDelegatingCreator(factory, null);
return true;
}
@@ -708,7 +774,7 @@
}
return prop;
}
-
+
protected PropertyName _findParamName(AnnotatedParameter param, AnnotationIntrospector intr)
{
if (param != null && intr != null) {
@@ -726,6 +792,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 +835,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 +864,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 +959,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 +990,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 +1118,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 +1167,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 +1189,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 +1229,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 +1411,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 +1451,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..555134b 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java
@@ -326,7 +326,7 @@
@SuppressWarnings("resource")
protected Object _deserializeUsingPropertyBased(final JsonParser jp, final DeserializationContext ctxt)
throws IOException, JsonProcessingException
- {
+ {
final PropertyBasedCreator creator = _propertyBasedCreator;
PropertyValueBuffer buffer = creator.startBuilding(jp, ctxt, _objectIdReader);
@@ -391,7 +391,7 @@
unknown.writeFieldName(propName);
unknown.copyCurrentStructure(jp);
}
-
+
// We hit END_OBJECT, so:
Object bean;
try {
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..62cc3e1 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);
}
@@ -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..2c20c1e 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.
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/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/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/ContainerDeserializerBase.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/ContainerDeserializerBase.java
index 063cb22..bf2ae16 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..396b862 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) {
@@ -96,38 +92,52 @@
*/
@Override
public boolean isCachable() { return true; }
-
+
+ /*
+ /**********************************************************
+ /* 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 +149,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 +168,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/MapDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/MapDeserializer.java
index 7c719f0..1af0083 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
@@ -304,7 +303,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) {
@@ -538,24 +538,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)
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..d524529
--- /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 #resolve} 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/ObjectArrayDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/ObjectArrayDeserializer.java
index 45174c6..49d447e 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
@@ -157,7 +157,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/StdDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java
index e4eeddc..3f9fa66 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
@@ -800,6 +800,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..c73a196 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
@@ -162,9 +162,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);
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/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/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..98b0ecb 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair.java
@@ -315,16 +315,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 +332,7 @@
@Override
public Class<?> findSerializationType(Annotated a) {
- Class<?> r = _primary.findSerializationType(a);
+ Class<?> r = _primary.findSerializationType(a);
return (r == null) ? _secondary.findSerializationType(a) : r;
}
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..de54967 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/BasicBeanDescription.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/BasicBeanDescription.java
@@ -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..62f6167 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,9 +721,9 @@
* 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);
}
-
+
/*
/**********************************************************
/* Helper methods
@@ -725,7 +732,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 +752,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 +781,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/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..1beecfc 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()
@@ -981,6 +977,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
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/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/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/node/ArrayNode.java b/src/main/java/com/fasterxml/jackson/databind/node/ArrayNode.java
index 9704471..a862c70 100644
--- a/src/main/java/com/fasterxml/jackson/databind/node/ArrayNode.java
+++ b/src/main/java/com/fasterxml/jackson/databind/node/ArrayNode.java
@@ -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/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 2a710f1..16fac48 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,
@@ -368,6 +367,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 +439,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;
@@ -719,9 +734,15 @@
elementTypeSerializer, elementValueSerializer);
} 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
@@ -733,6 +754,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
@@ -789,39 +834,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..71dd6e0 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());
+ }
}
}
}
@@ -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/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 6e465b9..53cd683 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.
@@ -30,12 +32,12 @@
implements ContextualSerializer
{
protected final static JavaType UNSPECIFIED_TYPE = TypeFactory.unknownType();
-
+
/**
* Map-valued property being serialized with this instance
*/
protected final BeanProperty _property;
-
+
/**
* Set of entries to omit during serialization, if any
*/
@@ -92,6 +94,15 @@
* @since 2.4
*/
protected final boolean _sortKeys;
+
+ /**
+ * Value that indicates suppression mechanism to use; either one of
+ * values of {@link JsonInclude.Include}, or actual object to compare
+ * against ("default value")
+ *
+ * @since 2.5
+ */
+ protected final Object _suppressableValue;
/*
/**********************************************************
@@ -99,6 +110,9 @@
/**********************************************************
*/
+ /**
+ * @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/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..9393b49 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
@@ -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..f013b3f 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
+ * @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
+ * @parameterClasses 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..3623d9e 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
{
@@ -238,7 +235,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/databind/TestObjectMapper.java b/src/test/java/com/fasterxml/jackson/databind/TestObjectMapper.java
index 6a6ab67..6fb488c 100644
--- a/src/test/java/com/fasterxml/jackson/databind/TestObjectMapper.java
+++ b/src/test/java/com/fasterxml/jackson/databind/TestObjectMapper.java
@@ -152,7 +152,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..844420f 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
diff --git a/src/test/java/com/fasterxml/jackson/databind/TestParserUsingMapper.java b/src/test/java/com/fasterxml/jackson/databind/TestParserUsingMapper.java
index 8f8ebf3..c0db914 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;
diff --git a/src/test/java/com/fasterxml/jackson/databind/TestReadValues.java b/src/test/java/com/fasterxml/jackson/databind/TestReadValues.java
index 54da2b2..643a66e 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
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/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/creators/SingleArgCreatorTest.java b/src/test/java/com/fasterxml/jackson/databind/creators/SingleArgCreatorTest.java
index 0e4ce69..8a39da0 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/SingleArgCreatorTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/creators/SingleArgCreatorTest.java
@@ -1,8 +1,10 @@
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
{
@@ -20,6 +22,35 @@
public String getSs() { return _ss; }
}
+ // [Databind#557]
+
+ static class StringyBean
+ {
+ public final String value;
+
+ private StringyBean(String value) { this.value = value; }
+
+ public String getValue() {
+ return value;
+ }
+ }
+
+ @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 "value";
+ }
+ return "param"+ap.getIndex();
+ }
+ return super.findImplicitPropertyName(param);
+ }
+ }
+
/*
/**********************************************************
/* Test methods
@@ -34,4 +65,13 @@
SingleNamedStringBean.class);
assertEquals("foobar", bean._ss);
}
+
+ public void testSingleStringArgWithImplicitName() throws Exception
+ {
+ final ObjectMapper mapper = new ObjectMapper();
+ mapper.setAnnotationIntrospector(new MyParamIntrospector());
+ StringyBean bean = mapper.readValue(quote("foobar"), StringyBean.class);
+ assertEquals("foobar", bean.getValue());
+ }
}
+
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/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/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/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/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/TestEnumDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestEnumDeserialization.java
index 5c39333..3ac2752 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
@@ -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/TestMapDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestMapDeserialization.java
index 13a9fd1..adf9296 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;
}
@@ -83,8 +76,6 @@
public static enum ConcreteType implements ITestType {
ONE, TWO;
}
-
-
/*
/**********************************************************
@@ -302,6 +293,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.withType(new TypeReference<EnumMap<Key,String>>() { })
+ .readValue(JSON);
+ assertNull(result2);
+ }
+
/*
/**********************************************************
/* Test methods, maps with enums
@@ -468,7 +484,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 +493,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/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/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/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..d14dae7 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 {
+ private 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/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..a2d9f32 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>>();
@@ -69,9 +60,6 @@
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));
JSONResponse<List<Parent>> out = mapper.readValue(json, 0, json.length, rootType);
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..5c3cfc1 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedContainerSerialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedContainerSerialization.java
@@ -125,7 +125,7 @@
{
ArrayList<Animal> animals = new ArrayList<Animal>();
animals.add(new Dog("Spot"));
- JavaType rootType = TypeFactory.defaultInstance().constructParametricType(Iterator.class, Animal.class);
+ JavaType rootType = TypeFactory.defaultInstance().constructParametrizedType(Iterator.class, Iterator.class, Animal.class);
String json = mapper.writerWithType(rootType).writeValueAsString(animals.iterator());
if (json.indexOf("\"object-type\":\"doggy\"") < 0) {
fail("No polymorphic type retained, should be; JSON = '"+json+"'");
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..e6c5256 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
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..8c5aab9 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestWithGenerics.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestWithGenerics.java
@@ -157,7 +157,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.writerWithType(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/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..bee4813 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);
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 07c9af3..ff657db 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;
+ }
/*
/**********************************************************
@@ -203,7 +214,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));
}
@@ -273,15 +284,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]
@@ -294,6 +301,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..0ebb608 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestExceptionHandling.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestExceptionHandling.java
@@ -6,7 +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;
/**
@@ -15,7 +14,7 @@
* with Object serialization.
*/
public class TestExceptionHandling
- extends BaseTest
+ extends BaseMapTest
{
/*
/**********************************************************
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..7cee0de 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,13 +64,66 @@
}
}
+ // [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
{
@@ -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/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/struct/TestObjectIdDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/struct/TestObjectIdDeserialization.java
index 047a80b..37f18a3 100644
--- a/src/test/java/com/fasterxml/jackson/databind/struct/TestObjectIdDeserialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestObjectIdDeserialization.java
@@ -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
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/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/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 97%
rename from src/test/java/com/fasterxml/jackson/failing/TestExternalTypeId.java
rename to src/test/java/com/fasterxml/jackson/failing/TestExternalTypeId222.java
index ab68534..8f1649c 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 {
@@ -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..74bb097 100644
--- a/src/test/java/com/fasterxml/jackson/failing/TestJavaType76.java
+++ b/src/test/java/com/fasterxml/jackson/failing/TestJavaType76.java
@@ -2,14 +2,14 @@
import java.util.*;
+import com.fasterxml.jackson.databind.BaseMapTest;
import com.fasterxml.jackson.databind.JavaType;
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/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..8c04b57
--- /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).withType(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/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);
- }
-}