diff --git a/.gitattributes b/.gitattributes index 780a8a8fe1..055e55b98c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,3 +5,9 @@ # (and if ever need to disable change `true` to `false` pom.xml merge=ours +.travis.yml merge=ours + +# 28-Sep-2017, tatu: For 2.x -> 3.0 also block these + +release-notes/VERSION merge=ours +release-notes/CREDITS merge=ours diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 84517993c9..e976191f54 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,7 +1,17 @@ -We appreciate issues as very valuable contributions, but just to make sure here are things that are important to do before filing an issue: +We appreciate issues as very valuable contributions, but just to make sure here are things that are important to do before filing an issue. -* Only report issues (and perhaps request new features, FEATURE):,Usage Questions should be asked on [Jackson-users](https://groups.google.com/forum/#!search/jackson-users) list -- you are more likely to get help that way (and we will promptly close questions-as-issues) -* Check to see if this issue has already been reported (quick glance at existing issues): no deep search necessary, just quick sanity check +First, and foremost: THIS IS NOT A GENERAL-PURPOSES SUPPORT FORUM FOR ASKING QUESTIONS. +Such a place exixsts at: + + https://groups.google.com/forum/#!forum/jackson-user + +so please go there for anything other than bug reports and feature/improvement suggestions. +You are more likely to get help that way (and we will promptly close questions-as-issues) + +Also, we would appreciate if you: + +* Check to see if this issue has already been reported (quick glance at existing issues) + * no deep search necessary, just quick sanity check * Include version information for Jackson version you use * (optional but highly recommended) Verify that the problem occurs with the latest patch of same minor version; and even better, if possible, try using the latest stable patch version * For example: if you observe an issue with version `2.4.1`, first upgrade to `2.4.6` to ensure problem has not already been fixed. diff --git a/.travis.yml b/.travis.yml index f71ad3886c..76cfc20a13 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,9 @@ language: java -# Since Jackson 2.7, build requires jdk7, although module itself works on jdk6 still (for now) +# Jackson 3.x only compiles on Java 8 and above so... jdk: - - openjdk7 - openjdk8 + - oraclejdk9 # Below this line is configuration for deploying to the Sonatype OSS repo # http://blog.xeiam.com/2013/05/configure-travis-ci-to-deploy-snapshots.html @@ -16,7 +16,7 @@ after_success: branches: only: - master - - "2.10" + - "3.0" env: global: diff --git a/README.md b/README.md index 7076dd2d80..8a0b0cc91a 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # Overview This project contains the general-purpose data-binding functionality -and tree-model for [Jackson Data Processor](http://wiki.fasterxml.com/JacksonHome). -It builds on [core streaming parser/generator](../../../jackson-core) package, +and tree-model for [Jackson Data Processor](../../../jackson). +It builds on [Streaming API](../../../jackson-core) (stream parser/generator) package, and uses [Jackson Annotations](../../../jackson-annotations) for configuration. Project is licensed under [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0). @@ -10,7 +10,7 @@ While the original use case for Jackson was JSON data-binding, it can now be use Naming of classes uses word 'JSON' in many places even though there is no actual hard dependency to JSON format. [![Build Status](https://travis-ci.org/FasterXML/jackson-databind.svg?branch=master)](https://travis-ci.org/FasterXML/jackson-databind) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.fasterxml.jackson.core/jackson-databind/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.fasterxml.jackson.core/jackson-databind) -[![Javadoc](https://javadoc-emblem.rhcloud.com/doc/com.fasterxml.jackson.core/jackson-databind/badge.svg)](http://www.javadoc.io/doc/com.fasterxml.jackson.core/jackson-databind) +[![Javadoc](https://javadoc.io/badge/com.fasterxml.jackson.core/jackson-databind.svg)](http://www.javadoc.io/doc/com.fasterxml.jackson.core/jackson-databind) [![Coverage Status](https://coveralls.io/repos/github/FasterXML/jackson-databind/badge.svg?branch=master)](https://coveralls.io/github/FasterXML/jackson-databind?branch=master) ----- @@ -25,7 +25,7 @@ Functionality of this package is contained in Java package `com.fasterxml.jackso ... - 2.9.0 + 2.9.7 ... @@ -180,7 +180,7 @@ But let's look at a simple teaser to whet your appetite. JsonFactory f = mapper.getFactory(); // may alternatively construct directly too // First: write simple JSON output -File jsonFile = new JsonFile("test.json"); +File jsonFile = new File("test.json"); JsonGenerator g = f.createGenerator(jsonFile); // write JSON: { "message" : "Hello world!" } g.writeStartObject(); @@ -412,19 +412,19 @@ usually a Jackson module. ----- -# Differences from Jackson 1.x +## Differences from Jackson 1.x -Project contains versions 2.0 and above: source code for earlier (1.x) versions was available from [Codehaus](http://jackson.codehaus.org) SVN repository, but due to Codehaus closure is currently (July 2015) not officially available. -We may try to create Jackson1x repository at Github in future (if you care about this, ping Jackson team via mailing lists, or file an issue for this project). +Project contains versions 2.0 and above: source code for last (1.x) release, 1.9, is available at +[Jackson-1](../../../jackson-1) repo. -Main differences compared to 1.0 "mapper" jar are: +Main differences compared to 1.x "mapper" jar are: * Maven build instead of Ant * Java package is now `com.fasterxml.jackson.databind` (instead of `org.codehaus.jackson.map`) ----- -# Further reading +## Further reading * [Overall Jackson Docs](../../../jackson-docs) * [Project wiki page](https://github.com/FasterXML/jackson-databind/wiki) @@ -433,4 +433,5 @@ Related: * [Core annotations](https://github.com/FasterXML/jackson-annotations) package defines annotations commonly used for configuring databinding details * [Core parser/generator](https://github.com/FasterXML/jackson-core) package defines low-level incremental/streaming parsers, generators -* [Jackson Project Home](http://wiki.fasterxml.com/JacksonHome) has additional documentation (although much of it for Jackson 1.x) +* [Jackson Project Home](../../../jackson) has links to all modules +* [Jackson Docs](../../../jackson-docs) is project's documentation hub diff --git a/pom.xml b/pom.xml index d12f922d8f..c8d21ee602 100644 --- a/pom.xml +++ b/pom.xml @@ -5,12 +5,12 @@ com.fasterxml.jackson jackson-base - 2.10.0-SNAPSHOT + 3.0.0-SNAPSHOT com.fasterxml.jackson.core jackson-databind - 2.10.0-SNAPSHOT + 3.0.0-SNAPSHOT jackson-databind bundle General data-binding functionality for Jackson: works on core streaming API @@ -25,24 +25,17 @@ - - 1.7 - 1.7 - com.fasterxml.jackson.databind.*;version=${project.version} + 2.0.0-beta.5 + com/fasterxml/jackson/databind/cfg com.fasterxml.jackson.databind.cfg - + com.fasterxml.jackson.databind @@ -66,19 +59,31 @@ - + + org.powermock + powermock-core + ${version.powermock} + test + org.powermock powermock-module-junit4 - 1.7.4 + ${version.powermock} test org.powermock - powermock-api-mockito - 1.7.4 + powermock-api-mockito2 + ${version.powermock} test + + org.assertj + assertj-core + 3.9.1 + test + + javax.measure @@ -86,6 +91,7 @@ 1.0.0 test + - - maven-enforcer-plugin - - - enforce-properties - validate - enforce - - - + + org.jacoco + jacoco-maven-plugin + + + + + org.apache.maven.plugins ${version.plugin.surefire} maven-surefire-plugin @@ -128,24 +145,37 @@ - org.apache.maven.plugins maven-javadoc-plugin - http://fasterxml.github.com/jackson-annotations/javadoc/2.9 - http://fasterxml.github.com/jackson-core/javadoc/2.9 + http://fasterxml.github.com/jackson-annotations/javadoc/3.0 + http://fasterxml.github.com/jackson-core/javadoc/3.0 - - + + com.google.code.maven-replacer-plugin replacer + + + org.apache.maven.plugins + maven-compiler-plugin + true + + true + + -parameters + + + + org.eluder.coveralls @@ -155,15 +185,6 @@ - - - - org.codehaus.mojo - cobertura-maven-plugin - - - - release diff --git a/release-notes/CREDITS b/release-notes/CREDITS new file mode 100644 index 0000000000..8450266b34 --- /dev/null +++ b/release-notes/CREDITS @@ -0,0 +1,11 @@ +Here are people who have contributed to the development of Jackson JSON processor +databind core component, version 3.x +Version numbers in brackets indicate release in which the problem was fixed + +(note: for older credits, see `CREDITS-2.x` instead) + +Tatu Saloranta, tatu.saloranta@iki.fi: author + +Alexander Koshman (akoshman@github) + * Requested #1600: Serializing locale with underscore, not standard hyphen + [3.0.0] diff --git a/release-notes/VERSION b/release-notes/VERSION new file mode 100644 index 0000000000..fa42e730d1 --- /dev/null +++ b/release-notes/VERSION @@ -0,0 +1,40 @@ +Project: jackson-databind +Versions: 3.x (for earlier see VERSION-2.x) + +------------------------------------------------------------------------ +=== Releases === +------------------------------------------------------------------------ + +3.0.0 (not yet released) + +#1058: Add a way to pass std and format-specific parser/generator flags during + parser/generation construction +#1600: Serializing locale with underscore, not standard hyphen + (requested by Alexander K) +#1762: `StdDateFormat`: serialize time offset using colon +#1772: Remove `MapperFeature. USE_STD_BEAN_NAMING` +#1773: Remove `MapperFeature.AUTO_DETECT_xxx` features +#1774: Merge Java8 datatype (`Optional`, `Stream`) support (`jackson-datatype-jdk8` +#1775: Merge Java8 parameter name support (`jackson-module-parameter-names`) +#1781: Return `ObjectNode` from `ObjectNode` set-methods in order to allow better chaining + (reported by timo-schmid@github) +#1789: Add `createGenerator` methods in `ObjectMapper`, `ObjectWriter` +#1790: Add `createParser` methods in `ObjectMapper`, `ObjectReader` +#1883: Add "abstract type mapping" for deserialization from `Map` + into `EnumMap` (and `Set` to `EnumSet`) +#1888: Merge `ResolvableSerializer` into `JsonSerializer`, `ResolvableDeserializer` + into `JsonDeserializer` +#1889: Merge `ContextualSerializer` into `JsonSerializer`, `ContextualDeserializer` + into `JsonDeserializer` +#1916: Change `MapperFeature.USE_GETTERS_AS_SETTERS)` default to `false` +#1917: Remove `canSerialize` and `canDeserialize` methods from `ObjectMapper` +#1954: Add Builder pattern for creating configured `ObjectMapper` instances +#1955: Change the way `Module`s configure, interact with `ObjectMapper` +#1973: Remove support for "default [Map] key serializer" configuration from + `SerializerProvider` +#1994: Limit size of `SerializerCache`, auto-flush on exceeding +#1995: Limit size of `DeserializerCache`, auto-flush on exceeding +#2040: Remove `JsonSerializer.isEmpty()` from 3.0 +#2176: Add `JsonMapper.shared()` static method +- Remove `MappingJsonFactory` +- Add context parameter for `TypeSerializer` contextualization (`forProperty()`) diff --git a/src/main/java/com/fasterxml/jackson/databind/AbstractTypeResolver.java b/src/main/java/com/fasterxml/jackson/databind/AbstractTypeResolver.java index 27175e0c51..dbe4cb998c 100644 --- a/src/main/java/com/fasterxml/jackson/databind/AbstractTypeResolver.java +++ b/src/main/java/com/fasterxml/jackson/databind/AbstractTypeResolver.java @@ -30,18 +30,6 @@ public JavaType findTypeMapping(DeserializationConfig config, JavaType type) { return null; } - /** - * Older variant of {@link #resolveAbstractType(DeserializationConfig, BeanDescription)}; - * obsoleted in 2.7 - * - * @deprecated since 2.8 (may be removed from 2.9 or later) - */ - @Deprecated - public JavaType resolveAbstractType(DeserializationConfig config, - JavaType type) { - return null; - } - /** * Method called to try to resolve an abstract type into * concrete type (usually for purposes of deserializing), @@ -56,8 +44,6 @@ public JavaType resolveAbstractType(DeserializationConfig config, * @return Resolved concrete type (which should retain generic * type parameters of input type, if any), if resolution succeeds; * null if resolver does not know how to resolve given type - * - * @since 2.7 */ public JavaType resolveAbstractType(DeserializationConfig config, BeanDescription typeDesc) { diff --git a/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java b/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java index 50a0d4aac2..3bcff6ec33 100644 --- a/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java +++ b/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java @@ -15,7 +15,6 @@ import com.fasterxml.jackson.databind.deser.ValueInstantiator; import com.fasterxml.jackson.databind.introspect.*; import com.fasterxml.jackson.databind.jsontype.NamedType; -import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; import com.fasterxml.jackson.databind.util.Converter; import com.fasterxml.jackson.databind.util.NameTransformer; @@ -42,9 +41,9 @@ public abstract class AnnotationIntrospector implements Versioned, java.io.Serializable { /* - /********************************************************** + /********************************************************************** /* Helper types - /********************************************************** + /********************************************************************** */ /** @@ -92,9 +91,9 @@ public ReferenceProperty(Type t, String n) { } /* - /********************************************************** + /********************************************************************** /* Factory methods - /********************************************************** + /********************************************************************** */ /** @@ -111,9 +110,9 @@ public static AnnotationIntrospector pair(AnnotationIntrospector a1, AnnotationI } /* - /********************************************************** + /********************************************************************** /* Access to possibly chained introspectors - /********************************************************** + /********************************************************************** */ /** @@ -147,37 +146,35 @@ public Collection allIntrospectors(Collection config, Annotated ann) { return null; } /** * Method for figuring out additional properties of an Object Identity reference - * - * @since 2.1 */ - public ObjectIdInfo findObjectReferenceInfo(Annotated ann, ObjectIdInfo objectIdInfo) { + public ObjectIdInfo findObjectReferenceInfo(MapperConfig config, + Annotated ann, ObjectIdInfo objectIdInfo) { return objectIdInfo; } /* - /********************************************************** + /********************************************************************** /* General class annotations - /********************************************************** + /********************************************************************** */ /** @@ -213,9 +207,6 @@ public ObjectIdInfo findObjectReferenceInfo(Annotated ann, ObjectIdInfo objectId * for XML compatibility purposes) for given class, if one * is defined. Returns null if no declaration found; can return * explicit empty String, which is usually ignored as well as null. - *

- * NOTE: method signature changed in 2.1, to return {@link PropertyName} - * instead of String. */ public PropertyName findRootName(AnnotatedClass ac) { return null; @@ -224,17 +215,9 @@ public PropertyName findRootName(AnnotatedClass ac) { /** * Method for finding information about properties to ignore either by * name, or by more general specification ("ignore all unknown"). - * This method combines multiple aspects of ignorals and deprecates - * earlier methods such as - * {@link #findPropertiesToIgnore(Annotated, boolean)} and - * {@link #findIgnoreUnknownProperties(AnnotatedClass)}. - * - * @since 2.8 */ public JsonIgnoreProperties.Value findPropertyIgnorals(Annotated ac) { - // 18-Oct-2016, tatu: Used to call deprecated methods for backwards - // compatibility in 2.8, but not any more in 2.9 return JsonIgnoreProperties.Value.empty(); } @@ -268,8 +251,6 @@ public JsonIgnoreProperties.Value findPropertyIgnorals(Annotated ac) * * @return Sub-class or instance of {@link PropertyNamingStrategy}, if one * is specified for given class; null if not. - * - * @since 2.1 */ public Object findNamingStrategy(AnnotatedClass ac) { return null; } @@ -281,44 +262,13 @@ public JsonIgnoreProperties.Value findPropertyIgnorals(Annotated ac) * is not defined. * * @return Human-readable description, if any. - * - * @since 2.7 */ public String findClassDescription(AnnotatedClass ac) { return null; } - /** - * @param forSerialization True if requesting properties to ignore for serialization; - * false if for deserialization - * - * @since 2.6 - * - * @deprecated Since 2.8, use {@link #findPropertyIgnorals} instead - */ - @Deprecated // since 2.8 - public String[] findPropertiesToIgnore(Annotated ac, boolean forSerialization) { - return null; - } - - /** - * @deprecated Since 2.6, use variant that takes second argument. - */ - @Deprecated // since 2.6 - public String[] findPropertiesToIgnore(Annotated ac) { - return null; - } - - /** - * Method for checking whether an annotation indicates that all unknown properties - * - * @deprecated Since 2.8, use {@link #findPropertyIgnorals} instead - */ - @Deprecated // since 2.8 - public Boolean findIgnoreUnknownProperties(AnnotatedClass ac) { return null; } - /* - /********************************************************** + /********************************************************************** /* Property auto-detection - /********************************************************** + /********************************************************************** */ /** @@ -328,74 +278,39 @@ public String[] findPropertiesToIgnore(Annotated ac) { * (if no annotations are found), or build and return a derived instance (using * checker's build methods). */ - public VisibilityChecker findAutoDetectVisibility(AnnotatedClass ac, VisibilityChecker checker) { + public VisibilityChecker findAutoDetectVisibility(MapperConfig config, + AnnotatedClass ac, VisibilityChecker checker) { return checker; } - + /* - /********************************************************** + /********************************************************************** /* Annotations for Polymorphic type handling - /********************************************************** + /********************************************************************** */ - + /** - * Method for checking if given class has annotations that indicate - * that specific type resolver is to be used for handling instances. - * This includes not only - * instantiating resolver builder, but also configuring it based on - * relevant annotations (not including ones checked with a call to - * {@link #findSubtypes} + * Method for checking whether given Class or Property Accessor specifies + * polymorphic type-handling information, to indicate need for polymorphic + * handling. * - * @param config Configuration settings in effect (for serialization or deserialization) - * @param ac Annotated class to check for annotations - * @param baseType Base java type of value for which resolver is to be found - * - * @return Type resolver builder for given type, if one found; null if none + * @since 3.0 */ - public TypeResolverBuilder findTypeResolver(MapperConfig config, - AnnotatedClass ac, JavaType baseType) { + public JsonTypeInfo.Value findPolymorphicTypeInfo(MapperConfig config, Annotated ann) { return null; } /** - * Method for checking if given property entity (field or method) has annotations - * that indicate that specific type resolver is to be used for handling instances. - * This includes not only - * instantiating resolver builder, but also configuring it based on - * relevant annotations (not including ones checked with a call to - * {@link #findSubtypes} - * - * @param config Configuration settings in effect (for serialization or deserialization) - * @param am Annotated member (field or method) to check for annotations - * @param baseType Base java type of property for which resolver is to be found - * - * @return Type resolver builder for properties of given entity, if one found; - * null if none + * @since 3.0 */ - public TypeResolverBuilder findPropertyTypeResolver(MapperConfig config, - AnnotatedMember am, JavaType baseType) { + public Object findTypeResolverBuilder(MapperConfig config, Annotated ann) { return null; } /** - * Method for checking if given structured property entity (field or method that - * has nominal value of Map, Collection or array type) has annotations - * that indicate that specific type resolver is to be used for handling type - * information of contained values. - * This includes not only - * instantiating resolver builder, but also configuring it based on - * relevant annotations (not including ones checked with a call to - * {@link #findSubtypes} - * - * @param config Configuration settings in effect (for serialization or deserialization) - * @param am Annotated member (field or method) to check for annotations - * @param containerType Type of property for which resolver is to be found (must be a container type) - * - * @return Type resolver builder for values contained in properties of given entity, - * if one found; null if none - */ - public TypeResolverBuilder findPropertyContentTypeResolver(MapperConfig config, - AnnotatedMember am, JavaType containerType) { + * @since 3.0 + */ + public Object findTypeIdResolver(MapperConfig config, Annotated ann) { return null; } @@ -408,21 +323,21 @@ public TypeResolverBuilder findPropertyContentTypeResolver(MapperConfig co * * @param a Annotated entity (class, field/method) to check for annotations */ - public List findSubtypes(Annotated a) { return null; } + public List findSubtypes(MapperConfig config, Annotated a) { return null; } /** * Method for checking if specified type has explicit name. * * @param ac Class to check for type name annotations */ - public String findTypeName(AnnotatedClass ac) { return null; } + public String findTypeName(MapperConfig config, AnnotatedClass ac) { return null; } /** * Method for checking whether given accessor claims to represent * type id: if so, its value may be used as an override, * instead of generated type id. */ - public Boolean isTypeId(AnnotatedMember member) { return null; } + public Boolean isTypeId(MapperConfig config, AnnotatedMember member) { return null; } /* /********************************************************** @@ -467,16 +382,8 @@ public TypeResolverBuilder findPropertyContentTypeResolver(MapperConfig co * * @return Identifier of value to inject, if any; null if no injection * indicator is found - * - * @since 2.9 */ public JacksonInject.Value findInjectableValue(AnnotatedMember m) { - // 05-Apr-2017, tatu: Just for 2.9, call deprecated method to help - // with some cases of overrides for legacy code - Object id = findInjectableValueId(m); - if (id != null) { - return JacksonInject.Value.forId(id); - } return null; } @@ -510,8 +417,6 @@ public JacksonInject.Value findInjectableValue(AnnotatedMember m) { * Return value is typically used by serializers and/or * deserializers to customize presentation aspects of the * serialized value. - * - * @since 2.1 */ public JsonFormat.Value findFormat(Annotated memberOrClass) { return JsonFormat.Value.empty(); @@ -521,12 +426,10 @@ public JsonFormat.Value findFormat(Annotated memberOrClass) { * Method used to check if specified property has annotation that indicates * that it should be wrapped in an element; and if so, name to use. * Note that not all serializers and deserializers support use this method: - * currently (2.1) it is only used by XML-backed handlers. + * currently (3.0) it is only used by XML-backed handlers. * * @return Wrapper name to use, if any, or {@link PropertyName#USE_DEFAULT} * to indicate that no wrapper element should be used. - * - * @since 2.1 */ public PropertyName findWrapperName(Annotated ann) { return null; } @@ -535,8 +438,6 @@ public JsonFormat.Value findFormat(Annotated memberOrClass) { * for the property. While core databind does not make any use of it, it is exposed * for extension modules to use: an expected use is generation of schema representations * and documentation. - * - * @since 2.5 */ public String findPropertyDefaultValue(Annotated ann) { return null; } @@ -547,8 +448,6 @@ public JsonFormat.Value findFormat(Annotated memberOrClass) { * these may be marked up using HTML is not defined. * * @return Human-readable description, if any. - * - * @since 2.3 */ public String findPropertyDescription(Annotated ann) { return null; } @@ -559,8 +458,6 @@ public JsonFormat.Value findFormat(Annotated memberOrClass) { * (some binary formats mandate use of index instead of name) and ordering * of properties (for documentation, or during serialization). * - * @since 2.4 - * * @return Explicitly specified index for the property, if any */ public Integer findPropertyIndex(Annotated ann) { return null; } @@ -569,14 +466,12 @@ public JsonFormat.Value findFormat(Annotated memberOrClass) { * Method for finding implicit name for a property that given annotated * member (field, method, creator parameter) may represent. * This is different from explicit, annotation-based property name, in that - * it is "weak" and does not either proof that a property exists (for example, + * it is "weak" and does not either prove that a property exists (for example, * if visibility is not high enough), or override explicit names. * In practice this method is used to introspect optional names for creator * parameters (which may or may not be available and cannot be detected * by standard databind); or to provide alternate name mangling for * fields, getters and/or setters. - * - * @since 2.4 */ public String findImplicitPropertyName(AnnotatedMember member) { return null; } @@ -585,8 +480,6 @@ public JsonFormat.Value findFormat(Annotated memberOrClass) { * * @return `null` if member has no information; otherwise a `List` (possibly * empty) of aliases to use. - * - * @since 2.9 */ public List findPropertyAliases(Annotated ann) { return null; } @@ -596,8 +489,6 @@ public JsonFormat.Value findFormat(Annotated memberOrClass) { * or read-write cases, visibility rules may be modified. Note, however, * that even more specific annotations (like one for ignoring specific accessor) * may further override behavior of the access definition. - * - * @since 2.6 */ public JsonProperty.Access findPropertyAccess(Annotated ann) { return null; } @@ -606,22 +497,12 @@ public JsonFormat.Value findFormat(Annotated memberOrClass) { * for the same logical property, and default logic is not enough to figure * out clear precedence. Introspector may try to choose one to use; or, if * unable, return `null` to indicate it cannot resolve the problem. - * - * @since 2.7 */ public AnnotatedMethod resolveSetterConflict(MapperConfig config, AnnotatedMethod setter1, AnnotatedMethod setter2) { return null; } - /** - * @deprecated Since 2.9 Use {@link #findInjectableValue} instead - */ - @Deprecated // since 2.9 - public Object findInjectableValueId(AnnotatedMember m) { - return null; - } - /* /********************************************************** /* Serialization: general annotations @@ -634,7 +515,7 @@ public Object findInjectableValueId(AnnotatedMember m) { * or Class (of {@code Class<JsonSerializer} implementation subtype); * if value of different type is returned, a runtime exception may be thrown by caller. */ - public Object findSerializer(Annotated am) { + public Object findSerializer(MapperConfig config, Annotated am) { return null; } @@ -644,7 +525,7 @@ public Object findSerializer(Annotated am) { * or Class (of type {@code Class<JsonSerializer>}); * if value of different type is returned, a runtime exception may be thrown by caller. */ - public Object findKeySerializer(Annotated am) { + public Object findKeySerializer(MapperConfig config, Annotated am) { return null; } @@ -656,17 +537,15 @@ public Object findKeySerializer(Annotated am) { * if value of different * type is returned, a runtime exception may be thrown by caller. */ - public Object findContentSerializer(Annotated am) { + public Object findContentSerializer(MapperConfig config, Annotated am) { return null; } /** * Method for getting a serializer definition for serializer to use * for nulls (null values) of associated property or type. - * - * @since 2.3 */ - public Object findNullSerializer(Annotated am) { + public Object findNullSerializer(MapperConfig config, Annotated am) { return null; } @@ -678,7 +557,7 @@ public Object findNullSerializer(Annotated am) { * * @return Typing mode to use, if annotation is found; null otherwise */ - public JsonSerialize.Typing findSerializationTyping(Annotated a) { + public JsonSerialize.Typing findSerializationTyping(MapperConfig config, Annotated a) { return null; } @@ -698,12 +577,9 @@ public JsonSerialize.Typing findSerializationTyping(Annotated a) { * type handling, or object identity handling; if such features are needed * an explicit serializer is usually better way to handle serialization. * - * @param a Annotated property (field, method) or class to check for - * annotations - * - * @since 2.2 + * @param a Annotated property (field, method) or class to check for annotations */ - public Object findSerializationConverter(Annotated a) { + public Object findSerializationConverter(MapperConfig config, Annotated a) { return null; } @@ -722,10 +598,8 @@ public Object findSerializationConverter(Annotated a) { * Other notes are same as those for {@link #findSerializationConverter} * * @param a Annotated property (field, method) to check. - * - * @since 2.2 */ - public Object findSerializationContentConverter(AnnotatedMember a) { + public Object findSerializationContentConverter(MapperConfig config, AnnotatedMember a) { return null; } @@ -734,49 +608,11 @@ public Object findSerializationContentConverter(AnnotatedMember a) { * name is bit unfortunate -- not just for properties!). * In case of class, acts as the default for properties POJO contains; for properties * acts as override for class defaults and possible global defaults. - * - * @since 2.6 */ - public JsonInclude.Value findPropertyInclusion(Annotated a) { + public JsonInclude.Value findPropertyInclusion(MapperConfig config, Annotated a) { return JsonInclude.Value.empty(); } - /** - * Method for checking whether given annotated entity (class, method, - * 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. - *

- * 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 - * - * @deprecated Since 2.7 Use {@link #findPropertyInclusion} instead - */ - @Deprecated // since 2.7 - public JsonInclude.Include findSerializationInclusion(Annotated a, JsonInclude.Include defValue) { - return defValue; - } - - /** - * Method for checking whether content (entries) of a {@link java.util.Map} property - * are to be included during serialization or not. - * NOTE: this is NOT called for POJO properties, or array/Collection elements. - * - * @since 2.5 - * - * @deprecated Since 2.7 Use {@link #findPropertyInclusion} instead - */ - @Deprecated // since 2.7 - public JsonInclude.Include findSerializationInclusionForContent(Annotated a, JsonInclude.Include defValue) { - return defValue; - } - /* /********************************************************** /* Serialization: type refinements @@ -787,8 +623,6 @@ public JsonInclude.Include findSerializationInclusionForContent(Annotated a, Jso * Method called to find out possible type refinements to use * for deserialization, including not just value itself but * key and/or content type, if type has those. - * - * @since 2.7 */ public JavaType refineSerializationType(final MapperConfig config, final Annotated a, final JavaType baseType) throws JsonMappingException @@ -796,30 +630,6 @@ public JavaType refineSerializationType(final MapperConfig config, return baseType; } - /** - * @deprecated Since 2.7 call {@link #refineSerializationType} instead - */ - @Deprecated // since 2.7 - public Class findSerializationType(Annotated a) { - return null; - } - - /** - * @deprecated Since 2.7 call {@link #refineSerializationType} instead - */ - @Deprecated // since 2.7 - public Class findSerializationKeyType(Annotated am, JavaType baseType) { - return null; - } - - /** - * @deprecated Since 2.7 call {@link #refineSerializationType} instead - */ - @Deprecated // since 2.7 - public Class findSerializationContentType(Annotated am, JavaType baseType) { - return null; - } - /* /********************************************************** /* Serialization: class annotations @@ -846,8 +656,6 @@ public Boolean findSerializationSortAlphabetically(Annotated ann) { /** * Method for adding possible virtual properties to be serialized along * with regular properties. - * - * @since 2.5 */ public void findAndAddVirtualProperties(MapperConfig config, AnnotatedClass ac, List properties) { } @@ -869,8 +677,6 @@ public void findAndAddVirtualProperties(MapperConfig config, AnnotatedClass a * @param a Property accessor to check * * @return Name to use if found; null if not. - * - * @since 2.1 */ public PropertyName findNameForSerialization(Annotated a) { return null; @@ -886,16 +692,8 @@ public PropertyName findNameForSerialization(Annotated a) { * {@link Boolean#FALSE} if disabled annotation (block) is found (to indicate * accessor is definitely NOT to be used "as value"); or `null` if no * information found. - * - * @since 2.9 */ public Boolean hasAsValue(Annotated a) { - // 20-Nov-2016, tatu: Delegate in 2.9; remove redirect from later versions - if (a instanceof AnnotatedMethod) { - if (hasAsValueAnnotation((AnnotatedMethod) a)) { - return true; - } - } return null; } @@ -907,17 +705,8 @@ public Boolean hasAsValue(Annotated a) { * * @return True if such annotation is found (and is not disabled), * false otherwise - * - * @since 2.9 */ public Boolean hasAnyGetter(Annotated a) { - - // 21-Nov-2016, tatu: Delegate in 2.9; remove redirect from later versions - if (a instanceof AnnotatedMethod) { - if (hasAnyGetterAnnotation((AnnotatedMethod) a)) { - return true; - } - } return null; } @@ -925,15 +714,8 @@ public Boolean hasAnyGetter(Annotated a) { * Method for efficiently figuring out which if given set of Enum values * have explicitly defined name. Method will overwrite entries in incoming names * array with explicit names found, if any, leaving other entries unmodified. - *

- * Default implementation will simply delegate to {@link #findEnumValue}, which is close - * enough, although unfortunately NOT 100% equivalent (as it will also consider name() - * to give explicit value). - * - * @since 2.7 */ public String[] findEnumValues(Class enumType, Enum[] enumValues, String[] names) { - // 18-Oct-2016, tatu: In 2.8 delegated to deprecated method; not so in 2.9 and beyond return names; } @@ -942,46 +724,11 @@ public String[] findEnumValues(Class enumType, Enum[] enumValues, String[] * * @param enumCls The Enum class to scan for the default value. * @return null if none found or it's not possible to determine one. - * - * @since 2.8 */ public Enum findDefaultEnumValue(Class> enumCls) { return null; } - /** - * Method for determining the String value to use for serializing - * given enumeration entry; used when serializing enumerations - * as Strings (the standard method). - * - * @return Serialized enum value. - * - * @deprecated Since 2.8: use {@link #findEnumValues} instead because this method - * does not properly handle override settings (defaults to enum.name - * without indicating whether that is explicit or not), and is inefficient to - * call one-by-one. - */ - @Deprecated - public String findEnumValue(Enum value) { - return value.name(); - } - - /** - * @deprecated Since 2.9 Use {@link #hasAsValue(Annotated)} instead. - */ - @Deprecated // since 2.9 - public boolean hasAsValueAnnotation(AnnotatedMethod am) { - return false; - } - - /** - * @deprecated Since 2.9 Use {@link #hasAnyGetter} instead - */ - @Deprecated - public boolean hasAnyGetterAnnotation(AnnotatedMethod am) { - return false; - } - /* /********************************************************** /* Deserialization: general annotations @@ -995,7 +742,7 @@ public boolean hasAnyGetterAnnotation(AnnotatedMethod am) { * or Class (of type {@code Class<JsonDeserializer>}); * type is returned, a runtime exception may be thrown by caller. */ - public Object findDeserializer(Annotated am) { + public Object findDeserializer(MapperConfig config, Annotated am) { return null; } @@ -1007,7 +754,7 @@ public Object findDeserializer(Annotated am) { * if value of different * type is returned, a runtime exception may be thrown by caller. */ - public Object findKeyDeserializer(Annotated am) { + public Object findKeyDeserializer(MapperConfig config, Annotated am) { return null; } @@ -1020,7 +767,7 @@ public Object findKeyDeserializer(Annotated am) { * if value of different * type is returned, a runtime exception may be thrown by caller. */ - public Object findContentDeserializer(Annotated am) { + public Object findContentDeserializer(MapperConfig config, Annotated am) { return null; } @@ -1041,12 +788,9 @@ public Object findContentDeserializer(Annotated am) { * type handling, or object identity handling; if such features are needed * an explicit deserializer is usually better way to handle deserialization. * - * @param a Annotated property (field, method) or class to check for - * annotations - * - * @since 2.2 + * @param a Annotated property (field, method) or class to check for annotations */ - public Object findDeserializationConverter(Annotated a) { + public Object findDeserializationConverter(MapperConfig config, Annotated a) { return null; } @@ -1065,10 +809,8 @@ public Object findDeserializationConverter(Annotated a) { * Other notes are same as those for {@link #findDeserializationConverter} * * @param a Annotated property (field, method) to check. - * - * @since 2.2 */ - public Object findDeserializationContentConverter(AnnotatedMember a) { + public Object findDeserializationContentConverter(MapperConfig config, AnnotatedMember a) { return null; } @@ -1081,68 +823,13 @@ public Object findDeserializationContentConverter(AnnotatedMember a) { /** * Method called to find out possible type refinements to use * for deserialization. - * - * @since 2.7 */ - public JavaType refineDeserializationType(final MapperConfig config, + public JavaType refineDeserializationType(MapperConfig config, final Annotated a, final JavaType baseType) throws JsonMappingException { return baseType; } - /** - * Method for accessing annotated type definition that a - * property can have, to be used as the type for deserialization - * instead of the static (declared) type. - * Type is usually narrowing conversion (i.e.subtype of declared type). - * Declared return type of the method is also considered acceptable. - * - * @param baseType Assumed type before considering annotations - * - * @return Class to use for deserialization instead of declared type - * - * @deprecated Since 2.7 call {@link #refineDeserializationType} instead - */ - @Deprecated - public Class findDeserializationType(Annotated am, JavaType baseType) { - return null; - } - - /** - * Method for accessing additional narrowing type definition that a - * method can have, to define more specific key type to use. - * It should be only be used with {@link java.util.Map} types. - * - * @param baseKeyType Assumed key type before considering annotations - * - * @return Class specifying more specific type to use instead of - * declared type, if annotation found; null if not - * - * @deprecated Since 2.7 call {@link #refineDeserializationType} instead - */ - @Deprecated - public Class findDeserializationKeyType(Annotated am, JavaType baseKeyType) { - return null; - } - - /** - * Method for accessing additional narrowing type definition that a - * method can have, to define more specific content type to use; - * content refers to Map values and Collection/array elements. - * It should be only be used with Map, Collection and array types. - * - * @param baseContentType Assumed content (value) type before considering annotations - * - * @return Class specifying more specific type to use instead of - * declared type, if annotation found; null if not - * - * @deprecated Since 2.7 call {@link #refineDeserializationType} instead - */ - @Deprecated - public Class findDeserializationContentType(Annotated am, JavaType baseContentType) { - return null; - } - /* /********************************************************** /* Deserialization: class annotations @@ -1154,7 +841,7 @@ public Class findDeserializationContentType(Annotated am, JavaType baseConten * type (class): return value can either be an instance of * instantiator, or class of instantiator to create. */ - public Object findValueInstantiator(AnnotatedClass ac) { + public Object findValueInstantiator(MapperConfig config, AnnotatedClass ac) { return null; } @@ -1168,17 +855,12 @@ public Object findValueInstantiator(AnnotatedClass ac) { * method does not allow returning instances: the reason is * that builders have state, and a separate instance needs * to be created for each deserialization call. - * - * @since 2.0 */ - public Class findPOJOBuilder(AnnotatedClass ac) { + public Class findPOJOBuilder(MapperConfig config, AnnotatedClass ac) { return null; } - /** - * @since 2.0 - */ - public JsonPOJOBuilder.Value findPOJOBuilderConfig(AnnotatedClass ac) { + public JsonPOJOBuilder.Value findPOJOBuilderConfig(MapperConfig config, AnnotatedClass ac) { return null; } @@ -1199,8 +881,6 @@ public JsonPOJOBuilder.Value findPOJOBuilderConfig(AnnotatedClass ac) { * @param a Property accessor to check * * @return Name to use if found; null if not. - * - * @since 2.1 */ public PropertyName findNameForDeserialization(Annotated a) { return null; @@ -1214,8 +894,6 @@ public PropertyName findNameForDeserialization(Annotated a) { * * @return True if such annotation is found (and is not disabled), * false otherwise - * - * @since 2.9 */ public Boolean hasAnySetter(Annotated a) { return null; @@ -1224,8 +902,6 @@ public Boolean hasAnySetter(Annotated a) { /** * Method for finding possible settings for property, given annotations * on an accessor. - * - * @since 2.9 */ public JsonSetter.Value findSetterInfo(Annotated a) { return JsonSetter.Value.empty(); @@ -1233,8 +909,6 @@ public JsonSetter.Value findSetterInfo(Annotated a) { /** * Method for finding merge settings for property, if any. - * - * @since 2.9 */ public Boolean findMergeInfo(Annotated a) { return null; @@ -1252,61 +926,11 @@ public Boolean findMergeInfo(Annotated a) { * * @param config Configuration settings in effect (for serialization or deserialization) * @param a Annotated accessor (usually constructor or static method) to check - * - * @since 2.9 */ public JsonCreator.Mode findCreatorAnnotation(MapperConfig config, Annotated a) { - // 13-Sep-2016, tatu: for backwards compatibility, implement using delegation - /// (remove from version AFTER 2.9) - if (hasCreatorAnnotation(a)) { - JsonCreator.Mode mode = findCreatorBinding(a); - if (mode == null) { - mode = JsonCreator.Mode.DEFAULT; - } - return mode; - } return null; } - /** - * Method for checking whether given annotated item (method, constructor) - * has an annotation - * that suggests that the method is a "creator" (aka factory) - * method to be used for construct new instances of deserialized - * values. - * - * @return True if such annotation is found (and is not disabled), - * false otherwise - * - * @deprecated Since 2.9 use {@link #findCreatorAnnotation} instead. - */ - @Deprecated - public boolean hasCreatorAnnotation(Annotated a) { - return false; - } - - /** - * Method for finding indication of creator binding mode for - * a creator (something for which {@link #hasCreatorAnnotation} returns - * true), for cases where there may be ambiguity (currently: single-argument - * creator with implicit but no explicit name for the argument). - * - * @since 2.5 - * @deprecated Since 2.9 use {@link #findCreatorAnnotation} instead. - */ - @Deprecated - public JsonCreator.Mode findCreatorBinding(Annotated a) { - return null; - } - - /** - * @deprecated Since 2.9 use {@link #hasAnySetter} instead. - */ - @Deprecated // since 2.9 - public boolean hasAnySetterAnnotation(AnnotatedMethod am) { - return false; - } - /* /********************************************************** /* Overridable methods: may be used as low-level extension @@ -1325,8 +949,6 @@ public boolean hasAnySetterAnnotation(AnnotatedMethod am) { * * return annotated.getAnnotation(annoClass); * - * - * @since 2.5 */ protected A _findAnnotation(Annotated annotated, Class annoClass) { @@ -1343,8 +965,6 @@ protected A _findAnnotation(Annotated annotated, * * return annotated.hasAnnotation(annoClass); * - * - * @since 2.5 */ protected boolean _hasAnnotation(Annotated annotated, Class annoClass) { return annotated.hasAnnotation(annoClass); @@ -1353,8 +973,6 @@ protected boolean _hasAnnotation(Annotated annotated, Class[] annoClasses) { return annotated.hasOneOf(annoClasses); diff --git a/src/main/java/com/fasterxml/jackson/databind/BeanDescription.java b/src/main/java/com/fasterxml/jackson/databind/BeanDescription.java index 24ddfe9e3a..a38df9179e 100644 --- a/src/main/java/com/fasterxml/jackson/databind/BeanDescription.java +++ b/src/main/java/com/fasterxml/jackson/databind/BeanDescription.java @@ -8,7 +8,6 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; import com.fasterxml.jackson.databind.introspect.*; -import com.fasterxml.jackson.databind.type.TypeBindings; import com.fasterxml.jackson.databind.util.Annotations; import com.fasterxml.jackson.databind.util.Converter; @@ -28,9 +27,9 @@ public abstract class BeanDescription protected final JavaType _type; /* - /********************************************************** + /********************************************************************** /* Life-cycle - /********************************************************** + /********************************************************************** */ protected BeanDescription(JavaType type) { @@ -38,9 +37,9 @@ protected BeanDescription(JavaType type) { } /* - /********************************************************** + /********************************************************************** /* Simple accesors - /********************************************************** + /********************************************************************** */ /** @@ -51,9 +50,6 @@ protected BeanDescription(JavaType type) { public Class getBeanClass() { return _type.getRawClass(); } - /** - * @since 2.9 - */ public boolean isNonStaticInnerClass() { return getClassInfo().isNonStaticInnerClass(); } @@ -76,26 +72,6 @@ public boolean isNonStaticInnerClass() { */ public abstract boolean hasKnownClassAnnotations(); - /** - * Accessor for type bindings that may be needed to fully resolve - * types of member object, such as return and argument types of - * methods and constructors, and types of fields. - * - * @deprecated Since 2.7, should not need to access bindings directly - */ - @Deprecated - public abstract TypeBindings bindingsForBeanType(); - - /** - * Method for resolving given JDK type, using this bean as the - * generic type resolution context. - * - * @deprecated Since 2.8, should simply call getType of - * property accessor directly. - */ - @Deprecated - public abstract JavaType resolveType(java.lang.reflect.Type jdkType); - /** * Method for accessing collection of annotations the bean * class has. @@ -103,9 +79,9 @@ public boolean isNonStaticInnerClass() { public abstract Annotations getClassAnnotations(); /* - /********************************************************** + /********************************************************************** /* Basic API for finding properties - /********************************************************** + /********************************************************************** */ /** @@ -118,23 +94,13 @@ public boolean isNonStaticInnerClass() { /** * Method for locating all back-reference properties (setters, fields) bean has - * - * @since 2.9 */ public abstract List findBackReferences(); - /** - * Method for locating all back-reference properties (setters, fields) bean has - * - * @deprecated Since 2.9 use {@link #findBackReferences()} instead - */ - @Deprecated - public abstract Map findBackReferenceProperties(); - /* - /********************************************************** + /********************************************************************** /* Basic API for finding creator members - /********************************************************** + /********************************************************************** */ public abstract List getConstructors(); @@ -168,9 +134,9 @@ public boolean isNonStaticInnerClass() { public abstract Method findFactoryMethod(Class... expArgTypes); /* - /********************************************************** + /********************************************************************** /* Basic API for finding property accessors - /********************************************************** + /********************************************************************** */ /** @@ -179,8 +145,6 @@ public boolean isNonStaticInnerClass() { * {@link com.fasterxml.jackson.annotation.JsonValue} annotation, * if any. If multiple ones are found, * an error is reported by throwing {@link IllegalArgumentException} - * - * @since 2.9 */ public abstract AnnotatedMember findJsonValueAccessor(); @@ -195,44 +159,15 @@ public boolean isNonStaticInnerClass() { * Additional checks are also made to see that method signature * is acceptable: needs to take 2 arguments, first one String or * Object; second any can be any type. - * - * @since 2.9 */ public abstract AnnotatedMember findAnySetterAccessor(); public abstract AnnotatedMethod findMethod(String name, Class[] paramTypes); - @Deprecated // since 2.9 - public abstract AnnotatedMethod findJsonValueMethod(); - - /** - * @deprecated Since 2.9: use {@link #findAnySetterAccessor} instead - */ - @Deprecated - public AnnotatedMethod findAnySetter() { - AnnotatedMember m = findAnySetterAccessor(); - if (m instanceof AnnotatedMethod) { - return (AnnotatedMethod) m; - } - return null; - } - - /** - * @deprecated Since 2.9: use {@link #findAnySetterAccessor} instead - */ - @Deprecated - public AnnotatedMember findAnySetterField() { - AnnotatedMember m = findAnySetterAccessor(); - if (m instanceof AnnotatedField) { - return m; - } - return null; - } - /* - /********************************************************** + /********************************************************************** /* Basic API, class configuration - /********************************************************** + /********************************************************************** */ /** @@ -241,48 +176,46 @@ public AnnotatedMember findAnySetterField() { *

* NOTE: does NOT use global inclusion default settings as the base, unless * passed as `defValue`. - * - * @since 2.7 */ public abstract JsonInclude.Value findPropertyInclusion(JsonInclude.Value defValue); /** * 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. - * - * @since 2.1 + * defined by possible annotations (but NOT config overrides) + * + * @deprecated Since 3.0 + */ + @Deprecated // since 3.0 + public abstract JsonFormat.Value findExpectedFormat(); + + /** + * Method for checking what is the expected format for POJO, as + * defined by possible annotations and possible per-type config overrides. */ - public abstract JsonFormat.Value findExpectedFormat(JsonFormat.Value defValue); + public abstract JsonFormat.Value findExpectedFormat(Class baseType); /** * Method for finding {@link Converter} used for serializing instances * of this class. - * - * @since 2.2 */ public abstract Converter findSerializationConverter(); /** * Method for finding {@link Converter} used for serializing instances * of this class. - * - * @since 2.2 */ public abstract Converter findDeserializationConverter(); /** * Accessor for possible description for the bean type, used for constructing * documentation. - * - * @since 2.7 */ public String findClassDescription() { return null; } /* - /********************************************************** + /********************************************************************** /* Basic API, other - /********************************************************** + /********************************************************************** */ public abstract Map findInjectables(); @@ -318,8 +251,6 @@ public AnnotatedMember findAnySetterField() { * Method for finding out if the POJO specifies default view(s) to * use for properties, considering both per-type annotations and * global default settings. - * - * @since 2.9 */ public abstract Class[] findDefaultViews(); } diff --git a/src/main/java/com/fasterxml/jackson/databind/BeanProperty.java b/src/main/java/com/fasterxml/jackson/databind/BeanProperty.java index e8a562c3f9..2784092785 100644 --- a/src/main/java/com/fasterxml/jackson/databind/BeanProperty.java +++ b/src/main/java/com/fasterxml/jackson/databind/BeanProperty.java @@ -12,8 +12,7 @@ import com.fasterxml.jackson.databind.introspect.AnnotatedMember; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; import com.fasterxml.jackson.databind.type.TypeFactory; -import com.fasterxml.jackson.databind.util.Annotations; -import com.fasterxml.jackson.databind.util.Named; +import com.fasterxml.jackson.databind.util.FullyNamed; /** * Bean properties are logical entities that represent data @@ -26,33 +25,22 @@ *

* Instances are not typically passed when constructing serializers * and deserializers, but rather only passed when context - * is known when - * {@link com.fasterxml.jackson.databind.ser.ContextualSerializer} and - * {@link com.fasterxml.jackson.databind.deser.ContextualDeserializer} - * resolution occurs (createContextual(...) method is called). + * is known and + * {@link JsonSerializer#createContextual} and + * {@link JsonDeserializer#createContextual} are called. * References may (need to) be retained by serializers and deserializers, * especially when further resolving dependent handlers like value * serializers/deserializers or structured types. */ -public interface BeanProperty extends Named +public interface BeanProperty extends FullyNamed { public final static JsonFormat.Value EMPTY_FORMAT = new JsonFormat.Value(); public final static JsonInclude.Value EMPTY_INCLUDE = JsonInclude.Value.empty(); - /** - * Method to get logical name of the property - */ - @Override - public String getName(); - - /** - * Method for getting full name definition, including possible - * format-specific additional properties (such as namespace when - * using XML backend). - * - * @since 2.3 - */ - public PropertyName getFullName(); + // // // From FullyNamed + + //public String getName(); + //public PropertyName getFullName(); /** * Method to get declared type of the property. @@ -62,16 +50,12 @@ public interface BeanProperty extends Named /** * If property is indicated to be wrapped, name of * wrapper element to use. - * - * @since 2.2 */ public PropertyName getWrapperName(); /** * Accessor for additional optional information about property. - * - * @since 2.3 - * + * * @return Metadata about property; never null. */ public PropertyMetadata getMetadata(); @@ -83,23 +67,19 @@ public interface BeanProperty extends Named * * getMetadata().isRequired() * - * - * @since 2.2 */ public boolean isRequired(); /** * Accessor for checking whether there is an actual physical property * behind this property abstraction or not. - * - * @since 2.7 */ public boolean isVirtual(); /* - /********************************************************** + /********************************************************************** /* Access to annotation information - /********************************************************** + /********************************************************************** */ /** @@ -133,27 +113,20 @@ public interface BeanProperty extends Named public AnnotatedMember getMember(); /** - * Convenience method that is roughly equivalent to - *

-     *   return intr.findFormat(getMember());
-     *
- * and specifically does NOT try to find per-type format defaults to merge; - * use {@link #findPropertyFormat} if such defaults would be useful. - * - * @since 2.6 - * - * @deprecated since 2.8 use {@link #findPropertyFormat} instead. + * Helper method used to look up format settings applicable to this property, + * considering both possible per-type configuration settings */ - @Deprecated - public JsonFormat.Value findFormatOverrides(AnnotationIntrospector intr); + public JsonFormat.Value findPropertyFormat(MapperConfig config, Class baseType); /** - * Helper method used to look up format settings applicable to this property, - * considering both possible per-type configuration settings + * Helper method used to only access property-specified format overrides, if any, + * not considering type or global default format settings. + * + * @return Format override settings if any; `null` if no overrides * - * @since 2.7 + * @since 3.0 */ - public JsonFormat.Value findPropertyFormat(MapperConfig config, Class baseType); + public JsonFormat.Value findFormatOverrides(MapperConfig config); /** * Convenience method that is roughly equivalent to @@ -161,8 +134,6 @@ public interface BeanProperty extends Named * return config.getAnnotationIntrospector().findPropertyInclusion(getMember()); * * but also considers global default settings for inclusion - * - * @since 2.7 */ public JsonInclude.Value findPropertyInclusion(MapperConfig config, Class baseType); @@ -171,15 +142,13 @@ public interface BeanProperty extends Named * during deserialization. * * @return List (possibly empty) of alternate names; never null - * - * @since 2.9 */ public List findAliases(MapperConfig config); /* - /********************************************************** + /********************************************************************** /* Schema/introspection support - /********************************************************** + /********************************************************************** */ /** @@ -188,23 +157,17 @@ public interface BeanProperty extends Named * Note that not all implementations support traversal with this * method; those that do not should throw * {@link UnsupportedOperationException}. - *

- * NOTE: Starting with 2.7, takes explicit {@link SerializerProvider} - * argument to reduce the need to rely on provider visitor may or may not - * have assigned. * * @param objectVisitor Visitor to used as the callback handler - * - * @since 2.2 (although signature did change in 2.7) */ public void depositSchemaProperty(JsonObjectFormatVisitor objectVisitor, SerializerProvider provider) throws JsonMappingException; /* - /********************************************************** + /********************************************************************** /* Helper classes - /********************************************************** + /********************************************************************** */ /** @@ -212,9 +175,9 @@ public void depositSchemaProperty(JsonObjectFormatVisitor objectVisitor, * or base class for more complex implementations. */ public static class Std implements BeanProperty, - java.io.Serializable // 2.9 + java.io.Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 3L; protected final PropertyName _name; protected final JavaType _type; @@ -239,20 +202,6 @@ public Std(PropertyName name, JavaType type, PropertyName wrapperName, _member = member; } - /** - * @deprecated Since 2.9 - */ - @Deprecated - public Std(PropertyName name, JavaType type, PropertyName wrapperName, - Annotations contextAnnotations, - AnnotatedMember member, PropertyMetadata metadata) - { - this(name, type, wrapperName, member, metadata); - } - - /** - * @since 2.6 - */ public Std(Std base, JavaType newType) { this(base._name, newType, base._wrapperName, base._member, base._metadata); } @@ -272,17 +221,10 @@ public A getContextAnnotation(Class acls) { } @Override - @Deprecated - public JsonFormat.Value findFormatOverrides(AnnotationIntrospector intr) { - if ((_member != null) && (intr != null)) { - JsonFormat.Value v = intr.findFormat(_member); - if (v != null) { - return v; - } - } - return EMPTY_FORMAT; + public JsonFormat.Value findFormatOverrides(MapperConfig config) { + return null; } - + @Override public JsonFormat.Value findPropertyFormat(MapperConfig config, Class baseType) { JsonFormat.Value v0 = config.getDefaultPropertyFormat(baseType); @@ -305,7 +247,7 @@ public JsonInclude.Value findPropertyInclusion(MapperConfig config, Class if ((intr == null) || (_member == null)) { return v0; } - JsonInclude.Value v = intr.findPropertyInclusion(_member); + JsonInclude.Value v = intr.findPropertyInclusion(config, _member); if (v == null) { return v0; } @@ -346,8 +288,6 @@ public void depositSchemaProperty(JsonObjectFormatVisitor objectVisitor, /** * Alternative "Null" implementation that can be used in cases where a non-null * {@link BeanProperty} is needed - * - * @since 2.9 */ public static class Bogus implements BeanProperty { @@ -402,11 +342,10 @@ public AnnotatedMember getMember() { } @Override - @Deprecated - public Value findFormatOverrides(AnnotationIntrospector intr) { - return Value.empty(); + public JsonFormat.Value findFormatOverrides(MapperConfig config) { + return null; } - + @Override public Value findPropertyFormat(MapperConfig config, Class baseType) { return Value.empty(); diff --git a/src/main/java/com/fasterxml/jackson/databind/DatabindContext.java b/src/main/java/com/fasterxml/jackson/databind/DatabindContext.java index 11110380b8..31b2da43a9 100644 --- a/src/main/java/com/fasterxml/jackson/databind/DatabindContext.java +++ b/src/main/java/com/fasterxml/jackson/databind/DatabindContext.java @@ -20,8 +20,6 @@ * process. Designed so that some of implementations can rely on shared * aspects like access to secondary contextual objects like type factories * or handler instantiators. - * - * @since 2.2 */ public abstract class DatabindContext { @@ -29,8 +27,6 @@ public abstract class DatabindContext * Let's limit length of error messages, for cases where underlying data * may be very large -- no point in spamming logs with megabytes of meaningless * data. - * - * @since 2.9 */ private final static int MAX_ERROR_STR_LEN = 500; @@ -84,24 +80,15 @@ public abstract class DatabindContext */ public abstract Class getActiveView(); - /** - * @since 2.6 - */ public abstract Locale getLocale(); - /** - * @since 2.6 - */ public abstract TimeZone getTimeZone(); - /** - * @since 2.7 - */ public abstract JsonFormat.Value getDefaultPropertyFormat(Class baseType); /* /********************************************************** - /* Generic attributes (2.3+) + /* Generic attributes /********************************************************** */ @@ -113,8 +100,6 @@ public abstract class DatabindContext * * @param key Key of the attribute to get * @return Value of the attribute, if any; null otherwise - * - * @since 2.3 */ public abstract Object getAttribute(Object key); @@ -127,8 +112,6 @@ public abstract class DatabindContext * @param value Value to set attribute to * * @return This context object, to allow chaining - * - * @since 2.3 */ public abstract DatabindContext setAttribute(Object key, Object value); @@ -164,8 +147,6 @@ public JavaType constructSpecializedType(JavaType baseType, Class subclass) { /** * Lookup method called when code needs to resolve class name from input; * usually simple lookup - * - * @since 2.9 */ public JavaType resolveSubType(JavaType baseType, String subClass) throws JsonMappingException @@ -209,8 +190,6 @@ public JavaType resolveSubType(JavaType baseType, String subClass) * Note that most of the time this method should NOT be called directly: instead, * method handleUnknownTypeId() should be called which will call this method * if necessary. - * - * @since 2.9 */ protected abstract JsonMappingException invalidTypeIdException(JavaType baseType, String typeId, String extraDesc); @@ -301,14 +280,9 @@ public Converter converterInstance(Annotated annotated, * Helper method called to indicate a generic problem that stems from type * definition(s), not input data, or input/output state; typically this * means throwing a {@link com.fasterxml.jackson.databind.exc.InvalidDefinitionException}. - * - * @since 2.9 */ public abstract T reportBadDefinition(JavaType type, String msg) throws JsonMappingException; - /** - * @since 2.9 - */ public T reportBadDefinition(Class type, String msg) throws JsonMappingException { return reportBadDefinition(constructType(type), msg); } @@ -319,9 +293,6 @@ public T reportBadDefinition(Class type, String msg) throws JsonMappingEx /********************************************************** */ - /** - * @since 2.9 - */ protected final String _format(String msg, Object... msgArgs) { if (msgArgs.length > 0) { return String.format(msg, msgArgs); @@ -329,9 +300,6 @@ protected final String _format(String msg, Object... msgArgs) { return msg; } - /** - * @since 2.9 - */ protected final String _truncate(String desc) { if (desc == null) { return ""; @@ -342,9 +310,6 @@ protected final String _truncate(String desc) { return desc.substring(0, MAX_ERROR_STR_LEN) + "]...[" + desc.substring(desc.length() - MAX_ERROR_STR_LEN); } - /** - * @since 2.9 - */ protected String _quotedString(String desc) { if (desc == null) { return "[N/A]"; @@ -352,10 +317,7 @@ protected String _quotedString(String desc) { // !!! should we quote it? (in case there are control chars, linefeeds) return String.format("\"%s\"", _truncate(desc)); } - - /** - * @since 2.9 - */ + protected String _colonConcat(String msgBase, String extra) { if (extra == null) { return msgBase; @@ -363,9 +325,6 @@ protected String _colonConcat(String msgBase, String extra) { return msgBase + ": " + extra; } - /** - * @since 2.9 - */ protected String _desc(String desc) { if (desc == null) { return "[N/A]"; diff --git a/src/main/java/com/fasterxml/jackson/databind/DefaultTyping.java b/src/main/java/com/fasterxml/jackson/databind/DefaultTyping.java new file mode 100644 index 0000000000..bfa9574367 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/DefaultTyping.java @@ -0,0 +1,59 @@ +package com.fasterxml.jackson.databind; + +import com.fasterxml.jackson.core.TreeNode; +import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; + +/** + * Enumeration used with ObjectMapper.enableDefaultTyping() + * to specify what kind of types (classes) default typing should + * be used for. It will only be used if no explicit type information + * is found, but this enumeration further limits subset of those types. + *

+ * Since 2.4 there are special exceptions for JSON Tree model + * types (sub-types of {@link TreeNode}: default typing is never + * applied to them. + * Since 2.8 additional checks are made to avoid attempts at default + * typing primitive-valued properties. + *

+ * NOTE: use of Default Typing can be a potential security risk if incoming + * content comes from untrusted sources, and it is recommended that this + * is either not done, or, if enabled, use ObjectMapper.setDefaultTyping() + * passing a custom {@link TypeResolverBuilder} implementation that white-lists + * legal types to use. + */ +public enum DefaultTyping { + /** + * This value means that only properties that have + * {@link java.lang.Object} as declared type (including + * generic types without explicit type) will use default + * typing. + */ + JAVA_LANG_OBJECT, + + /** + * Value that means that default typing will be used for + * properties with declared type of {@link java.lang.Object} + * or an abstract type (abstract class or interface). + * Note that this does not include array types. + * This does NOT apply to {@link TreeNode} and its subtypes. + */ + OBJECT_AND_NON_CONCRETE, + + /** + * Value that means that default typing will be used for + * all types covered by {@link #OBJECT_AND_NON_CONCRETE} + * plus all array types for them. + * This does NOT apply to {@link TreeNode} and its subtypes. + */ + NON_CONCRETE_AND_ARRAYS, + + /** + * Value that means that default typing will be used for + * all non-final types, with exception of small number of + * "natural" types (String, Boolean, Integer, Double), which + * can be correctly inferred from JSON; as well as for + * all arrays of non-final types. + * This does NOT apply to {@link TreeNode} and its subtypes. + */ + NON_FINAL +} \ No newline at end of file diff --git a/src/main/java/com/fasterxml/jackson/databind/DeserializationConfig.java b/src/main/java/com/fasterxml/jackson/databind/DeserializationConfig.java index 8ff4d1461d..b89ac6cf68 100644 --- a/src/main/java/com/fasterxml/jackson/databind/DeserializationConfig.java +++ b/src/main/java/com/fasterxml/jackson/databind/DeserializationConfig.java @@ -1,14 +1,13 @@ package com.fasterxml.jackson.databind; -import java.util.*; - import com.fasterxml.jackson.core.*; -import com.fasterxml.jackson.core.json.JsonReadFeature; + import com.fasterxml.jackson.databind.cfg.*; import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler; import com.fasterxml.jackson.databind.introspect.*; import com.fasterxml.jackson.databind.jsontype.*; -import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.type.TypeFactory; +import com.fasterxml.jackson.databind.util.ArrayIterator; import com.fasterxml.jackson.databind.util.LinkedNode; import com.fasterxml.jackson.databind.util.RootNameLookup; @@ -23,33 +22,14 @@ */ public final class DeserializationConfig extends MapperConfigBase - implements java.io.Serializable // since 2.1 + implements java.io.Serializable { - // since 2.9 - private static final long serialVersionUID = 2; - - /* - /********************************************************** - /* Configured helper objects - /********************************************************** - */ - - /** - * Linked list that contains all registered problem handlers. - * Implementation as front-added linked list allows for sharing - * of the list (tail) without copying the list. - */ - protected final LinkedNode _problemHandlers; - - /** - * Factory used for constructing {@link com.fasterxml.jackson.databind.JsonNode} instances. - */ - protected final JsonNodeFactory _nodeFactory; + private static final long serialVersionUID = 3L; /* - /********************************************************** - /* Deserialization features - /********************************************************** + /********************************************************************** + /* Deserialization, parser, format features + /********************************************************************** */ /** @@ -57,151 +37,101 @@ public final class DeserializationConfig */ protected final int _deserFeatures; - /* - /********************************************************** - /* Parser features: generic, format-specific - /********************************************************** + /** + * States of {@link com.fasterxml.jackson.core.StreamReadFeature}s to enable/disable. */ + protected final int _streamReadFeatures; /** - * States of {@link com.fasterxml.jackson.core.JsonParser.Feature}s to enable/disable. + * States of {@link com.fasterxml.jackson.core.FormatFeature}s to enable/disable. */ - protected final int _parserFeatures; + protected final int _formatReadFeatures; - /** - * Bitflag of {@link com.fasterxml.jackson.core.JsonParser.Feature}s to enable/disable + /* + /********************************************************************** + /* Configured helper objects + /********************************************************************** */ - protected final int _parserFeaturesToChange; /** - * States of {@link com.fasterxml.jackson.core.FormatFeature}s to enable/disable. - * - * @since 2.7 + * Linked list that contains all registered problem handlers. + * Implementation as front-added linked list allows for sharing + * of the list (tail) without copying the list. */ - protected final int _formatReadFeatures; + protected final LinkedNode _problemHandlers; /** - * Bitflag of {@link com.fasterxml.jackson.core.FormatFeature}s to enable/disable + * List of objects that may be able to resolve abstract types to + * concrete types. Used by functionality like "mr Bean" to materialize + * types as needed, although may be used for other kinds of defaulting + * as well. * - * @since 2.7 + * @since 3.0 */ - protected final int _formatReadFeaturesToChange; + protected final AbstractTypeResolver[] _abstractTypeResolvers; /* - /********************************************************** + /********************************************************************** /* Life-cycle, primary constructors for new instances - /********************************************************** + /********************************************************************** */ /** - * Constructor used by ObjectMapper to create default configuration object instance. + * @since 3.0 */ - public DeserializationConfig(BaseSettings base, - SubtypeResolver str, SimpleMixInResolver mixins, RootNameLookup rootNames, - ConfigOverrides configOverrides) + public DeserializationConfig(MapperBuilder b, int mapperFeatures, + int deserFeatures, int streamReadFeatures, int formatReadFeatures, + ConfigOverrides configOverrides, + TypeFactory tf, ClassIntrospector classIntr, MixInHandler mixins, SubtypeResolver str, + RootNameLookup rootNames, + AbstractTypeResolver[] atrs) { - super(base, str, mixins, rootNames, configOverrides); - _deserFeatures = collectFeatureDefaults(DeserializationFeature.class); - _nodeFactory = JsonNodeFactory.instance; - _problemHandlers = null; - _parserFeatures = 0; - _parserFeaturesToChange = 0; - _formatReadFeatures = 0; - _formatReadFeaturesToChange = 0; - } - - /** - * Copy-constructor used for making a copy used by new {@link ObjectMapper}. - * - * @since 2.9 - */ - protected DeserializationConfig(DeserializationConfig src, - SimpleMixInResolver mixins, RootNameLookup rootNames, - ConfigOverrides configOverrides) - { - super(src, mixins, rootNames, configOverrides); - _deserFeatures = src._deserFeatures; - _problemHandlers = src._problemHandlers; - _nodeFactory = src._nodeFactory; - _parserFeatures = src._parserFeatures; - _parserFeaturesToChange = src._parserFeaturesToChange; - _formatReadFeatures = src._formatReadFeatures; - _formatReadFeaturesToChange = src._formatReadFeaturesToChange; + super(b, mapperFeatures, tf, classIntr, mixins, str, configOverrides, rootNames); + _deserFeatures = deserFeatures; + _streamReadFeatures = streamReadFeatures; + _formatReadFeatures = formatReadFeatures; + _problemHandlers = b.deserializationProblemHandlers(); + _abstractTypeResolvers = atrs; } /* - /********************************************************** + /********************************************************************** /* Life-cycle, secondary constructors to support /* "mutant factories", with single property changes - /********************************************************** + /********************************************************************** */ private DeserializationConfig(DeserializationConfig src, - int mapperFeatures, int deserFeatures, - int parserFeatures, int parserFeatureMask, - int formatFeatures, int formatFeatureMask) + int deserFeatures, int streamReadFeatures, int formatReadFeatures) { - super(src, mapperFeatures); + super(src); _deserFeatures = deserFeatures; - _nodeFactory = src._nodeFactory; - _problemHandlers = src._problemHandlers; - _parserFeatures = parserFeatures; - _parserFeaturesToChange = parserFeatureMask; - _formatReadFeatures = formatFeatures; - _formatReadFeaturesToChange = formatFeatureMask; - } - - /** - * Copy constructor used to create a non-shared instance with given mix-in - * annotation definitions and subtype resolver. - */ - private DeserializationConfig(DeserializationConfig src, SubtypeResolver str) - { - super(src, str); - _deserFeatures = src._deserFeatures; - _nodeFactory = src._nodeFactory; + _streamReadFeatures = streamReadFeatures; + _formatReadFeatures = formatReadFeatures; _problemHandlers = src._problemHandlers; - _parserFeatures = src._parserFeatures; - _parserFeaturesToChange = src._parserFeaturesToChange; - _formatReadFeatures = src._formatReadFeatures; - _formatReadFeaturesToChange = src._formatReadFeaturesToChange; + _abstractTypeResolvers = src._abstractTypeResolvers; } private DeserializationConfig(DeserializationConfig src, BaseSettings base) { super(src, base); _deserFeatures = src._deserFeatures; - _nodeFactory = src._nodeFactory; - _problemHandlers = src._problemHandlers; - _parserFeatures = src._parserFeatures; - _parserFeaturesToChange = src._parserFeaturesToChange; + _streamReadFeatures = src._streamReadFeatures; _formatReadFeatures = src._formatReadFeatures; - _formatReadFeaturesToChange = src._formatReadFeaturesToChange; - } - - private DeserializationConfig(DeserializationConfig src, JsonNodeFactory f) - { - super(src); - _deserFeatures = src._deserFeatures; _problemHandlers = src._problemHandlers; - _nodeFactory = f; - _parserFeatures = src._parserFeatures; - _parserFeaturesToChange = src._parserFeaturesToChange; - _formatReadFeatures = src._formatReadFeatures; - _formatReadFeaturesToChange = src._formatReadFeaturesToChange; + _abstractTypeResolvers = src._abstractTypeResolvers; } private DeserializationConfig(DeserializationConfig src, - LinkedNode problemHandlers) + LinkedNode problemHandlers, + AbstractTypeResolver[] atr) { super(src); _deserFeatures = src._deserFeatures; - _problemHandlers = problemHandlers; - _nodeFactory = src._nodeFactory; - _parserFeatures = src._parserFeatures; - _parserFeaturesToChange = src._parserFeaturesToChange; + _streamReadFeatures = src._streamReadFeatures; _formatReadFeatures = src._formatReadFeatures; - _formatReadFeaturesToChange = src._formatReadFeaturesToChange; + _problemHandlers = problemHandlers; + _abstractTypeResolvers = atr; } private DeserializationConfig(DeserializationConfig src, PropertyName rootName) @@ -209,11 +139,9 @@ private DeserializationConfig(DeserializationConfig src, PropertyName rootName) super(src, rootName); _deserFeatures = src._deserFeatures; _problemHandlers = src._problemHandlers; - _nodeFactory = src._nodeFactory; - _parserFeatures = src._parserFeatures; - _parserFeaturesToChange = src._parserFeaturesToChange; + _streamReadFeatures = src._streamReadFeatures; _formatReadFeatures = src._formatReadFeatures; - _formatReadFeaturesToChange = src._formatReadFeaturesToChange; + _abstractTypeResolvers = src._abstractTypeResolvers; } private DeserializationConfig(DeserializationConfig src, Class view) @@ -221,11 +149,9 @@ private DeserializationConfig(DeserializationConfig src, Class view) super(src, view); _deserFeatures = src._deserFeatures; _problemHandlers = src._problemHandlers; - _nodeFactory = src._nodeFactory; - _parserFeatures = src._parserFeatures; - _parserFeaturesToChange = src._parserFeaturesToChange; + _streamReadFeatures = src._streamReadFeatures; _formatReadFeatures = src._formatReadFeatures; - _formatReadFeaturesToChange = src._formatReadFeaturesToChange; + _abstractTypeResolvers = src._abstractTypeResolvers; } protected DeserializationConfig(DeserializationConfig src, ContextAttributes attrs) @@ -233,57 +159,31 @@ protected DeserializationConfig(DeserializationConfig src, ContextAttributes att super(src, attrs); _deserFeatures = src._deserFeatures; _problemHandlers = src._problemHandlers; - _nodeFactory = src._nodeFactory; - _parserFeatures = src._parserFeatures; - _parserFeaturesToChange = src._parserFeaturesToChange; + _streamReadFeatures = src._streamReadFeatures; _formatReadFeatures = src._formatReadFeatures; - _formatReadFeaturesToChange = src._formatReadFeaturesToChange; - } - - protected DeserializationConfig(DeserializationConfig src, SimpleMixInResolver mixins) - { - super(src, mixins); - _deserFeatures = src._deserFeatures; - _problemHandlers = src._problemHandlers; - _nodeFactory = src._nodeFactory; - _parserFeatures = src._parserFeatures; - _parserFeaturesToChange = src._parserFeaturesToChange; - _formatReadFeatures = src._formatReadFeatures; - _formatReadFeaturesToChange = src._formatReadFeaturesToChange; + _abstractTypeResolvers = src._abstractTypeResolvers; } // for unit tests only: protected BaseSettings getBaseSettings() { return _base; } /* - /********************************************************** + /********************************************************************** /* Life-cycle, general factory methods from MapperConfig(Base) - /********************************************************** + /********************************************************************** */ - @Override // since 2.9 + @Override protected final DeserializationConfig _withBase(BaseSettings newBase) { return (_base == newBase) ? this : new DeserializationConfig(this, newBase); } - @Override // since 2.9 - protected final DeserializationConfig _withMapperFeatures(int mapperFeatures) { - return new DeserializationConfig(this, mapperFeatures, _deserFeatures, - _parserFeatures, _parserFeaturesToChange, - _formatReadFeatures, _formatReadFeaturesToChange); - } - /* - /********************************************************** + /********************************************************************** /* Life-cycle, specific factory methods from MapperConfig - /********************************************************** + /********************************************************************** */ - @Override - public DeserializationConfig with(SubtypeResolver str) { - return (_subtypeResolver == str) ? this : new DeserializationConfig(this, str); - } - @Override public DeserializationConfig withRootName(PropertyName rootName) { if (rootName == null) { @@ -307,9 +207,9 @@ public DeserializationConfig with(ContextAttributes attrs) { } /* - /********************************************************** + /********************************************************************** /* Life-cycle, DeserializationFeature-based factory methods - /********************************************************** + /********************************************************************** */ /** @@ -320,9 +220,8 @@ public DeserializationConfig with(DeserializationFeature feature) { int newDeserFeatures = (_deserFeatures | feature.getMask()); return (newDeserFeatures == _deserFeatures) ? this : - new DeserializationConfig(this, _mapperFeatures, newDeserFeatures, - _parserFeatures, _parserFeaturesToChange, - _formatReadFeatures, _formatReadFeaturesToChange); + new DeserializationConfig(this, newDeserFeatures, _streamReadFeatures, + _formatReadFeatures); } /** @@ -337,9 +236,8 @@ public DeserializationConfig with(DeserializationFeature first, newDeserFeatures |= f.getMask(); } return (newDeserFeatures == _deserFeatures) ? this : - new DeserializationConfig(this, _mapperFeatures, newDeserFeatures, - _parserFeatures, _parserFeaturesToChange, - _formatReadFeatures, _formatReadFeaturesToChange); + new DeserializationConfig(this, newDeserFeatures, _streamReadFeatures, + _formatReadFeatures); } /** @@ -353,9 +251,8 @@ public DeserializationConfig withFeatures(DeserializationFeature... features) newDeserFeatures |= f.getMask(); } return (newDeserFeatures == _deserFeatures) ? this : - new DeserializationConfig(this, _mapperFeatures, newDeserFeatures, - _parserFeatures, _parserFeaturesToChange, - _formatReadFeatures, _formatReadFeaturesToChange); + new DeserializationConfig(this, newDeserFeatures, + _streamReadFeatures, _formatReadFeatures); } /** @@ -366,9 +263,8 @@ public DeserializationConfig without(DeserializationFeature feature) { int newDeserFeatures = _deserFeatures & ~feature.getMask(); return (newDeserFeatures == _deserFeatures) ? this : - new DeserializationConfig(this, _mapperFeatures, newDeserFeatures, - _parserFeatures, _parserFeaturesToChange, - _formatReadFeatures, _formatReadFeaturesToChange); + new DeserializationConfig(this, newDeserFeatures, + _streamReadFeatures, _formatReadFeatures); } /** @@ -383,9 +279,8 @@ public DeserializationConfig without(DeserializationFeature first, newDeserFeatures &= ~f.getMask(); } return (newDeserFeatures == _deserFeatures) ? this : - new DeserializationConfig(this, _mapperFeatures, newDeserFeatures, - _parserFeatures, _parserFeaturesToChange, - _formatReadFeatures, _formatReadFeaturesToChange); + new DeserializationConfig(this, newDeserFeatures, _streamReadFeatures, + _formatReadFeatures); } /** @@ -399,257 +294,134 @@ public DeserializationConfig withoutFeatures(DeserializationFeature... features) newDeserFeatures &= ~f.getMask(); } return (newDeserFeatures == _deserFeatures) ? this : - new DeserializationConfig(this, _mapperFeatures, newDeserFeatures, - _parserFeatures, _parserFeaturesToChange, - _formatReadFeatures, _formatReadFeaturesToChange); + new DeserializationConfig(this, + newDeserFeatures, _streamReadFeatures, _formatReadFeatures); } /* - /********************************************************** + /********************************************************************** /* Life-cycle, JsonParser.Feature-based factory methods - /********************************************************** + /********************************************************************** */ /** * Fluent factory method that will construct and return a new configuration * object instance with specified features enabled. - * - * @since 2.5 */ - public DeserializationConfig with(JsonParser.Feature feature) + public DeserializationConfig with(StreamReadFeature feature) { - int newSet = _parserFeatures | feature.getMask(); - int newMask = _parserFeaturesToChange | feature.getMask(); - return ((_parserFeatures == newSet) && (_parserFeaturesToChange == newMask)) ? this : - new DeserializationConfig(this, _mapperFeatures, _deserFeatures, - newSet, newMask, - _formatReadFeatures, _formatReadFeaturesToChange); + int newSet = _streamReadFeatures | feature.getMask(); + return (_streamReadFeatures == newSet)? this : + new DeserializationConfig(this, + _deserFeatures, newSet, _formatReadFeatures); } /** * Fluent factory method that will construct and return a new configuration * object instance with specified features enabled. - * - * @since 2.5 */ - public DeserializationConfig withFeatures(JsonParser.Feature... features) + public DeserializationConfig withFeatures(StreamReadFeature... features) { - int newSet = _parserFeatures; - int newMask = _parserFeaturesToChange; - for (JsonParser.Feature f : features) { - int mask = f.getMask(); - newSet |= mask; - newMask |= mask; + int newSet = _streamReadFeatures; + for (StreamReadFeature f : features) { + newSet |= f.getMask(); } - return ((_parserFeatures == newSet) && (_parserFeaturesToChange == newMask)) ? this : - new DeserializationConfig(this, _mapperFeatures, _deserFeatures, - newSet, newMask, - _formatReadFeatures, _formatReadFeaturesToChange); + return (_streamReadFeatures == newSet) ? this : + new DeserializationConfig(this, _deserFeatures, newSet, + _formatReadFeatures); } /** * Fluent factory method that will construct and return a new configuration * object instance with specified feature disabled. - * - * @since 2.5 */ - public DeserializationConfig without(JsonParser.Feature feature) + public DeserializationConfig without(StreamReadFeature feature) { - int newSet = _parserFeatures & ~feature.getMask(); - int newMask = _parserFeaturesToChange | feature.getMask(); - return ((_parserFeatures == newSet) && (_parserFeaturesToChange == newMask)) ? this : - new DeserializationConfig(this, _mapperFeatures, _deserFeatures, - newSet, newMask, - _formatReadFeatures, _formatReadFeaturesToChange); + int newSet = _streamReadFeatures & ~feature.getMask(); + return (_streamReadFeatures == newSet) ? this : + new DeserializationConfig(this, _deserFeatures, newSet, + _formatReadFeatures); } /** * Fluent factory method that will construct and return a new configuration * object instance with specified features disabled. - * - * @since 2.5 */ - public DeserializationConfig withoutFeatures(JsonParser.Feature... features) + public DeserializationConfig withoutFeatures(StreamReadFeature... features) { - int newSet = _parserFeatures; - int newMask = _parserFeaturesToChange; - for (JsonParser.Feature f : features) { - int mask = f.getMask(); - newSet &= ~mask; - newMask |= mask; + int newSet = _streamReadFeatures; + for (StreamReadFeature f : features) { + newSet &= ~f.getMask(); } - return ((_parserFeatures == newSet) && (_parserFeaturesToChange == newMask)) ? this : - new DeserializationConfig(this, _mapperFeatures, _deserFeatures, - newSet, newMask, - _formatReadFeatures, _formatReadFeaturesToChange); + return (_streamReadFeatures == newSet)? this : + new DeserializationConfig(this, _deserFeatures, newSet, _formatReadFeatures); } /* - /********************************************************** + /********************************************************************** /* Life-cycle, JsonParser.FormatFeature-based factory methods - /********************************************************** + /********************************************************************** */ /** * Fluent factory method that will construct and return a new configuration * object instance with specified features enabled. - * - * @since 2.7 */ public DeserializationConfig with(FormatFeature feature) { - // 08-Oct-2018, tatu: Alas, complexity due to newly (2.10) refactored json-features: - if (feature instanceof JsonReadFeature) { - return _withJsonReadFeatures(feature); - } int newSet = _formatReadFeatures | feature.getMask(); - int newMask = _formatReadFeaturesToChange | feature.getMask(); - return ((_formatReadFeatures == newSet) && (_formatReadFeaturesToChange == newMask)) ? this : - new DeserializationConfig(this, _mapperFeatures, _deserFeatures, - _parserFeatures, _parserFeaturesToChange, - newSet, newMask); + return (_formatReadFeatures == newSet) ? this + : new DeserializationConfig(this, + _deserFeatures, _streamReadFeatures, newSet); } /** * Fluent factory method that will construct and return a new configuration * object instance with specified features enabled. - * - * @since 2.7 */ public DeserializationConfig withFeatures(FormatFeature... features) { - // 08-Oct-2018, tatu: Alas, complexity due to newly (2.10) refactored json-features: - if (features.length > 0 && (features[0] instanceof JsonReadFeature)) { - return _withJsonReadFeatures(features); - } int newSet = _formatReadFeatures; - int newMask = _formatReadFeaturesToChange; for (FormatFeature f : features) { - int mask = f.getMask(); - newSet |= mask; - newMask |= mask; + newSet |= f.getMask(); } - return ((_formatReadFeatures == newSet) && (_formatReadFeaturesToChange == newMask)) ? this : - new DeserializationConfig(this, _mapperFeatures, _deserFeatures, - _parserFeatures, _parserFeaturesToChange, - newSet, newMask); + return (_formatReadFeatures == newSet) ? this + : new DeserializationConfig(this, + _deserFeatures, _streamReadFeatures, newSet); } - + /** * Fluent factory method that will construct and return a new configuration * object instance with specified feature disabled. - * - * @since 2.7 */ public DeserializationConfig without(FormatFeature feature) { - // 08-Oct-2018, tatu: Alas, complexity due to newly (2.10) refactored json-features: - if (feature instanceof JsonReadFeature) { - return _withoutJsonReadFeatures(feature); - } int newSet = _formatReadFeatures & ~feature.getMask(); - int newMask = _formatReadFeaturesToChange | feature.getMask(); - return ((_formatReadFeatures == newSet) && (_formatReadFeaturesToChange == newMask)) ? this : - new DeserializationConfig(this, _mapperFeatures, _deserFeatures, - _parserFeatures, _parserFeaturesToChange, - newSet, newMask); + return (_formatReadFeatures == newSet) ? this + : new DeserializationConfig(this, + _deserFeatures, _streamReadFeatures, newSet); } /** * Fluent factory method that will construct and return a new configuration * object instance with specified features disabled. - * - * @since 2.7 */ public DeserializationConfig withoutFeatures(FormatFeature... features) { - // 08-Oct-2018, tatu: Alas, complexity due to newly (2.10) refactored json-features: - if (features.length > 0 && (features[0] instanceof JsonReadFeature)) { - return _withoutJsonReadFeatures(features); - } - int newSet = _formatReadFeatures; - int newMask = _formatReadFeaturesToChange; - for (FormatFeature f : features) { - int mask = f.getMask(); - newSet &= ~mask; - newMask |= mask; - } - return ((_formatReadFeatures == newSet) && (_formatReadFeaturesToChange == newMask)) ? this : - new DeserializationConfig(this, _mapperFeatures, _deserFeatures, - _parserFeatures, _parserFeaturesToChange, - newSet, newMask); - } - - // temporary for 2.10 - private DeserializationConfig _withJsonReadFeatures(FormatFeature... features) { - int parserSet = _parserFeatures; - int parserMask = _parserFeaturesToChange; int newSet = _formatReadFeatures; - int newMask = _formatReadFeaturesToChange; for (FormatFeature f : features) { - final int mask = f.getMask(); - newSet |= mask; - newMask |= mask; - - if (f instanceof JsonReadFeature) { - JsonParser.Feature oldF = ((JsonReadFeature) f).mappedFeature(); - if (oldF != null) { - final int pmask = oldF.getMask(); - parserSet |= pmask; - parserMask |= pmask; - } - } - } - return ((_formatReadFeatures == newSet) && (_formatReadFeaturesToChange == newMask) - && (_parserFeatures == parserSet) && (_parserFeaturesToChange == parserMask) - ) ? this : - new DeserializationConfig(this, _mapperFeatures, _deserFeatures, - parserSet, parserMask, newSet, newMask); - } - - // temporary for 2.10 - private DeserializationConfig _withoutJsonReadFeatures(FormatFeature... features) { - int parserSet = _parserFeatures; - int parserMask = _parserFeaturesToChange; - int newSet = _formatReadFeatures; - int newMask = _formatReadFeaturesToChange; - for (FormatFeature f : features) { - final int mask = f.getMask(); - newSet &= ~mask; - newMask |= mask; - - if (f instanceof JsonReadFeature) { - JsonParser.Feature oldF = ((JsonReadFeature) f).mappedFeature(); - if (oldF != null) { - final int pmask = oldF.getMask(); - parserSet &= ~pmask; - parserMask |= pmask; - } - } + newSet &= ~f.getMask(); } - return ((_formatReadFeatures == newSet) && (_formatReadFeaturesToChange == newMask) - && (_parserFeatures == parserSet) && (_parserFeaturesToChange == parserMask) - ) ? this : - new DeserializationConfig(this, _mapperFeatures, _deserFeatures, - parserSet, parserMask, newSet, newMask); + return (_formatReadFeatures == newSet) ? this + : new DeserializationConfig(this, + _deserFeatures, _streamReadFeatures, newSet); } /* - /********************************************************** + /********************************************************************** /* Life-cycle, deserialization-specific factory methods - /********************************************************** - */ - - /** - * Fluent factory method that will construct a new instance with - * specified {@link JsonNodeFactory} + /********************************************************************** */ - public DeserializationConfig with(JsonNodeFactory f) { - if (_nodeFactory == f) { - return this; - } - return new DeserializationConfig(this, f); - } /** * Method that can be used to add a handler that can (try to) @@ -658,11 +430,10 @@ public DeserializationConfig with(JsonNodeFactory f) { public DeserializationConfig withHandler(DeserializationProblemHandler h) { // Sanity check: let's prevent adding same handler multiple times - if (LinkedNode.contains(_problemHandlers, h)) { - return this; - } - return new DeserializationConfig(this, - new LinkedNode(h, _problemHandlers)); + return LinkedNode.contains(_problemHandlers, h) ? this + : new DeserializationConfig(this, + new LinkedNode(h, _problemHandlers), + _abstractTypeResolvers); } /** @@ -670,39 +441,35 @@ public DeserializationConfig withHandler(DeserializationProblemHandler h) * existing handler(s) with different one(s) */ public DeserializationConfig withNoProblemHandlers() { - if (_problemHandlers == null) { - return this; - } - return new DeserializationConfig(this, - (LinkedNode) null); + return (_problemHandlers == null) ? this + : new DeserializationConfig(this, + (LinkedNode) null, _abstractTypeResolvers); } /* - /********************************************************** - /* JsonParser initialization - /********************************************************** + /********************************************************************** + /* Support for ObjectReadContext + /********************************************************************** */ /** - * Method called by {@link ObjectMapper} and {@link ObjectReader} - * to modify those {@link com.fasterxml.jackson.core.JsonParser.Feature} settings - * that have been configured via this config instance. - * - * @since 2.5 + * @since 3.0 */ - public void initialize(JsonParser p) { - if (_parserFeaturesToChange != 0) { - p.overrideStdFeatures(_parserFeatures, _parserFeaturesToChange); - } - if (_formatReadFeaturesToChange != 0) { - p.overrideFormatFeatures(_formatReadFeatures, _formatReadFeaturesToChange); - } + public int getStreamReadFeatures() { + return _streamReadFeatures; + } + + /** + * @since 3.0 + */ + public int getFormatReadFeatures() { + return _formatReadFeatures; } /* - /********************************************************** + /********************************************************************** /* MapperConfig implementation/overrides: other - /********************************************************** + /********************************************************************** */ @Override @@ -718,19 +485,17 @@ public final boolean isEnabled(DeserializationFeature f) { return (_deserFeatures & f.getMask()) != 0; } - public final boolean isEnabled(JsonParser.Feature f, JsonFactory factory) { - int mask = f.getMask(); - if ((_parserFeaturesToChange & mask) != 0) { - return (_parserFeatures & f.getMask()) != 0; - } - return factory.isEnabled(f); + public final boolean isEnabled(StreamReadFeature f) { + return (_streamReadFeatures & f.getMask()) != 0; } + public final boolean hasFormatFeature(FormatFeature f) { + return (_formatReadFeatures & f.getMask()) != 0; + } + /** * Bulk access method for checking that all features specified by * mask are enabled. - * - * @since 2.3 */ public final boolean hasDeserializationFeatures(int featureMask) { return (_deserFeatures & featureMask) == featureMask; @@ -739,8 +504,6 @@ public final boolean hasDeserializationFeatures(int featureMask) { /** * Bulk access method for checking that at least one of features specified by * mask is enabled. - * - * @since 2.6 */ public final boolean hasSomeOfFeatures(int featureMask) { return (_deserFeatures & featureMask) != 0; @@ -755,21 +518,78 @@ public final int getDeserializationFeatures() { } /** - * Convenience method equivalant to: + * Convenience method equivalent to: * * isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS) * - * - * @since 2.9 */ public final boolean requiresFullValue() { return DeserializationFeature.FAIL_ON_TRAILING_TOKENS.enabledIn(_deserFeatures); } /* - /********************************************************** + /********************************************************************** + /* Abstract type mapping + /********************************************************************** + */ + + /** + * @since 3.0 + */ + public boolean hasAbstractTypeResolvers() { return _abstractTypeResolvers.length > 0; } + + /** + * @since 3.0 + */ + public Iterable abstractTypeResolvers() { + return new ArrayIterator(_abstractTypeResolvers); + } + + /** + * @since 3.0 + */ + public JavaType mapAbstractType(JavaType type) + { + if (!hasAbstractTypeResolvers()) { + return type; + } + // first, general mappings + while (true) { + JavaType next = _mapAbstractType2(type); + if (next == null) { + return type; + } + // Should not have to worry about cycles; but better verify since they will invariably occur... :-) + // (also: guard against invalid resolution to a non-related type) + Class prevCls = type.getRawClass(); + Class nextCls = next.getRawClass(); + if ((prevCls == nextCls) || !prevCls.isAssignableFrom(nextCls)) { + throw new IllegalArgumentException("Invalid abstract type resolution from "+type+" to "+next+": latter is not a subtype of former"); + } + type = next; + } + } + + /** + * Method that will find abstract type mapping for specified type, doing a single + * lookup through registered abstract type resolvers; will not do recursive lookups. + */ + private JavaType _mapAbstractType2(JavaType type) + { + Class currClass = type.getRawClass(); + for (AbstractTypeResolver resolver : abstractTypeResolvers()) { + JavaType concrete = resolver.findTypeMapping(this, type); + if ((concrete != null) && !concrete.hasRawClass(currClass)) { + return concrete; + } + } + return null; + } + + /* + /********************************************************************** /* Other configuration - /********************************************************** + /********************************************************************** */ /** @@ -779,15 +599,11 @@ public final boolean requiresFullValue() { public LinkedNode getProblemHandlers() { return _problemHandlers; } - - public final JsonNodeFactory getNodeFactory() { - return _nodeFactory; - } - + /* - /********************************************************** + /********************************************************************** /* Introspection methods - /********************************************************** + /********************************************************************** */ /** @@ -796,60 +612,19 @@ public final JsonNodeFactory getNodeFactory() { * * @param type Type of class to be introspected */ - @SuppressWarnings("unchecked") - public T introspect(JavaType type) { - return (T) getClassIntrospector().forDeserialization(this, type, this); + public BeanDescription introspect(JavaType type) { + return getClassIntrospector().forDeserialization(this, type, this); } /** * Method that will introspect subset of bean properties needed to * construct bean instance. */ - @SuppressWarnings("unchecked") - public T introspectForCreation(JavaType type) { - return (T) getClassIntrospector().forCreation(this, type, this); + public BeanDescription introspectForCreation(JavaType type) { + return getClassIntrospector().forCreation(this, type, this); } - /** - * @since 2.0 - */ - @SuppressWarnings("unchecked") - public T introspectForBuilder(JavaType type) { - return (T) getClassIntrospector().forDeserializationWithBuilder(this, type, this); - } - - /* - /********************************************************** - /* Support for polymorphic type handling - /********************************************************** - */ - - /** - * Helper method that is needed to properly handle polymorphic referenced - * types, such as types referenced by {@link java.util.concurrent.atomic.AtomicReference}, - * or various "optional" types. - * - * @since 2.4 - */ - public TypeDeserializer findTypeDeserializer(JavaType baseType) - throws JsonMappingException - { - BeanDescription bean = introspectClassAnnotations(baseType.getRawClass()); - AnnotatedClass ac = bean.getClassInfo(); - TypeResolverBuilder b = getAnnotationIntrospector().findTypeResolver(this, ac, baseType); - - /* Ok: if there is no explicit type info handler, we may want to - * use a default. If so, config object knows what to use. - */ - Collection subtypes = null; - if (b == null) { - b = getDefaultTyper(baseType); - if (b == null) { - return null; - } - } else { - subtypes = getSubtypeResolver().collectAndResolveSubtypesByTypeId(this, ac); - } - return b.buildTypeDeserializer(this, baseType, subtypes); + public BeanDescription introspectForBuilder(JavaType type) { + return getClassIntrospector().forDeserializationWithBuilder(this, type, this); } } diff --git a/src/main/java/com/fasterxml/jackson/databind/DeserializationContext.java b/src/main/java/com/fasterxml/jackson/databind/DeserializationContext.java index 040ab092ed..9e00ee4624 100644 --- a/src/main/java/com/fasterxml/jackson/databind/DeserializationContext.java +++ b/src/main/java/com/fasterxml/jackson/databind/DeserializationContext.java @@ -4,14 +4,16 @@ import java.text.DateFormat; import java.text.ParseException; import java.util.*; -import java.util.concurrent.atomic.AtomicReference; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.ObjectIdGenerator; import com.fasterxml.jackson.annotation.ObjectIdResolver; import com.fasterxml.jackson.core.*; - +import com.fasterxml.jackson.core.tree.ArrayTreeNode; +import com.fasterxml.jackson.core.tree.ObjectTreeNode; +import com.fasterxml.jackson.core.type.ResolvedType; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.cfg.ContextAttributes; import com.fasterxml.jackson.databind.deser.*; import com.fasterxml.jackson.databind.deser.impl.ObjectIdReader; @@ -24,6 +26,7 @@ import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException; import com.fasterxml.jackson.databind.exc.ValueInstantiationException; import com.fasterxml.jackson.databind.introspect.Annotated; +import com.fasterxml.jackson.databind.introspect.AnnotatedMember; import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; @@ -35,88 +38,87 @@ * Context for the process of deserialization a single root-level value. * Used to allow passing in configuration settings and reusable temporary * objects (scrap arrays, containers). - *

- * Instance life-cycle is such that a partially configured "blueprint" object - * is registered with {@link ObjectMapper} (and {@link ObjectReader}, - * and when actual instance is needed for deserialization, - * a fully configured instance will be created using a method in extended internal - * API of sub-class - * ({@link com.fasterxml.jackson.databind.deser.DefaultDeserializationContext#createInstance}). - * Each instance is guaranteed to only be used from single-threaded context; - * instances may be reused if (and only if) no configuration has changed. - *

- * Defined as abstract class so that implementations must define methods - * for reconfiguring blueprints and creating instances. + * Constructed by {@link ObjectMapper} (and {@link ObjectReader} based on + * configuration, + * used mostly by {@link JsonDeserializer}s to access contextual information. */ public abstract class DeserializationContext extends DatabindContext - implements java.io.Serializable + implements ObjectReadContext // 3.0 { - private static final long serialVersionUID = 1L; // 2.6 - /* - /********************************************************** - /* Configuration, immutable - /********************************************************** - */ - - /** - * Object that handle details of {@link JsonDeserializer} caching. + /********************************************************************** + /* Per-mapper configuration (immutable via ObjectReader) + /********************************************************************** */ - protected final DeserializerCache _cache; - /* - /********************************************************** - /* Configuration, changeable via fluent factories - /********************************************************** + /** + * Low-level {@link TokenStreamFactory} that may be used for constructing + * embedded parsers. */ + final protected TokenStreamFactory _streamFactory; /** * Read-only factory instance; exposed to let * owners (ObjectMapper, ObjectReader) * access it. */ - protected final DeserializerFactory _factory; + final protected DeserializerFactory _factory; + + /** + * Object that handle details of {@link JsonDeserializer} caching. + */ + final protected DeserializerCache _cache; /* - /********************************************************** - /* Configuration that gets set for instances (not blueprints) - /* (partly denormalized for performance) - /********************************************************** + /********************************************************************** + /* Configuration that may vary by ObjectReader + /********************************************************************** */ /** * Generic deserialization processing configuration */ - protected final DeserializationConfig _config; + final protected DeserializationConfig _config; /** * Bitmap of {@link DeserializationFeature}s that are enabled */ - protected final int _featureFlags; + final protected int _featureFlags; /** * Currently active view, if any. */ - protected final Class _view; + final protected Class _activeView; /** - * Currently active parser used for deserialization. - * May be different from the outermost parser - * when content is buffered. + * Schema for underlying parser to use, if any. */ - protected transient JsonParser _parser; - + final protected FormatSchema _schema; + /** * Object used for resolving references to injectable * values. */ - protected final InjectableValues _injectableValues; - + final protected InjectableValues _injectableValues; + /* - /********************************************************** + /********************************************************************** + /* Other State + /********************************************************************** + */ + + /** + * Currently active parser used for deserialization. + * May be different from the outermost parser + * when content is buffered. + */ + protected transient JsonParser _parser; + + /* + /********************************************************************** /* Per-operation reusable helper objects (not for blueprints) - /********************************************************** + /********************************************************************** */ protected transient ArrayBuilders _arrayBuilders; @@ -127,104 +129,50 @@ public abstract class DeserializationContext /** * Lazily-constructed holder for per-call attributes. - * - * @since 2.3 */ protected transient ContextAttributes _attributes; /** - * Type of {@link JsonDeserializer} (or, more specifically, - * {@link ContextualDeserializer}) that is being - * contextualized currently. - * - * @since 2.5 + * Type of {@link JsonDeserializer} on which {@link JsonDeserializer#createContextual} + * is being called currently. */ protected LinkedNode _currentType; - + /* - /********************************************************** + /********************************************************************** /* Life-cycle - /********************************************************** + /********************************************************************** */ - protected DeserializationContext(DeserializerFactory df) { - this(df, null); - } - - protected DeserializationContext(DeserializerFactory df, - DeserializerCache cache) + protected DeserializationContext(TokenStreamFactory streamFactory, + DeserializerFactory df, DeserializerCache cache, + DeserializationConfig config, FormatSchema schema, + InjectableValues injectableValues) { - if (df == null) { - throw new IllegalArgumentException("Cannot pass null DeserializerFactory"); - } + _streamFactory = streamFactory; _factory = df; - if (cache == null) { - cache = new DeserializerCache(); - } _cache = cache; - _featureFlags = 0; - _config = null; - _injectableValues = null; - _view = null; - _attributes = null; - } - - protected DeserializationContext(DeserializationContext src, - DeserializerFactory factory) - { - _cache = src._cache; - _factory = factory; - - _config = src._config; - _featureFlags = src._featureFlags; - _view = src._view; - _parser = src._parser; - _injectableValues = src._injectableValues; - _attributes = src._attributes; - } - /** - * Constructor used for creating actual per-call instances. - */ - protected DeserializationContext(DeserializationContext src, - DeserializationConfig config, JsonParser p, - InjectableValues injectableValues) - { - _cache = src._cache; - _factory = src._factory; - _config = config; _featureFlags = config.getDeserializationFeatures(); - _view = config.getActiveView(); - _parser = p; + _activeView = config.getActiveView(); + _schema = schema; + _injectableValues = injectableValues; _attributes = config.getAttributes(); } - /** - * Copy-constructor for use with copy() by {@link ObjectMapper#copy()} - */ - protected DeserializationContext(DeserializationContext src) { - _cache = new DeserializerCache(); - _factory = src._factory; - - _config = src._config; - _featureFlags = src._featureFlags; - _view = src._view; - _injectableValues = null; - } - /* - /********************************************************** + /********************************************************************** /* DatabindContext implementation - /********************************************************** + /********************************************************************** */ @Override public DeserializationConfig getConfig() { return _config; } @Override - public final Class getActiveView() { return _view; } + public final Class getActiveView() { return _activeView; } @Override public final boolean canOverrideAccessModifiers() { @@ -274,9 +222,9 @@ public TimeZone getTimeZone() { } /* - /********************************************************** - /* Access to per-call state, like generic attributes (2.3+) - /********************************************************** + /********************************************************************** + /* Access to per-call state, like generic attributes + /********************************************************************** */ @Override @@ -293,15 +241,13 @@ public DeserializationContext setAttribute(Object key, Object value) /** * Accessor to {@link JavaType} of currently contextualized - * {@link ContextualDeserializer}, if any. + * {@link JsonDeserializer}, if any. * This is sometimes useful for generic {@link JsonDeserializer}s that * do not get passed (or do not retain) type information when being * constructed: happens for example for deserializers constructed * from annotations. - * - * @since 2.5 * - * @return Type of {@link ContextualDeserializer} being contextualized, + * @return Type of {@link JsonDeserializer} being contextualized, * if process is on-going; null if not. */ public JavaType getContextualType() { @@ -309,9 +255,113 @@ public JavaType getContextualType() { } /* - /********************************************************** + /********************************************************************** + /* ObjectReadContext impl, config access + /********************************************************************** + */ + + @Override + public TokenStreamFactory getParserFactory() { + return _streamFactory; + } + + @Override + public FormatSchema getSchema() { + return _schema; + } + + @Override + public int getStreamReadFeatures(int defaults) { + return _config.getStreamReadFeatures(); + } + + @Override + public int getFormatReadFeatures(int defaults) { + return _config.getFormatReadFeatures(); + } + + /* + /********************************************************************** + /* ObjectReadContext impl, Tree creation + /********************************************************************** + */ + + @Override + public ArrayTreeNode createArrayNode() { + return getNodeFactory().arrayNode(); + } + + @Override + public ObjectTreeNode createObjectNode() { + return getNodeFactory().objectNode(); + } + + /* + /********************************************************************** + /* ObjectReadContext impl, databind + /********************************************************************** + */ + + @SuppressWarnings("unchecked") + @Override + public T readTree(JsonParser p) throws IOException { + // NOTE: inlined version of `_bindAsTree()` from `ObjectReader` + JsonToken t = p.currentToken(); + if (t == null) { + t = p.nextToken(); + if (t == null) { // [databind#1406]: expose end-of-input as `null` + return null; + } + } + if (t == JsonToken.VALUE_NULL) { + return (T) getNodeFactory().nullNode(); + } + JsonDeserializer deser = findRootValueDeserializer(ObjectReader.JSON_NODE_TYPE); + return (T) deser.deserialize(p, this); + } + + /** + * Convenience method that may be used by composite or container deserializers, + * for reading one-off values contained (for sequences, it is more efficient + * to actually fetch deserializer once for the whole collection). + *

+ * NOTE: when deserializing values of properties contained in composite types, + * rather use {@link #readPropertyValue(JsonParser, BeanProperty, Class)}; + * this method does not allow use of contextual annotations. + */ + @Override + public T readValue(JsonParser p, Class type) throws IOException { + return readValue(p, getTypeFactory().constructType(type)); + } + + @Override + public T readValue(JsonParser p, TypeReference refType) throws IOException { + return readValue(p, getTypeFactory().constructType(refType)); + } + + @Override + public T readValue(JsonParser p, ResolvedType type) throws IOException { + if (!(type instanceof JavaType)) { + throw new UnsupportedOperationException( +"Only support `JavaType` implementation of `ResolvedType`, not: "+type.getClass().getName()); + } + return readValue(p, (JavaType) type); + } + + @SuppressWarnings("unchecked") + public T readValue(JsonParser p, JavaType type) throws IOException { + JsonDeserializer deser = findRootValueDeserializer(type); + if (deser == null) { + reportBadDefinition(type, + "Could not find JsonDeserializer for type "+type); + } + return (T) deser.deserialize(p, this); + } + + /* + /********************************************************************** /* Public API, config setting accessors - /********************************************************** + /********************************************************************** */ /** @@ -335,8 +385,6 @@ public final boolean isEnabled(DeserializationFeature feat) { /** * Bulk access method for getting the bit mask of all {@link DeserializationFeature}s * that are enabled. - * - * @since 2.6 */ public final int getDeserializationFeatures() { return _featureFlags; @@ -406,34 +454,37 @@ public final JsonNodeFactory getNodeFactory() { } /* - /********************************************************** - /* Public API, pass-through to DeserializerCache - /********************************************************** + /********************************************************************** + /* Introspection support + /********************************************************************** */ /** - * Method for checking whether we could find a deserializer - * for given type. - * - * @param type - * @since 2.3 + * Convenience method for doing full "for serialization" introspection of specified + * type; results may be cached during lifespan of this context as well. */ - public boolean hasValueDeserializerFor(JavaType type, AtomicReference cause) { - try { - return _cache.hasValueDeserializerFor(this, _factory, type); - } catch (JsonMappingException e) { - if (cause != null) { - cause.set(e); - } - } catch (RuntimeException e) { - if (cause == null) { // earlier behavior - throw e; - } - cause.set(e); - } - return false; + public BeanDescription introspect(JavaType type) throws JsonMappingException { + return _config.introspect(type); } - + + public BeanDescription introspectClassAnnotations(JavaType type) throws JsonMappingException { + return _config.introspectClassAnnotations(type); + } + + public BeanDescription introspectForCreation(JavaType type) throws JsonMappingException{ + return _config.introspectForCreation(type); + } + + public BeanDescription introspectForBuilder(JavaType type) throws JsonMappingException { + return _config.introspectForBuilder(type); + } + + /* + /********************************************************************** + /* Public API, value deserializer access + /********************************************************************** + */ + /** * Method for finding a value deserializer, and creating a contextual * version if necessary, for value reached via specified property. @@ -454,13 +505,11 @@ public final JsonDeserializer findContextualValueDeserializer(JavaType t * performing any contextualization (unlike {@link #findContextualValueDeserializer}) * or checking for need to create a {@link TypeDeserializer} (unlike * {@link #findRootValueDeserializer(JavaType)}. - * This method is usually called from within {@link ResolvableDeserializer#resolve}, + * This method is usually called from within {@link JsonDeserializer#resolve}, * and expectation is that caller then calls either * {@link #handlePrimaryContextualization(JsonDeserializer, BeanProperty, JavaType)} or * {@link #handleSecondaryContextualization(JsonDeserializer, BeanProperty, JavaType)} at a * later point, as necessary. - * - * @since 2.5 */ public final JsonDeserializer findNonContextualValueDeserializer(JavaType type) throws JsonMappingException @@ -481,7 +530,7 @@ public final JsonDeserializer findRootValueDeserializer(JavaType type) return null; } deser = (JsonDeserializer) handleSecondaryContextualization(deser, null, type); - TypeDeserializer typeDeser = _factory.findTypeDeserializer(_config, type); + TypeDeserializer typeDeser = findTypeDeserializer(type); if (typeDeser != null) { // important: contextualize to indicate this is for root value typeDeser = typeDeser.forProperty(null); @@ -490,14 +539,92 @@ public final JsonDeserializer findRootValueDeserializer(JavaType type) return deser; } + /* + /********************************************************************** + /* Public API, value type deserializer access + /********************************************************************** + */ + /** - * Convenience method, functionally same as: - *
-     *  getDeserializerProvider().findKeyDeserializer(getConfig(), prop.getType(), prop);
-     *
+ * Method called to find and create a type information deserializer for given base type, + * if one is needed. If not needed (no polymorphic handling configured for type), + * should return null. + *

+ * Note that this method is usually only directly called for values of container (Collection, + * array, Map) types and root values, but not for bean property values. + * + * @param baseType Declared base type of the value to deserializer (actual + * deserializer type will be this type or its subtype) + * + * @return Type deserializer to use for given base type, if one is needed; null if not. + */ + public TypeDeserializer findTypeDeserializer(JavaType baseType) + throws JsonMappingException + { + return findTypeDeserializer(baseType, introspectClassAnnotations(baseType)); + } + + public TypeDeserializer findTypeDeserializer(JavaType baseType, + BeanDescription beanDesc) + throws JsonMappingException + { + return _config.getTypeResolverProvider().findTypeDeserializer(this, + baseType, beanDesc.getClassInfo()); + } + + /** + * Method called to create a type information deserializer for values of + * given non-container property, if one is needed. + * If not needed (no polymorphic handling configured for property), should return null. + *

+ * Note that this method is only called for non-container bean properties, + * and not for values in container types or root values (or container properties) + * + * @param baseType Declared base type of the value to deserializer (actual + * deserializer type will be this type or its subtype) + * + * @return Type deserializer to use for given base type, if one is needed; null if not. + * + * @since 3.0 */ + public TypeDeserializer findPropertyTypeDeserializer(JavaType baseType, + AnnotatedMember accessor) + throws JsonMappingException + { + return _config.getTypeResolverProvider().findPropertyTypeDeserializer(this, + accessor, baseType); + } + + /** + * Method called to find and create a type information deserializer for values of + * given container (list, array, map) property, if one is needed. + * If not needed (no polymorphic handling configured for property), should return null. + *

+ * Note that this method is only called for container bean properties, + * and not for values in container types or root values (or non-container properties) + * + * @param containerType Type of property; must be a container type + * @param accessor Field or method that contains container property + * + * @since 3.0 + */ + public TypeDeserializer findPropertyContentTypeDeserializer(JavaType containerType, + AnnotatedMember accessor) + throws JsonMappingException + { + return _config.getTypeResolverProvider().findPropertyContentTypeDeserializer(this, + accessor, containerType); + } + + /* + /********************************************************************** + /* Public API, key deserializer access + /********************************************************************** + */ + public final KeyDeserializer findKeyDeserializer(JavaType keyType, - BeanProperty prop) throws JsonMappingException { + BeanProperty prop) throws JsonMappingException + { KeyDeserializer kd = _cache.findKeyDeserializer(this, _factory, keyType); // Second: contextualize? @@ -506,11 +633,11 @@ public final KeyDeserializer findKeyDeserializer(JavaType keyType, } return kd; } - + /* - /********************************************************** + /********************************************************************** /* Public API, ObjectId handling - /********************************************************** + /********************************************************************** */ /** @@ -529,9 +656,9 @@ public abstract void checkUnresolvedObjectId() throws UnresolvedForwardReference; /* - /********************************************************** + /********************************************************************** /* Public API, type handling - /********************************************************** + /********************************************************************** */ /** @@ -549,8 +676,6 @@ public final JavaType constructType(Class cls) { * Class instance, the reason being that it may be necessary to work around * various ClassLoader limitations, as well as to handle primitive type * signatures. - * - * @since 2.6 */ public Class findClass(String className) throws ClassNotFoundException { @@ -559,9 +684,9 @@ public Class findClass(String className) throws ClassNotFoundException } /* - /********************************************************** + /********************************************************************** /* Public API, helper object recycling - /********************************************************** + /********************************************************************** */ /** @@ -611,9 +736,9 @@ public final ArrayBuilders getArrayBuilders() } /* - /********************************************************** + /********************************************************************** /* Extended API: handler instantiation - /********************************************************** + /********************************************************************** */ public abstract JsonDeserializer deserializerInstance(Annotated annotated, @@ -625,31 +750,28 @@ public abstract KeyDeserializer keyDeserializerInstance(Annotated annotated, throws JsonMappingException; /* - /********************************************************** + /********************************************************************** /* Extended API: resolving contextual deserializers; called - /* by structured deserializers for their value/component - /* deserializers - /********************************************************** + /* by structured deserializers for their value/component deserializers + /********************************************************************** */ /** * Method called for primary property deserializers (ones * directly created to deserialize values of a POJO property), - * to handle details of resolving - * {@link ContextualDeserializer} with given property context. + * to handle details of calling + * {@link JsonDeserializer#createContextual} with given property context. * * @param prop Property for which the given primary deserializer is used; never null. - * - * @since 2.5 */ public JsonDeserializer handlePrimaryContextualization(JsonDeserializer deser, BeanProperty prop, JavaType type) throws JsonMappingException { - if (deser instanceof ContextualDeserializer) { + if (deser != null) { _currentType = new LinkedNode(type, _currentType); try { - deser = ((ContextualDeserializer) deser).createContextual(this, prop); + deser = deser.createContextual(this, prop); } finally { _currentType = _currentType.next(); } @@ -663,24 +785,22 @@ public JsonDeserializer handlePrimaryContextualization(JsonDeserializer de * but instead created as a component -- such as value deserializers * for structured types, or deserializers for root values) * to handle details of resolving - * {@link ContextualDeserializer} with given property context. + * {@link JsonDeserializer#createContextual} with given property context. * Given that these deserializers are not directly related to given property * (or, in case of root value property, to any property), annotations * accessible may or may not be relevant. * * @param prop Property for which deserializer is used, if any; null * when deserializing root values - * - * @since 2.5 */ public JsonDeserializer handleSecondaryContextualization(JsonDeserializer deser, BeanProperty prop, JavaType type) throws JsonMappingException { - if (deser instanceof ContextualDeserializer) { + if (deser != null) { _currentType = new LinkedNode(type, _currentType); try { - deser = ((ContextualDeserializer) deser).createContextual(this, prop); + deser =deser.createContextual(this, prop); } finally { _currentType = _currentType.next(); } @@ -689,9 +809,9 @@ public JsonDeserializer handleSecondaryContextualization(JsonDeserializer } /* - /********************************************************** + /********************************************************************** /* Parsing methods that may use reusable/-cyclable objects - /********************************************************** + /********************************************************************** */ /** @@ -728,54 +848,21 @@ public Calendar constructCalendar(Date d) { } /* - /********************************************************** + /********************************************************************** /* Convenience methods for reading parsed values - /********************************************************** + /********************************************************************** */ - - /** - * Convenience method that may be used by composite or container deserializers, - * for reading one-off values contained (for sequences, it is more efficient - * to actually fetch deserializer once for the whole collection). - *

- * NOTE: when deserializing values of properties contained in composite types, - * rather use {@link #readPropertyValue(JsonParser, BeanProperty, Class)}; - * this method does not allow use of contextual annotations. - * - * @since 2.4 - */ - public T readValue(JsonParser p, Class type) throws IOException { - return readValue(p, getTypeFactory().constructType(type)); - } - - /** - * @since 2.4 - */ - @SuppressWarnings("unchecked") - public T readValue(JsonParser p, JavaType type) throws IOException { - JsonDeserializer deser = findRootValueDeserializer(type); - if (deser == null) { - reportBadDefinition(type, - "Could not find JsonDeserializer for type "+type); - } - return (T) deser.deserialize(p, this); - } - + /** * Convenience method that may be used by composite or container deserializers, * for reading one-off values for the composite type, taking into account * annotations that the property (passed to this method -- usually property that * has custom serializer that called this method) has. - * - * @since 2.4 */ public T readPropertyValue(JsonParser p, BeanProperty prop, Class type) throws IOException { return readPropertyValue(p, prop, getTypeFactory().constructType(type)); } - /** - * @since 2.4 - */ @SuppressWarnings("unchecked") public T readPropertyValue(JsonParser p, BeanProperty prop, JavaType type) throws IOException { JsonDeserializer deser = findContextualValueDeserializer(type, prop); @@ -788,9 +875,9 @@ public T readPropertyValue(JsonParser p, BeanProperty prop, JavaType type) t } /* - /********************************************************** + /********************************************************************** /* Methods for problem handling - /********************************************************** + /********************************************************************** */ /** @@ -841,8 +928,6 @@ public boolean handleUnknownProperty(JsonParser p, JsonDeserializer deser, * @return Key value to use * * @throws IOException To indicate unrecoverable problem, usually based on msg - * - * @since 2.8 */ public Object handleWeirdKey(Class keyClass, String keyValue, String msg, Object... msgArgs) @@ -885,8 +970,6 @@ public Object handleWeirdKey(Class keyClass, String keyValue, * @return Property value to use * * @throws IOException To indicate unrecoverable problem, usually based on msg - * - * @since 2.8 */ public Object handleWeirdStringValue(Class targetClass, String value, String msg, Object... msgArgs) @@ -929,8 +1012,6 @@ public Object handleWeirdStringValue(Class targetClass, String value, * @return Property value to use * * @throws IOException To indicate unrecoverable problem, usually based on msg - * - * @since 2.8 */ public Object handleWeirdNumberValue(Class targetClass, Number value, String msg, Object... msgArgs) @@ -991,8 +1072,6 @@ public Object handleWeirdNativeValue(JavaType targetType, Object badValue, * @param p Parser that points to the JSON value to decode * * @return Object that should be constructed, if any; has to be of type instClass - * - * @since 2.9 (2.8 had alternate that did not take ValueInstantiator) */ @SuppressWarnings("resource") public Object handleMissingInstantiator(Class instClass, ValueInstantiator valueInst, @@ -1024,7 +1103,7 @@ public Object handleMissingInstantiator(Class instClass, ValueInstantiator va // exists), or input mismatch problem (otherwise) since none of existing creators // match with token. if ((valueInst != null) && !valueInst.canInstantiate()) { - msg = String.format("Cannot construct instance of %s (no Creators, like default construct, exist): %s", + msg = String.format("Cannot construct instance of %s (no Creators, like default constructor, exist): %s", ClassUtil.nameOf(instClass), msg); return reportBadDefinition(constructType(instClass), msg); } @@ -1047,8 +1126,6 @@ public Object handleMissingInstantiator(Class instClass, ValueInstantiator va * @param t Exception that caused failure * * @return Object that should be constructed, if any; has to be of type instClass - * - * @since 2.8 */ public Object handleInstantiationProblem(Class instClass, Object argument, Throwable t) @@ -1085,13 +1162,11 @@ public Object handleInstantiationProblem(Class instClass, Object argument, * @param p Parser that points to the JSON value to decode * * @return Object that should be constructed, if any; has to be of type instClass - * - * @since 2.8 */ public Object handleUnexpectedToken(Class instClass, JsonParser p) throws IOException { - return handleUnexpectedToken(instClass, p.getCurrentToken(), p, null); + return handleUnexpectedToken(instClass, p.currentToken(), p, null); } /** @@ -1106,8 +1181,6 @@ public Object handleUnexpectedToken(Class instClass, JsonParser p) * @param p Parser that points to the JSON value to decode * * @return Object that should be constructed, if any; has to be of type instClass - * - * @since 2.8 */ public Object handleUnexpectedToken(Class instClass, JsonToken t, JsonParser p, String msg, Object... msgArgs) @@ -1158,8 +1231,6 @@ public Object handleUnexpectedToken(Class instClass, JsonToken t, * * @throws IOException To indicate unrecoverable problem, if resolution cannot * be made to work - * - * @since 2.8 */ public JavaType handleUnknownTypeId(JavaType baseType, String id, TypeIdResolver idResolver, String extraDesc) throws IOException @@ -1188,9 +1259,6 @@ public JavaType handleUnknownTypeId(JavaType baseType, String id, throw invalidTypeIdException(baseType, id, extraDesc); } - /** - * @since 2.9 - */ public JavaType handleMissingTypeId(JavaType baseType, TypeIdResolver idResolver, String extraDesc) throws IOException { @@ -1219,9 +1287,6 @@ public JavaType handleMissingTypeId(JavaType baseType, throw missingTypeIdException(baseType, extraDesc); } - /** - * @since 2.9.2 - */ protected boolean _isCompatible(Class target, Object value) { if ((value == null) || target.isInstance(value)) { @@ -1233,10 +1298,10 @@ protected boolean _isCompatible(Class target, Object value) } /* - /********************************************************** + /********************************************************************** /* Methods for problem reporting, in cases where recovery /* is not considered possible: input problem - /********************************************************** + /********************************************************************** */ /** @@ -1246,8 +1311,6 @@ protected boolean _isCompatible(Class target, Object value) * Note that this method will throw a {@link JsonMappingException} and no * recovery is attempted (via {@link DeserializationProblemHandler}, as * problem is considered to be difficult to recover from, in general. - * - * @since 2.9 */ public void reportWrongTokenException(JsonDeserializer deser, JsonToken expToken, String msg, Object... msgArgs) @@ -1264,8 +1327,6 @@ public void reportWrongTokenException(JsonDeserializer deser, * Note that this method will throw a {@link JsonMappingException} and no * recovery is attempted (via {@link DeserializationProblemHandler}, as * problem is considered to be difficult to recover from, in general. - * - * @since 2.9 */ public void reportWrongTokenException(JavaType targetType, JsonToken expToken, String msg, Object... msgArgs) @@ -1282,8 +1343,6 @@ public void reportWrongTokenException(JavaType targetType, * Note that this method will throw a {@link JsonMappingException} and no * recovery is attempted (via {@link DeserializationProblemHandler}, as * problem is considered to be difficult to recover from, in general. - * - * @since 2.9 */ public void reportWrongTokenException(Class targetType, JsonToken expToken, String msg, Object... msgArgs) @@ -1293,9 +1352,6 @@ public void reportWrongTokenException(Class targetType, throw wrongTokenException(getParser(), targetType, expToken, msg); } - /** - * @since 2.8 - */ public T reportUnresolvedObjectId(ObjectIdReader oidReader, Object bean) throws JsonMappingException { @@ -1307,8 +1363,6 @@ public T reportUnresolvedObjectId(ObjectIdReader oidReader, Object bean) /** * Helper method used to indicate a problem with input in cases where more * specific reportXxx() method was not available. - * - * @since 2.9 */ public T reportInputMismatch(BeanProperty prop, String msg, Object... msgArgs) throws JsonMappingException @@ -1321,8 +1375,6 @@ public T reportInputMismatch(BeanProperty prop, /** * Helper method used to indicate a problem with input in cases where more * specific reportXxx() method was not available. - * - * @since 2.9 */ public T reportInputMismatch(JsonDeserializer src, String msg, Object... msgArgs) throws JsonMappingException @@ -1334,8 +1386,6 @@ public T reportInputMismatch(JsonDeserializer src, /** * Helper method used to indicate a problem with input in cases where more * specific reportXxx() method was not available. - * - * @since 2.9 */ public T reportInputMismatch(Class targetType, String msg, Object... msgArgs) throws JsonMappingException @@ -1347,8 +1397,6 @@ public T reportInputMismatch(Class targetType, /** * Helper method used to indicate a problem with input in cases where more * specific reportXxx() method was not available. - * - * @since 2.9 */ public T reportInputMismatch(JavaType targetType, String msg, Object... msgArgs) throws JsonMappingException @@ -1366,49 +1414,6 @@ public T reportTrailingTokens(Class targetType, )); } - @Deprecated // since 2.9 - public void reportWrongTokenException(JsonParser p, - JsonToken expToken, String msg, Object... msgArgs) - throws JsonMappingException - { - msg = _format(msg, msgArgs); - throw wrongTokenException(p, expToken, msg); - } - - /** - * Helper method for reporting a problem with unhandled unknown property. - * - * @param instanceOrClass Either value being populated (if one has been - * instantiated), or Class that indicates type that would be (or - * have been) instantiated - * @param deser Deserializer that had the problem, if called by deserializer - * (or on behalf of one) - * - * @deprecated Since 2.8 call {@link #handleUnknownProperty} instead - */ - @Deprecated - public void reportUnknownProperty(Object instanceOrClass, String fieldName, - JsonDeserializer deser) - throws JsonMappingException - { - if (isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)) { - // Do we know properties that are expected instead? - Collection propIds = (deser == null) ? null : deser.getKnownPropertyNames(); - throw UnrecognizedPropertyException.from(_parser, - instanceOrClass, fieldName, propIds); - } - } - - /** - * @since 2.8 - * - * @deprecated Since 2.9: not clear this ever occurs - */ - @Deprecated // since 2.9 - public void reportMissingContent(String msg, Object... msgArgs) throws JsonMappingException { - throw MismatchedInputException.from(getParser(), (JavaType) null, "No content to map due to end-of-input"); - } - /* /********************************************************** /* Methods for problem reporting, in cases where recovery @@ -1420,8 +1425,6 @@ public void reportMissingContent(String msg, Object... msgArgs) throws JsonMappi * Helper method called to indicate problem in POJO (serialization) definitions or settings * regarding specific Java type, unrelated to actual JSON content to map. * Default behavior is to construct and throw a {@link JsonMappingException}. - * - * @since 2.9 */ public T reportBadTypeDefinition(BeanDescription bean, String msg, Object... msgArgs) throws JsonMappingException { @@ -1435,8 +1438,6 @@ public T reportBadTypeDefinition(BeanDescription bean, * Helper method called to indicate problem in POJO (serialization) definitions or settings * regarding specific property (of a type), unrelated to actual JSON content to map. * Default behavior is to construct and throw a {@link JsonMappingException}. - * - * @since 2.9 */ public T reportBadPropertyDefinition(BeanDescription bean, BeanPropertyDefinition prop, String msg, Object... msgArgs) throws JsonMappingException { @@ -1461,8 +1462,6 @@ public T reportBadDefinition(JavaType type, String msg) throws JsonMappingEx * Note that if {@link MapperFeature#IGNORE_MERGE_FOR_UNMERGEABLE} is enabled, * this method will simply return null; otherwise {@link InvalidDefinitionException} * will be thrown. - * - * @since 2.9 */ public T reportBadMerge(JsonDeserializer deser) throws JsonMappingException { @@ -1475,10 +1474,10 @@ public T reportBadMerge(JsonDeserializer deser) throws JsonMappingExcepti } /* - /********************************************************** + /********************************************************************** /* Methods for constructing semantic exceptions; usually not /* to be called directly, call `handleXxx()` instead - /********************************************************** + /********************************************************************** */ /** @@ -1488,14 +1487,12 @@ public T reportBadMerge(JsonDeserializer deser) throws JsonMappingExcepti * Note that most of the time this method should NOT be directly called; * instead, {@link #reportWrongTokenException} should be called and will * call this method as necessary. - * - * @since 2.9 */ public JsonMappingException wrongTokenException(JsonParser p, JavaType targetType, JsonToken expToken, String extra) { String msg = String.format("Unexpected token (%s), expected %s", - p.getCurrentToken(), expToken); + p.currentToken(), expToken); msg = _colonConcat(msg, extra); return MismatchedInputException.from(p, targetType, msg); } @@ -1503,18 +1500,11 @@ public JsonMappingException wrongTokenException(JsonParser p, JavaType targetTyp public JsonMappingException wrongTokenException(JsonParser p, Class targetType, JsonToken expToken, String extra) { - String msg = String.format("Unexpected token (%s), expected %s", - p.getCurrentToken(), expToken); + JsonToken t = (p == null) ? null : p.currentToken(); + String msg = String.format("Unexpected token (%s), expected %s", t, expToken); msg = _colonConcat(msg, extra); return MismatchedInputException.from(p, targetType, msg); } - - @Deprecated // since 2.9 - public JsonMappingException wrongTokenException(JsonParser p, JsonToken expToken, - String msg) - { - return wrongTokenException(p, (JavaType) null, expToken, msg); - } /** * Helper method for constructing exception to indicate that given JSON @@ -1542,8 +1532,6 @@ public JsonMappingException weirdKeyException(Class keyClass, String keyValue * @param value String value from input being deserialized * @param instClass Type that String should be deserialized into * @param msgBase Message that describes specific problem - * - * @since 2.1 */ public JsonMappingException weirdStringException(String value, Class instClass, String msgBase) { @@ -1574,8 +1562,6 @@ public JsonMappingException weirdNumberException(Number value, Class instClas * and can not be used to construct value of specified type (usually POJO). * Note that most of the time this method should NOT be called; instead, * {@link #handleWeirdNativeValue} should be called which will call this method - * - * @since 2.9 */ public JsonMappingException weirdNativeValueException(Object value, Class instClass) { @@ -1634,9 +1620,6 @@ public JsonMappingException invalidTypeIdException(JavaType baseType, String typ return InvalidTypeIdException.from(_parser, _colonConcat(msg, extraDesc), baseType, typeId); } - /** - * @since 2.9 - */ public JsonMappingException missingTypeIdException(JavaType baseType, String extraDesc) { String msg = String.format("Missing type id when trying to resolve subtype of %s", @@ -1645,130 +1628,27 @@ public JsonMappingException missingTypeIdException(JavaType baseType, } /* - /********************************************************** - /* Deprecated exception factory methods - /********************************************************** - */ - - /** - * @since 2.5 - * - * @deprecated Since 2.8 use {@link #handleUnknownTypeId} instead - */ - @Deprecated - public JsonMappingException unknownTypeException(JavaType type, String id, - String extraDesc) - { - String msg = String.format("Could not resolve type id '%s' into a subtype of %s", - id, type); - msg = _colonConcat(msg, extraDesc); - return MismatchedInputException.from(_parser, type, msg); - } - - /** - * Helper method for constructing exception to indicate that end-of-input was - * reached while still expecting more tokens to deserialize value of specified type. - * - * @deprecated Since 2.8; currently no way to catch EOF at databind level - */ - @Deprecated - public JsonMappingException endOfInputException(Class instClass) { - return MismatchedInputException.from(_parser, instClass, - "Unexpected end-of-input when trying to deserialize a "+instClass.getName()); - } - - /* - /********************************************************** - /* Deprecated methods for constructing, throwing non-specific - /* JsonMappingExceptions: as of 2.9, should use more specific - /* ones. - /********************************************************** - */ - - /** - * Fallback method that may be called if no other reportXxx - * is applicable -- but only in that case. - * - * @since 2.8 - * - * @deprecated Since 2.9: use a more specific method, or {@link #reportBadDefinition(JavaType, String)}, - * or {@link #reportInputMismatch} instead - */ - @Deprecated // since 2.9 - public void reportMappingException(String msg, Object... msgArgs) - throws JsonMappingException - { - throw JsonMappingException.from(getParser(), _format(msg, msgArgs)); - } - - /** - * Helper method for constructing generic mapping exception with specified - * message and current location information. - * Note that application code should almost always call - * one of handleXxx methods, or {@link #reportMappingException(String, Object...)} - * instead. - * - * @since 2.6 - * - * @deprecated Since 2.9 use more specific error reporting methods instead - */ - @Deprecated - public JsonMappingException mappingException(String message) { - return JsonMappingException.from(getParser(), message); - } - - /** - * Helper method for constructing generic mapping exception with specified - * message and current location information - * Note that application code should almost always call - * one of handleXxx methods, or {@link #reportMappingException(String, Object...)} - * instead. - * - * @since 2.6 - * - * @deprecated Since 2.9 use more specific error reporting methods instead - */ - @Deprecated - public JsonMappingException mappingException(String msg, Object... msgArgs) { - return JsonMappingException.from(getParser(), _format(msg, msgArgs)); - } - - /** - * Helper method for constructing generic mapping exception for specified type - * - * @deprecated Since 2.8 use {@link #handleUnexpectedToken(Class, JsonParser)} instead + /********************************************************************** + /* Other internal methods + /********************************************************************** */ - @Deprecated - public JsonMappingException mappingException(Class targetClass) { - return mappingException(targetClass, _parser.getCurrentToken()); - } /** - * @deprecated Since 2.8 use {@link #handleUnexpectedToken(Class, JsonParser)} instead + * Helper method to get a non-shared instance of {@link DateFormat} with default + * configuration; instance is lazily constructed, reused within same instance of + * context (that is, within same life-cycle of readValue() from mapper + * or reader). Reuse is safe since access will not occur from multiple threads + * (unless caller somehow manages to share context objects across threads which is not + * supported). */ - @Deprecated - public JsonMappingException mappingException(Class targetClass, JsonToken token) { - return JsonMappingException.from(_parser, - String.format("Cannot deserialize instance of %s out of %s token", - ClassUtil.nameOf(targetClass), token)); - } - - /* - /********************************************************** - /* Other internal methods - /********************************************************** - */ - protected DateFormat getDateFormat() { if (_dateFormat != null) { return _dateFormat; } - /* 24-Feb-2012, tatu: At this point, all timezone configuration - * should have occurred, with respect to default dateformat - * and timezone configuration. But we still better clone - * an instance as formatters may be stateful. - */ + // 24-Feb-2012, tatu: At this point, all timezone configuration should have + // occurred, with respect to default date format and time zone configuration. + // But we still better clone an instance as formatters may be stateful. DateFormat df = _config.getDateFormat(); _dateFormat = df = (DateFormat) df.clone(); return df; diff --git a/src/main/java/com/fasterxml/jackson/databind/DeserializationFeature.java b/src/main/java/com/fasterxml/jackson/databind/DeserializationFeature.java index cc05d78390..301117814b 100644 --- a/src/main/java/com/fasterxml/jackson/databind/DeserializationFeature.java +++ b/src/main/java/com/fasterxml/jackson/databind/DeserializationFeature.java @@ -20,9 +20,9 @@ public enum DeserializationFeature implements ConfigFeature { /* - /****************************************************** - /* Value (mostly scalar) conversion features - /****************************************************** + /********************************************************************** + /* Value (mostly scalar) mapping features + /********************************************************************** */ /** @@ -83,8 +83,6 @@ public enum DeserializationFeature implements ConfigFeature * Feature is disabled by default, meaning that "untyped" integral * numbers will by default be deserialized using {@link java.lang.Integer} * if value fits. - * - * @since 2.6 */ USE_LONG_FOR_INTS(false), @@ -100,9 +98,9 @@ public enum DeserializationFeature implements ConfigFeature USE_JAVA_ARRAY_FOR_JSON_ARRAY(false), /* - /****************************************************** + /********************************************************************** /* Error handling features - /****************************************************** + /********************************************************************** */ /** @@ -154,8 +152,6 @@ public enum DeserializationFeature implements ConfigFeature *

* Feature is enabled by default so that exception is thrown for missing or invalid * type information. - * - * @since 2.2 */ FAIL_ON_INVALID_SUBTYPE(true), @@ -171,8 +167,6 @@ public enum DeserializationFeature implements ConfigFeature * keys. New features may be added to control additional cases. *

* Feature is disabled by default so that no exception is thrown. - * - * @since 2.3 */ FAIL_ON_READING_DUP_TREE_KEY(false), @@ -182,8 +176,6 @@ public enum DeserializationFeature implements ConfigFeature * {@link JsonMappingException} is thrown; if false, property is quietly skipped. *

* Feature is disabled by default so that no exception is thrown. - * - * @since 2.3 */ FAIL_ON_IGNORED_PROPERTIES(false), @@ -198,8 +190,6 @@ public enum DeserializationFeature implements ConfigFeature *

* Feature is enabled by default, so that unknown Object Ids will result in an * exception being thrown, at the end of deserialization. - * - * @since 2.5 */ FAIL_ON_UNRESOLVED_OBJECT_IDS(true), @@ -219,8 +209,6 @@ public enum DeserializationFeature implements ConfigFeature *

* Feature is disabled by default, so that no exception is thrown for missing creator * property values, unless they are explicitly marked as `required`. - * - * @since 2.6 */ FAIL_ON_MISSING_CREATOR_PROPERTIES(false), @@ -232,8 +220,6 @@ public enum DeserializationFeature implements ConfigFeature * if you are using Java or Scala optionals for non-mandatory fields. * Feature is disabled by default, so that no exception is thrown for missing creator * property values, unless they are explicitly marked as `required`. - * - * @since 2.8 */ FAIL_ON_NULL_CREATOR_PROPERTIES(false), @@ -246,8 +232,6 @@ public enum DeserializationFeature implements ConfigFeature *

* Feature is enabled by default, so that exception is thrown when a subtype property is * missing. - * - * @since 2.9 */ FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY(true), @@ -266,8 +250,6 @@ public enum DeserializationFeature implements ConfigFeature *

* Feature is disabled by default (so that no check is made for possible trailing * token(s)) for backwards compatibility reasons. - * - * @since 2.9 */ FAIL_ON_TRAILING_TOKENS(false), @@ -289,9 +271,9 @@ public enum DeserializationFeature implements ConfigFeature WRAP_EXCEPTIONS(true), /* - /****************************************************** + /********************************************************************** /* Structural conversion features - /****************************************************** + /********************************************************************** */ /** @@ -314,7 +296,6 @@ public enum DeserializationFeature implements ConfigFeature *

* * Feature is disabled by default - * @since 2.4 */ UNWRAP_SINGLE_VALUE_ARRAYS(false), @@ -331,10 +312,32 @@ public enum DeserializationFeature implements ConfigFeature UNWRAP_ROOT_VALUE(false), /* - /****************************************************** - /* Value conversion features - /****************************************************** + /********************************************************************** + /* Value conversion/coercion features + /********************************************************************** */ + + /** + * Feature that determines whether coercions from secondary representations are allowed + * for simple non-textual scalar types: numbers and booleans. This includes `primitive` + * types and their wrappers, but excludes `java.lang.String` and date/time types. + *

+ * When feature is disabled, only strictly compatible input may be bound: numbers for + * numbers, boolean values for booleans. When feature is enabled, conversions from + * JSON String are allowed, as long as textual value matches (for example, String + * "true" is allowed as equivalent of JSON boolean token `true`; or String "1.0" + * for `double`). + *

+ * Note that it is possible that other configurability options can override this + * in closer scope (like on per-type or per-property basis); this is just the global + * default. + *

+ * Feature is enabled by default (for backwards compatibility since this was the + * default behavior) + * + * @since 3.0 (in 2.x was a `MapperFeature` instead) + */ + ALLOW_COERCION_OF_SCALARS(true), /** * Feature that can be enabled to allow JSON empty String @@ -348,7 +351,7 @@ public enum DeserializationFeature implements ConfigFeature *

* NOTE: this does NOT apply to scalar values such as booleans and numbers; * whether they can be coerced depends on - * {@link MapperFeature#ALLOW_COERCION_OF_SCALARS}. + * {@link #ALLOW_COERCION_OF_SCALARS}. *

* Feature is disabled by default. */ @@ -365,8 +368,6 @@ public enum DeserializationFeature implements ConfigFeature * to be equivalent of JSON null. *

* Feature is disabled by default. - * - * @since 2.5 */ ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT(false), @@ -379,8 +380,6 @@ public enum DeserializationFeature implements ConfigFeature * will be thrown. *

* Feature is enabled by default. - * - * @since 2.6 */ ACCEPT_FLOAT_AS_INT(true), @@ -407,8 +406,6 @@ public enum DeserializationFeature implements ConfigFeature * cases). *

* Feature is disabled by default. - * - * @since 2.0 */ READ_UNKNOWN_ENUM_VALUES_AS_NULL(false), @@ -419,8 +416,6 @@ public enum DeserializationFeature implements ConfigFeature * If enabled, but no predefined default Enum value is specified, an exception will be thrown as well. *

* Feature is disabled by default. - * - * @since 2.8 */ READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE(false), @@ -436,8 +431,6 @@ public enum DeserializationFeature implements ConfigFeature * This is the counterpart to {@link SerializationFeature#WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS}. *

* Feature is enabled by default, to support most accurate time values possible. - * - * @since 2.2 */ READ_DATE_TIMESTAMPS_AS_NANOSECONDS(true), @@ -460,15 +453,13 @@ public enum DeserializationFeature implements ConfigFeature *

* Taking above into account, this feature is supported only by extension modules for * Joda and Java 8 date/tyime datatypes. - * - * @since 2.2 */ ADJUST_DATES_TO_CONTEXT_TIME_ZONE(true), /* - /****************************************************** + /********************************************************************** /* Other - /****************************************************** + /********************************************************************** */ /** @@ -482,8 +473,6 @@ public enum DeserializationFeature implements ConfigFeature * feature: only consider that if there are actual perceived problems. *

* Feature is enabled by default. - * - * @since 2.1 */ EAGER_DESERIALIZER_FETCH(true) diff --git a/src/main/java/com/fasterxml/jackson/databind/InjectableValues.java b/src/main/java/com/fasterxml/jackson/databind/InjectableValues.java index 9f773c3ab8..4469aaa587 100644 --- a/src/main/java/com/fasterxml/jackson/databind/InjectableValues.java +++ b/src/main/java/com/fasterxml/jackson/databind/InjectableValues.java @@ -2,6 +2,8 @@ import java.util.*; +import com.fasterxml.jackson.core.util.Snapshottable; + import com.fasterxml.jackson.databind.util.ClassUtil; /** @@ -9,6 +11,7 @@ * "inject" during deserialization. An instance of this object */ public abstract class InjectableValues + implements Snapshottable { /** * Method called to find value identified by id valueId to @@ -46,7 +49,7 @@ public static class Std protected final Map _values; public Std() { - this(new HashMap()); + this(new HashMap<>()); } public Std(Map values) { @@ -62,7 +65,15 @@ public Std addValue(Class classKey, Object value) { _values.put(classKey.getName(), value); return this; } - + + @Override + public Std snapshot() { + if (_values.isEmpty()) { + return new Std(); + } + return new Std(new HashMap<>(_values)); + } + @Override public Object findInjectableValue(Object valueId, DeserializationContext ctxt, BeanProperty forProperty, Object beanInstance) throws JsonMappingException diff --git a/src/main/java/com/fasterxml/jackson/databind/JavaType.java b/src/main/java/com/fasterxml/jackson/databind/JavaType.java index 3fdbb2b1f2..ae1227ed9d 100644 --- a/src/main/java/com/fasterxml/jackson/databind/JavaType.java +++ b/src/main/java/com/fasterxml/jackson/databind/JavaType.java @@ -13,14 +13,11 @@ *

* Instances can (only) be constructed by * com.fasterxml.jackson.databind.type.TypeFactory. - *

- * Since 2.2 this implements {@link java.lang.reflect.Type} to allow - * it to be pushed through interfaces that only expose that type. */ public abstract class JavaType extends ResolvedType - implements java.io.Serializable, // 2.1 - java.lang.reflect.Type // 2.2 + implements java.io.Serializable, + java.lang.reflect.Type { private static final long serialVersionUID = 1; @@ -57,8 +54,6 @@ public abstract class JavaType /** * Whether entities defined with this type should be handled using * static typing (as opposed to dynamic runtime type) or not. - * - * @since 2.2 */ protected final boolean _asStatic; @@ -85,8 +80,6 @@ protected JavaType(Class raw, int additionalHash, /** * Copy-constructor used when refining/upgrading type instances. - * - * @since 2.7 */ protected JavaType(JavaType base) { @@ -134,8 +127,6 @@ protected JavaType(JavaType base) * Mutant factory method that will try to copy handlers that the specified * source type instance had, if any; this must be done recursively where * necessary (as content types may be structured). - * - * @since 2.8.4 */ public JavaType withHandlersFrom(JavaType src) { JavaType type = this; @@ -162,8 +153,6 @@ public JavaType withHandlersFrom(JavaType src) { * will be thrown. * * @return Newly created type instance - * - * @since 2.7 */ public abstract JavaType withContentType(JavaType contentType); @@ -175,8 +164,6 @@ public JavaType withHandlersFrom(JavaType src) { * The main use case is to allow forcing of specific root value serialization type, * and specifically in resolving serializers for contained types (element types * for arrays, Collections and Maps). - * - * @since 2.2 */ public abstract JavaType withStaticTyping(); @@ -191,31 +178,9 @@ public JavaType withHandlersFrom(JavaType src) { * for known parameterized types; for other types will return `null` to indicate * that no just refinement makes necessary sense, without trying to detect * special status through implemented interfaces. - * - * @since 2.7 */ public abstract JavaType refine(Class rawType, TypeBindings bindings, JavaType superClass, JavaType[] superInterfaces); - - /** - * Legacy method used for forcing sub-typing of this type into - * type specified by specific type erasure. - * Deprecated as of 2.7 as such specializations really ought to - * go through {@link TypeFactory}, not directly via {@link JavaType}. - * - * @since 2.7 - */ - @Deprecated - public JavaType forcedNarrowBy(Class subclass) - { - if (subclass == _class) { // can still optimize for simple case - return this; - } - return _narrow(subclass); - } - - @Deprecated // since 2.7 - protected abstract JavaType _narrow(Class subclass); /* /********************************************************** @@ -238,23 +203,15 @@ public JavaType forcedNarrowBy(Class subclass) * Accessor that allows determining whether {@link #getContentType()} should * return a non-null value (that is, there is a "content type") or not. * True if {@link #isContainerType()} or {@link #isReferenceType()} return true. - * - * @since 2.8 */ public boolean hasContentType() { return true; } - /** - * @since 2.6 - */ public final boolean isTypeOrSubTypeOf(Class clz) { return (_class == clz) || clz.isAssignableFrom(_class); } - /** - * @since 2.9 - */ public final boolean isTypeOrSuperTypeOf(Class clz) { return (_class == clz) || _class.isAssignableFrom(clz); } @@ -328,8 +285,6 @@ public boolean isConcrete() { * getRawClass() == Object.class * * and used to figure if we basically have "untyped" type object. - * - * @since 2.5 */ public final boolean isJavaLangObject() { return _class == Object.class; } @@ -338,8 +293,6 @@ public boolean isConcrete() { * this type should use static typing (as opposed to dynamic typing). * Note that while value of 'true' does mean that static typing is to * be used, value of 'false' may still be overridden by other settings. - * - * @since 2.2 */ public final boolean useStaticType() { return _asStatic; } @@ -358,7 +311,7 @@ public boolean isConcrete() { @Override public JavaType getContentType() { return null; } - @Override // since 2.6 + @Override public JavaType getReferencedType() { return null; } @Override @@ -366,16 +319,6 @@ public boolean isConcrete() { @Override public abstract JavaType containedType(int index); - - @Deprecated // since 2.7 - @Override - public abstract String containedTypeName(int index); - - @Deprecated // since 2.7 - @Override - public Class getParameterSource() { - return null; - } /* /********************************************************** @@ -396,17 +339,12 @@ public Class getParameterSource() { * where we just want to check if containedType is available first; and * if not, use "unknown type" (which translates to java.lang.Object * basically). - * - * @since 2.5 */ public JavaType containedTypeOrUnknown(int index) { JavaType t = containedType(index); return (t == null) ? TypeFactory.unknownType() : t; } - /** - * @since 2.7 - */ public abstract TypeBindings getBindings(); /** @@ -415,32 +353,24 @@ public JavaType containedTypeOrUnknown(int index) { * type has given erased type), one of its supertypes that has the * erased types, or null if target is neither this type or any of its * supertypes. - * - * @since 2.7 */ public abstract JavaType findSuperType(Class erasedTarget); /** * Accessor for finding fully resolved parent class of this type, * if it has one; null if not. - * - * @since 2.7 */ public abstract JavaType getSuperClass(); /** * Accessor for finding fully resolved interfaces this type implements, * if any; empty array if none. - * - * @since 2.7 */ public abstract List getInterfaces(); /** * Method that may be used to find paramaterization this type has for * given type-erased generic target type. - * - * @since 2.7 */ public abstract JavaType[] findTypeParameters(Class expType); @@ -462,19 +392,11 @@ public JavaType containedTypeOrUnknown(int index) { @SuppressWarnings("unchecked") public T getTypeHandler() { return (T) _typeHandler; } - /** - * @since 2.7 - */ public Object getContentValueHandler() { return null; } - /** - * @since 2.7 - */ public Object getContentTypeHandler() { return null; } - /** - * @since 2.6 - */ + public boolean hasValueHandler() { return _valueHandler != null; } /** @@ -482,8 +404,6 @@ public JavaType containedTypeOrUnknown(int index) { * or content type has {@link #getValueHandler} or {@link #getTypeHandler()}; * that is, are there any non-standard handlers associated with this * type object. - * - * @since 2.8 */ public boolean hasHandlers() { return (_typeHandler != null) || (_valueHandler != null); diff --git a/src/main/java/com/fasterxml/jackson/databind/JsonDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/JsonDeserializer.java index 469055f174..a7b46dabe2 100644 --- a/src/main/java/com/fasterxml/jackson/databind/JsonDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/JsonDeserializer.java @@ -22,7 +22,7 @@ *

* If deserializer is an aggregate one -- meaning it delegates handling of some * of its contents by using other deserializer(s) -- it typically also needs - * to implement {@link com.fasterxml.jackson.databind.deser.ResolvableDeserializer}, + * to implement {@link #resolve} * which can locate dependant deserializers. This is important to allow dynamic * overrides of deserializers; separate call interface is needed to separate * resolution of dependant deserializers (which may have cyclic link back @@ -30,26 +30,66 @@ *

* In addition, to support per-property annotations (to configure aspects * of deserialization on per-property basis), deserializers may want - * to implement - * {@link com.fasterxml.jackson.databind.deser.ContextualDeserializer}, - * which allows specialization of deserializers: call to - * {@link com.fasterxml.jackson.databind.deser.ContextualDeserializer#createContextual} - * is passed information on property, and can create a newly configured + * to override + * {@link #createContextual} which allows specialization of deserializers: + * it is passed information on property, and can create a newly configured * deserializer for handling that particular property. - *

- * If both - * {@link com.fasterxml.jackson.databind.deser.ResolvableDeserializer} and - * {@link com.fasterxml.jackson.databind.deser.ContextualDeserializer} - * are implemented, resolution of deserializers occurs before - * contextualization. + *
+ * Resolution of deserializers occurs before contextualization. */ public abstract class JsonDeserializer - implements NullValueProvider // since 2.9 + implements NullValueProvider { /* - /********************************************************** + /********************************************************************** + /* Initialization, with former `ResolvableDeserializer`, `ContextualDeserializer` + /********************************************************************** + */ + + /** + * Method called after deserializer instance has been constructed + * (and registered as necessary by provider objects), + * but before it has returned it to the caller. + * Called object can then resolve its dependencies to other types, + * including self-references (direct or indirect). + * + * @param ctxt Context to use for accessing configuration, resolving + * secondary deserializers + */ + public void resolve(DeserializationContext ctxt) throws JsonMappingException { + // Default implementation does nothing + } + + /** + * Method called to see if a different (or differently configured) deserializer + * is needed to deserialize values of specified property. + * Note that instance that this method is called on is typically shared one and + * as a result method should NOT modify this instance but rather construct + * and return a new instance. This instance should only be returned as-is, in case + * it is already suitable for use. + * + * @param ctxt Deserialization context to access configuration, additional + * deserializers that may be needed by this deserializer + * @param property Method, field or constructor parameter that represents the property + * (and is used to assign deserialized value). + * Should be available; but there may be cases where caller cannot provide it and + * null is passed instead (in which case impls usually pass 'this' deserializer as is) + * + * @return Deserializer to use for deserializing values of specified property; + * may be this instance or a new instance. + * + * @throws JsonMappingException + */ + public JsonDeserializer createContextual(DeserializationContext ctxt, + BeanProperty property) throws JsonMappingException { + // default implementation returns instance unmodified + return this; + } + + /* + /********************************************************************** /* Main deserialization methods - /********************************************************** + /********************************************************************** */ /** @@ -155,9 +195,9 @@ public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, } /* - /********************************************************** + /********************************************************************** /* Fluent factory methods for constructing decorated versions - /********************************************************** + /********************************************************************** */ /** @@ -169,7 +209,8 @@ public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, * Default implementation just returns 'this' * indicating that no unwrapped variant exists */ - public JsonDeserializer unwrappingDeserializer(NameTransformer unwrapper) { + public JsonDeserializer unwrappingDeserializer(DeserializationContext ctxt, + NameTransformer unwrapper) { return this; } @@ -179,18 +220,16 @@ public JsonDeserializer unwrappingDeserializer(NameTransformer unwrapper) { * delegate anything; or it does not want any changes), should either * throw {@link UnsupportedOperationException} (if operation does not * make sense or is not allowed); or return this deserializer as is. - * - * @since 2.1 */ public JsonDeserializer replaceDelegatee(JsonDeserializer delegatee) { throw new UnsupportedOperationException(); } /* - /********************************************************** + /********************************************************************** /* Introspection methods for figuring out configuration/setup /* of this deserializer instance and/or type it handles - /********************************************************** + /********************************************************************** */ /** @@ -202,8 +241,6 @@ public JsonDeserializer replaceDelegatee(JsonDeserializer delegatee) { * Default implementation will return null, which means almost same * same as returning Object.class would; that is, that * nothing is known about handled type. - *

- * @since 2.3 */ public Class handledType() { return null; } @@ -212,14 +249,14 @@ public JsonDeserializer replaceDelegatee(JsonDeserializer delegatee) { * usable for other properties of same type (type for which instance * was created). *

- * Note that cached instances are still resolved on per-property basis, - * if instance implements {@link com.fasterxml.jackson.databind.deser.ResolvableDeserializer}: - * cached instance is just as the base. This means that in most cases it is safe to + * Note that cached instances are still contextualized on per-property basis + * (but note that {@link JsonDeserializer#resolve(DeserializationContext)}d + * just once!) + * This means that in most cases it is safe to * cache instances; however, it only makes sense to cache instances * if instantiation is expensive, or if instances are heavy-weight. *

- * Default implementation returns false, to indicate that no caching - * is done. + * Default implementation returns false, to indicate that no caching is done. */ public boolean isCachable() { return false; } @@ -231,8 +268,6 @@ public JsonDeserializer replaceDelegatee(JsonDeserializer delegatee) { * * @return Deserializer this deserializer delegates calls to, if null; * null otherwise. - * - * @since 2.1 */ public JsonDeserializer getDelegatee() { return null; @@ -247,17 +282,15 @@ public JsonDeserializer getDelegatee() { * This is only to be used for error reporting and diagnostics * purposes (most commonly, to accompany "unknown property" * exception). - * - * @since 2.0 */ public Collection getKnownPropertyNames() { return null; } /* - /********************************************************** + /********************************************************************** /* Default NullValueProvider implementation - /********************************************************** + /********************************************************************** */ /** @@ -267,18 +300,14 @@ public Collection getKnownPropertyNames() { * Java null, but for some types (especially primitives) it may be * necessary to use non-null values. *

- * Since version 2.6 (in which the context argument was added), call is - * expected to be made each and every time a null token needs to - * be handled. + * Call is expected to be made each and every time a null token + * needs to be handled. *

* Default implementation simply returns null. - * - * @since 2.6 Added to replace earlier no-arguments variant */ @Override public T getNullValue(DeserializationContext ctxt) throws JsonMappingException { - // Change the direction in 2.7 - return getNullValue(); + return null; } /** @@ -299,17 +328,15 @@ public AccessPattern getNullAccessPattern() { * {@link #getEmptyValue(DeserializationContext)}, to check whether it needs * to be called just once (static values), or each time empty value is * needed. - * - * @since 2.9 */ public AccessPattern getEmptyAccessPattern() { return AccessPattern.DYNAMIC; } /* - /********************************************************** + /********************************************************************** /* Other accessors - /********************************************************** + /********************************************************************** */ /** @@ -327,8 +354,6 @@ public AccessPattern getEmptyAccessPattern() { *

* Default implementation simply calls {@link #getNullValue} and * returns value. - * - * @since 2.6 Added to replace earlier no-arguments variant */ public Object getEmptyValue(DeserializationContext ctxt) throws JsonMappingException { return getNullValue(ctxt); @@ -349,16 +374,12 @@ public Object getEmptyValue(DeserializationContext ctxt) throws JsonMappingExcep * @return ObjectIdReader used for resolving possible Object Identifier * value, instead of full value serialization, if deserializer can do that; * null if no Object Id is expected. - * - * @since 2.0 */ - public ObjectIdReader getObjectIdReader() { return null; } + public ObjectIdReader getObjectIdReader(DeserializationContext ctxt) { return null; } /** * Method needed by {@link BeanDeserializerFactory} to properly link * managed- and back-reference pairs. - * - * @since 2.2 (was moved out of BeanDeserializerBase) */ public SettableBeanProperty findBackReference(String refName) { @@ -382,35 +403,15 @@ public SettableBeanProperty findBackReference(String refName) *

* Default implementation returns null to allow explicit per-type * or per-property attempts. - * - * @since 2.9 */ public Boolean supportsUpdate(DeserializationConfig config) { return null; } /* - /********************************************************** - /* Deprecated methods - /********************************************************** - */ - - /** - * @deprecated Since 2.6 Use overloaded variant that takes context argument - */ - @Deprecated - public T getNullValue() { return null; } - - /** - * @deprecated Since 2.6 Use overloaded variant that takes context argument - */ - @Deprecated - public Object getEmptyValue() { return getNullValue(); } - - /* - /********************************************************** + /********************************************************************** /* Helper classes - /********************************************************** + /********************************************************************** */ /** diff --git a/src/main/java/com/fasterxml/jackson/databind/JsonMappingException.java b/src/main/java/com/fasterxml/jackson/databind/JsonMappingException.java index 4c258186f4..f12dba460b 100644 --- a/src/main/java/com/fasterxml/jackson/databind/JsonMappingException.java +++ b/src/main/java/com/fasterxml/jackson/databind/JsonMappingException.java @@ -207,33 +207,6 @@ Object writeReplace() { /********************************************************** */ - /** - * @deprecated Since 2.7 Use variant that takes {@link JsonParser} instead - */ - @Deprecated // since 2.7 - public JsonMappingException(String msg) { super(msg); } - - /** - * @deprecated Since 2.7 Use variant that takes {@link JsonParser} instead - */ - @Deprecated // since 2.7 - public JsonMappingException(String msg, Throwable rootCause) { super(msg, rootCause); } - - /** - * @deprecated Since 2.7 Use variant that takes {@link JsonParser} instead - */ - @Deprecated // since 2.7 - public JsonMappingException(String msg, JsonLocation loc) { super(msg, loc); } - - /** - * @deprecated Since 2.7 Use variant that takes {@link JsonParser} instead - */ - @Deprecated // since 2.7 - public JsonMappingException(String msg, JsonLocation loc, Throwable rootCause) { super(msg, loc, rootCause); } - - /** - * @since 2.7 - */ public JsonMappingException(Closeable processor, String msg) { super(msg); _processor = processor; @@ -245,9 +218,6 @@ public JsonMappingException(Closeable processor, String msg) { } } - /** - * @since 2.7 - */ public JsonMappingException(Closeable processor, String msg, Throwable problem) { super(msg, problem); _processor = processor; @@ -256,73 +226,46 @@ public JsonMappingException(Closeable processor, String msg, Throwable problem) } } - /** - * @since 2.7 - */ public JsonMappingException(Closeable processor, String msg, JsonLocation loc) { super(msg, loc); _processor = processor; } - /** - * @since 2.7 - */ public static JsonMappingException from(JsonParser p, String msg) { return new JsonMappingException(p, msg); } - /** - * @since 2.7 - */ public static JsonMappingException from(JsonParser p, String msg, Throwable problem) { return new JsonMappingException(p, msg, problem); } - /** - * @since 2.7 - */ public static JsonMappingException from(JsonGenerator g, String msg) { return new JsonMappingException(g, msg, (Throwable) null); } - /** - * @since 2.7 - */ public static JsonMappingException from(JsonGenerator g, String msg, Throwable problem) { return new JsonMappingException(g, msg, problem); } - /** - * @since 2.7 - */ public static JsonMappingException from(DeserializationContext ctxt, String msg) { return new JsonMappingException(ctxt.getParser(), msg); } - /** - * @since 2.7 - */ public static JsonMappingException from(DeserializationContext ctxt, String msg, Throwable t) { return new JsonMappingException(ctxt.getParser(), msg, t); } - /** - * @since 2.7 - */ public static JsonMappingException from(SerializerProvider ctxt, String msg) { return new JsonMappingException(ctxt.getGenerator(), msg); } - /** - * @since 2.7 - */ public static JsonMappingException from(SerializerProvider ctxt, String msg, Throwable problem) { /* 17-Aug-2015, tatu: As per [databind#903] this is bit problematic as * SerializerProvider instance does not currently hold on to generator... */ return new JsonMappingException(ctxt.getGenerator(), msg, problem); } - + /** * Factory method used when "upgrading" an {@link IOException} into * {@link JsonMappingException}: usually only needed to comply with @@ -330,8 +273,6 @@ public static JsonMappingException from(SerializerProvider ctxt, String msg, Thr *

* NOTE: since 2.9 should usually NOT be used on input-side (deserialization) * exceptions; instead use method(s) of InputMismatchException - * - * @since 2.1 */ public static JsonMappingException fromUnexpectedIOE(IOException src) { return new JsonMappingException(null, diff --git a/src/main/java/com/fasterxml/jackson/databind/JsonNode.java b/src/main/java/com/fasterxml/jackson/databind/JsonNode.java index d8c226604f..e41003f0c7 100644 --- a/src/main/java/com/fasterxml/jackson/databind/JsonNode.java +++ b/src/main/java/com/fasterxml/jackson/databind/JsonNode.java @@ -30,7 +30,7 @@ * {@link com.fasterxml.jackson.databind.node}. *

* Note that it is possible to "read" from nodes, using - * method {@link TreeNode#traverse(ObjectCodec)}, which will result in + * method {@link TreeNode#traverse}, which will result in * a {@link JsonParser} being constructed. This can be used for (relatively) * efficient conversations between different representations; and it is what * core databind uses for methods like {@link ObjectMapper#treeToValue(TreeNode, Class)} @@ -41,9 +41,9 @@ public abstract class JsonNode implements TreeNode, Iterable { /* - /********************************************************** + /********************************************************************** /* Construction, related - /********************************************************** + /********************************************************************** */ protected JsonNode() { } @@ -59,18 +59,16 @@ protected JsonNode() { } * Note: return type is guaranteed to have same type as the * node method is called on; which is why method is declared * with local generic type. - * - * @since 2.0 - * + * * @return Node that is either a copy of this node (and all non-leaf * children); or, for immutable leaf nodes, node itself. */ public abstract T deepCopy(); /* - /********************************************************** + /********************************************************************** /* TreeNode implementation - /********************************************************** + /********************************************************************** */ // public abstract JsonToken asToken(); @@ -201,8 +199,6 @@ public Iterator fieldNames() { * * @return Node that matches given JSON Pointer: if no match exists, * will return a node for which {@link #isMissingNode()} returns true. - * - * @since 2.3 */ @Override public final JsonNode at(JsonPointer ptr) @@ -233,8 +229,6 @@ public final JsonNode at(JsonPointer ptr) * * @return Node that matches given JSON Pointer: if no match exists, * will return a node for which {@link TreeNode#isMissingNode()} returns true. - * - * @since 2.3 */ @Override public final JsonNode at(String jsonPtrExpr) { @@ -244,9 +238,9 @@ public final JsonNode at(String jsonPtrExpr) { protected abstract JsonNode _at(JsonPointer ptr); /* - /********************************************************** + /********************************************************************** /* Public API, type introspection - /********************************************************** + /********************************************************************** */ // // First high-level division between values, containers and "missing" @@ -255,8 +249,6 @@ public final JsonNode at(String jsonPtrExpr) { * Return the type of this node * * @return the node type as a {@link JsonNodeType} enum value - * - * @since 2.2 */ public abstract JsonNodeType getNodeType(); @@ -328,9 +320,6 @@ public final boolean isNumber() { */ public boolean isLong() { return false; } - /** - * @since 2.2 - */ public boolean isFloat() { return false; } public boolean isDouble() { return false; } @@ -384,8 +373,6 @@ public final boolean isBinary() { * from JSON String into Number; so even if this method returns false, * it is possible that {@link #asInt} could still succeed * if node is a JSON String representing integral number, or boolean. - * - * @since 2.0 */ public boolean canConvertToInt() { return false; } @@ -400,15 +387,13 @@ public final boolean isBinary() { * from JSON String into Number; so even if this method returns false, * it is possible that {@link #asLong} could still succeed * if node is a JSON String representing integral number, or boolean. - * - * @since 2.0 */ public boolean canConvertToLong() { return false; } /* - /********************************************************** + /********************************************************************** /* Public API, straight value access - /********************************************************** + /********************************************************************** */ /** @@ -501,8 +486,6 @@ public byte[] binaryValue() throws IOException { * that an overflow is possible for `long` values * * @return 32-bit float value this node contains, if any; 0.0 for non-number nodes. - * - * @since 2.2 */ public float floatValue() { return 0.0f; } @@ -514,8 +497,6 @@ public byte[] binaryValue() throws IOException { * in overflows with {@link BigInteger} values. * * @return 64-bit double value this node contains, if any; 0.0 for non-number nodes. - * - * @since 2.2 */ public double doubleValue() { return 0.0; } @@ -529,7 +510,7 @@ public byte[] binaryValue() throws IOException { public BigDecimal decimalValue() { return BigDecimal.ZERO; } /** - * Returns integer value for this node (as {@link BigDecimal}), if and only if + * Returns integer value for this node (as {@link BigInteger}), if and only if * this node is numeric ({@link #isNumber} returns true). For other * types returns BigInteger.ZERO. * @@ -538,9 +519,9 @@ public byte[] binaryValue() throws IOException { public BigInteger bigIntegerValue() { return BigInteger.ZERO; } /* - /********************************************************** + /********************************************************************** /* Public API, value access with conversion(s)/coercion(s) - /********************************************************** + /********************************************************************** */ /** @@ -556,8 +537,6 @@ public byte[] binaryValue() throws IOException { * defaultValue in cases where null value would be returned; * either for missing nodes (trying to access missing property, or element * at invalid item for array) or explicit nulls. - * - * @since 2.4 */ public String asText(String defaultValue) { String str = asText(); @@ -567,8 +546,8 @@ public String asText(String defaultValue) { /** * Method that will try to convert value of this node to a Java int. * Numbers are coerced using default Java rules; booleans convert to 0 (false) - * and 1 (true), and Strings are parsed using default Java language integer - * parsing rules. + * and 1 (true), and Strings are parsed using + * {@link com.fasterxml.jackson.core.io.NumberInput#parseAsInt(String, int)} *

* If representation cannot be converted to an int (including structured types * like Objects and Arrays), @@ -581,8 +560,8 @@ public int asInt() { /** * Method that will try to convert value of this node to a Java int. * Numbers are coerced using default Java rules; booleans convert to 0 (false) - * and 1 (true), and Strings are parsed using default Java language integer - * parsing rules. + * and 1 (true), and Strings are parsed using + * {@link com.fasterxml.jackson.core.io.NumberInput#parseAsInt(String, int)} *

* If representation cannot be converted to an int (including structured types * like Objects and Arrays), @@ -595,8 +574,8 @@ public int asInt(int defaultValue) { /** * Method that will try to convert value of this node to a Java long. * Numbers are coerced using default Java rules; booleans convert to 0 (false) - * and 1 (true), and Strings are parsed using default Java language integer - * parsing rules. + * and 1 (true), and Strings are parsed using + * {@link com.fasterxml.jackson.core.io.NumberInput#parseAsLong(String, long)} *

* If representation cannot be converted to an long (including structured types * like Objects and Arrays), @@ -609,8 +588,8 @@ public long asLong() { /** * Method that will try to convert value of this node to a Java long. * Numbers are coerced using default Java rules; booleans convert to 0 (false) - * and 1 (true), and Strings are parsed using default Java language integer - * parsing rules. + * and 1 (true), and Strings are parsed using + * {@link com.fasterxml.jackson.core.io.NumberInput#parseAsLong(String, long)} *

* If representation cannot be converted to an long (including structured types * like Objects and Arrays), @@ -623,8 +602,8 @@ public long asLong(long defaultValue) { /** * Method that will try to convert value of this node to a Java double. * Numbers are coerced using default Java rules; booleans convert to 0.0 (false) - * and 1.0 (true), and Strings are parsed using default Java language integer - * parsing rules. + * and 1.0 (true), and Strings are parsed using + * {@link com.fasterxml.jackson.core.io.NumberInput#parseAsDouble(String, double)} *

* If representation cannot be converted to an int (including structured types * like Objects and Arrays), @@ -637,8 +616,8 @@ public double asDouble() { /** * Method that will try to convert value of this node to a Java double. * Numbers are coerced using default Java rules; booleans convert to 0.0 (false) - * and 1.0 (true), and Strings are parsed using default Java language integer - * parsing rules. + * and 1.0 (true), and Strings are parsed using + * {@link com.fasterxml.jackson.core.io.NumberInput#parseAsDouble(String, double)} *

* If representation cannot be converted to an int (including structured types * like Objects and Arrays), @@ -677,9 +656,9 @@ public boolean asBoolean(boolean defaultValue) { } /* - /********************************************************** + /********************************************************************** /* Public API, value find / existence check methods - /********************************************************** + /********************************************************************** */ /** @@ -740,8 +719,6 @@ public boolean has(int index) { *

      *   node.get(fieldName) != null && !node.get(fieldName).isNull()
      *
- * - * @since 2.1 */ public boolean hasNonNull(String fieldName) { JsonNode n = get(fieldName); @@ -756,8 +733,6 @@ public boolean hasNonNull(String fieldName) { *
      *   node.get(index) != null && !node.get(index).isNull()
      *
- * - * @since 2.1 */ public boolean hasNonNull(int index) { JsonNode n = get(index); @@ -765,9 +740,9 @@ public boolean hasNonNull(int index) { } /* - /********************************************************** + /********************************************************************** /* Public API, container access - /********************************************************** + /********************************************************************** */ /** @@ -797,9 +772,9 @@ public Iterator> fields() { } /* - /********************************************************** + /********************************************************************** /* Public API, find methods - /********************************************************** + /********************************************************************** */ /** @@ -892,9 +867,9 @@ public final List findParents(String fieldName) public abstract List findParents(String fieldName, List foundSoFar); /* - /********************************************************** + /********************************************************************** /* Public API, path handling - /********************************************************** + /********************************************************************** */ /** @@ -924,9 +899,9 @@ public JsonNode withArray(String propertyName) { } /* - /********************************************************** + /********************************************************************** /* Public API, comparison - /********************************************************** + /********************************************************************** */ /** @@ -944,21 +919,19 @@ public JsonNode withArray(String propertyName) { * * @param comparator Object called to compare two scalar {@link JsonNode} * instances, and return either 0 (are equals) or non-zero (not equal) - * - * @since 2.6 */ public boolean equals(Comparator comparator, JsonNode other) { return comparator.compare(this, other) == 0; } /* - /********************************************************** + /********************************************************************** /* Overridden standard methods - /********************************************************** + /********************************************************************** */ /** - * Method that will produce (as of Jackson 2.10) valid JSON using + * Method that will produce valid JSON using * default settings of databind, as String. * If you want other kinds of JSON output (or output formatted using one of * other Jackson-supported data formats) make sure to use diff --git a/src/main/java/com/fasterxml/jackson/databind/JsonSerializable.java b/src/main/java/com/fasterxml/jackson/databind/JsonSerializable.java index a08898a74c..f3b4419a57 100644 --- a/src/main/java/com/fasterxml/jackson/databind/JsonSerializable.java +++ b/src/main/java/com/fasterxml/jackson/databind/JsonSerializable.java @@ -48,8 +48,6 @@ public void serializeWithType(JsonGenerator gen, SerializerProvider serializers, * that core Jackson databinding makes use of. * Use of this base class is strongly recommended over directly implementing * {@link JsonSerializable}. - * - * @since 2.6 */ public abstract static class Base implements JsonSerializable { diff --git a/src/main/java/com/fasterxml/jackson/databind/JsonSerializer.java b/src/main/java/com/fasterxml/jackson/databind/JsonSerializer.java index 11c01b24a4..390bdf25f5 100644 --- a/src/main/java/com/fasterxml/jackson/databind/JsonSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/JsonSerializer.java @@ -3,6 +3,7 @@ import java.io.IOException; import java.util.Iterator; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitable; @@ -16,47 +17,98 @@ * Abstract class that defines API used by {@link ObjectMapper} (and * other chained {@link JsonSerializer}s too) to serialize Objects of * arbitrary types into JSON, using provided {@link JsonGenerator}. - * {@link com.fasterxml.jackson.databind.ser.std.StdSerializer} instead - * of this class, since it will implement many of optional - * methods of this class. - *

- * NOTE: various serialize methods are never (to be) called - * with null values -- caller must handle null values, usually - * by calling {@link SerializerProvider#findNullValueSerializer} to obtain - * serializer to use. - * This also means that custom serializers cannot be directly used to change - * the output to produce when serializing null values. + * Note that although API is defined here, custom serializer implementations + * should almost always be based on {@link com.fasterxml.jackson.databind.ser.std.StdSerializer} + * since it will implement many of optional methods of this class. *

* If serializer is an aggregate one -- meaning it delegates handling of some * of its contents by using other serializer(s) -- it typically also needs - * to implement {@link com.fasterxml.jackson.databind.ser.ResolvableSerializer}, - * which can locate secondary serializers needed. This is important to allow dynamic + * to implement {@link #resolve} which can locate secondary serializers needed. + * This is important to allow dynamic * overrides of serializers; separate call interface is needed to separate * resolution of secondary serializers (which may have cyclic link back * to serializer itself, directly or indirectly). *

- * In addition, to support per-property annotations (to configure aspects - * of serialization on per-property basis), serializers may want - * to implement - * {@link com.fasterxml.jackson.databind.ser.ContextualSerializer}, - * which allows specialization of serializers: call to - * {@link com.fasterxml.jackson.databind.ser.ContextualSerializer#createContextual} - * is passed information on property, and can create a newly configured - * serializer for handling that particular property. + * Initialization of serializers is handled by two main methods: + *

    + *
  1. {@link #resolve}: called after instance is configured to be used for specific type, + * but without yet knowing property it will be used for (or, in case of root values, without property). + * Method needs to be implemented for serializers that may work on cyclic types, and specifically + * is implemented by standard POJO serializer ({@code BeanSerializer}). It is usually not needed for + * container types as their type definitions are not cyclic, unlike some POJO types. + *
  2. {@link #createContextual}: called on resolved instance (whether newly created, or found via cache), + * when serializer is to be used for specific property, or as root value serializer (no referring property). + * It is used to apply annotations from property accessors (getter, field), and may also be used for resolving + * nested types for container serializers (such as ones for {@link java.util.Collection}s). + *
+ * Caching of serializers occurs after {@link #resolve} is called: cached instances are not contextual. *

- * If both - * {@link com.fasterxml.jackson.databind.ser.ResolvableSerializer} and - * {@link com.fasterxml.jackson.databind.ser.ContextualSerializer} - * are implemented, resolution of serializers occurs before - * contextualization. + * NOTE: various serialize methods are never (to be) called + * with null values -- caller must handle null values, usually + * by calling {@link SerializerProvider#findNullValueSerializer} to obtain + * serializer to use. + * This also means that custom serializers cannot be directly used to change + * the output to produce when serializing null values. */ public abstract class JsonSerializer - implements JsonFormatVisitable // since 2.1 + implements JsonFormatVisitable { /* - /********************************************************** + /********************************************************************** + /* Initialization, with former `ResolvableSerializer`, + /* `ContextualSerializer`. + /********************************************************************** + */ + + /** + * Method called after {@link SerializerProvider} has registered + * the serializer, but before it has returned it to the caller. + * Called object can then resolve its dependencies to other types, + * including self-references (direct or indirect). + *

+ * Note that this method does NOT return serializer, since resolution + * is not allowed to change actual serializer to use. + * + * @param provider Provider that has constructed serializer this method + * is called on. + */ + public void resolve(SerializerProvider provider) throws JsonMappingException { + // Default implementation does nothing + } + + /** + * Method called to see if a different (or differently configured) serializer + * is needed to serialize values of specified property (or, for root values, in which + * case `null` is passed). + * Note that instance that this method is called on is typically shared one and + * as a result method should NOT modify this instance but rather construct + * and return a new instance. This instance should only be returned as-is, in case + * it is already suitable for use. + *

+ * Note that method is only called once per POJO property, and for the first usage as root + * value serializer; it is not called for every serialization, as doing that would have + * significant performance impact; most serializers cache contextual instances for future + * use. + * + * @param prov Serializer provider to use for accessing config, other serializers + * @param property Property (defined by one or more accessors - field or method - used + * for accessing logical property value) for which serializer is used to be used; + * or, `null` for root value (or in cases where caller does not have this information, + * which is handled as root value case). + * + * @return Serializer to use for serializing values of specified property; + * may be this instance or a new instance. + */ + public JsonSerializer createContextual(SerializerProvider prov, + BeanProperty property) throws JsonMappingException { + // default implementation returns instance unmodified + return this; + } + + /* + /********************************************************************** /* Fluent factory methods for constructing decorated versions - /********************************************************** + /********************************************************************** */ /** @@ -83,8 +135,6 @@ public JsonSerializer unwrappingSerializer(NameTransformer unwrapper) { * delegate anything; or it does not want any changes), should either * throw {@link UnsupportedOperationException} (if operation does not * make sense or is not allowed); or return this serializer as is. - * - * @since 2.1 */ public JsonSerializer replaceDelegatee(JsonSerializer delegatee) { throw new UnsupportedOperationException(); @@ -97,17 +147,54 @@ public JsonSerializer replaceDelegatee(JsonSerializer delegatee) { *

* Default implementation simply returns this; sub-classes that do support * filtering will need to create and return new instance if filter changes. - * - * @since 2.6 */ public JsonSerializer withFilterId(Object filterId) { return this; } + /** + * Mutant factory called if there is need to create a serializer with specified + * format overrides (typically from property on which this serializer would be used, + * based on type declaration). Method is called before {@link #createContextual} + * but right after serializer is either constructed or fetched from cache. + *

+ * Method can do one of three things: + *

    + *
  • Return {@code this} instance as is: this means that none of overrides has any effect + *
  • + *
  • Return an alternate {@link JsonSerializer}, suitable for use with specified format + *
  • + *
  • Return {@code null} to indicate that this serializer instance is not suitable for + * handling format variation, but does not know how to construct new serializer: caller + * will typically then call {@link com.fasterxml.jackson.databind.ser.SerializerFactory} with overrides to construct new serializer + *
  • + *
+ * One example of second approach is the case where {@link com.fasterxml.jackson.annotation.JsonFormat.Shape#STRING} indicates String + * representation and code can just construct simple "string-like serializer", or variant of itself + * (similar to how {@link #createContextual} is often implemented). + * And third case (returning {@code null}) is applicable for cases like format defines + * {@link com.fasterxml.jackson.annotation.JsonFormat.Shape#POJO}, requesting "introspect serializer for POJO regardless of type": + * {@link com.fasterxml.jackson.databind.ser.SerializerFactory} is needed for full re-introspection, typically. + * + * @param formatOverrides (not null) Override settings, NOT including original format settings (which + * serializer needs to explicitly retain if needed) + * + * @since 3.0 + */ + public JsonSerializer withFormatOverrides(SerializationConfig config, + JsonFormat.Value formatOverrides) + { + // First: if no override, safe to use as is: + if (formatOverrides.getShape() == JsonFormat.Shape.ANY) { + return this; + } + return null; + } + /* - /********************************************************** + /********************************************************************** /* Serialization methods - /********************************************************** + /********************************************************************** */ /** @@ -163,9 +250,9 @@ public void serializeWithType(T value, JsonGenerator gen, SerializerProvider ser } /* - /********************************************************** - /* Other accessors - /********************************************************** + /********************************************************************** + /* Accessors for serializer metadata + /********************************************************************** */ /** @@ -174,43 +261,10 @@ public void serializeWithType(T value, JsonGenerator gen, SerializerProvider ser * may be a more generic (super-type) -- but it should not be * incorrect (return a non-related type). *

- * Default implementation will return null, which essentially means - * same as returning Object.class would; that is, that - * nothing is known about handled type. - *

+ * NOTE: starting with 3.0, left {@code abstract}. */ - public Class handledType() { return null; } + public Class handledType() { return Object.class; } - /** - * Method called to check whether given serializable value is - * considered "empty" value (for purposes of suppressing serialization - * of empty values). - *

- * Default implementation will consider only null values to be empty. - * - * @deprecated Since 2.5 Use {@link #isEmpty(SerializerProvider, Object)} instead; - * will be removed from 3.0 - */ - @Deprecated - public boolean isEmpty(T value) { - return isEmpty(null, value); - } - - /** - * Method called to check whether given serializable value is - * considered "empty" value (for purposes of suppressing serialization - * of empty values). - *

- * Default implementation will consider only null values to be empty. - *

- * NOTE: replaces {@link #isEmpty(Object)}, which was deprecated in 2.5 - * - * @since 2.5 - */ - public boolean isEmpty(SerializerProvider provider, T value) { - return (value == null); - } - /** * Method that can be called to see whether this serializer instance * will use Object Id to handle cyclic references. @@ -228,7 +282,7 @@ public boolean usesObjectId() { public boolean isUnwrappingSerializer() { return false; } - + /** * Accessor that can be used to determine if this serializer uses * another serializer for actual serialization, by delegating @@ -237,8 +291,6 @@ public boolean isUnwrappingSerializer() { * * @return Serializer this serializer delegates calls to, if null; * null otherwise. - * - * @since 2.1 */ public JsonSerializer getDelegatee() { return null; @@ -251,23 +303,38 @@ public JsonSerializer getDelegatee() { * {@link com.fasterxml.jackson.databind.ser.BeanPropertyWriter}. * Of standard Jackson serializers, only {@link com.fasterxml.jackson.databind.ser.BeanSerializer} * exposes properties. - * - * @since 2.6 */ public Iterator properties() { return ClassUtil.emptyIterator(); } /* - /********************************************************** + /********************************************************************** + /* Accessors for introspecting handling of values + /********************************************************************** + */ + + /** + * Method called to check whether given serializable value is + * considered "empty" value (for purposes of suppressing serialization + * of empty values). + *

+ * Default implementation will consider only null values to be empty. + */ + public boolean isEmpty(SerializerProvider provider, T value) + throws IOException + { + return (value == null); + } + + /* + /********************************************************************** /* Default JsonFormatVisitable implementation - /********************************************************** + /********************************************************************** */ /** * Default implementation simply calls {@link JsonFormatVisitorWrapper#expectAnyFormat(JavaType)}. - * - * @since 2.1 */ @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType type) @@ -277,9 +344,9 @@ public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType t } /* - /********************************************************** + /********************************************************************** /* Helper class(es) - /********************************************************** + /********************************************************************** */ /** diff --git a/src/main/java/com/fasterxml/jackson/databind/KeyDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/KeyDeserializer.java index 217527d412..12c5a2bac3 100644 --- a/src/main/java/com/fasterxml/jackson/databind/KeyDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/KeyDeserializer.java @@ -9,6 +9,32 @@ */ public abstract class KeyDeserializer { + /* + /********************************************************** + /* Initialization, with former `ResolvableDeserializer` + /********************************************************** + */ + + /** + * Method called after deserializer instance has been constructed + * (and registered as necessary by provider objects), + * but before it has returned it to the caller. + * Called object can then resolve its dependencies to other types, + * including self-references (direct or indirect). + * + * @param ctxt Context to use for accessing configuration, resolving + * secondary deserializers + */ + public void resolve(DeserializationContext ctxt) throws JsonMappingException { + // Default implementation does nothing + } + + /* + /********************************************************** + /* Main API + /********************************************************** + */ + /** * Method called to deserialize a {@link java.util.Map} key from JSON property name. */ diff --git a/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java b/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java index 9b7bb96f34..ab8d33980e 100644 --- a/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java +++ b/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java @@ -46,9 +46,9 @@ public enum MapperFeature implements ConfigFeature * precedence than setters, so they are only used if no * setter is found for the Map/Collection property. *

- * Feature is enabled by default. + * Feature is disabled by default since 3.0 (with 2.x was enabled) */ - USE_GETTERS_AS_SETTERS(true), + USE_GETTERS_AS_SETTERS(false), /** * Feature that determines how transient modifier for fields @@ -58,99 +58,9 @@ public enum MapperFeature implements ConfigFeature * Feature is disabled by default, meaning that existence of `transient` * for a field does not necessarily lead to ignoral of getters or setters * but just ignoring the use of field for access. - * - * @since 2.6 */ PROPAGATE_TRANSIENT_MARKER(false), - /* - /****************************************************** - /* Introspection-based property auto-detection - /****************************************************** - */ - - /** - * Feature that determines whether "creator" methods are - * automatically detected by consider public constructors, - * and static single argument methods with name "valueOf". - * If disabled, only methods explicitly annotated are considered - * creator methods (except for the no-arg default constructor which - * is always considered a factory method). - *

- * Note that this feature has lower precedence than per-class - * annotations, and is only used if there isn't more granular - * configuration available. - *

- * Feature is enabled by default. - */ - AUTO_DETECT_CREATORS(true), - - /** - * Feature that determines whether non-static fields are recognized as - * properties. - * If yes, then all public member fields - * are considered as properties. If disabled, only fields explicitly - * annotated are considered property fields. - *

- * Note that this feature has lower precedence than per-class - * annotations, and is only used if there isn't more granular - * configuration available. - *

- * Feature is enabled by default. - */ - AUTO_DETECT_FIELDS(true), - - /** - * Feature that determines whether regular "getter" methods are - * automatically detected based on standard Bean naming convention - * or not. If yes, then all public zero-argument methods that - * start with prefix "get" - * are considered as getters. - * If disabled, only methods explicitly annotated are considered getters. - *

- * Note that since version 1.3, this does NOT include - * "is getters" (see {@link #AUTO_DETECT_IS_GETTERS} for details) - *

- * Note that this feature has lower precedence than per-class - * annotations, and is only used if there isn't more granular - * configuration available. - *

- * Feature is enabled by default. - */ - AUTO_DETECT_GETTERS(true), - - /** - * Feature that determines whether "is getter" methods are - * automatically detected based on standard Bean naming convention - * or not. If yes, then all public zero-argument methods that - * start with prefix "is", and whose return type is boolean - * are considered as "is getters". - * If disabled, only methods explicitly annotated are considered getters. - *

- * Note that this feature has lower precedence than per-class - * annotations, and is only used if there isn't more granular - * configuration available. - *

- * Feature is enabled by default. - */ - AUTO_DETECT_IS_GETTERS(true), - - /** - * Feature that determines whether "setter" methods are - * automatically detected based on standard Bean naming convention - * or not. If yes, then all public one-argument methods that - * start with prefix "set" - * are considered setters. If disabled, only methods explicitly - * annotated are considered setters. - *

- * Note that this feature has lower precedence than per-class - * annotations, and is only used if there isn't more granular - * configuration available. - *

- * Feature is enabled by default. - */ - AUTO_DETECT_SETTERS(true), - /** * Feature that determines whether getters (getter methods) * can be auto-detected if there is no matching mutator (setter, @@ -171,8 +81,6 @@ public enum MapperFeature implements ConfigFeature * explicitly annotated for such use. *

* Feature is enabled by default, for backwards compatibility reasons. - * - * @since 2.2 */ ALLOW_FINAL_FIELDS_AS_MUTATORS(true), @@ -189,8 +97,6 @@ public enum MapperFeature implements ConfigFeature * bean-style naming) or explicitly annotated. *

* Feature is enabled by default. - * - * @since 2.2 */ INFER_PROPERTY_MUTATORS(true), @@ -208,8 +114,6 @@ public enum MapperFeature implements ConfigFeature * for deserialization. *

* Feature is enabled by default. - * - * @since 2.9 */ INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES(true), @@ -258,8 +162,6 @@ public enum MapperFeature implements ConfigFeature *

* Feature is enabled by default, for legacy reasons (it was the behavior * until 2.6) - * - * @since 2.7 */ OVERRIDE_PUBLIC_ACCESS_MODIFIERS(true), @@ -286,6 +188,18 @@ public enum MapperFeature implements ConfigFeature */ USE_STATIC_TYPING(false), + /** + * Feature that enables inferring builder type bindings from the value type + * being deserialized. This requires that the generic type declaration on + * the value type match that on the builder exactly. + *

+ * Feature is disabled by default which means that deserialization does + * not support deserializing types via builders with type parameters. + *

+ * See: https://github.com/FasterXML/jackson-databind/issues/921 + */ + INFER_BUILDER_TYPE_BINDINGS(false), + /** * Feature that specifies whether the declared base type of a polymorphic value * is to be used as the "default" implementation, if no explicit default class @@ -364,8 +278,6 @@ public enum MapperFeature implements ConfigFeature * letters. Overhead for names that are already lower-case should be negligible however. *

* Feature is disabled by default. - * - * @since 2.5 */ ACCEPT_CASE_INSENSITIVE_PROPERTIES(false), @@ -377,8 +289,6 @@ public enum MapperFeature implements ConfigFeature * explicit override) do not need to match. *

* Feature is disabled by default. - * - * @since 2.9 */ ACCEPT_CASE_INSENSITIVE_ENUMS(false), @@ -391,91 +301,24 @@ public enum MapperFeature implements ConfigFeature * If disabled, wrapper name is only used for wrapping (if anything). *

* Feature is disabled by default. - * - * @since 2.1 */ USE_WRAPPER_NAME_AS_PROPERTY_NAME(false), - /** - * Feature that may be enabled to enforce strict compatibility with - * Bean name introspection, instead of slightly different mechanism - * Jackson defaults to. - * Specific difference is that Jackson always lower cases leading upper-case - * letters, so "getURL()" becomes "url" property; whereas standard Bean - * naming only lower-cases the first letter if it is NOT followed by - * another upper-case letter (so "getURL()" would result in "URL" property). - *

- * Feature is disabled by default for backwards compatibility purposes: earlier - * Jackson versions used Jackson's own mechanism. - * - * @since 2.5 - */ - USE_STD_BEAN_NAMING(false), - /** * Feature that when enabled will allow explicitly named properties (i.e., fields or methods * annotated with {@link com.fasterxml.jackson.annotation.JsonProperty}("explicitName")) to * be re-named by a {@link PropertyNamingStrategy}, if one is configured. *

* Feature is disabled by default. - * - * @since 2.7 */ ALLOW_EXPLICIT_PROPERTY_RENAMING(false), - /* - /****************************************************** - /* Coercion features - /****************************************************** - */ - - /** - * Feature that determines whether coercions from secondary representations are allowed - * for simple non-textual scalar types: numbers and booleans. This includes `primitive` - * types and their wrappers, but excludes `java.lang.String` and date/time types. - *

- * When feature is disabled, only strictly compatible input may be bound: numbers for - * numbers, boolean values for booleans. When feature is enabled, conversions from - * JSON String are allowed, as long as textual value matches (for example, String - * "true" is allowed as equivalent of JSON boolean token `true`; or String "1.0" - * for `double`). - *

- * Note that it is possible that other configurability options can override this - * in closer scope (like on per-type or per-property basis); this is just the global - * default. - *

- * Feature is enabled by default (for backwards compatibility since this was the - * default behavior) - * - * @since 2.9 - */ - ALLOW_COERCION_OF_SCALARS(true), - /* /****************************************************** /* Other features /****************************************************** */ - /** - * Feature that determines whether multiple registrations of same module - * should be ignored or not; if enabled, only the first registration call - * results in module being called, and possible duplicate calls are silently - * ignored; if disabled, no checking is done and all registration calls are - * dispatched to module. - *

- * Definition of "same module" is based on using {@link Module#getTypeId()}; - * modules with same non-null type id are considered same for - * purposes of duplicate registration. This also avoids having to keep track - * of actual module instances; only ids will be kept track of (and only if - * this feature is enabled). - *

- * Feature is enabled by default. - * - * @since 2.5 - */ - IGNORE_DUPLICATE_MODULE_REGISTRATIONS(true), - /** * Setting that determines what happens if an attempt is made to explicitly * "merge" value of a property, where value does not support merging; either @@ -485,8 +328,6 @@ public enum MapperFeature implements ConfigFeature * Feature is disabled by default since non-mergeable property types are ignored * even if defaults call for merging, and usually explicit per-type or per-property * settings for such types should result in an exception. - * - * @since 2.9 */ IGNORE_MERGE_FOR_UNMERGEABLE(true) diff --git a/src/main/java/com/fasterxml/jackson/databind/MappingIterator.java b/src/main/java/com/fasterxml/jackson/databind/MappingIterator.java index 06a4758bed..15e72414fa 100644 --- a/src/main/java/com/fasterxml/jackson/databind/MappingIterator.java +++ b/src/main/java/com/fasterxml/jackson/databind/MappingIterator.java @@ -5,6 +5,7 @@ import java.util.*; import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.exc.RuntimeJsonMappingException; /** * Iterator exposed by {@link ObjectMapper} when binding sequence of @@ -17,9 +18,9 @@ public class MappingIterator implements Iterator, Closeable new MappingIterator(null, null, null, null, false, null); /* - /********************************************************** + /********************************************************************** /* State constants - /********************************************************** + /********************************************************************** */ /** @@ -45,9 +46,9 @@ public class MappingIterator implements Iterator, Closeable protected final static int STATE_HAS_VALUE = 3; /* - /********************************************************** + /********************************************************************** /* Configuration - /********************************************************** + /********************************************************************** */ /** @@ -76,7 +77,7 @@ public class MappingIterator implements Iterator, Closeable * Context to resynchronize to, in case an exception is encountered * but caller wants to try to read more elements. */ - protected final JsonStreamContext _seqContext; + protected final TokenStreamContext _seqContext; /** * If not null, "value to update" instead of creating a new instance @@ -92,9 +93,9 @@ public class MappingIterator implements Iterator, Closeable protected final boolean _closeParser; /* - /********************************************************** + /********************************************************************** /* Parsing state - /********************************************************** + /********************************************************************** */ /** @@ -103,9 +104,9 @@ public class MappingIterator implements Iterator, Closeable protected int _state; /* - /********************************************************** + /********************************************************************** /* Construction - /********************************************************** + /********************************************************************** */ /** @@ -144,7 +145,7 @@ protected MappingIterator(JavaType type, JsonParser p, DeserializationContext ct _seqContext = null; _state = STATE_CLOSED; } else { - JsonStreamContext sctxt = p.getParsingContext(); + TokenStreamContext sctxt = p.getParsingContext(); if (managedParser && p.isExpectedStartArrayToken()) { // If pointing to START_ARRAY, context should be that ARRAY p.clearCurrentToken(); @@ -152,7 +153,7 @@ protected MappingIterator(JavaType type, JsonParser p, DeserializationContext ct // regardless, recovery context should be whatever context we have now, // with sole exception of pointing to a start marker, in which case it's // the parent - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if ((t == JsonToken.START_OBJECT) || (t == JsonToken.START_ARRAY)) { sctxt = sctxt.getParent(); } @@ -168,9 +169,9 @@ protected static MappingIterator emptyIterator() { } /* - /********************************************************** + /********************************************************************** /* Basic iterator impl - /********************************************************** + /********************************************************************** */ @Override @@ -191,9 +192,9 @@ public T next() try { return nextValue(); } catch (JsonMappingException e) { - throw new RuntimeJsonMappingException(e.getMessage(), e); + return _handleMappingException(e); } catch (IOException e) { - throw new RuntimeException(e.getMessage(), e); + return _handleIOException(e); } } @@ -213,15 +214,11 @@ public void close() throws IOException { } /* - /********************************************************** + /********************************************************************** /* Extended API, iteration - /********************************************************** + /********************************************************************** */ - - /* - */ - /** * Equivalent of {@link #next} but one that may throw checked * exceptions from Jackson due to invalid input. @@ -235,7 +232,7 @@ public boolean hasNextValue() throws IOException _resync(); // fall-through case STATE_MAY_HAVE_VALUE: - JsonToken t = _parser.getCurrentToken(); + JsonToken t = _parser.currentToken(); if (t == null) { // un-initialized or cleared; find next t = _parser.nextToken(); // If EOF, no more, or if we hit END_ARRAY (although we don't clear the token). @@ -283,10 +280,8 @@ public T nextValue() throws IOException return value; } finally { _state = nextState; - /* 24-Mar-2015, tatu: As per [#733], need to mark token consumed no - * matter what, to avoid infinite loop for certain failure cases. - * For 2.6 need to improve further. - */ + // 24-Mar-2015, tatu: As per [#733], need to mark token consumed no + // matter what, to avoid infinite loop for certain failure cases. _parser.clearCurrentToken(); } } @@ -296,8 +291,6 @@ public T nextValue() throws IOException * this iterator; resulting container will be a {@link java.util.ArrayList}. * * @return List of entries read - * - * @since 2.2 */ public List readAll() throws IOException { return readAll(new ArrayList()); @@ -308,8 +301,6 @@ public List readAll() throws IOException { * this iterator * * @return List of entries read (same as passed-in argument) - * - * @since 2.2 */ public > L readAll(L resultList) throws IOException { @@ -322,8 +313,6 @@ public > L readAll(L resultList) throws IOException /** * Convenience method for reading all entries accessible via * this iterator - * - * @since 2.5 */ public > C readAll(C results) throws IOException { @@ -334,15 +323,13 @@ public > C readAll(C results) throws IOException } /* - /********************************************************** + /********************************************************************** /* Extended API, accessors - /********************************************************** + /********************************************************************** */ /** * Accessor for getting underlying parser this iterator uses. - * - * @since 2.2 */ public JsonParser getParser() { return _parser; @@ -352,8 +339,6 @@ public JsonParser getParser() { * Accessor for accessing {@link FormatSchema} that the underlying parser * (as per {@link #getParser}) is using, if any; only parser of schema-aware * formats use schemas. - * - * @since 2.2 */ public FormatSchema getParserSchema() { return _parser.getSchema(); @@ -366,17 +351,15 @@ public FormatSchema getParserSchema() { * * * @return Location of the input stream of the underlying parser - * - * @since 2.2.1 */ public JsonLocation getCurrentLocation() { return _parser.getCurrentLocation(); } /* - /********************************************************** + /********************************************************************** /* Helper methods - /********************************************************** + /********************************************************************** */ protected void _resync() throws IOException diff --git a/src/main/java/com/fasterxml/jackson/databind/MappingJsonFactory.java b/src/main/java/com/fasterxml/jackson/databind/MappingJsonFactory.java deleted file mode 100644 index a7939f63e1..0000000000 --- a/src/main/java/com/fasterxml/jackson/databind/MappingJsonFactory.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.fasterxml.jackson.databind; - -import java.io.IOException; - -import com.fasterxml.jackson.core.*; -import com.fasterxml.jackson.core.format.InputAccessor; -import com.fasterxml.jackson.core.format.MatchStrength; - -/** - * Sub-class of {@link JsonFactory} that will create a proper - * {@link ObjectCodec} to allow seam-less conversions between - * JSON content and Java objects (POJOs). - * The only addition to regular {@link JsonFactory} currently - * is that {@link ObjectMapper} is constructed and passed as - * the codec to use. - */ -public class MappingJsonFactory - extends JsonFactory -{ - private static final long serialVersionUID = -1; // since 2.7 - - public MappingJsonFactory() - { - this(null); - } - - public MappingJsonFactory(ObjectMapper mapper) - { - super(mapper); - if (mapper == null) { - setCodec(new ObjectMapper(this)); - } - } - - public MappingJsonFactory(JsonFactory src, ObjectMapper mapper) - { - super(src, mapper); - if (mapper == null) { - setCodec(new ObjectMapper(this)); - } - } - - /** - * We'll override the method to return more specific type; co-variance - * helps here - */ - @Override - public final ObjectMapper getCodec() { return (ObjectMapper) _objectCodec; } - - // @since 2.1 - @Override - public JsonFactory copy() - { - _checkInvalidCopy(MappingJsonFactory.class); - // note: as with base class, must NOT copy mapper reference - return new MappingJsonFactory(this, null); - } - - /* - /********************************************************** - /* Format detection functionality (since 1.8) - /********************************************************** - */ - - /** - * Sub-classes need to override this method - */ - @Override - public String getFormatName() - { - /* since non-JSON factories typically should not extend this class, - * let's just always return JSON as name. - */ - return FORMAT_NAME_JSON; - } - - /** - * Sub-classes need to override this method - */ - @Override - public MatchStrength hasFormat(InputAccessor acc) throws IOException - { - if (getClass() == MappingJsonFactory.class) { - return hasJSONFormat(acc); - } - return null; - } -} diff --git a/src/main/java/com/fasterxml/jackson/databind/Module.java b/src/main/java/com/fasterxml/jackson/databind/Module.java index 5884e6b3b5..f7139cdef8 100644 --- a/src/main/java/com/fasterxml/jackson/databind/Module.java +++ b/src/main/java/com/fasterxml/jackson/databind/Module.java @@ -1,15 +1,13 @@ package com.fasterxml.jackson.databind; import java.util.Collection; +import java.util.function.UnaryOperator; import com.fasterxml.jackson.core.*; + +import com.fasterxml.jackson.databind.cfg.MapperBuilder; import com.fasterxml.jackson.databind.cfg.MutableConfigOverride; -import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier; -import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler; -import com.fasterxml.jackson.databind.deser.Deserializers; -import com.fasterxml.jackson.databind.deser.KeyDeserializers; -import com.fasterxml.jackson.databind.deser.ValueInstantiators; -import com.fasterxml.jackson.databind.introspect.ClassIntrospector; +import com.fasterxml.jackson.databind.deser.*; import com.fasterxml.jackson.databind.jsontype.NamedType; import com.fasterxml.jackson.databind.ser.BeanSerializerModifier; import com.fasterxml.jackson.databind.ser.Serializers; @@ -25,11 +23,11 @@ public abstract class Module implements Versioned { /* - /********************************************************** + /********************************************************************** /* Simple accessors - /********************************************************** + /********************************************************************** */ - + /** * Method that returns a display that can be used by Jackson * for informational purposes, as well as in associating extensions with @@ -47,38 +45,28 @@ public abstract class Module /** * Method that returns an id that may be used to determine if two {@link Module} * instances are considered to be of same type, for purpose of preventing - * multiple registrations of "same type of" module - * (see {@link com.fasterxml.jackson.databind.MapperFeature#IGNORE_DUPLICATE_MODULE_REGISTRATIONS}) - * If `null` is returned, every instance is considered unique. - * If non-null value is returned, equality of id Objects is used to check whether - * modules should be considered to be "of same type" + * multiple registrations of "same" module, *

* Default implementation returns value of class name ({@link Class#getName}). * - * @since 2.5 + * @since 3.0 */ - public Object getTypeId() { + public Object getRegistrationId() { return getClass().getName(); } - + /* - /********************************************************** + /********************************************************************** /* Life-cycle: registration - /********************************************************** + /********************************************************************** */ - + /** * Method called by {@link ObjectMapper} when module is registered. * It is called to let module register functionality it provides, * using callback methods passed-in context object exposes. */ public abstract void setupModule(SetupContext context); - - /* - /********************************************************** - /* Helper types - /********************************************************** - */ /** * Interface Jackson exposes to modules for purpose of registering @@ -90,11 +78,11 @@ public Object getTypeId() { public static interface SetupContext { /* - /********************************************************** + /****************************************************************** /* Simple accessors - /********************************************************** + /****************************************************************** */ - + /** * Method that returns version information about {@link ObjectMapper} * that implements this context. Modules can use this to choose @@ -103,9 +91,14 @@ public static interface SetupContext */ public Version getMapperVersion(); + /** + * @since 3.0 + */ + public String getFormatName(); + /** * Fallback access method that allows modules to refer to the - * {@link ObjectMapper} that provided this context. + * {@link MapperBuilder} that provided this context. * It should NOT be needed by most modules; and ideally should * not be used -- however, there may be cases where this may * be necessary due to various design constraints. @@ -115,14 +108,13 @@ public static interface SetupContext * to allow access to new features in cases where Module API * has not yet been extended, or there are oversights. *

- * Return value is chosen to not leak dependency to {@link ObjectMapper}; - * however, instance will always be of that type. - * This is why return value is declared generic, to allow caller to - * specify context to often avoid casting. - * - * @since 2.0 + * Return value is chosen to force casting, to make caller aware that + * this is a fallback accessor, used only when everything else fails: + * type is, however, guaranteed to be {@link MapperBuilder} (and more + * specifally format-specific subtype that mapper constructed, in case + * format-specific access is needed). */ - public C getOwner(); + public Object getOwner(); /** * Accessor for finding {@link TypeFactory} that is currently configured @@ -130,27 +122,22 @@ public static interface SetupContext *

* NOTE: since it is possible that other modules might change or replace * TypeFactory, use of this method adds order-dependency for registrations. - * - * @since 2.0 */ - public TypeFactory getTypeFactory(); - + public TypeFactory typeFactory(); + + public TokenStreamFactory tokenStreamFactory(); + public boolean isEnabled(MapperFeature f); - public boolean isEnabled(DeserializationFeature f); - public boolean isEnabled(SerializationFeature f); - - public boolean isEnabled(JsonFactory.Feature f); - - public boolean isEnabled(JsonParser.Feature f); - - public boolean isEnabled(JsonGenerator.Feature f); + public boolean isEnabled(TokenStreamFactory.Feature f); + public boolean isEnabled(StreamReadFeature f); + public boolean isEnabled(StreamWriteFeature f); /* - /********************************************************** + /****************************************************************** /* Mutant accessors - /********************************************************** + /****************************************************************** */ /** @@ -167,108 +154,102 @@ public static interface SetupContext * to change the default format to use for properties of type * {@link java.util.Date} (possibly further overridden by per-property * annotations) - * - * @since 2.8 */ public MutableConfigOverride configOverride(Class type); - + /* - /********************************************************** - /* Handler registration; serializers/deserializers - /********************************************************** + /****************************************************************** + /* Handler registration; deserializers, related + /****************************************************************** */ /** * Method that module can use to register additional deserializers to use for * handling types. - * + * * @param d Object that can be called to find deserializer for types supported * by module (null returned for non-supported types) */ - public void addDeserializers(Deserializers d); + public SetupContext addDeserializers(Deserializers d); /** * Method that module can use to register additional deserializers to use for * handling Map key values (which are separate from value deserializers because * they are always serialized from String values) */ - public void addKeyDeserializers(KeyDeserializers s); + public SetupContext addKeyDeserializers(KeyDeserializers s); + /** + * Method that module can use to register additional modifier objects to + * customize configuration and construction of bean deserializers. + * + * @param mod Modifier to register + */ + public SetupContext addDeserializerModifier(BeanDeserializerModifier mod); + + /** + * Method that module can use to register additional {@link com.fasterxml.jackson.databind.deser.ValueInstantiator}s, + * by adding {@link ValueInstantiators} object that gets called when + * instantatiator is needed by a deserializer. + * + * @param instantiators Object that can provide {@link com.fasterxml.jackson.databind.deser.ValueInstantiator}s for + * constructing POJO values during deserialization + */ + public SetupContext addValueInstantiators(ValueInstantiators instantiators); + + /* + /****************************************************************** + /* Handler registration; serializers, related + /****************************************************************** + */ + /** * Method that module can use to register additional serializers to use for * handling types. - * + * * @param s Object that can be called to find serializer for types supported * by module (null returned for non-supported types) */ - public void addSerializers(Serializers s); + public SetupContext addSerializers(Serializers s); /** * Method that module can use to register additional serializers to use for * handling Map key values (which are separate from value serializers because * they must write JsonToken.FIELD_NAME instead of String value). */ - public void addKeySerializers(Serializers s); - - /* - /********************************************************** - /* Handler registration; other - /********************************************************** - */ - - /** - * Method that module can use to register additional modifier objects to - * customize configuration and construction of bean deserializers. - * - * @param mod Modifier to register - */ - public void addBeanDeserializerModifier(BeanDeserializerModifier mod); + public SetupContext addKeySerializers(Serializers s); /** * Method that module can use to register additional modifier objects to * customize configuration and construction of bean serializers. - * + * * @param mod Modifier to register */ - public void addBeanSerializerModifier(BeanSerializerModifier mod); - - /** - * Method that module can use to register additional - * {@link AbstractTypeResolver} instance, to handle resolution of - * abstract to concrete types (either by defaulting, or by materializing). - * - * @param resolver Resolver to add. - */ - public void addAbstractTypeResolver(AbstractTypeResolver resolver); + public SetupContext addSerializerModifier(BeanSerializerModifier mod); /** - * Method that module can use to register additional - * {@link TypeModifier} instance, which can augment {@link com.fasterxml.jackson.databind.JavaType} - * instances constructed by {@link com.fasterxml.jackson.databind.type.TypeFactory}. - * - * @param modifier to add + * Method that module can use to override handler called to write JSON Object key + * for {@link java.util.Map} values. + * + * @param ser Serializer called to write output for JSON Object key of which value + * on Java side is `null` */ - public void addTypeModifier(TypeModifier modifier); + public SetupContext overrideDefaultNullKeySerializer(JsonSerializer ser); /** - * Method that module can use to register additional {@link com.fasterxml.jackson.databind.deser.ValueInstantiator}s, - * by adding {@link ValueInstantiators} object that gets called when - * instantatiator is needed by a deserializer. - * - * @param instantiators Object that can provide {@link com.fasterxml.jackson.databind.deser.ValueInstantiator}s for - * constructing POJO values during deserialization + * Method that module can use to override handler called to write Java `null` as + * a value (Property or Map value, Collection/array element). + * + * @param ser Serializer called to write output for Java `null` as value (as distinct from + * key_ */ - public void addValueInstantiators(ValueInstantiators instantiators); + public SetupContext overrideDefaultNullValueSerializer(JsonSerializer ser); - /** - * Method for replacing the default class introspector with a derived class that - * overrides specific behavior. - * - * @param ci Derived class of ClassIntrospector with overriden behavior - * - * @since 2.2 + /* + /****************************************************************** + /* Handler registration, annotation introspectors + /****************************************************************** */ - public void setClassIntrospector(ClassIntrospector ci); /** * Method for registering specified {@link AnnotationIntrospector} as the highest @@ -277,7 +258,7 @@ public static interface SetupContext * * @param ai Annotation introspector to register. */ - public void insertAnnotationIntrospector(AnnotationIntrospector ai); + public SetupContext insertAnnotationIntrospector(AnnotationIntrospector ai); /** * Method for registering specified {@link AnnotationIntrospector} as the lowest @@ -286,28 +267,71 @@ public static interface SetupContext * * @param ai Annotation introspector to register. */ - public void appendAnnotationIntrospector(AnnotationIntrospector ai); + public SetupContext appendAnnotationIntrospector(AnnotationIntrospector ai); + + /* + /****************************************************************** + /* Type handling + /****************************************************************** + */ + + /** + * Method that module can use to register additional + * {@link AbstractTypeResolver} instance, to handle resolution of + * abstract to concrete types (either by defaulting, or by materializing). + * + * @param resolver Resolver to add. + */ + public SetupContext addAbstractTypeResolver(AbstractTypeResolver resolver); + + /** + * Method that module can use to register additional + * {@link TypeModifier} instance, which can augment {@link com.fasterxml.jackson.databind.JavaType} + * instances constructed by {@link com.fasterxml.jackson.databind.type.TypeFactory}. + * + * @param modifier to add + */ + public SetupContext addTypeModifier(TypeModifier modifier); /** * Method for registering specified classes as subtypes (of supertype(s) * they have) */ - public void registerSubtypes(Class... subtypes); + public SetupContext registerSubtypes(Class... subtypes); /** * Method for registering specified classes as subtypes (of supertype(s) * they have), using specified type names. */ - public void registerSubtypes(NamedType... subtypes); + public SetupContext registerSubtypes(NamedType... subtypes); /** * Method for registering specified classes as subtypes (of supertype(s) * they have) + */ + public SetupContext registerSubtypes(Collection> subtypes); + + /* + /****************************************************************** + /* Handler registration, other + /****************************************************************** + */ + + /** + * Add a deserialization problem handler * - * @since 2.9 + * @param handler The deserialization problem handler */ - public void registerSubtypes(Collection> subtypes); - + public SetupContext addHandler(DeserializationProblemHandler handler); + + /** + * Replace default {@link InjectableValues} that have been configured to be + * used for mapper being built. + * + * @since 3.0 + */ + public SetupContext overrideInjectableValues(UnaryOperator v); + /** * Method used for defining mix-in annotations to use for augmenting * specified class or interface. @@ -326,21 +350,6 @@ public static interface SetupContext * @param mixinSource Class (or interface) whose annotations are to * be "added" to target's annotations, overriding as necessary */ - public void setMixInAnnotations(Class target, Class mixinSource); - - /** - * Add a deserialization problem handler - * - * @param handler The deserialization problem handler - */ - public void addDeserializationProblemHandler(DeserializationProblemHandler handler); - - /** - * Method that may be used to override naming strategy that is used - * by {@link ObjectMapper}. - * - * @since 2.3 - */ - public void setNamingStrategy(PropertyNamingStrategy naming); + public SetupContext setMixIn(Class target, Class mixinSource); } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java index 1a48ed4bed..14ef08afec 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java +++ b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java @@ -3,34 +3,30 @@ import java.io.*; import java.lang.reflect.Type; import java.net.URL; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.text.DateFormat; -import java.util.*; +import java.util.Collection; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicReference; -import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.io.CharacterEscapes; import com.fasterxml.jackson.core.io.SegmentedStringWriter; +import com.fasterxml.jackson.core.json.JsonFactory; import com.fasterxml.jackson.core.type.ResolvedType; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.util.*; + import com.fasterxml.jackson.databind.cfg.*; import com.fasterxml.jackson.databind.deser.*; import com.fasterxml.jackson.databind.exc.MismatchedInputException; import com.fasterxml.jackson.databind.introspect.*; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; -import com.fasterxml.jackson.databind.jsontype.*; -import com.fasterxml.jackson.databind.jsontype.impl.StdSubtypeResolver; -import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder; +import com.fasterxml.jackson.databind.jsontype.SubtypeResolver; import com.fasterxml.jackson.databind.node.*; import com.fasterxml.jackson.databind.ser.*; import com.fasterxml.jackson.databind.type.*; import com.fasterxml.jackson.databind.util.ClassUtil; import com.fasterxml.jackson.databind.util.RootNameLookup; -import com.fasterxml.jackson.databind.util.StdDateFormat; import com.fasterxml.jackson.databind.util.TokenBuffer; /** @@ -38,11 +34,22 @@ * either to and from basic POJOs (Plain Old Java Objects), or to and from * a general-purpose JSON Tree Model ({@link JsonNode}), as well as * related functionality for performing conversions. - * It is also highly customizable to work both with different styles of JSON - * content, and to support more advanced Object concepts such as - * polymorphism and Object identity. - * ObjectMapper also acts as a factory for more advanced {@link ObjectReader} - * and {@link ObjectWriter} classes. + * In addition to directly reading and writing JSON (and with different underlying + * {@link TokenStreamFactory} configuration, other formats), it is also the + * mechanism for creating {@link ObjectReader}s and {@link ObjectWriter}s which + * offer more advancing reading/writing functionality. + *

+ * Construction of mapper instances proceeds either via no-arguments constructor + * (producing instance with default configuration); or through one of two build + * methods. + * First build method is the static builder() on exact type + * and second {@link #rebuild()} method method on an existing mapper. + * Former starts with default configuration (same as one that no-arguments constructor + * created mapper has), and latter starts with configuration of the mapper it is called + * on. + * In both cases, after configuration (including addition of {@link Module}s) is complete, + * instance is created by calling {@link MapperBuilder#build()} method. + *

* Mapper (and {@link ObjectReader}s, {@link ObjectWriter}s it constructs) will * use instances of {@link JsonParser} and {@link JsonGenerator} * for implementing actual reading/writing of JSON. @@ -67,37 +74,8 @@ // and find values by, for example, using a {@link com.fasterxml.jackson.core.JsonPointer} expression: int age = root.at("/personal/age").getValueAsInt(); - *

- * The main conversion API is defined in {@link ObjectCodec}, so that - * implementation details of this class need not be exposed to - * streaming parser and generator classes. Usage via {@link ObjectCodec} is, - * however, usually only for cases where dependency to {@link ObjectMapper} is - * either not possible (from Streaming API), or undesireable (when only relying - * on Streaming API). *

- * Mapper instances are fully thread-safe provided that ALL configuration of the - * instance occurs before ANY read or write calls. If configuration of a mapper instance - * is modified after first usage, changes may or may not take effect, and configuration - * calls themselves may fail. - * If you need to use different configuration, you have two main possibilities: - *

    - *
  • Construct and use {@link ObjectReader} for reading, {@link ObjectWriter} for writing. - * Both types are fully immutable and you can freely create new instances with different - * configuration using either factory methods of {@link ObjectMapper}, or readers/writers - * themselves. Construction of new {@link ObjectReader}s and {@link ObjectWriter}s is - * a very light-weight operation so it is usually appropriate to create these on per-call - * basis, as needed, for configuring things like optional indentation of JSON. - *
  • - *
  • If the specific kind of configurability is not available via {@link ObjectReader} and - * {@link ObjectWriter}, you may need to use multiple {@link ObjectMapper} instead (for example: - * you cannot change mix-in annotations on-the-fly; or, set of custom (de)serializers). - * To help with this usage, you may want to use method {@link #copy()} which creates a clone - * of the mapper with specific configuration, and allows configuration of the copied instance - * before it gets used. Note that {@link #copy} operation is as expensive as constructing - * a new {@link ObjectMapper} instance: if possible, you should still pool and reuse mappers - * if you intend to use them for multiple operations. - *
  • - *
+ * Mapper instances are fully thread-safe as of Jackson 3.0. *

* Note on caching: root-level deserializers are always cached, and accessed * using full (generics-aware) type information. This is different from @@ -108,176 +86,63 @@ * produce differing deserializers), and that the performance impact * greatest at root level (since it'll essentially cache the full * graph of deserializers involved). - *

- * Notes on security: use "default typing" feature (see {@link #enableDefaultTyping()}) - * is a potential security risk, if used with untrusted content (content generated by - * untrusted external parties). If so, you may want to construct a custom - * {@link TypeResolverBuilder} implementation to limit possible types to instantiate, - * (using {@link #setDefaultTyping}). */ public class ObjectMapper - extends ObjectCodec implements Versioned, - java.io.Serializable // as of 2.1 + java.io.Serializable { - private static final long serialVersionUID = 2L; // as of 2.9 + private static final long serialVersionUID = 3L; /* - /********************************************************** + /********************************************************************** /* Helper classes, enums - /********************************************************** + /********************************************************************** */ /** - * Enumeration used with {@link ObjectMapper#enableDefaultTyping()} - * to specify what kind of types (classes) default typing should - * be used for. It will only be used if no explicit type information - * is found, but this enumeration further limits subset of those types. - *

- * Since 2.4 there are special exceptions for JSON Tree model - * types (sub-types of {@link TreeNode}: default typing is never - * applied to them. - * Since 2.8(.4) additional checks are made to avoid attempts at default - * typing primitive-valued properties. - *

- * NOTE: use of Default Typing can be a potential security risk if incoming - * content comes from untrusted sources, and it is recommended that this - * is either not done, or, if enabled, use {@link #setDefaultTyping} - * passing a custom {@link TypeResolverBuilder} implementation that white-lists - * legal types to use. - */ - public enum DefaultTyping { - /** - * This value means that only properties that have - * {@link java.lang.Object} as declared type (including - * generic types without explicit type) will use default - * typing. - */ - JAVA_LANG_OBJECT, - - /** - * Value that means that default typing will be used for - * properties with declared type of {@link java.lang.Object} - * or an abstract type (abstract class or interface). - * Note that this does not include array types. - *

- * Since 2.4, this does NOT apply to {@link TreeNode} and its subtypes. - */ - OBJECT_AND_NON_CONCRETE, - - /** - * Value that means that default typing will be used for - * all types covered by {@link #OBJECT_AND_NON_CONCRETE} - * plus all array types for them. - *

- * Since 2.4, this does NOT apply to {@link TreeNode} and its subtypes. - */ - NON_CONCRETE_AND_ARRAYS, - - /** - * Value that means that default typing will be used for - * all non-final types, with exception of small number of - * "natural" types (String, Boolean, Integer, Double), which - * can be correctly inferred from JSON; as well as for - * all arrays of non-final types. - *

- * Since 2.4, this does NOT apply to {@link TreeNode} and its subtypes. - */ - NON_FINAL - } - - /** - * Customized {@link TypeResolverBuilder} that provides type resolver builders - * used with so-called "default typing" - * (see {@link ObjectMapper#enableDefaultTyping()} for details). - *

- * Type resolver construction is based on configuration: implementation takes care - * of only providing builders in cases where type information should be applied. - * This is important since build calls may be sent for any and all types, and - * type information should NOT be applied to all of them. - */ - public static class DefaultTypeResolverBuilder - extends StdTypeResolverBuilder - implements java.io.Serializable + * Base implementation for "Vanilla" {@link ObjectMapper}, only defined to support + * backwards-compatibility with some of 2.x usage patterns. + */ + private static class PrivateBuilder extends MapperBuilder { - private static final long serialVersionUID = 1L; - - /** - * Definition of what types is this default typer valid for. - */ - protected final DefaultTyping _appliesFor; - - public DefaultTypeResolverBuilder(DefaultTyping t) { - _appliesFor = t; + public PrivateBuilder(TokenStreamFactory tsf) { + super(tsf); } @Override - public TypeDeserializer buildTypeDeserializer(DeserializationConfig config, - JavaType baseType, Collection subtypes) - { - return useForType(baseType) ? super.buildTypeDeserializer(config, baseType, subtypes) : null; + public ObjectMapper build() { + return new ObjectMapper(this); } @Override - public TypeSerializer buildTypeSerializer(SerializationConfig config, - JavaType baseType, Collection subtypes) - { - return useForType(baseType) ? super.buildTypeSerializer(config, baseType, subtypes) : null; + protected MapperBuilderState _saveState() { + return new StateImpl(this); } - /** - * Method called to check if the default type handler should be - * used for given type. - * Note: "natural types" (String, Boolean, Integer, Double) will never - * use typing; that is both due to them being concrete and final, - * and since actual serializers and deserializers will also ignore any - * attempts to enforce typing. - */ - public boolean useForType(JavaType t) - { - // 03-Oct-2016, tatu: As per [databind#1395], need to skip - // primitive types too, regardless - if (t.isPrimitive()) { - return false; + public PrivateBuilder(MapperBuilderState state) { + super(state); + } + + // We also need actual instance of state as base class can not implement logic + // for reinstating mapper (via mapper builder) from state. + static class StateImpl extends MapperBuilderState { + private static final long serialVersionUID = 3L; + + public StateImpl(PrivateBuilder b) { + super(b); } - switch (_appliesFor) { - case NON_CONCRETE_AND_ARRAYS: - while (t.isArrayType()) { - t = t.getContentType(); - } - // fall through - case OBJECT_AND_NON_CONCRETE: - // 19-Apr-2016, tatu: ReferenceType like Optional also requires similar handling: - while (t.isReferenceType()) { - t = t.getReferencedType(); - } - return t.isJavaLangObject() - || (!t.isConcrete() - // [databind#88] Should not apply to JSON tree models: - && !TreeNode.class.isAssignableFrom(t.getRawClass())); - - case NON_FINAL: - while (t.isArrayType()) { - t = t.getContentType(); - } - // 19-Apr-2016, tatu: ReferenceType like Optional also requires similar handling: - while (t.isReferenceType()) { - t = t.getReferencedType(); - } - // [databind#88] Should not apply to JSON tree models: - return !t.isFinal() && !TreeNode.class.isAssignableFrom(t.getRawClass()); - default: - //case JAVA_LANG_OBJECT: - return t.isJavaLangObject(); + @Override + protected Object readResolve() { + return new PrivateBuilder(this).build(); } } } /* - /********************************************************** + /********************************************************************** /* Internal constants, singletons - /********************************************************** + /********************************************************************** */ // Quick little shortcut, to avoid having to use global TypeFactory instance... @@ -287,150 +152,72 @@ public boolean useForType(JavaType t) SimpleType.constructUnsafe(JsonNode.class); // TypeFactory.defaultInstance().constructType(JsonNode.class); - // 16-May-2009, tatu: Ditto ^^^ - protected final static AnnotationIntrospector DEFAULT_ANNOTATION_INTROSPECTOR = new JacksonAnnotationIntrospector(); - - /** - * Base settings contain defaults used for all {@link ObjectMapper} - * instances. - */ - protected final static BaseSettings DEFAULT_BASE = new BaseSettings( - null, // cannot share global ClassIntrospector any more (2.5+) - DEFAULT_ANNOTATION_INTROSPECTOR, - null, TypeFactory.defaultInstance(), - null, StdDateFormat.instance, null, - Locale.getDefault(), - null, // to indicate "use Jackson default TimeZone" (UTC since Jackson 2.7) - Base64Variants.getDefaultVariant() // 2.1 - ); - /* - /********************************************************** + /********************************************************************** /* Configuration settings, shared - /********************************************************** + /********************************************************************** */ /** * Factory used to create {@link JsonParser} and {@link JsonGenerator} * instances as necessary. */ - protected final JsonFactory _jsonFactory; + protected final TokenStreamFactory _streamFactory; /** * Specific factory used for creating {@link JavaType} instances; * needed to allow modules to add more custom type handling * (mostly to support types of non-Java JVM languages) */ - protected TypeFactory _typeFactory; + protected final TypeFactory _typeFactory; /** * Provider for values to inject in deserialized POJOs. */ - protected InjectableValues _injectableValues; - - /** - * Thing used for registering sub-types, resolving them to - * super/sub-types as needed. - */ - protected SubtypeResolver _subtypeResolver; - - /** - * Currently active per-type configuration overrides, accessed by - * declared type of property. - * - * @since 2.9 - */ - protected final ConfigOverrides _configOverrides; - - /* - /********************************************************** - /* Configuration settings: mix-in annotations - /********************************************************** - */ - - /** - * Mapping that defines how to apply mix-in annotations: key is - * the type to received additional annotations, and value is the - * type that has annotations to "mix in". - *

- * Annotations associated with the value classes will be used to - * override annotations of the key class, associated with the - * same field or method. They can be further masked by sub-classes: - * you can think of it as injecting annotations between the target - * class and its sub-classes (or interfaces) - * - * @since 2.6 (earlier was a simple {@link java.util.Map} - */ - protected SimpleMixInResolver _mixIns; + protected final InjectableValues _injectableValues; /* - /********************************************************** + /********************************************************************** /* Configuration settings, serialization - /********************************************************** - */ - - /** - * Configuration object that defines basic global - * settings for the serialization process + /********************************************************************** */ - protected SerializationConfig _serializationConfig; /** - * Object that manages access to serializers used for serialization, - * including caching. - * It is configured with {@link #_serializerFactory} to allow - * for constructing custom serializers. + * Factory used for constructing per-call {@link SerializerProvider}s. *

* Note: while serializers are only exposed {@link SerializerProvider}, * mappers and readers need to access additional API defined by * {@link DefaultSerializerProvider} */ - protected DefaultSerializerProvider _serializerProvider; + protected final SerializationContexts _serializationContexts; /** - * Serializer factory used for constructing serializers. + * Configuration object that defines basic global + * settings for the serialization process */ - protected SerializerFactory _serializerFactory; + protected final SerializationConfig _serializationConfig; /* - /********************************************************** + /********************************************************************** /* Configuration settings, deserialization - /********************************************************** + /********************************************************************** */ /** - * Configuration object that defines basic global - * settings for the serialization process - */ - protected DeserializationConfig _deserializationConfig; - - /** - * Blueprint context object; stored here to allow custom - * sub-classes. Contains references to objects needed for - * deserialization construction (cache, factory). - */ - protected DefaultDeserializationContext _deserializationContext; - - /* - /********************************************************** - /* Module-related - /********************************************************** + * Factory used for constructing per-call {@link DeserializationContext}s. */ + protected final DeserializationContexts _deserializationContexts; /** - * Set of module types (as per {@link Module#getTypeId()} that have been - * registered; kept track of iff {@link MapperFeature#IGNORE_DUPLICATE_MODULE_REGISTRATIONS} - * is enabled, so that duplicate registration calls can be ignored - * (to avoid adding same handlers multiple times, mostly). - * - * @since 2.5 + * Configuration object that defines basic global + * settings for the serialization process */ - protected Set _registeredModuleTypes; + protected final DeserializationConfig _deserializationConfig; /* - /********************************************************** + /********************************************************************** /* Caching - /********************************************************** + /********************************************************************** */ /* Note: handling of serializers and deserializers is not symmetric; @@ -451,250 +238,155 @@ public boolean useForType(JavaType t) * (should very quickly converge to zero after startup), let's * explicitly define a low concurrency setting. *

- * Since version 1.5, these may are either "raw" deserializers (when + * These may are either "raw" deserializers (when * no type information is needed for base type), or type-wrapped * deserializers (if it is needed) */ - final protected ConcurrentHashMap> _rootDeserializers + protected final ConcurrentHashMap> _rootDeserializers = new ConcurrentHashMap>(64, 0.6f, 2); /* - /********************************************************** - /* Life-cycle: constructing instance - /********************************************************** + /********************************************************************** + /* Saved state to allow re-building + /********************************************************************** */ /** - * Default constructor, which will construct the default - * {@link JsonFactory} as necessary, use - * {@link SerializerProvider} as its - * {@link SerializerProvider}, and - * {@link BeanSerializerFactory} as its - * {@link SerializerFactory}. - * This means that it - * can serialize all standard JDK types, as well as regular - * Java Beans (based on method names and Jackson-specific annotations), - * but does not support JAXB annotations. + * Minimal state retained to allow both re-building (by + * creating new builder) and JDK serialization of this mapper. + * + * @since 3.0 */ - public ObjectMapper() { - this(null, null, null); - } + protected final MapperBuilderState _savedBuilderState; - /** - * Constructs instance that uses specified {@link JsonFactory} - * for constructing necessary {@link JsonParser}s and/or - * {@link JsonGenerator}s. + /* + /********************************************************************** + /* Life-cycle: legacy constructors + /********************************************************************** */ - public ObjectMapper(JsonFactory jf) { - this(jf, null, null); - } /** - * Copy-constructor, mostly used to support {@link #copy}. - * - * @since 2.1 + * Default constructor, which will construct the default JSON-handling + * {@link TokenStreamFactory} as necessary and all other unmodified + * default settings, and no additional registered modules. */ - protected ObjectMapper(ObjectMapper src) - { - _jsonFactory = src._jsonFactory.copy(); - _jsonFactory.setCodec(this); - _subtypeResolver = src._subtypeResolver; - _typeFactory = src._typeFactory; - _injectableValues = src._injectableValues; - _configOverrides = src._configOverrides.copy(); - _mixIns = src._mixIns.copy(); - - RootNameLookup rootNames = new RootNameLookup(); - _serializationConfig = new SerializationConfig(src._serializationConfig, - _mixIns, rootNames, _configOverrides); - _deserializationConfig = new DeserializationConfig(src._deserializationConfig, - _mixIns, rootNames, _configOverrides); - _serializerProvider = src._serializerProvider.copy(); - _deserializationContext = src._deserializationContext.copy(); - - // Default serializer factory is stateless, can just assign - _serializerFactory = src._serializerFactory; - - // as per [databind#922], [databind#1078] make sure to copy registered modules as appropriate - Set reg = src._registeredModuleTypes; - if (reg == null) { - _registeredModuleTypes = null; - } else { - _registeredModuleTypes = new LinkedHashSet(reg); - } + public ObjectMapper() { + this(new PrivateBuilder(new JsonFactory())); } /** - * Constructs instance that uses specified {@link JsonFactory} + * Constructs instance that uses specified {@link TokenStreamFactory} * for constructing necessary {@link JsonParser}s and/or - * {@link JsonGenerator}s, and uses given providers for accessing - * serializers and deserializers. - * - * @param jf JsonFactory to use: if null, a new {@link MappingJsonFactory} will be constructed - * @param sp SerializerProvider to use: if null, a {@link SerializerProvider} will be constructed - * @param dc Blueprint deserialization context instance to use for creating - * actual context objects; if null, will construct standard - * {@link DeserializationContext} - */ - public ObjectMapper(JsonFactory jf, - DefaultSerializerProvider sp, DefaultDeserializationContext dc) - { - /* 02-Mar-2009, tatu: Important: we MUST default to using - * the mapping factory, otherwise tree serialization will - * have problems with POJONodes. - * 03-Jan-2010, tatu: and obviously we also must pass 'this', - * to create actual linking. - */ - if (jf == null) { - _jsonFactory = new MappingJsonFactory(this); - } else { - _jsonFactory = jf; - if (jf.getCodec() == null) { // as per [JACKSON-741] - _jsonFactory.setCodec(this); - } - } - _subtypeResolver = new StdSubtypeResolver(); - RootNameLookup rootNames = new RootNameLookup(); - // and default type factory is shared one - _typeFactory = TypeFactory.defaultInstance(); - - SimpleMixInResolver mixins = new SimpleMixInResolver(null); - _mixIns = mixins; - BaseSettings base = DEFAULT_BASE.withClassIntrospector(defaultClassIntrospector()); - _configOverrides = new ConfigOverrides(); - _serializationConfig = new SerializationConfig(base, - _subtypeResolver, mixins, rootNames, _configOverrides); - _deserializationConfig = new DeserializationConfig(base, - _subtypeResolver, mixins, rootNames, _configOverrides); - - // Some overrides we may need - final boolean needOrder = _jsonFactory.requiresPropertyOrdering(); - if (needOrder ^ _serializationConfig.isEnabled(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY)) { - configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, needOrder); - } - - _serializerProvider = (sp == null) ? new DefaultSerializerProvider.Impl() : sp; - _deserializationContext = (dc == null) ? - new DefaultDeserializationContext.Impl(BeanDeserializerFactory.instance) : dc; - - // Default serializer factory is stateless, can just assign - _serializerFactory = BeanSerializerFactory.instance; - } - - /** - * Overridable helper method used to construct default {@link ClassIntrospector} - * to use. - * - * @since 2.5 + * {@link JsonGenerator}s, but without registering additional modules. */ - protected ClassIntrospector defaultClassIntrospector() { - return new BasicClassIntrospector(); + public ObjectMapper(TokenStreamFactory streamFactory) { + this(new PrivateBuilder(streamFactory)); } /* - /********************************************************** - /* 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} - * instance. - *

- * Method is typically - * used when multiple, differently configured mappers are needed. - * Although configuration is shared, cached serializers and deserializers - * are NOT shared, which means that the new instance may be re-configured - * before use; meaning that it behaves the same way as if an instance - * was constructed from scratch. - * - * @since 2.1 + /********************************************************************** + /* Life-cycle: builder-style construction + /********************************************************************** */ - public ObjectMapper copy() { - _checkInvalidCopy(ObjectMapper.class); - return new ObjectMapper(this); - } /** - * @since 2.1 + * Constructor usually called either by {@link MapperBuilder#build} or + * by sub-class constructor: will get all the settings through passed-in + * builder, including registration of any modules added to builder. */ - protected void _checkInvalidCopy(Class exp) + protected ObjectMapper(MapperBuilder builder) { - if (getClass() != exp) { - // 10-Nov-2016, tatu: could almost use `ClassUtil.verifyMustOverride()` but not quite - throw new IllegalStateException("Failed copy(): "+getClass().getName() - +" (version: "+version()+") does not override copy(); it has to"); + // First things first: finalize building process. Saved state + // consists of snapshots and is safe to keep references to; used + // for rebuild()ing mapper instances + + _savedBuilderState = builder.saveStateApplyModules(); + + // But we will ALSO need to take snapshot of anything builder has, + // in case caller keeps on tweaking with builder. So rules are the + // as with above call, or when creating new builder for rebuild()ing + + // General framework factories + _streamFactory = builder.streamFactory(); + final ConfigOverrides configOverrides; + { + // bit tricky as we do NOT want to expose simple accessors (to a mutable thing) + final AtomicReference ref = new AtomicReference<>(); + builder.withAllConfigOverrides(overrides -> ref.set(overrides)); + configOverrides = Snapshottable.takeSnapshot(ref.get()); } - } - /* - /********************************************************** - /* Methods sub-classes MUST override if providing custom - /* ObjectReader/ObjectWriter implementations - /********************************************************** - */ - - /** - * Factory method sub-classes must override, to produce {@link ObjectReader} - * instances of proper sub-type - * - * @since 2.5 - */ - protected ObjectReader _newReader(DeserializationConfig config) { - return new ObjectReader(this, config); - } + // Handlers, introspection + _typeFactory = Snapshottable.takeSnapshot(builder.typeFactory()); + ClassIntrospector classIntr = builder.classIntrospector().forMapper(this); + SubtypeResolver subtypeResolver = Snapshottable.takeSnapshot(builder.subtypeResolver()); + MixInHandler mixIns = (MixInHandler) Snapshottable.takeSnapshot(builder.mixInHandler()); + // NOTE: TypeResolverProvider apparently ok without snapshot, hence config objects fetch + // it directly from MapperBuilder, not passed by us. - /** - * 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); + // Serialization factories + _serializationContexts = builder.serializationContexts() + .forMapper(this, _streamFactory, builder.serializerFactory()); + + // Deserialization factories + + _deserializationContexts = builder.deserializationContexts() + .forMapper(this, _streamFactory, builder.deserializerFactory()); + _injectableValues = Snapshottable.takeSnapshot(builder.injectableValues()); + + // And then finalize serialization/deserialization Config containers + + RootNameLookup rootNames = new RootNameLookup(); + FilterProvider filterProvider = Snapshottable.takeSnapshot(builder.filterProvider()); + _deserializationConfig = builder.buildDeserializationConfig(configOverrides, + mixIns, _typeFactory, classIntr, subtypeResolver, + rootNames); + _serializationConfig = builder.buildSerializationConfig(configOverrides, + mixIns, _typeFactory, classIntr, subtypeResolver, + rootNames, filterProvider); } /** - * Factory method sub-classes must override, to produce {@link ObjectWriter} - * instances of proper sub-type - * - * @since 2.5 + * Method for creating a new {@link MapperBuilder} for constructing differently configured + * {@link ObjectMapper} instance, starting with current configuration including base settings + * and registered modules. + * + * @since 3.0 */ - protected ObjectWriter _newWriter(SerializationConfig config) { - return new ObjectWriter(this, config); + @SuppressWarnings("unchecked") + public > MapperBuilder rebuild() { + // 27-Feb-2018, tatu: since we still have problem with `ObjectMapper` being both API + // and implementation for JSON, need more checking here + ClassUtil.verifyMustOverride(ObjectMapper.class, this, "rebuild"); + return (MapperBuilder) new PrivateBuilder(_savedBuilderState); } - /** - * Factory method sub-classes must override, to produce {@link ObjectWriter} - * instances of proper sub-type - * - * @since 2.5 + /* + /********************************************************************** + /* Life-cycle: JDK serialization support + /********************************************************************** */ - protected ObjectWriter _newWriter(SerializationConfig config, FormatSchema schema) { - return new ObjectWriter(this, config, schema); + + // Logic here is simple: instead of serializing mapper via its contents, + // we have pre-packaged `MapperBuilderState` in a way that makes serialization + // easier, and we go with that. + // But note that return direction has to be supported, then, by that state object + // and NOT anything in here. + protected Object writeReplace() { + return _savedBuilderState; } - - /** - * 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); + + // Just as a sanity check verify there is no attempt at directly instantiating mapper here + protected Object readResolve() { + throw new IllegalStateException("Should never deserialize `"+getClass().getName()+"` directly"); } /* - /********************************************************** + /********************************************************************** /* Versioned impl - /********************************************************** + /********************************************************************** */ - + /** * Method that will return version information stored in and read from jar * that contains this class. @@ -704,1238 +396,110 @@ public Version version() { return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION; } - /* - /********************************************************** - /* Module registration, discovery - /********************************************************** - */ - - /** - * Method for registering a module that can extend functionality - * provided by this mapper; for example, by adding providers for - * custom serializers and deserializers. - * - * @param module Module to register - */ - public ObjectMapper registerModule(Module module) - { - if (isEnabled(MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS)) { - Object typeId = module.getTypeId(); - if (typeId != null) { - if (_registeredModuleTypes == null) { - // plus let's keep them in order too, easier to debug or expose - // in registration order if that matter - _registeredModuleTypes = new LinkedHashSet(); - } - // try adding; if already had it, should skip - if (!_registeredModuleTypes.add(typeId)) { - return this; - } - } - } - - /* Let's ensure we have access to name and version information, - * even if we do not have immediate use for either. This way we know - * that they will be available from beginning - */ - String name = module.getModuleName(); - if (name == null) { - throw new IllegalArgumentException("Module without defined name"); - } - Version version = module.version(); - if (version == null) { - throw new IllegalArgumentException("Module without defined version"); - } - - // And then call registration - module.setupModule(new Module.SetupContext() - { - // // // Accessors - - @Override - public Version getMapperVersion() { - return version(); - } - - @SuppressWarnings("unchecked") - @Override - public C getOwner() { - // why do we need the cast here?!? - return (C) ObjectMapper.this; - } - - @Override - public TypeFactory getTypeFactory() { - return _typeFactory; - } - - @Override - public boolean isEnabled(MapperFeature f) { - return ObjectMapper.this.isEnabled(f); - } - - @Override - public boolean isEnabled(DeserializationFeature f) { - return ObjectMapper.this.isEnabled(f); - } - - @Override - public boolean isEnabled(SerializationFeature f) { - return ObjectMapper.this.isEnabled(f); - } - - @Override - public boolean isEnabled(JsonFactory.Feature f) { - return ObjectMapper.this.isEnabled(f); - } - - @Override - public boolean isEnabled(JsonParser.Feature f) { - return ObjectMapper.this.isEnabled(f); - } - - @Override - public boolean isEnabled(JsonGenerator.Feature f) { - return ObjectMapper.this.isEnabled(f); - } - - // // // Mutant accessors - - @Override - public MutableConfigOverride configOverride(Class type) { - return ObjectMapper.this.configOverride(type); - } - - // // // Methods for registering handlers: deserializers - - @Override - public void addDeserializers(Deserializers d) { - DeserializerFactory df = _deserializationContext._factory.withAdditionalDeserializers(d); - _deserializationContext = _deserializationContext.with(df); - } - - @Override - public void addKeyDeserializers(KeyDeserializers d) { - DeserializerFactory df = _deserializationContext._factory.withAdditionalKeyDeserializers(d); - _deserializationContext = _deserializationContext.with(df); - } - - @Override - public void addBeanDeserializerModifier(BeanDeserializerModifier modifier) { - DeserializerFactory df = _deserializationContext._factory.withDeserializerModifier(modifier); - _deserializationContext = _deserializationContext.with(df); - } - - // // // Methods for registering handlers: serializers - - @Override - public void addSerializers(Serializers s) { - _serializerFactory = _serializerFactory.withAdditionalSerializers(s); - } - - @Override - public void addKeySerializers(Serializers s) { - _serializerFactory = _serializerFactory.withAdditionalKeySerializers(s); - } - - @Override - public void addBeanSerializerModifier(BeanSerializerModifier modifier) { - _serializerFactory = _serializerFactory.withSerializerModifier(modifier); - } - - // // // Methods for registering handlers: other - - @Override - public void addAbstractTypeResolver(AbstractTypeResolver resolver) { - DeserializerFactory df = _deserializationContext._factory.withAbstractTypeResolver(resolver); - _deserializationContext = _deserializationContext.with(df); - } - - @Override - public void addTypeModifier(TypeModifier modifier) { - TypeFactory f = _typeFactory; - f = f.withModifier(modifier); - setTypeFactory(f); - } - - @Override - public void addValueInstantiators(ValueInstantiators instantiators) { - DeserializerFactory df = _deserializationContext._factory.withValueInstantiators(instantiators); - _deserializationContext = _deserializationContext.with(df); - } - - @Override - public void setClassIntrospector(ClassIntrospector ci) { - _deserializationConfig = _deserializationConfig.with(ci); - _serializationConfig = _serializationConfig.with(ci); - } - - @Override - public void insertAnnotationIntrospector(AnnotationIntrospector ai) { - _deserializationConfig = _deserializationConfig.withInsertedAnnotationIntrospector(ai); - _serializationConfig = _serializationConfig.withInsertedAnnotationIntrospector(ai); - } - - @Override - public void appendAnnotationIntrospector(AnnotationIntrospector ai) { - _deserializationConfig = _deserializationConfig.withAppendedAnnotationIntrospector(ai); - _serializationConfig = _serializationConfig.withAppendedAnnotationIntrospector(ai); - } - - @Override - public void registerSubtypes(Class... subtypes) { - ObjectMapper.this.registerSubtypes(subtypes); - } - - @Override - public void registerSubtypes(NamedType... subtypes) { - ObjectMapper.this.registerSubtypes(subtypes); - } - - @Override - public void registerSubtypes(Collection> subtypes) { - ObjectMapper.this.registerSubtypes(subtypes); - } - - @Override - public void setMixInAnnotations(Class target, Class mixinSource) { - addMixIn(target, mixinSource); - } - - @Override - public void addDeserializationProblemHandler(DeserializationProblemHandler handler) { - addHandler(handler); - } - - @Override - public void setNamingStrategy(PropertyNamingStrategy naming) { - setPropertyNamingStrategy(naming); - } - }); - return this; - } - - /** - * Convenience method for registering specified modules in order; - * functionally equivalent to: - *
-     *   for (Module module : modules) {
-     *      registerModule(module);
-     *   }
-     *
- * - * @since 2.2 - */ - public ObjectMapper registerModules(Module... modules) - { - for (Module module : modules) { - registerModule(module); - } - return this; - } - - /** - * Convenience method for registering specified modules in order; - * functionally equivalent to: - *
-     *   for (Module module : modules) {
-     *      registerModule(module);
-     *   }
-     *
- * - * @since 2.2 - */ - public ObjectMapper registerModules(Iterable modules) - { - for (Module module : modules) { - registerModule(module); - } - return this; - } - - /** - * The set of {@link Module} typeIds that are registered in this - * ObjectMapper. By default the typeId for a module is it's full - * class name (see {@link Module#getTypeId()}). - * - * @since 2.9.6 - */ - public Set getRegisteredModuleIds() - { - return Collections.unmodifiableSet(_registeredModuleTypes); - } - - /** - * Method for locating available methods, using JDK {@link ServiceLoader} - * facility, along with module-provided SPI. - *

- * Note that method does not do any caching, so calls should be considered - * potentially expensive. - * - * @since 2.2 - */ - public static List findModules() { - return findModules(null); - } - - /** - * Method for locating available methods, using JDK {@link ServiceLoader} - * facility, along with module-provided SPI. - *

- * Note that method does not do any caching, so calls should be considered - * potentially expensive. - * - * @since 2.2 - */ - public static List findModules(ClassLoader classLoader) - { - ArrayList modules = new ArrayList(); - ServiceLoader loader = secureGetServiceLoader(Module.class, classLoader); - for (Module module : loader) { - modules.add(module); - } - return modules; - } - - private static ServiceLoader secureGetServiceLoader(final Class clazz, final ClassLoader classLoader) { - final SecurityManager sm = System.getSecurityManager(); - if (sm == null) { - return (classLoader == null) ? - ServiceLoader.load(clazz) : ServiceLoader.load(clazz, classLoader); - } - return AccessController.doPrivileged(new PrivilegedAction>() { - @Override - public ServiceLoader run() { - return (classLoader == null) ? - ServiceLoader.load(clazz) : ServiceLoader.load(clazz, classLoader); - } - }); - } - - /** - * Convenience method that is functionally equivalent to: - * - * mapper.registerModules(mapper.findModules()); - * - *

- * As with {@link #findModules()}, no caching is done for modules, so care - * needs to be taken to either create and share a single mapper instance; - * or to cache introspected set of modules. - * - * @since 2.2 - */ - public ObjectMapper findAndRegisterModules() { - return registerModules(findModules()); - } - - /* - /********************************************************** - /* Configuration: main config object access - /********************************************************** - */ - - /** - * Method that returns the shared default {@link SerializationConfig} - * object that defines configuration settings for serialization. - *

- * Note that since instances are immutable, you can NOT change settings - * by accessing an instance and calling methods: this will simply create - * new instance of config object. - */ - public SerializationConfig getSerializationConfig() { - return _serializationConfig; - } - - /** - * Method that returns - * the shared default {@link DeserializationConfig} object - * that defines configuration settings for deserialization. - *

- * Note that since instances are immutable, you can NOT change settings - * by accessing an instance and calling methods: this will simply create - * new instance of config object. - */ - public DeserializationConfig getDeserializationConfig() { - return _deserializationConfig; - } - - /** - * Method for getting current {@link DeserializationContext}. - *

- * Note that since instances are immutable, you can NOT change settings - * by accessing an instance and calling methods: this will simply create - * new instance of context object. - */ - public DeserializationContext getDeserializationContext() { - return _deserializationContext; - } - - /* - /********************************************************** - /* Configuration: ser/deser factory, provider access - /********************************************************** - */ - - /** - * Method for setting specific {@link SerializerFactory} to use - * for constructing (bean) serializers. - */ - public ObjectMapper setSerializerFactory(SerializerFactory f) { - _serializerFactory = f; - return this; - } - - /** - * Method for getting current {@link SerializerFactory}. - *

- * Note that since instances are immutable, you can NOT change settings - * by accessing an instance and calling methods: this will simply create - * new instance of factory object. - */ - public SerializerFactory getSerializerFactory() { - return _serializerFactory; - } - - /** - * Method for setting "blueprint" {@link SerializerProvider} instance - * to use as the base for actual provider instances to use for handling - * caching of {@link JsonSerializer} instances. - */ - public ObjectMapper setSerializerProvider(DefaultSerializerProvider p) { - _serializerProvider = p; - return this; - } - - /** - * Accessor for the "blueprint" (or, factory) instance, from which instances - * are created by calling {@link DefaultSerializerProvider#createInstance}. - * Note that returned instance cannot be directly used as it is not properly - * configured: to get a properly configured instance to call, use - * {@link #getSerializerProviderInstance()} instead. - */ - public SerializerProvider getSerializerProvider() { - return _serializerProvider; - } - - /** - * Accessor for constructing and returning a {@link SerializerProvider} - * instance that may be used for accessing serializers. This is same as - * calling {@link #getSerializerProvider}, and calling createInstance - * on it. - * - * @since 2.7 - */ - public SerializerProvider getSerializerProviderInstance() { - return _serializerProvider(_serializationConfig); - } - - /* - /********************************************************** - /* Configuration: mix-in annotations - /********************************************************** - */ - - /** - * Method to use for defining mix-in annotations to use for augmenting - * annotations that processable (serializable / deserializable) - * classes have. - * Mixing in is done when introspecting class annotations and properties. - * Map passed contains keys that are target classes (ones to augment - * with new annotation overrides), and values that are source classes - * (have annotations to use for augmentation). - * Annotations from source classes (and their supertypes) - * will override - * annotations that target classes (and their super-types) have. - *

- * Note that this method will CLEAR any previously defined mix-ins - * for this mapper. - * - * @since 2.5 - */ - public ObjectMapper setMixIns(Map, Class> sourceMixins) - { - // NOTE: does NOT change possible externally configured resolver, just local defs - _mixIns.setLocalDefinitions(sourceMixins); - return this; - } - - /** - * Method to use for adding mix-in annotations to use for augmenting - * specified class or interface. All annotations from - * mixinSource are taken to override annotations - * that target (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 ObjectMapper addMixIn(Class target, Class mixinSource) - { - _mixIns.addLocalDefinition(target, mixinSource); - return this; - } - - /** - * Method that can be called to specify given resolver for locating - * mix-in classes to use, overriding directly added mappings. - * Note that direct mappings are not cleared, but they are only applied - * if resolver does not provide mix-in matches. - * - * @since 2.6 - */ - public ObjectMapper setMixInResolver(ClassIntrospector.MixInResolver resolver) - { - SimpleMixInResolver r = _mixIns.withOverrides(resolver); - if (r != _mixIns) { - _mixIns = r; - _deserializationConfig = new DeserializationConfig(_deserializationConfig, r); - _serializationConfig = new SerializationConfig(_serializationConfig, r); - } - return this; - } - - public Class findMixInClassFor(Class cls) { - return _mixIns.findMixInClassFor(cls); - } - - // For testing only: - public int mixInCount() { - return _mixIns.localSize(); - } - - /** - * @deprecated Since 2.5: replaced by a fluent form of the method; {@link #setMixIns}. - */ - @Deprecated - public void setMixInAnnotations(Map, 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); - } - - /* - /********************************************************** - /* Configuration, introspection - /********************************************************** - */ - - /** - * Method for accessing currently configured visibility checker; - * object used for determining whether given property element - * (method, field, constructor) can be auto-detected or not. - */ - public VisibilityChecker getVisibilityChecker() { - return _serializationConfig.getDefaultVisibilityChecker(); - } - - /** - * Method for setting currently configured default {@link VisibilityChecker}, - * object used for determining whether given property element - * (method, field, constructor) can be auto-detected or not. - * This default checker is used as the base visibility: - * per-class overrides (both via annotations and per-type config overrides) - * can further change these settings. - * - * @since 2.6 - */ - public ObjectMapper setVisibility(VisibilityChecker vc) { - _configOverrides.setDefaultVisibility(vc); - return this; - } - - /** - * Convenience method that allows changing configuration for - * underlying {@link VisibilityChecker}s, to change details of what kinds of - * properties are auto-detected. - * Basically short cut for doing: - *

-     *  mapper.setVisibilityChecker(
-     *     mapper.getVisibilityChecker().withVisibility(forMethod, visibility)
-     *  );
-     *
- * one common use case would be to do: - *
-     *  mapper.setVisibility(JsonMethod.FIELD, Visibility.ANY);
-     *
- * which would make all member fields serializable without further annotations, - * instead of just public fields (default setting). - * - * @param forMethod Type of property descriptor affected (field, getter/isGetter, - * setter, creator) - * @param visibility Minimum visibility to require for the property descriptors of type - * - * @return Modified mapper instance (that is, "this"), to allow chaining - * of configuration calls - */ - public ObjectMapper setVisibility(PropertyAccessor forMethod, JsonAutoDetect.Visibility visibility) - { - VisibilityChecker vc = _configOverrides.getDefaultVisibility(); - vc = vc.withVisibility(forMethod, visibility); - _configOverrides.setDefaultVisibility(vc); - return this; - } - - /** - * Method for accessing subtype resolver in use. - */ - public SubtypeResolver getSubtypeResolver() { - return _subtypeResolver; - } - - /** - * Method for setting custom subtype resolver to use. - */ - public ObjectMapper setSubtypeResolver(SubtypeResolver str) { - _subtypeResolver = str; - _deserializationConfig = _deserializationConfig.with(str); - _serializationConfig = _serializationConfig.with(str); - return this; - } - - /** - * Method for setting {@link AnnotationIntrospector} used by this - * mapper instance for both serialization and deserialization. - * Note that doing this will replace the current introspector, which - * may lead to unavailability of core Jackson annotations. - * If you want to combine handling of multiple introspectors, - * have a look at {@link com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair}. - * - * @see com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair - */ - public ObjectMapper setAnnotationIntrospector(AnnotationIntrospector ai) { - _serializationConfig = _serializationConfig.with(ai); - _deserializationConfig = _deserializationConfig.with(ai); - return this; - } - - /** - * Method for changing {@link AnnotationIntrospector} instances used - * by this mapper instance for serialization and deserialization, - * specifying them separately so that different introspection can be - * used for different aspects - * - * @since 2.1 - * - * @param serializerAI {@link AnnotationIntrospector} to use for configuring - * serialization - * @param deserializerAI {@link AnnotationIntrospector} to use for configuring - * deserialization - * - * @see com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair - */ - public ObjectMapper setAnnotationIntrospectors(AnnotationIntrospector serializerAI, - AnnotationIntrospector deserializerAI) { - _serializationConfig = _serializationConfig.with(serializerAI); - _deserializationConfig = _deserializationConfig.with(deserializerAI); - return this; - } - - /** - * Method for setting custom property naming strategy to use. - */ - public ObjectMapper setPropertyNamingStrategy(PropertyNamingStrategy s) { - _serializationConfig = _serializationConfig.with(s); - _deserializationConfig = _deserializationConfig.with(s); - return this; - } - - /** - * @since 2.5 - */ - public PropertyNamingStrategy getPropertyNamingStrategy() { - // arbitrary choice but let's do: - return _serializationConfig.getPropertyNamingStrategy(); - } - - /** - * Method for specifying {@link PrettyPrinter} to use when "default pretty-printing" - * is enabled (by enabling {@link SerializationFeature#INDENT_OUTPUT}) - * - * @param pp Pretty printer to use by default. - * - * @return This mapper, useful for call-chaining - * - * @since 2.6 - */ - public ObjectMapper setDefaultPrettyPrinter(PrettyPrinter pp) { - _serializationConfig = _serializationConfig.withDefaultPrettyPrinter(pp); - return this; - } - - /** - * @deprecated Since 2.6 use {@link #setVisibility(VisibilityChecker)} instead. - */ - @Deprecated - public void setVisibilityChecker(VisibilityChecker vc) { - setVisibility(vc); - } - - /* - /********************************************************** - /* Configuration: global-default/per-type override settings - /********************************************************** - */ - - /** - * Convenience method, equivalent to calling: - *
-     *  setPropertyInclusion(JsonInclude.Value.construct(incl, incl));
-     *
- *

- * NOTE: behavior differs slightly from 2.8, where second argument was - * implied to be JsonInclude.Include.ALWAYS. - */ - public ObjectMapper setSerializationInclusion(JsonInclude.Include incl) { - setPropertyInclusion(JsonInclude.Value.construct(incl, incl)); - return this; - } - - /** - * @since 2.7 - * @deprecated Since 2.9 use {@link #setDefaultPropertyInclusion} - */ - @Deprecated - public ObjectMapper setPropertyInclusion(JsonInclude.Value incl) { - return setDefaultPropertyInclusion(incl); - } - - /** - * Method for setting default POJO property inclusion strategy for serialization, - * applied for all properties for which there are no per-type or per-property - * overrides (via annotations or config overrides). - * - * @since 2.9 (basically rename of setPropertyInclusion) - */ - public ObjectMapper setDefaultPropertyInclusion(JsonInclude.Value incl) { - _configOverrides.setDefaultInclusion(incl); - return this; - } - - /** - * Short-cut for: - *

-     *  setDefaultPropertyInclusion(JsonInclude.Value.construct(incl, incl));
-     *
- * - * @since 2.9 (basically rename of setPropertyInclusion) - */ - public ObjectMapper setDefaultPropertyInclusion(JsonInclude.Include incl) { - _configOverrides.setDefaultInclusion(JsonInclude.Value.construct(incl, incl)); - return this; - } - - /** - * Method for setting default Setter configuration, regarding things like - * merging, null-handling; used for properties for which there are - * no per-type or per-property overrides (via annotations or config overrides). - * - * @since 2.9 - */ - public ObjectMapper setDefaultSetterInfo(JsonSetter.Value v) { - _configOverrides.setDefaultSetterInfo(v); - return this; - } - - /** - * Method for setting auto-detection visibility definition - * defaults, which are in effect unless overridden by - * annotations (like JsonAutoDetect) or per-type - * visibility overrides. - * - * @since 2.9 - */ - public ObjectMapper setDefaultVisibility(JsonAutoDetect.Value vis) { - _configOverrides.setDefaultVisibility(VisibilityChecker.Std.construct(vis)); - return this; - } - - /** - * Method for setting default Setter configuration, regarding things like - * merging, null-handling; used for properties for which there are - * no per-type or per-property overrides (via annotations or config overrides). - * - * @since 2.9 - */ - public ObjectMapper setDefaultMergeable(Boolean b) { - _configOverrides.setDefaultMergeable(b); - return this; - } - - /* - /********************************************************** - /* Type information configuration - /********************************************************** - */ - - /** - * Convenience method that is equivalent to calling - *
-     *  enableDefaultTyping(DefaultTyping.OBJECT_AND_NON_CONCRETE);
-     *
- *

- * NOTE: use of Default Typing can be a potential security risk if incoming - * content comes from untrusted sources, and it is recommended that this - * is either not done, or, if enabled, use {@link #setDefaultTyping} - * passing a custom {@link TypeResolverBuilder} implementation that white-lists - * legal types to use. - */ - public ObjectMapper enableDefaultTyping() { - return enableDefaultTyping(DefaultTyping.OBJECT_AND_NON_CONCRETE); - } - - /** - * Convenience method that is equivalent to calling - *

-     *  enableDefaultTyping(dti, JsonTypeInfo.As.WRAPPER_ARRAY);
-     *
- *

- * NOTE: use of Default Typing can be a potential security risk if incoming - * content comes from untrusted sources, and it is recommended that this - * is either not done, or, if enabled, use {@link #setDefaultTyping} - * passing a custom {@link TypeResolverBuilder} implementation that white-lists - * legal types to use. - */ - public ObjectMapper enableDefaultTyping(DefaultTyping dti) { - return enableDefaultTyping(dti, JsonTypeInfo.As.WRAPPER_ARRAY); - } - - /** - * 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}). - *

- * NOTE: use of JsonTypeInfo.As#EXTERNAL_PROPERTY NOT SUPPORTED; - * and attempts of do so will throw an {@link IllegalArgumentException} to make - * this limitation explicit. - *

- * NOTE: use of Default Typing can be a potential security risk if incoming - * content comes from untrusted sources, and it is recommended that this - * is either not done, or, if enabled, use {@link #setDefaultTyping} - * passing a custom {@link TypeResolverBuilder} implementation that white-lists - * legal types to use. - * - * @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("Cannot use includeAs of "+includeAs); - } - - TypeResolverBuilder typer = _constructDefaultTypeResolverBuilder(applicability); - // we'll always use full class name, when using defaulting - typer = typer.init(JsonTypeInfo.Id.CLASS, null); - typer = typer.inclusion(includeAs); - return setDefaultTyping(typer); - } - - /** - * 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}) -- - * using "As.PROPERTY" inclusion mechanism and specified property name - * to use for inclusion (default being "@class" since default type information - * always uses class name as type identifier) - *

- * NOTE: use of Default Typing can be a potential security risk if incoming - * content comes from untrusted sources, and it is recommended that this - * is either not done, or, if enabled, use {@link #setDefaultTyping} - * passing a custom {@link TypeResolverBuilder} implementation that white-lists - * legal types to use. - */ - public ObjectMapper enableDefaultTypingAsProperty(DefaultTyping applicability, String propertyName) - { - TypeResolverBuilder typer = _constructDefaultTypeResolverBuilder(applicability); - // we'll always use full class name, when using defaulting - typer = typer.init(JsonTypeInfo.Id.CLASS, null); - typer = typer.inclusion(JsonTypeInfo.As.PROPERTY); - typer = typer.typeProperty(propertyName); - return setDefaultTyping(typer); - } - - /** - * Method for disabling automatic inclusion of type information; if so, only - * explicitly annotated types (ones with - * {@link com.fasterxml.jackson.annotation.JsonTypeInfo}) will have - * additional embedded type information. - */ - public ObjectMapper disableDefaultTyping() { - return setDefaultTyping(null); - } - - /** - * Method for enabling automatic inclusion of type information, using - * specified handler object for determining which types this affects, - * as well as details of how information is embedded. - *

- * NOTE: use of Default Typing can be a potential security risk if incoming - * content comes from untrusted sources, so care should be taken to use - * a {@link TypeResolverBuilder} that can limit allowed classes to - * deserialize. - * - * @param typer Type information inclusion handler - */ - public ObjectMapper setDefaultTyping(TypeResolverBuilder typer) { - _deserializationConfig = _deserializationConfig.with(typer); - _serializationConfig = _serializationConfig.with(typer); - return this; - } - - /** - * Method for registering specified class as a subtype, so that - * typename-based resolution can link supertypes to subtypes - * (as an alternative to using annotations). - * Type for given class is determined from appropriate annotation; - * or if missing, default name (unqualified class name) - */ - public void registerSubtypes(Class... classes) { - getSubtypeResolver().registerSubtypes(classes); - } - - /** - * Method for registering specified class as a subtype, so that - * typename-based resolution can link supertypes to subtypes - * (as an alternative to using annotations). - * Name may be provided as part of argument, but if not will - * be based on annotations or use default name (unqualified - * class name). - */ - public void registerSubtypes(NamedType... types) { - getSubtypeResolver().registerSubtypes(types); - } - - /** - * @since 2.9 - */ - public void registerSubtypes(Collection> subtypes) { - getSubtypeResolver().registerSubtypes(subtypes); - } - - /* - /********************************************************** - /* Configuration, basic type handling - /********************************************************** - */ - - /** - * Accessor for getting a mutable configuration override object for - * given type, needed to add or change per-type overrides applied - * to properties of given type. - * Usage is through returned object by colling "setter" methods, which - * directly modify override object and take effect directly. - * For example you can do - *

-     *   mapper.configOverride(java.util.Date.class)
-     *       .setFormat(JsonFormat.Value.forPattern("yyyy-MM-dd"));
-     *
- * to change the default format to use for properties of type - * {@link java.util.Date} (possibly further overridden by per-property - * annotations) - * - * @since 2.8 - */ - public MutableConfigOverride configOverride(Class type) { - return _configOverrides.findOrCreateOverride(type); - } - - /* - /********************************************************** - /* Configuration, basic type handling - /********************************************************** - */ - - /** - * Accessor for getting currently configured {@link TypeFactory} instance. - */ - public TypeFactory getTypeFactory() { - return _typeFactory; - } - - /** - * Method that can be used to override {@link TypeFactory} instance - * used by this mapper. - *

- * Note: will also set {@link TypeFactory} that deserialization and - * serialization config objects use. - */ - public ObjectMapper setTypeFactory(TypeFactory f) - { - _typeFactory = f; - _deserializationConfig = _deserializationConfig.with(f); - _serializationConfig = _serializationConfig.with(f); - return this; - } - - /** - * Convenience method for constructing {@link JavaType} out of given - * type (typically java.lang.Class), but without explicit - * context. - */ - public JavaType constructType(Type t) { - return _typeFactory.constructType(t); - } - - /* - /********************************************************** - /* Configuration, deserialization - /********************************************************** - */ - - /** - * Method that can be used to get hold of {@link JsonNodeFactory} - * that this mapper will use when directly constructing - * root {@link JsonNode} instances for Trees. - *

- * Note: this is just a shortcut for calling - *

-     *   getDeserializationConfig().getNodeFactory()
-     *
- */ - public JsonNodeFactory getNodeFactory() { - return _deserializationConfig.getNodeFactory(); - } - - /** - * Method for specifying {@link JsonNodeFactory} to use for - * constructing root level tree nodes (via method - * {@link #createObjectNode} - */ - public ObjectMapper setNodeFactory(JsonNodeFactory f) { - _deserializationConfig = _deserializationConfig.with(f); - return this; - } - - /** - * Method for adding specified {@link DeserializationProblemHandler} - * to be used for handling specific problems during deserialization. - */ - public ObjectMapper addHandler(DeserializationProblemHandler h) { - _deserializationConfig = _deserializationConfig.withHandler(h); - return this; - } - - /** - * Method for removing all registered {@link DeserializationProblemHandler}s - * instances from this mapper. - */ - public ObjectMapper clearProblemHandlers() { - _deserializationConfig = _deserializationConfig.withNoProblemHandlers(); - return this; - } - - /** - * Method that allows overriding of the underlying {@link DeserializationConfig} - * object. - * It is added as a fallback method that may be used if no other configuration - * modifier method works: it should not be used if there are alternatives, - * and its use is generally discouraged. - *

- * NOTE: only use this method if you know what you are doing -- it allows - * by-passing some of checks applied to other configuration methods. - * Also keep in mind that as with all configuration of {@link ObjectMapper}, - * this is only thread-safe if done before calling any deserialization methods. - * - * @since 2.4 - */ - public ObjectMapper setConfig(DeserializationConfig config) { - _deserializationConfig = config; - return this; - } - - /* - /********************************************************** - /* Configuration, serialization - /********************************************************** - */ - - /** - * @deprecated Since 2.6, use {@link #setFilterProvider} instead (allows chaining) - */ - @Deprecated - public void setFilters(FilterProvider filterProvider) { - _serializationConfig = _serializationConfig.withFilters(filterProvider); - } - - /** - * Method for configuring this mapper to use specified {@link FilterProvider} for - * mapping Filter Ids to actual filter instances. - *

- * Note that usually it is better to use method {@link #writer(FilterProvider)}; - * however, sometimes - * this method is more convenient. For example, some frameworks only allow configuring - * of ObjectMapper instances and not {@link ObjectWriter}s. - * - * @since 2.6 - */ - public ObjectMapper setFilterProvider(FilterProvider filterProvider) { - _serializationConfig = _serializationConfig.withFilters(filterProvider); - return this; - } - + /* + /********************************************************************** + /* Configuration: main config object access + /********************************************************************** + */ + /** - * Method that will configure default {@link Base64Variant} that - * byte[] serializers and deserializers will use. - * - * @param v Base64 variant to use - * - * @return This mapper, for convenience to allow chaining - * - * @since 2.1 + * Accessor for internal configuration object that contains settings for + * serialization operations (writeValue(...) methods) + *
+ * NOTE: Not to be used by application code; needed by some tests */ - public ObjectMapper setBase64Variant(Base64Variant v) { - _serializationConfig = _serializationConfig.with(v); - _deserializationConfig = _deserializationConfig.with(v); - return this; + public SerializationConfig serializationConfig() { + return _serializationConfig; } /** - * Method that allows overriding of the underlying {@link SerializationConfig} - * object, which contains serialization-specific configuration settings. - * It is added as a fallback method that may be used if no other configuration - * modifier method works: it should not be used if there are alternatives, - * and its use is generally discouraged. - *

- * NOTE: only use this method if you know what you are doing -- it allows - * by-passing some of checks applied to other configuration methods. - * Also keep in mind that as with all configuration of {@link ObjectMapper}, - * this is only thread-safe if done before calling any serialization methods. - * - * @since 2.4 + * Accessor for internal configuration object that contains settings for + * deserialization operations (readValue(...) methods) + *
+ * NOTE: Not to be used by application code; needed by some tests */ - public ObjectMapper setConfig(SerializationConfig config) { - _serializationConfig = config; - return this; + public DeserializationConfig deserializationConfig() { + return _deserializationConfig; } - - /* - /********************************************************** - /* Configuration, other - /********************************************************** - */ /** - * Method that can be used to get hold of {@link JsonFactory} that this + * Method that can be used to get hold of {@link TokenStreamFactory} that this * mapper uses if it needs to construct {@link JsonParser}s * and/or {@link JsonGenerator}s. *

* WARNING: note that all {@link ObjectReader} and {@link ObjectWriter} * instances created by this mapper usually share the same configured - * {@link JsonFactory}, so changes to its configuration will "leak". + * {@link TokenStreamFactory}, so changes to its configuration will "leak". * To avoid such observed changes you should always use "with()" and * "without()" method of {@link ObjectReader} and {@link ObjectWriter} - * for changing {@link com.fasterxml.jackson.core.JsonParser.Feature} - * and {@link com.fasterxml.jackson.core.JsonGenerator.Feature} + * for changing {@link StreamReadFeature} + * and {@link StreamWriteFeature} * settings to use on per-call basis. * - * @return {@link JsonFactory} that this mapper uses when it needs to + * @return {@link TokenStreamFactory} that this mapper uses when it needs to * construct Json parser and generators * - * @since 2.10 + * @since 3.0 */ - public JsonFactory tokenStreamFactory() { return _jsonFactory; } + public TokenStreamFactory tokenStreamFactory() { return _streamFactory; } - @Override - public JsonFactory getFactory() { return _jsonFactory; } - /** - * @deprecated Since 2.1: Use {@link #getFactory} instead + * Method that can be used to get hold of {@link JsonNodeFactory} + * that this mapper will use when directly constructing + * root {@link JsonNode} instances for Trees. + *

+ * Note: this is just a shortcut for calling + *

+     *   getDeserializationConfig().getNodeFactory()
+     *
*/ - @Deprecated - @Override - public JsonFactory getJsonFactory() { return getFactory(); } - - /** - * Method for configuring the default {@link DateFormat} to use when serializing time - * values as Strings, and deserializing from JSON Strings. - * This is preferably to directly modifying {@link SerializationConfig} and - * {@link DeserializationConfig} instances. - * If you need per-request configuration, use {@link #writer(DateFormat)} to - * create properly configured {@link ObjectWriter} and use that; this because - * {@link ObjectWriter}s are thread-safe whereas ObjectMapper itself is only - * thread-safe when configuring methods (such as this one) are NOT called. - */ - public ObjectMapper setDateFormat(DateFormat dateFormat) - { - _deserializationConfig = _deserializationConfig.with(dateFormat); - _serializationConfig = _serializationConfig.with(dateFormat); - return this; + public JsonNodeFactory getNodeFactory() { + return _deserializationConfig.getNodeFactory(); } - /** - * @since 2.5 - */ - public DateFormat getDateFormat() { - // arbitrary choice but let's do: - return _serializationConfig.getDateFormat(); + public InjectableValues getInjectableValues() { + return _injectableValues; } - - /** - * Method for configuring {@link HandlerInstantiator} to use for creating - * instances of handlers (such as serializers, deserializers, type and type - * id resolvers), given a class. - * - * @param hi Instantiator to use; if null, use the default implementation + + /* + /********************************************************************** + /* Configuration, access to type factory, type resolution + /********************************************************************** */ - public Object setHandlerInstantiator(HandlerInstantiator hi) - { - _deserializationConfig = _deserializationConfig.with(hi); - _serializationConfig = _serializationConfig.with(hi); - return this; - } - + /** - * Method for configuring {@link InjectableValues} which used to find - * values to inject. + * Accessor for getting currently configured {@link TypeFactory} instance. */ - public ObjectMapper setInjectableValues(InjectableValues injectableValues) { - _injectableValues = injectableValues; - return this; + public TypeFactory getTypeFactory() { + return _typeFactory; } /** - * @since 2.6 + * Convenience method for constructing {@link JavaType} out of given + * type (typically java.lang.Class), but without explicit + * context. */ - public InjectableValues getInjectableValues() { - return _injectableValues; + public JavaType constructType(Type t) { + return _typeFactory.constructType(t); } - /** - * Method for overriding default locale to use for formatting. - * Default value used is {@link Locale#getDefault()}. + /* + /********************************************************************** + /* Configuration, accessing features + /********************************************************************** */ - public ObjectMapper setLocale(Locale l) { - _deserializationConfig = _deserializationConfig.with(l); - _serializationConfig = _serializationConfig.with(l); - return this; + + public boolean isEnabled(JsonFactory.Feature f) { + return _streamFactory.isEnabled(f); } - /** - * Method for overriding default TimeZone to use for formatting. - * Default value used is UTC (NOT default TimeZone of JVM). - */ - public ObjectMapper setTimeZone(TimeZone tz) { - _deserializationConfig = _deserializationConfig.with(tz); - _serializationConfig = _serializationConfig.with(tz); - return this; + public boolean isEnabled(StreamReadFeature f) { + return _deserializationConfig.isEnabled(f); } - /* - /********************************************************** - /* Configuration, simple features: MapperFeature - /********************************************************** - */ + public boolean isEnabled(StreamWriteFeature f) { + return _serializationConfig.isEnabled(f); + } /** * Method for checking whether given {@link MapperFeature} is enabled. @@ -1944,45 +508,15 @@ public boolean isEnabled(MapperFeature f) { // ok to use either one, should be kept in sync return _serializationConfig.isEnabled(f); } - - /** - * @deprecated Since 2.10 use {@code ObjectMapper.builder().disable(...)} - */ - @Deprecated - public ObjectMapper configure(MapperFeature f, boolean state) { - _serializationConfig = state ? - _serializationConfig.with(f) : _serializationConfig.without(f); - _deserializationConfig = state ? - _deserializationConfig.with(f) : _deserializationConfig.without(f); - return this; - } - - /** - * @deprecated Since 2.10 use {@code ObjectMapper.builder().disable(...)} - */ - @Deprecated - public ObjectMapper enable(MapperFeature... f) { - _deserializationConfig = _deserializationConfig.with(f); - _serializationConfig = _serializationConfig.with(f); - return this; - } /** - * @deprecated Since 2.10 use {@code ObjectMapper.builder().disable(...)} + * Method for checking whether given deserialization-specific + * feature is enabled. */ - @Deprecated - public ObjectMapper disable(MapperFeature... f) { - _deserializationConfig = _deserializationConfig.without(f); - _serializationConfig = _serializationConfig.without(f); - return this; + public boolean isEnabled(DeserializationFeature f) { + return _deserializationConfig.isEnabled(f); } - /* - /********************************************************** - /* Configuration, simple features: SerializationFeature - /********************************************************** - */ - /** * Method for checking whether given serialization-specific * feature is enabled. @@ -1991,286 +525,244 @@ public boolean isEnabled(SerializationFeature f) { return _serializationConfig.isEnabled(f); } - /** - * Method for changing state of an on/off serialization feature for - * this object mapper. - */ - public ObjectMapper configure(SerializationFeature f, boolean state) { - _serializationConfig = state ? - _serializationConfig.with(f) : _serializationConfig.without(f); - return this; - } - - /** - * Method for enabling specified {@link DeserializationConfig} feature. - * Modifies and returns this instance; no new object is created. + /* + /********************************************************************** + /* Configuration, accessing module information + /********************************************************************** */ - public ObjectMapper enable(SerializationFeature f) { - _serializationConfig = _serializationConfig.with(f); - return this; - } /** - * Method for enabling specified {@link DeserializationConfig} features. - * Modifies and returns this instance; no new object is created. + * Method that may be used to find out {@link Module}s that were registered + * when creating this mapper (if any). + * + * @since 3.0 */ - public ObjectMapper enable(SerializationFeature first, - SerializationFeature... f) { - _serializationConfig = _serializationConfig.with(first, f); - return this; + public Collection getRegisteredModules() { + return _savedBuilderState.modules(); } - /** - * Method for enabling specified {@link DeserializationConfig} features. - * Modifies and returns this instance; no new object is created. + /* + /********************************************************************** + /* Public API: constructing Parsers that are properly linked + /* to `ObjectReadContext` + /********************************************************************** */ - public ObjectMapper disable(SerializationFeature f) { - _serializationConfig = _serializationConfig.without(f); - return this; - } /** - * Method for enabling specified {@link DeserializationConfig} features. - * Modifies and returns this instance; no new object is created. + * Factory method for constructing {@link JsonParser} that is properly + * wired to allow callbacks for deserialization: basically + * constructs a {@link ObjectReadContext} and then calls + * {@link TokenStreamFactory#createParser(ObjectReadContext,java.io.File)}. + * + * @since 3.0 */ - public ObjectMapper disable(SerializationFeature first, - SerializationFeature... f) { - _serializationConfig = _serializationConfig.without(first, f); - return this; + public JsonParser createParser(File src) throws IOException { + DefaultDeserializationContext ctxt = createDeserializationContext(); + return ctxt.assignAndReturnParser(_streamFactory.createParser(ctxt, src)); } - - /* - /********************************************************** - /* Configuration, simple features: DeserializationFeature - /********************************************************** - */ /** - * Method for checking whether given deserialization-specific - * feature is enabled. + * Factory method for constructing {@link JsonParser} that is properly + * wired to allow callbacks for deserialization: basically + * constructs a {@link ObjectReadContext} and then calls + * {@link TokenStreamFactory#createParser(ObjectReadContext,java.net.URL)}. + * + * @since 3.0 */ - public boolean isEnabled(DeserializationFeature f) { - return _deserializationConfig.isEnabled(f); + public JsonParser createParser(URL src) throws IOException { + DefaultDeserializationContext ctxt = createDeserializationContext(); + return ctxt.assignAndReturnParser(_streamFactory.createParser(ctxt, src)); } /** - * Method for changing state of an on/off deserialization feature for - * this object mapper. + * Factory method for constructing {@link JsonParser} that is properly + * wired to allow callbacks for deserialization: basically + * constructs a {@link ObjectReadContext} and then calls + * {@link TokenStreamFactory#createParser(ObjectReadContext,InputStream)}. + * + * @since 3.0 */ - public ObjectMapper configure(DeserializationFeature f, boolean state) { - _deserializationConfig = state ? - _deserializationConfig.with(f) : _deserializationConfig.without(f); - return this; + public JsonParser createParser(InputStream in) throws IOException { + DefaultDeserializationContext ctxt = createDeserializationContext(); + return ctxt.assignAndReturnParser(_streamFactory.createParser(ctxt, in)); } /** - * Method for enabling specified {@link DeserializationConfig} features. - * Modifies and returns this instance; no new object is created. + * Factory method for constructing {@link JsonParser} that is properly + * wired to allow callbacks for deserialization: basically + * constructs a {@link ObjectReadContext} and then calls + * {@link TokenStreamFactory#createParser(ObjectReadContext,Reader)}. + * + * @since 3.0 */ - public ObjectMapper enable(DeserializationFeature feature) { - _deserializationConfig = _deserializationConfig.with(feature); - return this; + public JsonParser createParser(Reader r) throws IOException { + DefaultDeserializationContext ctxt = createDeserializationContext(); + return ctxt.assignAndReturnParser(_streamFactory.createParser(ctxt, r)); } /** - * Method for enabling specified {@link DeserializationConfig} features. - * Modifies and returns this instance; no new object is created. + * Factory method for constructing {@link JsonParser} that is properly + * wired to allow callbacks for deserialization: basically + * constructs a {@link ObjectReadContext} and then calls + * {@link TokenStreamFactory#createParser(ObjectReadContext,byte[])}. + * + * @since 3.0 */ - public ObjectMapper enable(DeserializationFeature first, - DeserializationFeature... f) { - _deserializationConfig = _deserializationConfig.with(first, f); - return this; + public JsonParser createParser(byte[] data) throws IOException { + DefaultDeserializationContext ctxt = createDeserializationContext(); + return ctxt.assignAndReturnParser(_streamFactory.createParser(ctxt, data)); } - + /** - * Method for enabling specified {@link DeserializationConfig} features. - * Modifies and returns this instance; no new object is created. + * Factory method for constructing {@link JsonParser} that is properly + * wired to allow callbacks for deserialization: basically + * constructs a {@link ObjectReadContext} and then calls + * {@link TokenStreamFactory#createParser(ObjectReadContext,byte[],int,int)}. + * + * @since 3.0 */ - public ObjectMapper disable(DeserializationFeature feature) { - _deserializationConfig = _deserializationConfig.without(feature); - return this; + public JsonParser createParser(byte[] data, int offset, int len) throws IOException { + DefaultDeserializationContext ctxt = createDeserializationContext(); + return ctxt.assignAndReturnParser(_streamFactory.createParser(ctxt, data, offset, len)); } /** - * Method for enabling specified {@link DeserializationConfig} features. - * Modifies and returns this instance; no new object is created. + * Factory method for constructing {@link JsonParser} that is properly + * wired to allow callbacks for deserialization: basically + * constructs a {@link ObjectReadContext} and then calls + * {@link TokenStreamFactory#createParser(ObjectReadContext,String)}. + * + * @since 3.0 */ - public ObjectMapper disable(DeserializationFeature first, - DeserializationFeature... f) { - _deserializationConfig = _deserializationConfig.without(first, f); - return this; + public JsonParser createParser(String content) throws IOException { + DefaultDeserializationContext ctxt = createDeserializationContext(); + return ctxt.assignAndReturnParser(_streamFactory.createParser(ctxt, content)); } - - /* - /********************************************************** - /* Configuration, simple features: JsonParser.Feature - /********************************************************** - */ - public boolean isEnabled(JsonParser.Feature f) { - return _deserializationConfig.isEnabled(f, _jsonFactory); + /** + * Factory method for constructing {@link JsonParser} that is properly + * wired to allow callbacks for deserialization: basically + * constructs a {@link ObjectReadContext} and then calls + * {@link TokenStreamFactory#createParser(ObjectReadContext,char[])}. + * + * @since 3.0 + */ + public JsonParser createParser(char[] content) throws IOException { + DefaultDeserializationContext ctxt = createDeserializationContext(); + return ctxt.assignAndReturnParser(_streamFactory.createParser(ctxt, content)); } /** - * Method for changing state of specified {@link com.fasterxml.jackson.core.JsonParser.Feature}s - * for parser instances this object mapper creates. - *

- * Note that this is equivalent to directly calling same method - * on {@link #getFactory}. - *

- * WARNING: since this method directly modifies state of underlying {@link JsonFactory}, - * it will change observed configuration by {@link ObjectReader}s as well -- to avoid - * this, use {@link ObjectReader#with(JsonParser.Feature)} instead. + * Factory method for constructing {@link JsonParser} that is properly + * wired to allow callbacks for deserialization: basically + * constructs a {@link ObjectReadContext} and then calls + * {@link TokenStreamFactory#createParser(ObjectReadContext,char[],int,int)}. + * + * @since 3.0 */ - public ObjectMapper configure(JsonParser.Feature f, boolean state) { - _jsonFactory.configure(f, state); - return this; + public JsonParser createParser(char[] content, int offset, int len) throws IOException { + DefaultDeserializationContext ctxt = createDeserializationContext(); + return ctxt.assignAndReturnParser(_streamFactory.createParser(ctxt, content, offset, len)); } /** - * Method for enabling specified {@link com.fasterxml.jackson.core.JsonParser.Feature}s - * for parser instances this object mapper creates. - *

- * Note that this is equivalent to directly calling same method on {@link #getFactory}. - *

- * WARNING: since this method directly modifies state of underlying {@link JsonFactory}, - * it will change observed configuration by {@link ObjectReader}s as well -- to avoid - * this, use {@link ObjectReader#with(JsonParser.Feature)} instead. + * Factory method for constructing {@link JsonParser} that is properly + * wired to allow callbacks for deserialization: basically + * constructs a {@link ObjectReadContext} and then calls + * {@link TokenStreamFactory#createParser(ObjectReadContext,DataInput)}. * - * @since 2.5 + * @since 3.0 */ - public ObjectMapper enable(JsonParser.Feature... features) { - for (JsonParser.Feature f : features) { - _jsonFactory.enable(f); - } - return this; + public JsonParser createParser(DataInput content) throws IOException { + DefaultDeserializationContext ctxt = createDeserializationContext(); + return ctxt.assignAndReturnParser(_streamFactory.createParser(ctxt, content)); } - + /** - * Method for disabling specified {@link com.fasterxml.jackson.core.JsonParser.Feature}s - * for parser instances this object mapper creates. - *

- * Note that this is equivalent to directly calling same method on {@link #getFactory}. - *

- * WARNING: since this method directly modifies state of underlying {@link JsonFactory}, - * it will change observed configuration by {@link ObjectReader}s as well -- to avoid - * this, use {@link ObjectReader#without(JsonParser.Feature)} instead. + * Factory method for constructing non-blocking {@link JsonParser} that is properly + * wired to allow configuration access (and, if relevant for parser, callbacks): + * essentially constructs a {@link ObjectReadContext} and then calls + * {@link TokenStreamFactory#createParser(ObjectReadContext,DataInput)}. * - * @since 2.5 + * @since 3.0 */ - public ObjectMapper disable(JsonParser.Feature... features) { - for (JsonParser.Feature f : features) { - _jsonFactory.disable(f); - } - return this; + public JsonParser createNonBlockingByteArrayParser() throws IOException { + DefaultDeserializationContext ctxt = createDeserializationContext(); + return ctxt.assignAndReturnParser(_streamFactory.createNonBlockingByteArrayParser(ctxt)); } - + /* - /********************************************************** - /* Configuration, simple features: JsonGenerator.Feature - /********************************************************** + /********************************************************************** + /* Public API: constructing Generator that are properly linked + /* to `ObjectWriteContext` + /********************************************************************** */ - public boolean isEnabled(JsonGenerator.Feature f) { - return _serializationConfig.isEnabled(f, _jsonFactory); - } - /** - * Method for changing state of an on/off {@link JsonGenerator} feature for - * generator instances this object mapper creates. - *

- * Note that this is equivalent to directly calling same method - * on {@link #getFactory}. - *

- * WARNING: since this method directly modifies state of underlying {@link JsonFactory}, - * it will change observed configuration by {@link ObjectWriter}s as well -- to avoid - * this, use {@link ObjectWriter#with(JsonGenerator.Feature)} instead. + * Factory method for constructing {@link JsonGenerator} that is properly + * wired to allow callbacks for serialization: basically + * constructs a {@link ObjectWriteContext} and then calls + * {@link TokenStreamFactory#createGenerator(ObjectWriteContext,OutputStream)}. + * + * @since 3.0 */ - public ObjectMapper configure(JsonGenerator.Feature f, boolean state) { - _jsonFactory.configure(f, state); - return this; + public JsonGenerator createGenerator(OutputStream out) throws IOException { + return _streamFactory.createGenerator(_serializerProvider(), out); } /** - * Method for enabling specified {@link com.fasterxml.jackson.core.JsonGenerator.Feature}s - * for parser instances this object mapper creates. - *

- * Note that this is equivalent to directly calling same method on {@link #getFactory}. - *

- * WARNING: since this method directly modifies state of underlying {@link JsonFactory}, - * it will change observed configuration by {@link ObjectWriter}s as well -- to avoid - * this, use {@link ObjectWriter#with(JsonGenerator.Feature)} instead. + * Factory method for constructing {@link JsonGenerator} that is properly + * wired to allow callbacks for serialization: basically + * constructs a {@link ObjectWriteContext} and then calls + * {@link TokenStreamFactory#createGenerator(ObjectWriteContext,OutputStream,JsonEncoding)}. * - * @since 2.5 + * @since 3.0 */ - public ObjectMapper enable(JsonGenerator.Feature... features) { - for (JsonGenerator.Feature f : features) { - _jsonFactory.enable(f); - } - return this; + public JsonGenerator createGenerator(OutputStream out, JsonEncoding enc) throws IOException { + return _streamFactory.createGenerator(_serializerProvider(), out, enc); } /** - * Method for disabling specified {@link com.fasterxml.jackson.core.JsonGenerator.Feature}s - * for parser instances this object mapper creates. - *

- * Note that this is equivalent to directly calling same method on {@link #getFactory}. - *

- * WARNING: since this method directly modifies state of underlying {@link JsonFactory}, - * it will change observed configuration by {@link ObjectWriter}s as well -- to avoid - * this, use {@link ObjectWriter#without(JsonGenerator.Feature)} instead. + * Factory method for constructing {@link JsonGenerator} that is properly + * wired to allow callbacks for serialization: basically + * constructs a {@link ObjectWriteContext} and then calls + * {@link TokenStreamFactory#createGenerator(ObjectWriteContext,Writer)}. * - * @since 2.5 + * @since 3.0 */ - public ObjectMapper disable(JsonGenerator.Feature... features) { - for (JsonGenerator.Feature f : features) { - _jsonFactory.disable(f); - } - return this; + public JsonGenerator createGenerator(Writer w) throws IOException { + return _streamFactory.createGenerator(_serializerProvider(), w); } - /* - /********************************************************** - /* Configuration, simple features: JsonFactory.Feature - /********************************************************** - */ - /** - * Convenience method, equivalent to: - *

-     *  getJsonFactory().isEnabled(f);
-     *
+ * Factory method for constructing {@link JsonGenerator} that is properly + * wired to allow callbacks for serialization: basically + * constructs a {@link ObjectWriteContext} and then calls + * {@link TokenStreamFactory#createGenerator(ObjectWriteContext,File,JsonEncoding)}. + * + * @since 3.0 */ - public boolean isEnabled(JsonFactory.Feature f) { - return _jsonFactory.isEnabled(f); + public JsonGenerator createGenerator(File f, JsonEncoding enc) + throws IOException { + return _streamFactory.createGenerator(_serializerProvider(), f, enc); } - /* - /********************************************************** - /* Configuration, 2.10+ stream features - /********************************************************** - */ - /** - * @since 2.10 + * Factory method for constructing {@link JsonGenerator} that is properly + * wired to allow callbacks for serialization: basically + * constructs a {@link ObjectWriteContext} and then calls + * {@link TokenStreamFactory#createGenerator(ObjectWriteContext,DataOutput)}. + * + * @since 3.0 */ - public boolean isEnabled(StreamReadFeature f) { - return isEnabled(f.mappedFeature()); + public JsonGenerator createGenerator(DataOutput out) throws IOException { + return _streamFactory.createGenerator(_serializerProvider(), out); } - /** - * @since 2.10 - */ - public boolean isEnabled(StreamWriteFeature f) { - return isEnabled(f.mappedFeature()); - } - /* - /********************************************************** - /* Public API (from ObjectCodec): deserialization - /* (mapping from JSON to Java types); - /* main methods - /********************************************************** + /********************************************************************** + /* Public API deserialization, main methods + /********************************************************************** */ /** @@ -2292,12 +784,12 @@ public boolean isEnabled(StreamWriteFeature f) { * @throws JsonMappingException if the input JSON structure does not match structure * expected for result type (or has other mismatch issues) */ - @Override @SuppressWarnings("unchecked") public T readValue(JsonParser p, Class valueType) throws IOException, JsonParseException, JsonMappingException { - return (T) _readValue(getDeserializationConfig(), p, _typeFactory.constructType(valueType)); + DeserializationContext ctxt = createDeserializationContext(p); + return (T) _readValue(ctxt, p, _typeFactory.constructType(valueType)); } /** @@ -2316,12 +808,12 @@ public T readValue(JsonParser p, Class valueType) * @throws JsonMappingException if the input JSON structure does not match structure * expected for result type (or has other mismatch issues) */ - @Override @SuppressWarnings("unchecked") public T readValue(JsonParser p, TypeReference valueTypeRef) throws IOException, JsonParseException, JsonMappingException { - return (T) _readValue(getDeserializationConfig(), p, _typeFactory.constructType(valueTypeRef)); + DeserializationContext ctxt = createDeserializationContext(p); + return (T) _readValue(ctxt, p, _typeFactory.constructType(valueTypeRef)); } /** @@ -2339,12 +831,12 @@ public T readValue(JsonParser p, TypeReference valueTypeRef) * @throws JsonMappingException if the input JSON structure does not match structure * expected for result type (or has other mismatch issues) */ - @Override @SuppressWarnings("unchecked") public final T readValue(JsonParser p, ResolvedType valueType) throws IOException, JsonParseException, JsonMappingException { - return (T) _readValue(getDeserializationConfig(), p, (JavaType) valueType); + DeserializationContext ctxt = createDeserializationContext(p); + return (T) _readValue(ctxt, p, (JavaType) valueType); } /** @@ -2363,9 +855,10 @@ public final T readValue(JsonParser p, ResolvedType valueType) public T readValue(JsonParser p, JavaType valueType) throws IOException, JsonParseException, JsonMappingException { - return (T) _readValue(getDeserializationConfig(), p, valueType); + DeserializationContext ctxt = createDeserializationContext(p); + return (T) _readValue(ctxt, p, valueType); } - + /** * Method to deserialize JSON content as a tree {@link JsonNode}. * Returns {@link JsonNode} that represents the root of the resulting tree, if there @@ -2389,21 +882,20 @@ public T readValue(JsonParser p, JavaType valueType) * @throws JsonParseException if underlying input contains invalid content * of type {@link JsonParser} supports (JSON for default case) */ - @Override public T readTree(JsonParser p) throws IOException, JsonProcessingException { // Must check for EOF here before calling readValue(), since that'll choke on it otherwise - DeserializationConfig cfg = getDeserializationConfig(); - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == null) { t = p.nextToken(); if (t == null) { return null; } } + DeserializationContext ctxt = createDeserializationContext(p); // NOTE! _readValue() will check for trailing tokens - JsonNode n = (JsonNode) _readValue(cfg, p, JSON_NODE_TYPE); + JsonNode n = (JsonNode) _readValue(ctxt, p, JSON_NODE_TYPE); if (n == null) { n = getNodeFactory().nullNode(); } @@ -2430,26 +922,10 @@ public T readTree(JsonParser p) *

* Note that {@link ObjectReader} has more complete set of variants. */ - @Override - public MappingIterator readValues(JsonParser p, ResolvedType valueType) - throws IOException, JsonProcessingException - { - return readValues(p, (JavaType) valueType); - } - - /** - * Convenience method, equivalent in function to: - *

-     *   readerFor(valueType).readValues(p);
-     *
- *

- * Type-safe overload of {@link #readValues(JsonParser, ResolvedType)}. - */ public MappingIterator readValues(JsonParser p, JavaType valueType) throws IOException, JsonProcessingException { - DeserializationConfig config = getDeserializationConfig(); - DeserializationContext ctxt = createDeserializationContext(p, config); + DeserializationContext ctxt = createDeserializationContext(p); JsonDeserializer deser = _findRootDeserializer(ctxt, valueType); // false -> do NOT close JsonParser (since caller passed it) return new MappingIterator(valueType, p, ctxt, deser, @@ -2462,30 +938,19 @@ public MappingIterator readValues(JsonParser p, JavaType valueType) * readerFor(valueType).readValues(p); * *

- * Type-safe overload of {@link #readValues(JsonParser, ResolvedType)}. + * Type-safe overload of {@link #readValues(JsonParser, JavaType)}. */ - @Override public MappingIterator readValues(JsonParser p, Class valueType) throws IOException, JsonProcessingException { return readValues(p, _typeFactory.constructType(valueType)); } - /** - * Method for reading sequence of Objects from parser stream. - */ - @Override - public MappingIterator readValues(JsonParser p, TypeReference valueTypeRef) - throws IOException, JsonProcessingException - { - return readValues(p, _typeFactory.constructType(valueTypeRef)); - } - /* - /********************************************************** - /* Public API not included in ObjectCodec: deserialization - /* (mapping from JSON to Java types) - /********************************************************** + /********************************************************************** + /* Public API: deserialization + /* (mapping from token stream to Java types) + /********************************************************************** */ /** @@ -2516,7 +981,8 @@ public MappingIterator readValues(JsonParser p, TypeReference valueTyp */ public JsonNode readTree(InputStream in) throws IOException { - return _readTreeAndClose(_jsonFactory.createParser(in)); + DeserializationContext ctxt = createDeserializationContext(); + return _readTreeAndClose(ctxt, _streamFactory.createParser(ctxt, in)); } /** @@ -2524,7 +990,8 @@ public JsonNode readTree(InputStream in) throws IOException * passed-in {@link Reader} */ public JsonNode readTree(Reader r) throws IOException { - return _readTreeAndClose(_jsonFactory.createParser(r)); + DeserializationContext ctxt = createDeserializationContext(); + return _readTreeAndClose(ctxt, _streamFactory.createParser(ctxt, r)); } /** @@ -2532,7 +999,8 @@ public JsonNode readTree(Reader r) throws IOException { * passed-in {@link String} */ public JsonNode readTree(String content) throws IOException { - return _readTreeAndClose(_jsonFactory.createParser(content)); + DeserializationContext ctxt = createDeserializationContext(); + return _readTreeAndClose(ctxt, _streamFactory.createParser(ctxt, content)); } /** @@ -2540,7 +1008,8 @@ public JsonNode readTree(String content) throws IOException { * passed-in byte array. */ public JsonNode readTree(byte[] content) throws IOException { - return _readTreeAndClose(_jsonFactory.createParser(content)); + DeserializationContext ctxt = createDeserializationContext(); + return _readTreeAndClose(ctxt, _streamFactory.createParser(ctxt, content)); } /** @@ -2548,7 +1017,8 @@ public JsonNode readTree(byte[] content) throws IOException { * passed-in byte array. */ public JsonNode readTree(byte[] content, int offset, int len) throws IOException { - return _readTreeAndClose(_jsonFactory.createParser(content, offset, len)); + DeserializationContext ctxt = createDeserializationContext(); + return _readTreeAndClose(ctxt, _streamFactory.createParser(ctxt, content, offset, len)); } /** @@ -2558,7 +1028,8 @@ public JsonNode readTree(byte[] content, int offset, int len) throws IOException public JsonNode readTree(File file) throws IOException, JsonProcessingException { - return _readTreeAndClose(_jsonFactory.createParser(file)); + DeserializationContext ctxt = createDeserializationContext(); + return _readTreeAndClose(ctxt, _streamFactory.createParser(ctxt, file)); } /** @@ -2566,37 +1037,34 @@ public JsonNode readTree(File file) * passed-in {@link URL}. */ public JsonNode readTree(URL source) throws IOException { - return _readTreeAndClose(_jsonFactory.createParser(source)); + DeserializationContext ctxt = createDeserializationContext(); + return _readTreeAndClose(ctxt, _streamFactory.createParser(ctxt, source)); } /* - /********************************************************** - /* Public API (from ObjectCodec): serialization - /* (mapping from Java types to Json) - /********************************************************** + /********************************************************************** + /* Public API serialization + /* (mapping from Java types to token streams) + /********************************************************************** */ /** * Method that can be used to serialize any Java value as * JSON output, using provided {@link JsonGenerator}. */ - @Override public void writeValue(JsonGenerator g, Object value) throws IOException, JsonGenerationException, JsonMappingException { - SerializationConfig config = getSerializationConfig(); - - /* 12-May-2015/2.6, tatu: Looks like we do NOT want to call the usual - * 'config.initialize(g)` here, since it is assumed that generator - * has been configured by caller. But for some reason we don't - * trust indentation settings... - */ - // 10-Aug-2012, tatu: as per [Issue#12], must handle indentation: + SerializationConfig config = serializationConfig(); + // 04-Oct-2017, tatu: Generator should come properly configured and we should not + // change its state in any way, I think (at least with Jackson 3.0) + /* if (config.isEnabled(SerializationFeature.INDENT_OUTPUT)) { if (g.getPrettyPrinter() == null) { g.setPrettyPrinter(config.constructDefaultPrettyPrinter()); } } + */ if (config.isEnabled(SerializationFeature.CLOSE_CLOSEABLE) && (value instanceof Closeable)) { _writeCloseableValue(g, value, config); } else { @@ -2608,19 +1076,18 @@ public void writeValue(JsonGenerator g, Object value) } /* - /********************************************************** - /* Public API (from TreeCodec via ObjectCodec): Tree Model support - /********************************************************** + /********************************************************************** + /* Public API: Tree Model support + /********************************************************************** */ - @Override - public void writeTree(JsonGenerator jgen, TreeNode rootNode) + public void writeTree(JsonGenerator g, TreeNode rootNode) throws IOException, JsonProcessingException { - SerializationConfig config = getSerializationConfig(); - _serializerProvider(config).serializeValue(jgen, rootNode); + SerializationConfig config = serializationConfig(); + _serializerProvider(config).serializeValue(g, rootNode); if (config.isEnabled(SerializationFeature.FLUSH_AFTER_WRITE_VALUE)) { - jgen.flush(); + g.flush(); } } @@ -2628,13 +1095,13 @@ public void writeTree(JsonGenerator jgen, TreeNode rootNode) * Method to serialize given JSON Tree, using generator * provided. */ - public void writeTree(JsonGenerator jgen, JsonNode rootNode) + public void writeTree(JsonGenerator g, JsonNode rootNode) throws IOException, JsonProcessingException { - SerializationConfig config = getSerializationConfig(); - _serializerProvider(config).serializeValue(jgen, rootNode); + SerializationConfig config = serializationConfig(); + _serializerProvider(config).serializeValue(g, rootNode); if (config.isEnabled(SerializationFeature.FLUSH_AFTER_WRITE_VALUE)) { - jgen.flush(); + g.flush(); } } @@ -2645,7 +1112,6 @@ public void writeTree(JsonGenerator jgen, JsonNode rootNode) * part of core package, whereas impls are part of mapper * package) */ - @Override public ObjectNode createObjectNode() { return _deserializationConfig.getNodeFactory().objectNode(); } @@ -2657,7 +1123,6 @@ public ObjectNode createObjectNode() { * part of core package, whereas impls are part of mapper * package) */ - @Override public ArrayNode createArrayNode() { return _deserializationConfig.getNodeFactory().arrayNode(); } @@ -2668,9 +1133,9 @@ public ArrayNode createArrayNode() { * * @param n Root node of the tree that resulting parser will read from */ - @Override public JsonParser treeAsTokens(TreeNode n) { - return new TreeTraversingParser((JsonNode) n, this); + DeserializationContext ctxt = createDeserializationContext(); + return new TreeTraversingParser((JsonNode) n, ctxt); } /** @@ -2683,7 +1148,6 @@ public JsonParser treeAsTokens(TreeNode n) { * */ @SuppressWarnings("unchecked") - @Override public T treeToValue(TreeNode n, Class valueType) throws JsonProcessingException { @@ -2736,14 +1200,23 @@ public T treeToValue(TreeNode n, Class valueType) public T valueToTree(Object fromValue) throws IllegalArgumentException { - if (fromValue == null) return null; - TokenBuffer buf = new TokenBuffer(this, false); + if (fromValue == null) { + return null; + } + // 06-Oct-2017, tatu: `convertValue()` disables root value wrapping so + // do it here too + SerializationConfig config = serializationConfig() + .without(SerializationFeature.WRAP_ROOT_VALUE); + DefaultSerializerProvider prov = _serializerProvider(config); + TokenBuffer buf = TokenBuffer.forValueConversion(prov); + // Would like to let buffer decide, but it won't have deser config to check so... if (isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) { buf = buf.forceUseOfBigDecimal(true); } JsonNode result; try { - writeValue(buf, fromValue); + // Equivalent to `writeValue()`, basically: + prov.serializeValue(buf, fromValue); JsonParser p = buf.asParser(); result = readTree(p); p.close(); @@ -2751,85 +1224,12 @@ public T valueToTree(Object fromValue) throw new IllegalArgumentException(e.getMessage(), e); } return (T) result; - } - - /* - /********************************************************** - /* Extended Public API, accessors - /********************************************************** - */ - - /** - * Method that can be called to check whether mapper thinks - * it could serialize an instance of given Class. - * Check is done - * by checking whether a serializer can be found for the type. - *

- * NOTE: since this method does NOT throw exceptions, but internal - * processing may, caller usually has little information as to why - * 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 - * serializable) - */ - public boolean canSerialize(Class type) { - return _serializerProvider(getSerializationConfig()).hasSerializerFor(type, null); - } - - /** - * Method similar to {@link #canSerialize(Class)} but that can return - * actual {@link Throwable} that was thrown when trying to construct - * serializer: this may be useful in figuring out what the actual problem is. - * - * @since 2.3 - */ - public boolean canSerialize(Class type, AtomicReference cause) { - return _serializerProvider(getSerializationConfig()).hasSerializerFor(type, cause); - } - - /** - * 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 registered deserializer can - * be found or built for the type; if not (either by no mapping being - * found, or through an Exception being thrown, false - * is returned. - *

- * NOTE: 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 - * serializable) - */ - public boolean canDeserialize(JavaType type) - { - return createDeserializationContext(null, - getDeserializationConfig()).hasValueDeserializerFor(type, null); } - /** - * Method similar to {@link #canDeserialize(JavaType)} but that can return - * actual {@link Throwable} that was thrown when trying to construct - * serializer: this may be useful in figuring out what the actual problem is. - * - * @since 2.3 - */ - public boolean canDeserialize(JavaType type, AtomicReference cause) - { - return createDeserializationContext(null, - getDeserializationConfig()).hasValueDeserializerFor(type, cause); - } - /* - /********************************************************** - /* Extended Public API, deserialization, - /* convenience methods - /********************************************************** + /********************************************************************** + /* Public API, deserialization (ext format to Java Objects) + /********************************************************************** */ /** @@ -2845,10 +1245,11 @@ public boolean canDeserialize(JavaType type, AtomicReference cause) * expected for result type (or has other mismatch issues) */ @SuppressWarnings("unchecked") - public T readValue(File src, Class valueType) - throws IOException, JsonParseException, JsonMappingException + public T readValue(File src, Class valueType) throws IOException { - return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueType)); + DefaultDeserializationContext ctxt = createDeserializationContext(); + return (T) _readMapAndClose(ctxt, _streamFactory.createParser(ctxt, src), + _typeFactory.constructType(valueType)); } /** @@ -2864,10 +1265,11 @@ public T readValue(File src, Class valueType) * expected for result type (or has other mismatch issues) */ @SuppressWarnings({ "unchecked" }) - public T readValue(File src, TypeReference valueTypeRef) - throws IOException, JsonParseException, JsonMappingException + public T readValue(File src, TypeReference valueTypeRef) throws IOException { - return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueTypeRef)); + DefaultDeserializationContext ctxt = createDeserializationContext(); + return (T) _readMapAndClose(ctxt, _streamFactory.createParser(ctxt, src), + _typeFactory.constructType(valueTypeRef)); } /** @@ -2886,7 +1288,8 @@ public T readValue(File src, TypeReference valueTypeRef) public T readValue(File src, JavaType valueType) throws IOException, JsonParseException, JsonMappingException { - return (T) _readMapAndClose(_jsonFactory.createParser(src), valueType); + DefaultDeserializationContext ctxt = createDeserializationContext(); + return (T) _readMapAndClose(ctxt, _streamFactory.createParser(ctxt, src), valueType); } /** @@ -2905,7 +1308,9 @@ public T readValue(File src, JavaType valueType) public T readValue(URL src, Class valueType) throws IOException, JsonParseException, JsonMappingException { - return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueType)); + DefaultDeserializationContext ctxt = createDeserializationContext(); + return (T) _readMapAndClose(ctxt, + _streamFactory.createParser(ctxt, src), _typeFactory.constructType(valueType)); } /** @@ -2921,17 +1326,19 @@ public T readValue(URL src, Class valueType) * expected for result type (or has other mismatch issues) */ @SuppressWarnings({ "unchecked" }) - public T readValue(URL src, TypeReference valueTypeRef) - throws IOException, JsonParseException, JsonMappingException + public T readValue(URL src, TypeReference valueTypeRef) throws IOException { - return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueTypeRef)); + DefaultDeserializationContext ctxt = createDeserializationContext(); + return (T) _readMapAndClose(ctxt, + _streamFactory.createParser(ctxt, src), _typeFactory.constructType(valueTypeRef)); } @SuppressWarnings("unchecked") - public T readValue(URL src, JavaType valueType) - throws IOException, JsonParseException, JsonMappingException + public T readValue(URL src, JavaType valueType) throws IOException { - return (T) _readMapAndClose(_jsonFactory.createParser(src), valueType); + DefaultDeserializationContext ctxt = createDeserializationContext(); + return (T) _readMapAndClose(ctxt, + _streamFactory.createParser(ctxt, src), valueType); } /** @@ -2950,7 +1357,9 @@ public T readValue(URL src, JavaType valueType) public T readValue(String content, Class valueType) throws IOException, JsonParseException, JsonMappingException { - return (T) _readMapAndClose(_jsonFactory.createParser(content), _typeFactory.constructType(valueType)); + DefaultDeserializationContext ctxt = createDeserializationContext(); + return (T) _readMapAndClose(ctxt, + _streamFactory.createParser(ctxt, content), _typeFactory.constructType(valueType)); } /** @@ -2966,10 +1375,11 @@ public T readValue(String content, Class valueType) * expected for result type (or has other mismatch issues) */ @SuppressWarnings({ "unchecked" }) - public T readValue(String content, TypeReference valueTypeRef) - throws IOException, JsonParseException, JsonMappingException + public T readValue(String content, TypeReference valueTypeRef) throws IOException { - return (T) _readMapAndClose(_jsonFactory.createParser(content), _typeFactory.constructType(valueTypeRef)); + DefaultDeserializationContext ctxt = createDeserializationContext(); + return (T) _readMapAndClose(ctxt, + _streamFactory.createParser(ctxt, content), _typeFactory.constructType(valueTypeRef)); } /** @@ -2988,114 +1398,140 @@ public T readValue(String content, TypeReference valueTypeRef) public T readValue(String content, JavaType valueType) throws IOException, JsonParseException, JsonMappingException { - return (T) _readMapAndClose(_jsonFactory.createParser(content), valueType); - } + DefaultDeserializationContext ctxt = createDeserializationContext(); + return (T) _readMapAndClose(ctxt, + _streamFactory.createParser(ctxt, content), valueType); + } @SuppressWarnings("unchecked") public T readValue(Reader src, Class valueType) throws IOException, JsonParseException, JsonMappingException { - return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueType)); - } + DefaultDeserializationContext ctxt = createDeserializationContext(); + return (T) _readMapAndClose(ctxt, + _streamFactory.createParser(ctxt, src), _typeFactory.constructType(valueType)); + } @SuppressWarnings({ "unchecked" }) public T readValue(Reader src, TypeReference valueTypeRef) throws IOException, JsonParseException, JsonMappingException { - return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueTypeRef)); - } + DefaultDeserializationContext ctxt = createDeserializationContext(); + return (T) _readMapAndClose(ctxt, + _streamFactory.createParser(ctxt, src), _typeFactory.constructType(valueTypeRef)); + } @SuppressWarnings("unchecked") public T readValue(Reader src, JavaType valueType) throws IOException, JsonParseException, JsonMappingException { - return (T) _readMapAndClose(_jsonFactory.createParser(src), valueType); - } + DefaultDeserializationContext ctxt = createDeserializationContext(); + return (T) _readMapAndClose(ctxt, + _streamFactory.createParser(ctxt, src), valueType); + } @SuppressWarnings("unchecked") public T readValue(InputStream src, Class valueType) throws IOException, JsonParseException, JsonMappingException { - return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueType)); - } + DefaultDeserializationContext ctxt = createDeserializationContext(); + return (T) _readMapAndClose(ctxt, + _streamFactory.createParser(ctxt, src), _typeFactory.constructType(valueType)); + } @SuppressWarnings({ "unchecked" }) public T readValue(InputStream src, TypeReference valueTypeRef) throws IOException, JsonParseException, JsonMappingException { - return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueTypeRef)); - } + DefaultDeserializationContext ctxt = createDeserializationContext(); + return (T) _readMapAndClose(ctxt, + _streamFactory.createParser(ctxt, src), _typeFactory.constructType(valueTypeRef)); + } @SuppressWarnings("unchecked") public T readValue(InputStream src, JavaType valueType) throws IOException, JsonParseException, JsonMappingException { - return (T) _readMapAndClose(_jsonFactory.createParser(src), valueType); - } + DefaultDeserializationContext ctxt = createDeserializationContext(); + return (T) _readMapAndClose(ctxt, + _streamFactory.createParser(ctxt, src), valueType); + } @SuppressWarnings("unchecked") public T readValue(byte[] src, Class valueType) throws IOException, JsonParseException, JsonMappingException { - return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueType)); - } - + DefaultDeserializationContext ctxt = createDeserializationContext(); + return (T) _readMapAndClose(ctxt, + _streamFactory.createParser(ctxt, src), _typeFactory.constructType(valueType)); + } + @SuppressWarnings("unchecked") - public T readValue(byte[] src, int offset, int len, - Class valueType) + public T readValue(byte[] src, int offset, int len, Class valueType) throws IOException, JsonParseException, JsonMappingException { - return (T) _readMapAndClose(_jsonFactory.createParser(src, offset, len), _typeFactory.constructType(valueType)); - } + DefaultDeserializationContext ctxt = createDeserializationContext(); + return (T) _readMapAndClose(ctxt, + _streamFactory.createParser(ctxt, src, offset, len), _typeFactory.constructType(valueType)); + } @SuppressWarnings({ "unchecked" }) public T readValue(byte[] src, TypeReference valueTypeRef) throws IOException, JsonParseException, JsonMappingException { - return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueTypeRef)); - } - + DefaultDeserializationContext ctxt = createDeserializationContext(); + return (T) _readMapAndClose(ctxt, + _streamFactory.createParser(ctxt, src), _typeFactory.constructType(valueTypeRef)); + } + @SuppressWarnings({ "unchecked" }) - public T readValue(byte[] src, int offset, int len, - TypeReference valueTypeRef) + public T readValue(byte[] src, int offset, int len, TypeReference valueTypeRef) throws IOException, JsonParseException, JsonMappingException { - return (T) _readMapAndClose(_jsonFactory.createParser(src, offset, len), _typeFactory.constructType(valueTypeRef)); - } + DefaultDeserializationContext ctxt = createDeserializationContext(); + return (T) _readMapAndClose(ctxt, + _streamFactory.createParser(ctxt, src, offset, len), _typeFactory.constructType(valueTypeRef)); + } @SuppressWarnings("unchecked") public T readValue(byte[] src, JavaType valueType) throws IOException, JsonParseException, JsonMappingException { - return (T) _readMapAndClose(_jsonFactory.createParser(src), valueType); - } + DefaultDeserializationContext ctxt = createDeserializationContext(); + return (T) _readMapAndClose(ctxt, + _streamFactory.createParser(ctxt, src), valueType); + } @SuppressWarnings("unchecked") public T readValue(byte[] src, int offset, int len, JavaType valueType) throws IOException, JsonParseException, JsonMappingException { - return (T) _readMapAndClose(_jsonFactory.createParser(src, offset, len), valueType); + DefaultDeserializationContext ctxt = createDeserializationContext(); + return (T) _readMapAndClose(ctxt, + _streamFactory.createParser(ctxt, src, offset, len), valueType); } @SuppressWarnings("unchecked") public T readValue(DataInput src, Class valueType) throws IOException { - return (T) _readMapAndClose(_jsonFactory.createParser(src), - _typeFactory.constructType(valueType)); + DefaultDeserializationContext ctxt = createDeserializationContext(); + return (T) _readMapAndClose(ctxt, + _streamFactory.createParser(ctxt, src), _typeFactory.constructType(valueType)); } @SuppressWarnings("unchecked") public T readValue(DataInput src, JavaType valueType) throws IOException { - return (T) _readMapAndClose(_jsonFactory.createParser(src), valueType); + DefaultDeserializationContext ctxt = createDeserializationContext(); + return (T) _readMapAndClose(ctxt, + _streamFactory.createParser(ctxt, src), valueType); } /* - /********************************************************** - /* Extended Public API: serialization - /* (mapping from Java types to JSON) - /********************************************************** + /********************************************************************** + /* Public API: serialization (mapping from Java types to external format) + /********************************************************************** */ /** @@ -3105,7 +1541,9 @@ public T readValue(DataInput src, JavaType valueType) throws IOException public void writeValue(File resultFile, Object value) throws IOException, JsonGenerationException, JsonMappingException { - _configAndWriteValue(_jsonFactory.createGenerator(resultFile, JsonEncoding.UTF8), value); + DefaultSerializerProvider prov = _serializerProvider(); + _configAndWriteValue(prov, + _streamFactory.createGenerator(prov, resultFile, JsonEncoding.UTF8), value); } /** @@ -3114,7 +1552,7 @@ public void writeValue(File resultFile, Object value) * {@link JsonEncoding#UTF8}). *

* Note: method does not close the underlying stream explicitly - * here; however, {@link JsonFactory} this mapper uses may choose + * here; however, {@link TokenStreamFactory} this mapper uses may choose * to close the stream depending on its settings (by default, * it will try to close it when {@link JsonGenerator} we construct * is closed). @@ -3122,16 +1560,17 @@ public void writeValue(File resultFile, Object value) public void writeValue(OutputStream out, Object value) throws IOException, JsonGenerationException, JsonMappingException { - _configAndWriteValue(_jsonFactory.createGenerator(out, JsonEncoding.UTF8), value); + DefaultSerializerProvider prov = _serializerProvider(); + _configAndWriteValue(prov, + _streamFactory.createGenerator(prov, out, JsonEncoding.UTF8), value); } - - /** - * @since 2.8 - */ + public void writeValue(DataOutput out, Object value) throws IOException { - _configAndWriteValue(_jsonFactory.createGenerator(out, JsonEncoding.UTF8), value); + DefaultSerializerProvider prov = _serializerProvider(); + _configAndWriteValue(prov, + _streamFactory.createGenerator(prov, out), value); } /** @@ -3139,7 +1578,7 @@ public void writeValue(DataOutput out, Object value) * JSON output, using Writer provided. *

* Note: method does not close the underlying stream explicitly - * here; however, {@link JsonFactory} this mapper uses may choose + * here; however, {@link TokenStreamFactory} this mapper uses may choose * to close the stream depending on its settings (by default, * it will try to close it when {@link JsonGenerator} we construct * is closed). @@ -3147,7 +1586,8 @@ public void writeValue(DataOutput out, Object value) public void writeValue(Writer w, Object value) throws IOException, JsonGenerationException, JsonMappingException { - _configAndWriteValue(_jsonFactory.createGenerator(w), value); + DefaultSerializerProvider prov = _serializerProvider(); + _configAndWriteValue(prov, _streamFactory.createGenerator(prov, w), value); } /** @@ -3155,17 +1595,16 @@ public void writeValue(Writer w, Object value) * a String. Functionally equivalent to calling * {@link #writeValue(Writer,Object)} with {@link java.io.StringWriter} * and constructing String, but more efficient. - *

- * Note: prior to version 2.1, throws clause included {@link IOException}; 2.1 removed it. */ @SuppressWarnings("resource") public String writeValueAsString(Object value) throws JsonProcessingException { // alas, we have to pull the recycler directly here... - SegmentedStringWriter sw = new SegmentedStringWriter(_jsonFactory._getBufferRecycler()); + SegmentedStringWriter sw = new SegmentedStringWriter(_streamFactory._getBufferRecycler()); + DefaultSerializerProvider prov = _serializerProvider(); try { - _configAndWriteValue(_jsonFactory.createGenerator(sw), value); + _configAndWriteValue(prov, _streamFactory.createGenerator(prov, sw), value); } catch (JsonProcessingException e) { throw e; } catch (IOException e) { // shouldn't really happen, but is declared as possibility so: @@ -3180,17 +1619,17 @@ public String writeValueAsString(Object value) * {@link #writeValue(Writer,Object)} with {@link java.io.ByteArrayOutputStream} * and getting bytes, but more efficient. * Encoding used will be UTF-8. - *

- * Note: prior to version 2.1, throws clause included {@link IOException}; 2.1 removed it. */ @SuppressWarnings("resource") public byte[] writeValueAsBytes(Object value) throws JsonProcessingException { - ByteArrayBuilder bb = new ByteArrayBuilder(_jsonFactory._getBufferRecycler()); + DefaultSerializerProvider prov = _serializerProvider(); + ByteArrayBuilder bb = new ByteArrayBuilder(_streamFactory._getBufferRecycler()); try { - _configAndWriteValue(_jsonFactory.createGenerator(bb, JsonEncoding.UTF8), value); - } catch (JsonProcessingException e) { // to support [JACKSON-758] + _configAndWriteValue(prov, + _streamFactory.createGenerator(prov, bb, JsonEncoding.UTF8), value); + } catch (JsonProcessingException e) { throw e; } catch (IOException e) { // shouldn't really happen, but is declared as possibility so: throw JsonMappingException.fromUnexpectedIOE(e); @@ -3200,11 +1639,73 @@ public byte[] writeValueAsBytes(Object value) return result; } + /** + * Method called to configure the generator as necessary and then + * call write functionality + */ + protected final void _configAndWriteValue(DefaultSerializerProvider prov, + JsonGenerator g, Object value) + throws IOException + { + if (prov.isEnabled(SerializationFeature.CLOSE_CLOSEABLE) && (value instanceof Closeable)) { + _configAndWriteCloseable(prov, g, value); + return; + } + try { + prov.serializeValue(g, value); + } catch (Exception e) { + ClassUtil.closeOnFailAndThrowAsIOE(g, e); + return; + } + g.close(); + } + + /** + * Helper method used when value to serialize is {@link Closeable} and its close() + * method is to be called right after serialization has been called + */ + private final void _configAndWriteCloseable(DefaultSerializerProvider prov, + JsonGenerator g, Object value) + throws IOException + { + Closeable toClose = (Closeable) value; + try { + prov.serializeValue(g, value); + Closeable tmpToClose = toClose; + toClose = null; + tmpToClose.close(); + } catch (Exception e) { + ClassUtil.closeOnFailAndThrowAsIOE(g, toClose, e); + return; + } + g.close(); + } + + /** + * Helper method used when value to serialize is {@link Closeable} and its close() + * method is to be called right after serialization has been called + */ + protected final void _writeCloseableValue(JsonGenerator g, Object value, SerializationConfig cfg) + throws IOException + { + Closeable toClose = (Closeable) value; + try { + _serializerProvider(cfg).serializeValue(g, value); + if (cfg.isEnabled(SerializationFeature.FLUSH_AFTER_WRITE_VALUE)) { + g.flush(); + } + } catch (Exception e) { + ClassUtil.closeOnFailAndThrowAsIOE(null, toClose, e); + return; + } + toClose.close(); + } + /* - /********************************************************** - /* Extended Public API: constructing ObjectWriters + /********************************************************************** + /* Public API: constructing ObjectWriters /* for more advanced configuration - /********************************************************** + /********************************************************************** */ /** @@ -3212,7 +1713,7 @@ public byte[] writeValueAsBytes(Object value) * with default settings. */ public ObjectWriter writer() { - return _newWriter(getSerializationConfig()); + return _newWriter(serializationConfig()); } /** @@ -3221,7 +1722,7 @@ public ObjectWriter writer() { * mapper instance has). */ public ObjectWriter writer(SerializationFeature feature) { - return _newWriter(getSerializationConfig().with(feature)); + return _newWriter(serializationConfig().with(feature)); } /** @@ -3231,7 +1732,7 @@ public ObjectWriter writer(SerializationFeature feature) { */ public ObjectWriter writer(SerializationFeature first, SerializationFeature... other) { - return _newWriter(getSerializationConfig().with(first, other)); + return _newWriter(serializationConfig().with(first, other)); } /** @@ -3240,7 +1741,7 @@ public ObjectWriter writer(SerializationFeature first, * null passed, using timestamp (64-bit number. */ public ObjectWriter writer(DateFormat df) { - return _newWriter(getSerializationConfig().with(df)); + return _newWriter(serializationConfig().with(df)); } /** @@ -3248,7 +1749,7 @@ public ObjectWriter writer(DateFormat df) { * serialize objects using specified JSON View (filter). */ public ObjectWriter writerWithView(Class serializationView) { - return _newWriter(getSerializationConfig().withView(serializationView)); + return _newWriter(serializationConfig().withView(serializationView)); } /** @@ -3259,11 +1760,9 @@ public ObjectWriter writerWithView(Class serializationView) { * Main reason for using this method is performance, as writer is able * to pre-fetch serializer to use before write, and if writer is used * more than once this avoids addition per-value serializer lookups. - * - * @since 2.5 */ public ObjectWriter writerFor(Class rootType) { - return _newWriter(getSerializationConfig(), + return _newWriter(serializationConfig(), ((rootType == null) ? null :_typeFactory.constructType(rootType)), /*PrettyPrinter*/null); } @@ -3276,11 +1775,9 @@ public ObjectWriter writerFor(Class rootType) { * Main reason for using this method is performance, as writer is able * to pre-fetch serializer to use before write, and if writer is used * more than once this avoids addition per-value serializer lookups. - * - * @since 2.5 */ public ObjectWriter writerFor(TypeReference rootType) { - return _newWriter(getSerializationConfig(), + return _newWriter(serializationConfig(), ((rootType == null) ? null : _typeFactory.constructType(rootType)), /*PrettyPrinter*/null); } @@ -3293,31 +1790,17 @@ public ObjectWriter writerFor(TypeReference rootType) { * Main reason for using this method is performance, as writer is able * to pre-fetch serializer to use before write, and if writer is used * more than once this avoids addition per-value serializer lookups. - * - * @since 2.5 */ public ObjectWriter writerFor(JavaType rootType) { - return _newWriter(getSerializationConfig(), rootType, /*PrettyPrinter*/null); + return _newWriter(serializationConfig(), rootType, /*PrettyPrinter*/null); } - /** - * Factory method for constructing {@link ObjectWriter} that will - * serialize objects using specified pretty printer for indentation - * (or if null, no pretty printer) - */ - public ObjectWriter writer(PrettyPrinter pp) { - if (pp == null) { // need to use a marker to indicate explicit disabling of pp - pp = ObjectWriter.NULL_PRETTY_PRINTER; - } - return _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() { - SerializationConfig config = getSerializationConfig(); + SerializationConfig config = serializationConfig(); return _newWriter(config, /*root type*/ null, config.getDefaultPrettyPrinter()); } @@ -3327,7 +1810,7 @@ public ObjectWriter writerWithDefaultPrettyPrinter() { * serialize objects using specified filter provider. */ public ObjectWriter writer(FilterProvider filterProvider) { - return _newWriter(getSerializationConfig().withFilters(filterProvider)); + return _newWriter(serializationConfig().withFilters(filterProvider)); } /** @@ -3339,74 +1822,38 @@ public ObjectWriter writer(FilterProvider filterProvider) { */ public ObjectWriter writer(FormatSchema schema) { _verifySchemaType(schema); - return _newWriter(getSerializationConfig(), schema); + return _newWriter(serializationConfig(), schema); } /** * Factory method for constructing {@link ObjectWriter} that will * use specified Base64 encoding variant for Base64-encoded binary data. - * - * @since 2.1 */ public ObjectWriter writer(Base64Variant defaultBase64) { - return _newWriter(getSerializationConfig().with(defaultBase64)); + return _newWriter(serializationConfig().with(defaultBase64)); } /** * Factory method for constructing {@link ObjectReader} that will * use specified character escaping details for output. - * - * @since 2.3 */ public ObjectWriter writer(CharacterEscapes escapes) { - return _newWriter(getSerializationConfig()).with(escapes); + return _newWriter(serializationConfig()).with(escapes); } /** * Factory method for constructing {@link ObjectWriter} that will * use specified default attributes. - * - * @since 2.3 */ public ObjectWriter writer(ContextAttributes attrs) { - return _newWriter(getSerializationConfig().with(attrs)); - } - - /** - * @deprecated Since 2.5, use {@link #writerFor(Class)} instead - */ - @Deprecated - public ObjectWriter writerWithType(Class rootType) { - return _newWriter(getSerializationConfig(), - // 15-Mar-2013, tatu: Important! Indicate that static typing is needed: - ((rootType == null) ? null :_typeFactory.constructType(rootType)), - /*PrettyPrinter*/null); - } - - /** - * @deprecated Since 2.5, use {@link #writerFor(TypeReference)} instead - */ - @Deprecated - public ObjectWriter writerWithType(TypeReference rootType) { - return _newWriter(getSerializationConfig(), - // 15-Mar-2013, tatu: Important! Indicate that static typing is needed: - ((rootType == null) ? null : _typeFactory.constructType(rootType)), - /*PrettyPrinter*/null); + return _newWriter(serializationConfig().with(attrs)); } - /** - * @deprecated Since 2.5, use {@link #writerFor(JavaType)} instead - */ - @Deprecated - public ObjectWriter writerWithType(JavaType rootType) { - return _newWriter(getSerializationConfig(), rootType, /*PrettyPrinter*/null); - } - /* - /********************************************************** + /********************************************************************** /* Extended Public API: constructing ObjectReaders /* for more advanced configuration - /********************************************************** + /********************************************************************** */ /** @@ -3415,7 +1862,7 @@ public ObjectWriter writerWithType(JavaType rootType) { * without defining expected value type. */ public ObjectReader reader() { - return _newReader(getDeserializationConfig()).with(_injectableValues); + return _newReader(deserializationConfig()).with(_injectableValues); } /** @@ -3426,7 +1873,7 @@ public ObjectReader reader() { * without defining expected value type. */ public ObjectReader reader(DeserializationFeature feature) { - return _newReader(getDeserializationConfig().with(feature)); + return _newReader(deserializationConfig().with(feature)); } /** @@ -3438,7 +1885,7 @@ public ObjectReader reader(DeserializationFeature feature) { */ public ObjectReader reader(DeserializationFeature first, DeserializationFeature... other) { - return _newReader(getDeserializationConfig().with(first, other)); + return _newReader(deserializationConfig().with(first, other)); } /** @@ -3453,40 +1900,34 @@ public ObjectReader reader(DeserializationFeature first, */ public ObjectReader readerForUpdating(Object valueToUpdate) { JavaType t = _typeFactory.constructType(valueToUpdate.getClass()); - return _newReader(getDeserializationConfig(), t, valueToUpdate, + return _newReader(deserializationConfig(), t, valueToUpdate, null, _injectableValues); } /** * Factory method for constructing {@link ObjectReader} that will * read or update instances of specified type - * - * @since 2.6 */ public ObjectReader readerFor(JavaType type) { - return _newReader(getDeserializationConfig(), type, null, + return _newReader(deserializationConfig(), type, null, null, _injectableValues); } /** * Factory method for constructing {@link ObjectReader} that will * read or update instances of specified type - * - * @since 2.6 */ public ObjectReader readerFor(Class type) { - return _newReader(getDeserializationConfig(), _typeFactory.constructType(type), null, + return _newReader(deserializationConfig(), _typeFactory.constructType(type), null, null, _injectableValues); } /** * Factory method for constructing {@link ObjectReader} that will * read or update instances of specified type - * - * @since 2.6 */ public ObjectReader readerFor(TypeReference type) { - return _newReader(getDeserializationConfig(), _typeFactory.constructType(type), null, + return _newReader(deserializationConfig(), _typeFactory.constructType(type), null, null, _injectableValues); } @@ -3495,7 +1936,7 @@ public ObjectReader readerFor(TypeReference type) { * use specified {@link JsonNodeFactory} for constructing JSON trees. */ public ObjectReader reader(JsonNodeFactory f) { - return _newReader(getDeserializationConfig()).with(f); + return _newReader(deserializationConfig()).with(f); } /** @@ -3507,7 +1948,7 @@ public ObjectReader reader(JsonNodeFactory f) { */ public ObjectReader reader(FormatSchema schema) { _verifySchemaType(schema); - return _newReader(getDeserializationConfig(), null, null, + return _newReader(deserializationConfig(), null, null, schema, _injectableValues); } @@ -3518,7 +1959,7 @@ public ObjectReader reader(FormatSchema schema) { * @param injectableValues Injectable values to use */ public ObjectReader reader(InjectableValues injectableValues) { - return _newReader(getDeserializationConfig(), null, null, + return _newReader(deserializationConfig(), null, null, null, injectableValues); } @@ -3527,60 +1968,29 @@ public ObjectReader reader(InjectableValues injectableValues) { * deserialize objects using specified JSON View (filter). */ public ObjectReader readerWithView(Class view) { - return _newReader(getDeserializationConfig().withView(view)); + return _newReader(deserializationConfig().withView(view)); } /** * Factory method for constructing {@link ObjectReader} that will * use specified Base64 encoding variant for Base64-encoded binary data. - * - * @since 2.1 */ public ObjectReader reader(Base64Variant defaultBase64) { - return _newReader(getDeserializationConfig().with(defaultBase64)); + return _newReader(deserializationConfig().with(defaultBase64)); } /** * Factory method for constructing {@link ObjectReader} that will * use specified default attributes. - * - * @since 2.3 */ public ObjectReader reader(ContextAttributes attrs) { - return _newReader(getDeserializationConfig().with(attrs)); - } - - /** - * @deprecated Since 2.5, use {@link #readerFor(JavaType)} instead - */ - @Deprecated - public ObjectReader reader(JavaType type) { - return _newReader(getDeserializationConfig(), type, null, - null, _injectableValues); - } - - /** - * @deprecated Since 2.5, use {@link #readerFor(Class)} instead - */ - @Deprecated - public ObjectReader reader(Class type) { - return _newReader(getDeserializationConfig(), _typeFactory.constructType(type), null, - null, _injectableValues); - } - - /** - * @deprecated Since 2.5, use {@link #readerFor(TypeReference)} instead - */ - @Deprecated - public ObjectReader reader(TypeReference type) { - return _newReader(getDeserializationConfig(), _typeFactory.constructType(type), null, - null, _injectableValues); + return _newReader(deserializationConfig().with(attrs)); } /* - /********************************************************** + /********************************************************************** /* Extended Public API: convenience type conversion - /********************************************************** + /********************************************************************** */ /** @@ -3602,7 +2012,7 @@ public ObjectReader reader(TypeReference type) { * the goal is just to allow efficient value conversions for structurally * compatible Objects, according to standard Jackson configuration. *

- * Further note that functianality is not designed to support "advanced" use + * Further note that this functionality is not designed to support "advanced" use * cases, such as conversion of polymorphic values, or cases where Object Identity * is used. * @@ -3650,35 +2060,36 @@ protected Object _convert(Object fromValue, JavaType toValueType) throws IllegalArgumentException { // 25-Jan-2019, tatu: [databind#2220] Let's NOT try to short-circuit anything - - // Then use TokenBuffer, which is a JsonGenerator: - TokenBuffer buf = new TokenBuffer(this, false); + + // inlined 'writeValue' with minor changes: + // first: disable wrapping when writing + SerializationConfig config = serializationConfig() + .without(SerializationFeature.WRAP_ROOT_VALUE); + DefaultSerializerProvider prov = _serializerProvider(config); + TokenBuffer buf = TokenBuffer.forValueConversion(prov); + // Would like to let buffer decide, but it won't have deser config to check so... if (isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) { buf = buf.forceUseOfBigDecimal(true); } try { - // inlined 'writeValue' with minor changes: - // first: disable wrapping when writing - SerializationConfig config = getSerializationConfig().without(SerializationFeature.WRAP_ROOT_VALUE); // no need to check for closing of TokenBuffer - _serializerProvider(config).serializeValue(buf, fromValue); + prov.serializeValue(buf, fromValue); // then matching read, inlined 'readValue' with minor mods: - final JsonParser p = buf.asParser(); + DefaultDeserializationContext readCtxt = createDeserializationContext(); + final JsonParser p = buf.asParser(readCtxt); + readCtxt.assignParser(p); Object result; // ok to pass in existing feature flags; unwrapping handled by mapper - final DeserializationConfig deserConfig = getDeserializationConfig(); JsonToken t = _initForReading(p, toValueType); if (t == JsonToken.VALUE_NULL) { - DeserializationContext ctxt = createDeserializationContext(p, deserConfig); - result = _findRootDeserializer(ctxt, toValueType).getNullValue(ctxt); + result = _findRootDeserializer(readCtxt, toValueType).getNullValue(readCtxt); } else if (t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) { result = null; } else { // pointing to event other than null - DeserializationContext ctxt = createDeserializationContext(p, deserConfig); - JsonDeserializer deser = _findRootDeserializer(ctxt, toValueType); + JsonDeserializer deser = _findRootDeserializer(readCtxt, toValueType); // note: no handling of unwrapping - result = deser.deserialize(p, ctxt); + result = deser.deserialize(p, readCtxt); } p.close(); return result; @@ -3708,7 +2119,7 @@ protected Object _convert(Object fromValue, JavaType toValueType) * of container to update) are modified, unless properties themselves indicate that * merging should be applied for contents. Such merging can be specified using * annotations (see JsonMerge) as well as using "config overrides" (see - * {@link #configOverride(Class)} and {@link #setDefaultMergeable(Boolean)}). + * {@link MapperBuilder#withConfigOverride} and {@link MapperBuilder#defaultMergeable}). * * @param valueToUpdate Object to update * @param overrides Object to conceptually serialize and merge into value to @@ -3719,59 +2130,44 @@ protected Object _convert(Object fromValue, JavaType toValueType) * Java array will create a new array) * * @throws JsonMappingException if there are structural incompatibilities that prevent update. - * - * @since 2.9 */ @SuppressWarnings("resource") public T updateValue(T valueToUpdate, Object overrides) throws JsonMappingException { - T result = valueToUpdate; - if ((valueToUpdate != null) && (overrides != null)) { - TokenBuffer buf = new TokenBuffer(this, false); - if (isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) { - buf = buf.forceUseOfBigDecimal(true); - } - try { - SerializationConfig config = getSerializationConfig(). - without(SerializationFeature.WRAP_ROOT_VALUE); - _serializerProvider(config).serializeValue(buf, overrides); - JsonParser p = buf.asParser(); - result = readerForUpdating(valueToUpdate).readValue(p); - p.close(); - } catch (IOException e) { // should not occur, no real i/o... - if (e instanceof JsonMappingException) { - throw (JsonMappingException) e; - } - // 17-Mar-2017, tatu: Really ought not happen... - throw JsonMappingException.fromUnexpectedIOE(e); + if ((valueToUpdate == null) || (overrides == null)) { + return valueToUpdate; + } + SerializationConfig config = serializationConfig() + .without(SerializationFeature.WRAP_ROOT_VALUE); + DefaultSerializerProvider prov = _serializerProvider(config); + TokenBuffer buf = TokenBuffer.forValueConversion(prov); + // Would like to let buffer decide, but it won't have deser config to check so... + if (isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) { + buf = buf.forceUseOfBigDecimal(true); + } + T result; + try { + prov.serializeValue(buf, overrides); + JsonParser p = buf.asParser(); + result = readerForUpdating(valueToUpdate).readValue(p); + p.close(); + } catch (IOException e) { // should not occur, no real i/o... + if (e instanceof JsonMappingException) { + throw (JsonMappingException) e; } + // 17-Mar-2017, tatu: Really ought not happen... + throw JsonMappingException.fromUnexpectedIOE(e); } return result; } /* - /********************************************************** + /********************************************************************** /* Extended Public API: JSON Schema generation - /********************************************************** + /********************************************************************** */ - /** - * Generate Json-schema - * instance for specified class. - * - * @param t The class to generate schema for - * @return Constructed JSON schema. - * - * @deprecated Since 2.6 use external JSON Schema generator (https://github.com/FasterXML/jackson-module-jsonSchema) - * (which under the hood calls {@link #acceptJsonFormatVisitor(JavaType, JsonFormatVisitorWrapper)}) - */ - @Deprecated - public com.fasterxml.jackson.databind.jsonschema.JsonSchema generateJsonSchema(Class t) - throws JsonMappingException { - return _serializerProvider(getSerializationConfig()).generateJsonSchema(t); - } - /** * Method for visiting type hierarchy for given type, using specified visitor. *

@@ -3780,8 +2176,6 @@ public com.fasterxml.jackson.databind.jsonschema.JsonSchema generateJsonSchema(C * instance for specified type. * * @param type Type to generate schema for (possibly with generic signature) - * - * @since 2.1 */ public void acceptJsonFormatVisitor(Class type, JsonFormatVisitorWrapper visitor) throws JsonMappingException @@ -3789,6 +2183,12 @@ public void acceptJsonFormatVisitor(Class type, JsonFormatVisitorWrapper visi acceptJsonFormatVisitor(_typeFactory.constructType(type), visitor); } + public void acceptJsonFormatVisitor(TypeReference typeRef, JsonFormatVisitorWrapper visitor) + throws JsonMappingException + { + acceptJsonFormatVisitor(_typeFactory.constructType(typeRef), visitor); + } + /** * Method for visiting type hierarchy for given type, using specified visitor. * Visitation uses Serializer hierarchy and related properties @@ -3798,8 +2198,6 @@ public void acceptJsonFormatVisitor(Class type, JsonFormatVisitorWrapper visi * instance for specified type. * * @param type Type to generate schema for (possibly with generic signature) - * - * @since 2.1 */ public void acceptJsonFormatVisitor(JavaType type, JsonFormatVisitorWrapper visitor) throws JsonMappingException @@ -3807,29 +2205,13 @@ public void acceptJsonFormatVisitor(JavaType type, JsonFormatVisitorWrapper visi if (type == null) { throw new IllegalArgumentException("type must be provided"); } - _serializerProvider(getSerializationConfig()).acceptJsonFormatVisitor(type, visitor); - } - - /* - /********************************************************** - /* Internal factory methods for type ids, overridable - /********************************************************** - */ - - /** - * Overridable factory method, separate to allow format-specific mappers (and specifically - * XML-backed one, currently) to offer custom {@link TypeResolverBuilder} subtypes. - * - * @since 2.10 - */ - protected TypeResolverBuilder _constructDefaultTypeResolverBuilder(DefaultTyping applicability) { - return new DefaultTypeResolverBuilder(applicability); + _serializerProvider().acceptJsonFormatVisitor(type, visitor); } /* - /********************************************************** + /********************************************************************** /* Internal methods for serialization, overridable - /********************************************************** + /********************************************************************** */ /** @@ -3837,81 +2219,27 @@ protected TypeResolverBuilder _constructDefaultTypeResolverBuilder(DefaultTyp * {@link SerializerProvider} to use for serialization. */ protected DefaultSerializerProvider _serializerProvider(SerializationConfig config) { - return _serializerProvider.createInstance(config, _serializerFactory); - } - - /** - * Method called to configure the generator as necessary and then - * call write functionality - */ - protected final void _configAndWriteValue(JsonGenerator g, Object value) - throws IOException - { - SerializationConfig cfg = getSerializationConfig(); - cfg.initialize(g); // since 2.5 - if (cfg.isEnabled(SerializationFeature.CLOSE_CLOSEABLE) && (value instanceof Closeable)) { - _configAndWriteCloseable(g, value, cfg); - return; - } - try { - _serializerProvider(cfg).serializeValue(g, value); - } catch (Exception e) { - ClassUtil.closeOnFailAndThrowAsIOE(g, e); - return; - } - g.close(); - } - - /** - * Helper method used when value to serialize is {@link Closeable} and its close() - * method is to be called right after serialization has been called - */ - private final void _configAndWriteCloseable(JsonGenerator g, Object value, SerializationConfig cfg) - throws IOException - { - Closeable toClose = (Closeable) value; - try { - _serializerProvider(cfg).serializeValue(g, value); - Closeable tmpToClose = toClose; - toClose = null; - tmpToClose.close(); - } catch (Exception e) { - ClassUtil.closeOnFailAndThrowAsIOE(g, toClose, e); - return; - } - g.close(); + // 03-Oct-2017, tatu: Should be ok to pass "empty" generator settings... + return _serializationContexts.createContext(config, + GeneratorSettings.empty()); } - /** - * Helper method used when value to serialize is {@link Closeable} and its close() - * method is to be called right after serialization has been called - */ - private final void _writeCloseableValue(JsonGenerator g, Object value, SerializationConfig cfg) - throws IOException - { - Closeable toClose = (Closeable) value; - try { - _serializerProvider(cfg).serializeValue(g, value); - if (cfg.isEnabled(SerializationFeature.FLUSH_AFTER_WRITE_VALUE)) { - g.flush(); - } - } catch (Exception e) { - ClassUtil.closeOnFailAndThrowAsIOE(null, toClose, e); - return; - } - toClose.close(); + protected DefaultSerializerProvider _serializerProvider() { + // 03-Oct-2017, tatu: Should be ok to pass "empty" generator settings... + return _serializationContexts.createContext(serializationConfig(), + GeneratorSettings.empty()); } /* - /********************************************************** + /********************************************************************** /* Internal methods for deserialization, overridable - /********************************************************** + /********************************************************************** */ /** * Actual implementation of value reading+binding operation. */ - protected Object _readValue(DeserializationConfig cfg, JsonParser p, + protected Object _readValue(DeserializationContext ctxt, JsonParser p, JavaType valueType) throws IOException { @@ -3921,7 +2249,7 @@ protected Object _readValue(DeserializationConfig cfg, JsonParser p, */ Object result; JsonToken t = _initForReading(p, valueType); - final DeserializationContext ctxt = createDeserializationContext(p, cfg); + final DeserializationConfig config = ctxt.getConfig(); if (t == JsonToken.VALUE_NULL) { // Ask JsonDeserializer what 'null value' to use: result = _findRootDeserializer(ctxt, valueType).getNullValue(ctxt); @@ -3930,43 +2258,44 @@ protected Object _readValue(DeserializationConfig cfg, JsonParser p, } else { // pointing to event other than null JsonDeserializer deser = _findRootDeserializer(ctxt, valueType); // ok, let's get the value - if (cfg.useRootWrapping()) { - result = _unwrapAndDeserialize(p, ctxt, cfg, valueType, deser); + if (config.useRootWrapping()) { + result = _unwrapAndDeserialize(p, ctxt, config, valueType, deser); } else { result = deser.deserialize(p, ctxt); } } // Need to consume the token too p.clearCurrentToken(); - if (cfg.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)) { + if (config.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)) { _verifyNoTrailingTokens(p, ctxt, valueType); } return result; } - protected Object _readMapAndClose(JsonParser p0, JavaType valueType) + protected Object _readMapAndClose(DefaultDeserializationContext ctxt, + JsonParser p0, JavaType valueType) throws IOException { + ctxt.assignParser(p0); try (JsonParser p = p0) { Object result; JsonToken t = _initForReading(p, valueType); - final DeserializationConfig cfg = getDeserializationConfig(); - final DeserializationContext ctxt = createDeserializationContext(p, cfg); if (t == JsonToken.VALUE_NULL) { // Ask JsonDeserializer what 'null value' to use: result = _findRootDeserializer(ctxt, valueType).getNullValue(ctxt); } else if (t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) { result = null; } else { + final DeserializationConfig config = ctxt.getConfig(); JsonDeserializer deser = _findRootDeserializer(ctxt, valueType); - if (cfg.useRootWrapping()) { - result = _unwrapAndDeserialize(p, ctxt, cfg, valueType, deser); + if (config.useRootWrapping()) { + result = _unwrapAndDeserialize(p, ctxt, config, valueType, deser); } else { result = deser.deserialize(p, ctxt); } ctxt.checkUnresolvedObjectId(); } - if (cfg.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)) { + if (ctxt.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)) { _verifyNoTrailingTokens(p, ctxt, valueType); } return result; @@ -3974,22 +2303,18 @@ protected Object _readMapAndClose(JsonParser p0, JavaType valueType) } /** - * Similar to {@link #_readMapAndClose} but specialized for JsonNode - * reading. - * - * @since 2.9 + * Similar to {@link #_readMapAndClose} but specialized for JsonNode reading. */ - protected JsonNode _readTreeAndClose(JsonParser p0) throws IOException + protected JsonNode _readTreeAndClose(DeserializationContext ctxt, + JsonParser p0) throws IOException { try (JsonParser p = p0) { final JavaType valueType = JSON_NODE_TYPE; + DeserializationConfig cfg = deserializationConfig(); - DeserializationConfig cfg = getDeserializationConfig(); // 27-Oct-2016, tatu: Need to inline `_initForReading()` due to // special requirements by tree reading (no fail on eof) - - cfg.initialize(p); // since 2.5 - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == null) { t = p.nextToken(); if (t == null) { @@ -3998,18 +2323,11 @@ protected JsonNode _readTreeAndClose(JsonParser p0) throws IOException return cfg.getNodeFactory().missingNode(); } } - final boolean checkTrailing = cfg.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS); - DeserializationContext ctxt; JsonNode resultNode; if (t == JsonToken.VALUE_NULL) { resultNode = cfg.getNodeFactory().nullNode(); - if (!checkTrailing) { - return resultNode; - } - ctxt = createDeserializationContext(p, cfg); } else { - ctxt = createDeserializationContext(p, cfg); JsonDeserializer deser = _findRootDeserializer(ctxt, valueType); if (cfg.useRootWrapping()) { resultNode = (JsonNode) _unwrapAndDeserialize(p, ctxt, cfg, valueType, deser); @@ -4017,7 +2335,7 @@ protected JsonNode _readTreeAndClose(JsonParser p0) throws IOException resultNode = (JsonNode) deser.deserialize(p, ctxt); } } - if (checkTrailing) { + if (cfg.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)) { _verifyNoTrailingTokens(p, ctxt, valueType); } // No ObjectIds so can ignore @@ -4034,17 +2352,17 @@ protected Object _unwrapAndDeserialize(JsonParser p, DeserializationContext ctxt PropertyName expRootName = config.findRootName(rootType); // 12-Jun-2015, tatu: Should try to support namespaces etc but... String expSimpleName = expRootName.getSimpleName(); - if (p.getCurrentToken() != JsonToken.START_OBJECT) { + if (p.currentToken() != JsonToken.START_OBJECT) { ctxt.reportWrongTokenException(rootType, JsonToken.START_OBJECT, "Current token not START_OBJECT (needed to unwrap root name '%s'), but %s", - expSimpleName, p.getCurrentToken()); + expSimpleName, p.currentToken()); } if (p.nextToken() != JsonToken.FIELD_NAME) { ctxt.reportWrongTokenException(rootType, JsonToken.FIELD_NAME, "Current token not FIELD_NAME (to contain expected root name '%s'), but %s", - expSimpleName, p.getCurrentToken()); + expSimpleName, p.currentToken()); } - String actualName = p.getCurrentName(); + String actualName = p.currentName(); if (!expSimpleName.equals(actualName)) { ctxt.reportInputMismatch(rootType, "Root name '%s' does not match expected ('%s') for type %s", @@ -4057,7 +2375,7 @@ protected Object _unwrapAndDeserialize(JsonParser p, DeserializationContext ctxt if (p.nextToken() != JsonToken.END_OBJECT) { ctxt.reportWrongTokenException(rootType, JsonToken.END_OBJECT, "Current token not END_OBJECT (to match wrapper object with root name '%s'), but %s", - expSimpleName, p.getCurrentToken()); + expSimpleName, p.currentToken()); } if (config.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)) { _verifyNoTrailingTokens(p, ctxt, rootType); @@ -4070,11 +2388,24 @@ protected Object _unwrapAndDeserialize(JsonParser p, DeserializationContext ctxt * for deserializing a single root value. * Can be overridden if a custom context is needed. */ - protected DefaultDeserializationContext createDeserializationContext(JsonParser p, - DeserializationConfig cfg) { - return _deserializationContext.createInstance(cfg, p, _injectableValues); + protected DefaultDeserializationContext createDeserializationContext(JsonParser p) { + return _deserializationContexts.createContext(deserializationConfig(), + /* FormatSchema */ null, _injectableValues) + .assignParser(p); + } + + protected DefaultDeserializationContext createDeserializationContext() { + return _deserializationContexts.createContext(deserializationConfig(), + /* FormatSchema */ null, _injectableValues); } + protected DefaultDeserializationContext createDeserializationContext(DeserializationConfig config, + JsonParser p) { + return _deserializationContexts.createContext(config, + /* FormatSchema */ null, _injectableValues) + .assignParser(p); + } + /** * Method called to ensure that given parser is ready for reading * content for data binding. @@ -4092,12 +2423,10 @@ protected DefaultDeserializationContext createDeserializationContext(JsonParser */ protected JsonToken _initForReading(JsonParser p, JavaType targetType) throws IOException { - _deserializationConfig.initialize(p); // since 2.5 - // First: must point to a token; if not pointing to one, advance. // This occurs before first read from JsonParser, as well as // after clearing of current token. - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == null) { // and then we must get something... t = p.nextToken(); @@ -4111,14 +2440,6 @@ protected JsonToken _initForReading(JsonParser p, JavaType targetType) throws IO return t; } - @Deprecated // since 2.9, use method that takes JavaType too - protected JsonToken _initForReading(JsonParser p) throws IOException { - return _initForReading(p, null); - } - - /** - * @since 2.9 - */ protected final void _verifyNoTrailingTokens(JsonParser p, DeserializationContext ctxt, JavaType bindType) throws IOException @@ -4131,9 +2452,58 @@ protected final void _verifyNoTrailingTokens(JsonParser p, DeserializationContex } /* - /********************************************************** + /********************************************************************** + /* Internal factory methods for ObjectReaders/-Writers + /********************************************************************** + */ + + /** + * Factory method sub-classes must override, to produce {@link ObjectReader} + * instances of proper sub-type + */ + 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 + */ + 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 + */ + 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 + */ + 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 + */ + protected ObjectWriter _newWriter(SerializationConfig config, + JavaType rootType, PrettyPrinter pp) { + return new ObjectWriter(this, config, rootType, pp); + } + + /* + /********************************************************************** /* Internal methods, other - /********************************************************** + /********************************************************************** */ /** @@ -4158,15 +2528,12 @@ protected JsonDeserializer _findRootDeserializer(DeserializationContext return deser; } - /** - * @since 2.2 - */ protected void _verifySchemaType(FormatSchema schema) { if (schema != null) { - if (!_jsonFactory.canUseSchema(schema)) { + if (!_streamFactory.canUseSchema(schema)) { throw new IllegalArgumentException("Cannot use FormatSchema of type "+schema.getClass().getName() - +" for format "+_jsonFactory.getFormatName()); + +" for format "+_streamFactory.getFormatName()); } } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java index 9434203ed0..0693de7769 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java +++ b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java @@ -13,10 +13,12 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.cfg.ContextAttributes; -import com.fasterxml.jackson.databind.deser.DataFormatReaders; +import com.fasterxml.jackson.databind.cfg.DeserializationContexts; import com.fasterxml.jackson.databind.deser.DefaultDeserializationContext; import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler; +import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.TreeTraversingParser; import com.fasterxml.jackson.databind.type.SimpleType; import com.fasterxml.jackson.databind.type.TypeFactory; @@ -34,24 +36,21 @@ * reused, shared, cached; both because of thread-safety and because * instances are relatively light-weight. *

- * NOTE: this class is NOT meant as sub-classable (with Jackson 2.8 and - * above) by users. It is left as non-final mostly to allow frameworks - * that require bytecode generation for proxying and similar use cases, - * but there is no expecation that functionality should be extended - * by sub-classing. + * NOTE: this class is NOT meant as sub-classable by users. It is left as + * non-final mostly to allow frameworks that require bytecode generation for proxying + * and similar use cases, but there is no expectation that functionality + * should be extended by sub-classing. */ public class ObjectReader - extends ObjectCodec - implements Versioned, java.io.Serializable // since 2.1 + implements Versioned + // NOTE: since 3.x, NO LONGER JDK Serializable { - private static final long serialVersionUID = 2L; // since 2.9 - - private final static JavaType JSON_NODE_TYPE = SimpleType.constructUnsafe(JsonNode.class); + protected final static JavaType JSON_NODE_TYPE = SimpleType.constructUnsafe(JsonNode.class); /* - /********************************************************** + /********************************************************************** /* Immutable configuration from ObjectMapper - /********************************************************** + /********************************************************************** */ /** @@ -64,12 +63,12 @@ public class ObjectReader * Blueprint instance of deserialization context; used for creating * actual instance when needed. */ - protected final DefaultDeserializationContext _context; + protected final DeserializationContexts _contexts; /** - * Factory used for constructing {@link JsonGenerator}s + * Factory used for constructing {@link JsonParser}s */ - protected final JsonFactory _parserFactory; + protected final TokenStreamFactory _parserFactory; /** * Flag that indicates whether root values are expected to be unwrapped or not @@ -81,11 +80,11 @@ public class ObjectReader * Default value to be null as filter not considered. */ private final TokenFilter _filter; - + /* - /********************************************************** + /********************************************************************** /* Configuration that can be changed during building - /********************************************************** + /********************************************************************** */ /** @@ -103,8 +102,6 @@ public class ObjectReader * is known, and if so, reuse it afterwards. * This allows avoiding further deserializer lookups and increases * performance a bit on cases where readers are reused. - * - * @since 2.1 */ protected final JsonDeserializer _rootDeserializer; @@ -129,24 +126,10 @@ public class ObjectReader */ protected final InjectableValues _injectableValues; - /** - * Optional detector used for auto-detecting data format that byte-based - * input uses. - *

- * NOTE: If defined non-null, readValue() methods that take - * {@link Reader} or {@link String} input will fail with exception, - * because format-detection only works on byte-sources. Also, if format - * cannot be detect reliably (as per detector settings), - * a {@link JsonParseException} will be thrown). - * - * @since 2.1 - */ - protected final DataFormatReaders _dataFormatReaders; - /* - /********************************************************** + /********************************************************************** /* Caching - /********************************************************** + /********************************************************************** */ /** @@ -156,9 +139,9 @@ public class ObjectReader final protected ConcurrentHashMap> _rootDeserializers; /* - /********************************************************** + /********************************************************************** /* Life-cycle, construction - /********************************************************** + /********************************************************************** */ /** @@ -177,9 +160,9 @@ protected ObjectReader(ObjectMapper mapper, DeserializationConfig config, FormatSchema schema, InjectableValues injectableValues) { _config = config; - _context = mapper._deserializationContext; + _contexts = mapper._deserializationContexts; _rootDeserializers = mapper._rootDeserializers; - _parserFactory = mapper._jsonFactory; + _parserFactory = mapper._streamFactory; _valueType = valueType; _valueToUpdate = valueToUpdate; _schema = schema; @@ -187,7 +170,6 @@ protected ObjectReader(ObjectMapper mapper, DeserializationConfig config, _unwrapRoot = config.useRootWrapping(); _rootDeserializer = _prefetchRootDeserializer(valueType); - _dataFormatReaders = null; _filter = null; } @@ -196,11 +178,10 @@ protected ObjectReader(ObjectMapper mapper, DeserializationConfig config, */ protected ObjectReader(ObjectReader base, DeserializationConfig config, JavaType valueType, JsonDeserializer rootDeser, Object valueToUpdate, - FormatSchema schema, InjectableValues injectableValues, - DataFormatReaders dataFormatReaders) + FormatSchema schema, InjectableValues injectableValues) { _config = config; - _context = base._context; + _contexts = base._contexts; _rootDeserializers = base._rootDeserializers; _parserFactory = base._parserFactory; @@ -211,7 +192,6 @@ protected ObjectReader(ObjectReader base, DeserializationConfig config, _schema = schema; _injectableValues = injectableValues; _unwrapRoot = config.useRootWrapping(); - _dataFormatReaders = dataFormatReaders; _filter = base._filter; } @@ -221,7 +201,7 @@ protected ObjectReader(ObjectReader base, DeserializationConfig config, protected ObjectReader(ObjectReader base, DeserializationConfig config) { _config = config; - _context = base._context; + _contexts = base._contexts; _rootDeserializers = base._rootDeserializers; _parserFactory = base._parserFactory; @@ -232,33 +212,12 @@ protected ObjectReader(ObjectReader base, DeserializationConfig config) _schema = base._schema; _injectableValues = base._injectableValues; _unwrapRoot = config.useRootWrapping(); - _dataFormatReaders = base._dataFormatReaders; _filter = base._filter; } - - protected ObjectReader(ObjectReader base, JsonFactory f) - { - // may need to override ordering, based on data format capabilities - _config = base._config - .with(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, f.requiresPropertyOrdering()); - _context = base._context; - _rootDeserializers = base._rootDeserializers; - _parserFactory = f; - - _valueType = base._valueType; - _rootDeserializer = base._rootDeserializer; - _valueToUpdate = base._valueToUpdate; - _schema = base._schema; - _injectableValues = base._injectableValues; - _unwrapRoot = base._unwrapRoot; - _dataFormatReaders = base._dataFormatReaders; - _filter = base._filter; - } - protected ObjectReader(ObjectReader base, TokenFilter filter) { _config = base._config; - _context = base._context; + _contexts = base._contexts; _rootDeserializers = base._rootDeserializers; _parserFactory = base._parserFactory; _valueType = base._valueType; @@ -267,7 +226,6 @@ protected ObjectReader(ObjectReader base, TokenFilter filter) { _schema = base._schema; _injectableValues = base._injectableValues; _unwrapRoot = base._unwrapRoot; - _dataFormatReaders = base._dataFormatReaders; _filter = filter; } @@ -281,49 +239,33 @@ public Version version() { } /* - /********************************************************** + /********************************************************************** /* Helper methods used internally for invoking constructors /* Need to be overridden if sub-classing (not recommended) /* is used. - /********************************************************** + /********************************************************************** */ /** - * 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 + * Factory method called by various "withXxx()" methods */ protected ObjectReader _new(ObjectReader base, DeserializationConfig config) { return new ObjectReader(base, config); } /** - * Overridable factory method called by various "withXxx()" methods - * - * @since 2.5 + * Factory method called by various "withXxx()" methods */ protected ObjectReader _new(ObjectReader base, DeserializationConfig config, JavaType valueType, JsonDeserializer rootDeser, Object valueToUpdate, - FormatSchema schema, InjectableValues injectableValues, - DataFormatReaders dataFormatReaders) { + FormatSchema schema, InjectableValues injectableValues) { return new ObjectReader(base, config, valueType, rootDeser, valueToUpdate, - schema, injectableValues, dataFormatReaders); + schema, injectableValues); } /** * Factory method used to create {@link MappingIterator} instances; * either default, or custom subtype. - * - * @since 2.5 */ protected MappingIterator _newIterator(JsonParser p, DeserializationContext ctxt, JsonDeserializer deser, boolean parserManaged) @@ -333,24 +275,20 @@ protected MappingIterator _newIterator(JsonParser p, DeserializationConte } /* - /********************************************************** + /********************************************************************** /* Methods for initializing parser instance to use - /********************************************************** + /********************************************************************** */ - protected JsonToken _initForReading(DeserializationContext ctxt, JsonParser p) + protected JsonToken _initForReading(DefaultDeserializationContext ctxt, JsonParser p) throws IOException { - if (_schema != null) { - p.setSchema(_schema); - } - _config.initialize(p); // since 2.5 + ctxt.assignParser(p); - /* 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(); + // 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.currentToken(); if (t == null) { // and then we must get something... t = p.nextToken(); if (t == null) { @@ -368,22 +306,17 @@ protected JsonToken _initForReading(DeserializationContext ctxt, JsonParser p) * but need to do other initialization. *

* Base implementation only sets configured {@link FormatSchema}, if any, on parser. - * - * @since 2.8 */ - protected void _initForMultiRead(DeserializationContext ctxt, JsonParser p) + protected void _initForMultiRead(DefaultDeserializationContext ctxt, JsonParser p) throws IOException { - if (_schema != null) { - p.setSchema(_schema); - } - _config.initialize(p); + ctxt.assignParser(p); } /* - /********************************************************** + /********************************************************************** /* Life-cycle, fluent factory methods for DeserializationFeatures - /********************************************************** + /********************************************************************** */ /** @@ -438,16 +371,16 @@ public ObjectReader withoutFeatures(DeserializationFeature... features) { } /* - /********************************************************** + /********************************************************************** /* Life-cycle, fluent factory methods for JsonParser.Features - /********************************************************** + /********************************************************************** */ /** * Method for constructing a new reader instance that is configured * with specified feature enabled. */ - public ObjectReader with(JsonParser.Feature feature) { + public ObjectReader with(StreamReadFeature feature) { return _with(_config.with(feature)); } @@ -455,7 +388,7 @@ public ObjectReader with(JsonParser.Feature feature) { * Method for constructing a new reader instance that is configured * with specified features enabled. */ - public ObjectReader withFeatures(JsonParser.Feature... features) { + public ObjectReader withFeatures(StreamReadFeature... features) { return _with(_config.withFeatures(features)); } @@ -463,7 +396,7 @@ public ObjectReader withFeatures(JsonParser.Feature... features) { * Method for constructing a new reader instance that is configured * with specified feature disabled. */ - public ObjectReader without(JsonParser.Feature feature) { + public ObjectReader without(StreamReadFeature feature) { return _with(_config.without(feature)); } @@ -471,21 +404,19 @@ public ObjectReader without(JsonParser.Feature feature) { * Method for constructing a new reader instance that is configured * with specified features disabled. */ - public ObjectReader withoutFeatures(JsonParser.Feature... features) { + public ObjectReader withoutFeatures(StreamReadFeature... features) { return _with(_config.withoutFeatures(features)); } /* - /********************************************************** - /* Life-cycle, fluent factory methods for FormatFeature (2.7) - /********************************************************** + /********************************************************************** + /* Life-cycle, fluent factory methods for FormatFeature + /********************************************************************** */ /** * Method for constructing a new reader instance that is configured * with specified feature enabled. - * - * @since 2.7 */ public ObjectReader with(FormatFeature feature) { return _with(_config.with(feature)); @@ -494,8 +425,6 @@ public ObjectReader with(FormatFeature feature) { /** * Method for constructing a new reader instance that is configured * with specified features enabled. - * - * @since 2.7 */ public ObjectReader withFeatures(FormatFeature... features) { return _with(_config.withFeatures(features)); @@ -504,8 +433,6 @@ public ObjectReader withFeatures(FormatFeature... features) { /** * Method for constructing a new reader instance that is configured * with specified feature disabled. - * - * @since 2.7 */ public ObjectReader without(FormatFeature feature) { return _with(_config.without(feature)); @@ -514,34 +441,30 @@ public ObjectReader without(FormatFeature feature) { /** * Method for constructing a new reader instance that is configured * with specified features disabled. - * - * @since 2.7 */ public ObjectReader withoutFeatures(FormatFeature... features) { return _with(_config.withoutFeatures(features)); } /* - /********************************************************** + /********************************************************************** /* Life-cycle, fluent factory methods, other - /********************************************************** + /********************************************************************** */ /** * Convenience method to bind from {@link JsonPointer}. * {@link JsonPointerBasedFilter} is registered and will be used for parsing later. - * @since 2.6 */ - public ObjectReader at(final String value) { + public ObjectReader at(String value) { return new ObjectReader(this, new JsonPointerBasedFilter(value)); } /** * Convenience method to bind from {@link JsonPointer} * {@link JsonPointerBasedFilter} is registered and will be used for parsing later. - * @since 2.6 */ - public ObjectReader at(final JsonPointer pointer) { + public ObjectReader at(JsonPointer pointer) { return new ObjectReader(this, new JsonPointerBasedFilter(pointer)); } @@ -570,7 +493,7 @@ public ObjectReader with(InjectableValues injectableValues) } return _new(this, _config, _valueType, _rootDeserializer, _valueToUpdate, - _schema, injectableValues, _dataFormatReaders); + _schema, injectableValues); } /** @@ -585,29 +508,6 @@ public ObjectReader with(JsonNodeFactory f) { return _with(_config.with(f)); } - /** - * Method for constructing a new reader instance with configuration that uses - * passed {@link JsonFactory} for constructing underlying Readers. - *

- * NOTE: only factories that DO NOT REQUIRE SPECIAL MAPPERS - * (that is, ones that return false for - * {@link JsonFactory#requiresCustomCodec()}) can be used: trying - * to use one that requires custom codec will throw exception - * - * @since 2.1 - */ - public ObjectReader with(JsonFactory f) { - if (f == _parserFactory) { - return this; - } - ObjectReader r = _new(this, f); - // Also, try re-linking, if possible... - if (f.getCodec() == null) { - f.setCodec(r); - } - return r; - } - /** * Method for constructing a new instance with configuration that * specifies what root name to expect for "root name unwrapping". @@ -621,9 +521,6 @@ public ObjectReader withRootName(String rootName) { return _with(_config.withRootName(rootName)); } - /** - * @since 2.6 - */ public ObjectReader withRootName(PropertyName rootName) { return _with(_config.withRootName(rootName)); } @@ -635,8 +532,6 @@ public ObjectReader withRootName(PropertyName rootName) { * * which will forcibly prevent use of root name wrapping when writing * values with this {@link ObjectReader}. - * - * @since 2.6 */ public ObjectReader withoutRootName() { return _with(_config.withRootName(PropertyName.NO_NAME)); @@ -657,7 +552,7 @@ public ObjectReader with(FormatSchema schema) } _verifySchemaType(schema); return _new(this, _config, _valueType, _rootDeserializer, _valueToUpdate, - schema, _injectableValues, _dataFormatReaders); + schema, _injectableValues); } /** @@ -666,8 +561,6 @@ public ObjectReader with(FormatSchema schema) *

* Note that the method does NOT change state of this reader, but * rather construct and returns a newly configured instance. - * - * @since 2.5 */ public ObjectReader forType(JavaType valueType) { @@ -675,13 +568,8 @@ public ObjectReader forType(JavaType valueType) return this; } JsonDeserializer rootDeser = _prefetchRootDeserializer(valueType); - // type is stored here, no need to make a copy of config - DataFormatReaders det = _dataFormatReaders; - if (det != null) { - det = det.withType(valueType); - } return _new(this, _config, valueType, rootDeser, - _valueToUpdate, _schema, _injectableValues, det); + _valueToUpdate, _schema, _injectableValues); } /** @@ -690,8 +578,6 @@ public ObjectReader forType(JavaType valueType) *

* Note that the method does NOT change state of this reader, but * rather construct and returns a newly configured instance. - * - * @since 2.5 */ public ObjectReader forType(Class valueType) { return forType(_config.constructType(valueType)); @@ -703,45 +589,11 @@ public ObjectReader forType(Class valueType) { *

* Note that the method does NOT change state of this reader, but * rather construct and returns a newly configured instance. - * - * @since 2.5 */ public ObjectReader forType(TypeReference valueTypeRef) { return forType(_config.getTypeFactory().constructType(valueTypeRef.getType())); - } - - /** - * @deprecated since 2.5 Use {@link #forType(JavaType)} instead - */ - @Deprecated - public ObjectReader withType(JavaType valueType) { - return forType(valueType); - } - - /** - * @deprecated since 2.5 Use {@link #forType(Class)} instead - */ - @Deprecated - public ObjectReader withType(Class valueType) { - return forType(_config.constructType(valueType)); - } - - /** - * @deprecated since 2.5 Use {@link #forType(Class)} instead - */ - @Deprecated - public ObjectReader withType(java.lang.reflect.Type valueType) { - return forType(_config.getTypeFactory().constructType(valueType)); } - /** - * @deprecated since 2.5 Use {@link #forType(TypeReference)} instead - */ - @Deprecated - public ObjectReader withType(TypeReference valueTypeRef) { - return forType(_config.getTypeFactory().constructType(valueTypeRef.getType())); - } - /** * Method for constructing a new instance with configuration that * updates passed Object (as root value), instead of constructing @@ -757,7 +609,7 @@ public ObjectReader withValueToUpdate(Object value) // 18-Oct-2016, tatu: Actually, should be allowed, to remove value // to update, if any return _new(this, _config, _valueType, _rootDeserializer, null, - _schema, _injectableValues, _dataFormatReaders); + _schema, _injectableValues); } JavaType t; @@ -771,7 +623,7 @@ public ObjectReader withValueToUpdate(Object value) t = _valueType; } return _new(this, _config, t, _rootDeserializer, value, - _schema, _injectableValues, _dataFormatReaders); + _schema, _injectableValues); } /** @@ -801,101 +653,39 @@ public ObjectReader with(Base64Variant defaultBase64) { return _with(_config.with(defaultBase64)); } - /** - * Fluent factory method for constructing a reader that will try to - * auto-detect underlying data format, using specified list of - * {@link JsonFactory} instances, and default {@link DataFormatReaders} settings - * (for customized {@link DataFormatReaders}, you can construct instance yourself). - * to construct appropriate {@link JsonParser} for actual parsing. - *

- * Note: since format detection only works with byte sources, it is possible to - * get a failure from some 'readValue()' methods. Also, if input cannot be reliably - * (enough) detected as one of specified types, an exception will be thrown. - *

- * Note: not all {@link JsonFactory} types can be passed: specifically, ones that - * require "custom codec" (like XML factory) will not work. Instead, use - * method that takes {@link ObjectReader} instances instead of factories. - * - * @param readers Data formats accepted, in decreasing order of priority (that is, - * matches checked in listed order, first match wins) - * - * @return Newly configured writer instance - * - * @since 2.1 - */ - public ObjectReader withFormatDetection(ObjectReader... readers) { - return withFormatDetection(new DataFormatReaders(readers)); - } - - /** - * Fluent factory method for constructing a reader that will try to - * auto-detect underlying data format, using specified - * {@link DataFormatReaders}. - *

- * NOTE: since format detection only works with byte sources, it is possible to - * get a failure from some 'readValue()' methods. Also, if input cannot be reliably - * (enough) detected as one of specified types, an exception will be thrown. - * - * @param readers DataFormatReaders to use for detecting underlying format. - * - * @return Newly configured writer instance - * - * @since 2.1 - */ - public ObjectReader withFormatDetection(DataFormatReaders readers) { - return _new(this, _config, _valueType, _rootDeserializer, _valueToUpdate, - _schema, _injectableValues, readers); - } - - /** - * @since 2.3 - */ public ObjectReader with(ContextAttributes attrs) { return _with(_config.with(attrs)); } - /** - * @since 2.3 - */ public ObjectReader withAttributes(Map attrs) { return _with(_config.withAttributes(attrs)); } - /** - * @since 2.3 - */ public ObjectReader withAttribute(Object key, Object value) { return _with( _config.withAttribute(key, value)); } - /** - * @since 2.3 - */ public ObjectReader withoutAttribute(Object key) { return _with(_config.withoutAttribute(key)); } /* - /********************************************************** - /* Overridable factory methods may override - /********************************************************** + /********************************************************************** + /* Internal factory methods + /********************************************************************** */ - protected ObjectReader _with(DeserializationConfig newConfig) { + protected final 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; + return _new(this, newConfig); } - + /* - /********************************************************** + /********************************************************************** /* Simple accessors - /********************************************************** + /********************************************************************** */ public boolean isEnabled(DeserializationFeature f) { @@ -906,48 +696,212 @@ public boolean isEnabled(MapperFeature f) { return _config.isEnabled(f); } - public boolean isEnabled(JsonParser.Feature f) { - return _parserFactory.isEnabled(f); + public boolean isEnabled(StreamReadFeature f) { + return _config.isEnabled(f); } - /** - * @since 2.2 - */ public DeserializationConfig getConfig() { return _config; } - + /** - * @since 2.1 + * @since 3.0 */ - @Override - public JsonFactory getFactory() { + public TokenStreamFactory parserFactory() { return _parserFactory; } - public TypeFactory getTypeFactory() { + /** + * @since 3.0 + */ + public TypeFactory typeFactory() { return _config.getTypeFactory(); } - /** - * @since 2.3 - */ public ContextAttributes getAttributes() { return _config.getAttributes(); } - /** - * @since 2.6 - */ public InjectableValues getInjectableValues() { return _injectableValues; } + /** + * @deprecated Since 3.0 use {@link #typeFactory} + */ + @Deprecated + public TypeFactory getTypeFactory() { + return typeFactory(); + } + /* - /********************************************************** - /* Deserialization methods; basic ones to support ObjectCodec first - /* (ones that take JsonParser) - /********************************************************** + /********************************************************************** + /* Public API: constructing Parsers that are properly linked + /* to `ObjectReadContext` + /********************************************************************** + */ + + /** + * Factory method for constructing {@link JsonParser} that is properly + * wired to allow callbacks for deserialization: basically + * constructs a {@link ObjectReadContext} and then calls + * {@link TokenStreamFactory#createParser(ObjectReadContext,java.io.File)}. + * + * @since 3.0 + */ + public JsonParser createParser(File src) throws IOException { + DefaultDeserializationContext ctxt = createDeserializationContext(); + return ctxt.assignAndReturnParser(_parserFactory.createParser(ctxt, src)); + } + + /** + * Factory method for constructing {@link JsonParser} that is properly + * wired to allow callbacks for deserialization: basically + * constructs a {@link ObjectReadContext} and then calls + * {@link TokenStreamFactory#createParser(ObjectReadContext,java.net.URL)}. + * + * @since 3.0 + */ + public JsonParser createParser(URL src) throws IOException { + DefaultDeserializationContext ctxt = createDeserializationContext(); + return ctxt.assignAndReturnParser(_parserFactory.createParser(ctxt, src)); + } + + /** + * Factory method for constructing {@link JsonParser} that is properly + * wired to allow callbacks for deserialization: basically + * constructs a {@link ObjectReadContext} and then calls + * {@link TokenStreamFactory#createParser(ObjectReadContext,InputStream)}. + * + * @since 3.0 + */ + public JsonParser createParser(InputStream in) throws IOException { + DefaultDeserializationContext ctxt = createDeserializationContext(); + return ctxt.assignAndReturnParser(_parserFactory.createParser(ctxt, in)); + } + + /** + * Factory method for constructing {@link JsonParser} that is properly + * wired to allow callbacks for deserialization: basically + * constructs a {@link ObjectReadContext} and then calls + * {@link TokenStreamFactory#createParser(ObjectReadContext,Reader)}. + * + * @since 3.0 + */ + public JsonParser createParser(Reader r) throws IOException { + DefaultDeserializationContext ctxt = createDeserializationContext(); + return ctxt.assignAndReturnParser(_parserFactory.createParser(ctxt, r)); + } + + /** + * Factory method for constructing {@link JsonParser} that is properly + * wired to allow callbacks for deserialization: basically + * constructs a {@link ObjectReadContext} and then calls + * {@link TokenStreamFactory#createParser(ObjectReadContext,byte[])}. + * + * @since 3.0 + */ + public JsonParser createParser(byte[] data) throws IOException { + DefaultDeserializationContext ctxt = createDeserializationContext(); + return ctxt.assignAndReturnParser(_parserFactory.createParser(ctxt, data)); + } + + /** + * Factory method for constructing {@link JsonParser} that is properly + * wired to allow callbacks for deserialization: basically + * constructs a {@link ObjectReadContext} and then calls + * {@link TokenStreamFactory#createParser(ObjectReadContext,byte[],int,int)}. + * + * @since 3.0 + */ + public JsonParser createParser(byte[] data, int offset, int len) throws IOException { + DefaultDeserializationContext ctxt = createDeserializationContext(); + return ctxt.assignAndReturnParser(_parserFactory.createParser(ctxt, data, offset, len)); + } + + /** + * Factory method for constructing {@link JsonParser} that is properly + * wired to allow callbacks for deserialization: basically + * constructs a {@link ObjectReadContext} and then calls + * {@link TokenStreamFactory#createParser(ObjectReadContext,String)}. + * + * @since 3.0 + */ + public JsonParser createParser(String content) throws IOException { + DefaultDeserializationContext ctxt = createDeserializationContext(); + return ctxt.assignAndReturnParser(_parserFactory.createParser(ctxt, content)); + } + + /** + * Factory method for constructing {@link JsonParser} that is properly + * wired to allow callbacks for deserialization: basically + * constructs a {@link ObjectReadContext} and then calls + * {@link TokenStreamFactory#createParser(ObjectReadContext,char[])}. + * + * @since 3.0 + */ + public JsonParser createParser(char[] content) throws IOException { + DefaultDeserializationContext ctxt = createDeserializationContext(); + return ctxt.assignAndReturnParser(_parserFactory.createParser(ctxt, content)); + } + + /** + * Factory method for constructing {@link JsonParser} that is properly + * wired to allow callbacks for deserialization: basically + * constructs a {@link ObjectReadContext} and then calls + * {@link TokenStreamFactory#createParser(ObjectReadContext,char[],int,int)}. + * + * @since 3.0 + */ + public JsonParser createParser(char[] content, int offset, int len) throws IOException { + DefaultDeserializationContext ctxt = createDeserializationContext(); + return ctxt.assignAndReturnParser(_parserFactory.createParser(ctxt, content, offset, len)); + } + + /** + * Factory method for constructing {@link JsonParser} that is properly + * wired to allow callbacks for deserialization: basically + * constructs a {@link ObjectReadContext} and then calls + * {@link TokenStreamFactory#createParser(ObjectReadContext,DataInput)}. + * + * @since 3.0 + */ + public JsonParser createParser(DataInput content) throws IOException { + DefaultDeserializationContext ctxt = createDeserializationContext(); + return ctxt.assignAndReturnParser(_parserFactory.createParser(ctxt, content)); + } + + /** + * Factory method for constructing non-blocking {@link JsonParser} that is properly + * wired to allow configuration access (and, if relevant for parser, callbacks): + * essentially constructs a {@link ObjectReadContext} and then calls + * {@link TokenStreamFactory#createParser(ObjectReadContext,DataInput)}. + * + * @since 3.0 + */ + public JsonParser createNonBlockingByteArrayParser() throws IOException { + DefaultDeserializationContext ctxt = createDeserializationContext(); + return ctxt.assignAndReturnParser(_parserFactory.createNonBlockingByteArrayParser(ctxt)); + } + + /* + /********************************************************************** + /* Convenience methods for JsonNode creation + /********************************************************************** + */ + + public ObjectNode createObjectNode() { + return _config.getNodeFactory().objectNode(); + } + + public ArrayNode createArrayNode() { + return _config.getNodeFactory().arrayNode(); + } + + /* + /********************************************************************** + /* Deserialization methods; first ones for pre-constructed parsers + /********************************************************************** */ /** @@ -960,9 +914,9 @@ public InjectableValues getInjectableValues() { * (data-format specific) parser is given. */ @SuppressWarnings("unchecked") - public T readValue(JsonParser p) throws IOException - { - return (T) _bind(p, _valueToUpdate); + public T readValue(JsonParser p) throws IOException { + DefaultDeserializationContext ctxt = createDeserializationContext(p); + return (T) _bind(ctxt, p, _valueToUpdate); } /** @@ -975,11 +929,8 @@ public T readValue(JsonParser p) throws IOException * NOTE: this method never tries to auto-detect format, since actual * (data-format specific) parser is given. */ - @SuppressWarnings("unchecked") - @Override - public T readValue(JsonParser p, Class valueType) throws IOException - { - return (T) forType(valueType).readValue(p); + public T readValue(JsonParser p, Class valueType) throws IOException { + return forType(valueType).readValue(p); } /** @@ -992,11 +943,8 @@ public T readValue(JsonParser p, Class valueType) throws IOException * NOTE: this method never tries to auto-detect format, since actual * (data-format specific) parser is given. */ - @SuppressWarnings("unchecked") - @Override - public T readValue(JsonParser p, TypeReference valueTypeRef) throws IOException - { - return (T) forType(valueTypeRef).readValue(p); + public T readValue(JsonParser p, TypeReference valueTypeRef) throws IOException { + return forType(valueTypeRef).readValue(p); } /** @@ -1009,7 +957,6 @@ public T readValue(JsonParser p, TypeReference valueTypeRef) throws IOExc * NOTE: this method never tries to auto-detect format, since actual * (data-format specific) parser is given. */ - @Override @SuppressWarnings("unchecked") public T readValue(JsonParser p, ResolvedType valueType) throws IOException { return (T) forType((JavaType)valueType).readValue(p); @@ -1045,7 +992,6 @@ public T readValue(JsonParser p, JavaType valueType) throws IOException { * NOTE: this method never tries to auto-detect format, since actual * (data-format specific) parser is given. */ - @Override public Iterator readValues(JsonParser p, Class valueType) throws IOException { return forType(valueType).readValues(p); } @@ -1069,7 +1015,6 @@ public Iterator readValues(JsonParser p, Class valueType) throws IOExc * NOTE: this method never tries to auto-detect format, since actual * (data-format specific) parser is given. */ - @Override public Iterator readValues(JsonParser p, TypeReference valueTypeRef) throws IOException { return forType(valueTypeRef).readValues(p); } @@ -1093,7 +1038,6 @@ public Iterator readValues(JsonParser p, TypeReference valueTypeRef) t * NOTE: this method never tries to auto-detect format, since actual * (data-format specific) parser is given. */ - @Override public Iterator readValues(JsonParser p, ResolvedType valueType) throws IOException { return readValues(p, (JavaType) valueType); } @@ -1122,27 +1066,17 @@ public Iterator readValues(JsonParser p, JavaType valueType) throws IOExc } /* - /********************************************************** + /********************************************************************** /* TreeCodec impl - /********************************************************** + /********************************************************************** */ - @Override - public JsonNode createArrayNode() { - return _config.getNodeFactory().arrayNode(); + public JsonParser treeAsTokens(TreeNode n) { + return treeAsTokens((JsonNode) n, createDeserializationContext()); } - @Override - public JsonNode createObjectNode() { - return _config.getNodeFactory().objectNode(); - } - - @Override - public JsonParser treeAsTokens(TreeNode n) { - // 05-Dec-2017, tatu: Important! Must clear "valueToUpdate" since we do not - // want update to be applied here, as a side effect - ObjectReader codec = withValueToUpdate(null); - return new TreeTraversingParser((JsonNode) n, codec); + protected JsonParser treeAsTokens(JsonNode n, DeserializationContext ctxt) { + return new TreeTraversingParser(n, ctxt); } /** @@ -1164,22 +1098,16 @@ public JsonParser treeAsTokens(TreeNode n) { * (data-format specific) parser is given. */ @SuppressWarnings("unchecked") - @Override public T readTree(JsonParser p) throws IOException { - return (T) _bindAsTreeOrNull(p); - } - - @Override - public void writeTree(JsonGenerator g, TreeNode rootNode) { - throw new UnsupportedOperationException(); + return (T) _bindAsTreeOrNull(createDeserializationContext(p), p); } /* - /********************************************************** + /********************************************************************** /* Deserialization methods; others similar to what ObjectMapper has - /********************************************************** + /********************************************************************** */ - + /** * Method that binds content read from given input source, * using configuration of this reader. @@ -1189,10 +1117,9 @@ public void writeTree(JsonGenerator g, TreeNode rootNode) { @SuppressWarnings("unchecked") public T readValue(InputStream src) throws IOException { - if (_dataFormatReaders != null) { - return (T) _detectBindAndClose(_dataFormatReaders.findFormat(src), false); - } - return (T) _bindAndClose(_considerFilter(_parserFactory.createParser(src), false)); + DefaultDeserializationContext ctxt = createDeserializationContext(); + return (T) _bindAndClose(ctxt, + _considerFilter(_parserFactory.createParser(ctxt, src), false)); } /** @@ -1204,10 +1131,9 @@ public T readValue(InputStream src) throws IOException @SuppressWarnings("unchecked") public T readValue(Reader src) throws IOException { - if (_dataFormatReaders != null) { - _reportUndetectableSource(src); - } - return (T) _bindAndClose(_considerFilter(_parserFactory.createParser(src), false)); + DefaultDeserializationContext ctxt = createDeserializationContext(); + return (T) _bindAndClose(ctxt, + _considerFilter(_parserFactory.createParser(ctxt, src), false)); } /** @@ -1219,11 +1145,9 @@ public T readValue(Reader src) throws IOException @SuppressWarnings("unchecked") public T readValue(String src) throws IOException { - if (_dataFormatReaders != null) { - _reportUndetectableSource(src); - } - - return (T) _bindAndClose(_considerFilter(_parserFactory.createParser(src), false)); + DefaultDeserializationContext ctxt = createDeserializationContext(); + return (T) _bindAndClose(ctxt, + _considerFilter(_parserFactory.createParser(ctxt, src), false)); } /** @@ -1235,10 +1159,9 @@ public T readValue(String src) throws IOException @SuppressWarnings("unchecked") public T readValue(byte[] src) throws IOException { - if (_dataFormatReaders != null) { - return (T) _detectBindAndClose(src, 0, src.length); - } - return (T) _bindAndClose(_considerFilter(_parserFactory.createParser(src), false)); + DefaultDeserializationContext ctxt = createDeserializationContext(); + return (T) _bindAndClose(ctxt, + _considerFilter(_parserFactory.createParser(ctxt, src), false)); } /** @@ -1248,25 +1171,19 @@ public T readValue(byte[] src) throws IOException * was specified with {@link #withValueToUpdate(Object)}. */ @SuppressWarnings("unchecked") - public T readValue(byte[] src, int offset, int length) - throws IOException + public T readValue(byte[] src, int offset, int length) throws IOException { - if (_dataFormatReaders != null) { - return (T) _detectBindAndClose(src, offset, length); - } - return (T) _bindAndClose(_considerFilter(_parserFactory.createParser(src, offset, length), - false)); + DefaultDeserializationContext ctxt = createDeserializationContext(); + return (T) _bindAndClose(ctxt, + _considerFilter(_parserFactory.createParser(ctxt, src, offset, length), false)); } @SuppressWarnings("unchecked") - public T readValue(File src) - throws IOException + public T readValue(File src) throws IOException { - if (_dataFormatReaders != null) { - return (T) _detectBindAndClose(_dataFormatReaders.findFormat(_inputStream(src)), true); - } - - return (T) _bindAndClose(_considerFilter(_parserFactory.createParser(src), false)); + DefaultDeserializationContext ctxt = createDeserializationContext(); + return (T) _bindAndClose(ctxt, + _considerFilter(_parserFactory.createParser(ctxt, src), false)); } /** @@ -1276,13 +1193,11 @@ public T readValue(File src) * was specified with {@link #withValueToUpdate(Object)}. */ @SuppressWarnings("unchecked") - public T readValue(URL src) - throws IOException + public T readValue(URL src) throws IOException { - if (_dataFormatReaders != null) { - return (T) _detectBindAndClose(_dataFormatReaders.findFormat(_inputStream(src)), true); - } - return (T) _bindAndClose(_considerFilter(_parserFactory.createParser(src), false)); + DefaultDeserializationContext ctxt = createDeserializationContext(); + return (T) _bindAndClose(ctxt, + _considerFilter(_parserFactory.createParser(ctxt, src), false)); } /** @@ -1292,26 +1207,20 @@ public T readValue(URL src) * objectReader.readValue(src.traverse()) * */ - @SuppressWarnings({ "unchecked", "resource" }) - public T readValue(JsonNode src) - throws IOException + @SuppressWarnings("unchecked") + public T readValue(JsonNode src) throws IOException { - if (_dataFormatReaders != null) { - _reportUndetectableSource(src); - } - return (T) _bindAndClose(_considerFilter(treeAsTokens(src), false)); + DefaultDeserializationContext ctxt = createDeserializationContext(); + return (T) _bindAndClose(ctxt, + _considerFilter(treeAsTokens(src, ctxt), false)); } - /** - * @since 2.8 - */ @SuppressWarnings("unchecked") public T readValue(DataInput src) throws IOException { - if (_dataFormatReaders != null) { - _reportUndetectableSource(src); - } - return (T) _bindAndClose(_considerFilter(_parserFactory.createParser(src), false)); + DefaultDeserializationContext ctxt = createDeserializationContext(); + return (T) _bindAndClose(ctxt, + _considerFilter(_parserFactory.createParser(ctxt, src), false)); } /* @@ -1339,10 +1248,9 @@ public T readValue(DataInput src) throws IOException */ public JsonNode readTree(InputStream in) throws IOException { - if (_dataFormatReaders != null) { - return _detectBindAndCloseAsTree(in); - } - return _bindAndCloseAsTree(_considerFilter(_parserFactory.createParser(in), false)); + DefaultDeserializationContext ctxt = createDeserializationContext(); + return _bindAndCloseAsTree(ctxt, + _considerFilter(_parserFactory.createParser(ctxt, in), false)); } /** @@ -1351,10 +1259,9 @@ public JsonNode readTree(InputStream in) throws IOException */ public JsonNode readTree(Reader r) throws IOException { - if (_dataFormatReaders != null) { - _reportUndetectableSource(r); - } - return _bindAndCloseAsTree(_considerFilter(_parserFactory.createParser(r), false)); + DefaultDeserializationContext ctxt = createDeserializationContext(); + return _bindAndCloseAsTree(ctxt, + _considerFilter(_parserFactory.createParser(ctxt, r), false)); } /** @@ -1363,10 +1270,9 @@ public JsonNode readTree(Reader r) throws IOException */ public JsonNode readTree(String json) throws IOException { - if (_dataFormatReaders != null) { - _reportUndetectableSource(json); - } - return _bindAndCloseAsTree(_considerFilter(_parserFactory.createParser(json), false)); + DefaultDeserializationContext ctxt = createDeserializationContext(); + return _bindAndCloseAsTree(ctxt, + _considerFilter(_parserFactory.createParser(ctxt, json), false)); } /** @@ -1375,22 +1281,20 @@ public JsonNode readTree(String json) throws IOException */ public JsonNode readTree(byte[] json) throws IOException { - if (_dataFormatReaders != null) { - _reportUndetectableSource(json); - } - return _bindAndCloseAsTree(_considerFilter(_parserFactory.createParser(json), false)); + DefaultDeserializationContext ctxt = createDeserializationContext(); + return _bindAndCloseAsTree(ctxt, + _considerFilter(_parserFactory.createParser(ctxt, json), false)); } - + /** * Same as {@link #readTree(InputStream)} except content read from * passed-in byte array. */ public JsonNode readTree(byte[] json, int offset, int len) throws IOException { - if (_dataFormatReaders != null) { - _reportUndetectableSource(json); - } - return _bindAndCloseAsTree(_considerFilter(_parserFactory.createParser(json, offset, len), false)); + DefaultDeserializationContext ctxt = createDeserializationContext(); + return _bindAndCloseAsTree(ctxt, + _considerFilter(_parserFactory.createParser(ctxt, json, offset, len), false)); } /** @@ -1399,16 +1303,15 @@ public JsonNode readTree(byte[] json, int offset, int len) throws IOException */ public JsonNode readTree(DataInput src) throws IOException { - if (_dataFormatReaders != null) { - _reportUndetectableSource(src); - } - return _bindAndCloseAsTree(_considerFilter(_parserFactory.createParser(src), false)); + DefaultDeserializationContext ctxt = createDeserializationContext(); + return _bindAndCloseAsTree(ctxt, + _considerFilter(_parserFactory.createParser(ctxt, src), false)); } /* - /********************************************************** + /********************************************************************** /* Deserialization methods; reading sequence of values - /********************************************************** + /********************************************************************** */ /** @@ -1450,28 +1353,21 @@ public MappingIterator readValues(JsonParser p) * points to the first token of the first element (i.e. the second * START_ARRAY which is part of the first element). */ - public MappingIterator readValues(InputStream src) - throws IOException + public MappingIterator readValues(InputStream src) throws IOException { - if (_dataFormatReaders != null) { - return _detectBindAndReadValues(_dataFormatReaders.findFormat(src), false); - } - - return _bindAndReadValues(_considerFilter(_parserFactory.createParser(src), true)); + DefaultDeserializationContext ctxt = createDeserializationContext(); + return _bindAndReadValues(ctxt, + _considerFilter(_parserFactory.createParser(ctxt, src), true)); } /** * Overloaded version of {@link #readValue(InputStream)}. */ @SuppressWarnings("resource") - public MappingIterator readValues(Reader src) - throws IOException + public MappingIterator readValues(Reader src) throws IOException { - if (_dataFormatReaders != null) { - _reportUndetectableSource(src); - } - JsonParser p = _considerFilter(_parserFactory.createParser(src), true); - DeserializationContext ctxt = createDeserializationContext(p); + DefaultDeserializationContext ctxt = createDeserializationContext(); + JsonParser p = _considerFilter(_parserFactory.createParser(ctxt, src), true); _initForMultiRead(ctxt, p); p.nextToken(); return _newIterator(p, ctxt, _findRootDeserializer(ctxt), true); @@ -1483,14 +1379,10 @@ public MappingIterator readValues(Reader src) * @param json String that contains JSON content to parse */ @SuppressWarnings("resource") - public MappingIterator readValues(String json) - throws IOException + public MappingIterator readValues(String json) throws IOException { - if (_dataFormatReaders != null) { - _reportUndetectableSource(json); - } - JsonParser p = _considerFilter(_parserFactory.createParser(json), true); - DeserializationContext ctxt = createDeserializationContext(p); + DefaultDeserializationContext ctxt = createDeserializationContext(); + JsonParser p = _considerFilter(_parserFactory.createParser(ctxt, json), true); _initForMultiRead(ctxt, p); p.nextToken(); return _newIterator(p, ctxt, _findRootDeserializer(ctxt), true); @@ -1499,35 +1391,28 @@ public MappingIterator readValues(String json) /** * Overloaded version of {@link #readValue(InputStream)}. */ - public MappingIterator readValues(byte[] src, int offset, int length) - throws IOException + public MappingIterator readValues(byte[] src, int offset, int length) throws IOException { - if (_dataFormatReaders != null) { - return _detectBindAndReadValues(_dataFormatReaders.findFormat(src, offset, length), false); - } - return _bindAndReadValues(_considerFilter(_parserFactory.createParser(src, offset, length), - true)); + DefaultDeserializationContext ctxt = createDeserializationContext(); + return _bindAndReadValues(ctxt, + _considerFilter(_parserFactory.createParser(ctxt, src, offset, length), true)); } /** * Overloaded version of {@link #readValue(InputStream)}. */ - public final MappingIterator readValues(byte[] src) - throws IOException { + public final MappingIterator readValues(byte[] src) throws IOException { return readValues(src, 0, src.length); } - + /** * Overloaded version of {@link #readValue(InputStream)}. */ - public MappingIterator readValues(File src) - throws IOException + public MappingIterator readValues(File src) throws IOException { - if (_dataFormatReaders != null) { - return _detectBindAndReadValues( - _dataFormatReaders.findFormat(_inputStream(src)), false); - } - return _bindAndReadValues(_considerFilter(_parserFactory.createParser(src), true)); + DefaultDeserializationContext ctxt = createDeserializationContext(); + return _bindAndReadValues(ctxt, + _considerFilter(_parserFactory.createParser(ctxt, src), true)); } /** @@ -1535,34 +1420,26 @@ public MappingIterator readValues(File src) * * @param src URL to read to access JSON content to parse. */ - public MappingIterator readValues(URL src) - throws IOException + public MappingIterator readValues(URL src) throws IOException { - if (_dataFormatReaders != null) { - return _detectBindAndReadValues( - _dataFormatReaders.findFormat(_inputStream(src)), true); - } - return _bindAndReadValues(_considerFilter(_parserFactory.createParser(src), true)); + DefaultDeserializationContext ctxt = createDeserializationContext(); + return _bindAndReadValues(ctxt, + _considerFilter(_parserFactory.createParser(ctxt, src), true)); } - /** - * @since 2.8 - */ public MappingIterator readValues(DataInput src) throws IOException { - if (_dataFormatReaders != null) { - _reportUndetectableSource(src); - } - return _bindAndReadValues(_considerFilter(_parserFactory.createParser(src), true)); + DefaultDeserializationContext ctxt = createDeserializationContext(); + return _bindAndReadValues(ctxt, + _considerFilter(_parserFactory.createParser(ctxt, src), true)); } /* - /********************************************************** + /********************************************************************** /* Implementation of rest of ObjectCodec methods - /********************************************************** + /********************************************************************** */ - @Override public T treeToValue(TreeNode n, Class valueType) throws JsonProcessingException { try { @@ -1572,29 +1449,24 @@ public T treeToValue(TreeNode n, Class valueType) throws JsonProcessingEx } catch (IOException e) { // should not occur, no real i/o... throw JsonMappingException.fromUnexpectedIOE(e); } - } - - @Override - public void writeValue(JsonGenerator gen, Object value) throws IOException { - throw new UnsupportedOperationException("Not implemented for ObjectReader"); } /* - /********************************************************** + /********************************************************************** /* Helper methods, data-binding - /********************************************************** + /********************************************************************** */ /** * Actual implementation of value reading+binding operation. */ - protected Object _bind(JsonParser p, Object valueToUpdate) throws IOException + protected Object _bind(DefaultDeserializationContext ctxt, + JsonParser p, 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) */ Object result; - final DeserializationContext ctxt = createDeserializationContext(p); JsonToken t = _initForReading(ctxt, p); if (t == JsonToken.VALUE_NULL) { if (valueToUpdate == null) { @@ -1626,12 +1498,12 @@ protected Object _bind(JsonParser p, Object valueToUpdate) throws IOException return result; } - protected Object _bindAndClose(JsonParser p0) throws IOException + protected Object _bindAndClose(DefaultDeserializationContext ctxt, + JsonParser p0) throws IOException { try (JsonParser p = p0) { Object result; - DeserializationContext ctxt = createDeserializationContext(p); JsonToken t = _initForReading(ctxt, p); if (t == JsonToken.VALUE_NULL) { if (_valueToUpdate == null) { @@ -1661,45 +1533,35 @@ protected Object _bindAndClose(JsonParser p0) throws IOException } } - protected final JsonNode _bindAndCloseAsTree(JsonParser p0) throws IOException { + protected final JsonNode _bindAndCloseAsTree(DefaultDeserializationContext ctxt, + JsonParser p0) throws IOException { try (JsonParser p = p0) { - return _bindAsTree(p); + return _bindAsTree(ctxt, p); } } - protected final JsonNode _bindAsTree(JsonParser p) throws IOException + protected final JsonNode _bindAsTree(DefaultDeserializationContext ctxt, + JsonParser p) throws IOException { - // Need to inline `_initForReading()` due to tree reading handling end-of-input specially - _config.initialize(p); - if (_schema != null) { - p.setSchema(_schema); - } - - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == null) { t = p.nextToken(); if (t == null) { + // [databind#2211]: return `MissingNode` (supercedes [databind#1406] which dictated + // returning `null` return _config.getNodeFactory().missingNode(); } } - final DeserializationContext ctxt; final JsonNode resultNode; - final boolean checkTrailing = _config.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS); - if (t == JsonToken.VALUE_NULL) { - resultNode = _config.getNodeFactory().nullNode(); - if (!checkTrailing) { - return resultNode; - } - ctxt = createDeserializationContext(p); + resultNode = ctxt.getNodeFactory().nullNode(); } else { - ctxt = createDeserializationContext(p); final JsonDeserializer deser = _findTreeDeserializer(ctxt); if (_unwrapRoot) { - resultNode = (JsonNode) _unwrapAndDeserialize(p, ctxt, JSON_NODE_TYPE, deser); - } else { - resultNode = (JsonNode) deser.deserialize(p, ctxt); + // NOTE: will do "check if trailing" check in call + return (JsonNode) _unwrapAndDeserialize(p, ctxt, JSON_NODE_TYPE, deser); } + resultNode = (JsonNode) deser.deserialize(p, ctxt); } if (_config.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)) { _verifyNoTrailingTokens(p, ctxt, JSON_NODE_TYPE); @@ -1711,49 +1573,36 @@ protected final JsonNode _bindAsTree(JsonParser p) throws IOException * Same as {@link #_bindAsTree} except end-of-input is reported by returning * {@code null}, not "missing node" */ - protected final JsonNode _bindAsTreeOrNull(JsonParser p) throws IOException + protected final JsonNode _bindAsTreeOrNull(DefaultDeserializationContext ctxt, + JsonParser p) throws IOException { - _config.initialize(p); - if (_schema != null) { - p.setSchema(_schema); - } - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == null) { t = p.nextToken(); - if (t == null) { + if (t == null) { // unlike above, here we do return `null` return null; } } - final DeserializationContext ctxt; final JsonNode resultNode; - final boolean checkTrailing = _config.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS); if (t == JsonToken.VALUE_NULL) { - resultNode = _config.getNodeFactory().nullNode(); - if (!checkTrailing) { - return resultNode; - } - ctxt = createDeserializationContext(p); + resultNode = ctxt.getNodeFactory().nullNode(); } else { - ctxt = createDeserializationContext(p); final JsonDeserializer deser = _findTreeDeserializer(ctxt); if (_unwrapRoot) { - resultNode = (JsonNode) _unwrapAndDeserialize(p, ctxt, JSON_NODE_TYPE, deser); - } else { - resultNode = (JsonNode) deser.deserialize(p, ctxt); + // NOTE: will do "check if trailing" check in call + return (JsonNode) _unwrapAndDeserialize(p, ctxt, JSON_NODE_TYPE, deser); } + resultNode = (JsonNode) deser.deserialize(p, ctxt); } - if (checkTrailing) { + if (_config.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)) { _verifyNoTrailingTokens(p, ctxt, JSON_NODE_TYPE); } return resultNode; } - /** - * @since 2.1 - */ - protected MappingIterator _bindAndReadValues(JsonParser p) throws IOException + protected MappingIterator _bindAndReadValues(DefaultDeserializationContext ctxt, + JsonParser p) throws IOException { - DeserializationContext ctxt = createDeserializationContext(p); _initForMultiRead(ctxt, p); p.nextToken(); return _newIterator(p, ctxt, _findRootDeserializer(ctxt), true); @@ -1766,17 +1615,17 @@ protected Object _unwrapAndDeserialize(JsonParser p, DeserializationContext ctxt // 12-Jun-2015, tatu: Should try to support namespaces etc but... String expSimpleName = expRootName.getSimpleName(); - if (p.getCurrentToken() != JsonToken.START_OBJECT) { + if (p.currentToken() != JsonToken.START_OBJECT) { ctxt.reportWrongTokenException(rootType, JsonToken.START_OBJECT, "Current token not START_OBJECT (needed to unwrap root name '%s'), but %s", - expSimpleName, p.getCurrentToken()); + expSimpleName, p.currentToken()); } if (p.nextToken() != JsonToken.FIELD_NAME) { ctxt.reportWrongTokenException(rootType, JsonToken.FIELD_NAME, "Current token not FIELD_NAME (to contain expected root name '%s'), but %s", - expSimpleName, p.getCurrentToken()); + expSimpleName, p.currentToken()); } - String actualName = p.getCurrentName(); + String actualName = p.currentName(); if (!expSimpleName.equals(actualName)) { ctxt.reportInputMismatch(rootType, "Root name '%s' does not match expected ('%s') for type %s", @@ -1795,7 +1644,7 @@ protected Object _unwrapAndDeserialize(JsonParser p, DeserializationContext ctxt if (p.nextToken() != JsonToken.END_OBJECT) { ctxt.reportWrongTokenException(rootType, JsonToken.END_OBJECT, "Current token not END_OBJECT (to match wrapper object with root name '%s'), but %s", - expSimpleName, p.getCurrentToken()); + expSimpleName, p.currentToken()); } if (_config.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)) { _verifyNoTrailingTokens(p, ctxt, _valueType); @@ -1813,9 +1662,6 @@ protected JsonParser _considerFilter(final JsonParser p, boolean multiValue) { ? p : new FilteringParserDelegate(p, _filter, false, multiValue); } - /** - * @since 2.9 - */ protected final void _verifyNoTrailingTokens(JsonParser p, DeserializationContext ctxt, JavaType bindType) throws IOException @@ -1833,89 +1679,11 @@ protected final void _verifyNoTrailingTokens(JsonParser p, DeserializationContex } /* - /********************************************************** - /* Internal methods, format auto-detection - /********************************************************** - */ - - @SuppressWarnings("resource") - protected Object _detectBindAndClose(byte[] src, int offset, int length) throws IOException - { - DataFormatReaders.Match match = _dataFormatReaders.findFormat(src, offset, length); - if (!match.hasMatch()) { - _reportUnkownFormat(_dataFormatReaders, match); - } - JsonParser p = match.createParserWithMatch(); - return match.getReader()._bindAndClose(p); - } - - @SuppressWarnings({ "resource" }) - protected Object _detectBindAndClose(DataFormatReaders.Match match, boolean forceClosing) - throws IOException - { - if (!match.hasMatch()) { - _reportUnkownFormat(_dataFormatReaders, match); - } - JsonParser p = match.createParserWithMatch(); - // One more thing: we Own the input stream now; and while it's - // not super clean way to do it, we must ensure closure so: - if (forceClosing) { - p.enable(JsonParser.Feature.AUTO_CLOSE_SOURCE); - } - // important: use matching ObjectReader (may not be 'this') - return match.getReader()._bindAndClose(p); - } - - @SuppressWarnings({ "resource" }) - protected MappingIterator _detectBindAndReadValues(DataFormatReaders.Match match, boolean forceClosing) - throws IOException - { - if (!match.hasMatch()) { - _reportUnkownFormat(_dataFormatReaders, match); - } - JsonParser p = match.createParserWithMatch(); - // One more thing: we Own the input stream now; and while it's - // not super clean way to do it, we must ensure closure so: - if (forceClosing) { - p.enable(JsonParser.Feature.AUTO_CLOSE_SOURCE); - } - // important: use matching ObjectReader (may not be 'this') - return match.getReader()._bindAndReadValues(p); - } - - @SuppressWarnings({ "resource" }) - protected JsonNode _detectBindAndCloseAsTree(InputStream in) throws IOException - { - DataFormatReaders.Match match = _dataFormatReaders.findFormat(in); - if (!match.hasMatch()) { - _reportUnkownFormat(_dataFormatReaders, match); - } - JsonParser p = match.createParserWithMatch(); - p.enable(JsonParser.Feature.AUTO_CLOSE_SOURCE); - return match.getReader()._bindAndCloseAsTree(p); - } - - /** - * Method called to indicate that format detection failed to detect format - * of given input - */ - protected void _reportUnkownFormat(DataFormatReaders detector, DataFormatReaders.Match match) - throws JsonProcessingException - { - // 17-Aug-2015, tatu: Unfortunately, no parser/generator available so: - throw new JsonParseException(null, "Cannot detect format from input, does not look like any of detectable formats " - +detector.toString()); - } - - /* - /********************************************************** + /********************************************************************** /* Internal methods, other - /********************************************************** + /********************************************************************** */ - /** - * @since 2.2 - */ protected void _verifySchemaType(FormatSchema schema) { if (schema != null) { @@ -1931,8 +1699,13 @@ protected void _verifySchemaType(FormatSchema schema) * for deserializing a single root value. * Can be overridden if a custom context is needed. */ + protected DefaultDeserializationContext createDeserializationContext() { + return _contexts.createContext(_config, _schema, _injectableValues); + } + protected DefaultDeserializationContext createDeserializationContext(JsonParser p) { - return _context.createInstance(_config, p, _injectableValues); + return _contexts.createContext(_config, _schema, _injectableValues) + .assignParser(p); } protected InputStream _inputStream(URL src) throws IOException { @@ -1951,9 +1724,9 @@ protected void _reportUndetectableSource(Object src) throws JsonProcessingExcept } /* - /********************************************************** + /********************************************************************** /* Helper methods, locating deserializers etc - /********************************************************** + /********************************************************************** */ /** @@ -1986,9 +1759,6 @@ protected JsonDeserializer _findRootDeserializer(DeserializationContext return deser; } - /** - * @since 2.6 - */ protected JsonDeserializer _findTreeDeserializer(DeserializationContext ctxt) throws JsonMappingException { @@ -2020,7 +1790,7 @@ protected JsonDeserializer _prefetchRootDeserializer(JavaType valueType) if (deser == null) { try { // If not, need to resolve; for which we need a temporary context as well: - DeserializationContext ctxt = createDeserializationContext(null); + DeserializationContext ctxt = createDeserializationContext(); deser = ctxt.findRootValueDeserializer(valueType); if (deser != null) { _rootDeserializers.put(valueType, deser); diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectWriter.java b/src/main/java/com/fasterxml/jackson/databind/ObjectWriter.java index 5098612467..51de144e3e 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ObjectWriter.java +++ b/src/main/java/com/fasterxml/jackson/databind/ObjectWriter.java @@ -5,17 +5,19 @@ import java.util.Locale; import java.util.Map; import java.util.TimeZone; -import java.util.concurrent.atomic.AtomicReference; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.io.CharacterEscapes; import com.fasterxml.jackson.core.io.SegmentedStringWriter; -import com.fasterxml.jackson.core.io.SerializedString; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.util.*; import com.fasterxml.jackson.databind.cfg.ContextAttributes; +import com.fasterxml.jackson.databind.cfg.GeneratorSettings; +import com.fasterxml.jackson.databind.cfg.SerializationContexts; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.ser.*; import com.fasterxml.jackson.databind.ser.impl.TypeWrappedSerializer; import com.fasterxml.jackson.databind.type.TypeFactory; @@ -30,21 +32,13 @@ * reused in completely thread-safe manner with no explicit synchronization */ public class ObjectWriter - implements Versioned, - java.io.Serializable // since 2.1 + implements Versioned + // NOTE: since 3.x, NO LONGER JDK Serializable { - private static final long serialVersionUID = 1; // since 2.5 - - /** - * We need to keep track of explicit disabling of pretty printing; - * easiest to do by a token value. - */ - protected final static PrettyPrinter NULL_PRETTY_PRINTER = new MinimalPrettyPrinter(); - /* - /********************************************************** + /********************************************************************** /* Immutable configuration from ObjectMapper - /********************************************************** + /********************************************************************** */ /** @@ -52,26 +46,29 @@ public class ObjectWriter */ protected final SerializationConfig _config; - protected final DefaultSerializerProvider _serializerProvider; - - protected final SerializerFactory _serializerFactory; + /** + * Factory used for constructing per-call {@link SerializerProvider}s. + *

+ * Note: while serializers are only exposed {@link SerializerProvider}, + * mappers and readers need to access additional API defined by + * {@link DefaultSerializerProvider} + */ + protected final SerializationContexts _serializationContexts; /** * Factory used for constructing {@link JsonGenerator}s */ - protected final JsonFactory _generatorFactory; + protected final TokenStreamFactory _generatorFactory; /* - /********************************************************** + /********************************************************************** /* Configuration that can be changed via mutant factories - /********************************************************** + /********************************************************************** */ /** * Container for settings that need to be passed to {@link JsonGenerator} * constructed for serializing values. - * - * @since 2.5 */ protected final GeneratorSettings _generatorSettings; @@ -80,15 +77,13 @@ public class ObjectWriter * is known (has been explicitly declared), and if so, reuse it afterwards. * This allows avoiding further serializer lookups and increases * performance a bit on cases where readers are reused. - * - * @since 2.5 */ protected final Prefetch _prefetch; /* - /********************************************************** + /********************************************************************** /* Life-cycle, constructors - /********************************************************** + /********************************************************************** */ /** @@ -98,9 +93,8 @@ protected ObjectWriter(ObjectMapper mapper, SerializationConfig config, JavaType rootType, PrettyPrinter pp) { _config = config; - _serializerProvider = mapper._serializerProvider; - _serializerFactory = mapper._serializerFactory; - _generatorFactory = mapper._jsonFactory; + _serializationContexts = mapper._serializationContexts; + _generatorFactory = mapper._streamFactory; _generatorSettings = (pp == null) ? GeneratorSettings.empty : new GeneratorSettings(pp, null, null, null); @@ -119,9 +113,8 @@ protected ObjectWriter(ObjectMapper mapper, SerializationConfig config, protected ObjectWriter(ObjectMapper mapper, SerializationConfig config) { _config = config; - _serializerProvider = mapper._serializerProvider; - _serializerFactory = mapper._serializerFactory; - _generatorFactory = mapper._jsonFactory; + _serializationContexts = mapper._serializationContexts; + _generatorFactory = mapper._streamFactory; _generatorSettings = GeneratorSettings.empty; _prefetch = Prefetch.empty; @@ -135,9 +128,8 @@ protected ObjectWriter(ObjectMapper mapper, SerializationConfig config, { _config = config; - _serializerProvider = mapper._serializerProvider; - _serializerFactory = mapper._serializerFactory; - _generatorFactory = mapper._jsonFactory; + _serializationContexts = mapper._serializationContexts; + _generatorFactory = mapper._streamFactory; _generatorSettings = (s == null) ? GeneratorSettings.empty : new GeneratorSettings(null, s, null, null); @@ -152,8 +144,7 @@ protected ObjectWriter(ObjectWriter base, SerializationConfig config, { _config = config; - _serializerProvider = base._serializerProvider; - _serializerFactory = base._serializerFactory; + _serializationContexts = base._serializationContexts; _generatorFactory = base._generatorFactory; _generatorSettings = genSettings; @@ -167,31 +158,13 @@ protected ObjectWriter(ObjectWriter base, SerializationConfig config) { _config = config; - _serializerProvider = base._serializerProvider; - _serializerFactory = base._serializerFactory; + _serializationContexts = base._serializationContexts; _generatorFactory = base._generatorFactory; _generatorSettings = base._generatorSettings; _prefetch = base._prefetch; } - /** - * @since 2.3 - */ - protected ObjectWriter(ObjectWriter base, JsonFactory f) - { - // may need to override ordering, based on data format capabilities - _config = base._config - .with(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, f.requiresPropertyOrdering()); - - _serializerProvider = base._serializerProvider; - _serializerFactory = base._serializerFactory; - _generatorFactory = f; - - _generatorSettings = base._generatorSettings; - _prefetch = base._prefetch; - } - /** * Method that will return version information stored in and read from jar * that contains this class. @@ -202,26 +175,13 @@ public Version version() { } /* - /********************************************************** - /* Methods sub-classes MUST override, used for constructing - /* writer instances, (re)configuring parser instances. - /* Added in 2.5 - /********************************************************** - */ - - /** - * Overridable factory method called by various "withXxx()" methods - * - * @since 2.5 + /********************************************************************** + /* Helper methdos to simplify mutant-factory implementation + /********************************************************************** */ - protected ObjectWriter _new(ObjectWriter base, JsonFactory f) { - return new ObjectWriter(base, f); - } /** * Overridable factory method called by various "withXxx()" methods - * - * @since 2.5 */ protected ObjectWriter _new(ObjectWriter base, SerializationConfig config) { if (config == _config) { @@ -234,8 +194,6 @@ protected ObjectWriter _new(ObjectWriter base, SerializationConfig config) { * Overridable factory method called by various "withXxx()" methods. * It assumes `this` as base for settings other than those directly * passed in. - * - * @since 2.5 */ protected ObjectWriter _new(GeneratorSettings genSettings, Prefetch prefetch) { if ((_generatorSettings == genSettings) && (_prefetch == prefetch)) { @@ -247,24 +205,20 @@ protected ObjectWriter _new(GeneratorSettings genSettings, Prefetch prefetch) { /** * Overridable factory method called by {@link #writeValues(OutputStream)} * method (and its various overrides), and initializes it as necessary. - * - * @since 2.5 */ @SuppressWarnings("resource") - protected SequenceWriter _newSequenceWriter(boolean wrapInArray, - JsonGenerator gen, boolean managedInput) + protected final SequenceWriter _newSequenceWriter(DefaultSerializerProvider prov, + boolean wrapInArray, JsonGenerator gen, boolean managedInput) throws IOException { - _configureGenerator(gen); - return new SequenceWriter(_serializerProvider(), - gen, managedInput, _prefetch) + return new SequenceWriter(prov, gen, managedInput, _prefetch) .init(wrapInArray); } /* - /********************************************************** + /********************************************************************** /* Life-cycle, fluent factories for SerializationFeature - /********************************************************** + /********************************************************************** */ /** @@ -272,7 +226,7 @@ protected SequenceWriter _newSequenceWriter(boolean wrapInArray, * with specified feature enabled. */ public ObjectWriter with(SerializationFeature feature) { - return _new(this, _config.with(feature)); + return _new(this, _config.with(feature)); } /** @@ -293,7 +247,7 @@ public ObjectWriter withFeatures(SerializationFeature... features) { /** * Method for constructing a new instance that is configured - * with specified feature enabled. + * with specified feature disabled. */ public ObjectWriter without(SerializationFeature feature) { return _new(this, _config.without(feature)); @@ -301,7 +255,7 @@ public ObjectWriter without(SerializationFeature feature) { /** * Method for constructing a new instance that is configured - * with specified features enabled. + * with specified features disabled. */ public ObjectWriter without(SerializationFeature first, SerializationFeature... other) { return _new(this, _config.without(first, other)); @@ -309,84 +263,60 @@ public ObjectWriter without(SerializationFeature first, SerializationFeature... /** * Method for constructing a new instance that is configured - * with specified features enabled. + * with specified features disabled. */ public ObjectWriter withoutFeatures(SerializationFeature... features) { return _new(this, _config.withoutFeatures(features)); } /* - /********************************************************** - /* Life-cycle, fluent factories for JsonGenerator.Feature (2.5) - /********************************************************** + /********************************************************************** + /* Life-cycle, fluent factories for StreamWriteFeature + /********************************************************************** */ - /** - * @since 2.5 - */ - public ObjectWriter with(JsonGenerator.Feature feature) { + public ObjectWriter with(StreamWriteFeature feature) { return _new(this, _config.with(feature)); } - /** - * @since 2.5 - */ - public ObjectWriter withFeatures(JsonGenerator.Feature... features) { + public ObjectWriter withFeatures(StreamWriteFeature... features) { return _new(this, _config.withFeatures(features)); } - /** - * @since 2.5 - */ - public ObjectWriter without(JsonGenerator.Feature feature) { + public ObjectWriter without(StreamWriteFeature feature) { return _new(this, _config.without(feature)); } - /** - * @since 2.5 - */ - public ObjectWriter withoutFeatures(JsonGenerator.Feature... features) { + public ObjectWriter withoutFeatures(StreamWriteFeature... features) { return _new(this, _config.withoutFeatures(features)); } /* - /********************************************************** - /* Life-cycle, fluent factories for FormatFeature (2.7) - /********************************************************** + /********************************************************************** + /* Life-cycle, fluent factories for FormatFeature + /********************************************************************** */ - /** - * @since 2.7 - */ public ObjectWriter with(FormatFeature feature) { return _new(this, _config.with(feature)); } - /** - * @since 2.7 - */ public ObjectWriter withFeatures(FormatFeature... features) { return _new(this, _config.withFeatures(features)); } - /** - * @since 2.7 - */ public ObjectWriter without(FormatFeature feature) { return _new(this, _config.without(feature)); } - /** - * @since 2.7 - */ public ObjectWriter withoutFeatures(FormatFeature... features) { return _new(this, _config.withoutFeatures(features)); } /* - /********************************************************** + /********************************************************************** /* Life-cycle, fluent factories, type-related - /********************************************************** + /********************************************************************** */ /** @@ -396,8 +326,6 @@ public ObjectWriter withoutFeatures(FormatFeature... features) { *

* Note that method does NOT change state of this reader, but * rather construct and returns a newly configured instance. - * - * @since 2.5 */ public ObjectWriter forType(JavaType rootType) { return _new(_generatorSettings, _prefetch.forRootType(this, rootType)); @@ -407,8 +335,6 @@ public ObjectWriter forType(JavaType rootType) { * Method that will construct a new instance that uses specific type * as the root type for serialization, instead of runtime dynamic * type of the root object itself. - * - * @since 2.5 */ public ObjectWriter forType(Class rootType) { if (rootType == Object.class) { @@ -421,41 +347,15 @@ public ObjectWriter forType(Class rootType) { * Method that will construct a new instance that uses specific type * as the root type for serialization, instead of runtime dynamic * type of the root object itself. - * - * @since 2.5 */ public ObjectWriter forType(TypeReference rootType) { return forType(_config.getTypeFactory().constructType(rootType.getType())); } - /** - * @deprecated since 2.5 Use {@link #forType(JavaType)} instead - */ - @Deprecated // since 2.5 - public ObjectWriter withType(JavaType rootType) { - return forType(rootType); - } - - /** - * @deprecated since 2.5 Use {@link #forType(Class)} instead - */ - @Deprecated // since 2.5 - public ObjectWriter withType(Class rootType) { - return forType(rootType); - } - - /** - * @deprecated since 2.5 Use {@link #forType(TypeReference)} instead - */ - @Deprecated // since 2.5 - public ObjectWriter withType(TypeReference rootType) { - return forType(rootType); - } - /* - /********************************************************** + /********************************************************************** /* Life-cycle, fluent factories, other - /********************************************************** + /********************************************************************** */ /** @@ -512,9 +412,6 @@ public ObjectWriter withRootName(String rootName) { return _new(this, _config.withRootName(rootName)); } - /** - * @since 2.6 - */ public ObjectWriter withRootName(PropertyName rootName) { return _new(this, _config.withRootName(rootName)); } @@ -526,8 +423,6 @@ public ObjectWriter withRootName(PropertyName rootName) { * * which will forcibly prevent use of root name wrapping when writing * values with this {@link ObjectWriter}. - * - * @since 2.6 */ public ObjectWriter withoutRootName() { return _new(this, _config.withRootName(PropertyName.NO_NAME)); @@ -545,14 +440,6 @@ public ObjectWriter with(FormatSchema schema) { return _new(_generatorSettings.with(schema), _prefetch); } - /** - * @deprecated Since 2.5 use {@link #with(FormatSchema)} instead - */ - @Deprecated - public ObjectWriter withSchema(FormatSchema schema) { - return with(schema); - } - /** * Method that will construct a new instance that uses specified * serialization view for serialization (with null basically disables @@ -576,30 +463,15 @@ public ObjectWriter with(TimeZone tz) { /** * Method that will construct a new instance that uses specified default * {@link Base64Variant} for base64 encoding - * - * @since 2.1 */ public ObjectWriter with(Base64Variant b64variant) { return _new(this, _config.with(b64variant)); } - /** - * @since 2.3 - */ public ObjectWriter with(CharacterEscapes escapes) { return _new(_generatorSettings.with(escapes), _prefetch); } - /** - * @since 2.3 - */ - public ObjectWriter with(JsonFactory f) { - return (f == _generatorFactory) ? this : _new(this, f); - } - - /** - * @since 2.3 - */ public ObjectWriter with(ContextAttributes attrs) { return _new(this, _config.with(attrs)); } @@ -607,45 +479,112 @@ public ObjectWriter with(ContextAttributes attrs) { /** * Mutant factory method that allows construction of a new writer instance * that uses specified set of default attribute values. - * - * @since 2.3 */ public ObjectWriter withAttributes(Map attrs) { return _new(this, _config.withAttributes(attrs)); } - /** - * @since 2.3 - */ public ObjectWriter withAttribute(Object key, Object value) { return _new(this, _config.withAttribute(key, value)); } - /** - * @since 2.3 - */ public ObjectWriter withoutAttribute(Object key) { return _new(this, _config.withoutAttribute(key)); } - /** - * @since 2.5 - */ public ObjectWriter withRootValueSeparator(String sep) { return _new(_generatorSettings.withRootValueSeparator(sep), _prefetch); } - /** - * @since 2.5 - */ public ObjectWriter withRootValueSeparator(SerializableString sep) { return _new(_generatorSettings.withRootValueSeparator(sep), _prefetch); } /* - /********************************************************** - /* Factory methods for sequence writers (2.5) - /********************************************************** + /********************************************************************** + /* Public API: constructing Generator that are properly linked to `ObjectWriteContext` + /********************************************************************** + */ + + /** + * Factory method for constructing {@link JsonGenerator} that is properly + * wired to allow callbacks for serialization: basically + * constructs a {@link ObjectWriteContext} and then calls + * {@link TokenStreamFactory#createGenerator(ObjectWriteContext,OutputStream)}. + * + * @since 3.0 + */ + public JsonGenerator createGenerator(OutputStream out) throws IOException { + return _generatorFactory.createGenerator(_serializerProvider(), out); + } + + /** + * Factory method for constructing {@link JsonGenerator} that is properly + * wired to allow callbacks for serialization: basically + * constructs a {@link ObjectWriteContext} and then calls + * {@link TokenStreamFactory#createGenerator(ObjectWriteContext,OutputStream,JsonEncoding)}. + * + * @since 3.0 + */ + public JsonGenerator createGenerator(OutputStream out, JsonEncoding enc) throws IOException { + return _generatorFactory.createGenerator(_serializerProvider(), out, enc); + } + + /** + * Factory method for constructing {@link JsonGenerator} that is properly + * wired to allow callbacks for serialization: basically + * constructs a {@link ObjectWriteContext} and then calls + * {@link TokenStreamFactory#createGenerator(ObjectWriteContext,Writer)}. + * + * @since 3.0 + */ + public JsonGenerator createGenerator(Writer w) throws IOException { + return _generatorFactory.createGenerator(_serializerProvider(), w); + } + + /** + * Factory method for constructing {@link JsonGenerator} that is properly + * wired to allow callbacks for serialization: basically + * constructs a {@link ObjectWriteContext} and then calls + * {@link TokenStreamFactory#createGenerator(ObjectWriteContext,File,JsonEncoding)}. + * + * @since 3.0 + */ + public JsonGenerator createGenerator(File f, JsonEncoding enc) + throws IOException { + return _generatorFactory.createGenerator(_serializerProvider(), f, enc); + } + + /** + * Factory method for constructing {@link JsonGenerator} that is properly + * wired to allow callbacks for serialization: basically + * constructs a {@link ObjectWriteContext} and then calls + * {@link TokenStreamFactory#createGenerator(ObjectWriteContext,DataOutput)}. + * + * @since 3.0 + */ + public JsonGenerator createGenerator(DataOutput out) throws IOException { + return _generatorFactory.createGenerator(_serializerProvider(), out); + } + + /* + /********************************************************************** + /* Convenience methods for JsonNode creation + /********************************************************************** + */ + + public ObjectNode createObjectNode() { + return _config.getNodeFactory().objectNode(); + } + + public ArrayNode createArrayNode() { + return _config.getNodeFactory().arrayNode(); + } + + /* + /********************************************************************** + /* Factory methods for sequence writers + /********************************************************************** */ /** @@ -658,12 +597,11 @@ public ObjectWriter withRootValueSeparator(SerializableString sep) { * output stream. * * @param out Target file to write value sequence to. - * - * @since 2.5 */ public SequenceWriter writeValues(File out) throws IOException { - return _newSequenceWriter(false, - _generatorFactory.createGenerator(out, JsonEncoding.UTF8), true); + DefaultSerializerProvider prov = _serializerProvider(); + return _newSequenceWriter(prov, false, + _generatorFactory.createGenerator(prov, out, JsonEncoding.UTF8), true); } /** @@ -678,12 +616,9 @@ public SequenceWriter writeValues(File out) throws IOException { * * @param gen Low-level generator caller has already constructed that will * be used for actual writing of token stream. - * - * @since 2.5 */ public SequenceWriter writeValues(JsonGenerator gen) throws IOException { - _configureGenerator(gen); - return _newSequenceWriter(false, gen, false); + return _newSequenceWriter(_serializerProvider(), false, gen, false); } /** @@ -696,12 +631,11 @@ public SequenceWriter writeValues(JsonGenerator gen) throws IOException { * output stream. * * @param out Target writer to use for writing the token stream - * - * @since 2.5 */ public SequenceWriter writeValues(Writer out) throws IOException { - return _newSequenceWriter(false, - _generatorFactory.createGenerator(out), true); + DefaultSerializerProvider prov = _serializerProvider(); + return _newSequenceWriter(prov, false, + _generatorFactory.createGenerator(prov, out), true); } /** @@ -714,20 +648,17 @@ public SequenceWriter writeValues(Writer out) throws IOException { * output stream. * * @param out Physical output stream to use for writing the token stream - * - * @since 2.5 */ public SequenceWriter writeValues(OutputStream out) throws IOException { - return _newSequenceWriter(false, - _generatorFactory.createGenerator(out, JsonEncoding.UTF8), true); + DefaultSerializerProvider prov = _serializerProvider(); + return _newSequenceWriter(prov, false, + _generatorFactory.createGenerator(prov, out, JsonEncoding.UTF8), true); } - /** - * @since 2.8 - */ public SequenceWriter writeValues(DataOutput out) throws IOException { - return _newSequenceWriter(false, - _generatorFactory.createGenerator(out), true); + DefaultSerializerProvider prov = _serializerProvider(); + return _newSequenceWriter(prov, false, + _generatorFactory.createGenerator(prov, out), true); } /** @@ -742,12 +673,11 @@ public SequenceWriter writeValues(DataOutput out) throws IOException { * or {@link java.util.Collection} type. * * @param out File to write token stream to - * - * @since 2.5 */ public SequenceWriter writeValuesAsArray(File out) throws IOException { - return _newSequenceWriter(true, - _generatorFactory.createGenerator(out, JsonEncoding.UTF8), true); + DefaultSerializerProvider prov = _serializerProvider(); + return _newSequenceWriter(prov, true, + _generatorFactory.createGenerator(prov, out, JsonEncoding.UTF8), true); } /** @@ -763,11 +693,9 @@ public SequenceWriter writeValuesAsArray(File out) throws IOException { * or {@link java.util.Collection} type. * * @param gen Underlying generator to use for writing the token stream - * - * @since 2.5 */ public SequenceWriter writeValuesAsArray(JsonGenerator gen) throws IOException { - return _newSequenceWriter(true, gen, false); + return _newSequenceWriter(_serializerProvider(), true, gen, false); } /** @@ -782,11 +710,11 @@ public SequenceWriter writeValuesAsArray(JsonGenerator gen) throws IOException { * or {@link java.util.Collection} type. * * @param out Writer to use for writing the token stream - * - * @since 2.5 */ public SequenceWriter writeValuesAsArray(Writer out) throws IOException { - return _newSequenceWriter(true, _generatorFactory.createGenerator(out), true); + DefaultSerializerProvider prov = _serializerProvider(); + return _newSequenceWriter(prov, true, + _generatorFactory.createGenerator(prov, out), true); } /** @@ -801,25 +729,23 @@ public SequenceWriter writeValuesAsArray(Writer out) throws IOException { * or {@link java.util.Collection} type. * * @param out Physical output stream to use for writing the token stream - * - * @since 2.5 */ public SequenceWriter writeValuesAsArray(OutputStream out) throws IOException { - return _newSequenceWriter(true, - _generatorFactory.createGenerator(out, JsonEncoding.UTF8), true); + DefaultSerializerProvider prov = _serializerProvider(); + return _newSequenceWriter(prov, true, + _generatorFactory.createGenerator(prov, out, JsonEncoding.UTF8), true); } - /** - * @since 2.8 - */ public SequenceWriter writeValuesAsArray(DataOutput out) throws IOException { - return _newSequenceWriter(true, _generatorFactory.createGenerator(out), true); + DefaultSerializerProvider prov = _serializerProvider(); + return _newSequenceWriter(prov, true, + _generatorFactory.createGenerator(prov, out), true); } /* - /********************************************************** + /********************************************************************** /* Simple accessors - /********************************************************** + /********************************************************************** */ public boolean isEnabled(SerializationFeature f) { @@ -830,36 +756,24 @@ public boolean isEnabled(MapperFeature f) { return _config.isEnabled(f); } - /** - * @since 2.9 - */ - @Deprecated - public boolean isEnabled(JsonParser.Feature f) { + public boolean isEnabled(StreamWriteFeature f) { + // !!! 09-Oct-2017, tatu: Actually for full answer we really should check + // what actual combined settings are.... return _generatorFactory.isEnabled(f); } - /** - * @since 2.9 - */ - public boolean isEnabled(JsonGenerator.Feature f) { - return _generatorFactory.isEnabled(f); - } - - /** - * @since 2.2 - */ public SerializationConfig getConfig() { return _config; } /** - * @since 2.2 + * @since 3.0 */ - public JsonFactory getFactory() { + public TokenStreamFactory generatorFactory() { return _generatorFactory; } - - public TypeFactory getTypeFactory() { + + public TypeFactory typeFactory() { return _config.getTypeFactory(); } @@ -868,24 +782,27 @@ public TypeFactory getTypeFactory() { * has pre-fetched serializer to use: pre-fetching improves performance * when writer instances are reused as it avoids a per-call serializer * lookup. - * - * @since 2.2 */ public boolean hasPrefetchedSerializer() { return _prefetch.hasSerializer(); } - /** - * @since 2.3 - */ public ContextAttributes getAttributes() { return _config.getAttributes(); } - + + /** + * @deprecated Since 3.0 use {@link #typeFactory} + */ + @Deprecated + public TypeFactory getTypeFactory() { + return typeFactory(); + } + /* - /********************************************************** + /********************************************************************** /* Serialization methods; ones from ObjectCodec first - /********************************************************** + /********************************************************************** */ /** @@ -894,7 +811,6 @@ public ContextAttributes getAttributes() { */ public void writeValue(JsonGenerator gen, Object value) throws IOException { - _configureGenerator(gen); if (_config.isEnabled(SerializationFeature.CLOSE_CLOSEABLE) && (value instanceof Closeable)) { @@ -918,9 +834,9 @@ public void writeValue(JsonGenerator gen, Object value) throws IOException } /* - /********************************************************** + /********************************************************************** /* Serialization methods, others - /********************************************************** + /********************************************************************** */ /** @@ -930,7 +846,9 @@ public void writeValue(JsonGenerator gen, Object value) throws IOException public void writeValue(File resultFile, Object value) throws IOException, JsonGenerationException, JsonMappingException { - _configAndWriteValue(_generatorFactory.createGenerator(resultFile, JsonEncoding.UTF8), value); + DefaultSerializerProvider prov = _serializerProvider(); + _configAndWriteValue(prov, + _generatorFactory.createGenerator(prov, resultFile, JsonEncoding.UTF8), value); } /** @@ -939,7 +857,7 @@ public void writeValue(File resultFile, Object value) * {@link JsonEncoding#UTF8}). *

* Note: method does not close the underlying stream explicitly - * here; however, {@link JsonFactory} this mapper uses may choose + * here; however, {@link TokenStreamFactory} this mapper uses may choose * to close the stream depending on its settings (by default, * it will try to close it when {@link JsonGenerator} we construct * is closed). @@ -947,7 +865,9 @@ public void writeValue(File resultFile, Object value) public void writeValue(OutputStream out, Object value) throws IOException, JsonGenerationException, JsonMappingException { - _configAndWriteValue(_generatorFactory.createGenerator(out, JsonEncoding.UTF8), value); + DefaultSerializerProvider prov = _serializerProvider(); + _configAndWriteValue(prov, + _generatorFactory.createGenerator(prov, out, JsonEncoding.UTF8), value); } /** @@ -955,24 +875,23 @@ public void writeValue(OutputStream out, Object value) * JSON output, using Writer provided. *

* Note: method does not close the underlying stream explicitly - * here; however, {@link JsonFactory} this mapper uses may choose + * here; however, {@link TokenStreamFactory} this mapper uses may choose * to close the stream depending on its settings (by default, * it will try to close it when {@link JsonGenerator} we construct * is closed). */ - public void writeValue(Writer w, Object value) - throws IOException, JsonGenerationException, JsonMappingException + public void writeValue(Writer w, Object value) throws IOException { - _configAndWriteValue(_generatorFactory.createGenerator(w), value); + DefaultSerializerProvider prov = _serializerProvider(); + _configAndWriteValue(prov, + _generatorFactory.createGenerator(prov, w), value); } - /** - * @since 2.8 - */ - public void writeValue(DataOutput out, Object value) - throws IOException + public void writeValue(DataOutput out, Object value) throws IOException { - _configAndWriteValue(_generatorFactory.createGenerator(out), value); + DefaultSerializerProvider prov = _serializerProvider(); + _configAndWriteValue(prov, + _generatorFactory.createGenerator(prov, out), value); } /** @@ -980,8 +899,6 @@ public void writeValue(DataOutput out, Object value) * a String. Functionally equivalent to calling * {@link #writeValue(Writer,Object)} with {@link java.io.StringWriter} * and constructing String, but more efficient. - *

- * Note: prior to version 2.1, throws clause included {@link IOException}; 2.1 removed it. */ @SuppressWarnings("resource") public String writeValueAsString(Object value) @@ -989,8 +906,10 @@ public String writeValueAsString(Object value) { // alas, we have to pull the recycler directly here... SegmentedStringWriter sw = new SegmentedStringWriter(_generatorFactory._getBufferRecycler()); + DefaultSerializerProvider prov = _serializerProvider(); try { - _configAndWriteValue(_generatorFactory.createGenerator(sw), value); + _configAndWriteValue(prov, + _generatorFactory.createGenerator(prov, sw), value); } catch (JsonProcessingException e) { throw e; } catch (IOException e) { // shouldn't really happen, but is declared as possibility so: @@ -1005,16 +924,16 @@ public String writeValueAsString(Object value) * {@link #writeValue(Writer,Object)} with {@link java.io.ByteArrayOutputStream} * and getting bytes, but more efficient. * Encoding used will be UTF-8. - *

- * Note: prior to version 2.1, throws clause included {@link IOException}; 2.1 removed it. */ @SuppressWarnings("resource") public byte[] writeValueAsBytes(Object value) throws JsonProcessingException { ByteArrayBuilder bb = new ByteArrayBuilder(_generatorFactory._getBufferRecycler()); + DefaultSerializerProvider prov = _serializerProvider(); try { - _configAndWriteValue(_generatorFactory.createGenerator(bb, JsonEncoding.UTF8), value); + _configAndWriteValue(prov, + _generatorFactory.createGenerator(prov, bb, JsonEncoding.UTF8), value); } catch (JsonProcessingException e) { // to support [JACKSON-758] throw e; } catch (IOException e) { // shouldn't really happen, but is declared as possibility so: @@ -1025,10 +944,50 @@ public byte[] writeValueAsBytes(Object value) return result; } + /** + * Method called to configure the generator as necessary and then + * call write functionality + */ + protected final void _configAndWriteValue(DefaultSerializerProvider prov, + JsonGenerator gen, Object value) throws IOException + { + if (_config.isEnabled(SerializationFeature.CLOSE_CLOSEABLE) && (value instanceof Closeable)) { + _writeCloseable(gen, value); + return; + } + try { + _prefetch.serialize(gen, value, prov); + } catch (Exception e) { + ClassUtil.closeOnFailAndThrowAsIOE(gen, e); + return; + } + gen.close(); + } + + /** + * Helper method used when value to serialize is {@link Closeable} and its close() + * method is to be called right after serialization has been called + */ + private final void _writeCloseable(JsonGenerator gen, Object value) + throws IOException + { + Closeable toClose = (Closeable) value; + try { + _prefetch.serialize(gen, value, _serializerProvider()); + Closeable tmpToClose = toClose; + toClose = null; + tmpToClose.close(); + } catch (Exception e) { + ClassUtil.closeOnFailAndThrowAsIOE(gen, toClose, e); + return; + } + gen.close(); + } + /* - /********************************************************** + /********************************************************************** /* Other public methods - /********************************************************** + /********************************************************************** */ /** @@ -1040,8 +999,6 @@ public byte[] writeValueAsBytes(Object value) * instance for specified type. * * @param type Type to generate schema for (possibly with generic signature) - * - * @since 2.2 */ public void acceptJsonFormatVisitor(JavaType type, JsonFormatVisitorWrapper visitor) throws JsonMappingException { @@ -1051,50 +1008,30 @@ public void acceptJsonFormatVisitor(JavaType type, JsonFormatVisitorWrapper visi _serializerProvider().acceptJsonFormatVisitor(type, visitor); } - /** - * Since 2.6 - */ public void acceptJsonFormatVisitor(Class rawType, JsonFormatVisitorWrapper visitor) throws JsonMappingException { acceptJsonFormatVisitor(_config.constructType(rawType), visitor); } - public boolean canSerialize(Class type) { - return _serializerProvider().hasSerializerFor(type, null); - } - - /** - * Method for checking whether instances of given type can be serialized, - * and optionally why (as per {@link Throwable} returned). - * - * @since 2.3 - */ - public boolean canSerialize(Class type, AtomicReference cause) { - return _serializerProvider().hasSerializerFor(type, cause); - } - /* - /********************************************************** + /********************************************************************** /* Overridable helper methods - /********************************************************** + /********************************************************************** */ /** * Overridable helper method used for constructing * {@link SerializerProvider} to use for serialization. */ - protected DefaultSerializerProvider _serializerProvider() { - return _serializerProvider.createInstance(_config, _serializerFactory); + protected final DefaultSerializerProvider _serializerProvider() { + return _serializationContexts.createContext(_config, _generatorSettings); } /* - /********************************************************** + /********************************************************************** /* Internal methods - /********************************************************** + /********************************************************************** */ - /** - * @since 2.2 - */ protected void _verifySchemaType(FormatSchema schema) { if (schema != null) { @@ -1105,199 +1042,16 @@ protected void _verifySchemaType(FormatSchema schema) } } - /** - * Method called to configure the generator as necessary and then - * call write functionality - */ - protected final void _configAndWriteValue(JsonGenerator gen, Object value) throws IOException - { - _configureGenerator(gen); - if (_config.isEnabled(SerializationFeature.CLOSE_CLOSEABLE) && (value instanceof Closeable)) { - _writeCloseable(gen, value); - return; - } - try { - _prefetch.serialize(gen, value, _serializerProvider()); - } catch (Exception e) { - ClassUtil.closeOnFailAndThrowAsIOE(gen, e); - return; - } - gen.close(); - } - - /** - * Helper method used when value to serialize is {@link Closeable} and its close() - * method is to be called right after serialization has been called - */ - private final void _writeCloseable(JsonGenerator gen, Object value) - throws IOException - { - Closeable toClose = (Closeable) value; - try { - _prefetch.serialize(gen, value, _serializerProvider()); - Closeable tmpToClose = toClose; - toClose = null; - tmpToClose.close(); - } catch (Exception e) { - ClassUtil.closeOnFailAndThrowAsIOE(gen, toClose, e); - return; - } - gen.close(); - } - - /** - * Helper method called to set or override settings of passed-in - * {@link JsonGenerator} - * - * @since 2.5 - */ - protected final void _configureGenerator(JsonGenerator gen) - { - // order is slightly significant: both may change PrettyPrinter - // settings. - _config.initialize(gen); // since 2.5 - _generatorSettings.initialize(gen); - } - /* - /********************************************************** + /********************************************************************** /* Helper classes for configuration - /********************************************************** + /********************************************************************** */ - /** - * Helper class used for containing settings specifically related - * to (re)configuring {@link JsonGenerator} constructed for - * writing output. - * - * @since 2.5 - */ - public final static class GeneratorSettings - implements java.io.Serializable - { - private static final long serialVersionUID = 1L; - - public final static GeneratorSettings empty = new GeneratorSettings(null, null, null, null); - - /** - * To allow for dynamic enabling/disabling of pretty printing, - * pretty printer can be optionally configured for writer - * as well - */ - public final PrettyPrinter prettyPrinter; - - /** - * When using data format that uses a schema, schema is passed - * to generator. - */ - public final FormatSchema schema; - - /** - * Caller may want to specify character escaping details, either as - * defaults, or on call-by-call basis. - */ - public final CharacterEscapes characterEscapes; - - /** - * Caller may want to override so-called "root value separator", - * String added (verbatim, with no quoting or escaping) between - * values in root context. Default value is a single space character, - * but this is often changed to linefeed. - */ - public final SerializableString rootValueSeparator; - - public GeneratorSettings(PrettyPrinter pp, FormatSchema sch, - CharacterEscapes esc, SerializableString rootSep) { - prettyPrinter = pp; - schema = sch; - characterEscapes = esc; - rootValueSeparator = rootSep; - } - - public GeneratorSettings with(PrettyPrinter pp) { - // since null would mean "don't care", need to use placeholder to indicate "disable" - if (pp == null) { - pp = NULL_PRETTY_PRINTER; - } - return (pp == prettyPrinter) ? this - : new GeneratorSettings(pp, schema, characterEscapes, rootValueSeparator); - } - - public GeneratorSettings with(FormatSchema sch) { - return (schema == sch) ? this - : new GeneratorSettings(prettyPrinter, sch, characterEscapes, rootValueSeparator); - } - - public GeneratorSettings with(CharacterEscapes esc) { - return (characterEscapes == esc) ? this - : new GeneratorSettings(prettyPrinter, schema, esc, rootValueSeparator); - } - - public GeneratorSettings withRootValueSeparator(String sep) { - if (sep == null) { - if (rootValueSeparator == null) { - return this; - } - return new GeneratorSettings(prettyPrinter, schema, characterEscapes, null); - } - if (sep.equals(_rootValueSeparatorAsString())) { - return this; - } - return new GeneratorSettings(prettyPrinter, schema, characterEscapes, - new SerializedString(sep)); - } - - public GeneratorSettings withRootValueSeparator(SerializableString sep) { - if (sep == null) { - if (rootValueSeparator == null) { - return this; - } - return new GeneratorSettings(prettyPrinter, schema, characterEscapes, null); - } - if (sep.equals(rootValueSeparator)) { - return this; - } - return new GeneratorSettings(prettyPrinter, schema, characterEscapes, sep); - } - - private final String _rootValueSeparatorAsString() { - return (rootValueSeparator == null) ? null : rootValueSeparator.getValue(); - } - - /** - * @since 2.6 - */ - public void initialize(JsonGenerator gen) - { - PrettyPrinter pp = prettyPrinter; - if (prettyPrinter != null) { - if (pp == NULL_PRETTY_PRINTER) { - gen.setPrettyPrinter(null); - } else { - if (pp instanceof Instantiatable) { - pp = (PrettyPrinter) ((Instantiatable) pp).createInstance(); - } - gen.setPrettyPrinter(pp); - } - } - if (characterEscapes != null) { - gen.setCharacterEscapes(characterEscapes); - } - if (schema != null) { - gen.setSchema(schema); - } - if (rootValueSeparator != null) { - gen.setRootValueSeparator(rootValueSeparator); - } - } - } - /** * As a minor optimization, we will make an effort to pre-fetch a serializer, * or at least relevant TypeSerializer, if given enough * information. - * - * @since 2.5 */ public final static class Prefetch implements java.io.Serializable @@ -1356,7 +1110,7 @@ public Prefetch forRootType(ObjectWriter parent, JavaType newType) { // serializer for polymorphic types, it is actually more efficient to do the // reverse here. try { - JsonSerializer ser = prov.findTypedValueSerializer(newType, true, null); + JsonSerializer ser = prov.findTypedValueSerializer(newType, true); // Important: for polymorphic types, "unwrap"... if (ser instanceof TypeWrappedSerializer) { return new Prefetch(newType, null, diff --git a/src/main/java/com/fasterxml/jackson/databind/PropertyMetadata.java b/src/main/java/com/fasterxml/jackson/databind/PropertyMetadata.java index 9b57186c2f..6e46aa1c78 100644 --- a/src/main/java/com/fasterxml/jackson/databind/PropertyMetadata.java +++ b/src/main/java/com/fasterxml/jackson/databind/PropertyMetadata.java @@ -8,8 +8,6 @@ * properties. Carved out to reduce number of distinct properties that * actual property implementations and place holders need to store; * since instances are immutable, they can be freely shared. - * - * @since 2.3 */ public class PropertyMetadata implements java.io.Serializable @@ -28,8 +26,6 @@ public class PropertyMetadata /** * Helper class used for containing information about expected merge * information for this property, if merging is expected. - * - * @since 2.9 */ public final static class MergeInfo // NOTE: need not be Serializable, not persisted @@ -129,9 +125,6 @@ protected PropertyMetadata(Boolean req, String desc, Integer index, String def, _contentNulls = contentNulls; } - /** - * @since 2.8.8 - */ public static PropertyMetadata construct(Boolean req, String desc, Integer index, String defaultValue) { if ((desc != null) || (index != null) || (defaultValue != null)) { @@ -144,16 +137,6 @@ public static PropertyMetadata construct(Boolean req, String desc, Integer index return req ? STD_REQUIRED : STD_OPTIONAL; } - @Deprecated // since 2.8.8 - public static PropertyMetadata construct(boolean req, String desc, Integer index, - String defaultValue) { - if (desc != null || index != null || defaultValue != null) { - return new PropertyMetadata(req, desc, index, defaultValue, - null, null, null); - } - return req ? STD_REQUIRED : STD_OPTIONAL; - } - /** * Minor optimization: let's canonicalize back to placeholders in cases * where there is no real data to consider @@ -176,17 +159,11 @@ public PropertyMetadata withDescription(String desc) { _mergeInfo, _valueNulls, _contentNulls); } - /** - * @since 2.9 - */ public PropertyMetadata withMergeInfo(MergeInfo mergeInfo) { return new PropertyMetadata(_required, _description, _index, _defaultValue, mergeInfo, _valueNulls, _contentNulls); } - /** - * @since 2.9 - */ public PropertyMetadata withNulls(Nulls valueNulls, Nulls contentNulls) { return new PropertyMetadata(_required, _description, _index, _defaultValue, diff --git a/src/main/java/com/fasterxml/jackson/databind/PropertyName.java b/src/main/java/com/fasterxml/jackson/databind/PropertyName.java index 5fcd044007..ab0d8dab04 100644 --- a/src/main/java/com/fasterxml/jackson/databind/PropertyName.java +++ b/src/main/java/com/fasterxml/jackson/databind/PropertyName.java @@ -5,15 +5,15 @@ import com.fasterxml.jackson.core.util.InternCache; import com.fasterxml.jackson.databind.cfg.MapperConfig; import com.fasterxml.jackson.databind.util.ClassUtil; +import com.fasterxml.jackson.databind.util.FullyNamed; /** * Simple value class used for containing names of properties as defined * by annotations (and possibly other configuration sources). - * - * @since 2.1 */ public class PropertyName - implements java.io.Serializable + implements FullyNamed, + java.io.Serializable { private static final long serialVersionUID = 1L; // 2.5 @@ -34,7 +34,9 @@ public class PropertyName * commonly this value disables behavior for which name would be needed. */ public final static PropertyName NO_NAME = new PropertyName(new String(_NO_NAME), null); - + + private final static InternCache INTERNER = InternCache.instance; + /** * Basic name of the property. */ @@ -52,8 +54,6 @@ public class PropertyName * NOTE: not defined as volatile to avoid performance problem with * concurrent access in multi-core environments; due to statelessness * of {@link SerializedString} at most leads to multiple instantiations. - * - * @since 2.4 */ protected SerializableString _encodedSimple; @@ -84,15 +84,12 @@ protected Object readResolve() { return this; } - /** - * @since 2.6 - */ public static PropertyName construct(String simpleName) { if (simpleName == null || simpleName.length() == 0) { return USE_DEFAULT; } - return new PropertyName(InternCache.instance.intern(simpleName), null); + return new PropertyName(INTERNER.intern(simpleName), null); } public static PropertyName construct(String simpleName, String ns) @@ -103,7 +100,7 @@ public static PropertyName construct(String simpleName, String ns) if (ns == null && simpleName.length() == 0) { return USE_DEFAULT; } - return new PropertyName(InternCache.instance.intern(simpleName), ns); + return new PropertyName(INTERNER.intern(simpleName), ns); } public PropertyName internSimpleName() @@ -148,6 +145,22 @@ public PropertyName withNamespace(String ns) { return new PropertyName(_simpleName, ns); } + /* + /********************************************************** + /* FullyNamed impl + /********************************************************** + */ + + @Override + public String getName() { + return _simpleName; + } + + @Override + public PropertyName getFullName() { + return this; + } + /* /********************************************************** /* Accessors @@ -161,8 +174,6 @@ public String getSimpleName() { /** * Accessor that may be used to get lazily-constructed efficient * representation of the simple name. - * - * @since 2.4 */ public SerializableString simpleAsEncoded(MapperConfig config) { SerializableString sstr = _encodedSimple; diff --git a/src/main/java/com/fasterxml/jackson/databind/PropertyNamingStrategy.java b/src/main/java/com/fasterxml/jackson/databind/PropertyNamingStrategy.java index e3024264a8..316d49de5d 100644 --- a/src/main/java/com/fasterxml/jackson/databind/PropertyNamingStrategy.java +++ b/src/main/java/com/fasterxml/jackson/databind/PropertyNamingStrategy.java @@ -27,24 +27,20 @@ * characters). */ @SuppressWarnings("serial") -public class PropertyNamingStrategy // NOTE: was abstract until 2.7 +public class PropertyNamingStrategy implements java.io.Serializable { /** * Naming convention used in languages like C, where words are in lower-case * letters, separated by underscores. * See {@link SnakeCaseStrategy} for details. - * - * @since 2.7 (was formerly called {@link #CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES}) */ public static final PropertyNamingStrategy SNAKE_CASE = new SnakeCaseStrategy(); /** * Naming convention used in languages like Pascal, where words are capitalized * and no separator is used between words. - * See {@link PascalCaseStrategy} for details. - * - * @since 2.7 (was formerly called {@link #PASCAL_CASE_TO_CAMEL_CASE}) + * See {@link UpperCamelCaseStrategy} for details. */ public static final PropertyNamingStrategy UPPER_CAMEL_CASE = new UpperCamelCaseStrategy(); @@ -53,8 +49,6 @@ public class PropertyNamingStrategy // NOTE: was abstract until 2.7 * and no separator is used between words. Since this is the native Java naming convention, * naming strategy will not do any transformation between names in data (JSON) and * POJOS. - * - * @since 2.7 (was formerly called {@link #PASCAL_CASE_TO_CAMEL_CASE}) */ public static final PropertyNamingStrategy LOWER_CAMEL_CASE = new PropertyNamingStrategy(); @@ -62,8 +56,6 @@ public class PropertyNamingStrategy // NOTE: was abstract until 2.7 * Naming convention in which all words of the logical name are in lower case, and * no separator is used between words. * See {@link LowerCaseStrategy} for details. - * - * @since 2.4 */ public static final PropertyNamingStrategy LOWER_CASE = new LowerCaseStrategy(); @@ -71,11 +63,16 @@ public class PropertyNamingStrategy // NOTE: was abstract until 2.7 * Naming convention used in languages like Lisp, where words are in lower-case * letters, separated by hyphens. * See {@link KebabCaseStrategy} for details. - * - * @since 2.7 */ public static final PropertyNamingStrategy KEBAB_CASE = new KebabCaseStrategy(); + /** + * Naming convention widely used as configuration properties name, where words are in + * lower-case letters, separated by dots. + * See {@link LowerCaseWithDotsStrategy} for details. + */ + public static final PropertyNamingStrategy LOWER_CASE_WITH_DOTS = new LowerCaseWithDotsStrategy(); + /* /********************************************************** /* API @@ -195,8 +192,7 @@ public String nameForConstructorParameter(MapperConfig config, AnnotatedParam public abstract String translate(String propertyName); } - - + /* /********************************************************** /* Standard implementations @@ -251,15 +247,14 @@ public String nameForConstructorParameter(MapperConfig config, AnnotatedParam * (the first of two underscores was removed) *
  • "user__name" is translated to "user__name" * (unchanged, with two underscores)
  • - * - * @since 2.7 (was previously called } */ public static class SnakeCaseStrategy extends PropertyNamingStrategyBase { @Override public String translate(String input) { - if (input == null) return input; // garbage in, garbage out + if (input == null) + return input; // garbage in, garbage out int length = input.length(); StringBuilder result = new StringBuilder(length * 2); int resultLength = 0; @@ -303,8 +298,6 @@ public String translate(String input) * This rules result in the following example translation from * Java property names to JSON element names. *
    • "userName" is translated to "UserName"
    - * - * @since 2.7 (was formerly called {@link PascalCaseStrategy}) */ public static class UpperCamelCaseStrategy extends PropertyNamingStrategyBase { @@ -354,77 +347,62 @@ public String translate(String input) { * Naming strategy similar to {@link SnakeCaseStrategy}, but instead of underscores * as separators, uses hyphens. Naming convention traditionally used for languages * like Lisp. - * - * @since 2.7 */ public static class KebabCaseStrategy extends PropertyNamingStrategyBase { @Override - public String translate(String input) - { - if (input == null) return input; // garbage in, garbage out - int length = input.length(); - if (length == 0) { - return input; - } - - StringBuilder result = new StringBuilder(length + (length >> 1)); - - int upperCount = 0; - - for (int i = 0; i < length; ++i) { - char ch = input.charAt(i); - char lc = Character.toLowerCase(ch); - - if (lc == ch) { // lower-case letter means we can get new word - // but need to check for multi-letter upper-case (acronym), where assumption - // is that the last upper-case char is start of a new word - if (upperCount > 1) { - // so insert hyphen before the last character now - result.insert(result.length() - 1, '-'); - } - upperCount = 0; - } else { - // Otherwise starts new word, unless beginning of string - if ((upperCount == 0) && (i > 0)) { - result.append('-'); - } - ++upperCount; - } - result.append(lc); - } - return result.toString(); + public String translate(String input){ + return translateLowerCaseWithSeparator(input, '-'); } } - - /* - /********************************************************** - /* Deprecated variants, aliases - /********************************************************** - */ - - /** - * @deprecated Since 2.7 use {@link #SNAKE_CASE} instead; - */ - @Deprecated // since 2.7 - public static final PropertyNamingStrategy CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES = SNAKE_CASE; /** - * @deprecated Since 2.7 use {@link #UPPER_CAMEL_CASE} instead; + * Naming strategy similar to {@link KebabCaseStrategy}, but instead of hyphens + * as separators, uses dots. Naming convention widely used as configuration properties name. */ - @Deprecated // since 2.7 - public static final PropertyNamingStrategy PASCAL_CASE_TO_CAMEL_CASE = UPPER_CAMEL_CASE; + public static class LowerCaseWithDotsStrategy extends PropertyNamingStrategyBase { + @Override + public String translate(String input){ + return translateLowerCaseWithSeparator(input, '.'); + } + } - /** - * @deprecated In 2.7 use {@link SnakeCaseStrategy} instead - */ - @Deprecated - public static class LowerCaseWithUnderscoresStrategy extends SnakeCaseStrategy {} + static String translateLowerCaseWithSeparator(String input, char separator){ + if (input == null) { + return input; // garbage in, garbage out + } - /** - * @deprecated In 2.7 use {@link UpperCamelCaseStrategy} instead - */ - @Deprecated - public static class PascalCaseStrategy extends UpperCamelCaseStrategy {} + int length = input.length(); + if (length == 0) { + return input; + } + + StringBuilder result = new StringBuilder(length + (length >> 1)); + + int upperCount = 0; + + for (int i = 0; i < length; ++i) { + char ch = input.charAt(i); + char lc = Character.toLowerCase(ch); + + if (lc == ch) { // lower-case letter means we can get new word + // but need to check for multi-letter upper-case (acronym), where assumption + // is that the last upper-case char is start of a new word + if (upperCount > 1) { + // so insert hyphen before the last character now + result.insert(result.length() - 1, separator); + } + upperCount = 0; + } else { + // Otherwise starts new word, unless beginning of string + if ((upperCount == 0) && (i > 0)) { + result.append(separator); + } + ++upperCount; + } + result.append(lc); + } + return result.toString(); + } } diff --git a/src/main/java/com/fasterxml/jackson/databind/SequenceWriter.java b/src/main/java/com/fasterxml/jackson/databind/SequenceWriter.java index 3902cf6433..02c1e4064a 100644 --- a/src/main/java/com/fasterxml/jackson/databind/SequenceWriter.java +++ b/src/main/java/com/fasterxml/jackson/databind/SequenceWriter.java @@ -25,16 +25,14 @@ *
  • Explicit {@link #close} is needed after all values have been written * ({@link ObjectWriter} can auto-close after individual value writes) * - * - * @since 2.5 */ public class SequenceWriter implements Versioned, java.io.Closeable, java.io.Flushable { /* - /********************************************************** + /********************************************************************** /* Configuration - /********************************************************** + /********************************************************************** */ protected final DefaultSerializerProvider _provider; @@ -49,9 +47,9 @@ public class SequenceWriter protected final boolean _cfgCloseCloseable; /* - /********************************************************** + /********************************************************************** /* State - /********************************************************** + /********************************************************************** */ /** @@ -70,9 +68,9 @@ public class SequenceWriter protected boolean _closed; /* - /********************************************************** + /********************************************************************** /* Life-cycle - /********************************************************** + /********************************************************************** */ public SequenceWriter(DefaultSerializerProvider prov, JsonGenerator gen, @@ -103,9 +101,9 @@ public SequenceWriter init(boolean wrapInArray) throws IOException } /* - /********************************************************** + /********************************************************************** /* Public API, basic accessors - /********************************************************** + /********************************************************************** */ /** @@ -118,9 +116,9 @@ public Version version() { } /* - /********************************************************** + /********************************************************************** /* Public API, write operations, related - /********************************************************** + /********************************************************************** */ /** @@ -239,9 +237,9 @@ public void close() throws IOException } /* - /********************************************************** + /********************************************************************** /* Internal helper methods, serializer lookups - /********************************************************** + /********************************************************************** */ protected SequenceWriter _writeCloseableValue(Object value) throws IOException @@ -306,7 +304,8 @@ private final JsonSerializer _findAndAddDynamic(Class type) throws Js result = _dynamicSerializers.findAndAddRootValueSerializer(type, _provider); } else { result = _dynamicSerializers.addSerializer(type, - new TypeWrappedSerializer(_typeSerializer, _provider.findValueSerializer(type, null))); + new TypeWrappedSerializer(_typeSerializer, + _provider.findRootValueSerializer(type))); } _dynamicSerializers = result.map; return result.serializer; @@ -319,7 +318,8 @@ private final JsonSerializer _findAndAddDynamic(JavaType type) throws Js result = _dynamicSerializers.findAndAddRootValueSerializer(type, _provider); } else { result = _dynamicSerializers.addSerializer(type, - new TypeWrappedSerializer(_typeSerializer, _provider.findValueSerializer(type, null))); + new TypeWrappedSerializer(_typeSerializer, + _provider.findRootValueSerializer(type))); } _dynamicSerializers = result.map; return result.serializer; diff --git a/src/main/java/com/fasterxml/jackson/databind/SerializationConfig.java b/src/main/java/com/fasterxml/jackson/databind/SerializationConfig.java index 2bd53e1b76..ef708cc681 100644 --- a/src/main/java/com/fasterxml/jackson/databind/SerializationConfig.java +++ b/src/main/java/com/fasterxml/jackson/databind/SerializationConfig.java @@ -2,18 +2,16 @@ import java.text.DateFormat; -import com.fasterxml.jackson.annotation.*; - import com.fasterxml.jackson.core.*; -import com.fasterxml.jackson.core.json.JsonWriteFeature; -import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; import com.fasterxml.jackson.core.util.Instantiatable; import com.fasterxml.jackson.databind.cfg.*; -import com.fasterxml.jackson.databind.introspect.SimpleMixInResolver; +import com.fasterxml.jackson.databind.introspect.ClassIntrospector; +import com.fasterxml.jackson.databind.introspect.MixInHandler; import com.fasterxml.jackson.databind.jsontype.SubtypeResolver; import com.fasterxml.jackson.databind.ser.FilterProvider; import com.fasterxml.jackson.databind.ser.SerializerFactory; +import com.fasterxml.jackson.databind.type.TypeFactory; import com.fasterxml.jackson.databind.util.RootNameLookup; /** @@ -29,18 +27,14 @@ */ public final class SerializationConfig extends MapperConfigBase - implements java.io.Serializable // since 2.1 + implements java.io.Serializable { - // since 2.5 - private static final long serialVersionUID = 1; - - // since 2.6 - protected final static PrettyPrinter DEFAULT_PRETTY_PRINTER = new DefaultPrettyPrinter(); + private static final long serialVersionUID = 3L; /* - /********************************************************** + /********************************************************************** /* Configured helper objects - /********************************************************** + /********************************************************************** */ /** @@ -52,15 +46,13 @@ public final class SerializationConfig /** * If "default pretty-printing" is enabled, it will create the instance * from this blueprint object. - * - * @since 2.6 */ protected final PrettyPrinter _defaultPrettyPrinter; /* - /********************************************************** - /* Serialization features - /********************************************************** + /********************************************************************** + /* Feature flags + /********************************************************************** */ /** @@ -68,111 +60,56 @@ public final class SerializationConfig */ protected final int _serFeatures; - /* - /********************************************************** - /* Generator features: generic, format-specific - /********************************************************** - */ /** - * States of {@link com.fasterxml.jackson.core.JsonGenerator.Feature}s to enable/disable. + * States of {@link com.fasterxml.jackson.core.StreamWriteFeature}s to enable/disable. */ - protected final int _generatorFeatures; - - /** - * Bitflag of {@link com.fasterxml.jackson.core.JsonGenerator.Feature}s to enable/disable - */ - protected final int _generatorFeaturesToChange; + protected final int _streamWriteFeatures; /** * States of {@link com.fasterxml.jackson.core.FormatFeature}s to enable/disable. - * - * @since 2.7 */ protected final int _formatWriteFeatures; - /** - * Bitflag of {@link com.fasterxml.jackson.core.FormatFeature}s to enable/disable - * - * @since 2.7 - */ - protected final int _formatWriteFeaturesToChange; - /* - /********************************************************** + /********************************************************************** /* Life-cycle, primary constructors for new instances - /********************************************************** - */ - - /** - * Constructor used by ObjectMapper to create default configuration object instance. - * - * @since 2.9 + /********************************************************************** */ - public SerializationConfig(BaseSettings base, - SubtypeResolver str, SimpleMixInResolver mixins, RootNameLookup rootNames, - ConfigOverrides configOverrides) - { - super(base, str, mixins, rootNames, configOverrides); - _serFeatures = collectFeatureDefaults(SerializationFeature.class); - _filterProvider = null; - _defaultPrettyPrinter = DEFAULT_PRETTY_PRINTER; - _generatorFeatures = 0; - _generatorFeaturesToChange = 0; - _formatWriteFeatures = 0; - _formatWriteFeaturesToChange = 0; - } /** - * Copy-constructor used for making a copy to be used by new {@link ObjectMapper}. - * - * @since 2.9 + * @since 3.0 */ - protected SerializationConfig(SerializationConfig src, - SimpleMixInResolver mixins, RootNameLookup rootNames, - ConfigOverrides configOverrides) + public SerializationConfig(MapperBuilder b, + int mapperFeatures, int serFeatures, int streamWriteFeatures, int formatWriteFeatures, + ConfigOverrides configOverrides, + TypeFactory tf, ClassIntrospector classIntr, MixInHandler mixins, SubtypeResolver str, + RootNameLookup rootNames, + FilterProvider filterProvider) { - super(src, mixins, rootNames, configOverrides); - _serFeatures = src._serFeatures; - _filterProvider = src._filterProvider; - _defaultPrettyPrinter = src._defaultPrettyPrinter; - _generatorFeatures = src._generatorFeatures; - _generatorFeaturesToChange = src._generatorFeaturesToChange; - _formatWriteFeatures = src._formatWriteFeatures; - _formatWriteFeaturesToChange = src._formatWriteFeaturesToChange; + super(b, mapperFeatures, tf, classIntr, mixins, str, configOverrides, rootNames); + _serFeatures = serFeatures; + _filterProvider = filterProvider; + _streamWriteFeatures = streamWriteFeatures; + _formatWriteFeatures = formatWriteFeatures; + _defaultPrettyPrinter = b.defaultPrettyPrinter(); } /* - /********************************************************** + /********************************************************************** /* Life-cycle, secondary constructors to support /* "mutant factories", with single property changes - /********************************************************** + /********************************************************************** */ - private SerializationConfig(SerializationConfig src, SubtypeResolver str) - { - super(src, str); - _serFeatures = src._serFeatures; - _filterProvider = src._filterProvider; - _defaultPrettyPrinter = src._defaultPrettyPrinter; - _generatorFeatures = src._generatorFeatures; - _generatorFeaturesToChange = src._generatorFeaturesToChange; - _formatWriteFeatures = src._formatWriteFeatures; - _formatWriteFeaturesToChange = src._formatWriteFeaturesToChange; - } - private SerializationConfig(SerializationConfig src, - int mapperFeatures, int serFeatures, - int generatorFeatures, int generatorFeatureMask, - int formatFeatures, int formatFeaturesMask) + int serFeatures, int streamWriteFeatures, int formatWriteFeatures) { - super(src, mapperFeatures); + super(src); _serFeatures = serFeatures; _filterProvider = src._filterProvider; _defaultPrettyPrinter = src._defaultPrettyPrinter; - _generatorFeatures = generatorFeatures; - _generatorFeaturesToChange = generatorFeatureMask; - _formatWriteFeatures = formatFeatures; - _formatWriteFeaturesToChange = formatFeaturesMask; + _streamWriteFeatures = streamWriteFeatures; + _formatWriteFeatures = formatWriteFeatures; } private SerializationConfig(SerializationConfig src, BaseSettings base) @@ -181,10 +118,8 @@ private SerializationConfig(SerializationConfig src, BaseSettings base) _serFeatures = src._serFeatures; _filterProvider = src._filterProvider; _defaultPrettyPrinter = src._defaultPrettyPrinter; - _generatorFeatures = src._generatorFeatures; - _generatorFeaturesToChange = src._generatorFeaturesToChange; + _streamWriteFeatures = src._streamWriteFeatures; _formatWriteFeatures = src._formatWriteFeatures; - _formatWriteFeaturesToChange = src._formatWriteFeaturesToChange; } private SerializationConfig(SerializationConfig src, FilterProvider filters) @@ -193,10 +128,8 @@ private SerializationConfig(SerializationConfig src, FilterProvider filters) _serFeatures = src._serFeatures; _filterProvider = filters; _defaultPrettyPrinter = src._defaultPrettyPrinter; - _generatorFeatures = src._generatorFeatures; - _generatorFeaturesToChange = src._generatorFeaturesToChange; + _streamWriteFeatures = src._streamWriteFeatures; _formatWriteFeatures = src._formatWriteFeatures; - _formatWriteFeaturesToChange = src._formatWriteFeaturesToChange; } private SerializationConfig(SerializationConfig src, Class view) @@ -205,10 +138,8 @@ private SerializationConfig(SerializationConfig src, Class view) _serFeatures = src._serFeatures; _filterProvider = src._filterProvider; _defaultPrettyPrinter = src._defaultPrettyPrinter; - _generatorFeatures = src._generatorFeatures; - _generatorFeaturesToChange = src._generatorFeaturesToChange; + _streamWriteFeatures = src._streamWriteFeatures; _formatWriteFeatures = src._formatWriteFeatures; - _formatWriteFeaturesToChange = src._formatWriteFeaturesToChange; } private SerializationConfig(SerializationConfig src, PropertyName rootName) @@ -217,75 +148,41 @@ private SerializationConfig(SerializationConfig src, PropertyName rootName) _serFeatures = src._serFeatures; _filterProvider = src._filterProvider; _defaultPrettyPrinter = src._defaultPrettyPrinter; - _generatorFeatures = src._generatorFeatures; - _generatorFeaturesToChange = src._generatorFeaturesToChange; + _streamWriteFeatures = src._streamWriteFeatures; _formatWriteFeatures = src._formatWriteFeatures; - _formatWriteFeaturesToChange = src._formatWriteFeaturesToChange; } - /** - * @since 2.1 - */ protected SerializationConfig(SerializationConfig src, ContextAttributes attrs) { super(src, attrs); _serFeatures = src._serFeatures; _filterProvider = src._filterProvider; _defaultPrettyPrinter = src._defaultPrettyPrinter; - _generatorFeatures = src._generatorFeatures; - _generatorFeaturesToChange = src._generatorFeaturesToChange; + _streamWriteFeatures = src._streamWriteFeatures; _formatWriteFeatures = src._formatWriteFeatures; - _formatWriteFeaturesToChange = src._formatWriteFeaturesToChange; } - /** - * @since 2.1 - */ - protected SerializationConfig(SerializationConfig src, SimpleMixInResolver mixins) - { - super(src, mixins); - _serFeatures = src._serFeatures; - _filterProvider = src._filterProvider; - _defaultPrettyPrinter = src._defaultPrettyPrinter; - _generatorFeatures = src._generatorFeatures; - _generatorFeaturesToChange = src._generatorFeaturesToChange; - _formatWriteFeatures = src._formatWriteFeatures; - _formatWriteFeaturesToChange = src._formatWriteFeaturesToChange; - } - - /** - * @since 2.6 - */ protected SerializationConfig(SerializationConfig src, PrettyPrinter defaultPP) { super(src); _serFeatures = src._serFeatures; _filterProvider = src._filterProvider; _defaultPrettyPrinter = defaultPP; - _generatorFeatures = src._generatorFeatures; - _generatorFeaturesToChange = src._generatorFeaturesToChange; + _streamWriteFeatures = src._streamWriteFeatures; _formatWriteFeatures = src._formatWriteFeatures; - _formatWriteFeaturesToChange = src._formatWriteFeaturesToChange; } /* - /********************************************************** + /********************************************************************** /* Life-cycle, factory methods from MapperConfig(Base) - /********************************************************** + /********************************************************************** */ - @Override // since 2.9 + @Override protected final SerializationConfig _withBase(BaseSettings newBase) { return (_base == newBase) ? this : new SerializationConfig(this, newBase); } - @Override // since 2.9 - protected final SerializationConfig _withMapperFeatures(int mapperFeatures) { - return new SerializationConfig(this, mapperFeatures, _serFeatures, - _generatorFeatures, _generatorFeaturesToChange, - _formatWriteFeatures, _formatWriteFeaturesToChange); - } - @Override public SerializationConfig withRootName(PropertyName rootName) { if (rootName == null) { @@ -298,11 +195,6 @@ public SerializationConfig withRootName(PropertyName rootName) { return new SerializationConfig(this, rootName); } - @Override - public SerializationConfig with(SubtypeResolver str) { - return (str == _subtypeResolver)? this : new SerializationConfig(this, str); - } - @Override public SerializationConfig withView(Class view) { return (_view == view) ? this : new SerializationConfig(this, view); @@ -314,9 +206,9 @@ public SerializationConfig with(ContextAttributes attrs) { } /* - /********************************************************** + /********************************************************************** /* Factory method overrides - /********************************************************** + /********************************************************************** */ /** @@ -335,9 +227,9 @@ public SerializationConfig with(DateFormat df) { } /* - /********************************************************** + /********************************************************************** /* Factory methods for SerializationFeature - /********************************************************** + /********************************************************************** */ /** @@ -348,9 +240,8 @@ public SerializationConfig with(SerializationFeature feature) { int newSerFeatures = _serFeatures | feature.getMask(); return (newSerFeatures == _serFeatures) ? this - : new SerializationConfig(this, _mapperFeatures, newSerFeatures, - _generatorFeatures, _generatorFeaturesToChange, - _formatWriteFeatures, _formatWriteFeaturesToChange); + : new SerializationConfig(this, + newSerFeatures, _streamWriteFeatures, _formatWriteFeatures); } /** @@ -364,9 +255,8 @@ public SerializationConfig with(SerializationFeature first, SerializationFeature newSerFeatures |= f.getMask(); } return (newSerFeatures == _serFeatures) ? this - : new SerializationConfig(this, _mapperFeatures, newSerFeatures, - _generatorFeatures, _generatorFeaturesToChange, - _formatWriteFeatures, _formatWriteFeaturesToChange); + : new SerializationConfig(this, + newSerFeatures, _streamWriteFeatures, _formatWriteFeatures); } /** @@ -380,9 +270,8 @@ public SerializationConfig withFeatures(SerializationFeature... features) newSerFeatures |= f.getMask(); } return (newSerFeatures == _serFeatures) ? this - : new SerializationConfig(this, _mapperFeatures, newSerFeatures, - _generatorFeatures, _generatorFeaturesToChange, - _formatWriteFeatures, _formatWriteFeaturesToChange); + : new SerializationConfig(this, + newSerFeatures, _streamWriteFeatures, _formatWriteFeatures); } /** @@ -393,9 +282,8 @@ public SerializationConfig without(SerializationFeature feature) { int newSerFeatures = _serFeatures & ~feature.getMask(); return (newSerFeatures == _serFeatures) ? this - : new SerializationConfig(this, _mapperFeatures, newSerFeatures, - _generatorFeatures, _generatorFeaturesToChange, - _formatWriteFeatures, _formatWriteFeaturesToChange); + : new SerializationConfig(this, + newSerFeatures, _streamWriteFeatures, _formatWriteFeatures); } /** @@ -409,9 +297,8 @@ public SerializationConfig without(SerializationFeature first, SerializationFeat newSerFeatures &= ~f.getMask(); } return (newSerFeatures == _serFeatures) ? this - : new SerializationConfig(this, _mapperFeatures, newSerFeatures, - _generatorFeatures, _generatorFeaturesToChange, - _formatWriteFeatures, _formatWriteFeaturesToChange); + : new SerializationConfig(this, newSerFeatures, + _streamWriteFeatures, _formatWriteFeatures); } /** @@ -425,273 +312,147 @@ public SerializationConfig withoutFeatures(SerializationFeature... features) newSerFeatures &= ~f.getMask(); } return (newSerFeatures == _serFeatures) ? this - : new SerializationConfig(this, _mapperFeatures, newSerFeatures, - _generatorFeatures, _generatorFeaturesToChange, - _formatWriteFeatures, _formatWriteFeaturesToChange); + : new SerializationConfig(this, newSerFeatures, + _streamWriteFeatures, _formatWriteFeatures); } /* - /********************************************************** - /* Factory methods for JsonGenerator.Feature (2.5) - /********************************************************** + /********************************************************************** + /* Factory methods for StreamWriteFeature + /********************************************************************** */ + /** * Fluent factory method that will construct and return a new configuration * object instance with specified feature enabled. - * - * @since 2.5 */ - public SerializationConfig with(JsonGenerator.Feature feature) + public SerializationConfig with(StreamWriteFeature feature) { - int newSet = _generatorFeatures | feature.getMask(); - int newMask = _generatorFeaturesToChange | feature.getMask(); - return ((_generatorFeatures == newSet) && (_generatorFeaturesToChange == newMask)) ? this : - new SerializationConfig(this, _mapperFeatures, _serFeatures, - newSet, newMask, - _formatWriteFeatures, _formatWriteFeaturesToChange); + int newSet = _streamWriteFeatures | feature.getMask(); + return (_streamWriteFeatures == newSet) ? this : + new SerializationConfig(this, _serFeatures, newSet, + _formatWriteFeatures); } /** * Fluent factory method that will construct and return a new configuration * object instance with specified features enabled. - * - * @since 2.5 */ - public SerializationConfig withFeatures(JsonGenerator.Feature... features) + public SerializationConfig withFeatures(StreamWriteFeature... features) { - int newSet = _generatorFeatures; - int newMask = _generatorFeaturesToChange; - for (JsonGenerator.Feature f : features) { - int mask = f.getMask(); - newSet |= mask; - newMask |= mask; + int newSet = _streamWriteFeatures; + for (StreamWriteFeature f : features) { + newSet |= f.getMask(); } - return ((_generatorFeatures == newSet) && (_generatorFeaturesToChange == newMask)) ? this : - new SerializationConfig(this, _mapperFeatures, _serFeatures, - newSet, newMask, - _formatWriteFeatures, _formatWriteFeaturesToChange); + return (_streamWriteFeatures == newSet) ? this : + new SerializationConfig(this, _serFeatures, newSet, + _formatWriteFeatures); } /** * Fluent factory method that will construct and return a new configuration * object instance with specified feature disabled. - * - * @since 2.5 */ - public SerializationConfig without(JsonGenerator.Feature feature) + public SerializationConfig without(StreamWriteFeature feature) { - int newSet = _generatorFeatures & ~feature.getMask(); - int newMask = _generatorFeaturesToChange | feature.getMask(); - return ((_generatorFeatures == newSet) && (_generatorFeaturesToChange == newMask)) ? this : - new SerializationConfig(this, _mapperFeatures, _serFeatures, - newSet, newMask, - _formatWriteFeatures, _formatWriteFeaturesToChange); + int newSet = _streamWriteFeatures & ~feature.getMask(); + return (_streamWriteFeatures == newSet) ? this : + new SerializationConfig(this, _serFeatures, newSet, + _formatWriteFeatures); } /** * Fluent factory method that will construct and return a new configuration * object instance with specified features disabled. - * - * @since 2.5 */ - public SerializationConfig withoutFeatures(JsonGenerator.Feature... features) + public SerializationConfig withoutFeatures(StreamWriteFeature... features) { - int newSet = _generatorFeatures; - int newMask = _generatorFeaturesToChange; - for (JsonGenerator.Feature f : features) { - int mask = f.getMask(); - newSet &= ~mask; - newMask |= mask; + int newSet = _streamWriteFeatures; + for (StreamWriteFeature f : features) { + newSet &= ~f.getMask(); } - return ((_generatorFeatures == newSet) && (_generatorFeaturesToChange == newMask)) ? this : - new SerializationConfig(this, _mapperFeatures, _serFeatures, - newSet, newMask, - _formatWriteFeatures, _formatWriteFeaturesToChange); + return (_streamWriteFeatures == newSet) ? this : + new SerializationConfig(this, _serFeatures, newSet, + _formatWriteFeatures); } /* - /********************************************************** - /* Factory methods for FormatFeature (2.7) - /********************************************************** + /********************************************************************** + /* Factory methods for FormatFeature + /********************************************************************** */ + /** * Fluent factory method that will construct and return a new configuration * object instance with specified feature enabled. - * - * @since 2.7 */ public SerializationConfig with(FormatFeature feature) { - // 27-Oct-2018, tatu: Alas, complexity due to newly (2.10) refactored json-features: - if (feature instanceof JsonWriteFeature) { - return _withJsonWriteFeatures(feature); - } int newSet = _formatWriteFeatures | feature.getMask(); - int newMask = _formatWriteFeaturesToChange | feature.getMask(); - return ((_formatWriteFeatures == newSet) && (_formatWriteFeaturesToChange == newMask)) ? this : - new SerializationConfig(this, _mapperFeatures, _serFeatures, - _generatorFeatures, _generatorFeaturesToChange, - newSet, newMask); + return (_formatWriteFeatures == newSet) ? this : + new SerializationConfig(this, + _serFeatures, _streamWriteFeatures, newSet); } /** * Fluent factory method that will construct and return a new configuration * object instance with specified features enabled. - * - * @since 2.7 */ public SerializationConfig withFeatures(FormatFeature... features) { - // 27-Oct-2018, tatu: Alas, complexity due to newly (2.10) refactored json-features: - if (features.length > 0 && (features[0] instanceof JsonWriteFeature)) { - return _withJsonWriteFeatures(features); - } int newSet = _formatWriteFeatures; - int newMask = _formatWriteFeaturesToChange; for (FormatFeature f : features) { - int mask = f.getMask(); - newSet |= mask; - newMask |= mask; + newSet |= f.getMask(); } - return ((_formatWriteFeatures == newSet) && (_formatWriteFeaturesToChange == newMask)) ? this : - new SerializationConfig(this, _mapperFeatures, _serFeatures, - _generatorFeatures, _generatorFeaturesToChange, - newSet, newMask); + return (_formatWriteFeatures == newSet) ? this : + new SerializationConfig(this, + _serFeatures, _streamWriteFeatures, newSet); } /** * Fluent factory method that will construct and return a new configuration * object instance with specified feature disabled. - * - * @since 2.7 */ public SerializationConfig without(FormatFeature feature) { - // 27-Oct-2018, tatu: Alas, complexity due to newly (2.10) refactored json-features: - if (feature instanceof JsonWriteFeature) { - return _withoutJsonWriteFeatures(feature); - } int newSet = _formatWriteFeatures & ~feature.getMask(); - int newMask = _formatWriteFeaturesToChange | feature.getMask(); - return ((_formatWriteFeatures == newSet) && (_formatWriteFeaturesToChange == newMask)) ? this : - new SerializationConfig(this, _mapperFeatures, _serFeatures, - _generatorFeatures, _generatorFeaturesToChange, - newSet, newMask); + return (_formatWriteFeatures == newSet) ? this : + new SerializationConfig(this, + _serFeatures, _streamWriteFeatures, newSet); } /** * Fluent factory method that will construct and return a new configuration * object instance with specified features disabled. - * - * @since 2.7 */ public SerializationConfig withoutFeatures(FormatFeature... features) { - if (features.length > 0 && (features[0] instanceof JsonWriteFeature)) { - return _withoutJsonWriteFeatures(features); - } - int newSet = _formatWriteFeatures; - int newMask = _formatWriteFeaturesToChange; - for (FormatFeature f : features) { - int mask = f.getMask(); - newSet &= ~mask; - newMask |= mask; - } - return ((_formatWriteFeatures == newSet) && (_formatWriteFeaturesToChange == newMask)) ? this : - new SerializationConfig(this, _mapperFeatures, _serFeatures, - _generatorFeatures, _generatorFeaturesToChange, - newSet, newMask); - } - - // temporary for 2.10 - private SerializationConfig _withJsonWriteFeatures(FormatFeature... features) { - int parserSet = _generatorFeatures; - int parserMask = _generatorFeaturesToChange; int newSet = _formatWriteFeatures; - int newMask = _formatWriteFeaturesToChange; for (FormatFeature f : features) { - final int mask = f.getMask(); - newSet |= mask; - newMask |= mask; - - if (f instanceof JsonWriteFeature) { - JsonGenerator.Feature oldF = ((JsonWriteFeature) f).mappedFeature(); - if (oldF != null) { - final int pmask = oldF.getMask(); - parserSet |= pmask; - parserMask |= pmask; - } - } + newSet &= ~f.getMask(); } - return ((_formatWriteFeatures == newSet) && (_formatWriteFeaturesToChange == newMask) - && (_generatorFeatures == parserSet) && (_generatorFeaturesToChange == parserMask) - ) ? this : - new SerializationConfig(this, _mapperFeatures, _serFeatures, - parserSet, parserMask, newSet, newMask); - } - - // temporary for 2.10 - private SerializationConfig _withoutJsonWriteFeatures(FormatFeature... features) { - int parserSet = _generatorFeatures; - int parserMask = _generatorFeaturesToChange; - int newSet = _formatWriteFeatures; - int newMask = _formatWriteFeaturesToChange; - for (FormatFeature f : features) { - final int mask = f.getMask(); - newSet &= ~mask; - newMask |= mask; - - if (f instanceof JsonWriteFeature) { - JsonGenerator.Feature oldF = ((JsonWriteFeature) f).mappedFeature(); - if (oldF != null) { - final int pmask = oldF.getMask(); - parserSet &= ~pmask; - parserMask |= pmask; - } - } - } - return ((_formatWriteFeatures == newSet) && (_formatWriteFeaturesToChange == newMask) - && (_generatorFeatures == parserSet) && (_generatorFeaturesToChange == parserMask) - ) ? this : - new SerializationConfig(this, _mapperFeatures, _serFeatures, - parserSet, parserMask, newSet, newMask); + return (_formatWriteFeatures == newSet) ? this : + new SerializationConfig(this, _serFeatures, _streamWriteFeatures, newSet); } /* - /********************************************************** + /********************************************************************** /* Factory methods, other - /********************************************************** + /********************************************************************** */ public SerializationConfig withFilters(FilterProvider filterProvider) { return (filterProvider == _filterProvider) ? this : new SerializationConfig(this, filterProvider); } - /** - * Mutant factory method for constructing a new instance with different - * default inclusion criteria configuration. - * - * @since 2.7 - * - * @deprecated Since 2.9; not needed any more - */ - @Deprecated - public SerializationConfig withPropertyInclusion(JsonInclude.Value incl) { - _configOverrides.setDefaultInclusion(incl); - return this; - } - - /** - * @since 2.6 - */ public SerializationConfig withDefaultPrettyPrinter(PrettyPrinter pp) { return (_defaultPrettyPrinter == pp) ? this: new SerializationConfig(this, pp); } /* - /********************************************************** + /********************************************************************** /* Factories for objects configured here - /********************************************************** + /********************************************************************** */ public PrettyPrinter constructDefaultPrettyPrinter() { @@ -701,70 +462,31 @@ public PrettyPrinter constructDefaultPrettyPrinter() { } return pp; } - + /* - /********************************************************** - /* JsonParser initialization - /********************************************************** + /********************************************************************** + /* Support for ObjectWriteContext + /********************************************************************** */ /** - * Method called by {@link ObjectMapper} and {@link ObjectWriter} - * to modify those {@link com.fasterxml.jackson.core.JsonGenerator.Feature} settings - * that have been configured via this config instance. - * - * @since 2.5 + * @since 3.0 */ - public void initialize(JsonGenerator g) - { - if (SerializationFeature.INDENT_OUTPUT.enabledIn(_serFeatures)) { - // but do not override an explicitly set one - if (g.getPrettyPrinter() == null) { - PrettyPrinter pp = constructDefaultPrettyPrinter(); - if (pp != null) { - g.setPrettyPrinter(pp); - } - } - } - @SuppressWarnings("deprecation") - boolean useBigDec = SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN.enabledIn(_serFeatures); - - int mask = _generatorFeaturesToChange; - if ((mask != 0) || useBigDec) { - int newFlags = _generatorFeatures; - // although deprecated, needs to be supported for now - if (useBigDec) { - int f = JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN.getMask(); - newFlags |= f; - mask |= f; - } - g.overrideStdFeatures(newFlags, mask); - } - if (_formatWriteFeaturesToChange != 0) { - g.overrideFormatFeatures(_formatWriteFeatures, _formatWriteFeaturesToChange); - } + public int getStreamWriteFeatures() { + return _streamWriteFeatures; } - /* - /********************************************************** - /* Configuration: default settings with per-type overrides - /********************************************************** - */ - /** - * @deprecated Since 2.7 use {@link #getDefaultPropertyInclusion} instead + * @since 3.0 */ - @Deprecated - public JsonInclude.Include getSerializationInclusion() - { - JsonInclude.Include incl = getDefaultPropertyInclusion().getValueInclusion(); - return (incl == JsonInclude.Include.USE_DEFAULTS) ? JsonInclude.Include.ALWAYS : incl; + public int getFormatWriteFeatures() { + return _formatWriteFeatures; } /* - /********************************************************** + /********************************************************************** /* Configuration: other - /********************************************************** + /********************************************************************** */ @Override @@ -784,22 +506,18 @@ public final boolean isEnabled(SerializationFeature f) { * Accessor method that first checks if we have any overrides * for feature, and only if not, checks state of passed-in * factory. - * - * @since 2.5 */ - public final boolean isEnabled(JsonGenerator.Feature f, JsonFactory factory) { - int mask = f.getMask(); - if ((_generatorFeaturesToChange & mask) != 0) { - return (_generatorFeatures & f.getMask()) != 0; - } - return factory.isEnabled(f); + public final boolean isEnabled(StreamWriteFeature f) { + return (_streamWriteFeatures & f.getMask()) != 0; } - + + public final boolean hasFormatFeature(FormatFeature f) { + return (_formatWriteFeatures & f.getMask()) != 0; + } + /** * "Bulk" access method for checking that all features specified by * mask are enabled. - * - * @since 2.3 */ public final boolean hasSerializationFeatures(int featureMask) { return (_serFeatures & featureMask) == featureMask; @@ -826,25 +544,22 @@ public FilterProvider getFilterProvider() { * NOTE: returns the "blueprint" instance, and does NOT construct * an instance ready to use; call {@link #constructDefaultPrettyPrinter()} if * actually usable instance is desired. - * - * @since 2.6 */ public PrettyPrinter getDefaultPrettyPrinter() { return _defaultPrettyPrinter; } /* - /********************************************************** + /********************************************************************** /* Introspection methods - /********************************************************** + /********************************************************************** */ /** * Method that will introspect full bean properties for the purpose * of building a bean serializer */ - @SuppressWarnings("unchecked") - public T introspect(JavaType type) { - return (T) getClassIntrospector().forSerialization(this, type, this); + public BeanDescription introspect(JavaType type) { + return getClassIntrospector().forSerialization(this, type, this); } } diff --git a/src/main/java/com/fasterxml/jackson/databind/SerializationFeature.java b/src/main/java/com/fasterxml/jackson/databind/SerializationFeature.java index 1b46dc073d..ca810d61af 100644 --- a/src/main/java/com/fasterxml/jackson/databind/SerializationFeature.java +++ b/src/main/java/com/fasterxml/jackson/databind/SerializationFeature.java @@ -271,23 +271,6 @@ public enum SerializationFeature implements ConfigFeature */ WRITE_ENUMS_USING_INDEX(false), - /** - * Feature that determines whether Map entries with null values are - * to be serialized (true) or not (false). - *

    - * NOTE: unlike other {@link SerializationFeature}s, this feature cannot be - * dynamically changed on per-call basis, because its effect is considered during - * construction of serializers and property handlers. - *

    - * Feature is enabled by default. - * - * @deprecated Since 2.9 there are better mechanism for specifying filtering; specifically - * using {@link com.fasterxml.jackson.annotation.JsonInclude} or configuration overrides - * (see {@link ObjectMapper#configOverride(Class)}}). - */ - @Deprecated // since 2.9 - WRITE_NULL_MAP_VALUES(true), - /** * Feature that determines whether Container properties (POJO properties * with declared value of Collection or array; i.e. things that produce JSON @@ -332,25 +315,6 @@ public enum SerializationFeature implements ConfigFeature */ WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED(false), - /** - * Feature that determines whether {@link java.math.BigDecimal} entries are - * serialized using {@link java.math.BigDecimal#toPlainString()} to prevent - * values to be written using scientific notation. - *

    - * NOTE: since this feature typically requires use of - * {@link com.fasterxml.jackson.core.JsonGenerator#writeNumber(String)} - * it may cause compatibility problems since not all {@link com.fasterxml.jackson.core.JsonGenerator} - * implementations support such mode of output: usually only text-based formats - * support it. - *

    - * Feature is disabled by default. - * - * @deprecated Since 2.5: use {@link com.fasterxml.jackson.core.JsonGenerator.Feature#WRITE_BIGDECIMAL_AS_PLAIN} instead - * (using {@link ObjectWriter#with(com.fasterxml.jackson.core.JsonGenerator.Feature)}). - */ - @Deprecated // since 2.5 - WRITE_BIGDECIMAL_AS_PLAIN(false), - /** * Feature that controls whether numeric timestamp values are * to be written using nanosecond timestamps (enabled) or not (disabled); diff --git a/src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java b/src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java index ac24e11286..0f4691d65c 100644 --- a/src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java +++ b/src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java @@ -9,16 +9,21 @@ import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.ObjectIdGenerator; -import com.fasterxml.jackson.core.JsonGenerator; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.io.CharacterEscapes; +import com.fasterxml.jackson.core.tree.ArrayTreeNode; +import com.fasterxml.jackson.core.tree.ObjectTreeNode; + import com.fasterxml.jackson.databind.cfg.ContextAttributes; -import com.fasterxml.jackson.databind.deser.ContextualDeserializer; +import com.fasterxml.jackson.databind.cfg.GeneratorSettings; import com.fasterxml.jackson.databind.exc.InvalidDefinitionException; import com.fasterxml.jackson.databind.exc.InvalidTypeIdException; import com.fasterxml.jackson.databind.introspect.Annotated; +import com.fasterxml.jackson.databind.introspect.AnnotatedMember; import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; import com.fasterxml.jackson.databind.ser.*; -import com.fasterxml.jackson.databind.ser.impl.FailingSerializer; import com.fasterxml.jackson.databind.ser.impl.ReadOnlyClassToSerializerMap; import com.fasterxml.jackson.databind.ser.impl.TypeWrappedSerializer; import com.fasterxml.jackson.databind.ser.impl.UnknownSerializer; @@ -36,133 +41,96 @@ * Provider handles caching aspects of serializer handling; all construction * details are delegated to {@link SerializerFactory} instance. *

    - * Object life-cycle is such that an initial instance ("blueprint") is created - * and referenced by {@link ObjectMapper} and {@link ObjectWriter} intances; - * but for actual usage, a configured instance is created by using - * a create method in sub-class - * {@link com.fasterxml.jackson.databind.ser.DefaultSerializerProvider}. - * Only this instance can be used for actual serialization calls; blueprint - * object is only to be used for creating instances. */ public abstract class SerializerProvider extends DatabindContext + implements // NOTE: not JDK serializable with 3.x (factory that creates these is) + ObjectWriteContext // 3.0, for use by jackson-core { - /** - * Setting for determining whether mappings for "unknown classes" should be - * cached for faster resolution. Usually this isn't needed, but maybe it - * is in some cases? - */ - protected final static boolean CACHE_UNKNOWN_MAPPINGS = false; - - public final static JsonSerializer DEFAULT_NULL_KEY_SERIALIZER = - new FailingSerializer("Null key for a Map not allowed in JSON (use a converting NullKeySerializer?)"); - /** * Placeholder serializer used when java.lang.Object typed property * is marked to be serialized. *
    * NOTE: starting with 2.6, this instance is NOT used for any other types, and * separate instances are constructed for "empty" Beans. - *

    - * NOTE: changed to protected for 2.3; no need to be publicly available. */ protected final static JsonSerializer DEFAULT_UNKNOWN_SERIALIZER = new UnknownSerializer(); /* - /********************************************************** + /********************************************************************** /* Configuration, general - /********************************************************** + /********************************************************************** */ - + /** * Serialization configuration to use for serialization processing. */ final protected SerializationConfig _config; /** - * View used for currently active serialization, if any. - * Only set for non-blueprint instances. - */ - final protected Class _serializationView; - - /* - /********************************************************** - /* Configuration, factories - /********************************************************** + * Configuration to be used by streaming generator when it is constructed. + * + * @since 3.0 */ + final protected GeneratorSettings _generatorConfig; /** - * Factory used for constructing actual serializer instances. - * Only set for non-blueprint instances. + * Low-level {@link TokenStreamFactory} that may be used for constructing + * embedded generators. */ - final protected SerializerFactory _serializerFactory; + final protected TokenStreamFactory _streamFactory; - /* - /********************************************************** - /* Helper objects for caching, reuse - /********************************************************** - */ - /** - * Cache for doing type-to-value-serializer lookups. + * Token stream generator actively used; only set for per-call instances + * + * @since 3.0 */ - final protected SerializerCache _serializerCache; + protected transient JsonGenerator _generator; /** - * Lazily-constructed holder for per-call attributes. - * Only set for non-blueprint instances. - * - * @since 2.3 + * View used for currently active serialization, if any. */ - protected transient ContextAttributes _attributes; + final protected Class _activeView; /* - /********************************************************** - /* Configuration, specialized serializers - /********************************************************** + /********************************************************************** + /* Configuration, serializer access + /********************************************************************** */ /** - * Serializer that gets called for values of types for which no - * serializers can be constructed. - *

    - * The default serializer will simply thrown an exception. - */ - protected JsonSerializer _unknownTypeSerializer = DEFAULT_UNKNOWN_SERIALIZER; - - /** - * Serializer used to output non-null keys of Maps (which will get - * output as JSON Objects), if not null; if null, us the standard - * default key serializer. + * Factory used for constructing actual serializer instances. + * Only set for non-blueprint instances. */ - protected JsonSerializer _keySerializer; - + final protected SerializerFactory _serializerFactory; + /** * Serializer used to output a null value. Default implementation * writes nulls using {@link JsonGenerator#writeNull}. */ - protected JsonSerializer _nullValueSerializer = NullSerializer.instance; + final protected JsonSerializer _nullValueSerializer; /** - * Serializer used to (try to) output a null key, due to an entry of - * {@link java.util.Map} having null key. - * The default implementation will throw an exception if this happens; - * alternative implementation (like one that would write an Empty String) - * can be defined. + * Flag set to indicate that we are using vanilla null value serialization */ - protected JsonSerializer _nullKeySerializer = DEFAULT_NULL_KEY_SERIALIZER; - + final protected boolean _stdNullValueSerializer; + /* - /********************************************************** - /* State, for non-blueprint instances: generic - /********************************************************** + /********************************************************************** + /* Helper objects for caching, reuse + /********************************************************************** */ + /** + * Cache for doing type-to-value-serializer lookups. + */ + final protected SerializerCache _serializerCache; + /** * For fast lookups, we will have a local non-shared read-only * map that contains serializers previously fetched. */ - protected final ReadOnlyClassToSerializerMap _knownSerializers; + final protected ReadOnlyClassToSerializerMap _knownSerializers; /** * Lazily acquired and instantiated formatter object: initialized @@ -171,151 +139,147 @@ public abstract class SerializerProvider */ protected DateFormat _dateFormat; - /** - * Flag set to indicate that we are using vanilla null value serialization - * - * @since 2.3 - */ - protected final boolean _stdNullValueSerializer; - /* - /********************************************************** - /* Life-cycle - /********************************************************** + /********************************************************************** + /* Other state + /********************************************************************** */ /** - * Constructor for creating master (or "blue-print") provider object, - * which is only used as the template for constructing per-binding - * instances. + * Lazily-constructed holder for per-call attributes. + * Only set for non-blueprint instances. */ - public SerializerProvider() - { - _config = null; - _serializerFactory = null; - _serializerCache = new SerializerCache(); - // Blueprints doesn't have access to any serializers... - _knownSerializers = null; - - _serializationView = null; - _attributes = null; - - // not relevant for blueprint instance, could set either way: - _stdNullValueSerializer = true; - } + protected ContextAttributes _attributes; - /** - * "Copy-constructor", used by sub-classes when creating actual non-blueprint - * instances to use. - * - * @param src Blueprint object used as the baseline for this instance + /* + /********************************************************************** + /* Life-cycle + /********************************************************************** */ - protected SerializerProvider(SerializerProvider src, - SerializationConfig config, SerializerFactory f) + + protected SerializerProvider(TokenStreamFactory streamFactory, + SerializationConfig config, GeneratorSettings generatorConfig, + SerializerFactory f, SerializerCache cache) { + _streamFactory = streamFactory; _serializerFactory = f; _config = config; + _generatorConfig = generatorConfig; - _serializerCache = src._serializerCache; - _unknownTypeSerializer = src._unknownTypeSerializer; - _keySerializer = src._keySerializer; - _nullValueSerializer = src._nullValueSerializer; - _nullKeySerializer = src._nullKeySerializer; + _serializerCache = cache; - _stdNullValueSerializer = (_nullValueSerializer == DEFAULT_NULL_KEY_SERIALIZER); + // Default null key, value serializers configured via SerializerFactory + { + JsonSerializer ser = f.getDefaultNullValueSerializer(); + if (ser == null) { + _stdNullValueSerializer = true; + ser = NullSerializer.instance; + } else { + _stdNullValueSerializer = false; + } + _nullValueSerializer = ser; + } - _serializationView = config.getActiveView(); + _activeView = config.getActiveView(); _attributes = config.getAttributes(); - /* Non-blueprint instances do have a read-only map; one that doesn't - * need synchronization for lookups. - */ + // Non-blueprint instances do have a read-only map; one that doesn't + // need synchronization for lookups. _knownSerializers = _serializerCache.getReadOnlyLookupMap(); } - /** - * Copy-constructor used when making a copy of a blueprint instance. - * - * @since 2.5 + /* + /********************************************************************** + /* ObjectWriteContext impl, config access + /********************************************************************** */ - protected SerializerProvider(SerializerProvider src) - { - // since this is assumed to be a blue-print instance, many settings missing: - _config = null; - _serializationView = null; - _serializerFactory = null; - _knownSerializers = null; - // and others initialized to default empty state - _serializerCache = new SerializerCache(); + @Override + public TokenStreamFactory getGeneratorFactory() { + return _streamFactory; + } - _unknownTypeSerializer = src._unknownTypeSerializer; - _keySerializer = src._keySerializer; - _nullValueSerializer = src._nullValueSerializer; - _nullKeySerializer = src._nullKeySerializer; + @Override + public FormatSchema getSchema() { return _generatorConfig.getSchema(); } + + @Override + public CharacterEscapes getCharacterEscapes() { return _generatorConfig.getCharacterEscapes(); } - _stdNullValueSerializer = src._stdNullValueSerializer; + @Override + public PrettyPrinter getPrettyPrinter() { + PrettyPrinter pp = _generatorConfig.getPrettyPrinter(); + if (pp == null) { + if (isEnabled(SerializationFeature.INDENT_OUTPUT)) { + pp = _config.constructDefaultPrettyPrinter(); + } + } + return pp; } - + + @Override + public SerializableString getRootValueSeparator(SerializableString defaultSeparator) { + return _generatorConfig.getRootValueSeparator(defaultSeparator); + } + + @Override + public int getStreamWriteFeatures(int defaults) { + return _config.getStreamWriteFeatures(); + } + + @Override + public int getFormatWriteFeatures(int defaults) { + return _config.getFormatWriteFeatures(); + } + /* - /********************************************************** - /* Methods for configuring default settings - /********************************************************** + /********************************************************************** + /* ObjectWriteContext impl, databind integration + /********************************************************************** */ - /** - * Method that can be used to specify serializer that will be - * used to write JSON property names matching null keys for Java - * Maps (which will throw an exception if try write such property - * name) - */ - public void setDefaultKeySerializer(JsonSerializer ks) - { - if (ks == null) { - throw new IllegalArgumentException("Cannot pass null JsonSerializer"); - } - _keySerializer = ks; + @Override + public ArrayTreeNode createArrayNode() { + return _config.getNodeFactory().arrayNode(); } - /** - * Method that can be used to specify serializer that will be - * used to write JSON values matching Java null values - * instead of default one (which simply writes JSON null). - *

    - * Note that you can get finer control over serializer to use by overriding - * {@link #findNullValueSerializer}, which gets called once per each - * property. - */ - public void setNullValueSerializer(JsonSerializer nvs) + @Override + public ObjectTreeNode createObjectNode() { + return _config.getNodeFactory().objectNode(); + } + + @Override + public void writeValue(JsonGenerator gen, Object value) throws IOException { - if (nvs == null) { - throw new IllegalArgumentException("Cannot pass null JsonSerializer"); + // Let's keep track of active generator; useful mostly for error reporting... + JsonGenerator prevGen = _generator; + _generator = gen; + try { + if (value == null) { + if (_stdNullValueSerializer) { // minor perf optimization + gen.writeNull(); + } else { + _nullValueSerializer.serialize(null, gen, this); + } + return; + } + Class cls = value.getClass(); + findTypedValueSerializer(cls, true).serialize(value, gen, this); + } finally { + _generator = prevGen; } - _nullValueSerializer = nvs; } - /** - * Method that can be used to specify serializer to use for serializing - * all non-null JSON property names, unless more specific key serializer - * is found (i.e. if not custom key serializer has been registered for - * Java type). - *

    - * Note that key serializer registration are different from value serializer - * registrations. - */ - public void setNullKeySerializer(JsonSerializer nks) + @Override + public void writeTree(JsonGenerator gen, TreeNode tree) throws IOException { - if (nks == null) { - throw new IllegalArgumentException("Cannot pass null JsonSerializer"); - } - _nullKeySerializer = nks; + // 05-Oct-2017, tatu: Should probably optimize or something? Or not? + writeValue(gen, tree); } - + /* - /********************************************************** - /* DatabindContext implementation (and closely related - /* but ser-specific) - /********************************************************** + /********************************************************************** + /* DatabindContext implementation (and closely related but ser-specific) + /********************************************************************** */ /** @@ -335,13 +299,7 @@ public final TypeFactory getTypeFactory() { } @Override - public final Class getActiveView() { return _serializationView; } - - /** - * @deprecated Since 2.2, use {@link #getActiveView} instead. - */ - @Deprecated - public final Class getSerializationView() { return _serializationView; } + public final Class getActiveView() { return _activeView; } @Override public final boolean canOverrideAccessModifiers() { @@ -358,9 +316,6 @@ public final JsonFormat.Value getDefaultPropertyFormat(Class baseType) { return _config.getDefaultPropertyFormat(baseType); } - /** - * @since 2.8 - */ public final JsonInclude.Value getDefaultPropertyInclusion(Class baseType) { return _config.getDefaultPropertyInclusion(); } @@ -388,9 +343,9 @@ public TimeZone getTimeZone() { } /* - /********************************************************** - /* Generic attributes (2.3+) - /********************************************************** + /********************************************************************** + /* Generic attributes + /********************************************************************** */ @Override @@ -406,9 +361,9 @@ public SerializerProvider setAttribute(Object key, Object value) } /* - /********************************************************** + /********************************************************************** /* Access to general configuration - /********************************************************** + /********************************************************************** */ /** @@ -426,8 +381,6 @@ public final boolean isEnabled(SerializationFeature feature) { /** * "Bulk" access method for checking that all features specified by * mask are enabled. - * - * @since 2.3 */ public final boolean hasSerializationFeatures(int featureMask) { return _config.hasSerializationFeatures(featureMask); @@ -444,21 +397,14 @@ public final FilterProvider getFilterProvider() { return _config.getFilterProvider(); } - /** - *

    - * NOTE: current implementation simply returns `null` as generator is not yet - * assigned to this provider. - * - * @since 2.8 - */ public JsonGenerator getGenerator() { - return null; + return _generator; } - + /* - /********************************************************** + /********************************************************************** /* Access to Object Id aspects - /********************************************************** + /********************************************************************** */ /** @@ -469,315 +415,303 @@ public JsonGenerator getGenerator() { */ public abstract WritableObjectId findObjectId(Object forPojo, ObjectIdGenerator generatorType); + + /* + /********************************************************************** + /* Introspection support + /********************************************************************** + */ + + /** + * Convenience method for doing full "for serialization" introspection of specified + * type; results may be cached during lifespan of this context as well. + */ + public BeanDescription introspect(JavaType type) throws JsonMappingException { + return _config.introspect(type); + } + + public BeanDescription introspectClassAnnotations(Class rawType) throws JsonMappingException { + return _config.introspectClassAnnotations(rawType); + } + + public BeanDescription introspectClassAnnotations(JavaType type) throws JsonMappingException { + return _config.introspectClassAnnotations(type); + } /* - /********************************************************** - /* General serializer locating functionality - /********************************************************** + /********************************************************************** + /* Serializer discovery: root/non-property value serializers + /********************************************************************** */ /** - * Method called to get hold of a serializer for a value of given type; - * or if no such serializer can be found, a default handler (which - * may do a best-effort generic serialization or just simply - * throw an exception when invoked). - *

    - * Note: this method is only called for non-null values; not for keys - * or null values. For these, check out other accessor methods. - *

    - * Note that serializers produced should NOT handle polymorphic serialization - * aspects; separate {@link TypeSerializer} is to be constructed by caller - * if and as necessary. - * - * @throws JsonMappingException if there are fatal problems with - * accessing suitable serializer; including that of not - * finding any serializer + * Method called to locate regular serializer, matching type serializer, + * and if both found, wrap them in a serializer that calls both in correct + * sequence. This method is mostly used for root-level serializer + * handling to allow for simpler caching. A call can always be replaced + * by equivalent calls to access serializer and type serializer separately. + * + * @param rawType Type for purpose of locating a serializer; usually dynamic + * runtime type, but can also be static declared type, depending on configuration + * @param cache Whether resulting value serializer should be cached or not */ - @SuppressWarnings("unchecked") - public JsonSerializer findValueSerializer(Class valueType, BeanProperty property) + public JsonSerializer findTypedValueSerializer(Class rawType, + boolean cache) throws JsonMappingException { - // Fast lookup from local lookup thingy works? - JsonSerializer ser = _knownSerializers.untypedValueSerializer(valueType); - if (ser == null) { - // If not, maybe shared map already has it? - ser = _serializerCache.untypedValueSerializer(valueType); - if (ser == null) { - // ... possibly as fully typed? - ser = _serializerCache.untypedValueSerializer(_config.constructType(valueType)); - if (ser == null) { - // If neither, must create - ser = _createAndCacheUntypedSerializer(valueType); - // 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? - if (CACHE_UNKNOWN_MAPPINGS) { - _serializerCache.addAndResolveNonTypedSerializer(valueType, ser, this); - } - return ser; - } - } - } + // First: do we have it cached? + JsonSerializer ser = _knownSerializers.typedValueSerializer(rawType); + if (ser != null) { + return ser; } - // at this point, resolution has occured, but not contextualization - return (JsonSerializer) handleSecondaryContextualization(ser, property); + // If not, compose from pieces: + JavaType fullType = _config.constructType(rawType); + ser = handleRootContextualization(findValueSerializer(rawType)); + TypeSerializer typeSer = findTypeSerializer(fullType); + if (typeSer != null) { + typeSer = typeSer.forProperty(this, null); + ser = new TypeWrappedSerializer(typeSer, ser); + } + if (cache) { + _serializerCache.addTypedSerializer(rawType, ser); + } + return ser; } /** - * Similar to {@link #findValueSerializer(Class,BeanProperty)}, but takes - * full generics-aware type instead of raw class. - * This is necessary for accurate handling of external type information, - * to handle polymorphic types. - *

    - * Note: this call will also contextualize serializer before returning it. + * Method called to locate regular serializer, matching type serializer, + * and if both found, wrap them in a serializer that calls both in correct + * sequence. This method is mostly used for root-level serializer + * handling to allow for simpler caching. A call can always be replaced + * by equivalent calls to access serializer and type serializer separately. * - * @param property When creating secondary serializers, property for which - * serializer is needed: annotations of the property (or bean that contains it) - * may be checked to create contextual serializers. + * @param valueType Declared type of value being serialized (which may not + * be actual runtime type); used for finding both value serializer and + * type serializer to use for adding polymorphic type (if any) + * @param cache Whether resulting value serializer should be cached or not */ - @SuppressWarnings("unchecked") - public JsonSerializer findValueSerializer(JavaType valueType, BeanProperty property) + public JsonSerializer findTypedValueSerializer(JavaType valueType, boolean cache) throws JsonMappingException { - if (valueType == null) { - reportMappingProblem("Null passed for `valueType` of `findValueSerializer()`"); + + JsonSerializer ser = _knownSerializers.typedValueSerializer(valueType); + if (ser != null) { + return ser; } - // (see comments from above method) - JsonSerializer 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; - } - } + ser = handleRootContextualization(findValueSerializer(valueType)); + TypeSerializer typeSer = findTypeSerializer(valueType); + if (typeSer != null) { + typeSer = typeSer.forProperty(this, null); + ser = new TypeWrappedSerializer(typeSer, ser); } - return (JsonSerializer) handleSecondaryContextualization(ser, property); + if (cache) { + _serializerCache.addTypedSerializer(valueType, ser); + } + 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 + * Method for finding (from cache) or creating (and caching) serializer for given type, + * without checking for polymorphic typing, and then contextualizing without actual + * property. This is most often used for root-level values (when writing + * sequences), but may sometimes be used for more esoteric value handling for + * delegation. + * + * @since 3.0 */ - public JsonSerializer findValueSerializer(Class valueType) throws JsonMappingException + public JsonSerializer findRootValueSerializer(Class rawType) throws JsonMappingException { - // (see comments from above method) - JsonSerializer ser = _knownSerializers.untypedValueSerializer(valueType); + JsonSerializer ser = _knownSerializers.untypedValueSerializer(rawType); if (ser == null) { - ser = _serializerCache.untypedValueSerializer(valueType); + JavaType fullType = _config.constructType(rawType); + ser = _serializerCache.untypedValueSerializer(fullType); 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); - } - } - } + ser = _createAndCacheUntypedSerializer(rawType, fullType); } } - return ser; + return handleRootContextualization(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 + * Method for finding (from cache) or creating (and caching) serializer for given type, + * without checking for polymorphic typing, and then contextualizing without actual + * property. This is most often used for root-level values (when writing + * sequences), but may sometimes be used for more esoteric value handling for + * delegation. + * + * @since 3.0 */ - public JsonSerializer findValueSerializer(JavaType valueType) + public JsonSerializer findRootValueSerializer(JavaType valueType) throws JsonMappingException { - // (see comments from above method) JsonSerializer 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); - } - } - } + ser = _createAndCacheUntypedSerializer(valueType); } - return ser; + return handleRootContextualization(ser); } - + + /* + /********************************************************************** + /* Serializer discovery: property value serializers + /********************************************************************** + */ + /** - * 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, + * Method used for locating "primary" property value serializer (one directly + * handling value of the property). Difference (if any) has to do with contextual resolution, * and method(s) called: this method should only be called when caller is * certain that this is the primary property value serializer. * * @param property Property that is being handled; will never be null, and its * type has to match valueType parameter. - * - * @since 2.3 */ - @SuppressWarnings("unchecked") - public JsonSerializer findPrimaryPropertySerializer(JavaType valueType, BeanProperty property) + public JsonSerializer findPrimaryPropertySerializer(JavaType valueType, + BeanProperty property) throws JsonMappingException { JsonSerializer ser = _knownSerializers.untypedValueSerializer(valueType); if (ser == null) { - ser = _serializerCache.untypedValueSerializer(valueType); - if (ser == null) { - ser = _createAndCacheUntypedSerializer(valueType); - if (ser == null) { - ser = getUnknownTypeSerializer(valueType.getRawClass()); - // Should this be added to lookups? - if (CACHE_UNKNOWN_MAPPINGS) { - _serializerCache.addAndResolveNonTypedSerializer(valueType, ser, this); - } - return ser; - } - } + ser = _createAndCachePropertySerializer(valueType, property); } - return (JsonSerializer) handlePrimaryContextualization(ser, property); + return handlePrimaryContextualization(ser, property); } - /** - * @since 2.3 - */ - @SuppressWarnings("unchecked") - public JsonSerializer findPrimaryPropertySerializer(Class valueType, + public JsonSerializer findPrimaryPropertySerializer(Class rawType, BeanProperty property) throws JsonMappingException { - JsonSerializer ser = _knownSerializers.untypedValueSerializer(valueType); + JsonSerializer ser = _knownSerializers.untypedValueSerializer(rawType); if (ser == null) { - ser = _serializerCache.untypedValueSerializer(valueType); + JavaType fullType = _config.constructType(rawType); + ser = _serializerCache.untypedValueSerializer(fullType); 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; - } - } + ser = _createAndCachePropertySerializer(rawType, fullType, property); } } - return (JsonSerializer) handlePrimaryContextualization(ser, property); + return handlePrimaryContextualization(ser, property); } - - /** - * Method called to locate regular serializer, matching type serializer, - * and if both found, wrap them in a serializer that calls both in correct - * sequence. This method is currently only used for root-level serializer - * handling to allow for simpler caching. A call can always be replaced - * by equivalent calls to access serializer and type serializer separately. - * - * @param valueType Type for purpose of locating a serializer; usually dynamic - * runtime type, but can also be static declared type, depending on configuration - * @param cache Whether resulting value serializer should be cached or not; this is just - * a hint - * @param property When creating secondary serializers, property for which - * serializer is needed: annotations of the property (or bean that contains it) - * may be checked to create contextual serializers. - */ - public JsonSerializer findTypedValueSerializer(Class valueType, - boolean cache, BeanProperty property) + + public JsonSerializer findSecondaryPropertySerializer(JavaType valueType, + BeanProperty property) throws JsonMappingException { - // Two-phase lookups; local non-shared cache, then shared: - JsonSerializer ser = _knownSerializers.typedValueSerializer(valueType); - if (ser != null) { - return ser; - } - // If not, maybe shared map already has it? - ser = _serializerCache.typedValueSerializer(valueType); - if (ser != null) { - return ser; + JsonSerializer ser = _knownSerializers.untypedValueSerializer(valueType); + if (ser == null) { + ser = _createAndCachePropertySerializer(valueType, property); } + return handleSecondaryContextualization(ser, property); + } - // Well, let's just compose from pieces: - ser = findValueSerializer(valueType, property); - TypeSerializer typeSer = _serializerFactory.createTypeSerializer(_config, - _config.constructType(valueType)); - if (typeSer != null) { - typeSer = typeSer.forProperty(property); - ser = new TypeWrappedSerializer(typeSer, ser); + public JsonSerializer findSecondaryPropertySerializer(Class rawType, + BeanProperty property) + throws JsonMappingException + { + JsonSerializer ser = _knownSerializers.untypedValueSerializer(rawType); + if (ser == null) { + JavaType fullType = _config.constructType(rawType); + ser = _serializerCache.untypedValueSerializer(fullType); + if (ser == null) { + ser = _createAndCachePropertySerializer(rawType, fullType, property); + } } - if (cache) { - _serializerCache.addTypedSerializer(valueType, ser); + return handleSecondaryContextualization(ser, property); + } + + /* + /********************************************************************** + /* General serializer locating functionality + /********************************************************************** + */ + + /** + * 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 + */ + public JsonSerializer findValueSerializer(Class rawType) throws JsonMappingException + { + JsonSerializer ser = _knownSerializers.untypedValueSerializer(rawType); + if (ser == null) { + JavaType fullType = _config.constructType(rawType); + ser = _serializerCache.untypedValueSerializer(fullType); + if (ser == null) { + ser = _createAndCacheUntypedSerializer(rawType, fullType); + } } return ser; } /** - * Method called to locate regular serializer, matching type serializer, - * and if both found, wrap them in a serializer that calls both in correct - * sequence. This method is currently only used for root-level serializer - * handling to allow for simpler caching. A call can always be replaced - * by equivalent calls to access serializer and type serializer separately. - * - * @param valueType Declared type of value being serialized (which may not - * be actual runtime type); used for finding both value serializer and - * type serializer to use for adding polymorphic type (if any) - * @param cache Whether resulting value serializer should be cached or not; this is just - * a hint - * @param property When creating secondary serializers, property for which - * serializer is needed: annotations of the property (or bean that contains it) - * may be checked to create contextual serializers. + * 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 */ - public JsonSerializer findTypedValueSerializer(JavaType valueType, boolean cache, - BeanProperty property) + public JsonSerializer findValueSerializer(JavaType valueType) throws JsonMappingException { - // Two-phase lookups; local non-shared cache, then shared: - JsonSerializer ser = _knownSerializers.typedValueSerializer(valueType); - if (ser != null) { - return ser; - } - // If not, maybe shared map already has it? - ser = _serializerCache.typedValueSerializer(valueType); - if (ser != null) { - return ser; - } - - // Well, let's just compose from pieces: - ser = findValueSerializer(valueType, property); - TypeSerializer typeSer = _serializerFactory.createTypeSerializer(_config, valueType); - if (typeSer != null) { - typeSer = typeSer.forProperty(property); - ser = new TypeWrappedSerializer(typeSer, ser); - } - if (cache) { - _serializerCache.addTypedSerializer(valueType, ser); + // (see comments from above method) + JsonSerializer ser = _knownSerializers.untypedValueSerializer(valueType); + if (ser == null) { + ser = _createAndCacheUntypedSerializer(valueType); } return ser; } + /* + /********************************************************************** + /* Serializer discovery: type serializers + /********************************************************************** + */ + /** * Method called to get the {@link TypeSerializer} to use for including Type Id necessary * for serializing for the given Java class. * Useful for schema generators. + */ + public TypeSerializer findTypeSerializer(JavaType baseType) throws JsonMappingException { + return findTypeSerializer(baseType, introspectClassAnnotations(baseType)); + } + + /** + * Method called to get the {@link TypeSerializer} to use for including Type Id necessary + * for serializing for the given Java class. + * Useful for schema generators. + * + * @since 3.0 + */ + public TypeSerializer findTypeSerializer(JavaType baseType, BeanDescription beanDesc) + throws JsonMappingException { + return _config.getTypeResolverProvider().findTypeSerializer(this, baseType, + beanDesc.getClassInfo()); + } + + /** + * Like {@link #findTypeSerializer(JavaType)}, but for use from specific POJO property. + * Method called to create a type information serializer for values of given + * non-container property + * if one is needed. If not needed (no polymorphic handling configured), should + * return null. + * + * @param baseType Declared type to use as the base type for type information serializer + * + * @return Type serializer to use for property values, if one is needed; null if not. * - * @since 2.6 + * @since 3.0 */ - public TypeSerializer findTypeSerializer(JavaType javaType) throws JsonMappingException { - return _serializerFactory.createTypeSerializer(_config, javaType); + public TypeSerializer findPropertyTypeSerializer(JavaType baseType, AnnotatedMember accessor) + throws JsonMappingException + { + return _config.getTypeResolverProvider() + .findPropertyTypeSerializer(this, accessor, baseType); } + /* + /********************************************************************** + /* Serializer discovery: key serializers + /********************************************************************** + */ + /** * Method called to get the serializer to use for serializing * non-null Map keys. Separation from regular @@ -791,64 +725,44 @@ public TypeSerializer findTypeSerializer(JavaType javaType) throws JsonMappingEx public JsonSerializer findKeySerializer(JavaType keyType, BeanProperty property) throws JsonMappingException { - JsonSerializer ser = _serializerFactory.createKeySerializer(_config, keyType, _keySerializer); - // 25-Feb-2011, tatu: As per [JACKSON-519], need to ensure contextuality works here, too - return _handleContextualResolvable(ser, property); + // 16-Mar-2018, tatu: Used to have "default key serializer" in 2.x; dropped to let/make + // custom code use Module interface or similar to provide key serializers + JsonSerializer ser = _serializerFactory.createKeySerializer(this, keyType, null); + // _handleContextualResolvable(ser, property): + ser.resolve(this); + return handleSecondaryContextualization(ser, property); } - /** - * @since 2.7 - */ public JsonSerializer findKeySerializer(Class rawKeyType, BeanProperty property) throws JsonMappingException { return findKeySerializer(_config.constructType(rawKeyType), property); } - /* - /******************************************************** - /* Accessors for specialized serializers - /******************************************************** - */ - - /** - * @since 2.0 - */ - public JsonSerializer getDefaultNullKeySerializer() { - return _nullKeySerializer; - } - - /** - * @since 2.0 - */ public JsonSerializer getDefaultNullValueSerializer() { return _nullValueSerializer; } - - /** - * Method called to get the serializer to use for serializing - * Map keys that are nulls: this is needed since JSON does not allow - * any non-String value as key, including null. - *

    - * Typically, returned serializer - * will either throw an exception, or use an empty String; but - * other behaviors are possible. - */ + /** * Method called to find a serializer to use for null values for given * declared type. Note that type is completely based on declared type, * since nulls in Java have no type and thus runtime type cannot be * determined. - * - * @since 2.0 */ public JsonSerializer findNullKeySerializer(JavaType serializationType, BeanProperty property) throws JsonMappingException { - return _nullKeySerializer; + // rarely needed (that is, not on critical perf path), delegate to factory + return _serializerFactory.getDefaultNullKeySerializer(); } + /* + /********************************************************************** + /* Serializer discovery: other misc serializers, null value, unknown + /********************************************************************** + */ + /** * Method called to get the serializer to use for serializing null * values for specified property. @@ -857,11 +771,10 @@ public JsonSerializer findNullKeySerializer(JavaType serializationType, * can be overridden to add custom null serialization for properties * of certain type or name. This gives method full granularity to basically * override null handling for any specific property or class of properties. - * - * @since 2.0 */ public JsonSerializer findNullValueSerializer(BeanProperty property) - throws JsonMappingException { + throws JsonMappingException + { return _nullValueSerializer; } @@ -880,7 +793,7 @@ public JsonSerializer findNullValueSerializer(BeanProperty property) public JsonSerializer getUnknownTypeSerializer(Class unknownType) { // 23-Apr-2015, tatu: Only return shared instance if nominal type is Object.class if (unknownType == Object.class) { - return _unknownTypeSerializer; + return DEFAULT_UNKNOWN_SERIALIZER; } // otherwise construct explicit instance with property handled type return new UnknownSerializer(unknownType); @@ -890,36 +803,151 @@ public JsonSerializer getUnknownTypeSerializer(Class unknownType) { * 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) { - if ((ser == _unknownTypeSerializer) || (ser == null)) { + if ((ser == DEFAULT_UNKNOWN_SERIALIZER) || (ser == null)) { return true; } // 23-Apr-2015, tatu: "empty" serializer is trickier; needs to consider // error handling if (isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS)) { - if (ser.getClass() == UnknownSerializer.class) { + if (ser instanceof UnknownSerializer) { return true; } } return false; } - + + /* + /********************************************************************** + /* Low-level methods for actually constructing and initializing serializers + /********************************************************************** + */ + + /** + * Method that will try to construct a value serializer; and if + * one is successfully created, cache it for reuse. + */ + protected JsonSerializer _createAndCacheUntypedSerializer(Class rawType, + JavaType fullType) + throws JsonMappingException + { + // Important: must introspect all annotations, not just class + BeanDescription beanDesc = introspect(fullType); + JsonSerializer ser; + try { + ser = _serializerFactory.createSerializer(this, fullType, beanDesc, null); + } catch (IllegalArgumentException iae) { + // We better only expose checked exceptions, since those are what caller is expected to handle + throw _mappingProblem(iae, ClassUtil.exceptionMessage(iae)); + } + // Always cache -- and in this case both for raw and full type + _serializerCache.addAndResolveNonTypedSerializer(rawType, fullType, ser, this); + return ser; + } + + protected JsonSerializer _createAndCacheUntypedSerializer(JavaType type) + throws JsonMappingException + { + // Important: must introspect all annotations, not just class + BeanDescription beanDesc = introspect(type); + JsonSerializer ser; + try { + ser = _serializerFactory.createSerializer(this, type, beanDesc, null); + } catch (IllegalArgumentException iae) { + // We better only expose checked exceptions, since those are what caller is expected to handle + throw _mappingProblem(iae, ClassUtil.exceptionMessage(iae)); + } + // always cache -- but only full type (may be parameterized) + _serializerCache.addAndResolveNonTypedSerializer(type, ser, this); + return ser; + } + + /** + * Alternative to {@link #_createAndCacheUntypedSerializer(Class, JavaType)}, used + * when serializer is requested for given property. + */ + protected JsonSerializer _createAndCachePropertySerializer(Class rawType, + JavaType fullType, BeanProperty prop) + throws JsonMappingException + { + BeanDescription beanDesc = introspect(fullType); + JsonSerializer ser; + try { + ser = _serializerFactory.createSerializer(this, fullType, beanDesc, null); + } catch (IllegalArgumentException iae) { + throw _mappingProblem(iae, ClassUtil.exceptionMessage(iae)); + } + _serializerCache.addAndResolveNonTypedSerializer(rawType, fullType, ser, this); + // Fine, we have to base instance. But how about per-property format overrides? + if (prop == null) { + return ser; + } + return _checkShapeShifting(fullType, beanDesc, prop, ser); + } + + /** + * Alternative to {@link #_createAndCacheUntypedSerializer(JavaType)}, used + * when serializer is requested for given property. + */ + protected JsonSerializer _createAndCachePropertySerializer(JavaType type, + BeanProperty prop) + throws JsonMappingException + { + BeanDescription beanDesc = introspect(type); + JsonSerializer ser; + try { + ser = _serializerFactory.createSerializer(this, type, beanDesc, null); + } catch (IllegalArgumentException iae) { + throw _mappingProblem(iae, ClassUtil.exceptionMessage(iae)); + } + _serializerCache.addAndResolveNonTypedSerializer(type, ser, this); + // Fine, we have to base instance. But how about per-property format overrides? + if (prop == null) { + return ser; + } + return _checkShapeShifting(type, beanDesc, prop, ser); + } + + @SuppressWarnings("unchecked") + private JsonSerializer _checkShapeShifting(JavaType type, BeanDescription beanDesc, + BeanProperty prop, JsonSerializer ser) + throws JsonMappingException + { + JsonFormat.Value overrides = prop.findFormatOverrides(_config); + if (overrides != null) { + // First: it may be completely fine to use serializer, despite some overrides + JsonSerializer ser2 = ser.withFormatOverrides(_config, overrides); + if (ser2 != null) { + ser = ser2; + } else { + // But if not, we need to re-create it via factory + ser = _serializerFactory.createSerializer(this, type, beanDesc, overrides); + } + } + return (JsonSerializer) ser; + } + + @SuppressWarnings("unchecked") + protected JsonSerializer _handleResolvable(JsonSerializer ser) + throws JsonMappingException + { + ser.resolve(this); + return (JsonSerializer) ser; + } + /* - /********************************************************** + /********************************************************************** /* Methods for creating instances based on annotations - /********************************************************** + /********************************************************************** */ /** * Method that can be called to construct and configure serializer instance, * either given a {@link Class} to instantiate (with default constructor), * or an uninitialized serializer instance. - * Either way, serialize will be properly resolved - * (via {@link com.fasterxml.jackson.databind.ser.ResolvableSerializer}) and/or contextualized - * (via {@link com.fasterxml.jackson.databind.ser.ContextualSerializer}) as necessary. + * Either way, serializer will be properly resolved + * (via {@link com.fasterxml.jackson.databind.JsonSerializer#resolve}). * * @param annotated Annotated entity that contained definition * @param serDef Serializer definition: either an instance or class @@ -935,8 +963,6 @@ public abstract JsonSerializer serializerInstance(Annotated annotated, * * @param forProperty (optional) If filter is created for a property, that property; * `null` if filter created via defaulting, global or per-type. - * - * @since 2.9 */ public abstract Object includeFilterInstance(BeanPropertyDefinition forProperty, Class filterClass) @@ -945,38 +971,33 @@ public abstract Object includeFilterInstance(BeanPropertyDefinition forProperty, /** * Follow-up method that may be called after calling {@link #includeFilterInstance}, * to check handling of `null` values by the filter. - * - * @since 2.9 */ public abstract boolean includeFilterSuppressNulls(Object filter) throws JsonMappingException; /* - /********************************************************** + /********************************************************************** /* Support for contextualization - /********************************************************** + /********************************************************************** */ /** * Method called for primary property serializers (ones * directly created to serialize values of a POJO property), - * to handle details of resolving - * {@link ContextualSerializer} with given property context. + * to handle details of contextualization, calling + * {@link JsonSerializer#createContextual(SerializerProvider, BeanProperty)} with given property context. * * @param property Property for which the given primary serializer is used; never null. - * - * @since 2.3 */ - public JsonSerializer handlePrimaryContextualization(JsonSerializer ser, + @SuppressWarnings("unchecked") + public JsonSerializer handlePrimaryContextualization(JsonSerializer ser, BeanProperty property) throws JsonMappingException { if (ser != null) { - if (ser instanceof ContextualSerializer) { - ser = ((ContextualSerializer) ser).createContextual(this, property); - } + ser = ser.createContextual(this, property); } - return ser; + return (JsonSerializer) ser; } /** @@ -984,33 +1005,43 @@ public JsonSerializer handlePrimaryContextualization(JsonSerializer ser, * NOT directly created to serialize values of a POJO property * but instead created as a dependant serializer -- such as value serializers * for structured types, or serializers for root values) - * to handle details of resolving - * {@link ContextualDeserializer} with given property context. + * to handle details of contextualization, calling + * {@link JsonSerializer#createContextual(SerializerProvider, BeanProperty)} with given property context. * Given that these serializers are not directly related to given property * (or, in case of root value property, to any property), annotations * accessible may or may not be relevant. * * @param property Property for which serializer is used, if any; null * when deserializing root values - * - * @since 2.3 */ - public JsonSerializer handleSecondaryContextualization(JsonSerializer ser, + @SuppressWarnings("unchecked") + public JsonSerializer handleSecondaryContextualization(JsonSerializer ser, BeanProperty property) throws JsonMappingException { if (ser != null) { - if (ser instanceof ContextualSerializer) { - ser = ((ContextualSerializer) ser).createContextual(this, property); - } + ser = ser.createContextual(this, property); } - return ser; + return (JsonSerializer) ser; } - + + /** + * @since 3.0 + */ + @SuppressWarnings("unchecked") + public JsonSerializer handleRootContextualization(JsonSerializer ser) + throws JsonMappingException + { + if (ser != null) { + ser = ser.createContextual(this, null); + } + return (JsonSerializer) ser; + } + /* - /******************************************************** + /********************************************************************** /* Convenience methods for serializing using default methods - /******************************************************** + /********************************************************************** */ /** @@ -1019,21 +1050,14 @@ public JsonSerializer handleSecondaryContextualization(JsonSerializer ser, * be called for all values including field and Map values, but usually * field values are best handled calling * {@link #defaultSerializeField} instead. + * + * @deprecated Use {@link #writeValue(JsonGenerator, Object)} instead */ - public final void defaultSerializeValue(Object value, JsonGenerator gen) throws IOException - { - if (value == null) { - if (_stdNullValueSerializer) { // minor perf optimization - gen.writeNull(); - } else { - _nullValueSerializer.serialize(null, gen, this); - } - } else { - Class cls = value.getClass(); - findTypedValueSerializer(cls, true, null).serialize(value, gen, this); - } + @Deprecated // since 3.0 + public final void defaultSerializeValue(Object value, JsonGenerator gen) throws IOException { + writeValue(gen, value); } - + /** * Convenience method that will serialize given field with specified * value. Value may be null. Serializer is done using the usual @@ -1043,19 +1067,7 @@ public final void defaultSerializeField(String fieldName, Object value, JsonGene throws IOException { gen.writeFieldName(fieldName); - if (value == null) { - /* Note: can't easily check for suppression at this point - * any more; caller must check it. - */ - if (_stdNullValueSerializer) { // minor perf optimization - gen.writeNull(); - } else { - _nullValueSerializer.serialize(null, gen, this); - } - } else { - Class cls = value.getClass(); - findTypedValueSerializer(cls, true, null).serialize(value, gen, this); - } + writeValue(gen, value); } /** @@ -1119,7 +1131,14 @@ public void defaultSerializeDateKey(Date date, JsonGenerator gen) throws IOExcep } } - public final void defaultSerializeNull(JsonGenerator gen) throws IOException + /** + * Method to call when serializing a `null` value (POJO property, Map entry value, + * Collection/array element) using configured standard mechanism. Note that this + * does NOT consider filtering any more as value is expected. + * + * @since 3.0 (in 2.x was called defaultSerializeNull) + */ + public final void defaultSerializeNullValue(JsonGenerator gen) throws IOException { if (_stdNullValueSerializer) { // minor perf optimization gen.writeNull(); @@ -1129,31 +1148,19 @@ public final void defaultSerializeNull(JsonGenerator gen) throws IOException } /* - /******************************************************** + /********************************************************************** /* Error reporting - /******************************************************** + /********************************************************************** */ - /** - * Helper method called to indicate problem; default behavior is to construct and - * throw a {@link JsonMappingException}, but in future may collect more than one - * and only throw after certain number, or at the end of serialization. - * - * @since 2.8 - */ - public void reportMappingProblem(String message, Object... args) throws JsonMappingException { - throw mappingException(message, args); - } - /** * Helper method called to indicate problem in POJO (serialization) definitions or settings * regarding specific Java type, unrelated to actual JSON content to map. * Default behavior is to construct and throw a {@link JsonMappingException}. - * - * @since 2.9 */ public T reportBadTypeDefinition(BeanDescription bean, - String msg, Object... msgArgs) throws JsonMappingException { + String msg, Object... msgArgs) throws JsonMappingException + { String beanDesc = "N/A"; if (bean != null) { beanDesc = ClassUtil.nameOf(bean.getBeanClass()); @@ -1167,8 +1174,6 @@ public T reportBadTypeDefinition(BeanDescription bean, * Helper method called to indicate problem in POJO (serialization) definitions or settings * regarding specific property (of a type), unrelated to actual JSON content to map. * Default behavior is to construct and throw a {@link JsonMappingException}. - * - * @since 2.9 */ public T reportBadPropertyDefinition(BeanDescription bean, BeanPropertyDefinition prop, String message, Object... msgArgs) throws JsonMappingException { @@ -1215,12 +1220,22 @@ public T reportBadDefinition(Class raw, String msg, Throwable cause) * Helper method called to indicate problem; default behavior is to construct and * throw a {@link JsonMappingException}, but in future may collect more than one * and only throw after certain number, or at the end of serialization. - * - * @since 2.8 */ public void reportMappingProblem(Throwable t, String message, Object... msgArgs) throws JsonMappingException { - message = _format(message, msgArgs); - throw JsonMappingException.from(getGenerator(), message, t); + throw _mappingProblem(t, message, msgArgs); + } + + protected JsonMappingException _mappingProblem(Throwable t, String message, Object... msgArgs) { + return JsonMappingException.from(getGenerator(), _format(message, msgArgs), t); + } + + /** + * Helper method called to indicate problem; default behavior is to construct and + * throw a {@link JsonMappingException}, but in future may collect more than one + * and only throw after certain number, or at the end of serialization. + */ + public void reportMappingProblem(String message, Object... msgArgs) throws JsonMappingException { + throw JsonMappingException.from(getGenerator(), _format(message, msgArgs)); } @Override @@ -1231,46 +1246,6 @@ public JsonMappingException invalidTypeIdException(JavaType baseType, String typ return InvalidTypeIdException.from(null, _colonConcat(msg, extraDesc), baseType, typeId); } - /* - /******************************************************** - /* Error reporting, deprecated methods - /******************************************************** - */ - - /** - * Factory method for constructing a {@link JsonMappingException}; - * usually only indirectly used by calling - * {@link #reportMappingProblem(String, Object...)}. - * - * @since 2.6 - * - * @deprecated Since 2.9 - */ - @Deprecated // since 2.9 - public JsonMappingException mappingException(String message, Object... msgArgs) { - return JsonMappingException.from(getGenerator(), _format(message, msgArgs)); - } - - /** - * Factory method for constructing a {@link JsonMappingException}; - * usually only indirectly used by calling - * {@link #reportMappingProblem(Throwable, String, Object...)} - * - * @since 2.8 - * - * @deprecated Since 2.9 - */ - @Deprecated // since 2.9 - protected JsonMappingException mappingException(Throwable t, String message, Object... msgArgs) { - return JsonMappingException.from(getGenerator(), _format(message, msgArgs), t); - } - - /* - /******************************************************** - /* Helper methods - /******************************************************** - */ - protected void _reportIncompatibleRootType(Object value, JavaType rootType) throws IOException { // One special case: allow primitive/wrapper type coercion @@ -1286,138 +1261,10 @@ protected void _reportIncompatibleRootType(Object value, JavaType rootType) thro rootType, ClassUtil.classNameOf(value))); } - /** - * Method that will try to find a serializer, either from cache - * or by constructing one; but will not return an "unknown" serializer - * if this cannot be done but rather returns null. - * - * @return Serializer if one can be found, null if not. - */ - protected JsonSerializer _findExplicitUntypedSerializer(Class runtimeType) - throws JsonMappingException - { - // Fast lookup from local lookup thingy works? - JsonSerializer ser = _knownSerializers.untypedValueSerializer(runtimeType); - 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; - } - return ser; - } - - /* - /********************************************************** - /* Low-level methods for actually constructing and initializing - /* serializers - /********************************************************** - */ - - /** - * Method that will try to construct a value serializer; and if - * one is successfully created, cache it for reuse. - */ - protected JsonSerializer _createAndCacheUntypedSerializer(Class rawType) - throws JsonMappingException - { - JavaType fullType = _config.constructType(rawType); - JsonSerializer ser; - try { - ser = _createUntypedSerializer(fullType); - } catch (IllegalArgumentException iae) { - // We better only expose checked exceptions, since those - // are what caller is expected to handle - ser = null; // doesn't matter but compiler whines otherwise - reportMappingProblem(iae, ClassUtil.exceptionMessage(iae)); - } - - if (ser != null) { - // 21-Dec-2015, tatu: Best to cache for both raw and full-type key - _serializerCache.addAndResolveNonTypedSerializer(rawType, fullType, ser, this); - } - return ser; - } - - protected JsonSerializer _createAndCacheUntypedSerializer(JavaType type) - throws JsonMappingException - { - JsonSerializer ser; - try { - ser = _createUntypedSerializer(type); - } catch (IllegalArgumentException iae) { - // We better only expose checked exceptions, since those - // are what caller is expected to handle - ser = null; - reportMappingProblem(iae, ClassUtil.exceptionMessage(iae)); - } - - if (ser != null) { - // 21-Dec-2015, tatu: Should we also cache using raw key? - _serializerCache.addAndResolveNonTypedSerializer(type, ser, this); - } - return ser; - } - - /** - * @since 2.1 - */ - protected JsonSerializer _createUntypedSerializer(JavaType type) - throws JsonMappingException - { - /* 27-Mar-2015, tatu: Wish I knew exactly why/what, but [databind#738] - * can be prevented by synchronizing on cache (not on 'this', however, - * since there's one instance per serialization). - * Perhaps not-yet-resolved instance might be exposed too early to callers. - */ - // 13-Apr-2018, tatu: Problem does NOT occur any more with late 2.8.x and 2.9.x - // versions, likely due to concurrency fixes for `AnnotatedClass` introspection. - // This sync block could probably be removed; but to minimize any risk of - // regression sync block will only be removed from 3.0. - synchronized (_serializerCache) { - // 17-Feb-2013, tatu: Used to call deprecated method (that passed property) - return (JsonSerializer)_serializerFactory.createSerializer(this, type); - } - } - - /** - * Helper method called to resolve and contextualize given - * serializer, if and as necessary. - */ - @SuppressWarnings("unchecked") - protected JsonSerializer _handleContextualResolvable(JsonSerializer ser, - BeanProperty property) - throws JsonMappingException - { - if (ser instanceof ResolvableSerializer) { - ((ResolvableSerializer) ser).resolve(this); - } - return (JsonSerializer) handleSecondaryContextualization(ser, property); - } - - @SuppressWarnings("unchecked") - protected JsonSerializer _handleResolvable(JsonSerializer ser) - throws JsonMappingException - { - if (ser instanceof ResolvableSerializer) { - ((ResolvableSerializer) ser).resolve(this); - } - return (JsonSerializer) ser; - } - /* - /********************************************************** - /* Internal methods - /********************************************************** + /********************************************************************** + /* Internal methods, other + /********************************************************************** */ protected final DateFormat _dateFormat() @@ -1425,10 +1272,9 @@ protected final DateFormat _dateFormat() if (_dateFormat != null) { return _dateFormat; } - /* At this point, all timezone configuration should have occurred, with respect - * to default dateformat configuration. But we still better clone - * an instance as formatters are stateful, not thread-safe. - */ + // At this point, all timezone configuration should have occurred, with respect + // to default dateformat configuration. But we still better clone + // an instance as formatters are stateful, not thread-safe. DateFormat df = _config.getDateFormat(); _dateFormat = df = (DateFormat) df.clone(); // [databind#939]: 26-Sep-2015, tatu: With 2.6, formatter has been (pre)configured diff --git a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonDeserialize.java b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonDeserialize.java index ba6a35f497..ee91a093e2 100644 --- a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonDeserialize.java +++ b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonDeserialize.java @@ -31,7 +31,7 @@ * class and NOT on target POJO class: for example @JsonIgnoreProperties should be on * Builder to prevent "unknown property" errors. * - *
  • Similarly configuration overrides (see {@link com.fasterxml.jackson.databind.ObjectMapper#configOverride}) + *
  • Similarly configuration overrides (see {@link com.fasterxml.jackson.databind.cfg.MapperBuilder#withConfigOverride}) * should be targeted at Builder class, not target POJO class. *
  • * 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 507cd3401b..2772e761db 100644 --- a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonNaming.java +++ b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonNaming.java @@ -10,8 +10,6 @@ * Note that if the {@link #value} property is omitted, its default value * means "use default naming" (that is, no alternate naming method is used). * This can be used as an override with mix-ins. - * - * @since 2.1 */ @Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) diff --git a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonSerialize.java b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonSerialize.java index 19767feeb3..7f66e23dca 100644 --- a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonSerialize.java +++ b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonSerialize.java @@ -69,8 +69,6 @@ public Class keyUsing() * default null serializer. * Note that using this property when annotation types (classes) has * no effect currently (it is possible this could be improved in future). - * - * @since 2.3 */ @SuppressWarnings("rawtypes") // to work around JDK8 bug wrt Class-valued annotation properties public Class nullsUsing() @@ -125,14 +123,12 @@ public Class nullsUsing() */ public Typing typing() default Typing.DEFAULT_TYPING; - // // // Annotations for specifying intermediate Converters (2.2+) - + // // // Annotations for specifying intermediate Converters + /** * Which helper object is to be used to convert type into something * that Jackson knows how to serialize; either because base type * cannot be serialized easily, or just to alter serialization. - * - * @since 2.2 */ @SuppressWarnings("rawtypes") // to work around JDK8 bug wrt Class-valued annotation properties public Class converter() default Converter.None.class; @@ -144,100 +140,9 @@ public Class nullsUsing() * it can only be used as property annotation: this because association between * container and value types is loose and as such converters seldom make sense * for such usage. - * - * @since 2.2 */ @SuppressWarnings("rawtypes") // to work around JDK8 bug wrt Class-valued annotation properties public Class contentConverter() default Converter.None.class; - - // // // Annotation(s) for inclusion criteria - - /** - * Which properties of annotated Bean are - * to be included in serialization (has no effect on other types - * like enums, primitives or collections). - * Choices are "all", "properties that have value other than null" - * and "properties that have non-default value" (i.e. default value - * being property setting for a Bean constructed with default no-arg - * constructor, often null). - *

    - * This property has been replaced by special-purpose {@link com.fasterxml.jackson.annotation.JsonInclude} - * annotation, introduced in Jackson 2.0. - *

    - * Note that Jackson 2.3 changed default to DEFAULT_INCLUSION, - * which is roughly same as saying "whatever". This is important because - * it allows hierarchic default values to be used. - * - * @deprecated As of Jackson 2.0, this annotation has been replaced - * by {@link com.fasterxml.jackson.annotation.JsonInclude} - */ - @Deprecated - public Inclusion include() default Inclusion.DEFAULT_INCLUSION; - - /* - /********************************************************** - /* Value enumerations needed - /********************************************************** - */ - - /** - * Enumeration used with {@link JsonSerialize#include} property - * to define which properties - * of Java Beans are to be included in serialization - */ - @Deprecated // since 2.0, marked deprecated in 2.6 - public enum Inclusion - { - /** - * Value that indicates that properties are to be always included, - * independent of value - */ - ALWAYS, - - /** - * Value that indicates that only properties with non-null - * values are to be included. - */ - NON_NULL, - - /** - * Value that indicates that only properties that have values - * that differ from default settings (meaning values they have - * when Bean is constructed with its no-arguments constructor) - * are to be included. Value is generally not useful with - * {@link java.util.Map}s, since they have no default values; - * and if used, works same as {@link #ALWAYS}. - */ - NON_DEFAULT, - - /** - * Value that indicates that only properties that have values - * that values that are null or what is considered empty are - * not to be included. - * Emptiness is defined for following type: - *

      - *
    • For {@link java.util.Collection}s and {@link java.util.Map}s, - * method isEmpty() is called; - *
    • - *
    • For Java arrays, empty arrays are ones with length of 0 - *
    • - *
    • For Java {@link java.lang.String}s, length() is called, - * and return value of 0 indicates empty String - *
    • - *
    - * For other types, non-null values are to be included. - */ - NON_EMPTY, - - /** - * Pseudo-value that is used to indicate - * "use whatever is default used at higher level". - * - * @since 2.3 - */ - DEFAULT_INCLUSION - ; - } /** * Enumeration used with {@link JsonSerialize#typing} property @@ -261,8 +166,6 @@ public enum Typing /** * Pseudo-value that is used to indicate * "use whatever is default used at higher level". - * - * @since 2.3 */ DEFAULT_TYPING ; diff --git a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonTypeIdResolver.java b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonTypeIdResolver.java index 8aa41cb522..e8199b8803 100644 --- a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonTypeIdResolver.java +++ b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonTypeIdResolver.java @@ -18,11 +18,9 @@ * for converting between java types and type id included in JSON content. * In simplest cases this can be a simple class with static mapping between * type names and matching classes. - *

    - * NOTE: since 2.4, applicable to properties as well (should have been long time - * ago, but problem only found then) */ -@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER}) +@Target({ElementType.ANNOTATION_TYPE, + ElementType.METHOD, ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @JacksonAnnotation public @interface JsonTypeIdResolver diff --git a/src/main/java/com/fasterxml/jackson/databind/annotation/NoClass.java b/src/main/java/com/fasterxml/jackson/databind/annotation/NoClass.java deleted file mode 100644 index ff8ebae322..0000000000 --- a/src/main/java/com/fasterxml/jackson/databind/annotation/NoClass.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.fasterxml.jackson.databind.annotation; - -/** - * Marker class used with annotations to indicate "no class". This is - * a silly but necessary work-around -- annotations cannot take nulls - * as either default or explicit values. Hence for class values we must - * explicitly use a bogus placeholder to denote equivalent of - * "no class" (for which 'null' is usually the natural choice). - *

    - * Note that since 2.4, most (but not all! - * {@link com.fasterxml.jackson.annotation.JsonTypeInfo#defaultImpl} is - * a notable exception}) usage should start using - * {@link java.lang.Void} instead as the "not defined" marker. - */ -public final class NoClass -{ - private NoClass() { } -} diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/BaseSettings.java b/src/main/java/com/fasterxml/jackson/databind/cfg/BaseSettings.java index e289ab6577..cdc6885a22 100644 --- a/src/main/java/com/fasterxml/jackson/databind/cfg/BaseSettings.java +++ b/src/main/java/com/fasterxml/jackson/databind/cfg/BaseSettings.java @@ -5,11 +5,11 @@ import java.util.TimeZone; import com.fasterxml.jackson.core.Base64Variant; + import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair; -import com.fasterxml.jackson.databind.introspect.ClassIntrospector; import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; -import com.fasterxml.jackson.databind.type.TypeFactory; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.util.StdDateFormat; /** @@ -20,31 +20,18 @@ public final class BaseSettings implements java.io.Serializable { - // for 2.6 private static final long serialVersionUID = 1L; /** * We will use a default TimeZone as the baseline. */ - private static final TimeZone DEFAULT_TIMEZONE = - // TimeZone.getDefault() - /* [databind#915] 05-Nov-2015, tatu: Changed to UTC, from earlier - * baseline of GMT (up to 2.6) - */ - TimeZone.getTimeZone("UTC"); - + private static final TimeZone DEFAULT_TIMEZONE = TimeZone.getTimeZone("UTC"); + /* /********************************************************** /* Configuration settings; introspection, related /********************************************************** */ - - /** - * Introspector used to figure out Bean properties needed for bean serialization - * and deserialization. Overridable so that it is possible to change low-level - * details of introspection, like adding new annotation types. - */ - protected final ClassIntrospector _classIntrospector; /** * Introspector used for accessing annotation value based configuration. @@ -56,13 +43,6 @@ public final class BaseSettings */ protected final PropertyNamingStrategy _propertyNamingStrategy; - /** - * Specific factory used for creating {@link JavaType} instances; - * needed to allow modules to add more custom type handling - * (mostly to support types of non-Java JVM languages) - */ - protected final TypeFactory _typeFactory; - /* /********************************************************** /* Configuration settings; type resolution @@ -70,11 +50,10 @@ public final class BaseSettings */ /** - * Type information handler used for "untyped" values (ones declared - * to have type Object.class) + * Type information handler used for "default typing". */ - protected final TypeResolverBuilder _typeResolverBuilder; - + protected final TypeResolverBuilder _defaultTyper; + /* /********************************************************** /* Configuration settings; other @@ -119,52 +98,35 @@ public final class BaseSettings * Explicitly default {@link Base64Variant} to use for handling * binary data (byte[]), used with data formats * that use base64 encoding (like JSON, CSV). - * - * @since 2.1 */ protected final Base64Variant _defaultBase64; - + + /** + * Factory used for constructing {@link com.fasterxml.jackson.databind.JsonNode} instances. + */ + protected final JsonNodeFactory _nodeFactory; + /* /********************************************************** /* Construction /********************************************************** */ - public BaseSettings(ClassIntrospector ci, AnnotationIntrospector ai, - PropertyNamingStrategy pns, TypeFactory tf, - TypeResolverBuilder typer, DateFormat dateFormat, HandlerInstantiator hi, - Locale locale, TimeZone tz, Base64Variant defaultBase64) + public BaseSettings(AnnotationIntrospector ai, + PropertyNamingStrategy pns, + TypeResolverBuilder defaultTyper, DateFormat dateFormat, HandlerInstantiator hi, + Locale locale, TimeZone tz, Base64Variant defaultBase64, + JsonNodeFactory nodeFactory) { - _classIntrospector = ci; _annotationIntrospector = ai; _propertyNamingStrategy = pns; - _typeFactory = tf; - _typeResolverBuilder = typer; + _defaultTyper = defaultTyper; _dateFormat = dateFormat; _handlerInstantiator = hi; _locale = locale; _timeZone = tz; _defaultBase64 = defaultBase64; - } - - /** - * Turns out we are not necessarily 100% stateless, alas, since {@link ClassIntrospector} - * typically has a cache. So this method is needed for deep copy() of Mapper. - * - * @since 2.9.6 - */ - public BaseSettings copy() { - return new BaseSettings(_classIntrospector.copy(), - _annotationIntrospector, - _propertyNamingStrategy, - _typeFactory, - _typeResolverBuilder, - _dateFormat, - _handlerInstantiator, - _locale, - _timeZone, - _defaultBase64); - + _nodeFactory = nodeFactory; } /* @@ -172,23 +134,14 @@ public BaseSettings copy() { /* Factory methods /********************************************************** */ - - public BaseSettings withClassIntrospector(ClassIntrospector ci) { - if (_classIntrospector == ci) { - return this; - } - return new BaseSettings(ci, _annotationIntrospector, _propertyNamingStrategy, _typeFactory, - _typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale, - _timeZone, _defaultBase64); - } - + public BaseSettings withAnnotationIntrospector(AnnotationIntrospector ai) { if (_annotationIntrospector == ai) { return this; } - return new BaseSettings(_classIntrospector, ai, _propertyNamingStrategy, _typeFactory, - _typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale, - _timeZone, _defaultBase64); + return new BaseSettings(ai, _propertyNamingStrategy, + _defaultTyper, _dateFormat, _handlerInstantiator, _locale, + _timeZone, _defaultBase64, _nodeFactory); } public BaseSettings withInsertedAnnotationIntrospector(AnnotationIntrospector ai) { @@ -199,44 +152,25 @@ public BaseSettings withAppendedAnnotationIntrospector(AnnotationIntrospector ai return withAnnotationIntrospector(AnnotationIntrospectorPair.create(_annotationIntrospector, ai)); } - /* - public BaseSettings withVisibility(PropertyAccessor forMethod, JsonAutoDetect.Visibility visibility) { - return new BaseSettings(_classIntrospector, _annotationIntrospector, - _visibilityChecker.withVisibility(forMethod, visibility), - _propertyNamingStrategy, _typeFactory, - _typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale, - _timeZone, _defaultBase64); - } - */ - - public BaseSettings withPropertyNamingStrategy(PropertyNamingStrategy pns) { + public BaseSettings with(PropertyNamingStrategy pns) { if (_propertyNamingStrategy == pns) { return this; } - return new BaseSettings(_classIntrospector, _annotationIntrospector, pns, _typeFactory, - _typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale, - _timeZone, _defaultBase64); + return new BaseSettings(_annotationIntrospector, pns, + _defaultTyper, _dateFormat, _handlerInstantiator, _locale, + _timeZone, _defaultBase64, _nodeFactory); } - public BaseSettings withTypeFactory(TypeFactory tf) { - if (_typeFactory == tf) { + public BaseSettings with(TypeResolverBuilder typer) { + if (_defaultTyper == typer) { return this; } - return new BaseSettings(_classIntrospector, _annotationIntrospector, _propertyNamingStrategy, tf, - _typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale, - _timeZone, _defaultBase64); - } - - public BaseSettings withTypeResolverBuilder(TypeResolverBuilder typer) { - if (_typeResolverBuilder == typer) { - return this; - } - return new BaseSettings(_classIntrospector, _annotationIntrospector, _propertyNamingStrategy, _typeFactory, + return new BaseSettings(_annotationIntrospector, _propertyNamingStrategy, typer, _dateFormat, _handlerInstantiator, _locale, - _timeZone, _defaultBase64); + _timeZone, _defaultBase64, _nodeFactory); } - public BaseSettings withDateFormat(DateFormat df) { + public BaseSettings with(DateFormat df) { if (_dateFormat == df) { return this; } @@ -245,27 +179,27 @@ public BaseSettings withDateFormat(DateFormat df) { if ((df != null) && hasExplicitTimeZone()) { df = _force(df, _timeZone); } - return new BaseSettings(_classIntrospector, _annotationIntrospector, _propertyNamingStrategy, _typeFactory, - _typeResolverBuilder, df, _handlerInstantiator, _locale, - _timeZone, _defaultBase64); + return new BaseSettings(_annotationIntrospector, _propertyNamingStrategy, + _defaultTyper, df, _handlerInstantiator, _locale, + _timeZone, _defaultBase64, _nodeFactory); } - public BaseSettings withHandlerInstantiator(HandlerInstantiator hi) { + public BaseSettings with(HandlerInstantiator hi) { if (_handlerInstantiator == hi) { return this; } - return new BaseSettings(_classIntrospector, _annotationIntrospector, _propertyNamingStrategy, _typeFactory, - _typeResolverBuilder, _dateFormat, hi, _locale, - _timeZone, _defaultBase64); + return new BaseSettings(_annotationIntrospector, _propertyNamingStrategy, + _defaultTyper, _dateFormat, hi, _locale, + _timeZone, _defaultBase64, _nodeFactory); } public BaseSettings with(Locale l) { if (_locale == l) { return this; } - return new BaseSettings(_classIntrospector, _annotationIntrospector, _propertyNamingStrategy, _typeFactory, - _typeResolverBuilder, _dateFormat, _handlerInstantiator, l, - _timeZone, _defaultBase64); + return new BaseSettings(_annotationIntrospector, _propertyNamingStrategy, + _defaultTyper, _dateFormat, _handlerInstantiator, l, + _timeZone, _defaultBase64, _nodeFactory); } /** @@ -283,35 +217,38 @@ public BaseSettings with(TimeZone tz) } DateFormat df = _force(_dateFormat, tz); - return new BaseSettings(_classIntrospector, _annotationIntrospector, - _propertyNamingStrategy, _typeFactory, - _typeResolverBuilder, df, _handlerInstantiator, _locale, - tz, _defaultBase64); + return new BaseSettings(_annotationIntrospector, + _propertyNamingStrategy, + _defaultTyper, df, _handlerInstantiator, _locale, + tz, _defaultBase64, _nodeFactory); } - /** - * @since 2.1 - */ public BaseSettings with(Base64Variant base64) { if (base64 == _defaultBase64) { return this; } - return new BaseSettings(_classIntrospector, _annotationIntrospector, - _propertyNamingStrategy, _typeFactory, - _typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale, - _timeZone, base64); + return new BaseSettings(_annotationIntrospector, + _propertyNamingStrategy, + _defaultTyper, _dateFormat, _handlerInstantiator, _locale, + _timeZone, base64, _nodeFactory); } - + + public BaseSettings with(JsonNodeFactory nodeFactory) { + if (nodeFactory == _nodeFactory) { + return this; + } + return new BaseSettings(_annotationIntrospector, + _propertyNamingStrategy, + _defaultTyper, _dateFormat, _handlerInstantiator, _locale, + _timeZone, _defaultBase64, nodeFactory); + } + /* /********************************************************** /* API /********************************************************** */ - public ClassIntrospector getClassIntrospector() { - return _classIntrospector; - } - public AnnotationIntrospector getAnnotationIntrospector() { return _annotationIntrospector; } @@ -320,12 +257,8 @@ public PropertyNamingStrategy getPropertyNamingStrategy() { return _propertyNamingStrategy; } - public TypeFactory getTypeFactory() { - return _typeFactory; - } - - public TypeResolverBuilder getTypeResolverBuilder() { - return _typeResolverBuilder; + public TypeResolverBuilder getDefaultTyper() { + return _defaultTyper; } public DateFormat getDateFormat() { @@ -349,8 +282,6 @@ public TimeZone getTimeZone() { * Accessor that may be called to determine whether this settings object * has been explicitly configured with a TimeZone (true), or is still * relying on the default settings (false). - * - * @since 2.7 */ public boolean hasExplicitTimeZone() { return (_timeZone != null); @@ -360,6 +291,10 @@ public Base64Variant getBase64Variant() { return _defaultBase64; } + public JsonNodeFactory getNodeFactory() { + return _nodeFactory; + } + /* /********************************************************** /* Helper methods diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigOverride.java b/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigOverride.java index 7557d9e04f..ec8e31f9fc 100644 --- a/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigOverride.java +++ b/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigOverride.java @@ -12,8 +12,6 @@ * type of the property. Such overrides have precedence over annotations * attached to actual type ({@link java.lang.Class}), but can be further * overridden by annotations attached to the property itself. - * - * @since 2.8 */ public abstract class ConfigOverride { @@ -34,8 +32,6 @@ public abstract class ConfigOverride * of POJO in which they are included). * Overrides global defaults, per-POJO inclusion defaults (see {#link {@link #_include}}), * may be overridden by per-property overrides. - * - * @since 2.9 */ protected JsonInclude.Value _includeAsProperty; @@ -47,15 +43,11 @@ public abstract class ConfigOverride /** * Definitions of setter overrides regarding null handling - * - * @since 2.9 */ - protected JsonSetter.Value _setterInfo; + protected JsonSetter.Value _nullHandling; /** * Overrides for auto-detection visibility rules for this type. - * - * @since 2.9 */ protected JsonAutoDetect.Value _visibility; @@ -79,7 +71,7 @@ protected ConfigOverride(ConfigOverride src) { _include = src._include; _includeAsProperty = src._includeAsProperty; _ignorals = src._ignorals; - _setterInfo = src._setterInfo; + _nullHandling = src._nullHandling; _visibility = src._visibility; _isIgnoredType = src._isIgnoredType; _mergeable = src._mergeable; @@ -87,19 +79,20 @@ protected ConfigOverride(ConfigOverride src) { /** * Accessor for immutable "empty" instance that has no configuration overrides defined. - * - * @since 2.9 */ public static ConfigOverride empty() { return Empty.INSTANCE; } public JsonFormat.Value getFormat() { return _format; } + + // @since 3.0 + public JsonFormat.Value getFormatOrEmpty() { + return (_format == null) ? JsonFormat.Value.empty() : _format; + } + public JsonInclude.Value getInclude() { return _include; } - /** - * @since 2.9 - */ public JsonInclude.Value getIncludeAsProperty() { return _includeAsProperty; } public JsonIgnoreProperties.Value getIgnorals() { return _ignorals; } @@ -107,27 +100,29 @@ public static ConfigOverride empty() { public Boolean getIsIgnoredType() { return _isIgnoredType; } - - /** - * @since 2.9 - */ - public JsonSetter.Value getSetterInfo() { return _setterInfo; } - /** - * @since 2.9 - */ + public JsonSetter.Value getNullHandling() { return _nullHandling; } + public JsonAutoDetect.Value getVisibility() { return _visibility; } - /** - * @since 2.9 - */ public Boolean getMergeable() { return _mergeable; } - + + @Override + public String toString() { + return new StringBuilder("[ConfigOverrides ") + .append("format=").append(_format) + .append(", include=").append(_include).append("/").append(_includeAsProperty) + .append(", ignorals=").append(_ignorals) + .append(", ignoredType=").append(_isIgnoredType) + .append(", nulls=").append(_ignorals) + .append(", visibility=").append(_visibility) + .append(", merge=").append(_mergeable) + .toString(); + } + /** * Implementation used solely for "empty" instance; has no mutators * and is not changed by core functionality. - * - * @since 2.9 */ final static class Empty extends ConfigOverride { final static Empty INSTANCE = new Empty(); diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigOverrides.java b/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigOverrides.java index 49c622cc68..75842c9a40 100644 --- a/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigOverrides.java +++ b/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigOverrides.java @@ -2,20 +2,31 @@ import java.util.*; +import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonSetter; + +import com.fasterxml.jackson.core.util.Snapshottable; + import com.fasterxml.jackson.databind.introspect.VisibilityChecker; /** * Container for individual {@link ConfigOverride} values. - * - * @since 2.8 */ public class ConfigOverrides - implements java.io.Serializable + implements java.io.Serializable, + Snapshottable { private static final long serialVersionUID = 1L; + /** + * Convenience value used as the default root setting. + * + * @since 3.0 + */ + public final static JsonInclude.Value INCLUDE_ALL + = JsonInclude.Value.construct(JsonInclude.Include.ALWAYS, JsonInclude.Include.ALWAYS); + /** * Per-type override definitions */ @@ -23,38 +34,25 @@ public class ConfigOverrides // // // Global defaulting - /** - * @since 2.9 - */ protected JsonInclude.Value _defaultInclusion; - /** - * @since 2.9 - */ - protected JsonSetter.Value _defaultSetterInfo; + protected JsonSetter.Value _defaultNullHandling; - /** - * @since 2.9 - */ - protected VisibilityChecker _visibilityChecker; + protected VisibilityChecker _visibilityChecker; - /** - * @since 2.9 - */ protected Boolean _defaultMergeable; /* - /********************************************************** + /********************************************************************** /* Life cycle - /********************************************************** + /********************************************************************** */ public ConfigOverrides() { this(null, - // !!! TODO: change to (ALWAYS, ALWAYS)? - JsonInclude.Value.empty(), + INCLUDE_ALL, JsonSetter.Value.empty(), - VisibilityChecker.Std.defaultInstance(), + VisibilityChecker.defaultInstance(), null ); } @@ -62,16 +60,17 @@ public ConfigOverrides() { protected ConfigOverrides(Map, MutableConfigOverride> overrides, JsonInclude.Value defIncl, JsonSetter.Value defSetter, - VisibilityChecker defVisibility, + VisibilityChecker defVisibility, Boolean defMergeable) { _overrides = overrides; _defaultInclusion = defIncl; - _defaultSetterInfo = defSetter; + _defaultNullHandling = defSetter; _visibilityChecker = defVisibility; _defaultMergeable = defMergeable; } - public ConfigOverrides copy() + @Override + public ConfigOverrides snapshot() { Map, MutableConfigOverride> newOverrides; if (_overrides == null) { @@ -83,13 +82,13 @@ public ConfigOverrides copy() } } return new ConfigOverrides(newOverrides, - _defaultInclusion, _defaultSetterInfo, _visibilityChecker, _defaultMergeable); + _defaultInclusion, _defaultNullHandling, _visibilityChecker, _defaultMergeable); } /* - /********************************************************** + /********************************************************************** /* Per-type override access - /********************************************************** + /********************************************************************** */ public ConfigOverride findOverride(Class type) { @@ -112,64 +111,93 @@ public MutableConfigOverride findOrCreateOverride(Class type) { } /* - /********************************************************** - /* Global defaults access - /********************************************************** + /********************************************************************** + /* Global defaults accessors + /********************************************************************** */ public JsonInclude.Value getDefaultInclusion() { return _defaultInclusion; } - public JsonSetter.Value getDefaultSetterInfo() { - return _defaultSetterInfo; + public JsonSetter.Value getDefaultNullHandling() { + return _defaultNullHandling; } public Boolean getDefaultMergeable() { return _defaultMergeable; } - /** - * @since 2.9 - */ - public VisibilityChecker getDefaultVisibility() { + public VisibilityChecker getDefaultVisibility() { return _visibilityChecker; } - /** - * @since 2.9 + /* + /********************************************************************** + /* Global defaults mutators + /********************************************************************** */ - public void setDefaultInclusion(JsonInclude.Value v) { + + public ConfigOverrides setDefaultInclusion(JsonInclude.Value v) { _defaultInclusion = v; + return this; } - /** - * @since 2.9 - */ - public void setDefaultSetterInfo(JsonSetter.Value v) { - _defaultSetterInfo = v; + public ConfigOverrides setDefaultNullHandling(JsonSetter.Value v) { + _defaultNullHandling = v; + return this; } - /** - * @since 2.9 - */ - public void setDefaultMergeable(Boolean v) { + public ConfigOverrides setDefaultMergeable(Boolean v) { _defaultMergeable = v; + return this; } - /** - * @since 2.9 - */ - public void setDefaultVisibility(VisibilityChecker v) { + public ConfigOverrides setDefaultVisibility(VisibilityChecker v) { _visibilityChecker = v; + return this; + } + + public ConfigOverrides setDefaultVisibility(JsonAutoDetect.Value vis) { + _visibilityChecker = VisibilityChecker.construct(vis); + return this; + } + + /* + /********************************************************************** + /* Standard methods (for diagnostics) + /********************************************************************** + */ + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("[ConfigOverrides ") + .append("incl=").append(_defaultInclusion) + .append(", nulls=").append(_defaultNullHandling) + .append(", merge=").append(_defaultMergeable) + .append(", visibility=").append(_visibilityChecker) + .append(", typed=") + ; + if (_overrides == null) { + sb.append("NLLL"); + } else { + sb.append("(").append(_overrides.size()).append("){"); + TreeMap sorted = new TreeMap<>(); + _overrides.forEach((k, v) -> sorted.put(k.getName(), v)); + sorted.forEach((k, v) -> { + sb.append(String.format("'%s'->%s", k, v)); + }); + sb.append("}"); + } + return sb.append("]").toString(); } /* - /********************************************************** + /********************************************************************** /* Helper methods - /********************************************************** + /********************************************************************** */ - + protected Map, MutableConfigOverride> _newMap() { return new HashMap, MutableConfigOverride>(); } diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/DeserializationContexts.java b/src/main/java/com/fasterxml/jackson/databind/cfg/DeserializationContexts.java new file mode 100644 index 0000000000..d98fb06bab --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/cfg/DeserializationContexts.java @@ -0,0 +1,136 @@ +package com.fasterxml.jackson.databind.cfg; + +import com.fasterxml.jackson.core.FormatSchema; +import com.fasterxml.jackson.core.TokenStreamFactory; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.DefaultDeserializationContext; +import com.fasterxml.jackson.databind.deser.DeserializerCache; +import com.fasterxml.jackson.databind.deser.DeserializerFactory; + +/** + * Factory/builder class that replaces Jackson 2.x concept of "blueprint" instance + * of {@link DeserializationContext}. It will be constructed and configured during + * {@link ObjectMapper} building phase, and will be called once per {@code readValue} + * call to construct actual stateful {@link DeserializationContext} to use during + * serialization. + *

    + * Note that since this object has to be serializable (to allow JDK serialization of + * mapper instances), {@link DeserializationContext} need not be serializable any more. + * + * @since 3.0 + */ +public abstract class DeserializationContexts + implements java.io.Serializable +{ + private static final long serialVersionUID = 3L; + + /* + /********************************************************************** + /* Configuration + /********************************************************************** + */ + + // NOTE! We do not need (or want) to serialize any of these because they + // get passed via `forMapper(...)` call; all we want to serialize is identity + // of this class (and possibly whatever sub-classes may want to retain). + // Hence `transient` modifiers + + /** + * Low-level {@link TokenStreamFactory} that may be used for constructing + * embedded generators. + */ + final transient protected TokenStreamFactory _streamFactory; + + /** + * Factory responsible for constructing standard serializers. + */ + final transient protected DeserializerFactory _deserializerFactory; + + /** + * Cache for doing type-to-value-serializer lookups. + */ + final transient protected DeserializerCache _cache; + + /* + /********************************************************************** + /* Life-cycle + /********************************************************************** + */ + + protected DeserializationContexts() { this(null, null, null); } + + protected DeserializationContexts(TokenStreamFactory tsf, + DeserializerFactory deserializerFactory, DeserializerCache cache) { + _streamFactory = tsf; + _deserializerFactory = deserializerFactory; + _cache = cache; + } + + /** + * Mutant factory method called when instance is actually created for use by mapper + * (as opposed to coming into existence during building, module registration). + * Necessary usually to initialize non-configuration state, such as caching. + */ + public DeserializationContexts forMapper(Object mapper, + TokenStreamFactory tsf, DeserializerFactory deserializerFactory) { + return forMapper(mapper, tsf, deserializerFactory, _defaultCache()); + } + + protected abstract DeserializationContexts forMapper(Object mapper, + TokenStreamFactory tsf, DeserializerFactory deserializerFactory, + DeserializerCache cache); + + /** + * Factory method for constructing context object for individual {@code writeValue} call. + */ + public abstract DefaultDeserializationContext createContext(DeserializationConfig config, + FormatSchema schema, InjectableValues injectables); + + /* + /********************************************************************** + /* Overridable default methods + /********************************************************************** + */ + + /** + * Factory method for constructing per-mapper serializer cache to use. + */ + protected DeserializerCache _defaultCache() { + return new DeserializerCache(); + } + + /* + /********************************************************************** + /* Vanilla implementation + /********************************************************************** + */ + + public static class DefaultImpl extends DeserializationContexts + { + private static final long serialVersionUID = 3L; + + public DefaultImpl() { super(); } + public DefaultImpl(TokenStreamFactory tsf, + DeserializerFactory serializerFactory, DeserializerCache cache) { + super(tsf, serializerFactory, cache); + } + + @Override + public DeserializationContexts forMapper(Object mapper, + TokenStreamFactory tsf, DeserializerFactory serializerFactory, + DeserializerCache cache) { + return new DefaultImpl(tsf, serializerFactory, cache); + } + + @Override + public DefaultDeserializationContext createContext(DeserializationConfig config, + FormatSchema schema, InjectableValues injectables) { + return new DefaultDeserializationContext.Impl(_streamFactory, + _deserializerFactory, _cache, + config, schema, injectables); + } + + // As per name, just for testing + public DeserializerCache cacheForTests() { return _cache; } + } +} diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/DeserializerFactoryConfig.java b/src/main/java/com/fasterxml/jackson/databind/cfg/DeserializerFactoryConfig.java index 20b048f3aa..1967ea0012 100644 --- a/src/main/java/com/fasterxml/jackson/databind/cfg/DeserializerFactoryConfig.java +++ b/src/main/java/com/fasterxml/jackson/databind/cfg/DeserializerFactoryConfig.java @@ -1,6 +1,5 @@ package com.fasterxml.jackson.databind.cfg; -import com.fasterxml.jackson.databind.AbstractTypeResolver; import com.fasterxml.jackson.databind.deser.*; import com.fasterxml.jackson.databind.deser.std.StdKeyDeserializers; import com.fasterxml.jackson.databind.util.ArrayBuilders; @@ -10,20 +9,17 @@ * Configuration settings container class for {@link DeserializerFactory}. */ public class DeserializerFactoryConfig - implements java.io.Serializable // since 2.1 + implements java.io.Serializable { - private static final long serialVersionUID = 1L; // since 2.5 + private static final long serialVersionUID = 3L; protected final static Deserializers[] NO_DESERIALIZERS = new Deserializers[0]; protected final static BeanDeserializerModifier[] NO_MODIFIERS = new BeanDeserializerModifier[0]; - protected final static AbstractTypeResolver[] NO_ABSTRACT_TYPE_RESOLVERS = new AbstractTypeResolver[0]; protected final static ValueInstantiators[] NO_VALUE_INSTANTIATORS = new ValueInstantiators[0]; /** * By default we plug default key deserializers using as "just another" set of * of key deserializers. - * - * @since 2.2 */ protected final static KeyDeserializers[] DEFAULT_KEY_DESERIALIZERS = new KeyDeserializers[] { new StdKeyDeserializers() @@ -47,13 +43,6 @@ public class DeserializerFactoryConfig */ protected final BeanDeserializerModifier[] _modifiers; - /** - * List of objects that may be able to resolve abstract types to - * concrete types. Used by functionality like "mr Bean" to materialize - * types as needed. - */ - protected final AbstractTypeResolver[] _abstractTypeResolvers; - /** * List of objects that know how to create instances of POJO types; * possibly using custom construction (non-annoted constructors; factory @@ -68,7 +57,7 @@ public class DeserializerFactoryConfig * handlers. */ public DeserializerFactoryConfig() { - this(null, null, null, null, null); + this(null, null, null, null); } /** @@ -78,7 +67,6 @@ public DeserializerFactoryConfig() { protected DeserializerFactoryConfig(Deserializers[] allAdditionalDeserializers, KeyDeserializers[] allAdditionalKeyDeserializers, BeanDeserializerModifier[] modifiers, - AbstractTypeResolver[] atr, ValueInstantiators[] vi) { _additionalDeserializers = (allAdditionalDeserializers == null) ? @@ -86,7 +74,6 @@ protected DeserializerFactoryConfig(Deserializers[] allAdditionalDeserializers, _additionalKeyDeserializers = (allAdditionalKeyDeserializers == null) ? DEFAULT_KEY_DESERIALIZERS : allAdditionalKeyDeserializers; _modifiers = (modifiers == null) ? NO_MODIFIERS : modifiers; - _abstractTypeResolvers = (atr == null) ? NO_ABSTRACT_TYPE_RESOLVERS : atr; _valueInstantiators = (vi == null) ? NO_VALUE_INSTANTIATORS : vi; } @@ -103,7 +90,7 @@ public DeserializerFactoryConfig withAdditionalDeserializers(Deserializers addit } Deserializers[] all = ArrayBuilders.insertInListNoDup(_additionalDeserializers, additional); return new DeserializerFactoryConfig(all, _additionalKeyDeserializers, _modifiers, - _abstractTypeResolvers, _valueInstantiators); + _valueInstantiators); } /** @@ -119,7 +106,7 @@ public DeserializerFactoryConfig withAdditionalKeyDeserializers(KeyDeserializers } KeyDeserializers[] all = ArrayBuilders.insertInListNoDup(_additionalKeyDeserializers, additional); return new DeserializerFactoryConfig(_additionalDeserializers, all, _modifiers, - _abstractTypeResolvers, _valueInstantiators); + _valueInstantiators); } /** @@ -134,24 +121,7 @@ public DeserializerFactoryConfig withDeserializerModifier(BeanDeserializerModifi throw new IllegalArgumentException("Cannot pass null modifier"); } BeanDeserializerModifier[] all = ArrayBuilders.insertInListNoDup(_modifiers, modifier); - return new DeserializerFactoryConfig(_additionalDeserializers, _additionalKeyDeserializers, all, - _abstractTypeResolvers, _valueInstantiators); - } - - /** - * Fluent/factory method used to construct a configuration object that - * has same configuration as this instance plus one additional - * abstract type resolver. - * Added resolver has the highest priority (that is, it - * gets called before any already registered resolver). - */ - public DeserializerFactoryConfig withAbstractTypeResolver(AbstractTypeResolver resolver) - { - if (resolver == null) { - throw new IllegalArgumentException("Cannot pass null resolver"); - } - AbstractTypeResolver[] all = ArrayBuilders.insertInListNoDup(_abstractTypeResolvers, resolver); - return new DeserializerFactoryConfig(_additionalDeserializers, _additionalKeyDeserializers, _modifiers, + return new DeserializerFactoryConfig(_additionalDeserializers, _additionalKeyDeserializers, all, _valueInstantiators); } @@ -171,8 +141,8 @@ public DeserializerFactoryConfig withValueInstantiators(ValueInstantiators insta throw new IllegalArgumentException("Cannot pass null resolver"); } ValueInstantiators[] all = ArrayBuilders.insertInListNoDup(_valueInstantiators, instantiators); - return new DeserializerFactoryConfig(_additionalDeserializers, _additionalKeyDeserializers, _modifiers, - _abstractTypeResolvers, all); + return new DeserializerFactoryConfig(_additionalDeserializers, _additionalKeyDeserializers, + _modifiers, all); } public boolean hasDeserializers() { return _additionalDeserializers.length > 0; } @@ -181,8 +151,6 @@ public DeserializerFactoryConfig withValueInstantiators(ValueInstantiators insta public boolean hasDeserializerModifiers() { return _modifiers.length > 0; } - public boolean hasAbstractTypeResolvers() { return _abstractTypeResolvers.length > 0; } - public boolean hasValueInstantiators() { return _valueInstantiators.length > 0; } public Iterable deserializers() { @@ -197,10 +165,6 @@ public Iterable deserializerModifiers() { return new ArrayIterator(_modifiers); } - public Iterable abstractTypeResolvers() { - return new ArrayIterator(_abstractTypeResolvers); - } - public Iterable valueInstantiators() { return new ArrayIterator(_valueInstantiators); } diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/GeneratorSettings.java b/src/main/java/com/fasterxml/jackson/databind/cfg/GeneratorSettings.java new file mode 100644 index 0000000000..74cf004a14 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/cfg/GeneratorSettings.java @@ -0,0 +1,147 @@ +package com.fasterxml.jackson.databind.cfg; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.io.CharacterEscapes; +import com.fasterxml.jackson.core.io.SerializedString; +import com.fasterxml.jackson.core.util.Instantiatable; + +/** + * Helper class used for containing settings specifically related + * to (re)configuring {@link JsonGenerator} constructed for + * writing output. + */ +public final class GeneratorSettings + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + protected final static GeneratorSettings EMPTY = new GeneratorSettings(null, null, null, null); + + /** + * Also need to use a null marker for root value separator + */ + protected final static SerializedString NULL_ROOT_VALUE_SEPARATOR = new SerializedString(""); + + public final static GeneratorSettings empty = new GeneratorSettings(null, null, null, + null); + + /** + * To allow for dynamic enabling/disabling of pretty printing, + * pretty printer can be optionally configured for writer + * as well + */ + public final PrettyPrinter prettyPrinter; + + /** + * When using data format that uses a schema, schema is passed + * to generator. + */ + public final FormatSchema schema; + + /** + * Caller may want to specify character escaping details, either as + * defaults, or on call-by-call basis. + */ + public final CharacterEscapes characterEscapes; + + /** + * Caller may want to override so-called "root value separator", + * String added (verbatim, with no quoting or escaping) between + * values in root context. Default value is a single space character, + * but this is often changed to linefeed. + */ + public final SerializableString rootValueSeparator; + + public GeneratorSettings(PrettyPrinter pp, FormatSchema sch, + CharacterEscapes esc, SerializableString rootSep) { + prettyPrinter = pp; + schema = sch; + characterEscapes = esc; + rootValueSeparator = rootSep; + } + + public static GeneratorSettings empty() { + return EMPTY; + } + + public GeneratorSettings with(PrettyPrinter pp) { + return (pp == prettyPrinter) ? this + : new GeneratorSettings(pp, schema, characterEscapes, rootValueSeparator); + } + + public GeneratorSettings with(FormatSchema sch) { + return (schema == sch) ? this + : new GeneratorSettings(prettyPrinter, sch, characterEscapes, rootValueSeparator); + } + + public GeneratorSettings with(CharacterEscapes esc) { + return (characterEscapes == esc) ? this + : new GeneratorSettings(prettyPrinter, schema, esc, rootValueSeparator); + } + + public GeneratorSettings withRootValueSeparator(String sep) { + if (sep == null) { + if (rootValueSeparator == NULL_ROOT_VALUE_SEPARATOR) { + return this; + } + return new GeneratorSettings(prettyPrinter, schema, characterEscapes, NULL_ROOT_VALUE_SEPARATOR); + } + if (sep.equals(_rootValueSeparatorAsString())) { + return this; + } + return new GeneratorSettings(prettyPrinter, schema, characterEscapes, + new SerializedString(sep)); + } + + public GeneratorSettings withRootValueSeparator(SerializableString sep) { + if (sep == null) { + if (rootValueSeparator == null) { + return this; + } + return new GeneratorSettings(prettyPrinter, schema, characterEscapes, null); + } + if (sep.equals(rootValueSeparator)) { + return this; + } + return new GeneratorSettings(prettyPrinter, schema, characterEscapes, sep); + } + + private final String _rootValueSeparatorAsString() { + return (rootValueSeparator == null) ? null : rootValueSeparator.getValue(); + } + + /* + /********************************************************** + /* ObjectWriteContext support methods + /********************************************************** + */ + + public FormatSchema getSchema() { + return schema; + } + + public CharacterEscapes getCharacterEscapes() { + return characterEscapes; + } + + public PrettyPrinter getPrettyPrinter() { + PrettyPrinter pp = prettyPrinter; + if (pp != null) { + if (pp instanceof Instantiatable) { + pp = (PrettyPrinter) ((Instantiatable) pp).createInstance(); + } + return pp; + } + return null; + } + + public SerializableString getRootValueSeparator(SerializableString defaultSep) { + if (rootValueSeparator == null) { + return defaultSep; + } + if (rootValueSeparator == NULL_ROOT_VALUE_SEPARATOR) { + return null; + } + return rootValueSeparator; + } +} \ No newline at end of file diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperBuilder.java b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperBuilder.java index be0cb5ef61..2a26ac2659 100644 --- a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperBuilder.java +++ b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperBuilder.java @@ -4,50 +4,434 @@ import java.security.PrivilegedAction; import java.text.DateFormat; import java.util.*; +import java.util.function.Consumer; +import java.util.function.UnaryOperator; -import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; +import com.fasterxml.jackson.core.util.Snapshottable; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.deser.*; +import com.fasterxml.jackson.databind.introspect.BasicClassIntrospector; +import com.fasterxml.jackson.databind.introspect.ClassIntrospector; +import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector; +import com.fasterxml.jackson.databind.introspect.MixInResolver; +import com.fasterxml.jackson.databind.introspect.MixInHandler; import com.fasterxml.jackson.databind.introspect.VisibilityChecker; import com.fasterxml.jackson.databind.jsontype.NamedType; import com.fasterxml.jackson.databind.jsontype.SubtypeResolver; +import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; +import com.fasterxml.jackson.databind.jsontype.TypeResolverProvider; +import com.fasterxml.jackson.databind.jsontype.impl.DefaultTypeResolverBuilder; +import com.fasterxml.jackson.databind.jsontype.impl.StdSubtypeResolver; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.ser.*; import com.fasterxml.jackson.databind.type.TypeFactory; +import com.fasterxml.jackson.databind.type.TypeModifier; +import com.fasterxml.jackson.databind.util.ArrayBuilders; +import com.fasterxml.jackson.databind.util.LinkedNode; +import com.fasterxml.jackson.databind.util.RootNameLookup; +import com.fasterxml.jackson.databind.util.StdDateFormat; /** - * Jackson 3 will introduce fully immutable, builder-based system for constructing - * {@link ObjectMapper}s. Same can not be done with 2.10 for backwards-compatibility - * reasons; but we can offer sort of "fake" builder, which simply encapsulates - * configuration calls. The main (and only) point is to allow gradual upgrade. + * Since {@link ObjectMapper} instances are immutable in Jackson 3.x for full thread-safety, + * we need means to construct configured instances. This is the shared base API for + * builders for all types of mappers. * - * @since 2.10 + * @since 3.0 */ public abstract class MapperBuilder> { - protected final M _mapper; + protected final static int DEFAULT_MAPPER_FEATURES = MapperConfig.collectFeatureDefaults(MapperFeature.class); + protected final static int DEFAULT_SER_FEATURES = MapperConfig.collectFeatureDefaults(SerializationFeature.class); + protected final static int DEFAULT_DESER_FEATURES = MapperConfig.collectFeatureDefaults(DeserializationFeature.class); + + protected final static PrettyPrinter DEFAULT_PRETTY_PRINTER = new DefaultPrettyPrinter(); + + // 16-May-2009, tatu: Ditto ^^^ + protected final static AnnotationIntrospector DEFAULT_ANNOTATION_INTROSPECTOR = new JacksonAnnotationIntrospector(); + + protected final static BaseSettings DEFAULT_BASE_SETTINGS = new BaseSettings( + DEFAULT_ANNOTATION_INTROSPECTOR, + null, + null, // no default typing, by default + StdDateFormat.instance, null, + Locale.getDefault(), + null, // to indicate "use Jackson default TimeZone" (UTC since Jackson 2.7) + Base64Variants.getDefaultVariant(), + JsonNodeFactory.instance + ); + + protected final static TypeResolverProvider DEFAULT_TYPE_RESOLVER_PROVIDER = new TypeResolverProvider(); + + protected final static AbstractTypeResolver[] NO_ABSTRACT_TYPE_RESOLVERS = new AbstractTypeResolver[0]; + /* + /********************************************************************** + /* Basic settings + /********************************************************************** + */ + + protected BaseSettings _baseSettings; + + /** + * Underlying stream factory + */ + protected TokenStreamFactory _streamFactory; + + /** + * Various configuration setting overrides, both global base settings + * and per-class overrides. + */ + protected final ConfigOverrides _configOverrides; + + /* + /********************************************************************** + /* Modules + /********************************************************************** + */ + + /** + * Modules registered for addition, indexed by registration id. + */ + protected Map _modules; + + /* + /********************************************************************** + /* Handlers, introspection + /********************************************************************** + */ + + /** + * Specific factory used for creating {@link JavaType} instances; + * needed to allow modules to add more custom type handling + * (mostly to support types of non-Java JVM languages) + */ + protected TypeFactory _typeFactory; + + /** + * Introspector used to figure out Bean properties needed for bean serialization + * and deserialization. Overridable so that it is possible to change low-level + * details of introspection, like adding new annotation types. + */ + protected ClassIntrospector _classIntrospector; + + /** + * Entity responsible for construction actual type resolvers + * ({@link com.fasterxml.jackson.databind.jsontype.TypeSerializer}s, + * {@link com.fasterxml.jackson.databind.jsontype.TypeDeserializer}s). + */ + protected TypeResolverProvider _typeResolverProvider; + + protected SubtypeResolver _subtypeResolver; + + /** + * Handler responsible for resolving mix-in classes registered, if any. + */ + protected MixInHandler _mixInHandler; + + /* + /********************************************************************** + /* Factories for serialization + /********************************************************************** + */ + + /** + * {@link SerializationContexts} to use as factory for stateful {@link SerializerProvider}s + */ + protected SerializationContexts _serializationContexts; + + protected SerializerFactory _serializerFactory; + + protected FilterProvider _filterProvider; + + protected PrettyPrinter _defaultPrettyPrinter; + + /* + /********************************************************************** + /* Factories etc for deserialization + /********************************************************************** + */ + + /** + * Factory to use for creating per-operation contexts. + */ + protected DeserializationContexts _deserializationContexts; + + protected DeserializerFactory _deserializerFactory; + + /** + * Provider for values to inject in deserialized POJOs. + */ + protected InjectableValues _injectableValues; + + /** + * Optional handlers that application may register to try to work-around + * various problem situations during deserialization + */ + protected LinkedNode _problemHandlers; + + protected AbstractTypeResolver[] _abstractTypeResolvers; + + /* + /********************************************************************** + /* Feature flags: ser, deser + /********************************************************************** + */ + + /** + * Set of shared mapper features enabled. + */ + protected int _mapperFeatures; + + /** + * Set of {@link SerializationFeature}s enabled. + */ + protected int _serFeatures; + + /** + * Set of {@link DeserializationFeature}s enabled. + */ + protected int _deserFeatures; + + /* + /********************************************************************** + /* Feature flags: generation, parsing + /********************************************************************** + */ + + /** + * States of {@link StreamReadFeature}s to enable/disable. + */ + protected int _streamReadFeatures; + + /** + * States of {@link StreamWriteFeature}s to enable/disable. + */ + protected int _streamWriteFeatures; + + /** + * Optional per-format parser feature flags. + */ + protected int _formatReadFeatures; + + /** + * Optional per-format generator feature flags. + */ + protected int _formatWriteFeatures; + + /* + /********************************************************************** + /* Transient state + /********************************************************************** + */ + + /** + * Configuration state after direct access, immediately before registration + * of modules (if any) and construction of actual mapper. Retained after + * first access, and returned from {@link #saveStateApplyModules()}, to + * allow future "rebuild". + */ + protected transient MapperBuilderState _savedState; + /* /********************************************************************** /* Life-cycle /********************************************************************** */ - protected MapperBuilder(M mapper) + protected MapperBuilder(TokenStreamFactory streamFactory) + { + _streamFactory = streamFactory; + _baseSettings = DEFAULT_BASE_SETTINGS; + _configOverrides = new ConfigOverrides(); + _modules = null; + + _streamReadFeatures = streamFactory.getStreamReadFeatures(); + _streamWriteFeatures = streamFactory.getStreamWriteFeatures(); + _formatReadFeatures = streamFactory.getFormatReadFeatures(); + _formatWriteFeatures = streamFactory.getFormatWriteFeatures(); + + _mapperFeatures = DEFAULT_MAPPER_FEATURES; + // Some overrides we may need based on format + if (streamFactory.requiresPropertyOrdering()) { + _mapperFeatures |= MapperFeature.SORT_PROPERTIES_ALPHABETICALLY.getMask(); + } + _deserFeatures = DEFAULT_DESER_FEATURES; + _serFeatures = DEFAULT_SER_FEATURES; + + _typeFactory = null; + _classIntrospector = null; + _typeResolverProvider = null; + _subtypeResolver = null; + _mixInHandler = null; + + _serializerFactory = null; + _serializationContexts = null; + _filterProvider = null; + + _deserializerFactory = null; + _deserializationContexts = null; + _injectableValues = null; + + _problemHandlers = null; + _abstractTypeResolvers = NO_ABSTRACT_TYPE_RESOLVERS; + } + + /** + * Constructor used to support "rebuild", starting with a previously taken + * snapshot, in order to create mappers that start with a known state of + * configuration, including a set of modules to register. + */ + protected MapperBuilder(MapperBuilderState state) + { + _streamFactory = state._streamFactory; + _baseSettings = state._baseSettings; + _configOverrides = Snapshottable.takeSnapshot(state._configOverrides); + + _streamReadFeatures = state._streamReadFeatures; + _streamWriteFeatures = state._streamWriteFeatures; + _formatReadFeatures = state._formatReadFeatures; + _formatWriteFeatures = state._formatWriteFeatures; + _mapperFeatures = state._mapperFeatures; + _deserFeatures = state._deserFeatures; + _serFeatures = state._serFeatures; + + // Handlers, introspection + _typeFactory = Snapshottable.takeSnapshot(state._typeFactory); + + _classIntrospector = state._classIntrospector; + _typeResolverProvider = state._typeResolverProvider; + _subtypeResolver = Snapshottable.takeSnapshot(state._subtypeResolver); + _mixInHandler = (MixInHandler) Snapshottable.takeSnapshot(state._mixInHandler); + + // Factories for serialization + _serializationContexts = state._serializationContexts; + _serializerFactory = state._serializerFactory; + _filterProvider = state._filterProvider; + _defaultPrettyPrinter = state._defaultPrettyPrinter; + + // Factories for deserialization + _deserializationContexts = state._deserializationContexts; + _deserializerFactory = state._deserializerFactory; + _injectableValues = Snapshottable.takeSnapshot(state._injectableValues); + _problemHandlers = state._problemHandlers; + _abstractTypeResolvers = state._abstractTypeResolvers; + + // Modules + if (state._modules == null) { + _modules = null; + } else { + _modules = new LinkedHashMap<>(); + for (Object mod : state._modules) { + addModule((com.fasterxml.jackson.databind.Module) mod); + } + } + } + + /* + protected MapperBuilder(MapperBuilder base) { - _mapper = mapper; + _streamFactory = base._streamFactory; + _baseSettings = base._baseSettings; + _configOverrides = base._configOverrides; + + _mapperFeatures = base._mapperFeatures; + _serFeatures = base._serFeatures; + _deserFeatures = base._deserFeatures; + + _streamReadFeatures = base._streamReadFeatures; + _stremWriteFeatures = base._stremWriteFeatures; + _formatReadFeatures = base._formatReadFeatures; + _formatWriteFeatures = base._formatWriteFeatures; + + _typeFactory = base._typeFactory; + _classIntrospector = base._classIntrospector; + _typeResolverProvider = base._typeResolverProvider; + _subtypeResolver = base._subtypeResolver; + _mixInHandler = base._mixInHandler; + + _serializerFactory = base._serializerFactory; + _serializationContexts = base._serializationContexts; + _filterProvider = base._filterProvider; + + _deserializerFactory = base._deserializerFactory; + _deserializationContext = base._deserializationContext; + _injectableValues = base._injectableValues; + + _problemHandlers = base._problemHandlers; } + */ + + /* + /********************************************************************** + /* Methods for actual build process + /********************************************************************** + */ /** * Method to call to create actual mapper instance, usually passing {@code this} * builder to its constructor. */ - public M build() { - return _mapper; + public abstract M build(); + + /** + * Method called by mapper being constructed to first save state (delegated to + * {@link #_saveState()}), then apply modules (if any), and then return + * the saved state (but retain reference to it). If method has been called previously, + * it will simply return retained state. + */ + public MapperBuilderState saveStateApplyModules() + { + if (_savedState == null) { + _savedState = _saveState(); + if (_modules != null) { + ModuleContextBase ctxt = _constructModuleContext(); + _modules.values().forEach(m -> m.setupModule(ctxt)); + // and since context may buffer some changes, ensure those are flushed: + ctxt.applyChanges(this); + } + } + return _savedState; + } + + protected ModuleContextBase _constructModuleContext() { + return new ModuleContextBase(this, _configOverrides); + } + + protected abstract MapperBuilderState _saveState(); + + /* + /********************************************************************** + /* Secondary factory methods + /********************************************************************** + */ + + public SerializationConfig buildSerializationConfig(ConfigOverrides configOverrides, + MixInHandler mixins, TypeFactory tf, ClassIntrospector classIntr, SubtypeResolver str, + RootNameLookup rootNames, + FilterProvider filterProvider) + { + return new SerializationConfig(this, + _mapperFeatures, _serFeatures, _streamWriteFeatures, _formatWriteFeatures, + configOverrides, + tf, classIntr, mixins, str, rootNames, + filterProvider); + } + + public DeserializationConfig buildDeserializationConfig(ConfigOverrides configOverrides, + MixInHandler mixins, TypeFactory tf, ClassIntrospector classIntr, SubtypeResolver str, + RootNameLookup rootNames) + { + return new DeserializationConfig(this, + _mapperFeatures, _deserFeatures, _streamReadFeatures, _formatReadFeatures, + configOverrides, + tf, classIntr, mixins, str, rootNames, + _abstractTypeResolvers); } /* @@ -57,171 +441,418 @@ public M build() { */ public boolean isEnabled(MapperFeature f) { - return _mapper.isEnabled(f); + return f.enabledIn(_mapperFeatures); } public boolean isEnabled(DeserializationFeature f) { - return _mapper.isEnabled(f); + return f.enabledIn(_deserFeatures); } public boolean isEnabled(SerializationFeature f) { - return _mapper.isEnabled(f); + return f.enabledIn(_serFeatures); } - public boolean isEnabled(JsonParser.Feature f) { - return _mapper.isEnabled(f); + public boolean isEnabled(StreamReadFeature f) { + return f.enabledIn(_streamReadFeatures); } - public boolean isEnabled(JsonGenerator.Feature f) { - return _mapper.isEnabled(f); + public boolean isEnabled(StreamWriteFeature f) { + return f.enabledIn(_streamWriteFeatures); } /* /********************************************************************** - /* Accessors, other + /* Accessors, base settings /********************************************************************** */ + public BaseSettings baseSettings() { + return _baseSettings; + } + public TokenStreamFactory streamFactory() { - return _mapper.tokenStreamFactory(); + return _streamFactory; + } + + public AnnotationIntrospector annotationIntrospector() { + return _baseSettings.getAnnotationIntrospector(); + } + + /* + /********************************************************************** + /* Accessors, introspection + /********************************************************************** + */ + + public TypeFactory typeFactory() { + if (_typeFactory == null) { + _typeFactory = _defaultTypeFactory(); + } + return _typeFactory; + } + + /** + * Overridable method for changing default {@link SubtypeResolver} instance to use + */ + protected TypeFactory _defaultTypeFactory() { + return TypeFactory.defaultInstance(); + } + + public ClassIntrospector classIntrospector() { + if (_classIntrospector == null) { + _classIntrospector = _defaultClassIntrospector(); + } + return _classIntrospector; + } + + /** + * Overridable method for changing default {@link SubtypeResolver} instance to use + */ + protected ClassIntrospector _defaultClassIntrospector() { + return new BasicClassIntrospector(); + } + + public TypeResolverProvider typeResolverProvider() { + if (_typeResolverProvider == null) { + _typeResolverProvider = _defaultTypeResolverProvider(); + } + return _typeResolverProvider; + } + + /** + * Overridable method for changing default {@link TypeResolverProvider} instance to use + */ + protected TypeResolverProvider _defaultTypeResolverProvider() { + return new TypeResolverProvider(); + } + + public SubtypeResolver subtypeResolver() { + if (_subtypeResolver == null) { + _subtypeResolver = _defaultSubtypeResolver(); + } + return _subtypeResolver; + } + + /** + * Overridable method for changing default {@link SubtypeResolver} prototype + * to use. + */ + protected SubtypeResolver _defaultSubtypeResolver() { + return new StdSubtypeResolver(); + } + + public MixInHandler mixInHandler() { + if (_mixInHandler == null) { + _mixInHandler = _defaultMixInHandler(); + } + return _mixInHandler; + } + + /** + * Overridable method for changing default {@link MixInHandler} prototype + * to use. + */ + protected MixInHandler _defaultMixInHandler() { + return new MixInHandler(null); + } + + /* + /********************************************************************** + /* Accessors, serialization factories, related + /********************************************************************** + */ + + public SerializationContexts serializationContexts() { + if (_serializationContexts == null) { + _serializationContexts = _defaultSerializationContexts(); + } + return _serializationContexts; + } + + /** + * Overridable method for changing default {@link SerializerProvider} prototype + * to use. + */ + protected SerializationContexts _defaultSerializationContexts() { + return new SerializationContexts.DefaultImpl(); + } + + public SerializerFactory serializerFactory() { + if (_serializerFactory == null) { + _serializerFactory = _defaultSerializerFactory(); + } + return _serializerFactory; + } + + protected SerializerFactory _defaultSerializerFactory() { + return BeanSerializerFactory.instance; + } + + public FilterProvider filterProvider() { + return _filterProvider; + } + + public PrettyPrinter defaultPrettyPrinter() { + if (_defaultPrettyPrinter == null) { + _defaultPrettyPrinter = _defaultPrettyPrinter(); + } + return _defaultPrettyPrinter; + } + + protected PrettyPrinter _defaultPrettyPrinter() { + return DEFAULT_PRETTY_PRINTER; } + /* + /********************************************************************** + /* Accessors, deserialization factories, related + /********************************************************************** + */ + + public DeserializationContexts deserializationContexts() { + if (_deserializationContexts == null) { + _deserializationContexts = _defaultDeserializationContexts(); + } + return _deserializationContexts; + } + + /** + * Overridable method for changing default {@link SerializerProvider} prototype + * to use. + */ + protected DeserializationContexts _defaultDeserializationContexts() { + return new DeserializationContexts.DefaultImpl(); + } + + public DeserializerFactory deserializerFactory() { + if (_deserializerFactory == null) { + _deserializerFactory = _defaultDeserializerFactory(); + } + return _deserializerFactory; + } + + DeserializerFactory _defaultDeserializerFactory() { + return BeanDeserializerFactory.instance; + } + + public InjectableValues injectableValues() { + return _injectableValues; + } + + public LinkedNode deserializationProblemHandlers() { + return _problemHandlers; + } + /* /********************************************************************** /* Changing features: mapper, ser, deser /********************************************************************** */ - @SuppressWarnings("deprecation") public B enable(MapperFeature... features) { - _mapper.enable(features); + for (MapperFeature f : features) { + _mapperFeatures |= f.getMask(); + } return _this(); } - @SuppressWarnings("deprecation") public B disable(MapperFeature... features) { - _mapper.disable(features); + for (MapperFeature f : features) { + _mapperFeatures &= ~f.getMask(); + } return _this(); } - @SuppressWarnings("deprecation") - public B configure(MapperFeature feature, boolean state) { - _mapper.configure(feature, state); + public B configure(MapperFeature feature, boolean state) + { + if (state) { + _mapperFeatures |= feature.getMask(); + } else { + _mapperFeatures &= ~feature.getMask(); + } return _this(); } public B enable(SerializationFeature... features) { for (SerializationFeature f : features) { - _mapper.enable(f); + _serFeatures |= f.getMask(); } return _this(); } public B disable(SerializationFeature... features) { for (SerializationFeature f : features) { - _mapper.disable(f); + _serFeatures &= ~f.getMask(); } return _this(); } - public B configure(SerializationFeature feature, boolean state) { - _mapper.configure(feature, state); + public B configure(SerializationFeature feature, boolean state) + { + if (state) { + _serFeatures |= feature.getMask(); + } else { + _serFeatures &= ~feature.getMask(); + } return _this(); } public B enable(DeserializationFeature... features) { for (DeserializationFeature f : features) { - _mapper.enable(f); + _deserFeatures |= f.getMask(); } return _this(); } public B disable(DeserializationFeature... features) { for (DeserializationFeature f : features) { - _mapper.disable(f); + _deserFeatures &= ~f.getMask(); } return _this(); } - public B configure(DeserializationFeature feature, boolean state) { - _mapper.configure(feature, state); + public B configure(DeserializationFeature feature, boolean state) + { + if (state) { + _deserFeatures |= feature.getMask(); + } else { + _deserFeatures &= ~feature.getMask(); + } return _this(); } /* /********************************************************************** - /* Changing features: parser, generator, pre-2.10 + /* Changing features: parser, generator /********************************************************************** */ - public B enable(JsonParser.Feature... features) { - _mapper.enable(features); + public B enable(StreamReadFeature... features) { + for (StreamReadFeature f : features) { + _streamReadFeatures |= f.getMask(); + } return _this(); } - public B disable(JsonParser.Feature... features) { - _mapper.disable(features); + public B disable(StreamReadFeature... features) { + for (StreamReadFeature f : features) { + _streamReadFeatures &= ~f.getMask(); + } return _this(); } - public B configure(JsonParser.Feature feature, boolean state) { - _mapper.configure(feature, state); + public B configure(StreamReadFeature feature, boolean state) { + if (state) { + _streamReadFeatures |= feature.getMask(); + } else { + _streamReadFeatures &= ~feature.getMask(); + } return _this(); } - public B enable(JsonGenerator.Feature... features) { - _mapper.enable(features); + public B enable(StreamWriteFeature... features) { + for (StreamWriteFeature f : features) { + _streamWriteFeatures |= f.getMask(); + } return _this(); } - public B disable(JsonGenerator.Feature... features) { - _mapper.disable(features); + public B disable(StreamWriteFeature... features) { + for (StreamWriteFeature f : features) { + _streamWriteFeatures &= ~f.getMask(); + } return _this(); } - public B configure(JsonGenerator.Feature feature, boolean state) { - _mapper.configure(feature, state); + public B configure(StreamWriteFeature feature, boolean state) { + if (state) { + _streamWriteFeatures |= feature.getMask(); + } else { + _streamWriteFeatures &= ~feature.getMask(); + } return _this(); } /* /********************************************************************** - /* Changing features: parser, generator, 2.10+ + /* Changing settings, config overrides /********************************************************************** */ - public B enable(StreamReadFeature... features) { - for (StreamReadFeature f : features) { - _mapper.enable(f.mappedFeature()); - } + /** + * Method for changing config overrides for specific type, through + * callback to specific handler. + */ + public B withConfigOverride(Class forType, + Consumer handler) { + handler.accept(_configOverrides.findOrCreateOverride(forType)); return _this(); } - public B disable(StreamReadFeature... features) { - for (StreamReadFeature f : features) { - _mapper.disable(f.mappedFeature()); - } + /** + * Method for changing various aspects of configuration overrides. + */ + public B withAllConfigOverrides(Consumer handler) { + handler.accept(_configOverrides); return _this(); } - public B configure(StreamReadFeature feature, boolean state) { - _mapper.configure(feature.mappedFeature(), state); + /** + * Method for changing currently configured default {@link VisibilityChecker}, + * object used for determining whether given property element + * (method, field, constructor) can be auto-detected or not. + * Checker to modify is used for all POJO types for which there is no specific + * per-type checker. + * + * @param handler Function that is given current default visibility checker and that + * needs to return either checker as is, or a new instance created using one or more of + * {@code withVisibility} (and similar) calls. + */ + public B changeDefaultVisibility(UnaryOperator handler) { + VisibilityChecker oldV = _configOverrides.getDefaultVisibility(); + VisibilityChecker newV = handler.apply(oldV); + if (newV != oldV) { + Objects.requireNonNull(newV, "Can not assign null default VisibilityChecker"); + _configOverrides.setDefaultVisibility(newV); + } return _this(); } - public B enable(StreamWriteFeature... features) { - for (StreamWriteFeature f : features) { - _mapper.enable(f.mappedFeature()); + /** + * Method for changing currently default settings for property inclusion, used for determining + * whether POJO properties with certain value should be excluded or not: most common case being + * exclusion of `null` values. + */ + public B changeDefaultPropertyInclusion(UnaryOperator handler) { + JsonInclude.Value oldIncl = _configOverrides.getDefaultInclusion(); + JsonInclude.Value newIncl = handler.apply(oldIncl); + if (newIncl != oldIncl) { + Objects.requireNonNull(newIncl, "Can not assign null default Property Inclusion"); + _configOverrides.setDefaultInclusion(newIncl); } + //public ObjectMapper setDefaultPropertyInclusion() { return _this(); } - public B disable(StreamWriteFeature... features) { - for (StreamWriteFeature f : features) { - _mapper.disable(f.mappedFeature()); + /** + * Method for changing currently default settings for handling of `null` values during + * deserialization, regarding whether they are set as-is, ignored completely, or possible + * transformed into "empty" value of the target type (if any). + */ + public B changeDefaultNullHandling(UnaryOperator handler) { + JsonSetter.Value oldIncl = _configOverrides.getDefaultNullHandling(); + JsonSetter.Value newIncl = handler.apply(oldIncl); + if (newIncl != oldIncl) { + Objects.requireNonNull(newIncl, "Can not assign null default Null Handling"); + _configOverrides.setDefaultNullHandling(newIncl); } return _this(); } - public B configure(StreamWriteFeature feature, boolean state) { - _mapper.configure(feature.mappedFeature(), state); + /** + * Method for setting default Setter configuration, regarding things like + * merging, null-handling; used for properties for which there are + * no per-type or per-property overrides (via annotations or config overrides). + */ + public B defaultMergeable(Boolean b) { + _configOverrides.setDefaultMergeable(b); return _this(); } @@ -231,9 +862,41 @@ public B configure(StreamWriteFeature feature, boolean state) { /********************************************************************** */ + /** + * Method that will drop all modules added (via {@link #addModule} and similar + * calls) to this builder. + */ + public B removeAllModules() { + _modules = null; + return _this(); + } + + /** + * Method will add given module to be registered when mapper is built, possibly + * replacing an earlier instance of the module (as specified by its + * {@link Module#getRegistrationId()}). + * Actual registration occurs in addition order (considering last add to count, + * in case of re-registration for same id) when {@link #build()} is called. + */ public B addModule(com.fasterxml.jackson.databind.Module module) { - _mapper.registerModule(module); + if (module.getModuleName() == null) { + throw new IllegalArgumentException("Module without defined name"); + } + if (module.version() == null) { + throw new IllegalArgumentException("Module without defined version"); + } + // If dups are ok we still need a key, but just need to ensure it is unique so: + final Object moduleId = module.getRegistrationId(); + if (_modules == null) { + _modules = new LinkedHashMap<>(); + } else { + // Important: since order matters, we won't try to simply replace existing one. + // Could do in different order (put, and only re-order if there was old value), + // but simple does it for now. + _modules.remove(moduleId); + } + _modules.put(moduleId, module); return _this(); } @@ -310,12 +973,28 @@ public B findAndAddModules() { return addModules(findModules()); } + /** + * "Accessor" method that will expose set of registered modules, in addition + * order, to given handler. + */ + public B withModules(Consumer handler) { + if (_modules != null) { + _modules.values().forEach(handler); + } + return _this(); + } + /* /********************************************************************** /* Changing base settings /********************************************************************** */ + public B baseSettings(BaseSettings b) { + _baseSettings = b; + return _this(); + } + /** * Method for replacing {@link AnnotationIntrospector} used by the * mapper instance to be built. @@ -327,12 +1006,12 @@ public B findAndAddModules() { * @see com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair */ public B annotationIntrospector(AnnotationIntrospector intr) { - _mapper.setAnnotationIntrospector(intr); + _baseSettings = _baseSettings.withAnnotationIntrospector(intr); return _this(); } public B nodeFactory(JsonNodeFactory f) { - _mapper.setNodeFactory(f); + _baseSettings = _baseSettings.with(f); return _this(); } @@ -343,22 +1022,29 @@ public B nodeFactory(JsonNodeFactory f) { */ public B typeFactory(TypeFactory f) { - _mapper.setTypeFactory(f); + _typeFactory = f; return _this(); } - public B subtypeResolver(SubtypeResolver r) { - _mapper.setSubtypeResolver(r); + public B addTypeModifier(TypeModifier modifier) { + // important! Need to use getter, to force lazy construction if need be + _typeFactory = typeFactory() + .withModifier(modifier); + return _this(); + } + + protected B typeResolverProvider(TypeResolverProvider p) { + _typeResolverProvider = p; return _this(); } - public B visibility(VisibilityChecker vc) { - _mapper.setVisibility(vc); + public B classIntrospector(ClassIntrospector ci) { + _classIntrospector = ci; return _this(); } - public B visibility(PropertyAccessor forMethod, JsonAutoDetect.Visibility visibility) { - _mapper.setVisibility(forMethod, visibility); + public B subtypeResolver(SubtypeResolver r) { + _subtypeResolver = r; return _this(); } @@ -370,12 +1056,12 @@ public B visibility(PropertyAccessor forMethod, JsonAutoDetect.Visibility visibi * @param hi Instantiator to use; if null, use the default implementation */ public B handlerInstantiator(HandlerInstantiator hi) { - _mapper.setHandlerInstantiator(hi); + _baseSettings = _baseSettings.with(hi); return _this(); } public B propertyNamingStrategy(PropertyNamingStrategy s) { - _mapper.setPropertyNamingStrategy(s); + _baseSettings = _baseSettings.with(s); return _this(); } @@ -386,7 +1072,12 @@ public B propertyNamingStrategy(PropertyNamingStrategy s) { */ public B serializerFactory(SerializerFactory f) { - _mapper.setSerializerFactory(f); + _serializerFactory = f; + return _this(); + } + + public B serializationContexts(SerializationContexts ctxt) { + _serializationContexts = ctxt; return _this(); } @@ -399,12 +1090,12 @@ public B serializerFactory(SerializerFactory f) { * of ObjectMapper instances and not {@link ObjectWriter}s. */ public B filterProvider(FilterProvider prov) { - _mapper.setFilterProvider(prov); + _filterProvider = prov; return _this(); } public B defaultPrettyPrinter(PrettyPrinter pp) { - _mapper.setDefaultPrettyPrinter(pp); + _defaultPrettyPrinter = pp; return _this(); } @@ -414,8 +1105,18 @@ public B defaultPrettyPrinter(PrettyPrinter pp) { /********************************************************************** */ + public B deserializerFactory(DeserializerFactory f) { + _deserializerFactory = f; + return _this(); + } + + public B deserializationContext(DeserializationContexts ctxt) { + _deserializationContexts = ctxt; + return _this(); + } + public B injectableValues(InjectableValues v) { - _mapper.setInjectableValues(v); + _injectableValues = v; return _this(); } @@ -425,7 +1126,9 @@ public B injectableValues(InjectableValues v) { * registered earlier). */ public B addHandler(DeserializationProblemHandler h) { - _mapper.addHandler(h); + if (!LinkedNode.contains(_problemHandlers, h)) { + _problemHandlers = new LinkedNode<>(h, _problemHandlers); + } return _this(); } @@ -434,7 +1137,19 @@ public B addHandler(DeserializationProblemHandler h) { * to this builder (if any). */ public B clearProblemHandlers() { - _mapper.clearProblemHandlers(); + _problemHandlers = null; + return _this(); + } + + /** + * Method for inserting specified {@link AbstractTypeResolver} as the first resolver + * in chain of possibly multiple resolvers. + */ + public B addAbstractTypeResolver(AbstractTypeResolver resolver) { + if (resolver == null) { + throw new IllegalArgumentException("Cannot pass null resolver"); + } + _abstractTypeResolvers = ArrayBuilders.insertInListNoDup(_abstractTypeResolvers, resolver); return _this(); } @@ -450,8 +1165,9 @@ public B clearProblemHandlers() { * If you need per-request configuration, factory methods in * {@link ObjectReader} and {@link ObjectWriter} instead. */ - public B defaultDateFormat(DateFormat df) { - _mapper.setDateFormat(df); + public B defaultDateFormat(DateFormat f) { + _baseSettings = _baseSettings.with(f); + configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, (f == null)); return _this(); } @@ -460,7 +1176,7 @@ public B defaultDateFormat(DateFormat df) { * Default value used is UTC (NOT default TimeZone of JVM). */ public B defaultTimeZone(TimeZone tz) { - _mapper.setTimeZone(tz); + _baseSettings = _baseSettings.with(tz); return _this(); } @@ -469,7 +1185,7 @@ public B defaultTimeZone(TimeZone tz) { * Default value used is {@link Locale#getDefault()}. */ public B defaultLocale(Locale locale) { - _mapper.setLocale(locale); + _baseSettings = _baseSettings.with(locale); return _this(); } @@ -488,12 +1204,7 @@ public B defaultLocale(Locale locale) { * @return This mapper, for convenience to allow chaining */ public B defaultBase64Variant(Base64Variant v) { - _mapper.setBase64Variant(v); - return _this(); - } - - public B serializationInclusion(JsonInclude.Include incl) { - _mapper.setSerializationInclusion(incl); + _baseSettings = _baseSettings.with(v); return _this(); } @@ -503,6 +1214,39 @@ public B serializationInclusion(JsonInclude.Include incl) { /********************************************************************** */ + /** + * Method that may be used to completely change mix-in handling by providing + * alternate {@link MixInHandler} implementation. + * Most of the time this is NOT the method you want to call, and rather are looking + * for {@link #mixInOverrides}. + */ + public B mixInHandler(MixInHandler h) { + _mixInHandler = h; + return _this(); + } + + /** + * Method that allows defining "override" mix-in resolver: something that is checked first, + * before simple mix-in definitions. + */ + public B mixInOverrides(MixInResolver r) { + _mixInHandler = mixInHandler().withOverrides(r); + return _this(); + } + + /** + * Method to use for defining mix-in annotations to use for augmenting + * annotations that processable (serializable / deserializable) + * classes have. + * This convenience method is equivalent to iterating over all entries + * and calling {@link #addMixIn} with `key` and `value` of each entry. + */ + public B addMixIns(Map, Class> sourceMixins) + { + mixInHandler().addLocalDefinitions(sourceMixins); + return _this(); + } + /** * Method to use for defining mix-in annotations to use for augmenting * annotations that classes have, for purpose of configuration serialization @@ -519,7 +1263,7 @@ public B serializationInclusion(JsonInclude.Include incl) { */ public B addMixIn(Class target, Class mixinSource) { - _mapper.addMixIn(target, mixinSource); + mixInHandler().addLocalDefinition(target, mixinSource); return _this(); } @@ -530,20 +1274,150 @@ public B addMixIn(Class target, Class mixinSource) */ public B registerSubtypes(Class... subtypes) { - _mapper.registerSubtypes(subtypes); + subtypeResolver().registerSubtypes(subtypes); return _this(); } public B registerSubtypes(NamedType... subtypes) { - _mapper.registerSubtypes(subtypes); + subtypeResolver().registerSubtypes(subtypes); return _this(); } public B registerSubtypes(Collection> subtypes) { - _mapper.registerSubtypes(subtypes); + subtypeResolver().registerSubtypes(subtypes); return _this(); } + /* + /********************************************************************** + /* Default typing (temporarily) + /********************************************************************** + */ + + /** + * Convenience method that is equivalent to calling + *

    +     *  enableDefaultTyping(DefaultTyping.OBJECT_AND_NON_CONCRETE);
    +     *
    + *

    + * NOTE: use of Default Typing can be a potential security risk if incoming + * content comes from untrusted sources, and it is recommended that this + * is either not done, or, if enabled, use {@link #setDefaultTyping} + * passing a custom {@link TypeResolverBuilder} implementation that white-lists + * legal types to use. + */ + public B enableDefaultTyping() { + return enableDefaultTyping(DefaultTyping.OBJECT_AND_NON_CONCRETE); + } + + /** + * Convenience method that is equivalent to calling + *

    +     *  enableDefaultTyping(dti, JsonTypeInfo.As.WRAPPER_ARRAY);
    +     *
    + *

    + * NOTE: use of Default Typing can be a potential security risk if incoming + * content comes from untrusted sources, and it is recommended that this + * is either not done, or, if enabled, use {@link #setDefaultTyping} + * passing a custom {@link TypeResolverBuilder} implementation that white-lists + * legal types to use. + */ + public B enableDefaultTyping(DefaultTyping dti) { + return enableDefaultTyping(dti, JsonTypeInfo.As.WRAPPER_ARRAY); + } + + /** + * 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}). + *

    + * NOTE: use of JsonTypeInfo.As#EXTERNAL_PROPERTY NOT SUPPORTED; + * and attempts of do so will throw an {@link IllegalArgumentException} to make + * this limitation explicit. + *

    + * NOTE: use of Default Typing can be a potential security risk if incoming + * content comes from untrusted sources, and it is recommended that this + * is either not done, or, if enabled, use {@link #setDefaultTyping} + * passing a custom {@link TypeResolverBuilder} implementation that white-lists + * legal types to use. + * + * @param applicability Defines kinds of types for which additional type information + * is added; see {@link DefaultTyping} for more information. + */ + public B 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("Cannot use includeAs of "+includeAs+" for Default Typing"); + } + return setDefaultTyping(_defaultDefaultTypingResolver(applicability, includeAs)); + } + + /** + * 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}) -- + * using "As.PROPERTY" inclusion mechanism and specified property name + * to use for inclusion (default being "@class" since default type information + * always uses class name as type identifier) + *

    + * NOTE: use of Default Typing can be a potential security risk if incoming + * content comes from untrusted sources, and it is recommended that this + * is either not done, or, if enabled, use {@link #setDefaultTyping} + * passing a custom {@link TypeResolverBuilder} implementation that white-lists + * legal types to use. + */ + public B enableDefaultTypingAsProperty(DefaultTyping applicability, String propertyName) + { + return setDefaultTyping(_defaultDefaultTypingResolver(applicability, propertyName)); + } + + /** + * Method for disabling automatic inclusion of type information; if so, only + * explicitly annotated types (ones with + * {@link com.fasterxml.jackson.annotation.JsonTypeInfo}) will have + * additional embedded type information. + */ + public B disableDefaultTyping() { + return setDefaultTyping(null); + } + + /** + * Method for enabling automatic inclusion of type information, using + * specified handler object for determining which types this affects, + * as well as details of how information is embedded. + *

    + * NOTE: use of Default Typing can be a potential security risk if incoming + * content comes from untrusted sources, so care should be taken to use + * a {@link TypeResolverBuilder} that can limit allowed classes to + * deserialize. + * + * @param typer Type information inclusion handler + */ + public B setDefaultTyping(TypeResolverBuilder typer) { + _baseSettings = _baseSettings.with(typer); + return _this(); + } + + /** + * Overridable method for changing default {@link TypeResolverBuilder} to construct + * for "default typing". + */ + protected TypeResolverBuilder _defaultDefaultTypingResolver(DefaultTyping applicability, + JsonTypeInfo.As includeAs) { + return new DefaultTypeResolverBuilder(applicability, includeAs); + } + + /** + * Overridable method for changing default {@link TypeResolverBuilder} to construct + * for "default typing". + */ + protected TypeResolverBuilder _defaultDefaultTypingResolver(DefaultTyping applicability, + String propertyName) { + return new DefaultTypeResolverBuilder(applicability, propertyName); + } + /* /********************************************************************** /* Other helper methods diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperBuilderState.java b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperBuilderState.java new file mode 100644 index 0000000000..4574524370 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperBuilderState.java @@ -0,0 +1,196 @@ +package com.fasterxml.jackson.databind.cfg; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +import com.fasterxml.jackson.core.PrettyPrinter; +import com.fasterxml.jackson.core.TokenStreamFactory; +import com.fasterxml.jackson.core.util.Snapshottable; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler; +import com.fasterxml.jackson.databind.deser.DeserializerFactory; +import com.fasterxml.jackson.databind.introspect.ClassIntrospector; +import com.fasterxml.jackson.databind.introspect.MixInHandler; +import com.fasterxml.jackson.databind.jsontype.SubtypeResolver; +import com.fasterxml.jackson.databind.jsontype.TypeResolverProvider; +import com.fasterxml.jackson.databind.ser.FilterProvider; +import com.fasterxml.jackson.databind.ser.SerializerFactory; +import com.fasterxml.jackson.databind.type.TypeFactory; +import com.fasterxml.jackson.databind.util.LinkedNode; + +/** + * Interface for State object used for preserving initial state of a + * {@link MapperBuilder} before modules are configured and resulting + * {@link com.fasterxml.jackson.databind.ObjectMapper} isn't constructed. + * It is passed to mapper to allow "re-building" via newly created builder. + *

    + * Note that JDK serialization is supported by switching this object in place + * of mapper. This requires some acrobatics on return direction. + */ +public abstract class MapperBuilderState + implements java.io.Serializable // important! +{ + private static final long serialVersionUID = 3L; + + /* + /********************************************************************** + /* Basic settings + /********************************************************************** + */ + + protected final BaseSettings _baseSettings; + protected final TokenStreamFactory _streamFactory; + protected final ConfigOverrides _configOverrides; + + /* + /********************************************************************** + /* Feature flags + /********************************************************************** + */ + + protected final int _mapperFeatures, _serFeatures, _deserFeatures; + protected final int _streamReadFeatures, _streamWriteFeatures; + protected final int _formatReadFeatures, _formatWriteFeatures; + + /* + /********************************************************************** + /* Modules + /********************************************************************** + */ + + /** + * Modules registered in registration order, if any; `null` if none. + */ + protected final com.fasterxml.jackson.databind.Module[] _modules; + + /* + /********************************************************************** + /* Handlers, introspection + /********************************************************************** + */ + + protected final TypeFactory _typeFactory; + protected final ClassIntrospector _classIntrospector; + protected final TypeResolverProvider _typeResolverProvider; + protected final SubtypeResolver _subtypeResolver; + protected final MixInHandler _mixInHandler; + + /* + /********************************************************************** + /* Factories for serialization + /********************************************************************** + */ + + protected final SerializationContexts _serializationContexts; + protected final SerializerFactory _serializerFactory; + protected final FilterProvider _filterProvider; + protected final PrettyPrinter _defaultPrettyPrinter; + + /* + /********************************************************************** + /* Factories for deserialization + /********************************************************************** + */ + + protected final DeserializationContexts _deserializationContexts; + protected final DeserializerFactory _deserializerFactory; + protected final InjectableValues _injectableValues; + + /** + * Optional handlers that application may register to try to work-around + * various problem situations during deserialization + */ + protected final LinkedNode _problemHandlers; + + protected final AbstractTypeResolver[] _abstractTypeResolvers; + + /* + /********************************************************************** + /* Construction + /********************************************************************** + */ + + /** + * Constructor called when "saving" state of mapper, to be used as base for + * {@link ObjectMapper#rebuild()} functionality. + */ + public MapperBuilderState(MapperBuilder src) + { + // Basic settings + _baseSettings = src._baseSettings; // immutable + _streamFactory = src._streamFactory; // immutable + _configOverrides = Snapshottable.takeSnapshot(src._configOverrides); + + // Feature flags (simple ints, no copy needed) + _mapperFeatures = src._mapperFeatures; + _serFeatures = src._serFeatures; + _deserFeatures = src._deserFeatures; + _streamReadFeatures = src._streamReadFeatures; + _streamWriteFeatures = src._streamWriteFeatures; + _formatReadFeatures = src._formatReadFeatures; + _formatWriteFeatures = src._formatWriteFeatures; + + // Handlers, introspection + _typeFactory = Snapshottable.takeSnapshot(src._typeFactory); + _classIntrospector = src._classIntrospector; // no snapshot needed (uses `forMapper()`) + _typeResolverProvider = src._typeResolverProvider; + _subtypeResolver = Snapshottable.takeSnapshot(src._subtypeResolver); + _mixInHandler = (MixInHandler) Snapshottable.takeSnapshot(src._mixInHandler); + + // Factories for serialization + _serializerFactory = src._serializerFactory; + _serializationContexts = src._serializationContexts; // no snapshot needed (uses `forMapper()`) + _filterProvider = Snapshottable.takeSnapshot(src._filterProvider); + _defaultPrettyPrinter = src._defaultPrettyPrinter; + + // Factories for deserialization + _deserializerFactory = src._deserializerFactory; + _deserializationContexts = src._deserializationContexts; + _injectableValues = Snapshottable.takeSnapshot(src._injectableValues); + // assume our usage of LinkedNode-based list is immutable here (should be) + _problemHandlers = src._problemHandlers; + _abstractTypeResolvers = src._abstractTypeResolvers; + + // Modules + if (src._modules == null) { + _modules = null; + } else { + _modules = _toArray(src._modules.values()); + } + } + + private static com.fasterxml.jackson.databind.Module[] _toArray(Collection coll) + { + if (coll == null || coll.isEmpty()) { + return null; + } + return coll.toArray(new com.fasterxml.jackson.databind.Module[coll.size()]); + } + + /* + /********************************************************************** + /* Configuration access by ObjectMapper + /********************************************************************** + */ + + public Collection modules() { + if (_modules == null) { + return Collections.emptyList(); + } + return Collections.unmodifiableList(Arrays.asList(_modules)); + } + + /* + /********************************************************************** + /* JDK deserialization support + /********************************************************************** + */ + + /** + * Method required to support JDK deserialization; made `abstract` here to ensure + * sub-classes must implement it. + */ + protected abstract Object readResolve(); +} diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfig.java b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfig.java index 7bc3e7f039..c32049819a 100644 --- a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfig.java +++ b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfig.java @@ -5,19 +5,19 @@ import java.util.TimeZone; import com.fasterxml.jackson.annotation.*; + import com.fasterxml.jackson.core.Base64Variant; import com.fasterxml.jackson.core.SerializableString; import com.fasterxml.jackson.core.io.SerializedString; import com.fasterxml.jackson.core.type.TypeReference; + import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.introspect.Annotated; -import com.fasterxml.jackson.databind.introspect.AnnotatedClass; -import com.fasterxml.jackson.databind.introspect.ClassIntrospector; -import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector; -import com.fasterxml.jackson.databind.introspect.VisibilityChecker; +import com.fasterxml.jackson.databind.introspect.*; import com.fasterxml.jackson.databind.jsontype.SubtypeResolver; import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; +import com.fasterxml.jackson.databind.jsontype.TypeResolverProvider; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.type.TypeFactory; import com.fasterxml.jackson.databind.util.ClassUtil; @@ -34,19 +34,13 @@ * that is shared between different types of instances. */ public abstract class MapperConfig> - implements ClassIntrospector.MixInResolver, + implements MixInResolver, java.io.Serializable { - private static final long serialVersionUID = 2L; // since 2.9 + private static final long serialVersionUID = 3L; // since 3.0 - /** - * @since 2.7 - */ protected final static JsonInclude.Value EMPTY_INCLUDE = JsonInclude.Value.empty(); - /** - * @since 2.7 - */ protected final static JsonFormat.Value EMPTY_FORMAT = JsonFormat.Value.empty(); /** @@ -60,9 +54,9 @@ public abstract class MapperConfig> protected final BaseSettings _base; /* - /********************************************************** + /********************************************************************** /* Life-cycle: constructors - /********************************************************** + /********************************************************************** */ protected MapperConfig(BaseSettings base, int mapperFeatures) @@ -71,12 +65,6 @@ protected MapperConfig(BaseSettings base, int mapperFeatures) _mapperFeatures = mapperFeatures; } - protected MapperConfig(MapperConfig src, int mapperFeatures) - { - _base = src._base; - _mapperFeatures = mapperFeatures; - } - protected MapperConfig(MapperConfig src, BaseSettings base) { _base = base; @@ -105,32 +93,9 @@ public static & ConfigFeature> int collectFeatureDefaults(Cla } /* - /********************************************************** - /* Life-cycle: factory methods - /********************************************************** - */ - - /** - * Method for constructing and returning a new instance with specified - * mapper features enabled. - */ - public abstract T with(MapperFeature... features); - - /** - * Method for constructing and returning a new instance with specified - * mapper features disabled. - */ - public abstract T without(MapperFeature... features); - - /** - * @since 2.3 - */ - public abstract T with(MapperFeature feature, boolean state); - - /* - /********************************************************** + /********************************************************************** /* Configuration: simple features - /********************************************************** + /********************************************************************** */ /** @@ -144,8 +109,6 @@ public final boolean isEnabled(MapperFeature f) { /** * "Bulk" access method for checking that all features specified by * mask are enabled. - * - * @since 2.3 */ public final boolean hasMapperFeatures(int featureMask) { return (_mapperFeatures & featureMask) == featureMask; @@ -192,9 +155,9 @@ public final boolean shouldSortPropertiesAlphabetically() { public abstract boolean useRootWrapping(); /* - /********************************************************** + /********************************************************************** /* Configuration: factory methods - /********************************************************** + /********************************************************************** */ /** @@ -205,26 +168,20 @@ public final boolean shouldSortPropertiesAlphabetically() { * @param src Text to represent * * @return Optimized text object constructed - * - * @since 2.4 */ public SerializableString compileString(String src) { - /* 20-Jan-2014, tatu: For now we will just construct it directly, but - * for 2.4 need to allow overriding to support non-standard extensions - * to be used by extensions like Afterburner. - */ + // 20-Jan-2014, tatu: For now we will just construct it directly but in distant + // future might want to allow overriding somehow? return new SerializedString(src); } /* - /********************************************************** + /********************************************************************** /* Configuration: introspectors, mix-ins - /********************************************************** + /********************************************************************** */ - - public ClassIntrospector getClassIntrospector() { - return _base.getClassIntrospector(); - } + + public abstract ClassIntrospector getClassIntrospector(); /** * Method for getting {@link AnnotationIntrospector} configured @@ -248,26 +205,29 @@ public final HandlerInstantiator getHandlerInstantiator() { } /* - /********************************************************** + /********************************************************************** /* Configuration: type and subtype handling - /********************************************************** + /********************************************************************** */ /** * Method called to locate a type info handler for types that do not have * one explicitly declared via annotations (or other configuration). - * If such default handler is configured, it is returned; otherwise + * If such a default handler is configured, it is returned; otherwise * null is returned. */ public final TypeResolverBuilder getDefaultTyper(JavaType baseType) { - return _base.getTypeResolverBuilder(); + return _base.getDefaultTyper(); } - + + /** + * @since 3.0 + */ + public abstract TypeResolverProvider getTypeResolverProvider(); + public abstract SubtypeResolver getSubtypeResolver(); - public final TypeFactory getTypeFactory() { - return _base.getTypeFactory(); - } + public abstract TypeFactory getTypeFactory(); /** * Helper method that will construct {@link JavaType} for given @@ -277,9 +237,7 @@ public final TypeFactory getTypeFactory() { * getTypeFactory().constructType(cls); * */ - public final JavaType constructType(Class cls) { - return getTypeFactory().constructType(cls); - } + public abstract JavaType constructType(Class cls); /** * Helper method that will construct {@link JavaType} for given @@ -289,18 +247,14 @@ public final JavaType constructType(Class cls) { * getTypeFactory().constructType(valueTypeRef); * */ - public final JavaType constructType(TypeReference valueTypeRef) { - return getTypeFactory().constructType(valueTypeRef.getType()); - } + public abstract JavaType constructType(TypeReference valueTypeRef); - public JavaType constructSpecializedType(JavaType baseType, Class subclass) { - return getTypeFactory().constructSpecializedType(baseType, subclass); - } + public abstract JavaType constructSpecializedType(JavaType baseType, Class subclass); /* - /********************************************************** + /********************************************************************** /* Configuration: introspection support - /********************************************************** + /********************************************************************** */ /** @@ -319,15 +273,7 @@ public BeanDescription introspectClassAnnotations(JavaType type) { return getClassIntrospector().forClassAnnotations(this, type, this); } - /** - * Accessor for getting bean description that only contains immediate class - * annotations: ones from the class, and its direct mix-in, if any, but - * not from super types. - */ - public BeanDescription introspectDirectClassAnnotations(Class cls) { - return introspectDirectClassAnnotations(constructType(cls)); - } - + // 23-May-2018, tatu: Used by Avro for schema generation /** * Accessor for getting bean description that only contains immediate class * annotations: ones from the class, and its direct mix-in, if any, but @@ -338,9 +284,9 @@ public final BeanDescription introspectDirectClassAnnotations(JavaType type) { } /* - /********************************************************** + /********************************************************************** /* Configuration: default settings with per-type overrides - /********************************************************** + /********************************************************************** */ /** @@ -349,8 +295,6 @@ public final BeanDescription introspectDirectClassAnnotations(JavaType type) { *

    * Note that only directly associated override * is found; no type hierarchy traversal is performed. - * - * @since 2.8 * * @return Override object to use for the type, if defined; null if none. */ @@ -363,8 +307,6 @@ public final BeanDescription introspectDirectClassAnnotations(JavaType type) { *

    * Note that only directly associated override * is found; no type hierarchy traversal is performed. - * - * @since 2.9 * * @return Override object to use for the type, never null (but may be empty) */ @@ -373,8 +315,6 @@ public final BeanDescription introspectDirectClassAnnotations(JavaType type) { /** * Accessor for default property inclusion to use for serialization, * used unless overridden by per-type or per-property overrides. - * - * @since 2.7 */ public abstract JsonInclude.Value getDefaultPropertyInclusion(); @@ -383,8 +323,6 @@ public final BeanDescription introspectDirectClassAnnotations(JavaType type) { * considering possible per-type override for given base type.
    * NOTE: if no override found, defaults to value returned by * {@link #getDefaultPropertyInclusion()}. - * - * @since 2.7 */ public abstract JsonInclude.Value getDefaultPropertyInclusion(Class baseType); @@ -394,8 +332,6 @@ public final BeanDescription introspectDirectClassAnnotations(JavaType type) { * if none found, returning given defaultIncl * * @param defaultIncl Inclusion setting to return if no overrides found. - * - * @since 2.8.2 */ public JsonInclude.Value getDefaultPropertyInclusion(Class baseType, JsonInclude.Value defaultIncl) @@ -416,8 +352,6 @@ public JsonInclude.Value getDefaultPropertyInclusion(Class baseType, * * @param baseType Type of the instance containing the targeted property. * @param propertyType Type of the property to look up inclusion setting for. - * - * @since 2.9 */ public abstract JsonInclude.Value getDefaultInclusion(Class baseType, Class propertyType); @@ -431,8 +365,6 @@ public abstract JsonInclude.Value getDefaultInclusion(Class baseType, * @param baseType Type of the instance containing the targeted property. * @param propertyType Type of the property to look up inclusion setting for. * @param defaultIncl Inclusion setting to return if no overrides found. - * - * @since 2.9 */ public JsonInclude.Value getDefaultInclusion(Class baseType, Class propertyType, JsonInclude.Value defaultIncl) @@ -449,15 +381,13 @@ public JsonInclude.Value getDefaultInclusion(Class baseType, * deserialization), considering baseline settings and per-type defaults * for given base type (if any). * - * @since 2.7 + * @return (non-null) Format settings to use, possibly `JsonFormat.Value.empty()` */ public abstract JsonFormat.Value getDefaultPropertyFormat(Class baseType); /** * Accessor for default property ignorals to use, if any, for given base type, * based on config overrides settings (see {@link #findConfigOverride(Class)}). - * - * @since 2.8 */ public abstract JsonIgnoreProperties.Value getDefaultPropertyIgnorals(Class baseType); @@ -466,8 +396,6 @@ public JsonInclude.Value getDefaultInclusion(Class baseType, * definitions from annotations (via {@link AnnotatedClass}) or through * "config overrides". If both exist, config overrides have precedence * over class annotations. - * - * @since 2.8 */ public abstract JsonIgnoreProperties.Value getDefaultPropertyIgnorals(Class baseType, AnnotatedClass actualClass); @@ -481,7 +409,7 @@ public abstract JsonIgnoreProperties.Value getDefaultPropertyIgnorals(Class b * can further override active checker used (using * {@link JsonAutoDetect} annotation) */ - public abstract VisibilityChecker getDefaultVisibilityChecker(); + public abstract VisibilityChecker getDefaultVisibilityChecker(); /** * Accessor for object used for determining whether specific property elements @@ -490,29 +418,25 @@ public abstract JsonIgnoreProperties.Value getDefaultPropertyIgnorals(Class b * (as would be returned by {@link #getDefaultVisibilityChecker()}, but * then modified by possible class annotation (see {@link JsonAutoDetect}) * and/or per-type config override (see {@link ConfigOverride#getVisibility()}). - * - * @since 2.9 */ - public abstract VisibilityChecker getDefaultVisibilityChecker(Class baseType, + public abstract VisibilityChecker getDefaultVisibilityChecker(Class baseType, AnnotatedClass actualClass); /** * Accessor for the baseline setter info used as the global baseline, * not considering possible per-type overrides. * - * @return Global base settings; never null + * @since 3.0 * - * @since 2.9 + * @return Global base settings; never null */ - public abstract JsonSetter.Value getDefaultSetterInfo(); + public abstract JsonSetter.Value getDefaultNullHandling(); /** * Accessor for the baseline merge info used as the global baseline, * not considering possible per-type overrides. * * @return Global base settings, if any; `null` if none. - * - * @since 2.9 */ public abstract Boolean getDefaultMergeable(); @@ -523,15 +447,13 @@ public abstract VisibilityChecker getDefaultVisibilityChecker(Class baseTy * @return Type-specific settings (if any); global defaults (same as * {@link #getDefaultMergeable()}) otherwise, if any defined; or `null` * if neither defined - * - * @since 2.9 */ public abstract Boolean getDefaultMergeable(Class baseType); /* - /********************************************************** + /********************************************************************** /* Configuration: other - /********************************************************** + /********************************************************************** */ /** @@ -582,29 +504,24 @@ public Base64Variant getBase64Variant() { return _base.getBase64Variant(); } + public final JsonNodeFactory getNodeFactory() { + return _base.getNodeFactory(); + } /** * Method for accessing per-instance shared (baseline/default) * attribute values; these are used as the basis for per-call * attributes. - * - * @since 2.3 */ public abstract ContextAttributes getAttributes(); - /** - * @since 2.6 - */ public abstract PropertyName findRootName(JavaType rootType); - /** - * @since 2.6 - */ public abstract PropertyName findRootName(Class rawRootType); /* - /********************************************************** + /********************************************************************** /* Methods for instantiating handlers - /********************************************************** + /********************************************************************** */ /** diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java index a7ca2e5adc..4caa5a0525 100644 --- a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java +++ b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java @@ -4,17 +4,14 @@ import java.util.*; import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; import com.fasterxml.jackson.core.Base64Variant; - +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.introspect.ClassIntrospector; -import com.fasterxml.jackson.databind.introspect.ClassIntrospector.MixInResolver; -import com.fasterxml.jackson.databind.introspect.AnnotatedClass; -import com.fasterxml.jackson.databind.introspect.SimpleMixInResolver; -import com.fasterxml.jackson.databind.introspect.VisibilityChecker; +import com.fasterxml.jackson.databind.introspect.*; import com.fasterxml.jackson.databind.jsontype.SubtypeResolver; import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; +import com.fasterxml.jackson.databind.jsontype.TypeResolverProvider; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.type.TypeFactory; import com.fasterxml.jackson.databind.util.RootNameLookup; @@ -24,37 +21,29 @@ public abstract class MapperConfigBase implements java.io.Serializable { - /** - * @since 2.9 - */ protected final static ConfigOverride EMPTY_OVERRIDE = ConfigOverride.empty(); - private final static int DEFAULT_MAPPER_FEATURES = collectFeatureDefaults(MapperFeature.class); + protected final static int DEFAULT_MAPPER_FEATURES = collectFeatureDefaults(MapperFeature.class); - /** - * @since 2.9 + /* + /********************************************************************** + /* Immutable config, factories + /********************************************************************** */ - private final static int AUTO_DETECT_MASK = - MapperFeature.AUTO_DETECT_FIELDS.getMask() - | MapperFeature.AUTO_DETECT_GETTERS.getMask() - | MapperFeature.AUTO_DETECT_IS_GETTERS.getMask() - | MapperFeature.AUTO_DETECT_SETTERS.getMask() - | MapperFeature.AUTO_DETECT_CREATORS.getMask() - ; - /* - /********************************************************** - /* Immutable config - /********************************************************** + /** + * Specific factory used for creating {@link JavaType} instances; + * needed to allow modules to add more custom type handling + * (mostly to support types of non-Java JVM languages) */ + protected final TypeFactory _typeFactory; + + protected final ClassIntrospector _classIntrospector; /** - * Mix-in annotation mappings to use, if any: immutable, - * cannot be changed once defined. - * - * @since 2.6 + * @since 3.0 */ - protected final SimpleMixInResolver _mixIns; + protected final TypeResolverProvider _typeResolverProvider; /** * Registered concrete subtypes that can be used instead of (or @@ -65,6 +54,17 @@ public abstract class MapperConfigBase * Note that instances are stateful (for caching) and as such may need to be copied, * and may NOT be demoted down to {@link BaseSettings}. - * - * @since 2.6 */ protected final RootNameLookup _rootNames; /** * Configuration overrides to apply, keyed by type of property. - * - * @since 2.8 */ protected final ConfigOverrides _configOverrides; /* - /********************************************************** + /********************************************************************** /* Construction - /********************************************************** + /********************************************************************** */ /** * Constructor used when creating a new instance (compared to * that of creating fluent copies) - * - * @since 2.8 */ - protected MapperConfigBase(BaseSettings base, - SubtypeResolver str, SimpleMixInResolver mixins, RootNameLookup rootNames, - ConfigOverrides configOverrides) + protected MapperConfigBase(MapperBuilder b, int mapperFeatures, + TypeFactory tf, ClassIntrospector classIntr, MixInHandler mixins, SubtypeResolver str, + ConfigOverrides configOverrides, RootNameLookup rootNames) { - super(base, DEFAULT_MAPPER_FEATURES); - _mixIns = mixins; + super(b.baseSettings(), mapperFeatures); + + _typeFactory = tf; + _classIntrospector = classIntr; + _typeResolverProvider = b.typeResolverProvider(); _subtypeResolver = str; + + _mixIns = mixins; _rootNames = rootNames; _rootName = null; _view = null; @@ -133,28 +130,6 @@ protected MapperConfigBase(BaseSettings base, _configOverrides = configOverrides; } - /** - * Copy constructor usually called to make a copy for use by - * ObjectMapper that is copied. - * - * @since 2.8 - */ - protected MapperConfigBase(MapperConfigBase src, - SimpleMixInResolver mixins, RootNameLookup rootNames, - ConfigOverrides configOverrides) - { - // 18-Apr-2018, tatu: [databind#1898] need to force copying of `ClassIntrospector` - // (to clear its cache) to avoid leakage - super(src, src._base.copy()); - _mixIns = mixins; - _subtypeResolver = src._subtypeResolver; - _rootNames = rootNames; - _rootName = src._rootName; - _view = src._view; - _attributes = src._attributes; - _configOverrides = configOverrides; - } - /** * Pass-through constructor used when no changes are needed to the * base class. @@ -162,8 +137,12 @@ protected MapperConfigBase(MapperConfigBase src, protected MapperConfigBase(MapperConfigBase src) { super(src); - _mixIns = src._mixIns; + _typeFactory = src._typeFactory; + _classIntrospector = src._classIntrospector; + _typeResolverProvider = src._typeResolverProvider; _subtypeResolver = src._subtypeResolver; + + _mixIns = src._mixIns; _rootNames = src._rootNames; _rootName = src._rootName; _view = src._view; @@ -174,31 +153,12 @@ protected MapperConfigBase(MapperConfigBase src) protected MapperConfigBase(MapperConfigBase src, BaseSettings base) { super(src, base); - _mixIns = src._mixIns; - _subtypeResolver = src._subtypeResolver; - _rootNames = src._rootNames; - _rootName = src._rootName; - _view = src._view; - _attributes = src._attributes; - _configOverrides = src._configOverrides; - } - - protected MapperConfigBase(MapperConfigBase src, int mapperFeatures) - { - super(src, mapperFeatures); - _mixIns = src._mixIns; + _typeFactory = src._typeFactory; + _classIntrospector = src._classIntrospector; + _typeResolverProvider = src._typeResolverProvider; _subtypeResolver = src._subtypeResolver; - _rootNames = src._rootNames; - _rootName = src._rootName; - _view = src._view; - _attributes = src._attributes; - _configOverrides = src._configOverrides; - } - protected MapperConfigBase(MapperConfigBase src, SubtypeResolver str) { - super(src); _mixIns = src._mixIns; - _subtypeResolver = str; _rootNames = src._rootNames; _rootName = src._rootName; _view = src._view; @@ -208,8 +168,12 @@ protected MapperConfigBase(MapperConfigBase src, SubtypeResolver str) { protected MapperConfigBase(MapperConfigBase src, PropertyName rootName) { super(src); - _mixIns = src._mixIns; + _typeFactory = src._typeFactory; + _classIntrospector = src._classIntrospector; + _typeResolverProvider = src._typeResolverProvider; _subtypeResolver = src._subtypeResolver; + + _mixIns = src._mixIns; _rootNames = src._rootNames; _rootName = rootName; _view = src._view; @@ -220,8 +184,12 @@ protected MapperConfigBase(MapperConfigBase src, PropertyName rootName) { protected MapperConfigBase(MapperConfigBase src, Class view) { super(src); - _mixIns = src._mixIns; + _typeFactory = src._typeFactory; + _classIntrospector = src._classIntrospector; + _typeResolverProvider = src._typeResolverProvider; _subtypeResolver = src._subtypeResolver; + + _mixIns = src._mixIns; _rootNames = src._rootNames; _rootName = src._rootName; _view = view; @@ -229,29 +197,15 @@ protected MapperConfigBase(MapperConfigBase src, Class view) _configOverrides = src._configOverrides; } - /** - * @since 2.1 - */ - protected MapperConfigBase(MapperConfigBase src, SimpleMixInResolver mixins) + protected MapperConfigBase(MapperConfigBase src, ContextAttributes attr) { super(src); - _mixIns = mixins; + _typeFactory = src._typeFactory; + _classIntrospector = src._classIntrospector; + _typeResolverProvider = src._typeResolverProvider; _subtypeResolver = src._subtypeResolver; - _rootNames = src._rootNames; - _rootName = src._rootName; - _view = src._view; - _attributes = src._attributes; - _configOverrides = src._configOverrides; - } - /** - * @since 2.3 - */ - protected MapperConfigBase(MapperConfigBase src, ContextAttributes attr) - { - super(src); _mixIns = src._mixIns; - _subtypeResolver = src._subtypeResolver; _rootNames = src._rootNames; _rootName = src._rootName; _view = src._view; @@ -260,143 +214,28 @@ protected MapperConfigBase(MapperConfigBase src, ContextAttributes attr) } /* - /********************************************************** + /********************************************************************** /* Abstract fluent factory methods to be implemented by subtypes - /********************************************************** + /********************************************************************** */ - /** - * @since 2.9 (in this case, demoted from sub-classes) - */ protected abstract T _withBase(BaseSettings newBase); - /** - * @since 2.9 (in this case, demoted from sub-classes) - */ - protected abstract T _withMapperFeatures(int mapperFeatures); - /* - /********************************************************** - /* Additional shared fluent factory methods; features - /********************************************************** - */ - - /** - * Fluent factory method that will construct and return a new configuration - * object instance with specified features enabled. - */ - @SuppressWarnings("unchecked") - @Override - public final T with(MapperFeature... features) - { - int newMapperFlags = _mapperFeatures; - for (MapperFeature f : features) { - newMapperFlags |= f.getMask(); - } - if (newMapperFlags == _mapperFeatures) { - return (T) this; - } - return _withMapperFeatures(newMapperFlags); - } - - /** - * Fluent factory method that will construct and return a new configuration - * object instance with specified features disabled. - */ - @SuppressWarnings("unchecked") - @Override - public final T without(MapperFeature... features) - { - int newMapperFlags = _mapperFeatures; - for (MapperFeature f : features) { - newMapperFlags &= ~f.getMask(); - } - if (newMapperFlags == _mapperFeatures) { - return (T) this; - } - return _withMapperFeatures(newMapperFlags); - } - - @SuppressWarnings("unchecked") - @Override - public final T with(MapperFeature feature, boolean state) - { - int newMapperFlags; - if (state) { - newMapperFlags = _mapperFeatures | feature.getMask(); - } else { - newMapperFlags = _mapperFeatures & ~feature.getMask(); - } - if (newMapperFlags == _mapperFeatures) { - return (T) this; - } - return _withMapperFeatures(newMapperFlags); - } - - /* - /********************************************************** - /* Additional shared fluent factory methods; introspectors - /********************************************************** - */ - - /** - * Method for constructing and returning a new instance with different - * {@link AnnotationIntrospector} to use (replacing old one). - *

    - * NOTE: make sure to register new instance with ObjectMapper - * if directly calling this method. - */ - public final T with(AnnotationIntrospector ai) { - return _withBase(_base.withAnnotationIntrospector(ai)); - } - - /** - * Method for constructing and returning a new instance with additional - * {@link AnnotationIntrospector} appended (as the lowest priority one) - */ - public final T withAppendedAnnotationIntrospector(AnnotationIntrospector ai) { - return _withBase(_base.withAppendedAnnotationIntrospector(ai)); - } - - /** - * Method for constructing and returning a new instance with additional - * {@link AnnotationIntrospector} inserted (as the highest priority one) - */ - public final T withInsertedAnnotationIntrospector(AnnotationIntrospector ai) { - return _withBase(_base.withInsertedAnnotationIntrospector(ai)); - } - - /** - * Method for constructing and returning a new instance with different - * {@link ClassIntrospector} - * to use. - *

    - * NOTE: make sure to register new instance with ObjectMapper - * if directly calling this method. - */ - public final T with(ClassIntrospector ci) { - return _withBase(_base.withClassIntrospector(ci)); - } - - /* - /********************************************************** + /********************************************************************** /* Additional shared fluent factory methods; attributes - /********************************************************** + /********************************************************************** */ /** * Method for constructing an instance that has specified * contextual attributes. - * - * @since 2.3 */ public abstract T with(ContextAttributes attrs); /** * Method for constructing an instance that has only specified * attributes, removing any attributes that exist before the call. - * - * @since 2.3 */ public T withAttributes(Map attributes) { return with(getAttributes().withSharedAttributes(attributes)); @@ -405,8 +244,6 @@ public T withAttributes(Map attributes) { /** * Method for constructing an instance that has specified * value for attribute for given key. - * - * @since 2.3 */ public T withAttribute(Object key, Object value) { return with(getAttributes().withSharedAttribute(key, value)); @@ -415,66 +252,39 @@ public T withAttribute(Object key, Object value) { /** * Method for constructing an instance that has no * value for attribute for given key. - * - * @since 2.3 */ public T withoutAttribute(Object key) { return with(getAttributes().withoutSharedAttribute(key)); } /* - /********************************************************** + /********************************************************************** /* Additional shared fluent factory methods; factories - /********************************************************** + /********************************************************************** */ - /** - * Method for constructing and returning a new instance with different - * {@link TypeFactory} - * to use. - */ - public final T with(TypeFactory tf) { - return _withBase( _base.withTypeFactory(tf)); - } - /** * Method for constructing and returning a new instance with different * {@link TypeResolverBuilder} to use. */ public final T with(TypeResolverBuilder trb) { - return _withBase(_base.withTypeResolverBuilder(trb)); + return _withBase(_base.with(trb)); } - /** - * Method for constructing and returning a new instance with different - * {@link PropertyNamingStrategy} - * to use. - *

    - * NOTE: make sure to register new instance with ObjectMapper - * if directly calling this method. + /* + /********************************************************************** + /* Additional shared fluent factory methods; other + /********************************************************************** */ - public final T with(PropertyNamingStrategy pns) { - return _withBase(_base.withPropertyNamingStrategy(pns)); - } /** - * Method for constructing and returning a new instance with different - * {@link HandlerInstantiator} - * to use. - *

    - * NOTE: make sure to register new instance with ObjectMapper - * if directly calling this method. + * Fluent factory method that will construct a new instance with + * specified {@link JsonNodeFactory} */ - public final T with(HandlerInstantiator hi) { - return _withBase(_base.withHandlerInstantiator(hi)); + public final T with(JsonNodeFactory f) { + return _withBase(_base.with(f)); } - /* - /********************************************************** - /* Additional shared fluent factory methods; other - /********************************************************** - */ - /** * Method for constructing and returning a new instance with different * default {@link Base64Variant} to use with base64-encoded binary values. @@ -491,7 +301,7 @@ public final T with(Base64Variant base64) { * NOTE: non-final since SerializationConfig needs to override this */ public T with(DateFormat df) { - return _withBase(_base.withDateFormat(df)); + return _withBase(_base.with(df)); } /** @@ -523,8 +333,6 @@ public final T with(TimeZone tz) { * @param rootName to use: if null, means "use default" (clear setting); * if empty String ("") means that no root name wrapping is used; * otherwise defines root name to use. - * - * @since 2.6 */ public abstract T withRootName(PropertyName rootName); @@ -534,16 +342,6 @@ public T withRootName(String rootName) { } return withRootName(PropertyName.construct(rootName)); } - - /** - * Method for constructing and returning a new instance with different - * {@link SubtypeResolver} - * to use. - *

    - * NOTE: make sure to register new instance with ObjectMapper - * if directly calling this method. - */ - public abstract T with(SubtypeResolver str); /** * Method for constructing and returning a new instance with different @@ -552,11 +350,26 @@ public T withRootName(String rootName) { public abstract T withView(Class view); /* - /********************************************************** - /* Simple accessors - /********************************************************** + /********************************************************************** + /* Simple factory access, related + /********************************************************************** */ + @Override + public final TypeFactory getTypeFactory() { + return _typeFactory; + } + + @Override + public ClassIntrospector getClassIntrospector() { + return _classIntrospector; + } + + @Override + public TypeResolverProvider getTypeResolverProvider() { + return _typeResolverProvider; + } + /** * Accessor for object used for finding out all reachable subtypes * for supertypes; needed when a logical type name is used instead @@ -567,17 +380,27 @@ public final SubtypeResolver getSubtypeResolver() { return _subtypeResolver; } - /** - * @deprecated Since 2.6 use {@link #getFullRootName} instead. - */ - @Deprecated // since 2.6 - public final String getRootName() { - return (_rootName == null) ? null : _rootName.getSimpleName(); + @Override + public final JavaType constructType(Class cls) { + return getTypeFactory().constructType(cls); } - /** - * @since 2.6 + @Override + public final JavaType constructType(TypeReference valueTypeRef) { + return getTypeFactory().constructType(valueTypeRef.getType()); + } + + @Override + public final JavaType constructSpecializedType(JavaType baseType, Class subclass) { + return getTypeFactory().constructSpecializedType(baseType, subclass); + } + + /* + /********************************************************************** + /* Simple config property access + /********************************************************************** */ + public final PropertyName getFullRootName() { return _rootName; } @@ -593,9 +416,9 @@ public final ContextAttributes getAttributes() { } /* - /********************************************************** + /********************************************************************** /* Configuration access; default/overrides - /********************************************************** + /********************************************************************** */ @Override @@ -673,38 +496,19 @@ public final JsonIgnoreProperties.Value getDefaultPropertyIgnorals(Class base } @Override - public final VisibilityChecker getDefaultVisibilityChecker() + public final VisibilityChecker getDefaultVisibilityChecker() { - VisibilityChecker vchecker = _configOverrides.getDefaultVisibility(); - // then global overrides (disabling) - // 05-Mar-2018, tatu: As per [databind#1947], need to see if any disabled - if ((_mapperFeatures & AUTO_DETECT_MASK) != AUTO_DETECT_MASK) { - if (!isEnabled(MapperFeature.AUTO_DETECT_FIELDS)) { - vchecker = vchecker.withFieldVisibility(Visibility.NONE); - } - if (!isEnabled(MapperFeature.AUTO_DETECT_GETTERS)) { - vchecker = vchecker.withGetterVisibility(Visibility.NONE); - } - if (!isEnabled(MapperFeature.AUTO_DETECT_IS_GETTERS)) { - vchecker = vchecker.withIsGetterVisibility(Visibility.NONE); - } - if (!isEnabled(MapperFeature.AUTO_DETECT_SETTERS)) { - vchecker = vchecker.withSetterVisibility(Visibility.NONE); - } - if (!isEnabled(MapperFeature.AUTO_DETECT_CREATORS)) { - vchecker = vchecker.withCreatorVisibility(Visibility.NONE); - } - } - return vchecker; + return _configOverrides.getDefaultVisibility(); } - @Override // since 2.9 - public final VisibilityChecker getDefaultVisibilityChecker(Class baseType, - AnnotatedClass actualClass) { - VisibilityChecker vc = getDefaultVisibilityChecker(); + @Override + public final VisibilityChecker getDefaultVisibilityChecker(Class baseType, + AnnotatedClass actualClass) + { + VisibilityChecker vc = getDefaultVisibilityChecker(); AnnotationIntrospector intr = getAnnotationIntrospector(); if (intr != null) { - vc = intr.findAutoDetectVisibility(actualClass, vc); + vc = intr.findAutoDetectVisibility(this, actualClass, vc); } ConfigOverride overrides = _configOverrides.findOverride(baseType); if (overrides != null) { @@ -714,8 +518,8 @@ public final VisibilityChecker getDefaultVisibilityChecker(Class baseType, } @Override - public final JsonSetter.Value getDefaultSetterInfo() { - return _configOverrides.getDefaultSetterInfo(); + public final JsonSetter.Value getDefaultNullHandling() { + return _configOverrides.getDefaultNullHandling(); } @Override @@ -737,9 +541,9 @@ public Boolean getDefaultMergeable(Class baseType) { } /* - /********************************************************** + /********************************************************************** /* Other config access - /********************************************************** + /********************************************************************** */ @Override @@ -759,9 +563,9 @@ public PropertyName findRootName(Class rawRootType) { } /* - /********************************************************** + /********************************************************************** /* ClassIntrospector.MixInResolver impl: - /********************************************************** + /********************************************************************** */ /** @@ -775,7 +579,7 @@ public final Class findMixInClassFor(Class cls) { // Not really relevant here (should not get called) @Override - public MixInResolver copy() { + public MixInResolver snapshot() { throw new UnsupportedOperationException(); } diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/ModuleContextBase.java b/src/main/java/com/fasterxml/jackson/databind/cfg/ModuleContextBase.java new file mode 100644 index 0000000000..4eb1f066e1 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/cfg/ModuleContextBase.java @@ -0,0 +1,306 @@ +package com.fasterxml.jackson.databind.cfg; + +import java.util.Collection; +import java.util.function.UnaryOperator; + +import com.fasterxml.jackson.core.*; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.Module.SetupContext; +import com.fasterxml.jackson.databind.deser.*; +import com.fasterxml.jackson.databind.jsontype.NamedType; +import com.fasterxml.jackson.databind.ser.BeanSerializerModifier; +import com.fasterxml.jackson.databind.ser.SerializerFactory; +import com.fasterxml.jackson.databind.ser.Serializers; +import com.fasterxml.jackson.databind.type.TypeFactory; +import com.fasterxml.jackson.databind.type.TypeModifier; + +public class ModuleContextBase + implements SetupContext +{ + // // // Immutable objects we need to access information + + protected final MapperBuilder _builder; + + // // // Other modifiable state + + protected final ConfigOverrides _configOverrides; + + /* + /********************************************************************** + /* Life-cycle + /********************************************************************** + */ + + public ModuleContextBase(MapperBuilder b, + ConfigOverrides configOverrides) + { + _builder = b; + _configOverrides = configOverrides; + } + + /** + * Method called after all changes have been applied through this context, to + * propagate buffered or pending changes (if any) back to builder. Note that + * base implementation does nothing here; it is only provded in case sub-classes + * might need it. + */ + public void applyChanges(MapperBuilder b) { + // nothing to apply at base class + } + + /* + /********************************************************************** + /* Accessors for metadata + /********************************************************************** + */ + + @Override + public Version getMapperVersion() { + return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION; + } + + @Override + public String getFormatName() { + return _streamFactory().getFormatName(); + } + + /* + /********************************************************************** + /* Accessors for subset of handlers + /********************************************************************** + */ + + @Override + public Object getOwner() { + return _builder; + } + + @Override + public TypeFactory typeFactory() { + return _builder.typeFactory(); + } + + @Override + public TokenStreamFactory tokenStreamFactory() { + return _streamFactory(); + } + + /* + /********************************************************************** + /* Accessors on/off features + /********************************************************************** + */ + + @Override + public boolean isEnabled(MapperFeature f) { + return _builder.isEnabled(f); + } + + @Override + public boolean isEnabled(DeserializationFeature f) { + return _builder.isEnabled(f); + } + + @Override + public boolean isEnabled(SerializationFeature f) { + return _builder.isEnabled(f); + } + + @Override + public boolean isEnabled(TokenStreamFactory.Feature f) { + return _streamFactory().isEnabled(f); + } + + @Override + public boolean isEnabled(StreamReadFeature f) { + return _builder.isEnabled(f); + } + + @Override + public boolean isEnabled(StreamWriteFeature f) { + return _builder.isEnabled(f); + } + + /* + /********************************************************************** + /* Mutators for adding deserializers, related + /********************************************************************** + */ + + @Override + public SetupContext addDeserializers(Deserializers d) { + _set(_deserializerFactory().withAdditionalDeserializers(d)); + return this; + } + + @Override + public SetupContext addKeyDeserializers(KeyDeserializers kd) { + _set(_deserializerFactory().withAdditionalKeyDeserializers(kd)); + return this; + } + + @Override + public SetupContext addDeserializerModifier(BeanDeserializerModifier modifier) { + _set(_deserializerFactory().withDeserializerModifier(modifier)); + return this; + } + + @Override + public SetupContext addValueInstantiators(ValueInstantiators instantiators) { + _set(_deserializerFactory().withValueInstantiators(instantiators)); + return this; + } + + /* + /********************************************************************** + /* Mutators for adding serializers, related + /********************************************************************** + */ + + @Override + public SetupContext addSerializers(Serializers s) { + _set(_serializerFactory().withAdditionalSerializers(s)); + return this; + } + + @Override + public SetupContext addKeySerializers(Serializers s) { + _set(_serializerFactory().withAdditionalKeySerializers(s)); + return this; + } + + @Override + public SetupContext addSerializerModifier(BeanSerializerModifier modifier) { + _set(_serializerFactory().withSerializerModifier(modifier)); + return this; + } + + @Override + public SetupContext overrideDefaultNullKeySerializer(JsonSerializer ser) { + _set(_serializerFactory().withNullKeySerializer(ser)); + return this; + } + + @Override + public SetupContext overrideDefaultNullValueSerializer(JsonSerializer ser) { + _set(_serializerFactory().withNullValueSerializer(ser)); + return this; + } + + /* + /********************************************************************** + /* Mutators for type handling + /********************************************************************** + */ + + @Override + public SetupContext addAbstractTypeResolver(AbstractTypeResolver resolver) { + _builder.addAbstractTypeResolver(resolver); + return this; + } + + @Override + public SetupContext addTypeModifier(TypeModifier modifier) { + _builder.addTypeModifier(modifier); + return this; + } + + @Override + public SetupContext registerSubtypes(Class... subtypes) { + _builder.subtypeResolver().registerSubtypes(subtypes); + return this; + } + + @Override + public SetupContext registerSubtypes(NamedType... subtypes) { + _builder.subtypeResolver().registerSubtypes(subtypes); + return this; + } + + @Override + public SetupContext registerSubtypes(Collection> subtypes) { + _builder.subtypeResolver().registerSubtypes(subtypes); + return this; + } + + /* + /********************************************************************** + /* Mutators for annotation introspection + /********************************************************************** + */ + + @Override + public SetupContext insertAnnotationIntrospector(AnnotationIntrospector ai) { + _builder.baseSettings(_builder.baseSettings() + .withInsertedAnnotationIntrospector(ai)); + return this; + } + + @Override + public SetupContext appendAnnotationIntrospector(AnnotationIntrospector ai) { + _builder.baseSettings(_builder.baseSettings() + .withAppendedAnnotationIntrospector(ai)); + return this; + } + + /* + /********************************************************************** + /* Mutators, other + /********************************************************************** + */ + + @Override + public MutableConfigOverride configOverride(Class type) { + return _configOverrides.findOrCreateOverride(type); + } + + @Override + public SetupContext addHandler(DeserializationProblemHandler handler) + { + _builder.addHandler(handler); + return this; + } + + @Override + public SetupContext overrideInjectableValues(UnaryOperator v) { + InjectableValues oldV = _builder.injectableValues(); + InjectableValues newV = v.apply(oldV); + if (newV != oldV) { + _builder.injectableValues(newV); + } + return this; + } + + @Override + public SetupContext setMixIn(Class target, Class mixinSource) { + _builder.addMixIn(target, mixinSource); + return this; + } + + /* + /********************************************************************** + /* Internal/sub-class helper methods + /********************************************************************** + */ + + protected TokenStreamFactory _streamFactory() { + return _builder.streamFactory(); + } + + protected DeserializerFactory _deserializerFactory() { + return _builder.deserializerFactory(); + } + + protected void _set(DeserializerFactory f) { + _builder.deserializerFactory(f); + } + + protected SerializerFactory _serializerFactory() { + return _builder.serializerFactory(); + } + + protected void _set(SerializerFactory f) { + _builder.serializerFactory(f); + } +} diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/MutableConfigOverride.java b/src/main/java/com/fasterxml/jackson/databind/cfg/MutableConfigOverride.java index b30b99b7d7..c717f5741f 100644 --- a/src/main/java/com/fasterxml/jackson/databind/cfg/MutableConfigOverride.java +++ b/src/main/java/com/fasterxml/jackson/databind/cfg/MutableConfigOverride.java @@ -70,25 +70,16 @@ public MutableConfigOverride setIsIgnoredType(Boolean v) { return this; } - /** - * @since 2.9 - */ - public MutableConfigOverride setSetterInfo(JsonSetter.Value v) { - _setterInfo = v; + public MutableConfigOverride setNullHandling(JsonSetter.Value v) { + _nullHandling = v; return this; } - /** - * @since 2.9 - */ public MutableConfigOverride setVisibility(JsonAutoDetect.Value v) { _visibility = v; return this; } - /** - * @since 2.9 - */ public MutableConfigOverride setMergeable(Boolean v) { _mergeable = v; return this; diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/SerializationContexts.java b/src/main/java/com/fasterxml/jackson/databind/cfg/SerializationContexts.java new file mode 100644 index 0000000000..117524eceb --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/cfg/SerializationContexts.java @@ -0,0 +1,164 @@ +package com.fasterxml.jackson.databind.cfg; + +import com.fasterxml.jackson.core.TokenStreamFactory; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationConfig; +import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider; +import com.fasterxml.jackson.databind.ser.SerializerCache; +import com.fasterxml.jackson.databind.ser.SerializerFactory; + +/** + * Factory/builder class that replaces Jackson 2.x concept of "blueprint" instance + * of {@link com.fasterxml.jackson.databind.SerializerProvider}. It will be constructed and configured during + * {@link ObjectMapper} building phase, and will be called once per {@code writeValue} + * call to construct actual stateful {@link com.fasterxml.jackson.databind.SerializerProvider} to use during + * serialization. + *

    + * Note that since this object has to be serializable (to allow JDK serialization of + * mapper instances), {@link com.fasterxml.jackson.databind.SerializerProvider} need not be serializable any more. + * + * @since 3.0 + */ +public abstract class SerializationContexts + implements java.io.Serializable +{ + private static final long serialVersionUID = 3L; + + /* + /********************************************************************** + /* Configuration + /********************************************************************** + */ + + // NOTE! We do not need (or want) to serialize any of these because they + // get passed via `forMapper(...)` call; all we want to serialize is identity + // of this class (and possibly whatever sub-classes may want to retain). + // Hence `transient` modifiers + + /** + * Low-level {@link TokenStreamFactory} that may be used for constructing + * embedded generators. + */ + final transient protected TokenStreamFactory _streamFactory; + + /** + * Factory responsible for constructing standard serializers. + */ + final transient protected SerializerFactory _serializerFactory; + + /** + * Cache for doing type-to-value-serializer lookups. + */ + final transient protected SerializerCache _cache; + + /* + /********************************************************************** + /* Life-cycle + /********************************************************************** + */ + + protected SerializationContexts() { this(null, null, null); } + + protected SerializationContexts(TokenStreamFactory tsf, + SerializerFactory serializerFactory, SerializerCache cache) { + _streamFactory = tsf; + _serializerFactory = serializerFactory; + _cache = cache; + } + + /** + * Mutant factory method called when instance is actually created for use by mapper + * (as opposed to coming into existence during building, module registration). + * Necessary usually to initialize non-configuration state, such as caching. + */ + public SerializationContexts forMapper(Object mapper, + TokenStreamFactory tsf, SerializerFactory serializerFactory) { + return forMapper(mapper, tsf, serializerFactory, _defaultCache()); + } + + protected abstract SerializationContexts forMapper(Object mapper, + TokenStreamFactory tsf, SerializerFactory serializerFactory, + SerializerCache cache); + + /** + * Factory method for constructing context object for individual {@code writeValue()} + * calls. + */ + public abstract DefaultSerializerProvider createContext(SerializationConfig config, + GeneratorSettings genSettings); + + /* + /********************************************************************** + /* Overridable default methods + /********************************************************************** + */ + + /** + * Factory method for constructing per-mapper serializer cache to use. + */ + protected SerializerCache _defaultCache() { + return new SerializerCache(); + } + + /* + /********************************************************************** + /* Access to caching details + /********************************************************************** + */ + + /** + * Method that can be used to determine how many serializers this + * provider is caching currently + * (if it does caching: default implementation does) + * Exact count depends on what kind of serializers get cached; + * default implementation caches all serializers, including ones that + * are eagerly constructed (for optimal access speed) + *

    + * The main use case for this method is to allow conditional flushing of + * serializer cache, if certain number of entries is reached. + */ + public int cachedSerializersCount() { + return _cache.size(); + } + + /** + * Method that will drop all serializers currently cached by this provider. + * This can be used to remove memory usage (in case some serializers are + * only used once or so), or to force re-construction of serializers after + * configuration changes for mapper than owns the provider. + */ + public void flushCachedSerializers() { + _cache.flush(); + } + + /* + /********************************************************************** + /* Vanilla implementation + /********************************************************************** + */ + + public static class DefaultImpl extends SerializationContexts + { + private static final long serialVersionUID = 3L; + + public DefaultImpl() { super(null, null, null); } + public DefaultImpl(TokenStreamFactory tsf, + SerializerFactory serializerFactory, SerializerCache cache) { + super(tsf, serializerFactory, cache); + } + + @Override + public SerializationContexts forMapper(Object mapper, + TokenStreamFactory tsf, SerializerFactory serializerFactory, + SerializerCache cache) { + return new DefaultImpl(tsf, serializerFactory, cache); + } + + @Override + public DefaultSerializerProvider createContext(SerializationConfig config, + GeneratorSettings genSettings) { + return new DefaultSerializerProvider.Impl(_streamFactory, + config, genSettings, _serializerFactory, _cache); + } + } +} diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/SerializerFactoryConfig.java b/src/main/java/com/fasterxml/jackson/databind/cfg/SerializerFactoryConfig.java index 843763359f..5a34c91eae 100644 --- a/src/main/java/com/fasterxml/jackson/databind/cfg/SerializerFactoryConfig.java +++ b/src/main/java/com/fasterxml/jackson/databind/cfg/SerializerFactoryConfig.java @@ -1,6 +1,10 @@ package com.fasterxml.jackson.databind.cfg; +import java.util.Objects; + +import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.ser.*; +import com.fasterxml.jackson.databind.ser.impl.FailingSerializer; import com.fasterxml.jackson.databind.util.ArrayBuilders; import com.fasterxml.jackson.databind.util.ArrayIterator; @@ -11,7 +15,11 @@ public final class SerializerFactoryConfig implements java.io.Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 3L; + + public final static JsonSerializer DEFAULT_NULL_KEY_SERIALIZER = + new FailingSerializer("Null key for a Map not allowed in JSON (use a converting NullKeySerializer?)"); + /** * Constant for empty Serializers array (which by definition * is stateless and reusable) @@ -37,53 +45,85 @@ public final class SerializerFactoryConfig * are configured and constructed. */ protected final BeanSerializerModifier[] _modifiers; + + /** + * Serializer used to output a null value, unless explicitly redefined for property. + */ + protected final JsonSerializer _nullValueSerializer; + + /** + * Serializer used to (try to) output a null key, due to an entry of + * {@link java.util.Map} having null key. + */ + protected final JsonSerializer _nullKeySerializer; public SerializerFactoryConfig() { - this(null, null, null); + this(null, null, null, + DEFAULT_NULL_KEY_SERIALIZER, null); + } protected SerializerFactoryConfig(Serializers[] allAdditionalSerializers, Serializers[] allAdditionalKeySerializers, - BeanSerializerModifier[] modifiers) + BeanSerializerModifier[] modifiers, + JsonSerializer nullKeySer, + JsonSerializer nullValueSer) { _additionalSerializers = (allAdditionalSerializers == null) ? NO_SERIALIZERS : allAdditionalSerializers; _additionalKeySerializers = (allAdditionalKeySerializers == null) ? NO_SERIALIZERS : allAdditionalKeySerializers; _modifiers = (modifiers == null) ? NO_MODIFIERS : modifiers; + _nullKeySerializer = nullKeySer; + _nullValueSerializer = nullValueSer; } public SerializerFactoryConfig withAdditionalSerializers(Serializers additional) { - if (additional == null) { - throw new IllegalArgumentException("Cannot pass null Serializers"); - } + Objects.requireNonNull(additional, "Cannot pass null Serializers"); Serializers[] all = ArrayBuilders.insertInListNoDup(_additionalSerializers, additional); - return new SerializerFactoryConfig(all, _additionalKeySerializers, _modifiers); + return new SerializerFactoryConfig(all, _additionalKeySerializers, _modifiers, + _nullKeySerializer, _nullValueSerializer); } public SerializerFactoryConfig withAdditionalKeySerializers(Serializers additional) { - if (additional == null) { - throw new IllegalArgumentException("Cannot pass null Serializers"); - } + Objects.requireNonNull(additional, "Cannot pass null Serializers"); Serializers[] all = ArrayBuilders.insertInListNoDup(_additionalKeySerializers, additional); - return new SerializerFactoryConfig(_additionalSerializers, all, _modifiers); + return new SerializerFactoryConfig(_additionalSerializers, all, _modifiers, + _nullKeySerializer, _nullValueSerializer); } - + public SerializerFactoryConfig withSerializerModifier(BeanSerializerModifier modifier) { - if (modifier == null) { - throw new IllegalArgumentException("Cannot pass null modifier"); - } + Objects.requireNonNull(modifier, "Cannot pass null BeanSerializerModifier"); BeanSerializerModifier[] modifiers = ArrayBuilders.insertInListNoDup(_modifiers, modifier); - return new SerializerFactoryConfig(_additionalSerializers, _additionalKeySerializers, modifiers); + return new SerializerFactoryConfig(_additionalSerializers, _additionalKeySerializers, modifiers, + _nullKeySerializer, _nullValueSerializer); + } + + @SuppressWarnings("unchecked") + public SerializerFactoryConfig withNullValueSerializer(JsonSerializer nvs) { + Objects.requireNonNull(nvs, "Cannot pass null JsonSerializer"); + return new SerializerFactoryConfig(_additionalSerializers, _additionalKeySerializers, _modifiers, + _nullKeySerializer, (JsonSerializer) nvs); + } + + @SuppressWarnings("unchecked") + public SerializerFactoryConfig withNullKeySerializer(JsonSerializer nks) { + Objects.requireNonNull(nks, "Cannot pass null JsonSerializer"); + return new SerializerFactoryConfig(_additionalSerializers, _additionalKeySerializers, _modifiers, + (JsonSerializer) nks, _nullValueSerializer); } public boolean hasSerializers() { return _additionalSerializers.length > 0; } public boolean hasKeySerializers() { return _additionalKeySerializers.length > 0; } public boolean hasSerializerModifiers() { return _modifiers.length > 0; } + public Iterable serializers() { return new ArrayIterator(_additionalSerializers); } public Iterable keySerializers() { return new ArrayIterator(_additionalKeySerializers); } public Iterable serializerModifiers() { return new ArrayIterator(_modifiers); } + + public JsonSerializer getNullKeySerializer() { return _nullKeySerializer; } + public JsonSerializer getNullValueSerializer() { return _nullValueSerializer; } } 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 1e1d80e1fa..6ce85912c5 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/AbstractDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/AbstractDeserializer.java @@ -25,8 +25,7 @@ */ public class AbstractDeserializer extends JsonDeserializer - implements ContextualDeserializer, // since 2.9 - java.io.Serializable + implements java.io.Serializable { private static final long serialVersionUID = 1L; @@ -73,12 +72,6 @@ public AbstractDeserializer(BeanDeserializerBuilder builder, _acceptDouble = (cls == Double.TYPE) || cls.isAssignableFrom(Double.class); } - @Deprecated // since 2.9 - public AbstractDeserializer(BeanDeserializerBuilder builder, - BeanDescription beanDesc, Map backRefProps) { - this(builder, beanDesc, backRefProps, null); - } - protected AbstractDeserializer(BeanDescription beanDesc) { _baseType = beanDesc.getType(); @@ -126,7 +119,7 @@ public JsonDeserializer createContextual(DeserializationContext ctxt, if (property != null && intr != null) { final AnnotatedMember accessor = property.getMember(); if (accessor != null) { - ObjectIdInfo objectIdInfo = intr.findObjectIdInfo(accessor); + ObjectIdInfo objectIdInfo = intr.findObjectIdInfo(ctxt.getConfig(), accessor); if (objectIdInfo != null) { // some code duplication here as well (from BeanDeserializerFactory) JavaType idType; ObjectIdGenerator idGen; @@ -134,7 +127,7 @@ public JsonDeserializer createContextual(DeserializationContext ctxt, ObjectIdResolver resolver = ctxt.objectIdResolverInstance(accessor, objectIdInfo); // 2.1: allow modifications by "id ref" annotations as well: - objectIdInfo = intr.findObjectReferenceInfo(accessor, objectIdInfo); + objectIdInfo = intr.findObjectReferenceInfo(ctxt.getConfig(), accessor, objectIdInfo); Class implClass = objectIdInfo.getGeneratorType(); if (implClass == ObjectIdGenerators.PropertyGenerator.class) { @@ -203,7 +196,7 @@ public Boolean supportsUpdate(DeserializationConfig config) { * (either via value type or referring property). */ @Override - public ObjectIdReader getObjectIdReader() { + public ObjectIdReader getObjectIdReader(DeserializationContext ctxt) { return _objectIdReader; } @@ -230,7 +223,7 @@ public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, // Hmmh. One tricky question; for scalar, is it an Object Id, or "Natural" type? // for now, prefer Object Id: if (_objectIdReader != null) { - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t != null) { // Most commonly, a scalar (int id, uuid String, ...) if (t.isScalarValue()) { @@ -241,7 +234,7 @@ public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, t = p.nextToken(); } if ((t == JsonToken.FIELD_NAME) && _objectIdReader.maySerializeAsObject() - && _objectIdReader.isValidReferencePropertyName(p.getCurrentName(), p)) { + && _objectIdReader.isValidReferencePropertyName(p.currentName(), p)) { return _deserializeFromObjectId(p, ctxt); } } @@ -280,7 +273,7 @@ protected Object _deserializeIfNatural(JsonParser p, DeserializationContext ctxt * Finally, we may have to consider possibility of custom handlers for * these values: but for now this should work ok. */ - switch (p.getCurrentTokenId()) { + switch (p.currentTokenId()) { case JsonTokenId.ID_STRING: if (_acceptString) { return p.getText(); 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 5e0132add3..33e49d70d8 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java @@ -11,7 +11,6 @@ import com.fasterxml.jackson.annotation.JsonCreator.Mode; import com.fasterxml.jackson.core.JsonLocation; -import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig; @@ -20,12 +19,13 @@ import com.fasterxml.jackson.databind.deser.impl.CreatorCollector; import com.fasterxml.jackson.databind.deser.impl.JavaUtilCollectionsDeserializers; import com.fasterxml.jackson.databind.deser.std.*; -import com.fasterxml.jackson.databind.exc.InvalidDefinitionException; import com.fasterxml.jackson.databind.ext.OptionalHandlerFactory; +import com.fasterxml.jackson.databind.ext.jdk8.Jdk8OptionalDeserializer; +import com.fasterxml.jackson.databind.ext.jdk8.OptionalDoubleDeserializer; +import com.fasterxml.jackson.databind.ext.jdk8.OptionalIntDeserializer; +import com.fasterxml.jackson.databind.ext.jdk8.OptionalLongDeserializer; import com.fasterxml.jackson.databind.introspect.*; -import com.fasterxml.jackson.databind.jsontype.NamedType; import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; -import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; import com.fasterxml.jackson.databind.type.*; import com.fasterxml.jackson.databind.util.*; @@ -89,20 +89,19 @@ public abstract class BasicDeserializerFactory _collectionFallbacks.put(Queue.class.getName(), LinkedList.class); // then JDK 1.6 types: - /* 17-May-2013, tatu: [databind#216] Should be fine to use straight Class references EXCEPT - * that some god-forsaken platforms (... looking at you, Android) do not - * include these. So, use "soft" references... - */ + // 17-May-2013, tatu: [databind#216] Should be fine to use straight Class references EXCEPT + // that some god-forsaken platforms (... looking at you, Android) do not + // include these. So, use "soft" references... _collectionFallbacks.put("java.util.Deque", LinkedList.class); _collectionFallbacks.put("java.util.NavigableSet", TreeSet.class); } /* - /********************************************************** + /********************************************************************** /* Config - /********************************************************** + /********************************************************************** */ - + /** * Configuration settings for this factory; immutable instance (just like this * factory), new version created via copy-constructor (fluent-style) @@ -110,9 +109,9 @@ public abstract class BasicDeserializerFactory protected final DeserializerFactoryConfig _factoryConfig; /* - /********************************************************** + /********************************************************************** /* Life cycle - /********************************************************** + /********************************************************************** */ protected BasicDeserializerFactory(DeserializerFactoryConfig config) { @@ -131,11 +130,11 @@ public DeserializerFactoryConfig getFactoryConfig() { } protected abstract DeserializerFactory withConfig(DeserializerFactoryConfig config); - + /* - /******************************************************** + /********************************************************************** /* Configuration handling: fluent factories - /******************************************************** + /********************************************************************** */ /** @@ -165,15 +164,6 @@ public final DeserializerFactory withDeserializerModifier(BeanDeserializerModifi return withConfig(_factoryConfig.withDeserializerModifier(modifier)); } - /** - * Convenience method for creating a new factory instance with additional - * {@link AbstractTypeResolver}. - */ - @Override - public final DeserializerFactory withAbstractTypeResolver(AbstractTypeResolver resolver) { - return withConfig(_factoryConfig.withAbstractTypeResolver(resolver)); - } - /** * Convenience method for creating a new factory instance with additional * {@link ValueInstantiators}. @@ -184,54 +174,9 @@ public final DeserializerFactory withValueInstantiators(ValueInstantiators insta } /* - /********************************************************** - /* DeserializerFactory impl (partial): type mappings - /********************************************************** - */ - - @Override - public JavaType mapAbstractType(DeserializationConfig config, JavaType type) throws JsonMappingException - { - // first, general mappings - while (true) { - JavaType next = _mapAbstractType2(config, type); - if (next == null) { - return type; - } - // Should not have to worry about cycles; but better verify since they will invariably occur... :-) - // (also: guard against invalid resolution to a non-related type) - Class prevCls = type.getRawClass(); - Class nextCls = next.getRawClass(); - if ((prevCls == nextCls) || !prevCls.isAssignableFrom(nextCls)) { - throw new IllegalArgumentException("Invalid abstract type resolution from "+type+" to "+next+": latter is not a subtype of former"); - } - type = next; - } - } - - /** - * Method that will find abstract type mapping for specified type, doing a single - * lookup through registered abstract type resolvers; will not do recursive lookups. - */ - private JavaType _mapAbstractType2(DeserializationConfig config, JavaType type) - throws JsonMappingException - { - Class currClass = type.getRawClass(); - if (_factoryConfig.hasAbstractTypeResolvers()) { - for (AbstractTypeResolver resolver : _factoryConfig.abstractTypeResolvers()) { - JavaType concrete = resolver.findTypeMapping(config, type); - if ((concrete != null) && !concrete.hasRawClass(currClass)) { - return concrete; - } - } - } - return null; - } - - /* - /********************************************************** + /********************************************************************** /* JsonDeserializerFactory impl (partial): ValueInstantiators - /********************************************************** + /********************************************************************** */ /** @@ -249,7 +194,7 @@ public ValueInstantiator findValueInstantiator(DeserializationContext ctxt, ValueInstantiator instantiator = null; // Check @JsonValueInstantiator before anything else AnnotatedClass ac = beanDesc.getClassInfo(); - Object instDef = ctxt.getAnnotationIntrospector().findValueInstantiator(ac); + Object instDef = ctxt.getAnnotationIntrospector().findValueInstantiator(ctxt.getConfig(), ac); if (instDef != null) { instantiator = _valueInstantiatorInstance(config, ac, instDef); } @@ -274,15 +219,6 @@ public ValueInstantiator findValueInstantiator(DeserializationContext ctxt, } } } - - // Sanity check: does the chosen ValueInstantiator have incomplete creators? - if (instantiator.getIncompleteParameter() != null) { - final AnnotatedParameter nonAnnotatedParam = instantiator.getIncompleteParameter(); - final AnnotatedWithParams ctor = nonAnnotatedParam.getOwner(); - throw new IllegalArgumentException("Argument #"+nonAnnotatedParam.getIndex() - +" of constructor "+ctor+" has no property name annotation; must have name when multiple-parameter constructor annotated as Creator"); - } - return instantiator; } @@ -323,7 +259,7 @@ protected ValueInstantiator _constructDefaultValueInstantiator(DeserializationCo // need to construct suitable visibility checker: final DeserializationConfig config = ctxt.getConfig(); - VisibilityChecker vchecker = config.getDefaultVisibilityChecker(beanDesc.getBeanClass(), + VisibilityChecker vchecker = config.getDefaultVisibilityChecker(beanDesc.getBeanClass(), beanDesc.getClassInfo()); /* 24-Sep-2014, tatu: Tricky part first; need to merge resolved property information @@ -338,10 +274,10 @@ protected ValueInstantiator _constructDefaultValueInstantiator(DeserializationCo beanDesc); // Important: first add factory methods; then constructors, so // latter can override former! - _addDeserializerFactoryMethods(ctxt, beanDesc, vchecker, intr, creators, creatorDefs); + _addFactoryCreators(ctxt, beanDesc, vchecker, intr, creators, creatorDefs); // constructors only usable on concrete types: if (beanDesc.getType().isConcrete()) { - _addDeserializerConstructors(ctxt, beanDesc, vchecker, intr, creators, creatorDefs); + _addConstructorCreators(ctxt, beanDesc, vchecker, intr, creators, creatorDefs); } return creators.constructValueInstantiator(ctxt); } @@ -415,16 +351,16 @@ public ValueInstantiator _valueInstantiatorInstance(DeserializationConfig config } /* - /********************************************************** - /* Creator introspection - /********************************************************** + /********************************************************************** + /* Creator introspection, main methods + /********************************************************************** */ - protected void _addDeserializerConstructors(DeserializationContext ctxt, - BeanDescription beanDesc, VisibilityChecker vchecker, - AnnotationIntrospector intr, CreatorCollector creators, - Map creatorParams) - throws JsonMappingException + protected void _addConstructorCreators(DeserializationContext ctxt, + BeanDescription beanDesc, VisibilityChecker vchecker, + AnnotationIntrospector intr, CreatorCollector creators, + Map creatorParams) + throws JsonMappingException { // 25-Jan-2017, tatu: As per [databind#1501], [databind#1502], [databind#1503], best // for now to skip attempts at using anything but no-args constructor (see @@ -454,7 +390,10 @@ protected void _addDeserializerConstructors(DeserializationContext ctxt, } if (creatorMode == null) { // let's check Visibility here, to avoid further processing for non-visible? - if (vchecker.isCreatorVisible(ctor)) { + boolean visible = (ctor.getParameterCount() == 1) + ? vchecker.isScalarConstructorVisible(ctor) + : vchecker.isCreatorVisible(ctor); + if (visible) { nonAnnotated.add(CreatorCandidate.construct(intr, ctor, creatorParams.get(ctor))); } continue; @@ -480,15 +419,14 @@ protected void _addDeserializerConstructors(DeserializationContext ctxt, return; } List implicitCtors = null; + for (CreatorCandidate candidate : nonAnnotated) { final int argCount = candidate.paramCount(); final AnnotatedWithParams ctor = candidate.creator(); - // some single-arg factory methods (String, number) are auto-detected if (argCount == 1) { BeanPropertyDefinition propDef = candidate.propertyDef(0); boolean useProps = _checkIfCreatorPropertyBased(intr, ctor, propDef); - if (useProps) { SettableBeanProperty[] properties = new SettableBeanProperty[1]; PropertyName name = candidate.paramName(0); @@ -497,8 +435,7 @@ protected void _addDeserializerConstructors(DeserializationContext ctxt, creators.addPropertyCreator(ctor, false, properties); } else { /*boolean added = */ _handleSingleArgumentCreator(creators, - ctor, false, - vchecker.isCreatorVisible(ctor)); + ctor, false, true); // not-annotated, yes, visible // one more thing: sever link to creator property, to avoid possible later // problems with "unresolved" constructor property if (propDef != null) { @@ -558,6 +495,7 @@ protected void _addDeserializerConstructors(DeserializationContext ctxt, } final int namedCount = explicitNameCount + implicitWithCreatorCount; + // Ok: if named or injectable, we have more work to do if ((explicitNameCount > 0) || (injectCount > 0)) { // simple case; everything covered: @@ -605,10 +543,152 @@ protected void _addDeserializerConstructors(DeserializationContext ctxt, } } + protected void _addFactoryCreators(DeserializationContext ctxt, + BeanDescription beanDesc, VisibilityChecker vchecker, + AnnotationIntrospector intr, CreatorCollector creators, + Map creatorParams) + throws JsonMappingException + { + List nonAnnotated = new LinkedList<>(); + int explCount = 0; + + // 21-Sep-2017, tatu: First let's handle explicitly annotated ones + for (AnnotatedMethod factory : beanDesc.getFactoryMethods()) { + JsonCreator.Mode creatorMode = intr.findCreatorAnnotation(ctxt.getConfig(), factory); + final int argCount = factory.getParameterCount(); + if (creatorMode == null) { + // Only potentially accept 1-argument factory methods + if ((argCount == 1) && vchecker.isCreatorVisible(factory)) { + nonAnnotated.add(CreatorCandidate.construct(intr, factory, null)); + } + continue; + } + if (creatorMode == Mode.DISABLED) { + continue; + } + + // zero-arg method factory methods fine, as long as explicit + if (argCount == 0) { + creators.setDefaultCreator(factory); + continue; + } + + switch (creatorMode) { + case DELEGATING: + _addExplicitDelegatingCreator(ctxt, beanDesc, creators, + CreatorCandidate.construct(intr, factory, null)); + break; + case PROPERTIES: + _addExplicitPropertyCreator(ctxt, beanDesc, creators, + CreatorCandidate.construct(intr, factory, creatorParams.get(factory))); + break; + case DEFAULT: + default: + _addExplicitAnyCreator(ctxt, beanDesc, creators, + CreatorCandidate.construct(intr, factory, creatorParams.get(factory))); + break; + } + ++explCount; + } + // And only if and when those handled, consider potentially visible ones + if (explCount > 0) { // TODO: split method into two since we could have expl factories + return; + } + // And then implicitly found + for (CreatorCandidate candidate : nonAnnotated) { + final int argCount = candidate.paramCount(); + AnnotatedWithParams factory = candidate.creator(); + final BeanPropertyDefinition[] propDefs = creatorParams.get(factory); + // some single-arg factory methods (String, number) are auto-detected + if (argCount != 1) { + continue; // 2 and more args? Must be explicit, handled earlier + } + BeanPropertyDefinition argDef = candidate.propertyDef(0); + boolean useProps = _checkIfCreatorPropertyBased(intr, factory, argDef); + if (!useProps) { // not property based but delegating + /*boolean added=*/ _handleSingleArgumentCreator(creators, + factory, false, vchecker.isCreatorVisible(factory)); + // 23-Sep-2016, tatu: [databind#1383]: Need to also sever link to avoid possible + // later problems with "unresolved" constructor property + if (argDef != null) { + ((POJOPropertyBuilder) argDef).removeConstructors(); + } + continue; + } + AnnotatedParameter nonAnnotatedParam = null; + SettableBeanProperty[] properties = new SettableBeanProperty[argCount]; + int implicitNameCount = 0; + int explicitNameCount = 0; + int injectCount = 0; + + for (int i = 0; i < argCount; ++i) { + final AnnotatedParameter param = factory.getParameter(i); + BeanPropertyDefinition propDef = (propDefs == null) ? null : propDefs[i]; + JacksonInject.Value injectable = intr.findInjectableValue(param); + final PropertyName name = (propDef == null) ? null : propDef.getFullName(); + + if (propDef != null && propDef.isExplicitlyNamed()) { + ++explicitNameCount; + properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectable); + continue; + } + if (injectable != null) { + ++injectCount; + properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectable); + continue; + } + NameTransformer unwrapper = intr.findUnwrappingNameTransformer(param); + if (unwrapper != null) { + _reportUnwrappedCreatorProperty(ctxt, beanDesc, param); + /* + properties[i] = constructCreatorProperty(ctxt, beanDesc, UNWRAPPED_CREATOR_PARAM_NAME, i, param, null); + ++implicitNameCount; + */ + 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 (explicitNameCount > 0 || injectCount > 0) { + // simple case; everything covered: + if ((namedCount + injectCount) == argCount) { + creators.addPropertyCreator(factory, false, properties); + } else if ((explicitNameCount == 0) && ((injectCount + 1) == argCount)) { + // [712] secondary: all but one injectable, one un-annotated (un-named) + creators.addDelegatingCreator(factory, false, properties, 0); + } else { // otherwise, epic fail + ctxt.reportBadTypeDefinition(beanDesc, +"Argument #%d of factory method %s has no property name annotation; must have name when multiple-parameter constructor annotated as Creator", + nonAnnotatedParam.getIndex(), factory); + } + } + } + } + + /* + /********************************************************************** + /* Creator introspection, explicitly annotated creators + /********************************************************************** + */ + /** * Helper method called when there is the explicit "is-creator" with mode of "delegating" - * - * @since 2.9.2 */ protected void _addExplicitDelegatingCreator(DeserializationContext ctxt, BeanDescription beanDesc, CreatorCollector creators, @@ -659,8 +739,6 @@ protected void _addExplicitDelegatingCreator(DeserializationContext ctxt, /** * Helper method called when there is the explicit "is-creator" with mode of "properties-based" - * - * @since 2.9.2 */ protected void _addExplicitPropertyCreator(DeserializationContext ctxt, BeanDescription beanDesc, CreatorCollector creators, @@ -699,8 +777,6 @@ protected void _addExplicitPropertyCreator(DeserializationContext ctxt, /** * Helper method called when there is the explicit "is-creator", but no mode declaration. - * - * @since 2.9.2 */ protected void _addExplicitAnyCreator(DeserializationContext ctxt, BeanDescription beanDesc, CreatorCollector creators, @@ -755,6 +831,12 @@ protected void _addExplicitAnyCreator(DeserializationContext ctxt, } } + /* + /********************************************************************** + /* Creator introspection, helper methods + /********************************************************************** + */ + private boolean _checkIfCreatorPropertyBased(AnnotationIntrospector intr, AnnotatedWithParams creator, BeanPropertyDefinition propDef) { @@ -778,7 +860,7 @@ private boolean _checkIfCreatorPropertyBased(AnnotationIntrospector intr, } private void _checkImplicitlyNamedConstructors(DeserializationContext ctxt, - BeanDescription beanDesc, VisibilityChecker vchecker, + BeanDescription beanDesc, VisibilityChecker vchecker, AnnotationIntrospector intr, CreatorCollector creators, List implicitCtors) throws JsonMappingException { @@ -791,6 +873,9 @@ private void _checkImplicitlyNamedConstructors(DeserializationContext ctxt, main_loop: for (AnnotatedWithParams ctor : implicitCtors) { + // 21-Sep-2017, tatu: Note that "scalar constructors" are always delegating, + // so use regular creator visibility here. +// if (!_constructorVisible(vchecker, ctor)) { if (!vchecker.isCreatorVisible(ctor)) { continue; } @@ -831,154 +916,6 @@ private void _checkImplicitlyNamedConstructors(DeserializationContext ctxt, } } - protected void _addDeserializerFactoryMethods - (DeserializationContext ctxt, BeanDescription beanDesc, VisibilityChecker vchecker, - AnnotationIntrospector intr, CreatorCollector creators, - Map creatorParams) - throws JsonMappingException - { - List nonAnnotated = new LinkedList<>(); - int explCount = 0; - - // 21-Sep-2017, tatu: First let's handle explicitly annotated ones - for (AnnotatedMethod factory : beanDesc.getFactoryMethods()) { - JsonCreator.Mode creatorMode = intr.findCreatorAnnotation(ctxt.getConfig(), factory); - final int argCount = factory.getParameterCount(); - if (creatorMode == null) { - // Only potentially accept 1-argument factory methods - if ((argCount == 1) && vchecker.isCreatorVisible(factory)) { - nonAnnotated.add(CreatorCandidate.construct(intr, factory, null)); - } - continue; - } - if (creatorMode == Mode.DISABLED) { - continue; - } - - // zero-arg method factory methods fine, as long as explicit - if (argCount == 0) { - creators.setDefaultCreator(factory); - continue; - } - - switch (creatorMode) { - case DELEGATING: - _addExplicitDelegatingCreator(ctxt, beanDesc, creators, - CreatorCandidate.construct(intr, factory, null)); - break; - case PROPERTIES: - _addExplicitPropertyCreator(ctxt, beanDesc, creators, - CreatorCandidate.construct(intr, factory, creatorParams.get(factory))); - break; - case DEFAULT: - default: - _addExplicitAnyCreator(ctxt, beanDesc, creators, - CreatorCandidate.construct(intr, factory, creatorParams.get(factory))); - break; - } - ++explCount; - } - // And only if and when those handled, consider potentially visible ones - if (explCount > 0) { // TODO: split method into two since we could have expl factories - return; - } - // And then implicitly found - for (CreatorCandidate candidate : nonAnnotated) { - final int argCount = candidate.paramCount(); - AnnotatedWithParams factory = candidate.creator(); - final BeanPropertyDefinition[] propDefs = creatorParams.get(factory); - // some single-arg factory methods (String, number) are auto-detected - if (argCount != 1) { - continue; // 2 and more args? Must be explicit, handled earlier - } - BeanPropertyDefinition argDef = candidate.propertyDef(0); - boolean useProps = _checkIfCreatorPropertyBased(intr, factory, argDef); - if (!useProps) { // not property based but delegating - /*boolean added=*/ _handleSingleArgumentCreator(creators, - factory, false, vchecker.isCreatorVisible(factory)); - // 23-Sep-2016, tatu: [databind#1383]: Need to also sever link to avoid possible - // later problems with "unresolved" constructor property - if (argDef != null) { - ((POJOPropertyBuilder) argDef).removeConstructors(); - } - continue; - } - AnnotatedParameter nonAnnotatedParam = null; - SettableBeanProperty[] properties = new SettableBeanProperty[argCount]; - int implicitNameCount = 0; - int explicitNameCount = 0; - int injectCount = 0; - - for (int i = 0; i < argCount; ++i) { - final AnnotatedParameter param = factory.getParameter(i); - BeanPropertyDefinition propDef = (propDefs == null) ? null : propDefs[i]; - JacksonInject.Value injectable = intr.findInjectableValue(param); - final PropertyName name = (propDef == null) ? null : propDef.getFullName(); - - if (propDef != null && propDef.isExplicitlyNamed()) { - ++explicitNameCount; - properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectable); - continue; - } - if (injectable != null) { - ++injectCount; - properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectable); - continue; - } - NameTransformer unwrapper = intr.findUnwrappingNameTransformer(param); - if (unwrapper != null) { - _reportUnwrappedCreatorProperty(ctxt, beanDesc, param); - /* - 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, injectable); - 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 (explicitNameCount > 0 || injectCount > 0) { - // simple case; everything covered: - if ((namedCount + injectCount) == argCount) { - creators.addPropertyCreator(factory, false, properties); - } else if ((explicitNameCount == 0) && ((injectCount + 1) == argCount)) { - // secondary: all but one injectable, one un-annotated (un-named) - creators.addDelegatingCreator(factory, false, properties, 0); - } else { // otherwise, epic fail - ctxt.reportBadTypeDefinition(beanDesc, -"Argument #%d of factory method %s has no property name annotation; must have name when multiple-parameter constructor annotated as Creator", - nonAnnotatedParam.getIndex(), factory); - } - } - } - } - protected boolean _handleSingleArgumentCreator(CreatorCollector creators, AnnotatedWithParams ctor, boolean isCreator, boolean isVisible) { @@ -1044,7 +981,6 @@ protected SettableBeanProperty constructCreatorProperty(DeserializationContext c JacksonInject.Value injectable) throws JsonMappingException { - final DeserializationConfig config = ctxt.getConfig(); final AnnotationIntrospector intr = ctxt.getAnnotationIntrospector(); PropertyMetadata metadata; { @@ -1065,7 +1001,7 @@ protected SettableBeanProperty constructCreatorProperty(DeserializationContext c TypeDeserializer typeDeser = (TypeDeserializer) type.getTypeHandler(); // or if not, based on type being referenced: if (typeDeser == null) { - typeDeser = findTypeDeserializer(config, type); + typeDeser = ctxt.findTypeDeserializer(type); } // Note: contextualization of typeDeser _should_ occur in constructor of CreatorProperty // so it is not called directly here @@ -1106,11 +1042,44 @@ private PropertyName _findParamName(AnnotatedParameter param, AnnotationIntrospe } /* - /********************************************************** + protected PropertyName _findImplicitParamName(AnnotatedParameter param, AnnotationIntrospector intr) + { + String str = intr.findImplicitPropertyName(param); + if (str != null && !str.isEmpty()) { + return PropertyName.construct(str); + } + return null; + } + + protected boolean _checkIfCreatorPropertyBased(AnnotationIntrospector intr, + AnnotatedWithParams creator, BeanPropertyDefinition propDef) + { + // If explicit name, or inject id, property-based + if (((propDef != null) && propDef.isExplicitlyNamed()) + || (intr.findInjectableValue(creator.getParameter(0)) != null)) { + return true; + } + if (propDef != null) { + // One more thing: if implicit name matches property with a getter + // or field, we'll consider it property-based as well + String implName = propDef.getName(); + if (implName != null && !implName.isEmpty()) { + if (propDef.couldSerialize()) { + return true; + } + } + } + // in absence of everything else, default to delegating + return false; + } +*/ + + /* + /********************************************************************** /* JsonDeserializerFactory impl: array deserializers - /********************************************************** + /********************************************************************** */ - + @Override public JsonDeserializer createArrayDeserializer(DeserializationContext ctxt, ArrayType type, final BeanDescription beanDesc) @@ -1125,7 +1094,7 @@ public JsonDeserializer createArrayDeserializer(DeserializationContext ctxt, TypeDeserializer elemTypeDeser = elemType.getTypeHandler(); // but if not, may still be possible to find: if (elemTypeDeser == null) { - elemTypeDeser = findTypeDeserializer(config, elemType); + elemTypeDeser = ctxt.findTypeDeserializer(elemType); } // 23-Nov-2010, tatu: Custom array deserializer? JsonDeserializer deser = _findCustomArrayDeserializer(type, @@ -1152,9 +1121,9 @@ public JsonDeserializer createArrayDeserializer(DeserializationContext ctxt, } /* - /********************************************************** + /********************************************************************** /* JsonDeserializerFactory impl: Collection(-like) deserializers - /********************************************************** + /********************************************************************** */ @Override @@ -1171,7 +1140,7 @@ public JsonDeserializer createCollectionDeserializer(DeserializationContext c TypeDeserializer contentTypeDeser = contentType.getTypeHandler(); // but if not, may still be possible to find: if (contentTypeDeser == null) { - contentTypeDeser = findTypeDeserializer(config, contentType); + contentTypeDeser = ctxt.findTypeDeserializer(contentType); } // 23-Nov-2010, tatu: Custom deserializer? JsonDeserializer deser = _findCustomCollectionDeserializer(type, @@ -1179,8 +1148,14 @@ public JsonDeserializer createCollectionDeserializer(DeserializationContext c if (deser == null) { Class collectionClass = type.getRawClass(); if (contentDeser == null) { // not defined by annotation + // [databind#1853]: Map `Set` to `EnumSet` + if (contentType.isEnumType() && (collectionClass == Set.class)) { + collectionClass = EnumSet.class; + type = (CollectionType) config.getTypeFactory().constructSpecializedType(type, collectionClass); + } // One special type: EnumSet: if (EnumSet.class.isAssignableFrom(collectionClass)) { + // 25-Jan-2018, tatu: shouldn't we pass `contentDeser`? deser = new EnumSetDeserializer(contentType, null); } } @@ -1207,7 +1182,7 @@ public JsonDeserializer createCollectionDeserializer(DeserializationContext c } else { type = implType; // But if so, also need to re-check creators... - beanDesc = config.introspectForCreation(type); + beanDesc = ctxt.introspectForCreation(type); } } if (deser == null) { @@ -1266,7 +1241,7 @@ public JsonDeserializer createCollectionLikeDeserializer(DeserializationConte TypeDeserializer contentTypeDeser = contentType.getTypeHandler(); // but if not, may still be possible to find: if (contentTypeDeser == null) { - contentTypeDeser = findTypeDeserializer(config, contentType); + contentTypeDeser = ctxt.findTypeDeserializer(contentType); } JsonDeserializer deser = _findCustomCollectionLikeDeserializer(type, config, beanDesc, contentTypeDeser, contentDeser); @@ -1282,9 +1257,9 @@ public JsonDeserializer createCollectionLikeDeserializer(DeserializationConte } /* - /********************************************************** + /********************************************************************** /* JsonDeserializerFactory impl: Map(-like) deserializers - /********************************************************** + /********************************************************************** */ @Override @@ -1306,7 +1281,7 @@ public JsonDeserializer createMapDeserializer(DeserializationContext ctxt, TypeDeserializer contentTypeDeser = contentType.getTypeHandler(); // but if not, may still be possible to find: if (contentTypeDeser == null) { - contentTypeDeser = findTypeDeserializer(config, contentType); + contentTypeDeser = ctxt.findTypeDeserializer(contentType); } // 23-Nov-2010, tatu: Custom deserializer? @@ -1316,6 +1291,12 @@ public JsonDeserializer createMapDeserializer(DeserializationContext ctxt, if (deser == null) { // Value handling is identical for all, but EnumMap requires special handling for keys Class mapClass = type.getRawClass(); + // [databind#1853]: Map `Map` to `EnumMap` + if ((mapClass == Map.class) && keyType.isEnumType()) { + mapClass = EnumMap.class; + type = (MapType) config.getTypeFactory().constructSpecializedType(type, mapClass); +// type = (MapType) config.getTypeFactory().constructMapType(mapClass, keyType, contentType); + } if (EnumMap.class.isAssignableFrom(mapClass)) { ValueInstantiator inst; @@ -1353,7 +1334,7 @@ public JsonDeserializer createMapDeserializer(DeserializationContext ctxt, mapClass = fallback; type = (MapType) config.constructSpecializedType(type, mapClass); // But if so, also need to re-check creators... - beanDesc = config.introspectForCreation(type); + beanDesc = ctxt.introspectForCreation(type); } else { // [databind#292]: Actually, may be fine, but only if polymorphic deser enabled if (type.getTypeHandler() == null) { @@ -1417,7 +1398,7 @@ public JsonDeserializer createMapLikeDeserializer(DeserializationContext ctxt TypeDeserializer contentTypeDeser = contentType.getTypeHandler(); // but if not, may still be possible to find: if (contentTypeDeser == null) { - contentTypeDeser = findTypeDeserializer(config, contentType); + contentTypeDeser = ctxt.findTypeDeserializer(contentType); } JsonDeserializer deser = _findCustomMapLikeDeserializer(type, config, beanDesc, keyDes, contentTypeDeser, contentDeser); @@ -1433,11 +1414,11 @@ public JsonDeserializer createMapLikeDeserializer(DeserializationContext ctxt } /* - /********************************************************** + /********************************************************************** /* JsonDeserializerFactory impl: other types - /********************************************************** + /********************************************************************** */ - + /** * Factory method for constructing serializers of {@link Enum} types. */ @@ -1454,7 +1435,7 @@ public JsonDeserializer createEnumDeserializer(DeserializationContext ctxt, if (deser == null) { ValueInstantiator valueInstantiator = _constructDefaultValueInstantiator(ctxt, beanDesc); SettableBeanProperty[] creatorProps = (valueInstantiator == null) ? null - : valueInstantiator.getFromObjectArguments(ctxt.getConfig()); + : valueInstantiator.getFromObjectArguments(ctxt); // May have @JsonCreator for static factory method: for (AnnotatedMethod factory : beanDesc.getFactoryMethods()) { if (_hasCreatorAnnotation(ctxt, factory)) { @@ -1516,27 +1497,36 @@ public JsonDeserializer createReferenceDeserializer(DeserializationContext ct // Then optional type info: if type has been resolved, we may already know type deserializer: TypeDeserializer contentTypeDeser = contentType.getTypeHandler(); if (contentTypeDeser == null) { // or if not, may be able to find: - contentTypeDeser = findTypeDeserializer(config, contentType); + contentTypeDeser = ctxt.findTypeDeserializer(contentType); } JsonDeserializer deser = _findCustomReferenceDeserializer(type, config, beanDesc, contentTypeDeser, contentDeser); if (deser == null) { - // Just one referential type as of JDK 1.7 / Java 7: AtomicReference (Java 8 adds Optional) + // 19-Sep-2017, tatu: Java 8 Optional directly supported in 3.x: + if (type.isTypeOrSubTypeOf(Optional.class)) { + // Not sure this can really work but let's try: + ValueInstantiator inst = type.hasRawClass(Optional.class) ? null + : findValueInstantiator(ctxt, beanDesc); + return new Jdk8OptionalDeserializer(type, inst, contentTypeDeser, contentDeser); + } if (type.isTypeOrSubTypeOf(AtomicReference.class)) { - Class rawType = type.getRawClass(); - ValueInstantiator inst; - if (rawType == AtomicReference.class) { - inst = null; - } else { - /* 23-Oct-2016, tatu: Note that subtypes are probably not supportable - * without either forcing merging (to avoid having to create instance) - * or something else... - */ - inst = findValueInstantiator(ctxt, beanDesc); - } + // 23-Oct-2016, tatu: Note that subtypes are probably not supportable + // without either forcing merging (to avoid having to create instance) + // or something else... + ValueInstantiator inst = type.hasRawClass(AtomicReference.class) ? null + : findValueInstantiator(ctxt, beanDesc); return new AtomicReferenceDeserializer(type, inst, contentTypeDeser, contentDeser); } + if (type.hasRawClass(OptionalInt.class)) { + return new OptionalIntDeserializer(); + } + if (type.hasRawClass(OptionalLong.class)) { + return new OptionalLongDeserializer(); + } + if (type.hasRawClass(OptionalDouble.class)) { + return new OptionalDoubleDeserializer(); + } } if (deser != null) { // and then post-process @@ -1550,56 +1540,13 @@ public JsonDeserializer createReferenceDeserializer(DeserializationContext ct } /* - /********************************************************** + /********************************************************************** /* JsonDeserializerFactory impl (partial): type deserializers - /********************************************************** + /********************************************************************** */ - @Override - public TypeDeserializer findTypeDeserializer(DeserializationConfig config, - JavaType baseType) - throws JsonMappingException - { - BeanDescription bean = config.introspectClassAnnotations(baseType.getRawClass()); - AnnotatedClass ac = bean.getClassInfo(); - AnnotationIntrospector ai = config.getAnnotationIntrospector(); - TypeResolverBuilder b = ai.findTypeResolver(config, ac, baseType); - - // Ok: if there is no explicit type info handler, we may want to - // use a default. If so, config object knows what to use. - Collection subtypes = null; - if (b == null) { - b = config.getDefaultTyper(baseType); - if (b == null) { - return null; - } - } else { - subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByTypeId(config, ac); - } - // May need to figure out default implementation, if none found yet - // (note: check for abstract type is not 100% mandatory, more of an optimization) - if ((b.getDefaultImpl() == null) && baseType.isAbstract()) { - JavaType defaultType = mapAbstractType(config, baseType); - if ((defaultType != null) && !defaultType.hasRawClass(baseType.getRawClass())) { - b = b.defaultImpl(defaultType.getRawClass()); - } - } - // 05-Apt-2018, tatu: Since we get non-mapping exception due to various limitations, - // map to better type here - try { - return b.buildTypeDeserializer(config, baseType, subtypes); - } catch (IllegalArgumentException e0) { - InvalidDefinitionException e = InvalidDefinitionException.from((JsonParser) null, - ClassUtil.exceptionMessage(e0), baseType); - e.initCause(e0); - throw e; - } - } - /** * Overridable method called after checking all other types. - * - * @since 2.2 */ protected JsonDeserializer findOptionalStdDeserializer(DeserializationContext ctxt, JavaType type, BeanDescription beanDesc) @@ -1609,9 +1556,9 @@ protected JsonDeserializer findOptionalStdDeserializer(DeserializationContext } /* - /********************************************************** + /********************************************************************** /* JsonDeserializerFactory impl (partial): key deserializers - /********************************************************** + /********************************************************************** */ @Override @@ -1635,7 +1582,7 @@ public KeyDeserializer createKeyDeserializer(DeserializationContext ctxt, if (type.isEnumType()) { deser = _createEnumKeyDeserializer(ctxt, type); } else { - deser = StdKeyDeserializers.findStringBasedKeyDeserializer(config, type); + deser = StdKeyDeserializers.findStringBasedKeyDeserializer(ctxt, type); } } // and then post-processing @@ -1656,7 +1603,7 @@ private KeyDeserializer _createEnumKeyDeserializer(DeserializationContext ctxt, final DeserializationConfig config = ctxt.getConfig(); Class enumClass = type.getRawClass(); - BeanDescription beanDesc = config.introspect(type); + BeanDescription beanDesc = ctxt.introspect(type); // 24-Sep-2015, bim: a key deserializer is the preferred thing. KeyDeserializer des = findKeyDeserializerFromAnnotation(ctxt, beanDesc.getClassInfo()); if (des != null) { @@ -1701,74 +1648,15 @@ private KeyDeserializer _createEnumKeyDeserializer(DeserializationContext ctxt, } /* - /********************************************************** + /********************************************************************** /* Extended API - /********************************************************** - */ - - /** - * Method called to create a type information deserializer for values of - * given non-container property, if one is needed. - * If not needed (no polymorphic handling configured for property), should return null. - *

    - * Note that this method is only called for non-container bean properties, - * and not for values in container types or root values (or container properties) - * - * @param baseType Declared base type of the value to deserializer (actual - * deserializer type will be this type or its subtype) - * - * @return Type deserializer to use for given base type, if one is needed; null if not. + /********************************************************************** */ - public TypeDeserializer findPropertyTypeDeserializer(DeserializationConfig config, - JavaType baseType, AnnotatedMember annotated) - throws JsonMappingException - { - AnnotationIntrospector ai = config.getAnnotationIntrospector(); - TypeResolverBuilder b = ai.findPropertyTypeResolver(config, annotated, baseType); - // Defaulting: if no annotations on member, check value class - if (b == null) { - return findTypeDeserializer(config, baseType); - } - // but if annotations found, may need to resolve subtypes: - Collection subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByTypeId( - config, annotated, baseType); - return b.buildTypeDeserializer(config, baseType, subtypes); - } - - /** - * Method called to find and create a type information deserializer for values of - * given container (list, array, map) property, if one is needed. - * If not needed (no polymorphic handling configured for property), should return null. - *

    - * Note that this method is only called for container bean properties, - * and not for values in container types or root values (or non-container properties) - * - * @param containerType Type of property; must be a container type - * @param propertyEntity Field or method that contains container property - */ - public TypeDeserializer findPropertyContentTypeDeserializer(DeserializationConfig config, - JavaType containerType, AnnotatedMember propertyEntity) - throws JsonMappingException - { - AnnotationIntrospector ai = config.getAnnotationIntrospector(); - TypeResolverBuilder b = ai.findPropertyContentTypeResolver(config, propertyEntity, containerType); - JavaType contentType = containerType.getContentType(); - // Defaulting: if no annotations on member, check class - if (b == null) { - return findTypeDeserializer(config, contentType); - } - // but if annotations found, may need to resolve subtypes: - Collection subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByTypeId( - config, propertyEntity, contentType); - return b.buildTypeDeserializer(config, contentType, subtypes); - } /** * Helper method called to find one of default serializers for "well-known" * platform types: JDK-provided types, and small number of public Jackson * API types. - * - * @since 2.2 */ public JsonDeserializer findDefaultDeserializer(DeserializationContext ctxt, JavaType type, BeanDescription beanDesc) @@ -1781,7 +1669,7 @@ public JsonDeserializer findDefaultDeserializer(DeserializationContext ctxt, DeserializationConfig config = ctxt.getConfig(); JavaType lt, mt; - if (_factoryConfig.hasAbstractTypeResolvers()) { + if (ctxt.getConfig().hasAbstractTypeResolvers()) { lt = _findRemappedType(config, List.class); mt = _findRemappedType(config, Map.class); } else { @@ -1808,7 +1696,7 @@ public JsonDeserializer findDefaultDeserializer(DeserializationContext ctxt, JavaType vt = type.containedTypeOrUnknown(1); TypeDeserializer vts = (TypeDeserializer) vt.getTypeHandler(); if (vts == null) { - vts = findTypeDeserializer(ctxt.getConfig(), vt); + vts = ctxt.findTypeDeserializer(vt); } JsonDeserializer valueDeser = vt.getValueHandler(); KeyDeserializer keyDes = (KeyDeserializer) kt.getValueHandler(); @@ -1833,18 +1721,20 @@ public JsonDeserializer findDefaultDeserializer(DeserializationContext ctxt, if (deser != null) { return deser; } - return JdkDeserializers.find(rawType, clsName); + return StdJdkDeserializers.find(rawType, clsName); } - protected JavaType _findRemappedType(DeserializationConfig config, Class rawType) throws JsonMappingException { - JavaType type = mapAbstractType(config, config.constructType(rawType)); + private JavaType _findRemappedType(DeserializationConfig config, Class rawType) + throws JsonMappingException + { + JavaType type = config.mapAbstractType(config.constructType(rawType)); return (type == null || type.hasRawClass(rawType)) ? null : type; } /* - /********************************************************** + /********************************************************************** /* Helper methods, finding custom deserializers - /********************************************************** + /********************************************************************** */ protected JsonDeserializer _findCustomTreeNodeDeserializer(Class type, @@ -1980,9 +1870,9 @@ protected JsonDeserializer _findCustomMapLikeDeserializer(MapLikeType type, } /* - /********************************************************** + /********************************************************************** /* Helper methods, value/content/key type introspection - /********************************************************** + /********************************************************************** */ /** @@ -1999,7 +1889,7 @@ protected JsonDeserializer findDeserializerFromAnnotation(Deserializatio { AnnotationIntrospector intr = ctxt.getAnnotationIntrospector(); if (intr != null) { - Object deserDef = intr.findDeserializer(ann); + Object deserDef = intr.findDeserializer(ctxt.getConfig(), ann); if (deserDef != null) { return ctxt.deserializerInstance(ann, deserDef); } @@ -2018,7 +1908,7 @@ protected KeyDeserializer findKeyDeserializerFromAnnotation(DeserializationConte { AnnotationIntrospector intr = ctxt.getAnnotationIntrospector(); if (intr != null) { - Object deserDef = intr.findKeyDeserializer(ann); + Object deserDef = intr.findKeyDeserializer(ctxt.getConfig(), ann); if (deserDef != null) { return ctxt.keyDeserializerInstance(ann, deserDef); } @@ -2026,16 +1916,13 @@ protected KeyDeserializer findKeyDeserializerFromAnnotation(DeserializationConte return null; } - /** - * @since 2.9 - */ protected JsonDeserializer findContentDeserializerFromAnnotation(DeserializationContext ctxt, Annotated ann) throws JsonMappingException { AnnotationIntrospector intr = ctxt.getAnnotationIntrospector(); if (intr != null) { - Object deserDef = intr.findContentDeserializer(ann); + Object deserDef = intr.findContentDeserializer(ctxt.getConfig(), ann); if (deserDef != null) { return ctxt.deserializerInstance(ann, deserDef); } @@ -2048,9 +1935,6 @@ protected JsonDeserializer findContentDeserializerFromAnnotation(Deseria * like type overrides, or handler (serializer, deserializer) overrides, * so that from declared field, property or constructor parameter type * is used as the base and modified based on annotations, if any. - * - * @since 2.8 Combines functionality of modifyTypeByAnnotation - * and resolveType */ protected JavaType resolveMemberAndTypeAnnotations(DeserializationContext ctxt, AnnotatedMember member, JavaType type) @@ -2067,7 +1951,7 @@ protected JavaType resolveMemberAndTypeAnnotations(DeserializationContext ctxt, if (type.isMapLikeType()) { JavaType keyType = type.getKeyType(); if (keyType != null) { - Object kdDef = intr.findKeyDeserializer(member); + Object kdDef = intr.findKeyDeserializer(ctxt.getConfig(), member); KeyDeserializer kd = ctxt.keyDeserializerInstance(member, kdDef); if (kd != null) { type = ((MapLikeType) type).withKeyValueHandler(kd); @@ -2077,19 +1961,18 @@ protected JavaType resolveMemberAndTypeAnnotations(DeserializationContext ctxt, } if (type.hasContentType()) { // that is, is either container- or reference-type - Object cdDef = intr.findContentDeserializer(member); + Object cdDef = intr.findContentDeserializer(ctxt.getConfig(), member); JsonDeserializer cd = ctxt.deserializerInstance(member, cdDef); if (cd != null) { type = type.withContentValueHandler(cd); } - TypeDeserializer contentTypeDeser = findPropertyContentTypeDeserializer( - ctxt.getConfig(), type, (AnnotatedMember) member); + TypeDeserializer contentTypeDeser = ctxt.findPropertyContentTypeDeserializer(type, + (AnnotatedMember) member); if (contentTypeDeser != null) { type = type.withContentTypeHandler(contentTypeDeser); } } - TypeDeserializer valueTypeDeser = findPropertyTypeDeserializer(ctxt.getConfig(), - type, (AnnotatedMember) member); + TypeDeserializer valueTypeDeser = ctxt.findPropertyTypeDeserializer(type, (AnnotatedMember) member); if (valueTypeDeser != null) { type = type.withTypeHandler(valueTypeDeser); } @@ -2119,9 +2002,6 @@ protected EnumResolver constructEnumResolver(Class enumClass, return EnumResolver.constructUnsafe(enumClass, config.getAnnotationIntrospector()); } - /** - * @since 2.9 - */ protected boolean _hasCreatorAnnotation(DeserializationContext ctxt, Annotated ann) { AnnotationIntrospector intr = ctxt.getAnnotationIntrospector(); @@ -2131,52 +2011,4 @@ protected boolean _hasCreatorAnnotation(DeserializationContext ctxt, } return false; } - - /* - /********************************************************** - /* Deprecated helper methods - /********************************************************** - */ - - /** - * Method called to see if given method has annotations that indicate - * a more specific type than what the argument specifies. - * - * @deprecated Since 2.8; call {@link #resolveMemberAndTypeAnnotations} instead - */ - @Deprecated - protected JavaType modifyTypeByAnnotation(DeserializationContext ctxt, - Annotated a, JavaType type) - throws JsonMappingException - { - AnnotationIntrospector intr = ctxt.getAnnotationIntrospector(); - if (intr == null) { - return type; - } - return intr.refineDeserializationType(ctxt.getConfig(), a, type); - } - - /** - * @deprecated since 2.8 call {@link #resolveMemberAndTypeAnnotations} instead. - */ - @Deprecated // since 2.8 - protected JavaType resolveType(DeserializationContext ctxt, - BeanDescription beanDesc, JavaType type, AnnotatedMember member) - throws JsonMappingException - { - return resolveMemberAndTypeAnnotations(ctxt, member, type); - } - - /** - * @deprecated since 2.8 call findJsonValueMethod on {@link BeanDescription} instead - */ - @Deprecated // not used, possibly remove as early as 2.9 - protected AnnotatedMethod _findJsonValueFor(DeserializationConfig config, JavaType enumType) - { - if (enumType == null) { - return null; - } - BeanDescription beanDesc = config.introspect(enumType); - return beanDesc.findJsonValueMethod(); - } } 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 88051e75da..7dbb953497 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java @@ -4,6 +4,7 @@ import java.util.*; import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.sym.FieldNameMatcher; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.deser.impl.*; import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId.Referring; @@ -34,22 +35,24 @@ public class BeanDeserializer /** * Lazily constructed exception used as root cause if reporting problem * with creator method that returns null (which is not allowed) - * - * @since 2.8 */ protected transient Exception _nullFromCreator; + // @since 3.0 + protected FieldNameMatcher _fieldMatcher; + + // @since 3.0 + protected SettableBeanProperty[] _fieldsByIndex; + /** * State marker we need in order to avoid infinite recursion for some cases * (not very clean, alas, but has to do for now) - * - * @since 2.9 */ - private volatile transient NameTransformer _currentlyTransforming; + protected volatile transient NameTransformer _currentlyTransforming; /* /********************************************************** - /* Life-cycle, construction, initialization + /* Life-cycle, constructors /********************************************************** */ @@ -69,32 +72,53 @@ public BeanDeserializer(BeanDeserializerBuilder builder, BeanDescription beanDes * Copy-constructor that can be used by sub-classes to allow * copy-on-write style copying of settings of an existing instance. */ - protected BeanDeserializer(BeanDeserializerBase src) { + protected BeanDeserializer(BeanDeserializer src) { super(src, src._ignoreAllUnknown); + _fieldMatcher = src._fieldMatcher; + _fieldsByIndex = src._fieldsByIndex; } - protected BeanDeserializer(BeanDeserializerBase src, boolean ignoreAllUnknown) { + protected BeanDeserializer(BeanDeserializer src, boolean ignoreAllUnknown) { super(src, ignoreAllUnknown); + _fieldMatcher = src._fieldMatcher; + _fieldsByIndex = src._fieldsByIndex; } - protected BeanDeserializer(BeanDeserializerBase src, NameTransformer unwrapper) { - super(src, unwrapper); + protected BeanDeserializer(BeanDeserializer src, + UnwrappedPropertyHandler unwrapHandler, BeanPropertyMap renamedProperties, + boolean ignoreAllUnknown) { + super(src, unwrapHandler, renamedProperties, ignoreAllUnknown); + _fieldMatcher = _beanProperties.getFieldMatcher(); + _fieldsByIndex = _beanProperties.getFieldMatcherProperties(); } - public BeanDeserializer(BeanDeserializerBase src, ObjectIdReader oir) { + public BeanDeserializer(BeanDeserializer src, ObjectIdReader oir) { super(src, oir); + _fieldMatcher = src._fieldMatcher; + _fieldsByIndex = src._fieldsByIndex; } - public BeanDeserializer(BeanDeserializerBase src, Set ignorableProps) { + public BeanDeserializer(BeanDeserializer src, Set ignorableProps) { super(src, ignorableProps); + _fieldMatcher = src._fieldMatcher; + _fieldsByIndex = src._fieldsByIndex; } - public BeanDeserializer(BeanDeserializerBase src, BeanPropertyMap props) { + public BeanDeserializer(BeanDeserializer src, BeanPropertyMap props) { super(src, props); + _fieldMatcher = _beanProperties.getFieldMatcher(); + _fieldsByIndex = _beanProperties.getFieldMatcherProperties(); } + /* + /********************************************************** + /* Life-cycle, mutant factories + /********************************************************** + */ + @Override - public JsonDeserializer unwrappingDeserializer(NameTransformer transformer) + public JsonDeserializer unwrappingDeserializer(DeserializationContext ctxt, + NameTransformer transformer) { // bit kludgy but we don't want to accidentally change type; sub-classes // MUST override this method to support unwrapped properties... @@ -108,7 +132,13 @@ public JsonDeserializer unwrappingDeserializer(NameTransformer transform } _currentlyTransforming = transformer; try { - return new BeanDeserializer(this, transformer); + UnwrappedPropertyHandler uwHandler = _unwrappedPropertyHandler; + if (uwHandler != null) { // delegate further unwraps, if any + uwHandler = uwHandler.renameAll(ctxt, transformer); + } + // and handle direct unwrapping as well: + return new BeanDeserializer(this, uwHandler, + _beanProperties.renameAll(ctxt, transformer), true); } finally { _currentlyTransforming = null; } } @@ -129,8 +159,20 @@ public BeanDeserializerBase withBeanProperties(BeanPropertyMap props) { @Override protected BeanDeserializerBase asArrayDeserializer() { - SettableBeanProperty[] props = _beanProperties.getPropertiesInInsertionOrder(); - return new BeanAsArrayDeserializer(this, props); + return new BeanAsArrayDeserializer(this, _beanProperties.getPrimaryProperties()); + } + + /* + /********************************************************** + /* Life-cycle, initialization + /********************************************************** + */ + + @Override + protected void initFieldMatcher(DeserializationContext ctxt) { + _beanProperties.initMatcher(ctxt.getParserFactory()); + _fieldMatcher = _beanProperties.getFieldMatcher(); + _fieldsByIndex = _beanProperties.getFieldMatcherProperties(); } /* @@ -148,7 +190,7 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOEx // common case first if (p.isExpectedStartObjectToken()) { if (_vanillaProcessing) { - return vanillaDeserialize(p, ctxt, p.nextToken()); + return _vanillaDeserialize(p, ctxt); } // 23-Sep-2015, tatu: This is wrong at some many levels, but for now... it is // what it is, including "expected behavior". @@ -158,7 +200,7 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOEx } return deserializeFromObject(p, ctxt); } - return _deserializeOther(p, ctxt, p.getCurrentToken()); + return _deserializeOther(p, ctxt, p.currentToken()); } protected final Object _deserializeOther(JsonParser p, DeserializationContext ctxt, @@ -186,7 +228,7 @@ protected final Object _deserializeOther(JsonParser p, DeserializationContext ct case FIELD_NAME: case END_OBJECT: // added to resolve [JACKSON-319], possible related issues if (_vanillaProcessing) { - return vanillaDeserialize(p, ctxt, t); + return _vanillaDeserialize(p, ctxt, t); } if (_objectIdReader != null) { return deserializeWithObjectId(p, ctxt); @@ -198,11 +240,6 @@ protected final Object _deserializeOther(JsonParser p, DeserializationContext ct return ctxt.handleUnexpectedToken(handledType(), p); } - @Deprecated // since 2.8; remove unless getting used - protected Object _missingToken(JsonParser p, DeserializationContext ctxt) throws IOException { - throw ctxt.endOfInputException(handledType()); - } - /** * Secondary deserialization method, called in cases where POJO * instance is created as part of deserialization, potentially @@ -230,12 +267,10 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt, Object bean if (propName == null) { return bean; } + } else if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)) { + propName = p.currentName(); } else { - if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)) { - propName = p.getCurrentName(); - } else { - return bean; - } + return bean; } if (_needViewProcesing) { Class view = ctxt.getActiveView(); @@ -243,20 +278,25 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt, Object bean return deserializeWithView(p, ctxt, bean, view); } } - do { + // May or may not be interned... + int ix = _fieldMatcher.matchName(propName); + while (ix >= 0) { p.nextToken(); - SettableBeanProperty prop = _beanProperties.find(propName); - - if (prop != null) { // normal case - try { - prop.deserializeAndSet(p, ctxt, bean); - } catch (Exception e) { - wrapAndThrow(e, bean, propName, ctxt); - } - continue; + SettableBeanProperty prop = _fieldsByIndex[ix]; + try { + prop.deserializeAndSet(p, ctxt, bean); + } catch (Exception e) { + throw wrapAndThrow(e, bean, prop.getName(), ctxt); } - handleUnknownVanilla(p, ctxt, bean, propName); - } while ((propName = p.nextFieldName()) != null); + ix = p.nextFieldName(_fieldMatcher); + } + if (ix != FieldNameMatcher.MATCH_END_OBJECT) { + if (ix == FieldNameMatcher.MATCH_UNKNOWN_NAME) { + return _vanillaDeserializeWithUnknown(p, ctxt, bean, + p.currentName()); + } + return _handleUnexpectedWithin(p, ctxt, bean); + } return bean; } @@ -266,37 +306,152 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt, Object bean /********************************************************** */ + /** + * Streamlined version that is only used when no "special" + * features are enabled, and when current logical token + * is {@link JsonToken#START_OBJECT} (or equivalent). + */ + private final Object _vanillaDeserialize(JsonParser p, DeserializationContext ctxt) + throws IOException + { + final Object bean = _valueInstantiator.createUsingDefault(ctxt); + // [databind#631]: Assign current value, to be accessible by custom serializers + p.setCurrentValue(bean); + + int ix = p.nextFieldName(_fieldMatcher); + while (ix >= 0) { + p.nextToken(); + SettableBeanProperty prop = _fieldsByIndex[ix]; + try { + prop.deserializeAndSet(p, ctxt, bean); + } catch (Exception e) { + wrapAndThrow(e, bean, prop.getName(), ctxt); + } + // Elem #2 + ix = p.nextFieldName(_fieldMatcher); + if (ix < 0) { + break; + } + p.nextToken(); + prop = _fieldsByIndex[ix]; + try { + prop.deserializeAndSet(p, ctxt, bean); + } catch (Exception e) { + wrapAndThrow(e, bean, prop.getName(), ctxt); + } + // Elem #3 + ix = p.nextFieldName(_fieldMatcher); + if (ix < 0) { + break; + } + p.nextToken(); + prop = _fieldsByIndex[ix]; + try { + prop.deserializeAndSet(p, ctxt, bean); + } catch (Exception e) { + wrapAndThrow(e, bean, prop.getName(), ctxt); + } + // Elem #4 + ix = p.nextFieldName(_fieldMatcher); + if (ix < 0) { + break; + } + p.nextToken(); + prop = _fieldsByIndex[ix]; + try { + prop.deserializeAndSet(p, ctxt, bean); + } catch (Exception e) { + wrapAndThrow(e, bean, prop.getName(), ctxt); + } + ix = p.nextFieldName(_fieldMatcher); + } + if (ix != FieldNameMatcher.MATCH_END_OBJECT) { + if (ix == FieldNameMatcher.MATCH_UNKNOWN_NAME) { + return _vanillaDeserializeWithUnknown(p, ctxt, bean, + p.currentName()); + } + return _handleUnexpectedWithin(p, ctxt, bean); + } + return bean; + } + /** * Streamlined version that is only used when no "special" * features are enabled. */ - private final Object vanillaDeserialize(JsonParser p, + private final Object _vanillaDeserialize(JsonParser p, DeserializationContext ctxt, JsonToken t) throws IOException { final Object bean = _valueInstantiator.createUsingDefault(ctxt); // [databind#631]: Assign current value, to be accessible by custom serializers p.setCurrentValue(bean); - if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)) { - String propName = p.getCurrentName(); - do { - p.nextToken(); - SettableBeanProperty prop = _beanProperties.find(propName); - if (prop != null) { // normal case - try { - prop.deserializeAndSet(p, ctxt, bean); - } catch (Exception e) { - wrapAndThrow(e, bean, propName, ctxt); - } - continue; - } - handleUnknownVanilla(p, ctxt, bean, propName); - } while ((propName = p.nextFieldName()) != null); + if (t != JsonToken.FIELD_NAME) { + return bean; + } + int ix = p.currentFieldName(_fieldMatcher); + while (ix >= 0) { // minor unrolling here (by-2), less likely on critical path + SettableBeanProperty prop = _fieldsByIndex[ix]; + p.nextToken(); + try { + prop.deserializeAndSet(p, ctxt, bean); + } catch (Exception e) { + throw wrapAndThrow(e, bean, p.currentName(), ctxt); + } + + // Elem #2 + ix = p.nextFieldName(_fieldMatcher); + if (ix < 0) { + break; + } + prop = _fieldsByIndex[ix]; + p.nextToken(); + try { + prop.deserializeAndSet(p, ctxt, bean); + } catch (Exception e) { + throw wrapAndThrow(e, bean, p.currentName(), ctxt); + } + ix = p.nextFieldName(_fieldMatcher); + } + if (ix != FieldNameMatcher.MATCH_END_OBJECT) { + if (ix == FieldNameMatcher.MATCH_UNKNOWN_NAME) { + return _vanillaDeserializeWithUnknown(p, ctxt, bean, + p.currentName()); + } + return _handleUnexpectedWithin(p, ctxt, bean); } return bean; } + private final Object _vanillaDeserializeWithUnknown(JsonParser p, + DeserializationContext ctxt, Object bean, String propName) throws IOException + { + p.nextToken(); + handleUnknownVanilla(p, ctxt, bean, propName); + + while (true) { + int ix = p.nextFieldName(_fieldMatcher); + if (ix >= 0) { // normal case + p.nextToken(); + try { + _fieldsByIndex[ix].deserializeAndSet(p, ctxt, bean); + } catch (Exception e) { + wrapAndThrow(e, bean, p.currentName(), ctxt); + } + continue; + } + if (ix == FieldNameMatcher.MATCH_END_OBJECT) { + return bean; + } + if (ix != FieldNameMatcher.MATCH_UNKNOWN_NAME) { + return _handleUnexpectedWithin(p, ctxt, bean); + } + p.nextToken(); + handleUnknownVanilla(p, ctxt, bean, p.currentName()); + } + } + /** * General version used when handling needs more advanced features. */ @@ -312,7 +467,7 @@ public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) t */ if ((_objectIdReader != null) && _objectIdReader.maySerializeAsObject()) { if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME) - && _objectIdReader.isValidReferencePropertyName(p.getCurrentName(), p)) { + && _objectIdReader.isValidReferencePropertyName(p.currentName(), p)) { return deserializeFromObjectId(p, ctxt); } } @@ -327,10 +482,9 @@ public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) t if (_injectables != null) { injectValues(ctxt, bean); } - /* 27-May-2014, tatu: I don't think view processing would work - * at this point, so commenting it out; but leaving in place - * just in case I forgot something fundamental... - */ + // 27-May-2014, tatu: I don't think view processing would work + // at this point, so commenting it out; but leaving in place + // just in case I forgot something fundamental... /* if (_needViewProcesing) { Class view = ctxt.getActiveView(); @@ -353,29 +507,35 @@ public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) t if (_injectables != null) { injectValues(ctxt, bean); } + if (!p.hasTokenId(JsonTokenId.ID_FIELD_NAME)) { + // should we check what exactly it is... ? + return bean; + } if (_needViewProcesing) { Class view = ctxt.getActiveView(); if (view != null) { return deserializeWithView(p, ctxt, bean, view); } } - if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)) { - String propName = p.getCurrentName(); - do { + for (int ix = p.currentFieldName(_fieldMatcher); ; ix = p.nextFieldName(_fieldMatcher)) { + if (ix >= 0) { // normal case p.nextToken(); - SettableBeanProperty prop = _beanProperties.find(propName); - if (prop != null) { // normal case - try { - prop.deserializeAndSet(p, ctxt, bean); - } catch (Exception e) { - wrapAndThrow(e, bean, propName, ctxt); - } - continue; + try { + _fieldsByIndex[ix].deserializeAndSet(p, ctxt, bean); + } catch (Exception e) { + throw wrapAndThrow(e, bean, p.currentName(), ctxt); } - handleUnknownVanilla(p, ctxt, bean, propName); - } while ((propName = p.nextFieldName()) != null); + continue; + } + if (ix == FieldNameMatcher.MATCH_END_OBJECT) { + return bean; + } + if (ix != FieldNameMatcher.MATCH_UNKNOWN_NAME) { + return _handleUnexpectedWithin(p, ctxt, bean); + } + p.nextToken(); + handleUnknownVanilla(p, ctxt, bean, p.currentName()); } - return bean; } /** @@ -396,15 +556,22 @@ protected Object _deserializeUsingPropertyBased(final JsonParser p, final Deseri TokenBuffer unknown = null; final Class activeView = _needViewProcesing ? ctxt.getActiveView() : null; - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); List referrings = null; for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) { - String propName = p.getCurrentName(); + String propName = p.currentName(); p.nextToken(); // to point to value // Object Id property? if (buffer.readIdProperty(propName)) { continue; } + // [databind#1891]: possible fix + /* + if (_ignorableProps != null && _ignorableProps.contains(propName)){ + continue; + } + */ + // creator property? SettableBeanProperty creatorProp = creator.findCreatorProperty(propName); if (creatorProp != null) { @@ -443,8 +610,9 @@ protected Object _deserializeUsingPropertyBased(final JsonParser p, final Deseri continue; } // regular property? needs buffering - SettableBeanProperty prop = _beanProperties.find(propName); - if (prop != null) { + int ix = _fieldMatcher.matchName(propName); + if (ix >= 0) { + SettableBeanProperty prop = _fieldsByIndex[ix]; try { buffer.bufferProperty(prop, _deserializeWithErrorWrapping(p, ctxt, prop)); } catch (UnresolvedForwardReference reference) { @@ -470,13 +638,13 @@ protected Object _deserializeUsingPropertyBased(final JsonParser p, final Deseri try { buffer.bufferAnyProperty(_anySetter, propName, _anySetter.deserialize(p, ctxt)); } catch (Exception e) { - wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt); + throw wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt); } continue; } // Ok then, let's collect the whole field; name and value if (unknown == null) { - unknown = new TokenBuffer(p, ctxt); + unknown = TokenBuffer.forInputBuffering(p, ctxt); } unknown.writeFieldName(propName); unknown.copyCurrentStructure(p); @@ -527,9 +695,7 @@ protected final Object _deserializeWithErrorWrapping(JsonParser p, try { return prop.deserialize(p, ctxt); } catch (Exception e) { - wrapAndThrow(e, _beanType.getRawClass(), prop.getName(), ctxt); - // never gets here, unless caller declines to throw an exception - return null; + throw wrapAndThrow(e, _beanType.getRawClass(), prop.getName(), ctxt); } } @@ -538,25 +704,23 @@ protected final Object _deserializeWithErrorWrapping(JsonParser p, * token. While this is most often an erroneous condition, there is one specific * case with XML handling where polymorphic type with no properties is exposed * as such, and should be handled same as empty Object. - * - * @since 2.7 */ protected Object deserializeFromNull(JsonParser p, DeserializationContext ctxt) throws IOException { // 17-Dec-2015, tatu: Highly specialized case, mainly to support polymorphic // "empty" POJOs deserialized from XML, where empty XML tag synthesizes a - // `VALUE_NULL` token. - if (p.requiresCustomCodec()) { // not only XML module, but mostly it... - @SuppressWarnings("resource") - TokenBuffer tb = new TokenBuffer(p, ctxt); + // `VALUE_NULL` tokens + if (p.canSynthesizeNulls()) { + TokenBuffer tb = TokenBuffer.forGeneration(); tb.writeEndObject(); - JsonParser p2 = tb.asParser(p); + JsonParser p2 = tb.asParser(ctxt, p); p2.nextToken(); // to point to END_OBJECT // note: don't have ObjectId to consider at this point, so: - Object ob = _vanillaProcessing ? vanillaDeserialize(p2, ctxt, JsonToken.END_OBJECT) + Object ob = _vanillaProcessing ? _vanillaDeserialize(p2, ctxt, JsonToken.END_OBJECT) : deserializeFromObject(p2, ctxt); p2.close(); + tb.close(); return ob; } return ctxt.handleUnexpectedToken(handledType(), p); @@ -572,30 +736,33 @@ protected final Object deserializeWithView(JsonParser p, DeserializationContext Object bean, Class activeView) throws IOException { - if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)) { - String propName = p.getCurrentName(); - do { + for (int ix = p.currentFieldName(_fieldMatcher); ; ix = p.nextFieldName(_fieldMatcher)) { + if (ix >= 0) { p.nextToken(); - // TODO: 06-Jan-2015, tatu: try streamlining call sequences here as well - SettableBeanProperty prop = _beanProperties.find(propName); - if (prop != null) { - if (!prop.visibleInView(activeView)) { - p.skipChildren(); - continue; - } - try { - prop.deserializeAndSet(p, ctxt, bean); - } catch (Exception e) { - wrapAndThrow(e, bean, propName, ctxt); - } + SettableBeanProperty prop = _fieldsByIndex[ix]; + if (!prop.visibleInView(activeView)) { + p.skipChildren(); continue; } - handleUnknownVanilla(p, ctxt, bean, propName); - } while ((propName = p.nextFieldName()) != null); + try { + prop.deserializeAndSet(p, ctxt, bean); + } catch (Exception e) { + wrapAndThrow(e, bean, p.currentName(), ctxt); + } + continue; + } + if (ix != FieldNameMatcher.MATCH_END_OBJECT) { + if (ix != FieldNameMatcher.MATCH_UNKNOWN_NAME) { + return _handleUnexpectedWithin(p, ctxt, bean); + } + p.nextToken(); + handleUnknownVanilla(p, ctxt, bean, p.currentName()); + continue; + } + return bean; } - return bean; } - + /* /********************************************************** /* Handling for cases where we have "unwrapped" values @@ -616,7 +783,7 @@ protected Object deserializeWithUnwrapped(JsonParser p, DeserializationContext c if (_propertyBasedCreator != null) { return deserializeUsingPropertyBasedWithUnwrapped(p, ctxt); } - TokenBuffer tokens = new TokenBuffer(p, ctxt); + TokenBuffer tokens = TokenBuffer.forInputBuffering(p, ctxt); tokens.writeStartObject(); final Object bean = _valueInstantiator.createUsingDefault(ctxt); @@ -627,12 +794,11 @@ protected Object deserializeWithUnwrapped(JsonParser p, DeserializationContext c injectValues(ctxt, bean); } final Class activeView = _needViewProcesing ? ctxt.getActiveView() : null; - String propName = p.hasTokenId(JsonTokenId.ID_FIELD_NAME) ? p.getCurrentName() : null; - for (; propName != null; propName = p.nextFieldName()) { - p.nextToken(); - SettableBeanProperty prop = _beanProperties.find(propName); - if (prop != null) { // normal case + for (int ix = p.currentFieldName(_fieldMatcher); ; ix = p.nextFieldName(_fieldMatcher)) { + if (ix >= 0) { // common case + p.nextToken(); + SettableBeanProperty prop = _fieldsByIndex[ix]; if ((activeView != null) && !prop.visibleInView(activeView)) { p.skipChildren(); continue; @@ -640,10 +806,18 @@ protected Object deserializeWithUnwrapped(JsonParser p, DeserializationContext c try { prop.deserializeAndSet(p, ctxt, bean); } catch (Exception e) { - wrapAndThrow(e, bean, propName, ctxt); + throw wrapAndThrow(e, bean, prop.getName(), ctxt); } continue; } + if (ix == FieldNameMatcher.MATCH_END_OBJECT) { + break; + } + if (ix == FieldNameMatcher.MATCH_ODD_TOKEN) { + return _handleUnexpectedWithin(p, ctxt, bean); + } + final String propName = p.currentName(); + p.nextToken(); // Things marked as ignorable should not be passed to any setter if (_ignorableProps != null && _ignorableProps.contains(propName)) { handleIgnoredProperty(p, ctxt, bean, propName); @@ -666,7 +840,7 @@ protected Object deserializeWithUnwrapped(JsonParser p, DeserializationContext c try { _anySetter.deserializeAndSet(b2.asParserOnFirstToken(), ctxt, bean, propName); } catch (Exception e) { - wrapAndThrow(e, bean, propName, ctxt); + throw wrapAndThrow(e, bean, propName, ctxt); } } tokens.writeEndObject(); @@ -679,30 +853,37 @@ protected Object deserializeWithUnwrapped(JsonParser p, DeserializationContext c Object bean) throws IOException { - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == JsonToken.START_OBJECT) { t = p.nextToken(); } - TokenBuffer tokens = new TokenBuffer(p, ctxt); + TokenBuffer tokens = TokenBuffer.forInputBuffering(p, ctxt); tokens.writeStartObject(); final Class activeView = _needViewProcesing ? ctxt.getActiveView() : null; - for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) { - String propName = p.getCurrentName(); - SettableBeanProperty prop = _beanProperties.find(propName); - p.nextToken(); - if (prop != null) { // normal case - if (activeView != null && !prop.visibleInView(activeView)) { + for (int ix = p.currentFieldName(_fieldMatcher); ; ix = p.nextFieldName(_fieldMatcher)) { + if (ix >= 0) { // common case + p.nextToken(); + SettableBeanProperty prop = _fieldsByIndex[ix]; + if ((activeView != null) && !prop.visibleInView(activeView)) { p.skipChildren(); continue; } try { prop.deserializeAndSet(p, ctxt, bean); } catch (Exception e) { - wrapAndThrow(e, bean, propName, ctxt); + throw wrapAndThrow(e, bean, prop.getName(), ctxt); } continue; } - if (_ignorableProps != null && _ignorableProps.contains(propName)) { + if (ix == FieldNameMatcher.MATCH_END_OBJECT) { + break; + } + if (ix == FieldNameMatcher.MATCH_ODD_TOKEN) { + return _handleUnexpectedWithin(p, ctxt, bean); + } + final String propName = p.currentName(); + p.nextToken(); + if ((_ignorableProps != null) && _ignorableProps.contains(propName)) { handleIgnoredProperty(p, ctxt, bean, propName); continue; } @@ -722,9 +903,8 @@ protected Object deserializeWithUnwrapped(JsonParser p, DeserializationContext c try { _anySetter.deserializeAndSet(b2.asParserOnFirstToken(), ctxt, bean, propName); } catch (Exception e) { - wrapAndThrow(e, bean, propName, ctxt); + throw wrapAndThrow(e, bean, propName, ctxt); } - continue; } } tokens.writeEndObject(); @@ -743,12 +923,12 @@ protected Object deserializeUsingPropertyBasedWithUnwrapped(JsonParser p, Deseri final PropertyBasedCreator creator = _propertyBasedCreator; PropertyValueBuffer buffer = creator.startBuilding(p, ctxt, _objectIdReader); - TokenBuffer tokens = new TokenBuffer(p, ctxt); + TokenBuffer tokens = TokenBuffer.forInputBuffering(p, ctxt); tokens.writeStartObject(); - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) { - String propName = p.getCurrentName(); + String propName = p.currentName(); p.nextToken(); // to point to value // creator property? SettableBeanProperty creatorProp = creator.findCreatorProperty(propName); @@ -795,8 +975,9 @@ protected Object deserializeUsingPropertyBasedWithUnwrapped(JsonParser p, Deseri continue; } // regular property? needs buffering - SettableBeanProperty prop = _beanProperties.find(propName); - if (prop != null) { + int ix = _fieldMatcher.matchName(propName); + if (ix >= 0) { + SettableBeanProperty prop = _fieldsByIndex[ix]; buffer.bufferProperty(prop, _deserializeWithErrorWrapping(p, ctxt, prop)); continue; } @@ -822,9 +1003,8 @@ protected Object deserializeUsingPropertyBasedWithUnwrapped(JsonParser p, Deseri buffer.bufferAnyProperty(_anySetter, propName, _anySetter.deserialize(b2.asParserOnFirstToken(), ctxt)); } catch (Exception e) { - wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt); + throw wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt); } - continue; } } @@ -872,14 +1052,13 @@ protected Object deserializeWithExternalTypeId(JsonParser p, DeserializationCont final Class activeView = _needViewProcesing ? ctxt.getActiveView() : null; final ExternalTypeHandler ext = _externalTypeIdHandler.start(); - for (JsonToken t = p.getCurrentToken(); t == JsonToken.FIELD_NAME; t = p.nextToken()) { - String propName = p.getCurrentName(); - t = p.nextToken(); - SettableBeanProperty prop = _beanProperties.find(propName); - if (prop != null) { // normal case + for (int ix = p.currentFieldName(_fieldMatcher); ; ix = p.nextFieldName(_fieldMatcher)) { + if (ix >= 0) { // normal case + SettableBeanProperty prop = _fieldsByIndex[ix]; + JsonToken t = p.nextToken(); // [JACKSON-831]: may have property AND be used as external type id: if (t.isScalarValue()) { - ext.handleTypePropertyValue(p, ctxt, propName, bean); + ext.handleTypePropertyValue(p, ctxt, p.currentName(), bean); } if (activeView != null && !prop.visibleInView(activeView)) { p.skipChildren(); @@ -888,12 +1067,20 @@ protected Object deserializeWithExternalTypeId(JsonParser p, DeserializationCont try { prop.deserializeAndSet(p, ctxt, bean); } catch (Exception e) { - wrapAndThrow(e, bean, propName, ctxt); + throw wrapAndThrow(e, bean, prop.getName(), ctxt); } continue; } + if (ix == FieldNameMatcher.MATCH_END_OBJECT) { + break; + } + if (ix != FieldNameMatcher.MATCH_UNKNOWN_NAME) { + return _handleUnexpectedWithin(p, ctxt, bean); + } // ignorable things should be ignored - if (_ignorableProps != null && _ignorableProps.contains(propName)) { + final String propName = p.currentName(); + p.nextToken(); + if ((_ignorableProps != null) && _ignorableProps.contains(propName)) { handleIgnoredProperty(p, ctxt, bean, propName); continue; } @@ -906,12 +1093,12 @@ protected Object deserializeWithExternalTypeId(JsonParser p, DeserializationCont try { _anySetter.deserializeAndSet(p, ctxt, bean, propName); } catch (Exception e) { - wrapAndThrow(e, bean, propName, ctxt); + throw wrapAndThrow(e, bean, propName, ctxt); } continue; } // Unknown: let's call handler method - handleUnknownProperty(p, ctxt, bean, propName); + handleUnknownProperty(p, ctxt, bean, p.currentName()); } // and when we get this far, let's try finalizing the deal: return ext.complete(p, ctxt, bean); @@ -925,12 +1112,11 @@ protected Object deserializeUsingPropertyBasedWithExternalTypeId(JsonParser p, D final PropertyBasedCreator creator = _propertyBasedCreator; PropertyValueBuffer buffer = creator.startBuilding(p, ctxt, _objectIdReader); - TokenBuffer tokens = new TokenBuffer(p, ctxt); + TokenBuffer tokens = TokenBuffer.forInputBuffering(p, ctxt); tokens.writeStartObject(); - JsonToken t = p.getCurrentToken(); - for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) { - String propName = p.getCurrentName(); + for (JsonToken t = p.currentToken(); t == JsonToken.FIELD_NAME; t = p.nextToken()) { + String propName = p.currentName(); p.nextToken(); // to point to value // creator property? SettableBeanProperty creatorProp = creator.findCreatorProperty(propName); @@ -948,8 +1134,7 @@ protected Object deserializeUsingPropertyBasedWithExternalTypeId(JsonParser p, D try { bean = creator.build(ctxt, buffer); } catch (Exception e) { - wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt); - continue; // never gets here + throw wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt); } // if so, need to copy all remaining tokens into buffer while (t == JsonToken.FIELD_NAME) { @@ -974,8 +1159,9 @@ protected Object deserializeUsingPropertyBasedWithExternalTypeId(JsonParser p, D continue; } // regular property? needs buffering - SettableBeanProperty prop = _beanProperties.find(propName); - if (prop != null) { + int ix = _fieldMatcher.matchName(propName); + if (ix >= 0) { + SettableBeanProperty prop = _fieldsByIndex[ix]; buffer.bufferProperty(prop, prop.deserialize(p, ctxt)); continue; } @@ -1007,8 +1193,6 @@ protected Object deserializeUsingPropertyBasedWithExternalTypeId(JsonParser p, D /** * Helper method for getting a lazily construct exception to be reported * to {@link DeserializationContext#handleInstantiationProblem(Class, Object, Throwable)}. - * - * @since 2.8 */ protected Exception _creatorReturnedNullException() { if (_nullFromCreator == null) { @@ -1018,8 +1202,17 @@ protected Exception _creatorReturnedNullException() { } /** - * @since 2.8 + * Method called if an unexpected token (other then FIELD_NAME) + * is found after POJO has been instantiated and partially bound. + * + * @since 3.0 */ + protected Object _handleUnexpectedWithin(JsonParser p, + DeserializationContext ctxt, Object bean) throws IOException + { + return ctxt.handleUnexpectedToken(handledType(), p); + } + static class BeanReferring extends Referring { private final DeserializationContext _context; 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 6ce41f7835..87f8e809df 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java @@ -12,7 +12,7 @@ import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.deser.impl.*; -import com.fasterxml.jackson.databind.deser.std.StdDelegatingDeserializer; +import com.fasterxml.jackson.databind.deser.std.StdConvertingDeserializer; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import com.fasterxml.jackson.databind.exc.IgnoredPropertyException; import com.fasterxml.jackson.databind.introspect.*; @@ -25,9 +25,8 @@ */ public abstract class BeanDeserializerBase extends StdDeserializer - implements ContextualDeserializer, ResolvableDeserializer, - ValueInstantiator.Gettable, // since 2.9 - java.io.Serializable // since 2.1 + implements ValueInstantiator.Gettable, + java.io.Serializable { private static final long serialVersionUID = 1; @@ -220,8 +219,7 @@ protected BeanDeserializerBase(BeanDeserializerBuilder builder, ; // Any transformation we may need to apply? - JsonFormat.Value format = beanDesc.findExpectedFormat(null); - _serializationShape = (format == null) ? null : format.getShape(); + _serializationShape = beanDesc.findExpectedFormat(_beanType.getRawClass()).getShape(); _needViewProcesing = hasViews; _vanillaProcessing = !_nonStandardCreation @@ -262,7 +260,13 @@ protected BeanDeserializerBase(BeanDeserializerBase src, boolean ignoreAllUnknow _vanillaProcessing = src._vanillaProcessing; } - protected BeanDeserializerBase(BeanDeserializerBase src, NameTransformer unwrapper) + /** + * Constructor used in cases where unwrapping-with-name-change has been + * invoked and lookup indices need to be updated. + */ + protected BeanDeserializerBase(BeanDeserializerBase src, + UnwrappedPropertyHandler unwrapHandler, BeanPropertyMap renamedProperties, + boolean ignoreAllUnknown) { super(src._beanType); @@ -274,25 +278,15 @@ protected BeanDeserializerBase(BeanDeserializerBase src, NameTransformer unwrapp _backRefs = src._backRefs; _ignorableProps = src._ignorableProps; - _ignoreAllUnknown = (unwrapper != null) || src._ignoreAllUnknown; + _ignoreAllUnknown = ignoreAllUnknown; _anySetter = src._anySetter; _injectables = src._injectables; _objectIdReader = src._objectIdReader; _nonStandardCreation = src._nonStandardCreation; - UnwrappedPropertyHandler uph = src._unwrappedPropertyHandler; - if (unwrapper != null) { - // delegate further unwraps, if any - if (uph != null) { // got handler, delegate - uph = uph.renameAll(unwrapper); - } - // and handle direct unwrapping as well: - _beanProperties = src._beanProperties.renameAll(unwrapper); - } else { - _beanProperties = src._beanProperties; - } - _unwrappedPropertyHandler = uph; + _unwrappedPropertyHandler = unwrapHandler; + _beanProperties = renamedProperties; _needViewProcesing = src._needViewProcesing; _serializationShape = src._serializationShape; @@ -300,7 +294,7 @@ protected BeanDeserializerBase(BeanDeserializerBase src, NameTransformer unwrapp _vanillaProcessing = false; } - public BeanDeserializerBase(BeanDeserializerBase src, ObjectIdReader oir) + protected BeanDeserializerBase(BeanDeserializerBase src, ObjectIdReader oir) { super(src._beanType); _beanType = src._beanType; @@ -327,10 +321,9 @@ public BeanDeserializerBase(BeanDeserializerBase src, ObjectIdReader oir) _beanProperties = src._beanProperties; _vanillaProcessing = src._vanillaProcessing; } else { - /* 18-Nov-2012, tatu: May or may not have annotations for id property; - * but no easy access. But hard to see id property being optional, - * so let's consider required at this point. - */ + // 18-Nov-2012, tatu: May or may not have annotations for id property; + // but no easy access. But hard to see id property being optional, + // so let's consider required at this point. ObjectIdValueProperty idProp = new ObjectIdValueProperty(oir, PropertyMetadata.STD_REQUIRED); _beanProperties = src._beanProperties.withProperty(idProp); _vanillaProcessing = false; @@ -365,9 +358,6 @@ public BeanDeserializerBase(BeanDeserializerBase src, Set ignorableProps _beanProperties = src._beanProperties.withoutProperties(ignorableProps); } - /** - * @since 2.8 - */ protected BeanDeserializerBase(BeanDeserializerBase src, BeanPropertyMap beanProps) { super(src._beanType); @@ -394,7 +384,8 @@ protected BeanDeserializerBase(BeanDeserializerBase src, BeanPropertyMap beanPro } @Override - public abstract JsonDeserializer unwrappingDeserializer(NameTransformer unwrapper); + public abstract JsonDeserializer unwrappingDeserializer(DeserializationContext ctxt, + NameTransformer unwrapper); public abstract BeanDeserializerBase withObjectIdReader(ObjectIdReader oir); @@ -403,8 +394,6 @@ protected BeanDeserializerBase(BeanDeserializerBase src, BeanPropertyMap beanPro /** * Mutant factory method that custom sub-classes must override; not left as * abstract to prevent more drastic backwards compatibility problems. - * - * @since 2.8 */ public BeanDeserializerBase withBeanProperties(BeanPropertyMap props) { throw new UnsupportedOperationException("Class "+getClass().getName() @@ -415,8 +404,6 @@ public BeanDeserializerBase withBeanProperties(BeanPropertyMap props) { * Fluent factory for creating a variant that can handle * POJO output as a JSON Array. Implementations may ignore this request * if no such input is possible. - * - * @since 2.1 */ protected abstract BeanDeserializerBase asArrayDeserializer(); @@ -439,7 +426,7 @@ public void resolve(DeserializationContext ctxt) throws JsonMappingException SettableBeanProperty[] creatorProps; if (_valueInstantiator.canCreateFromObjectWith()) { - creatorProps = _valueInstantiator.getFromObjectArguments(ctxt.getConfig()); + creatorProps = _valueInstantiator.getFromObjectArguments(ctxt); // 22-Jan-2018, tatu: May need to propagate "ignorable" status (from `Access.READ_ONLY` // or perhaps class-ignorables) into Creator properties too. Can not just delete, @@ -468,12 +455,14 @@ public void resolve(DeserializationContext ctxt) throws JsonMappingException for (SettableBeanProperty prop : _beanProperties) { if (!prop.hasValueDeserializer()) { // [databind#125]: allow use of converters - JsonDeserializer deser = findConvertingDeserializer(ctxt, prop); + JsonDeserializer deser = _findConvertingDeserializer(ctxt, prop); if (deser == null) { deser = ctxt.findNonContextualValueDeserializer(prop.getType()); } SettableBeanProperty newProp = prop.withValueDeserializer(deser); - _replaceProperty(_beanProperties, creatorProps, prop, newProp); + if (prop != newProp) { + _replaceProperty(_beanProperties, creatorProps, prop, newProp); + } } } @@ -494,8 +483,9 @@ public void resolve(DeserializationContext ctxt) throws JsonMappingException NameTransformer xform = _findPropertyUnwrapper(ctxt, prop); if (xform != null) { JsonDeserializer orig = prop.getValueDeserializer(); - JsonDeserializer unwrapping = orig.unwrappingDeserializer(xform); - if (unwrapping != orig && unwrapping != null) { + JsonDeserializer unwrapping = orig.unwrappingDeserializer(ctxt, xform); + + if ((unwrapping != orig) && (unwrapping != null)) { prop = prop.withValueDeserializer(unwrapping); if (unwrapped == null) { unwrapped = new UnwrappedPropertyHandler(); @@ -504,7 +494,7 @@ public void resolve(DeserializationContext ctxt) throws JsonMappingException // 12-Dec-2014, tatu: As per [databind#647], we will have problems if // the original property is left in place. So let's remove it now. // 25-Mar-2017, tatu: Wonder if this could be problematic wrt creators? - // (that is, should be remove it from creator too) + // (that is, should we remove it from creator too) _beanProperties.remove(prop); continue; } @@ -587,9 +577,6 @@ public void resolve(DeserializationContext ctxt) throws JsonMappingException _vanillaProcessing = _vanillaProcessing && !_nonStandardCreation; } - /** - * @since 2.8.8 - */ protected void _replaceProperty(BeanPropertyMap props, SettableBeanProperty[] creatorProps, SettableBeanProperty origProp, SettableBeanProperty newProp) { @@ -619,7 +606,8 @@ protected void _replaceProperty(BeanPropertyMap props, SettableBeanProperty[] cr @SuppressWarnings("unchecked") private JsonDeserializer _findDelegateDeserializer(DeserializationContext ctxt, - JavaType delegateType, AnnotatedWithParams delegateCreator) throws JsonMappingException + JavaType delegateType, AnnotatedWithParams delegateCreator) + throws JsonMappingException { // Need to create a temporary property to allow contextual deserializers: BeanProperty.Std property = new BeanProperty.Std(TEMP_PROPERTY_NAME, @@ -627,7 +615,7 @@ private JsonDeserializer _findDelegateDeserializer(DeserializationContex PropertyMetadata.STD_OPTIONAL); TypeDeserializer td = delegateType.getTypeHandler(); if (td == null) { - td = ctxt.getConfig().findTypeDeserializer(delegateType); + td = ctxt.findTypeDeserializer(delegateType); } // 04-May-2018, tatu: [databind#2021] check if there's custom deserializer attached // to type (resolved from parameter) @@ -651,23 +639,21 @@ private JsonDeserializer _findDelegateDeserializer(DeserializationContex *

    * NOTE: returned deserializer is NOT yet contextualized, caller needs to take * care to do that. - * - * @since 2.2 */ - protected JsonDeserializer findConvertingDeserializer(DeserializationContext ctxt, + protected JsonDeserializer _findConvertingDeserializer(DeserializationContext ctxt, SettableBeanProperty prop) throws JsonMappingException { final AnnotationIntrospector intr = ctxt.getAnnotationIntrospector(); if (intr != null) { - Object convDef = intr.findDeserializationConverter(prop.getMember()); + Object convDef = intr.findDeserializationConverter(ctxt.getConfig(), prop.getMember()); if (convDef != null) { Converter conv = ctxt.converterInstance(prop.getMember(), convDef); JavaType delegateType = conv.getInputType(ctxt.getTypeFactory()); // 25-Mar-2017, tatu: should not yet contextualize // JsonDeserializer deser = ctxt.findContextualValueDeserializer(delegateType, prop); JsonDeserializer deser = ctxt.findNonContextualValueDeserializer(delegateType); - return new StdDelegatingDeserializer(conv, delegateType, deser); + return new StdConvertingDeserializer(conv, delegateType, deser); } } return null; @@ -690,10 +676,10 @@ public JsonDeserializer createContextual(DeserializationContext ctxt, final AnnotationIntrospector intr = ctxt.getAnnotationIntrospector(); final AnnotatedMember accessor = _neitherNull(property, intr) ? property.getMember() : null; if (accessor != null) { - ObjectIdInfo objectIdInfo = intr.findObjectIdInfo(accessor); + ObjectIdInfo objectIdInfo = intr.findObjectIdInfo(ctxt.getConfig(), accessor); if (objectIdInfo != null) { // some code duplication here as well (from BeanDeserializerFactory) // 2.1: allow modifications by "id ref" annotations as well: - objectIdInfo = intr.findObjectReferenceInfo(accessor, objectIdInfo); + objectIdInfo = intr.findObjectReferenceInfo(ctxt.getConfig(), accessor, objectIdInfo); Class implClass = objectIdInfo.getGeneratorType(); // Property-based generator is trickier @@ -760,7 +746,7 @@ public JsonDeserializer createContextual(DeserializationContext ctxt, } } } - + contextual.initFieldMatcher(ctxt); if (shape == null) { shape = _serializationShape; } @@ -770,6 +756,9 @@ public JsonDeserializer createContextual(DeserializationContext ctxt, return contextual; } + // @since 3.0 + protected abstract void initFieldMatcher(DeserializationContext ctxt); + /** * Helper method called to see if given property is part of 'managed' property * pair (managed + back reference), and if so, handle resolution details. @@ -811,7 +800,7 @@ protected SettableBeanProperty _resolvedObjectIdProperty(DeserializationContext { ObjectIdInfo objectIdInfo = prop.getObjectIdInfo(); JsonDeserializer valueDeser = prop.getValueDeserializer(); - ObjectIdReader objectIdReader = (valueDeser == null) ? null : valueDeser.getObjectIdReader(); + ObjectIdReader objectIdReader = (valueDeser == null) ? null : valueDeser.getObjectIdReader(ctxt); if (objectIdInfo == null && objectIdReader == null) { return prop; } @@ -977,12 +966,12 @@ public Class handledType() { * (either via value type or referring property). */ @Override - public ObjectIdReader getObjectIdReader() { + public ObjectIdReader getObjectIdReader(DeserializationContext ctxt) { return _objectIdReader; } public boolean hasProperty(String propertyName) { - return _beanProperties.find(propertyName) != null; + return _beanProperties.findDefinition(propertyName) != null; } public boolean hasViews() { @@ -1002,15 +991,17 @@ public Collection getKnownPropertyNames() { for (SettableBeanProperty prop : _beanProperties) { names.add(prop.getName()); } + // 22-Nov-2017, tatu: Won't quite work yet... + /* + if (_unwrappedPropertyHandler != null) { + for (SettableBeanProperty prop : _unwrappedPropertyHandler.getHandledProperties()) { + names.add(prop.getName()); + } + } + */ return names; } - /** - * @deprecated Since 2.3, use {@link #handledType()} instead - */ - @Deprecated - public final Class getBeanClass() { return _beanType.getRawClass(); } - @Override public JavaType getValueType() { return _beanType; } @@ -1033,8 +1024,6 @@ public Iterator properties() * Accessor for finding properties that represents values to pass * through property-based creator method (constructor or * factory method) - * - * @since 2.0 */ public Iterator creatorProperties() { @@ -1054,13 +1043,11 @@ public SettableBeanProperty findProperty(PropertyName propertyName) * Accessor for finding the property with given name, if POJO * has one. Name used is the external name, i.e. name used * in external data representation (JSON). - * - * @since 2.0 */ - public SettableBeanProperty findProperty(String propertyName) + protected SettableBeanProperty findProperty(String propertyName) { SettableBeanProperty prop = (_beanProperties == null) ? - null : _beanProperties.find(propertyName); + null : _beanProperties.findDefinition(propertyName); if (prop == null && _propertyBasedCreator != null) { prop = _propertyBasedCreator.findCreatorProperty(propertyName); } @@ -1074,19 +1061,17 @@ public SettableBeanProperty findProperty(String propertyName) * since properties are not directly indexable; however, for most * instances difference is not significant as number of properties * is low. - * - * @since 2.3 */ public SettableBeanProperty findProperty(int propertyIndex) { SettableBeanProperty prop = (_beanProperties == null) ? - null : _beanProperties.find(propertyIndex); + null : _beanProperties.findDefinition(propertyIndex); if (prop == null && _propertyBasedCreator != null) { prop = _propertyBasedCreator.findCreatorProperty(propertyIndex); } return prop; } - + /** * Method needed by {@link BeanDeserializerFactory} to properly link * managed- and back-reference pairs. @@ -1105,30 +1090,6 @@ public ValueInstantiator getValueInstantiator() { return _valueInstantiator; } - /* - /********************************************************** - /* Mutators - /********************************************************** - */ - - /** - * Method that can be used to replace an existing property with - * a modified one. - *

    - * NOTE: only ever use this method if you know what you are doing; - * incorrect usage can break deserializer. - * - * @param original Property to replace - * @param replacement Property to replace it with - * - * @since 2.1 - */ - public void replaceProperty(SettableBeanProperty original, - SettableBeanProperty replacement) - { - _beanProperties.replace(original, replacement); - } - /* /********************************************************** /* Partial deserializer implementation @@ -1158,7 +1119,7 @@ public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, } } // or, Object Ids Jackson explicitly sets - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t != null) { // Most commonly, a scalar (int id, uuid String, ...) if (t.isScalarValue()) { @@ -1169,7 +1130,7 @@ public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, t = p.nextToken(); } if ((t == JsonToken.FIELD_NAME) && _objectIdReader.maySerializeAsObject() - && _objectIdReader.isValidReferencePropertyName(p.getCurrentName(), p)) { + && _objectIdReader.isValidReferencePropertyName(p.currentName(), p)) { return deserializeFromObjectId(p, ctxt); } } @@ -1179,10 +1140,8 @@ public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, } /** - * Offlined method called to handle "native" Object Id that has been read + * Off-lined method called to handle "native" Object Id that has been read * and known to be associated with given deserialized POJO. - * - * @since 2.3 */ protected Object _handleTypedObjectId(JsonParser p, DeserializationContext ctxt, Object pojo, Object rawId) @@ -1217,14 +1176,12 @@ protected Object _handleTypedObjectId(JsonParser p, DeserializationContext ctxt, * simple cast (for String ids), or something more complicated; in latter * case we may need to create bogus content buffer to allow use of * id deserializer. - * - * @since 2.3 */ @SuppressWarnings("resource") // TokenBuffers don't need close, nor parser thereof protected Object _convertObjectId(JsonParser p, DeserializationContext ctxt, Object rawId, JsonDeserializer idDeser) throws IOException { - TokenBuffer buf = new TokenBuffer(p, ctxt); + TokenBuffer buf = TokenBuffer.forInputBuffering(p, ctxt); if (rawId instanceof String) { buf.writeString((String) rawId); } else if (rawId instanceof Long) { @@ -1239,8 +1196,7 @@ protected Object _convertObjectId(JsonParser p, DeserializationContext ctxt, // but that won't work for default impl (JSON and most dataformats) buf.writeObject(rawId); } - JsonParser bufParser = buf.asParser(); - bufParser.nextToken(); + JsonParser bufParser = buf.asParserOnFirstToken(); return idDeser.deserialize(bufParser, ctxt); } @@ -1422,7 +1378,7 @@ public Object deserializeFromBoolean(JsonParser p, DeserializationContext ctxt) return bean; } } - boolean value = (p.getCurrentToken() == JsonToken.VALUE_TRUE); + boolean value = p.hasToken(JsonToken.VALUE_TRUE); return _valueInstantiator.createFromBoolean(ctxt, value); } @@ -1496,9 +1452,6 @@ public Object deserializeFromEmbedded(JsonParser p, DeserializationContext ctxt) return value; } - /** - * @since 2.9 - */ private final JsonDeserializer _delegateDeserializer() { JsonDeserializer deser = _delegateDeserializer; if (deser == null) { @@ -1537,7 +1490,7 @@ protected Object handleUnknownProperties(DeserializationContext ctxt, // note: buffer does NOT have starting START_OBJECT JsonParser bufferParser = unknownTokens.asParser(); while (bufferParser.nextToken() != JsonToken.END_OBJECT) { - String propName = bufferParser.getCurrentName(); + String propName = bufferParser.currentName(); // Unknown: let's call handler method bufferParser.nextToken(); handleUnknownProperty(bufferParser, ctxt, bean, propName); @@ -1553,14 +1506,14 @@ protected void handleUnknownVanilla(JsonParser p, DeserializationContext ctxt, Object bean, String propName) throws IOException { - if (_ignorableProps != null && _ignorableProps.contains(propName)) { + if ((_ignorableProps != null) && _ignorableProps.contains(propName)) { handleIgnoredProperty(p, ctxt, bean, propName); } else if (_anySetter != null) { try { // should we consider return type of any setter? _anySetter.deserializeAndSet(p, ctxt, bean, propName); } catch (Exception e) { - wrapAndThrow(e, bean, propName, ctxt); + throw wrapAndThrow(e, bean, propName, ctxt); } } else { // Unknown: let's call handler method @@ -1592,8 +1545,6 @@ protected void handleUnknownProperty(JsonParser p, DeserializationContext ctxt, /** * Method called when an explicitly ignored property (one specified with a * name to match, either by property annotation or class annotation) is encountered. - * - * @since 2.3 */ protected void handleIgnoredProperty(JsonParser p, DeserializationContext ctxt, Object beanOrClass, String propName) @@ -1703,8 +1654,14 @@ protected JsonDeserializer _findSubclassDeserializer(DeserializationCont *
  • "Plain" IOExceptions (ones that are not of type * {@link JsonMappingException} are to be passed as is * - */ - public void wrapAndThrow(Throwable t, Object bean, String fieldName, DeserializationContext ctxt) + * The method always throws but declares its return type as + * {@link IOException} in order to allow callers to invoke method as + * {@code throw wrapAndThrow(...);} thereby ensuring complete code + * coverage is possible. This also ensures that all call paths within + * this method throw an exception; otherwise they would be required + * to return. + */ + public IOException wrapAndThrow(Throwable t, Object bean, String fieldName, DeserializationContext ctxt) throws IOException { // Need to add reference information 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 c105307ebf..743ef9f02e 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBuilder.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBuilder.java @@ -26,9 +26,6 @@ public class BeanDeserializerBuilder final protected DeserializationConfig _config; - /** - * @since 2.9 - */ final protected DeserializationContext _context; /* @@ -348,10 +345,16 @@ public JsonDeserializer build() { Collection props = _properties.values(); _fixAccess(props); + if (_objectIdReader != null) { + // 18-Nov-2012, tatu: May or may not have annotations for id property; + // but no easy access. But hard to see id property being optional, + // so let's consider required at this point. + props = _addIdProp(_properties, + new ObjectIdValueProperty(_objectIdReader, PropertyMetadata.STD_REQUIRED)); + } BeanPropertyMap propertyMap = BeanPropertyMap.construct(props, _config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES), _collectAliases(props)); - propertyMap.assignIndexes(); // view processing must be enabled if: // (a) fields are not included by default (when deserializing with view), OR @@ -366,16 +369,6 @@ public JsonDeserializer build() } } - // one more thing: may need to create virtual ObjectId property: - if (_objectIdReader != null) { - /* 18-Nov-2012, tatu: May or may not have annotations for id property; - * but no easy access. But hard to see id property being optional, - * so let's consider required at this point. - */ - ObjectIdValueProperty prop = new ObjectIdValueProperty(_objectIdReader, PropertyMetadata.STD_REQUIRED); - propertyMap = propertyMap.withProperty(prop); - } - return new BeanDeserializer(this, _beanDesc, propertyMap, _backRefProperties, _ignorableProps, _ignoreAllUnknown, anyViews); @@ -385,8 +378,6 @@ public JsonDeserializer build() * Alternate build method used when we must be using some form of * abstract resolution, usually by using addition Type Id * ("polymorphic deserialization") - * - * @since 2.0 */ public AbstractDeserializer buildAbstract() { return new AbstractDeserializer(this, _beanDesc, _backRefProperties, _properties); @@ -422,13 +413,20 @@ public JsonDeserializer buildBuilderBased(JavaType valueType, String expBuild valueType.getRawClass().getName())); } } + _fixAccess(_properties.values()); // And if so, we can try building the deserializer - Collection props = _properties.values(); - _fixAccess(props); + Collection props; + if (_objectIdReader != null) { + // May or may not have annotations for id property; but no easy access. + // But hard to see id property being optional, so let's consider required at this point. + props = _addIdProp(_properties, + new ObjectIdValueProperty(_objectIdReader, PropertyMetadata.STD_REQUIRED)); + } else { + props = _properties.values(); + } BeanPropertyMap propertyMap = BeanPropertyMap.construct(props, _config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES), _collectAliases(props)); - propertyMap.assignIndexes(); boolean anyViews = !_config.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION); @@ -441,14 +439,6 @@ public JsonDeserializer buildBuilderBased(JavaType valueType, String expBuild } } - if (_objectIdReader != null) { - // May or may not have annotations for id property; but no easy access. - // But hard to see id property being optional, so let's consider required at this point. - ObjectIdValueProperty prop = new ObjectIdValueProperty(_objectIdReader, - PropertyMetadata.STD_REQUIRED); - propertyMap = propertyMap.withProperty(prop); - } - return new BuilderBasedDeserializer(this, _beanDesc, valueType, propertyMap, _backRefProperties, _ignorableProps, _ignoreAllUnknown, anyViews); @@ -500,25 +490,48 @@ protected void _fixAccess(Collection mainProps) } } - protected Map> _collectAliases(Collection props) + protected Collection _addIdProp(Map props, + SettableBeanProperty idProp) { - Map> mapping = null; + String name = idProp.getName(); + ArrayList result = new ArrayList<>(props.values()); + if (!props.containsKey(name)) { + result.add(idProp); + } else { + // Otherwise need to replace; couple of ways to go about it + ListIterator it = result.listIterator(); + while (true) { // no need to check, we must bump into it + if (it.next().getName().equals(name)) { + it.set(idProp); + break; + } + } + } + return result; + } + + protected PropertyName[][] _collectAliases(Collection props) + { + PropertyName[][] result = null; AnnotationIntrospector intr = _config.getAnnotationIntrospector(); if (intr != null) { + int i = -1; for (SettableBeanProperty prop : props) { - List aliases = intr.findPropertyAliases(prop.getMember()); + ++i; + AnnotatedMember member = prop.getMember(); + if (member == null) { + continue; + } + List aliases = intr.findPropertyAliases(member); if ((aliases == null) || aliases.isEmpty()) { continue; } - if (mapping == null) { - mapping = new HashMap<>(); + if (result == null) { + result = new PropertyName[props.size()][]; } - mapping.put(prop.getName(), aliases); + result[i] = aliases.toArray(new PropertyName[aliases.size()]); } } - if (mapping == null) { - return Collections.emptyMap(); - } - return mapping; + return result; } } 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 1f02d709f8..bb0bee774f 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java @@ -82,9 +82,8 @@ public DeserializerFactory withConfig(DeserializerFactoryConfig config) */ /** - * Method that {@link DeserializerCache}s call to create a new - * deserializer for types other than Collections, Maps, arrays and - * enums. + * Method that called to create a new deserializer for types other than Collections, + * Maps, arrays, referential types or enums, or "well-known" JDK scalar types. */ @Override public JsonDeserializer createBeanDeserializer(DeserializationContext ctxt, @@ -113,10 +112,9 @@ public JsonDeserializer createBeanDeserializer(DeserializationContext ct // Let's make it possible to materialize abstract types. JavaType concreteType = materializeAbstractType(ctxt, type, beanDesc); if (concreteType != null) { - /* important: introspect actual implementation (abstract class or - * interface doesn't have constructors, for one) - */ - beanDesc = config.introspect(concreteType); + // important: introspect actual implementation (abstract class or + // interface doesn't have constructors, for one) + beanDesc = ctxt.introspect(concreteType); return buildBeanDeserializer(ctxt, concreteType, beanDesc); } } @@ -138,13 +136,19 @@ public JsonDeserializer createBeanDeserializer(DeserializationContext ct } @Override - public JsonDeserializer createBuilderBasedDeserializer(DeserializationContext ctxt, - JavaType valueType, BeanDescription beanDesc, Class builderClass) - throws JsonMappingException + public JsonDeserializer createBuilderBasedDeserializer( + DeserializationContext ctxt, JavaType valueType, BeanDescription beanDesc, + Class builderClass) + throws JsonMappingException { // First: need a BeanDescription for builder class - JavaType builderType = ctxt.constructType(builderClass); - BeanDescription builderDesc = ctxt.getConfig().introspectForBuilder(builderType); + JavaType builderType; + if (ctxt.isEnabled(MapperFeature.INFER_BUILDER_TYPE_BINDINGS)) { + builderType = ctxt.getTypeFactory().constructParametricType(builderClass, valueType.getBindings()); + } else { + builderType = ctxt.constructType(builderClass); + } + BeanDescription builderDesc = ctxt.introspectForBuilder(builderType); return buildBuilderBasedDeserializer(ctxt, valueType, builderDesc); } @@ -169,14 +173,15 @@ protected JsonDeserializer findStdDeserializer(DeserializationContext ctxt, } return deser; } - + protected JavaType materializeAbstractType(DeserializationContext ctxt, JavaType type, BeanDescription beanDesc) throws JsonMappingException { + final DeserializationConfig config = ctxt.getConfig(); // May have multiple resolvers, call in precedence order until one returns non-null - for (AbstractTypeResolver r : _factoryConfig.abstractTypeResolvers()) { - JavaType concrete = r.resolveAbstractType(ctxt.getConfig(), beanDesc); + for (AbstractTypeResolver r : config.abstractTypeResolvers()) { + JavaType concrete = r.resolveAbstractType(config, beanDesc); if (concrete != null) { return concrete; } @@ -381,8 +386,8 @@ public JsonDeserializer buildThrowableDeserializer(DeserializationContex SettableBeanProperty prop = constructSettableProperty(ctxt, beanDesc, propDef, am.getParameterType(0)); if (prop != null) { - // 21-Aug-2011, tatus: We may actually have found 'cause' property - // to set... but let's replace it just in case, otherwise can end up with odd errors. + // 21-Aug-2011, tatus: We may actually have found 'cause' property to set... + // but let's replace it just in case, otherwise can end up with odd errors. builder.addOrReplaceProperty(prop, true); } } @@ -450,7 +455,7 @@ protected void addBeanProps(DeserializationContext ctxt, { final boolean isConcrete = !beanDesc.getType().isAbstract(); final SettableBeanProperty[] creatorProps = isConcrete - ? builder.getValueInstantiator().getFromObjectArguments(ctxt.getConfig()) + ? builder.getValueInstantiator().getFromObjectArguments(ctxt) : null; final boolean hasCreatorProps = (creatorProps != null); @@ -490,8 +495,10 @@ protected void addBeanProps(DeserializationContext ctxt, } } } - final boolean useGettersAsSetters = ctxt.isEnabled(MapperFeature.USE_GETTERS_AS_SETTERS) - && ctxt.isEnabled(MapperFeature.AUTO_DETECT_GETTERS); + final boolean useGettersAsSetters = ctxt.isEnabled(MapperFeature.USE_GETTERS_AS_SETTERS); + // 24-Sep-2017, tatu: Legacy setting removed from 3.x, not sure if other visibility checks + // should be checked? + // && ctxt.isEnabled(MapperFeature.AUTO_DETECT_GETTERS); // Ok: let's then filter out property definitions List propDefs = filterBeanProps(ctxt, @@ -675,14 +682,6 @@ protected void addBackReferenceProperties(DeserializationContext ctxt, } } - @Deprecated // since 2.9 (rename) - protected void addReferenceProperties(DeserializationContext ctxt, - BeanDescription beanDesc, BeanDeserializerBuilder builder) - throws JsonMappingException - { - addBackReferenceProperties(ctxt, beanDesc, builder); - } - /** * Method called locate all members used for value injection (if any), * constructor {@link com.fasterxml.jackson.databind.deser.impl.ValueInjector} instances, and add them to builder. diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BuilderBasedDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/BuilderBasedDeserializer.java index 4f7b345771..25ac8ea31c 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/BuilderBasedDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/BuilderBasedDeserializer.java @@ -4,6 +4,7 @@ import java.util.*; import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.sym.FieldNameMatcher; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.deser.impl.*; import com.fasterxml.jackson.databind.introspect.AnnotatedMethod; @@ -30,11 +31,21 @@ public class BuilderBasedDeserializer /** * Type that the builder will produce, target type; as opposed to * `handledType()` which refers to Builder class. - * - * @since 2.9 */ protected final JavaType _targetType; + // @since 3.0 + protected FieldNameMatcher _fieldMatcher; + + // @since 3.0 + protected SettableBeanProperty[] _fieldsByIndex; + + /** + * State marker we need in order to avoid infinite recursion for some cases + * (not very clean, alas, but has to do for now) + */ + private volatile transient NameTransformer _currentlyTransforming; + /* /********************************************************** /* Life-cycle, construction, initialization @@ -61,21 +72,6 @@ public BuilderBasedDeserializer(BeanDeserializerBuilder builder, } } - /** - * @deprecated Since 2.9 - */ - @Deprecated - public BuilderBasedDeserializer(BeanDeserializerBuilder builder, - BeanDescription beanDesc, - BeanPropertyMap properties, Map backRefs, - Set ignorableProps, boolean ignoreAllUnknown, - boolean hasViews) - { - this(builder, beanDesc, - beanDesc.getType(), // Wrong! But got no access via `BeanDeserializerBuilder` - properties, backRefs, ignorableProps, ignoreAllUnknown, hasViews); - } - /** * Copy-constructor that can be used by sub-classes to allow * copy-on-write styling copying of settings of an existing instance. @@ -90,40 +86,71 @@ protected BuilderBasedDeserializer(BuilderBasedDeserializer src, boolean ignoreA super(src, ignoreAllUnknown); _buildMethod = src._buildMethod; _targetType = src._targetType; + _fieldMatcher = src._fieldMatcher; + _fieldsByIndex = src._fieldsByIndex; } - protected BuilderBasedDeserializer(BuilderBasedDeserializer src, NameTransformer unwrapper) { - super(src, unwrapper); + protected BuilderBasedDeserializer(BuilderBasedDeserializer src, + UnwrappedPropertyHandler unwrapHandler, BeanPropertyMap renamedProperties, + boolean ignoreAllUnknown) { + super(src, unwrapHandler, renamedProperties, ignoreAllUnknown); _buildMethod = src._buildMethod; _targetType = src._targetType; + _fieldMatcher = _beanProperties.getFieldMatcher(); + _fieldsByIndex = _beanProperties.getFieldMatcherProperties(); } public BuilderBasedDeserializer(BuilderBasedDeserializer src, ObjectIdReader oir) { super(src, oir); _buildMethod = src._buildMethod; _targetType = src._targetType; + _fieldMatcher = src._fieldMatcher; + _fieldsByIndex = src._fieldsByIndex; } public BuilderBasedDeserializer(BuilderBasedDeserializer src, Set ignorableProps) { super(src, ignorableProps); _buildMethod = src._buildMethod; _targetType = src._targetType; + _fieldMatcher = src._fieldMatcher; + _fieldsByIndex = src._fieldsByIndex; } public BuilderBasedDeserializer(BuilderBasedDeserializer src, BeanPropertyMap props) { super(src, props); _buildMethod = src._buildMethod; _targetType = src._targetType; + _fieldMatcher = _beanProperties.getFieldMatcher(); + _fieldsByIndex = _beanProperties.getFieldMatcherProperties(); } @Override - public JsonDeserializer unwrappingDeserializer(NameTransformer unwrapper) + protected void initFieldMatcher(DeserializationContext ctxt) { + _beanProperties.initMatcher(ctxt.getParserFactory()); + _fieldMatcher = _beanProperties.getFieldMatcher(); + _fieldsByIndex = _beanProperties.getFieldMatcherProperties(); + } + + @Override + public JsonDeserializer unwrappingDeserializer(DeserializationContext ctxt, + NameTransformer transformer) { - /* main thing really is to just enforce ignoring of unknown - * properties; since there may be multiple unwrapped values - * and properties for all may be interleaved... - */ - return new BuilderBasedDeserializer(this, unwrapper); + // main thing really is to just enforce ignoring of unknown properties; since + // there may be multiple unwrapped values and properties for all may be interleaved... + if (_currentlyTransforming == transformer) { + return this; + } + _currentlyTransforming = transformer; + try { + UnwrappedPropertyHandler uwHandler = _unwrappedPropertyHandler; + // delegate further unwraps, if any + if (uwHandler != null) { + uwHandler = uwHandler.renameAll(ctxt, transformer); + } + // and handle direct unwrapping as well: + BeanPropertyMap props = _beanProperties.renameAll(ctxt, transformer); + return new BuilderBasedDeserializer(this, uwHandler, props, true); + } finally { _currentlyTransforming = null; } } @Override @@ -143,8 +170,9 @@ public BeanDeserializerBase withBeanProperties(BeanPropertyMap props) { @Override protected BeanDeserializerBase asArrayDeserializer() { - SettableBeanProperty[] props = _beanProperties.getPropertiesInInsertionOrder(); - return new BeanAsArrayBuilderDeserializer(this, _targetType, props, _buildMethod); + return new BeanAsArrayBuilderDeserializer(this, _targetType, + _beanProperties.getPrimaryProperties(), + _buildMethod); } /* @@ -187,16 +215,16 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { // common case first: - if (p.isExpectedStartObjectToken()) { - JsonToken t = p.nextToken(); + if (p.isExpectedStartObjectToken()) { if (_vanillaProcessing) { - return finishBuild(ctxt, vanillaDeserialize(p, ctxt, t)); + return finishBuild(ctxt, _vanillaDeserialize(p, ctxt)); } + p.nextToken(); Object builder = deserializeFromObject(p, ctxt); return finishBuild(ctxt, builder); } // and then others, generally requiring use of @JsonCreator - switch (p.getCurrentTokenId()) { + switch (p.currentTokenId()) { case JsonTokenId.ID_STRING: return finishBuild(ctxt, deserializeFromString(p, ctxt)); case JsonTokenId.ID_NUMBER_INT: @@ -254,27 +282,32 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt, * Streamlined version that is only used when no "special" * features are enabled. */ - private final Object vanillaDeserialize(JsonParser p, - DeserializationContext ctxt, JsonToken t) + private final Object _vanillaDeserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - Object bean = _valueInstantiator.createUsingDefault(ctxt); - for (; p.getCurrentToken() == JsonToken.FIELD_NAME; p.nextToken()) { - String propName = p.getCurrentName(); - // Skip field name: - p.nextToken(); - SettableBeanProperty prop = _beanProperties.find(propName); - if (prop != null) { // normal case + Object builder = _valueInstantiator.createUsingDefault(ctxt); + while (true) { + int ix = p.nextFieldName(_fieldMatcher); + if (ix >= 0) { + p.nextToken(); + SettableBeanProperty prop = _fieldsByIndex[ix]; try { - bean = prop.deserializeSetAndReturn(p, ctxt, bean); + builder = prop.deserializeSetAndReturn(p, ctxt, builder); } catch (Exception e) { - wrapAndThrow(e, bean, propName, ctxt); + throw wrapAndThrow(e, builder, prop.getName(), ctxt); } - } else { - handleUnknownVanilla(p, ctxt, bean, propName); + continue; } + if (ix == FieldNameMatcher.MATCH_END_OBJECT) { + return builder; + } + if (ix == FieldNameMatcher.MATCH_UNKNOWN_NAME) { + p.nextToken(); + handleUnknownVanilla(p, ctxt, builder, p.currentName()); + continue; + } + return _handleUnexpectedWithin(p, ctxt, builder); } - return bean; } /** @@ -304,22 +337,25 @@ public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) return deserializeWithView(p, ctxt, bean, view); } } - for (; p.getCurrentToken() == JsonToken.FIELD_NAME; p.nextToken()) { - String propName = p.getCurrentName(); - // Skip field name: - p.nextToken(); - SettableBeanProperty prop = _beanProperties.find(propName); - if (prop != null) { // normal case + for (int ix = p.currentFieldName(_fieldMatcher); ; ix = p.nextFieldName(_fieldMatcher)) { + if (ix >= 0) { // normal case + p.nextToken(); try { - bean = prop.deserializeSetAndReturn(p, ctxt, bean); + bean = _fieldsByIndex[ix].deserializeSetAndReturn(p, ctxt, bean); } catch (Exception e) { - wrapAndThrow(e, bean, propName, ctxt); + throw wrapAndThrow(e, bean, p.currentName(), ctxt); } continue; } - handleUnknownVanilla(p, ctxt, bean, propName); + if (ix == FieldNameMatcher.MATCH_END_OBJECT) { + return bean; + } + if (ix != FieldNameMatcher.MATCH_UNKNOWN_NAME) { + return _handleUnexpectedWithin(p, ctxt, bean); + } + p.nextToken(); + handleUnknownVanilla(p, ctxt, bean, p.currentName()); } - return bean; } /** @@ -345,9 +381,9 @@ protected Object _deserializeUsingPropertyBased(final JsonParser p, // 04-Jan-2010, tatu: May need to collect unknown properties for polymorphic cases TokenBuffer unknown = null; - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) { - String propName = p.getCurrentName(); + String propName = p.currentName(); p.nextToken(); // to point to value // creator property? SettableBeanProperty creatorProp = creator.findCreatorProperty(propName); @@ -363,8 +399,7 @@ protected Object _deserializeUsingPropertyBased(final JsonParser p, try { builder = creator.build(ctxt, buffer); } catch (Exception e) { - wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt); - continue; // never gets here + throw wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt); } // polymorphic? if (builder.getClass() != _beanType.getRawClass()) { @@ -383,8 +418,10 @@ protected Object _deserializeUsingPropertyBased(final JsonParser p, continue; } // regular property? needs buffering - SettableBeanProperty prop = _beanProperties.find(propName); - if (prop != null) { + int ix = _fieldMatcher.matchName(propName); + if (ix >= 0) { + SettableBeanProperty prop = _fieldsByIndex[ix]; + // !!! 21-Nov-2017, tatu: Regular deserializer handles references here... buffer.bufferProperty(prop, prop.deserialize(p, ctxt)); continue; } @@ -401,7 +438,7 @@ protected Object _deserializeUsingPropertyBased(final JsonParser p, } // Ok then, let's collect the whole field; name and value if (unknown == null) { - unknown = new TokenBuffer(p, ctxt); + unknown = TokenBuffer.forInputBuffering(p, ctxt); } unknown.writeFieldName(propName); unknown.copyCurrentStructure(p); @@ -436,7 +473,7 @@ protected final Object _deserialize(JsonParser p, if (p.hasToken(JsonToken.START_OBJECT)) { p.nextToken(); } - TokenBuffer tokens = new TokenBuffer(p, ctxt); + TokenBuffer tokens = TokenBuffer.forInputBuffering(p, ctxt); tokens.writeStartObject(); return deserializeWithUnwrapped(p, ctxt, builder, tokens); } @@ -449,28 +486,28 @@ protected final Object _deserialize(JsonParser p, return deserializeWithView(p, ctxt, builder, view); } } - JsonToken t = p.getCurrentToken(); - // 23-Mar-2010, tatu: In some cases, we start with full JSON object too... - if (t == JsonToken.START_OBJECT) { - t = p.nextToken(); - } - for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) { - String propName = p.getCurrentName(); - // Skip field name: - p.nextToken(); - SettableBeanProperty prop = _beanProperties.find(propName); - - if (prop != null) { // normal case + int ix = p.isExpectedStartObjectToken() ? + p.nextFieldName(_fieldMatcher) : p.currentFieldName(_fieldMatcher); + for (; ; ix = p.nextFieldName(_fieldMatcher)) { + if (ix >= 0) { + p.nextToken(); + SettableBeanProperty prop = _fieldsByIndex[ix]; try { builder = prop.deserializeSetAndReturn(p, ctxt, builder); } catch (Exception e) { - wrapAndThrow(e, builder, propName, ctxt); + throw wrapAndThrow(e, builder, prop.getName(), ctxt); } continue; } - handleUnknownVanilla(p, ctxt, handledType(), propName); + if (ix == FieldNameMatcher.MATCH_END_OBJECT) { + return builder; + } + if (ix != FieldNameMatcher.MATCH_UNKNOWN_NAME) { + return _handleUnexpectedWithin(p, ctxt, builder); + } + p.nextToken(); + handleUnknownVanilla(p, ctxt, handledType(), p.currentName()); } - return builder; } /* @@ -483,13 +520,10 @@ protected final Object deserializeWithView(JsonParser p, DeserializationContext Object bean, Class activeView) throws IOException { - JsonToken t = p.getCurrentToken(); - for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) { - String propName = p.getCurrentName(); - // Skip field name: - p.nextToken(); - SettableBeanProperty prop = _beanProperties.find(propName); - if (prop != null) { + for (int ix = p.currentFieldName(_fieldMatcher); ; ix = p.nextFieldName(_fieldMatcher)) { + if (ix >= 0) { + p.nextToken(); + SettableBeanProperty prop = _fieldsByIndex[ix]; if (!prop.visibleInView(activeView)) { p.skipChildren(); continue; @@ -497,13 +531,20 @@ protected final Object deserializeWithView(JsonParser p, DeserializationContext try { bean = prop.deserializeSetAndReturn(p, ctxt, bean); } catch (Exception e) { - wrapAndThrow(e, bean, propName, ctxt); + throw wrapAndThrow(e, bean, prop.getName(), ctxt); } continue; } - handleUnknownVanilla(p, ctxt, bean, propName); + if (ix != FieldNameMatcher.MATCH_END_OBJECT) { + if (ix != FieldNameMatcher.MATCH_UNKNOWN_NAME) { + return _handleUnexpectedWithin(p, ctxt, bean); + } + p.nextToken(); + handleUnknownVanilla(p, ctxt, bean, p.currentName()); + continue; + } + return bean; } - return bean; } /* @@ -526,7 +567,7 @@ protected Object deserializeWithUnwrapped(JsonParser p, DeserializationContext c if (_propertyBasedCreator != null) { return deserializeUsingPropertyBasedWithUnwrapped(p, ctxt); } - TokenBuffer tokens = new TokenBuffer(p, ctxt); + TokenBuffer tokens = TokenBuffer.forInputBuffering(p, ctxt); tokens.writeStartObject(); Object bean = _valueInstantiator.createUsingDefault(ctxt); @@ -535,24 +576,31 @@ protected Object deserializeWithUnwrapped(JsonParser p, DeserializationContext c } final Class activeView = _needViewProcesing ? ctxt.getActiveView() : null; - for (; p.getCurrentToken() == JsonToken.FIELD_NAME; p.nextToken()) { - String propName = p.getCurrentName(); - p.nextToken(); - SettableBeanProperty prop = _beanProperties.find(propName); - if (prop != null) { // normal case - if (activeView != null && !prop.visibleInView(activeView)) { + for (int ix = p.currentFieldName(_fieldMatcher); ; ix = p.nextFieldName(_fieldMatcher)) { + if (ix >= 0) { // common case + p.nextToken(); + SettableBeanProperty prop = _fieldsByIndex[ix]; + if ((activeView != null) && !prop.visibleInView(activeView)) { p.skipChildren(); continue; } try { bean = prop.deserializeSetAndReturn(p, ctxt, bean); } catch (Exception e) { - wrapAndThrow(e, bean, propName, ctxt); + throw wrapAndThrow(e, bean, prop.getName(), ctxt); } continue; } + if (ix == FieldNameMatcher.MATCH_END_OBJECT) { + break; + } + if (ix == FieldNameMatcher.MATCH_ODD_TOKEN) { + return _handleUnexpectedWithin(p, ctxt, bean); + } + final String propName = p.currentName(); + p.nextToken(); // ignorable things should be ignored - if (_ignorableProps != null && _ignorableProps.contains(propName)) { + if ((_ignorableProps != null) && _ignorableProps.contains(propName)) { handleIgnoredProperty(p, ctxt, bean, propName); continue; } @@ -564,7 +612,7 @@ protected Object deserializeWithUnwrapped(JsonParser p, DeserializationContext c try { _anySetter.deserializeAndSet(p, ctxt, bean, propName); } catch (Exception e) { - wrapAndThrow(e, bean, propName, ctxt); + throw wrapAndThrow(e, bean, propName, ctxt); } continue; } @@ -573,6 +621,50 @@ protected Object deserializeWithUnwrapped(JsonParser p, DeserializationContext c return _unwrappedPropertyHandler.processUnwrapped(p, ctxt, bean, tokens); } + protected Object deserializeWithUnwrapped(JsonParser p, + DeserializationContext ctxt, Object builder, TokenBuffer tokens) + throws IOException + { + final Class activeView = _needViewProcesing ? ctxt.getActiveView() : null; + for (int ix = p.currentFieldName(_fieldMatcher); ; ix = p.nextFieldName(_fieldMatcher)) { + if (ix >= 0) { // common case + p.nextToken(); + SettableBeanProperty prop = _fieldsByIndex[ix]; + if ((activeView != null) && !prop.visibleInView(activeView)) { + p.skipChildren(); + continue; + } + try { + builder = prop.deserializeSetAndReturn(p, ctxt, builder); + } catch (Exception e) { + wrapAndThrow(e, builder, prop.getName(), ctxt); + } + continue; + } + if (ix == FieldNameMatcher.MATCH_END_OBJECT) { + break; + } + if (ix == FieldNameMatcher.MATCH_ODD_TOKEN) { + return _handleUnexpectedWithin(p, ctxt, builder); + } + final String propName = p.currentName(); + p.nextToken(); + if ((_ignorableProps != null) && _ignorableProps.contains(propName)) { + handleIgnoredProperty(p, ctxt, builder, propName); + continue; + } + // but... others should be passed to unwrapped property deserializers + tokens.writeFieldName(propName); + tokens.copyCurrentStructure(p); + // how about any setter? We'll get copies but... + if (_anySetter != null) { + _anySetter.deserializeAndSet(p, ctxt, builder, propName); + } + } + tokens.writeEndObject(); + return _unwrappedPropertyHandler.processUnwrapped(p, ctxt, builder, tokens); + } + @SuppressWarnings("resource") protected Object deserializeUsingPropertyBasedWithUnwrapped(JsonParser p, DeserializationContext ctxt) @@ -581,13 +673,12 @@ protected Object deserializeUsingPropertyBasedWithUnwrapped(JsonParser p, final PropertyBasedCreator creator = _propertyBasedCreator; PropertyValueBuffer buffer = creator.startBuilding(p, ctxt, _objectIdReader); - TokenBuffer tokens = new TokenBuffer(p, ctxt); + TokenBuffer tokens = TokenBuffer.forInputBuffering(p, ctxt); tokens.writeStartObject(); - Object builder = null; - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) { - String propName = p.getCurrentName(); + String propName = p.currentName(); p.nextToken(); // to point to value // creator property? SettableBeanProperty creatorProp = creator.findCreatorProperty(propName); @@ -595,11 +686,11 @@ protected Object deserializeUsingPropertyBasedWithUnwrapped(JsonParser p, // Last creator property to set? if (buffer.assignParameter(creatorProp, creatorProp.deserialize(p, ctxt))) { t = p.nextToken(); // to move to following FIELD_NAME/END_OBJECT + Object builder = null; try { builder = creator.build(ctxt, buffer); } catch (Exception e) { - wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt); - continue; // never gets here + throw wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt); } if (builder.getClass() != _beanType.getRawClass()) { return handlePolymorphic(p, ctxt, builder, tokens); @@ -613,8 +704,9 @@ protected Object deserializeUsingPropertyBasedWithUnwrapped(JsonParser p, continue; } // regular property? needs buffering - SettableBeanProperty prop = _beanProperties.find(propName); - if (prop != null) { + int ix = _fieldMatcher.matchName(propName); + if (ix >= 0) { + SettableBeanProperty prop = _fieldsByIndex[ix]; buffer.bufferProperty(prop, prop.deserialize(p, ctxt)); continue; } @@ -632,50 +724,12 @@ protected Object deserializeUsingPropertyBasedWithUnwrapped(JsonParser p, tokens.writeEndObject(); // We hit END_OBJECT, so: - if (builder == null) { - try { - builder = creator.build(ctxt, buffer); - } catch (Exception e) { - return wrapInstantiationProblem(e, ctxt); - } - } - return _unwrappedPropertyHandler.processUnwrapped(p, ctxt, builder, tokens); - } - - protected Object deserializeWithUnwrapped(JsonParser p, - DeserializationContext ctxt, Object builder, TokenBuffer tokens) - throws IOException - { - final Class activeView = _needViewProcesing ? ctxt.getActiveView() : null; - for (JsonToken t = p.getCurrentToken(); t == JsonToken.FIELD_NAME; t = p.nextToken()) { - String propName = p.getCurrentName(); - SettableBeanProperty prop = _beanProperties.find(propName); - p.nextToken(); - if (prop != null) { // normal case - if (activeView != null && !prop.visibleInView(activeView)) { - p.skipChildren(); - continue; - } - try { - builder = prop.deserializeSetAndReturn(p, ctxt, builder); - } catch (Exception e) { - wrapAndThrow(e, builder, propName, ctxt); - } - continue; - } - if (_ignorableProps != null && _ignorableProps.contains(propName)) { - handleIgnoredProperty(p, ctxt, builder, propName); - continue; - } - // but... others should be passed to unwrapped property deserializers - tokens.writeFieldName(propName); - tokens.copyCurrentStructure(p); - // how about any setter? We'll get copies but... - if (_anySetter != null) { - _anySetter.deserializeAndSet(p, ctxt, builder, propName); - } + Object builder = null; + try { + builder = creator.build(ctxt, buffer); + } catch (Exception e) { + return wrapInstantiationProblem(e, ctxt); } - tokens.writeEndObject(); return _unwrappedPropertyHandler.processUnwrapped(p, ctxt, builder, tokens); } @@ -702,14 +756,13 @@ protected Object deserializeWithExternalTypeId(JsonParser p, final Class activeView = _needViewProcesing ? ctxt.getActiveView() : null; final ExternalTypeHandler ext = _externalTypeIdHandler.start(); - for (JsonToken t = p.getCurrentToken(); t == JsonToken.FIELD_NAME; t = p.nextToken()) { - String propName = p.getCurrentName(); - t = p.nextToken(); - SettableBeanProperty prop = _beanProperties.find(propName); - if (prop != null) { // normal case + for (int ix = p.currentFieldName(_fieldMatcher); ; ix = p.nextFieldName(_fieldMatcher)) { + if (ix >= 0) { // normal case + SettableBeanProperty prop = _fieldsByIndex[ix]; + JsonToken t = p.nextToken(); // May have property AND be used as external type id: if (t.isScalarValue()) { - ext.handleTypePropertyValue(p, ctxt, propName, bean); + ext.handleTypePropertyValue(p, ctxt, p.currentName(), bean); } if (activeView != null && !prop.visibleInView(activeView)) { p.skipChildren(); @@ -718,12 +771,19 @@ protected Object deserializeWithExternalTypeId(JsonParser p, try { bean = prop.deserializeSetAndReturn(p, ctxt, bean); } catch (Exception e) { - wrapAndThrow(e, bean, propName, ctxt); + throw wrapAndThrow(e, bean, prop.getName(), ctxt); } continue; } + if (ix == FieldNameMatcher.MATCH_END_OBJECT) { + break; + } + if (ix != FieldNameMatcher.MATCH_UNKNOWN_NAME) { + return _handleUnexpectedWithin(p, ctxt, bean); + } // ignorable things should be ignored - if (_ignorableProps != null && _ignorableProps.contains(propName)) { + final String propName = p.currentName(); + if ((_ignorableProps != null) && (_ignorableProps.contains(propName))) { handleIgnoredProperty(p, ctxt, bean, propName); continue; } @@ -736,7 +796,7 @@ protected Object deserializeWithExternalTypeId(JsonParser p, try { _anySetter.deserializeAndSet(p, ctxt, bean, propName); } catch (Exception e) { - wrapAndThrow(e, bean, propName, ctxt); + throw wrapAndThrow(e, bean, propName, ctxt); } continue; } else { @@ -758,4 +818,22 @@ protected Object deserializeUsingPropertyBasedWithExternalTypeId(JsonParser p, "Deserialization (of %s) with Builder, External type id, @JsonCreator not yet implemented", t)); } + + /* + /********************************************************** + /* Error handling + /********************************************************** + */ + + /** + * Method called if an unexpected token (other then FIELD_NAME) + * is found after POJO has been instantiated and partially bound. + * + * @since 3.0 + */ + protected Object _handleUnexpectedWithin(JsonParser p, + DeserializationContext ctxt, Object beanOrBuilder) throws IOException + { + return ctxt.handleUnexpectedToken(handledType(), p); + } } diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/ContextualDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/ContextualDeserializer.java index 9db1f7530d..f002c231a0 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/ContextualDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/ContextualDeserializer.java @@ -3,40 +3,11 @@ import com.fasterxml.jackson.databind.*; /** - * Add-on interface that {@link JsonDeserializer}s can implement to get a callback - * that can be used to create contextual (context-dependent) instances of - * deserializer to use for handling properties of supported type. - * This can be useful - * for deserializers that can be configured by annotations, or should otherwise - * have differing behavior depending on what kind of property is being deserialized. - *

    - * Note that in cases where deserializer needs both contextualization and - * resolution -- that is, implements both this interface and {@link ResolvableDeserializer} - * -- resolution via {@link ResolvableDeserializer} occurs first, and contextual - * resolution (via this interface) later on. + * @deprecated Since 3.0: method demoted to JsonDeserializer */ +@Deprecated public interface ContextualDeserializer { - /** - * Method called to see if a different (or differently configured) deserializer - * is needed to deserialize values of specified property. - * Note that instance that this method is called on is typically shared one and - * as a result method should NOT modify this instance but rather construct - * and return a new instance. This instance should only be returned as-is, in case - * it is already suitable for use. - * - * @param ctxt Deserialization context to access configuration, additional - * deserializers that may be needed by this deserializer - * @param property Method, field or constructor parameter that represents the property - * (and is used to assign deserialized value). - * Should be available; but there may be cases where caller cannot provide it and - * null is passed instead (in which case impls usually pass 'this' deserializer as is) - * - * @return Deserializer to use for deserializing values of specified property; - * may be this instance or a new instance. - * - * @throws JsonMappingException - */ public JsonDeserializer createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException; diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/DataFormatReaders.java b/src/main/java/com/fasterxml/jackson/databind/deser/DataFormatReaders.java deleted file mode 100644 index 7bd78fb69b..0000000000 --- a/src/main/java/com/fasterxml/jackson/databind/deser/DataFormatReaders.java +++ /dev/null @@ -1,388 +0,0 @@ -package com.fasterxml.jackson.databind.deser; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.Collection; - -import com.fasterxml.jackson.core.*; -import com.fasterxml.jackson.core.format.*; -import com.fasterxml.jackson.core.io.MergedStream; - -import com.fasterxml.jackson.databind.*; - -/** - * Alternative to {@link DataFormatDetector} that needs to be used when - * using data-binding. - * - * @since 2.1 - */ -public class DataFormatReaders -{ - /** - * By default we will look ahead at most 64 bytes; in most cases, - * much less (4 bytes or so) is needed, but we will allow bit more - * leniency to support data formats that need more complex heuristics. - */ - public final static int DEFAULT_MAX_INPUT_LOOKAHEAD = 64; - - /** - * Ordered list of readers which both represent data formats to - * detect (in precedence order, starting with highest) and contain - * factories used for actual detection. - */ - protected final ObjectReader[] _readers; - - /** - * Strength of match we consider to be good enough to be used - * without checking any other formats. - * Default value is {@link MatchStrength#SOLID_MATCH}, - */ - protected final MatchStrength _optimalMatch; - - /** - * Strength of minimal match we accept as the answer, unless - * better matches are found. - * Default value is {@link MatchStrength#WEAK_MATCH}, - */ - protected final MatchStrength _minimalMatch; - - /** - * Maximum number of leading bytes of the input that we can read - * to determine data format. - *

    - * Default value is {@link #DEFAULT_MAX_INPUT_LOOKAHEAD}. - */ - protected final int _maxInputLookahead; - - /* - /********************************************************** - /* Construction - /********************************************************** - */ - - public DataFormatReaders(ObjectReader... detectors) { - this(detectors, MatchStrength.SOLID_MATCH, MatchStrength.WEAK_MATCH, - DEFAULT_MAX_INPUT_LOOKAHEAD); - } - - public DataFormatReaders(Collection detectors) { - this(detectors.toArray(new ObjectReader[detectors.size()])); - } - - private DataFormatReaders(ObjectReader[] detectors, - MatchStrength optMatch, MatchStrength minMatch, - int maxInputLookahead) - { - _readers = detectors; - _optimalMatch = optMatch; - _minimalMatch = minMatch; - _maxInputLookahead = maxInputLookahead; - } - - /* - /********************************************************** - /* Fluent factories for changing match settings - /********************************************************** - */ - - public DataFormatReaders withOptimalMatch(MatchStrength optMatch) { - if (optMatch == _optimalMatch) { - return this; - } - return new DataFormatReaders(_readers, optMatch, _minimalMatch, _maxInputLookahead); - } - - public DataFormatReaders withMinimalMatch(MatchStrength minMatch) { - if (minMatch == _minimalMatch) { - return this; - } - return new DataFormatReaders(_readers, _optimalMatch, minMatch, _maxInputLookahead); - } - - public DataFormatReaders with(ObjectReader[] readers) { - return new DataFormatReaders(readers, _optimalMatch, _minimalMatch, _maxInputLookahead); - } - - public DataFormatReaders withMaxInputLookahead(int lookaheadBytes) - { - if (lookaheadBytes == _maxInputLookahead) { - return this; - } - return new DataFormatReaders(_readers, _optimalMatch, _minimalMatch, lookaheadBytes); - } - - /* - /********************************************************** - /* Fluent factories for changing underlying readers - /********************************************************** - */ - - public DataFormatReaders with(DeserializationConfig config) - { - final int len = _readers.length; - ObjectReader[] r = new ObjectReader[len]; - for (int i = 0; i < len; ++i) { - r[i] = _readers[i].with(config); - } - return new DataFormatReaders(r, _optimalMatch, _minimalMatch, _maxInputLookahead); - } - - public DataFormatReaders withType(JavaType type) - { - final int len = _readers.length; - ObjectReader[] r = new ObjectReader[len]; - for (int i = 0; i < len; ++i) { - r[i] = _readers[i].forType(type); - } - return new DataFormatReaders(r, _optimalMatch, _minimalMatch, _maxInputLookahead); - } - - /* - /********************************************************** - /* Public API - /********************************************************** - */ - - /** - * Method to call to find format that content (accessible via given - * {@link InputStream}) given has, as per configuration of this detector - * instance. - * - * @return Matcher object which contains result; never null, even in cases - * where no match (with specified minimal match strength) is found. - */ - public Match findFormat(InputStream in) throws IOException - { - return _findFormat(new AccessorForReader(in, new byte[_maxInputLookahead])); - } - - /** - * Method to call to find format that given content (full document) - * has, as per configuration of this detector instance. - * - * @return Matcher object which contains result; never null, even in cases - * where no match (with specified minimal match strength) is found. - */ - public Match findFormat(byte[] fullInputData) throws IOException - { - return _findFormat(new AccessorForReader(fullInputData)); - } - - /** - * Method to call to find format that given content (full document) - * has, as per configuration of this detector instance. - * - * @return Matcher object which contains result; never null, even in cases - * where no match (with specified minimal match strength) is found. - * - * @since 2.1 - */ - public Match findFormat(byte[] fullInputData, int offset, int len) throws IOException - { - return _findFormat(new AccessorForReader(fullInputData, offset, len)); - } - - /* - /********************************************************** - /* Overrides - /********************************************************** - */ - - @Override - public String toString() - { - StringBuilder sb = new StringBuilder(); - sb.append('['); - final int len = _readers.length; - if (len > 0) { - sb.append(_readers[0].getFactory().getFormatName()); - for (int i = 1; i < len; ++i) { - sb.append(", "); - sb.append(_readers[i].getFactory().getFormatName()); - } - } - sb.append(']'); - return sb.toString(); - } - - /* - /********************************************************** - /* Internal methods - /********************************************************** - */ - - private Match _findFormat(AccessorForReader acc) throws IOException - { - ObjectReader bestMatch = null; - MatchStrength bestMatchStrength = null; - for (ObjectReader f : _readers) { - acc.reset(); - MatchStrength strength = f.getFactory().hasFormat(acc); - // if not better than what we have so far (including minimal level limit), skip - if (strength == null || strength.ordinal() < _minimalMatch.ordinal()) { - continue; - } - // also, needs to better match than before - if (bestMatch != null) { - if (bestMatchStrength.ordinal() >= strength.ordinal()) { - continue; - } - } - // finally: if it's good enough match, we are done - bestMatch = f; - bestMatchStrength = strength; - if (strength.ordinal() >= _optimalMatch.ordinal()) { - break; - } - } - return acc.createMatcher(bestMatch, bestMatchStrength); - } - - /* - /********************************************************** - /* Helper classes - /********************************************************** - */ - - /** - * We need sub-class here as well, to be able to access efficiently. - */ - protected class AccessorForReader extends InputAccessor.Std - { - public AccessorForReader(InputStream in, byte[] buffer) { - super(in, buffer); - } - public AccessorForReader(byte[] inputDocument) { - super(inputDocument); - } - public AccessorForReader(byte[] inputDocument, int start, int len) { - super(inputDocument, start, len); - } - - public Match createMatcher(ObjectReader match, MatchStrength matchStrength) - { - return new Match(_in, _buffer, _bufferedStart, (_bufferedEnd - _bufferedStart), - match, matchStrength); - } - } - - /** - * Result class, similar to {@link DataFormatMatcher} - */ - public static class Match - { - protected final InputStream _originalStream; - - /** - * Content read during format matching process - */ - protected final byte[] _bufferedData; - - /** - * Pointer to the first byte in buffer available for reading - */ - protected final int _bufferedStart; - - /** - * Number of bytes available in buffer. - */ - protected final int _bufferedLength; - - /** - * Factory that produced sufficient match (if any) - */ - protected final ObjectReader _match; - - /** - * Strength of match with {@link #_match} - */ - protected final MatchStrength _matchStrength; - - protected Match(InputStream in, byte[] buffered, - int bufferedStart, int bufferedLength, - ObjectReader match, MatchStrength strength) - { - _originalStream = in; - _bufferedData = buffered; - _bufferedStart = bufferedStart; - _bufferedLength = bufferedLength; - _match = match; - _matchStrength = strength; - } - - /* - /********************************************************** - /* Public API, simple accessors - /********************************************************** - */ - - /** - * Accessor to use to see if any formats matched well enough with - * the input data. - */ - public boolean hasMatch() { return _match != null; } - - /** - * Method for accessing strength of the match, if any; if no match, - * will return {@link MatchStrength#INCONCLUSIVE}. - */ - public MatchStrength getMatchStrength() { - return (_matchStrength == null) ? MatchStrength.INCONCLUSIVE : _matchStrength; - } - - /** - * Accessor for {@link JsonFactory} that represents format that data matched. - */ - public ObjectReader getReader() { return _match; } - - /** - * Accessor for getting brief textual name of matched format if any (null - * if none). Equivalent to: - *

    -         *   return hasMatch() ? getMatch().getFormatName() : null;
    -         *
    - */ - public String getMatchedFormatName() { - return _match.getFactory().getFormatName(); - } - - /* - /********************************************************** - /* Public API, factory methods - /********************************************************** - */ - - /** - * Convenience method for trying to construct a {@link JsonParser} for - * parsing content which is assumed to be in detected data format. - * If no match was found, returns null. - */ - public JsonParser createParserWithMatch() throws IOException - { - if (_match == null) { - return null; - } - JsonFactory jf = _match.getFactory(); - if (_originalStream == null) { - return jf.createParser(_bufferedData, _bufferedStart, _bufferedLength); - } - return jf.createParser(getDataStream()); - } - - /** - * Method to use for accessing input for which format detection has been done. - * This must be used instead of using stream passed to detector - * unless given stream itself can do buffering. - * Stream will return all content that was read during matching process, as well - * as remaining contents of the underlying stream. - */ - public InputStream getDataStream() { - if (_originalStream == null) { - return new ByteArrayInputStream(_bufferedData, _bufferedStart, _bufferedLength); - } - return new MergedStream(null, _originalStream, _bufferedData, _bufferedStart, _bufferedLength); - } - } - -} 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 cdc90ed2e7..240bf561bd 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/DefaultDeserializationContext.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/DefaultDeserializationContext.java @@ -7,8 +7,9 @@ import com.fasterxml.jackson.annotation.ObjectIdResolver; import com.fasterxml.jackson.annotation.ObjectIdGenerator.IdKey; +import com.fasterxml.jackson.core.FormatSchema; import com.fasterxml.jackson.core.JsonParser; - +import com.fasterxml.jackson.core.TokenStreamFactory; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.cfg.HandlerInstantiator; import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId; @@ -21,16 +22,10 @@ * extended API for {@link ObjectMapper} (and {@link ObjectReader}) * to call, as well as implements certain parts that base class * has left abstract. - * The remaining abstract methods ({@link #createInstance}, {@link #with}) - * are left so that custom implementations will properly implement them - * to return intended subtype. */ public abstract class DefaultDeserializationContext extends DeserializationContext - implements java.io.Serializable // since 2.1 { - private static final long serialVersionUID = 1L; - protected transient LinkedHashMap _objectIds; private List _objectIdResolvers; @@ -40,51 +35,35 @@ public abstract class DefaultDeserializationContext * cache: cache may be null (in which case default implementation * will be used), factory cannot be null */ - protected DefaultDeserializationContext(DeserializerFactory df, DeserializerCache cache) { - super(df, cache); - } - - protected DefaultDeserializationContext(DefaultDeserializationContext src, - DeserializationConfig config, JsonParser jp, InjectableValues values) { - super(src, config, jp, values); + protected DefaultDeserializationContext(TokenStreamFactory tsf, + DeserializerFactory deserializerFactory, DeserializerCache cache, + DeserializationConfig config, FormatSchema schema, + InjectableValues values) { + super(tsf, deserializerFactory, cache, + config, schema, values); } - protected DefaultDeserializationContext(DefaultDeserializationContext src, - DeserializerFactory factory) { - super(src, factory); + public DefaultDeserializationContext assignParser(JsonParser p) { + _parser = p; + return this; } - /** - * @since 2.4.4 - */ - protected DefaultDeserializationContext(DefaultDeserializationContext src) { - super(src); - } - - /** - * Method needed to ensure that {@link ObjectMapper#copy} will work - * properly; specifically, that caches are cleared, but settings - * will otherwise remain identical; and that no sharing of state - * occurs. - * - * @since 2.4.4 - */ - public DefaultDeserializationContext copy() { - throw new IllegalStateException("DefaultDeserializationContext sub-class not overriding copy()"); + public JsonParser assignAndReturnParser(JsonParser p) { + _parser = p; + return p; } /* - /********************************************************** + /********************************************************************** /* Abstract methods impls, Object Id - /********************************************************** + /********************************************************************** */ @Override public ReadableObjectId findObjectId(Object id, ObjectIdGenerator gen, ObjectIdResolver resolverType) { - /* 02-Apr-2015, tatu: As per [databind#742] should allow 'null', similar to how - * missing id already works. - */ + // 02-Apr-2015, tatu: As per [databind#742] should allow 'null', similar to how + // missing id already works. if (id == null) { return null; } @@ -134,8 +113,6 @@ public ReadableObjectId findObjectId(Object id, ObjectIdGenerator gen, Object * * @param key The key to associate with the new ReadableObjectId * @return New ReadableObjectId instance - * - * @since 2.7 */ protected ReadableObjectId createReadableObjectId(IdKey key) { return new ReadableObjectId(key); @@ -182,8 +159,6 @@ public void checkUnresolvedObjectId() throws UnresolvedForwardReference *

    * Default implementation simply calls {@link ReadableObjectId#tryToResolveUnresolved} and * returns whatever it returns. - * - * @since 2.6 */ protected boolean tryToResolveUnresolvedObjectId(ReadableObjectId roid) { @@ -191,9 +166,9 @@ protected boolean tryToResolveUnresolvedObjectId(ReadableObjectId roid) } /* - /********************************************************** + /********************************************************************** /* Abstract methods impls, other factory methods - /********************************************************** + /********************************************************************** */ @SuppressWarnings("unchecked") @@ -209,9 +184,8 @@ public JsonDeserializer deserializerInstance(Annotated ann, Object deser if (deserDef instanceof JsonDeserializer) { deser = (JsonDeserializer) deserDef; } else { - /* Alas, there's no way to force return type of "either class - * X or Y" -- need to throw an exception after the fact - */ + // Alas, there's no way to force return type of "either class + // X or Y" -- need to throw an exception after the fact if (!(deserDef instanceof Class)) { throw new IllegalStateException("AnnotationIntrospector returned deserializer definition of type "+deserDef.getClass().getName()+"; expected type JsonDeserializer or Class instead"); } @@ -231,9 +205,7 @@ public JsonDeserializer deserializerInstance(Annotated ann, Object deser } } // First: need to resolve - if (deser instanceof ResolvableDeserializer) { - ((ResolvableDeserializer) deser).resolve(this); - } + deser.resolve(this); return (JsonDeserializer) deser; } @@ -272,35 +244,14 @@ public final KeyDeserializer keyDeserializerInstance(Annotated ann, Object deser } } // First: need to resolve - if (deser instanceof ResolvableDeserializer) { - ((ResolvableDeserializer) deser).resolve(this); - } + deser.resolve(this); return deser; } /* - /********************************************************** - /* Extended API - /********************************************************** - */ - - /** - * Fluent factory method used for constructing a blueprint instance - * with different factory - */ - public abstract DefaultDeserializationContext with(DeserializerFactory factory); - - /** - * Method called to create actual usable per-deserialization - * context instance. - */ - public abstract DefaultDeserializationContext createInstance( - DeserializationConfig config, JsonParser jp, InjectableValues values); - - /* - /********************************************************** + /********************************************************************** /* And then the concrete implementation class - /********************************************************** + /********************************************************************** */ /** @@ -308,42 +259,12 @@ public abstract DefaultDeserializationContext createInstance( */ public final static class Impl extends DefaultDeserializationContext { - private static final long serialVersionUID = 1L; - - /** - * Default constructor for a blueprint object, which will use the standard - * {@link DeserializerCache}, given factory. - */ - public Impl(DeserializerFactory df) { - super(df, null); - } - - protected Impl(Impl src, - DeserializationConfig config, JsonParser jp, InjectableValues values) { - super(src, config, jp, values); + public Impl(TokenStreamFactory tsf, + DeserializerFactory deserializerFactory, DeserializerCache cache, + DeserializationConfig config, FormatSchema schema, + InjectableValues values) { + super(tsf, deserializerFactory, cache, + config, schema, values); } - - protected Impl(Impl src) { super(src); } - - protected Impl(Impl src, DeserializerFactory factory) { - super(src, factory); - } - - @Override - public DefaultDeserializationContext copy() { - ClassUtil.verifyMustOverride(Impl.class, this, "copy"); - return new Impl(this); - } - - @Override - public DefaultDeserializationContext createInstance(DeserializationConfig config, - JsonParser p, InjectableValues values) { - return new Impl(this, config, p, values); - } - - @Override - public DefaultDeserializationContext with(DeserializerFactory factory) { - return new Impl(this, factory); - } } } diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/DeserializationProblemHandler.java b/src/main/java/com/fasterxml/jackson/databind/deser/DeserializationProblemHandler.java index 38b87051bd..5d053f7ea6 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/DeserializationProblemHandler.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/DeserializationProblemHandler.java @@ -35,8 +35,6 @@ public abstract class DeserializationProblemHandler /** * Marker value returned by some handler methods to indicate that * they could not handle problem and produce replacement value. - * - * @since 2.7 */ public final static Object NOT_HANDLED = new Object(); @@ -95,8 +93,6 @@ public boolean handleUnknownProperty(DeserializationContext ctxt, JsonParser p, * @return Either {@link #NOT_HANDLED} to indicate that handler does not know * what to do (and exception may be thrown), or value to use as key (possibly * null - * - * @since 2.8 */ public Object handleWeirdKey(DeserializationContext ctxt, Class rawKeyType, String keyValue, @@ -129,8 +125,6 @@ public Object handleWeirdKey(DeserializationContext ctxt, * @return Either {@link #NOT_HANDLED} to indicate that handler does not know * what to do (and exception may be thrown), or value to use as (possibly * null) - * - * @since 2.8 */ public Object handleWeirdStringValue(DeserializationContext ctxt, Class targetType, String valueToConvert, @@ -163,8 +157,6 @@ public Object handleWeirdStringValue(DeserializationContext ctxt, * @return Either {@link #NOT_HANDLED} to indicate that handler does not know * what to do (and exception may be thrown), or value to use as (possibly * null) - * - * @since 2.8 */ public Object handleWeirdNumberValue(DeserializationContext ctxt, Class targetType, Number valueToConvert, String failureMsg) @@ -191,8 +183,6 @@ public Object handleWeirdNumberValue(DeserializationContext ctxt, * @return Either {@link #NOT_HANDLED} to indicate that handler does not know * what to do (and exception may be thrown), or value to use (possibly * null) - * - * @since 2.9 */ public Object handleWeirdNativeValue(DeserializationContext ctxt, JavaType targetType, Object valueToConvert, JsonParser p) @@ -225,8 +215,6 @@ public Object handleWeirdNativeValue(DeserializationContext ctxt, * @return Either {@link #NOT_HANDLED} to indicate that handler does not know * what to do (and exception may be thrown), or value to use (possibly * null - * - * @since 2.8 */ public Object handleUnexpectedToken(DeserializationContext ctxt, Class targetType, JsonToken t, JsonParser p, @@ -260,8 +248,6 @@ public Object handleUnexpectedToken(DeserializationContext ctxt, * @return Either {@link #NOT_HANDLED} to indicate that handler does not know * what to do (and exception may be thrown), or value to use (possibly * null - * - * @since 2.8 */ public Object handleInstantiationProblem(DeserializationContext ctxt, Class instClass, Object argument, Throwable t) @@ -293,17 +279,13 @@ public Object handleInstantiationProblem(DeserializationContext ctxt, * @return Either {@link #NOT_HANDLED} to indicate that handler does not know * what to do (and exception may be thrown), or value to use (possibly * null - * - * @since 2.9 */ public Object handleMissingInstantiator(DeserializationContext ctxt, Class instClass, ValueInstantiator valueInsta, JsonParser p, String msg) throws IOException { - // 16-Oct-2016, tatu: Need to delegate to deprecated method from 2.8; - // remove redirect from later versions (post-2.9) - return handleMissingInstantiator(ctxt, instClass, p, msg); + return NOT_HANDLED; } /** @@ -333,8 +315,6 @@ public Object handleMissingInstantiator(DeserializationContext ctxt, * @return Actual type to use, if resolved; `null` if handler does not know what * to do; or `Void.class` to indicate that nothing should be deserialized for * type with the id (which caller may choose to do... or not) - * - * @since 2.8 */ public JavaType handleUnknownTypeId(DeserializationContext ctxt, JavaType baseType, String subTypeId, TypeIdResolver idResolver, @@ -369,8 +349,6 @@ public JavaType handleUnknownTypeId(DeserializationContext ctxt, * @return Actual type to use, if resolved; `null` if handler does not know what * to do; or `Void.class` to indicate that nothing should be deserialized for * type with the id (which caller may choose to do... or not) - * - * @since 2.9 */ public JavaType handleMissingTypeId(DeserializationContext ctxt, JavaType baseType, TypeIdResolver idResolver, @@ -379,22 +357,4 @@ public JavaType handleMissingTypeId(DeserializationContext ctxt, { return null; } - - /* - /********************************************************** - /* Deprecated - /********************************************************** - */ - - /** - * @since 2.8 - * @deprecated Since 2.9: use variant that takes {@link ValueInstantiator} - */ - @Deprecated - public Object handleMissingInstantiator(DeserializationContext ctxt, - Class instClass, JsonParser p, String msg) - throws IOException - { - return NOT_HANDLED; - } } diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/DeserializerCache.java b/src/main/java/com/fasterxml/jackson/databind/deser/DeserializerCache.java index 8aff0256a9..f055e070cb 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/DeserializerCache.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/DeserializerCache.java @@ -1,80 +1,88 @@ package com.fasterxml.jackson.databind.deser; import java.util.HashMap; -import java.util.concurrent.ConcurrentHashMap; import com.fasterxml.jackson.annotation.JsonFormat; + import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.deser.std.StdDelegatingDeserializer; +import com.fasterxml.jackson.databind.cfg.MapperConfig; +import com.fasterxml.jackson.databind.deser.std.StdConvertingDeserializer; import com.fasterxml.jackson.databind.introspect.Annotated; 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.SimpleLookupCache; /** - * Class that defines caching layer between callers (like - * {@link ObjectMapper}, + * Class that defines caching layer between callers (like {@link ObjectMapper}, * {@link com.fasterxml.jackson.databind.DeserializationContext}) * and classes that construct deserializers * ({@link com.fasterxml.jackson.databind.deser.DeserializerFactory}). */ public final class DeserializerCache - implements java.io.Serializable // since 2.1 -- needs to be careful tho + implements java.io.Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 3L; + + /** + * By default allow caching of up to 4000 deserializers. + */ + public final static int DEFAULT_MAX_CACHED = 4000; /* - /********************************************************** + /********************************************************************** /* Caching - /********************************************************** + /********************************************************************** */ /** * We will also cache some dynamically constructed deserializers; * specifically, ones that are expensive to construct. - * This currently means bean and Enum deserializers; starting with - * 2.5, container deserializers will also be cached. - *

    - * Given that we don't expect much concurrency for additions - * (should very quickly converge to zero after startup), let's - * define a relatively low concurrency setting. + * This currently (3.0) means POJO, Enum and Container (collection, + * map) deserializers. */ - final protected ConcurrentHashMap> _cachedDeserializers - = new ConcurrentHashMap>(64, 0.75f, 4); + private final SimpleLookupCache> _cachedDeserializers; /** * During deserializer construction process we may need to keep track of partially * completed deserializers, to resolve cyclic dependencies. This is the * map used for storing deserializers before they are fully complete. */ - final protected HashMap> _incompleteDeserializers - = new HashMap>(8); + private final transient HashMap> _incompleteDeserializers + = new HashMap<>(8); /* - /********************************************************** + /********************************************************************** /* Life-cycle - /********************************************************** + /********************************************************************** */ - public DeserializerCache() { } + public DeserializerCache() { this(DEFAULT_MAX_CACHED); } + + public DeserializerCache(int maxSize) { + int initial = Math.min(64, maxSize>>2); + _cachedDeserializers = new SimpleLookupCache<>(initial, maxSize); + } + + private DeserializerCache(DeserializerCache src) { + _cachedDeserializers = src._cachedDeserializers; + } /* - /********************************************************** + /********************************************************************** /* JDK serialization handling - /********************************************************** + /********************************************************************** */ - Object writeReplace() { - // instead of making this transient, just clear it: - _incompleteDeserializers.clear(); - // TODO: clear out "cheap" cached deserializers? - return this; + // Need to re-create just to initialize `transient` fields + protected Object readResolve() { + return new DeserializerCache(this); } - + /* - /********************************************************** + /********************************************************************** /* Access to caching aspects - /********************************************************** + /********************************************************************** */ /** @@ -105,9 +113,9 @@ public void flushCachedDeserializers() { } /* - /********************************************************** + /********************************************************************** /* General deserializer locating method - /********************************************************** + /********************************************************************** */ /** @@ -120,8 +128,8 @@ public void flushCachedDeserializers() { * Key deserializers can be accessed using {@link #findKeyDeserializer}. *

    * Note also that deserializer returned is guaranteed to be resolved - * (if it is of type {@link ResolvableDeserializer}), but - * not contextualized (wrt {@link ContextualDeserializer}): caller + * (see {@link JsonDeserializer#resolve}), but + * not contextualized (wrt {@link JsonDeserializer#createContextual}): caller * has to handle latter if necessary. * * @param ctxt Deserialization context @@ -168,35 +176,34 @@ public KeyDeserializer findKeyDeserializer(DeserializationContext ctxt, return _handleUnknownKeyDeserializer(ctxt, type); } // First: need to resolve? - if (kd instanceof ResolvableDeserializer) { - ((ResolvableDeserializer) kd).resolve(ctxt); - } + kd.resolve(ctxt); return kd; } + // as per [databind#1917], not needed any more: /** * Method called to find out whether provider would be able to find * a deserializer for given type, using a root reference (i.e. not * through fields or membership in an array or collection) - */ public boolean hasValueDeserializerFor(DeserializationContext ctxt, DeserializerFactory factory, JavaType type) throws JsonMappingException { - /* Note: mostly copied from findValueDeserializer, except for - * handling of unknown types - */ + // Note: mostly copied from findValueDeserializer, except for + // handling of unknown types JsonDeserializer deser = _findCachedDeserializer(type); if (deser == null) { deser = _createAndCacheValueDeserializer(ctxt, factory, type); } return (deser != null); } +*/ + /* - /********************************************************** + /********************************************************************** /* Helper methods that handle cache lookups - /********************************************************** + /********************************************************************** */ protected JsonDeserializer _findCachedDeserializer(JavaType type) @@ -288,9 +295,10 @@ protected JsonDeserializer _createAndCache2(DeserializationContext ctxt, /* Need to resolve? Mostly done for bean deserializers; required for * resolving cyclic references. */ - if (deser instanceof ResolvableDeserializer) { - _incompleteDeserializers.put(type, deser); - ((ResolvableDeserializer)deser).resolve(ctxt); + _incompleteDeserializers.put(type, deser); + try { + deser.resolve(ctxt); + } finally { _incompleteDeserializers.remove(type); } if (addToCache) { @@ -300,9 +308,9 @@ protected JsonDeserializer _createAndCache2(DeserializationContext ctxt, } /* - /********************************************************** + /********************************************************************** /* Helper methods for actual construction of deserializers - /********************************************************** + /********************************************************************** */ /** @@ -319,9 +327,9 @@ protected JsonDeserializer _createDeserializer(DeserializationContext ct // First things first: do we need to use abstract type mapping? if (type.isAbstract() || type.isMapLikeType() || type.isCollectionLikeType()) { - type = factory.mapAbstractType(config, type); + type = config.mapAbstractType(type); } - BeanDescription beanDesc = config.introspect(type); + BeanDescription beanDesc = ctxt.introspect(type); // Then: does type define explicit deserializer to use, with annotation(s)? JsonDeserializer deser = findDeserializerFromAnnotation(ctxt, beanDesc.getClassInfo()); @@ -333,7 +341,7 @@ protected JsonDeserializer _createDeserializer(DeserializationContext ct JavaType newType = modifyTypeByAnnotation(ctxt, beanDesc.getClassInfo(), type); if (newType != type) { type = newType; - beanDesc = config.introspect(newType); + beanDesc = ctxt.introspect(newType); } // We may also have a Builder type to consider... @@ -352,9 +360,9 @@ protected JsonDeserializer _createDeserializer(DeserializationContext ct JavaType delegateType = conv.getInputType(ctxt.getTypeFactory()); // One more twist, as per [databind#288]; probably need to get new BeanDesc if (!delegateType.hasRawClass(type.getRawClass())) { - beanDesc = config.introspect(delegateType); + beanDesc = ctxt.introspect(delegateType); } - return new StdDelegatingDeserializer(conv, delegateType, + return new StdConvertingDeserializer(conv, delegateType, _createDeserializer2(ctxt, factory, delegateType, beanDesc)); } @@ -377,8 +385,8 @@ protected JsonDeserializer _createDeserializer2(DeserializationContext ctxt, // Ideally we'd determine it bit later on (to allow custom handler checks) // but that won't work for other reasons. So do it here. // (read: rewrite for 3.0) - JsonFormat.Value format = beanDesc.findExpectedFormat(null); - if ((format == null) || format.getShape() != JsonFormat.Shape.OBJECT) { + JsonFormat.Value format = beanDesc.findExpectedFormat(type.getRawClass()); + if (format.getShape() != JsonFormat.Shape.POJO) { MapLikeType mlt = (MapLikeType) type; if (mlt.isTrueMapType()) { return factory.createMapDeserializer(ctxt,(MapType) mlt, beanDesc); @@ -387,13 +395,12 @@ protected JsonDeserializer _createDeserializer2(DeserializationContext ctxt, } } if (type.isCollectionLikeType()) { - /* 03-Aug-2012, tatu: As per [databind#40], one exception is if shape - * is to be Shape.OBJECT. Ideally we'd determine it bit later on - * (to allow custom handler checks), but that won't work for other - * reasons. So do it here. + /* One exception is if shape is to be Shape.POJO (or, as alias, OBJECT). + * Ideally we'd determine it bit later on (to allow custom handler checks), + * but that won't work for other reasons. So do it here. */ - JsonFormat.Value format = beanDesc.findExpectedFormat(null); - if ((format == null) || format.getShape() != JsonFormat.Shape.OBJECT) { + JsonFormat.Value format = beanDesc.findExpectedFormat(type.getRawClass()); + if (format.getShape() != JsonFormat.Shape.POJO) { CollectionLikeType clt = (CollectionLikeType) type; if (clt.isTrueCollectionType()) { return factory.createCollectionDeserializer(ctxt, (CollectionType) clt, beanDesc); @@ -420,7 +427,7 @@ protected JsonDeserializer findDeserializerFromAnnotation(Deserializatio Annotated ann) throws JsonMappingException { - Object deserDef = ctxt.getAnnotationIntrospector().findDeserializer(ann); + Object deserDef = ctxt.getAnnotationIntrospector().findDeserializer(ctxt.getConfig(), ann); if (deserDef == null) { return null; } @@ -444,14 +451,14 @@ protected JsonDeserializer findConvertingDeserializer(DeserializationCon return deser; } JavaType delegateType = conv.getInputType(ctxt.getTypeFactory()); - return (JsonDeserializer) new StdDelegatingDeserializer(conv, delegateType, deser); + return (JsonDeserializer) new StdConvertingDeserializer(conv, delegateType, deser); } protected Converter findConverter(DeserializationContext ctxt, Annotated a) throws JsonMappingException { - Object convDef = ctxt.getAnnotationIntrospector().findDeserializationConverter(a); + Object convDef = ctxt.getAnnotationIntrospector().findDeserializationConverter(ctxt.getConfig(), a); if (convDef == null) { return null; } @@ -481,6 +488,7 @@ private JavaType modifyTypeByAnnotation(DeserializationContext ctxt, if (intr == null) { return type; } + final MapperConfig config = ctxt.getConfig(); // First things first: find explicitly annotated deserializer(s) @@ -491,7 +499,7 @@ private JavaType modifyTypeByAnnotation(DeserializationContext ctxt, // (not 100% why or how, but this does seem to get called more than once, which // is not good: for now, let's just avoid errors) if (keyType != null && keyType.getValueHandler() == null) { - Object kdDef = intr.findKeyDeserializer(a); + Object kdDef = intr.findKeyDeserializer(config, a); if (kdDef != null) { KeyDeserializer kd = ctxt.keyDeserializerInstance(a, kdDef); if (kd != null) { @@ -504,7 +512,7 @@ private JavaType modifyTypeByAnnotation(DeserializationContext ctxt, JavaType contentType = type.getContentType(); if (contentType != null) { if (contentType.getValueHandler() == null) { // as with above, avoid resetting (which would trigger exception) - Object cdDef = intr.findContentDeserializer(a); + Object cdDef = intr.findContentDeserializer(config, a); if (cdDef != null) { JsonDeserializer cd = null; if (cdDef instanceof JsonDeserializer) { @@ -524,15 +532,15 @@ private JavaType modifyTypeByAnnotation(DeserializationContext ctxt, // And after handlers, possible type refinements // (note: could possibly avoid this if explicit deserializer was invoked?) - type = intr.refineDeserializationType(ctxt.getConfig(), a, type); + type = intr.refineDeserializationType(config, a, type); return type; } /* - /********************************************************** + /********************************************************************** /* Helper methods, other - /********************************************************** + /********************************************************************** */ /** @@ -577,9 +585,9 @@ private Class _verifyAsClass(Object src, String methodName, Class noneClas } /* - /********************************************************** - /* Overridable error reporting methods - /********************************************************** + /********************************************************************** + /* Error reporting methods + /********************************************************************** */ protected JsonDeserializer _handleUnknownValueDeserializer(DeserializationContext ctxt, JavaType type) diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/DeserializerFactory.java b/src/main/java/com/fasterxml/jackson/databind/deser/DeserializerFactory.java index d063852091..9eb7bb5309 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/DeserializerFactory.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/DeserializerFactory.java @@ -1,7 +1,6 @@ package com.fasterxml.jackson.databind.deser; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; import com.fasterxml.jackson.databind.type.*; /** @@ -43,56 +42,11 @@ public abstract class DeserializerFactory protected final static Deserializers[] NO_DESERIALIZERS = new Deserializers[0]; /* - /******************************************************** - /* Configuration handling - /******************************************************** + /********************************************************************** + /* Basic DeserializerFactory API + /********************************************************************** */ - /** - * Convenience method for creating a new factory instance with additional deserializer - * provider. - */ - public abstract DeserializerFactory withAdditionalDeserializers(Deserializers additional); - - /** - * Convenience method for creating a new factory instance with additional - * {@link KeyDeserializers}. - */ - public abstract DeserializerFactory withAdditionalKeyDeserializers(KeyDeserializers additional); - - /** - * Convenience method for creating a new factory instance with additional - * {@link BeanDeserializerModifier}. - */ - public abstract DeserializerFactory withDeserializerModifier(BeanDeserializerModifier modifier); - - /** - * Convenience method for creating a new factory instance with additional - * {@link AbstractTypeResolver}. - */ - public abstract DeserializerFactory withAbstractTypeResolver(AbstractTypeResolver resolver); - - /** - * Convenience method for creating a new factory instance with additional - * {@link ValueInstantiators}. - */ - public abstract DeserializerFactory withValueInstantiators(ValueInstantiators instantiators); - - /* - /********************************************************** - /* Basic DeserializerFactory API: - /********************************************************** - */ - - /** - * Method that can be called to try to resolve an abstract type - * (interface, abstract class) into a concrete type, or at least - * something "more concrete" (abstract class instead of interface). - * Will either return passed type, or a more specific type. - */ - public abstract JavaType mapAbstractType(DeserializationConfig config, JavaType type) - throws JsonMappingException; - /** * Method that is to find all creators (constructors, factory methods) * for the bean type to deserialize. @@ -131,9 +85,6 @@ public abstract JsonDeserializer createEnumDeserializer(DeserializationContex JavaType type, BeanDescription beanDesc) throws JsonMappingException; - /** - * @since 2.7 - */ public abstract JsonDeserializer createReferenceDeserializer(DeserializationContext ctxt, ReferenceType type, BeanDescription beanDesc) throws JsonMappingException; @@ -185,20 +136,33 @@ public abstract KeyDeserializer createKeyDeserializer(DeserializationContext ctx JavaType type) throws JsonMappingException; + /* + /********************************************************************** + /* Mutant factories for registering additional configuration + /********************************************************************** + */ + /** - * Method called to find and create a type information deserializer for given base type, - * if one is needed. If not needed (no polymorphic handling configured for type), - * should return null. - *

    - * Note that this method is usually only directly called for values of container (Collection, - * array, Map) types and root values, but not for bean property values. - * - * @param baseType Declared base type of the value to deserializer (actual - * deserializer type will be this type or its subtype) - * - * @return Type deserializer to use for given base type, if one is needed; null if not. + * Convenience method for creating a new factory instance with additional deserializer + * provider. */ - public abstract TypeDeserializer findTypeDeserializer(DeserializationConfig config, - JavaType baseType) - throws JsonMappingException; + public abstract DeserializerFactory withAdditionalDeserializers(Deserializers additional); + + /** + * Convenience method for creating a new factory instance with additional + * {@link KeyDeserializers}. + */ + public abstract DeserializerFactory withAdditionalKeyDeserializers(KeyDeserializers additional); + + /** + * Convenience method for creating a new factory instance with additional + * {@link BeanDeserializerModifier}. + */ + public abstract DeserializerFactory withDeserializerModifier(BeanDeserializerModifier modifier); + + /** + * Convenience method for creating a new factory instance with additional + * {@link ValueInstantiators}. + */ + public abstract DeserializerFactory withValueInstantiators(ValueInstantiators instantiators); } diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/Deserializers.java b/src/main/java/com/fasterxml/jackson/databind/deser/Deserializers.java index 360a08fbc3..4fd09db080 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/Deserializers.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/Deserializers.java @@ -100,8 +100,8 @@ public JsonDeserializer findReferenceDeserializer(ReferenceType refType, * the type information deserializer to use; should usually be used as is when constructing * array deserializer. * @param elementDeserializer Deserializer to use for elements, if explicitly defined (by using - * annotations, for exmple). May be null, in which case it should be resolved here (or using - * {@link ResolvableDeserializer} callback) + * annotations, for example). May be null, in which case it will need to be resolved + * by deserializer at a later point. * * @return Deserializer to use for the type; or null if this provider does not know how to construct it */ @@ -110,7 +110,6 @@ public JsonDeserializer findArrayDeserializer(ArrayType type, TypeDeserializer elementTypeDeserializer, JsonDeserializer elementDeserializer) throws JsonMappingException; - /** * Method called to locate serializer for specified {@link java.util.Collection} (List, Set etc) type. *

    @@ -127,8 +126,8 @@ public JsonDeserializer findArrayDeserializer(ArrayType type, * the type information deserializer to use; should usually be used as is when constructing * array deserializer. * @param elementDeserializer Deserializer to use for elements, if explicitly defined (by using - * annotations, for exmple). May be null, in which case it should be resolved here (or using - * {@link ResolvableDeserializer} callback) + * annotations, for example). May be null, in which case it will need to be resolved + * by deserializer at a later point. * * @return Deserializer to use for the type; or null if this provider does not know how to construct it */ @@ -155,8 +154,8 @@ public JsonDeserializer findCollectionDeserializer(CollectionType type, * the type information deserializer to use; should usually be used as is when constructing * array deserializer. * @param elementDeserializer Deserializer to use for elements, if explicitly defined (by using - * annotations, for exmple). May be null, in which case it should be resolved here (or using - * {@link ResolvableDeserializer} callback) + * annotations, for example). May be null, in which case it will need to be resolved + * by deserializer at a later point. * * @return Deserializer to use for the type; or null if this provider does not know how to construct it */ @@ -176,7 +175,7 @@ public JsonDeserializer findCollectionLikeDeserializer(CollectionLikeType typ * Similarly, a {@link KeyDeserializer} may be passed, but this is only done if there is * a specific configuration override (annotations) to indicate instance to use. * Otherwise null is passed, and key deserializer needs to be obtained later during - * resolution (using {@link ResolvableDeserializer#resolve}). + * resolution of map serializer constructed here. * * @param type Type of {@link java.util.Map} instances to deserialize * @param config Configuration in effect @@ -188,8 +187,8 @@ public JsonDeserializer findCollectionLikeDeserializer(CollectionLikeType typ * the type information deserializer to use; should usually be used as is when constructing * array deserializer. * @param elementDeserializer Deserializer to use for elements, if explicitly defined (by using - * annotations, for exmple). May be null, in which case it should be resolved here (or using - * {@link ResolvableDeserializer} callback) + * annotations, for example). May be null, in which case it will need to be resolved + * by deserializer at a later point. * * @return Deserializer to use for the type; or null if this provider does not know how to construct it */ @@ -212,7 +211,7 @@ public JsonDeserializer findMapDeserializer(MapType type, * Similarly, a {@link KeyDeserializer} may be passed, but this is only done if there is * a specific configuration override (annotations) to indicate instance to use. * Otherwise null is passed, and key deserializer needs to be obtained later during - * resolution (using {@link ResolvableDeserializer#resolve}). + * resolution, by deserializer constructed here. * * @param type Type of {@link java.util.Map} instances to deserialize * @param config Configuration in effect @@ -224,8 +223,8 @@ public JsonDeserializer findMapDeserializer(MapType type, * the type information deserializer to use; should usually be used as is when constructing * array deserializer. * @param elementDeserializer Deserializer to use for elements, if explicitly defined (by using - * annotations, for exmple). May be null, in which case it should be resolved here (or using - * {@link ResolvableDeserializer} callback) + * annotations, for example). May be null, in which case it will need to be resolved + * by deserializer at a later point. * * @return Deserializer to use for the type; or null if this provider does not know how to construct it */ diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/ResolvableDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/ResolvableDeserializer.java index 0d97b2d0de..d4720773ee 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/ResolvableDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/ResolvableDeserializer.java @@ -3,7 +3,7 @@ import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonMappingException; -/** +/* * Interface used to indicate deserializers that want to do post-processing * after construction but before being returned to caller (and possibly cached) * and used. @@ -27,19 +27,17 @@ * resolution -- that is, implements both this interface and {@link ContextualDeserializer} * -- resolution via this interface occurs first, and contextual * resolution (using {@link ContextualDeserializer}) later on. + * + * @deprecated Since 3.0: method demoted to JsonSerializer */ +/** + * Leftover interface from 2.x: method now merged in JsonSerializer + * + * @deprecated Since 3.0: method demoted to JsonSerializer +*/ +@Deprecated public interface ResolvableDeserializer { - /** - * Method called after deserializer instance has been constructed - * (and registered as necessary by provider objects), - * but before it has returned it to the caller. - * Called object can then resolve its dependencies to other types, - * including self-references (direct or indirect). - * - * @param ctxt Context to use for accessing configuration, resolving - * secondary deserializers - */ public abstract void resolve(DeserializationContext ctxt) throws JsonMappingException; } 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 465562eed9..9d79e25a4d 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/SettableAnyProperty.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/SettableAnyProperty.java @@ -43,10 +43,6 @@ public class SettableAnyProperty protected JsonDeserializer _valueDeserializer; protected final TypeDeserializer _valueTypeDeserializer; - - /** - * @since 2.9 - */ protected final KeyDeserializer _keyDeserializer; /* @@ -68,13 +64,6 @@ public SettableAnyProperty(BeanProperty property, AnnotatedMember setter, JavaTy _setterIsField = setter instanceof AnnotatedField; } - @Deprecated // since 2.9 - public SettableAnyProperty(BeanProperty property, AnnotatedMember setter, JavaType type, - JsonDeserializer valueDeser, TypeDeserializer typeDeser) - { - this(property, setter, type, null, valueDeser, typeDeser); - } - public SettableAnyProperty withValueDeserializer(JsonDeserializer deser) { return new SettableAnyProperty(_property, _setter, _type, _keyDeserializer, deser, _valueTypeDeserializer); @@ -133,7 +122,7 @@ public final void deserializeAndSet(JsonParser p, DeserializationContext ctxt, : _keyDeserializer.deserializeKey(propName, ctxt); set(instance, key, deserialize(p, ctxt)); } catch (UnresolvedForwardReference reference) { - if (!(_valueDeserializer.getObjectIdReader() != null)) { + if (_valueDeserializer.getObjectIdReader(ctxt) == null) { throw JsonMappingException.from(p, "Unresolved forward reference but no identity info.", reference); } AnySetterReferring referring = new AnySetterReferring(this, reference, @@ -144,7 +133,7 @@ public final void deserializeAndSet(JsonParser p, DeserializationContext ctxt, public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == JsonToken.VALUE_NULL) { return _valueDeserializer.getNullValue(ctxt); } 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 bd0c044b1e..ae4225abea 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/SettableBeanProperty.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/SettableBeanProperty.java @@ -30,8 +30,6 @@ public abstract class SettableBeanProperty /** * To avoid nasty NPEs, let's use a placeholder for _valueDeserializer, * if real deserializer is not (yet) available. - * - * @since 2.2 */ protected static final JsonDeserializer MISSING_VALUE_DESERIALIZER = new FailingDeserializer( "No _valueDeserializer assigned"); @@ -47,9 +45,6 @@ public abstract class SettableBeanProperty */ protected final JavaType _type; - /** - * @since 2.2 - */ protected final PropertyName _wrapperName; /** @@ -61,8 +56,6 @@ public abstract class SettableBeanProperty /** * Deserializer used for handling property value. - *

    - * NOTE: has been immutable since 2.3 */ protected final JsonDeserializer _valueDeserializer; @@ -77,8 +70,6 @@ public abstract class SettableBeanProperty * Entity used for possible translation from `null` into non-null * value of type of this property. * Often same as _valueDeserializer, but not always. - * - * @since 2.9 */ protected final NullValueProvider _nullProvider; @@ -168,8 +159,6 @@ protected SettableBeanProperty(PropertyName propName, JavaType type, PropertyNam /** * Constructor only used by {@link com.fasterxml.jackson.databind.deser.impl.ObjectIdValueProperty}. - * - * @since 2.3 */ protected SettableBeanProperty(PropertyName propName, JavaType type, PropertyMetadata metadata, JsonDeserializer valueDeser) @@ -280,12 +269,15 @@ protected SettableBeanProperty(SettableBeanProperty src, PropertyName newName) */ public abstract SettableBeanProperty withName(PropertyName newName); - /** - * @since 2.3 - */ public SettableBeanProperty withSimpleName(String simpleName) { - PropertyName n = (_propName == null) - ? new PropertyName(simpleName) : _propName.withSimpleName(simpleName); + PropertyName n; + + if (_propName == null) { + n = new PropertyName(simpleName); + } else { + n = _propName.withSimpleName(simpleName); + } + n = n.internSimpleName(); return (n == _propName) ? this : withName(n); } @@ -315,7 +307,9 @@ public void setViews(Class[] views) { */ public void assignIndex(int index) { if (_propertyIndex != -1) { - throw new IllegalStateException("Property '"+getName()+"' already had index ("+_propertyIndex+"), trying to assign "+index); + if (_propertyIndex != index) { + throw new IllegalStateException("Property '"+getName()+"' already had index ("+_propertyIndex+"), trying to assign "+index); + } } _propertyIndex = index; } @@ -598,10 +592,7 @@ protected void _throwAsIOE(JsonParser p, Exception e, Object value) throws IOExc } _throwAsIOE(p, e); } - - /** - * @since 2.7 - */ + protected IOException _throwAsIOE(JsonParser p, Exception e) throws IOException { ClassUtil.throwIfIOE(e); @@ -611,11 +602,6 @@ protected IOException _throwAsIOE(JsonParser p, Exception e) throws IOException throw JsonMappingException.from(p, ClassUtil.exceptionMessage(th), th); } - @Deprecated // since 2.7 - protected IOException _throwAsIOE(Exception e) throws IOException { - return _throwAsIOE((JsonParser) null, e); - } - // 10-Oct-2015, tatu: _Should_ be deprecated, too, but its remaining // callers cannot actually provide a JsonParser protected void _throwAsIOE(Exception e, Object value) throws IOException { @@ -637,8 +623,6 @@ protected void _throwAsIOE(Exception e, Object value) throws IOException { *

    * Class was specifically added to help with {@code Afterburner} * module, but its use is not limited to only support it. - * - * @since 2.9 */ public static abstract class Delegating extends SettableBeanProperty diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/UnresolvedForwardReference.java b/src/main/java/com/fasterxml/jackson/databind/deser/UnresolvedForwardReference.java index 50ace58d41..2be01db2ae 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/UnresolvedForwardReference.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/UnresolvedForwardReference.java @@ -13,48 +13,23 @@ /** * Exception thrown during deserialization when there are object id that can't * be resolved. - * - * @author pgelinas */ -public class UnresolvedForwardReference extends JsonMappingException { +public class UnresolvedForwardReference extends JsonMappingException +{ private static final long serialVersionUID = 1L; private ReadableObjectId _roid; private List _unresolvedIds; - /** - * @since 2.7 - */ public UnresolvedForwardReference(JsonParser p, String msg, JsonLocation loc, ReadableObjectId roid) { super(p, msg, loc); _roid = roid; } - /** - * @since 2.7 - */ public UnresolvedForwardReference(JsonParser p, String msg) { super(p, msg); _unresolvedIds = new ArrayList(); } - /** - * @deprecated Since 2.7 - */ - @Deprecated // since 2.7 - public UnresolvedForwardReference(String msg, JsonLocation loc, ReadableObjectId roid) { - super(msg, loc); - _roid = roid; - } - - /** - * @deprecated Since 2.7 - */ - @Deprecated // since 2.7 - public UnresolvedForwardReference(String msg) { - super(msg); - _unresolvedIds = new ArrayList(); - } - /* /********************************************************** /* Accessor methods diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/ValueInstantiator.java b/src/main/java/com/fasterxml/jackson/databind/deser/ValueInstantiator.java index a7a6951685..94053b95cd 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/ValueInstantiator.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/ValueInstantiator.java @@ -4,7 +4,6 @@ import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.deser.impl.PropertyValueBuffer; -import com.fasterxml.jackson.databind.introspect.AnnotatedParameter; import com.fasterxml.jackson.databind.introspect.AnnotatedWithParams; /** @@ -31,36 +30,35 @@ public abstract class ValueInstantiator { /* - /********************************************************** - /* Metadata accessors - /********************************************************** + /********************************************************************** + /* Introspection + /********************************************************************** */ /** - * Accessor for raw (type-erased) type of instances to create. - *

    - * NOTE: since this method has not existed since beginning of - * Jackson 2.0 series, default implementation will just return - * Object.class; implementations are expected - * to override it with real value. - * - * @since 2.8 - */ - public Class getValueClass() { - return Object.class; + * Tag-on interface to let deserializers indicate that they make use of + * {@link ValueInstantiator}s and there is access for instantiator assigned. + */ + public interface Gettable { + public ValueInstantiator getValueInstantiator(); } + + /* + /********************************************************************** + /* Metadata accessors + /********************************************************************** + */ /** + * Accessor for raw (type-erased) type of instances to create. + */ + public abstract Class getValueClass(); + + /** * Method that returns description of the value type this instantiator * handles. Used for error messages, diagnostics. */ - public String getValueTypeDesc() { - Class cls = getValueClass(); - if (cls == null) { - return "UNKNOWN"; - } - return cls.getName(); - } + public abstract String getValueTypeDesc(); /** * Method that will return true if any of canCreateXxx method @@ -123,8 +121,6 @@ public boolean canInstantiate() { * Method that can be called to check whether a array-delegate-based creator * (single-arg constructor or factory method) * is available for this instantiator - * - * @since 2.7 */ public boolean canCreateUsingArrayDelegate() { return false; } @@ -144,8 +140,11 @@ public boolean canInstantiate() { *

    * NOTE: all properties will be of type * {@link com.fasterxml.jackson.databind.deser.CreatorProperty}. + *

    + * NOTE: since 3.0, gets passed full {@link DeserializationContext}, + * not just DeserializationConfig */ - public SettableBeanProperty[] getFromObjectArguments(DeserializationConfig config) { + public SettableBeanProperty[] getFromObjectArguments(DeserializationContext ctxt) { return null; } @@ -164,15 +163,13 @@ public SettableBeanProperty[] getFromObjectArguments(DeserializationConfig confi * non-null type is returned, deserializer will bind JSON into specified * type (using standard deserializer for that type), and pass that to * instantiator. - * - * @since 2.7 */ public JavaType getArrayDelegateType(DeserializationConfig config) { return null; } /* - /********************************************************** + /********************************************************************** /* Instantiation methods for JSON Object - /********************************************************** + /********************************************************************** */ /** @@ -219,8 +216,6 @@ public Object createFromObjectWith(DeserializationContext ctxt, Object[] args) t * {@link PropertyValueBuffer#getParameter(SettableBeanProperty)} to safely * read the present properties only, and to have some other behavior for the * missing properties. - * - * @since 2.8 */ public Object createFromObjectWith(DeserializationContext ctxt, SettableBeanProperty[] props, PropertyValueBuffer buffer) @@ -248,10 +243,9 @@ public Object createUsingArrayDelegate(DeserializationContext ctxt, Object deleg } /* - /********************************************************** - /* Instantiation methods for JSON scalar types - /* (String, Number, Boolean) - /********************************************************** + /********************************************************************** + /* Instantiation methods for JSON scalar types (String, Number, Boolean) + /********************************************************************** */ public Object createFromString(DeserializationContext ctxt, String value) throws IOException { @@ -283,9 +277,9 @@ public Object createFromBoolean(DeserializationContext ctxt, boolean value) thro } /* - /********************************************************** + /********************************************************************** /* Accessors for underlying creator objects (optional) - /********************************************************** + /********************************************************************** */ /** @@ -331,21 +325,12 @@ public Object createFromBoolean(DeserializationContext ctxt, boolean value) thro */ public AnnotatedWithParams getWithArgsCreator() { return null; } - /** - * If an incomplete creator was found, this is the first parameter that - * needs further annotation to help make the creator complete. - */ - public AnnotatedParameter getIncompleteParameter() { return null; } - /* - /********************************************************** + /********************************************************************** /* Helper methods - /********************************************************** + /********************************************************************** */ - /** - * @since 2.4 (demoted from StdValueInstantiator) - */ protected Object _createFromStringFallbacks(DeserializationContext ctxt, String value) throws IOException { @@ -374,22 +359,28 @@ protected Object _createFromStringFallbacks(DeserializationContext ctxt, String } /* - /********************************************************** - /* Introspection - /********************************************************** + /********************************************************************** + /* Std method overrides for testing + /********************************************************************** */ - /** - * @since 2.9 - */ - public interface Gettable { - public ValueInstantiator getValueInstantiator(); + /* + @Override + public String toString() { + return String.format( +"(StdValueInstantiator: default=%s, delegate=%s, props=%s; str/int/long/double/boolean = %s/%s/%s/%s/%s)", + canCreateUsingDefault(), canCreateUsingDelegate() + , canCreateFromObjectWith(), canCreateFromString() + , canCreateFromInt(), canCreateFromLong() + , canCreateFromDouble(), canCreateFromBoolean() + ); } +*/ /* - /********************************************************** - /* Standard Base implementation (since 2.8) - /********************************************************** + /********************************************************************** + /* Standard Base implementation + /********************************************************************** */ /** @@ -407,7 +398,7 @@ public Base(Class type) { public Base(JavaType type) { _valueType = type.getRawClass(); } - + @Override public String getValueTypeDesc() { return _valueType.getName(); diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayBuilderDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayBuilderDeserializer.java index 0dbc50da32..93084cc79a 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayBuilderDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayBuilderDeserializer.java @@ -29,8 +29,6 @@ public class BeanAsArrayBuilderDeserializer /** * Type that the builder will produce, target type; as opposed to * `handledType()` which refers to Builder class. - * - * @since 2.9 */ protected final JavaType _targetType; @@ -44,8 +42,6 @@ public class BeanAsArrayBuilderDeserializer * Main constructor used both for creating new instances (by * {@link BeanDeserializer#asArrayDeserializer}) and for * creating copies with different delegate. - * - * @since 2.9 */ public BeanAsArrayBuilderDeserializer(BeanDeserializerBase delegate, JavaType targetType, @@ -60,13 +56,12 @@ public BeanAsArrayBuilderDeserializer(BeanDeserializerBase delegate, } @Override - public JsonDeserializer unwrappingDeserializer(NameTransformer unwrapper) + public JsonDeserializer unwrappingDeserializer(DeserializationContext ctxt, + NameTransformer unwrapper) { - /* We can't do much about this; could either replace _delegate - * with unwrapping instance, or just replace this one. Latter seems - * more sensible. - */ - return _delegate.unwrappingDeserializer(unwrapper); + // We can't do much about this; could either replace _delegate with unwrapping instance, + // or just replace this one. Latter seems more sensible. + return _delegate.unwrappingDeserializer(ctxt, unwrapper); } @Override @@ -92,6 +87,9 @@ protected BeanDeserializerBase asArrayDeserializer() { return this; } + @Override + protected void initFieldMatcher(DeserializationContext ctxt) { } + /* /********************************************************** /* Overrides @@ -147,7 +145,7 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt) try { builder = prop.deserializeSetAndReturn(p, ctxt, builder); } catch (Exception e) { - wrapAndThrow(e, builder, prop.getName(), ctxt); + throw wrapAndThrow(e, builder, prop.getName(), ctxt); } } else { // just skip? p.skipChildren(); @@ -225,7 +223,7 @@ protected Object _deserializeNonVanilla(JsonParser p, DeserializationContext ctx try { prop.deserializeSetAndReturn(p, ctxt, builder); } catch (Exception e) { - wrapAndThrow(e, builder, prop.getName(), ctxt); + throw wrapAndThrow(e, builder, prop.getName(), ctxt); } continue; } @@ -284,7 +282,7 @@ protected final Object _deserializeUsingPropertyBased(final JsonParser p, try { builder = prop.deserializeSetAndReturn(p, ctxt, builder); } catch (Exception e) { - wrapAndThrow(e, builder, prop.getName(), ctxt); + throw wrapAndThrow(e, builder, prop.getName(), ctxt); } continue; } @@ -297,8 +295,7 @@ protected final Object _deserializeUsingPropertyBased(final JsonParser p, try { builder = creator.build(ctxt, buffer); } catch (Exception e) { - wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt); - continue; // never gets here + throw wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt); } // polymorphic? if (builder.getClass() != _beanType.getRawClass()) { @@ -342,11 +339,11 @@ protected Object _deserializeFromNonArray(JsonParser p, DeserializationContext c throws IOException { // Let's start with failure - return ctxt.handleUnexpectedToken(handledType(), p.getCurrentToken(), p, + return ctxt.handleUnexpectedToken(handledType(), p.currentToken(), p, "Cannot deserialize a POJO (of type %s) from non-Array representation (token: %s): " +"type/property designed to be serialized as JSON Array", _beanType.getRawClass().getName(), - p.getCurrentToken()); + p.currentToken()); // in future, may allow use of "standard" POJO serialization as well; if so, do: //return _delegate.deserialize(p, ctxt); } diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayDeserializer.java index 2b39004b63..3ac0dbd7cb 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayDeserializer.java @@ -12,8 +12,6 @@ /** * Variant of {@link BeanDeserializer} used for handling deserialization * of POJOs when serialized as JSON Arrays, instead of JSON Objects. - * - * @since 2.1 */ public class BeanAsArrayDeserializer extends BeanDeserializerBase @@ -50,13 +48,12 @@ public BeanAsArrayDeserializer(BeanDeserializerBase delegate, } @Override - public JsonDeserializer unwrappingDeserializer(NameTransformer unwrapper) + public JsonDeserializer unwrappingDeserializer(DeserializationContext ctxt, + NameTransformer unwrapper) { - /* We can't do much about this; could either replace _delegate - * with unwrapping instance, or just replace this one. Latter seems - * more sensible. - */ - return _delegate.unwrappingDeserializer(unwrapper); + // We can't do much about this; could either replace _delegate with unwrapping + // instance, or just replace this one. Latter seems more sensible. + return _delegate.unwrappingDeserializer(ctxt, unwrapper); } @Override @@ -82,6 +79,9 @@ protected BeanDeserializerBase asArrayDeserializer() { return this; } + @Override + protected void initFieldMatcher(DeserializationContext ctxt) { } + /* /********************************************************** /* JsonDeserializer implementation @@ -106,6 +106,100 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt) final SettableBeanProperty[] props = _orderedProperties; int i = 0; final int propCount = props.length; + + for (; (i + 3) < propCount; i += 4) { + SettableBeanProperty prop; + if (p.nextToken() == JsonToken.END_ARRAY) return bean; + if ((prop = props[i]) != null) { + try { + prop.deserializeAndSet(p, ctxt, bean); + } catch (Exception e) { + wrapAndThrow(e, bean, prop.getName(), ctxt); + } + } else { + p.skipChildren(); + } + + if (p.nextToken() == JsonToken.END_ARRAY) return bean; + if ((prop = props[i+1]) != null) { // element #2 + try { + prop.deserializeAndSet(p, ctxt, bean); + } catch (Exception e) { + wrapAndThrow(e, bean, prop.getName(), ctxt); + } + } else { + p.skipChildren(); + } + + if (p.nextToken() == JsonToken.END_ARRAY) return bean; + if ((prop = props[i+2]) != null) { // element #3 + try { + prop.deserializeAndSet(p, ctxt, bean); + } catch (Exception e) { + wrapAndThrow(e, bean, prop.getName(), ctxt); + } + } else { + p.skipChildren(); + } + + if (p.nextToken() == JsonToken.END_ARRAY) return bean; + if ((prop = props[i+3]) != null) { // element #4 + try { + prop.deserializeAndSet(p, ctxt, bean); + } catch (Exception e) { + wrapAndThrow(e, bean, prop.getName(), ctxt); + } + } else { + p.skipChildren(); + } + } + + SettableBeanProperty prop; + switch (propCount - i) { + case 3: + if (p.nextToken() == JsonToken.END_ARRAY) { + return bean; + } + if ((prop = props[i++]) != null) { + try { + prop.deserializeAndSet(p, ctxt, bean); + } catch (Exception e) { + wrapAndThrow(e, bean, prop.getName(), ctxt); + } + } else { + p.skipChildren(); + } + // fall through + case 2: + if (p.nextToken() == JsonToken.END_ARRAY) { + return bean; + } + if ((prop = props[i++]) != null) { + try { + prop.deserializeAndSet(p, ctxt, bean); + } catch (Exception e) { + wrapAndThrow(e, bean, prop.getName(), ctxt); + } + } else { + p.skipChildren(); + } + // fall through + case 1: + if (p.nextToken() == JsonToken.END_ARRAY) { + return bean; + } + if ((prop = props[i]) != null) { + try { + prop.deserializeAndSet(p, ctxt, bean); + } catch (Exception e) { + wrapAndThrow(e, bean, prop.getName(), ctxt); + } + } else { + p.skipChildren(); + } + } + + /* while (true) { if (p.nextToken() == JsonToken.END_ARRAY) { return bean; @@ -118,24 +212,27 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt) try { prop.deserializeAndSet(p, ctxt, bean); } catch (Exception e) { - wrapAndThrow(e, bean, prop.getName(), ctxt); + throw wrapAndThrow(e, bean, prop.getName(), ctxt); } } else { // just skip? p.skipChildren(); } ++i; } - // Ok; extra fields? Let's fail, unless ignoring extra props is fine - if (!_ignoreAllUnknown && ctxt.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)) { - ctxt.reportWrongTokenException(this, JsonToken.END_ARRAY, - "Unexpected JSON values; expected at most %d properties (in JSON Array)", - propCount); - // never gets here + */ + if (p.nextToken() != JsonToken.END_ARRAY) { + // Ok; extra fields? Let's fail, unless ignoring extra props is fine + if (!_ignoreAllUnknown && ctxt.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)) { + ctxt.reportWrongTokenException(this, JsonToken.END_ARRAY, + "Unexpected JSON values; expected at most %d properties (in JSON Array)", + propCount); + // never gets here + } + // otherwise, skip until end + do { + p.skipChildren(); + } while (p.nextToken() != JsonToken.END_ARRAY); } - // otherwise, skip until end - do { - p.skipChildren(); - } while (p.nextToken() != JsonToken.END_ARRAY); return bean; } @@ -171,7 +268,7 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt, Object bean try { prop.deserializeAndSet(p, ctxt, bean); } catch (Exception e) { - wrapAndThrow(e, bean, prop.getName(), ctxt); + throw wrapAndThrow(e, bean, prop.getName(), ctxt); } } else { // just skip? p.skipChildren(); @@ -242,7 +339,7 @@ protected Object _deserializeNonVanilla(JsonParser p, DeserializationContext ctx try { prop.deserializeAndSet(p, ctxt, bean); } catch (Exception e) { - wrapAndThrow(e, bean, prop.getName(), ctxt); + throw wrapAndThrow(e, bean, prop.getName(), ctxt); } continue; } @@ -301,7 +398,7 @@ protected final Object _deserializeUsingPropertyBased(final JsonParser p, final try { prop.deserializeAndSet(p, ctxt, bean); } catch (Exception e) { - wrapAndThrow(e, bean, prop.getName(), ctxt); + throw wrapAndThrow(e, bean, prop.getName(), ctxt); } continue; } @@ -314,8 +411,7 @@ protected final Object _deserializeUsingPropertyBased(final JsonParser p, final try { bean = creator.build(ctxt, buffer); } catch (Exception e) { - wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt); - continue; // never gets here + throw wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt); } // [databind#631]: Assign current value, to be accessible by custom serializers p.setCurrentValue(bean); @@ -362,11 +458,11 @@ protected final Object _deserializeUsingPropertyBased(final JsonParser p, final protected Object _deserializeFromNonArray(JsonParser p, DeserializationContext ctxt) throws IOException { - return ctxt.handleUnexpectedToken(handledType(), p.getCurrentToken(), p, + return ctxt.handleUnexpectedToken(handledType(), p.currentToken(), p, "Cannot deserialize a POJO (of type %s) from non-Array representation (token: %s): " +"type/property designed to be serialized as JSON Array", _beanType.getRawClass().getName(), - p.getCurrentToken()); + p.currentToken()); // in future, may allow use of "standard" POJO serialization as well; if so, do: //return _delegate.deserialize(p, ctxt); } diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanPropertyMap.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanPropertyMap.java index b015bb5a29..428b2853aa 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanPropertyMap.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanPropertyMap.java @@ -1,18 +1,15 @@ package com.fasterxml.jackson.databind.deser.impl; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; import java.util.*; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.TokenStreamFactory; +import com.fasterxml.jackson.core.sym.FieldNameMatcher; +import com.fasterxml.jackson.core.util.InternCache; +import com.fasterxml.jackson.core.util.Named; import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.PropertyName; import com.fasterxml.jackson.databind.deser.SettableBeanProperty; -import com.fasterxml.jackson.databind.util.ClassUtil; import com.fasterxml.jackson.databind.util.NameTransformer; /** @@ -32,153 +29,98 @@ public class BeanPropertyMap { private static final long serialVersionUID = 2L; - /** - * @since 2.5 - */ - protected final boolean _caseInsensitive; - - private int _hashMask; - - /** - * Number of entries stored in the hash area. + /* + /********************************************************** + /* Configuration + /********************************************************** */ - private int _size; - private int _spillCount; + protected final boolean _caseInsensitive; /** - * Hash area that contains key/property pairs in adjacent elements. + * Configuration of alias mappings, if any (`null` if none), + * aligned with properties in _propsInOrder */ - private Object[] _hashArea; + private final PropertyName[][] _aliasDefs; /** * Array of properties in the exact order they were handed in. This is * used by as-array serialization, deserialization. + * Contains both primary properties (first _primaryCount + * entries) and possible aliased mappings */ - private final SettableBeanProperty[] _propsInOrder; + private SettableBeanProperty[] _propsInOrder; - /** - * Configuration of alias mappings, indexed by unmodified property name - * to unmodified aliases, if any; entries only included for properties - * that do have aliases. - * This is is used for constructing actual reverse lookup mapping, if - * needed, taking into account possible case-insensitivity, as well - * as possibility of name prefixes. - * - * @since 2.9 + /* + /********************************************************** + /* Lookup index information constructed + /********************************************************** */ - private final Map> _aliasDefs; + private transient FieldNameMatcher _fieldMatcher; + /** - * Mapping from secondary names (aliases) to primary names. - * - * @since 2.9 + * Lazily instantiated array of properties mapped from lookup index, in which + * first entries are ame as in _propsInOrder followed by alias + * mappings. */ - private final Map _aliasMapping; + private transient SettableBeanProperty[] _propsWithAliases; + /* + /********************************************************** + /* Construction + /********************************************************** + */ + /** - * @since 2.9 + * @param caseInsensitive Whether property name matching should case-insensitive or not + * @param props Sequence of primary properties to index + * @param aliasDefs Alias mappings, if any (null if none) + * @param assignIndexes Whether to assign indices to property entities or not */ - public BeanPropertyMap(boolean caseInsensitive, Collection props, - Map> aliasDefs) + protected BeanPropertyMap(boolean caseInsensitive, Collection props, + PropertyName[][] aliasDefs, + boolean assignIndexes) { _caseInsensitive = caseInsensitive; - _propsInOrder = props.toArray(new SettableBeanProperty[props.size()]); _aliasDefs = aliasDefs; - _aliasMapping = _buildAliasMapping(aliasDefs); - init(props); - } - - /* Copy constructors used when a property can replace existing one - * - * @since 2.9.6 - */ - private BeanPropertyMap(BeanPropertyMap src, - SettableBeanProperty newProp, int hashIndex, int orderedIndex) - { - // First, copy most fields as is: - _caseInsensitive = src._caseInsensitive; - _hashMask = src._hashMask; - _size = src._size; - _spillCount = src._spillCount; - _aliasDefs = src._aliasDefs; - _aliasMapping = src._aliasMapping; - - // but then make deep copy of arrays to modify - _hashArea = Arrays.copyOf(src._hashArea, src._hashArea.length); - _propsInOrder = Arrays.copyOf(src._propsInOrder, src._propsInOrder.length); - _hashArea[hashIndex] = newProp; - _propsInOrder[orderedIndex] = newProp; - } - - /* Copy constructors used when a property needs to be appended (can't replace) - * - * @since 2.9.6 - */ - private BeanPropertyMap(BeanPropertyMap src, - SettableBeanProperty newProp, String key, int slot) - { - // First, copy most fields as is: - _caseInsensitive = src._caseInsensitive; - _hashMask = src._hashMask; - _size = src._size; - _spillCount = src._spillCount; - _aliasDefs = src._aliasDefs; - _aliasMapping = src._aliasMapping; - - // but then make deep copy of arrays to modify - _hashArea = Arrays.copyOf(src._hashArea, src._hashArea.length); - int last = src._propsInOrder.length; - // and append property at the end of ordering - _propsInOrder = Arrays.copyOf(src._propsInOrder, last+1); - _propsInOrder[last] = newProp; - - final int hashSize = _hashMask+1; - int ix = (slot<<1); - - // primary slot not free? - if (_hashArea[ix] != null) { - // secondary? - ix = (hashSize + (slot >> 1)) << 1; - if (_hashArea[ix] != null) { - // ok, spill over. - ix = ((hashSize + (hashSize >> 1) ) << 1) + _spillCount; - _spillCount += 2; - if (ix >= _hashArea.length) { - _hashArea = Arrays.copyOf(_hashArea, _hashArea.length + 4); - } + _propsInOrder = props.toArray(new SettableBeanProperty[props.size()]); + // Former `assignIndexes` + // order is arbitrary, but stable: + if (assignIndexes) { + // note: only assign to primary entries, not to aliased (since they are dups) + for (int i = 0, end = props.size(); i < end; ++i) { + _propsInOrder[i].assignIndex(i); } } - _hashArea[ix] = key; - _hashArea[ix+1] = newProp; - } - - @Deprecated // since 2.8 - public BeanPropertyMap(boolean caseInsensitive, Collection props) - { - this(caseInsensitive, props, Collections.>emptyMap()); } - /** - * @since 2.8 - */ protected BeanPropertyMap(BeanPropertyMap base, boolean caseInsensitive) { _caseInsensitive = caseInsensitive; _aliasDefs = base._aliasDefs; - _aliasMapping = base._aliasMapping; // 16-May-2016, tatu: Alas, not enough to just change flag, need to re-init as well. _propsInOrder = Arrays.copyOf(base._propsInOrder, base._propsInOrder.length); - init(Arrays.asList(_propsInOrder)); +// init(Arrays.asList(_propsInOrder)); } + public static BeanPropertyMap construct(Collection props, + boolean caseInsensitive, PropertyName[][] aliases) + { + return new BeanPropertyMap(caseInsensitive, props, aliases, true); + } + + /* + /********************************************************** + /* "Mutant factory" methods + /********************************************************** + */ + /** * Mutant factory method that constructs a new instance if desired case-insensitivity * state differs from the state of this instance; if states are the same, returns * this. - * - * @since 2.8 */ public BeanPropertyMap withCaseInsensitivity(boolean state) { if (_caseInsensitive == state) { @@ -187,81 +129,6 @@ public BeanPropertyMap withCaseInsensitivity(boolean state) { return new BeanPropertyMap(this, state); } - protected void init(Collection props) - { - _size = props.size(); - - // First: calculate size of primary hash area - final int hashSize = findSize(_size); - _hashMask = hashSize-1; - - // and allocate enough to contain primary/secondary, expand for spillovers as need be - int alloc = (hashSize + (hashSize>>1)) * 2; - Object[] hashed = new Object[alloc]; - int spillCount = 0; - - for (SettableBeanProperty prop : props) { - // Due to removal, renaming, theoretically possible we'll have "holes" so: - if (prop == null) { - continue; - } - - String key = getPropertyName(prop); - int slot = _hashCode(key); - int ix = (slot<<1); - - // primary slot not free? - if (hashed[ix] != null) { - // secondary? - ix = (hashSize + (slot >> 1)) << 1; - if (hashed[ix] != null) { - // ok, spill over. - ix = ((hashSize + (hashSize >> 1) ) << 1) + spillCount; - spillCount += 2; - if (ix >= hashed.length) { - hashed = Arrays.copyOf(hashed, hashed.length + 4); - } - } - } - hashed[ix] = key; - hashed[ix+1] = prop; - - // and aliases - } - _hashArea = hashed; - _spillCount = spillCount; - } - - private final static int findSize(int size) - { - if (size <= 5) { - return 8; - } - if (size <= 12) { - return 16; - } - int needed = size + (size >> 2); // at most 80% full - int result = 32; - while (result < needed) { - result += result; - } - return result; - } - - /** - * @since 2.6 - */ - public static BeanPropertyMap construct(Collection props, - boolean caseInsensitive, Map> aliasMapping) { - return new BeanPropertyMap(caseInsensitive, props, aliasMapping); - } - - @Deprecated // since 2.9 - public static BeanPropertyMap construct(Collection props, boolean caseInsensitive) { - return construct(props, caseInsensitive, - Collections.>emptyMap()); - } - /** * Fluent copy method that creates a new instance that is a copy * of this instance except for one additional property that is @@ -271,74 +138,72 @@ public static BeanPropertyMap construct(Collection props, */ public BeanPropertyMap withProperty(SettableBeanProperty newProp) { - // First: may be able to just replace? - String key = getPropertyName(newProp); - - for (int i = 1, end = _hashArea.length; i < end; i += 2) { - SettableBeanProperty prop = (SettableBeanProperty) _hashArea[i]; - if ((prop != null) && prop.getName().equals(key)) { - return new BeanPropertyMap(this, newProp, i, _findFromOrdered(prop)); + // First: maybe just replace in place? + final String key = newProp.getName(); + for (int i = 0, end = _propsInOrder.length; i < end; ++i) { + if (_propsInOrder[i].getName().equals(key)) { + _propsInOrder[i] = newProp; + return this; } } - // If not, append - final int slot = _hashCode(key); - - return new BeanPropertyMap(this, newProp, key, slot); - } - public BeanPropertyMap assignIndexes() - { - // order is arbitrary, but stable: - int index = 0; - for (int i = 1, end = _hashArea.length; i < end; i += 2) { - SettableBeanProperty prop = (SettableBeanProperty) _hashArea[i]; - if (prop != null) { - prop.assignIndex(index++); - } - } - return this; + // If not, append + ArrayList newProps = new ArrayList(Arrays.asList(_propsInOrder)); + newProps.add(newProp); + // !!! TODO: assign index for the last entry? + return new BeanPropertyMap(_caseInsensitive, newProps, _aliasDefs, false); } /** * Mutant factory method for constructing a map where all entries use given * prefix */ - public BeanPropertyMap renameAll(NameTransformer transformer) + public BeanPropertyMap renameAll(DeserializationContext ctxt, + NameTransformer transformer) { if (transformer == null || (transformer == NameTransformer.NOP)) { return this; } // Try to retain insertion ordering as well final int len = _propsInOrder.length; - ArrayList newProps = new ArrayList(len); - + ArrayList newProps = new ArrayList(_propsInOrder.length); for (int i = 0; i < len; ++i) { - SettableBeanProperty prop = _propsInOrder[i]; - - // What to do with holes? For now, retain - if (prop == null) { - newProps.add(prop); - continue; - } - newProps.add(_rename(prop, transformer)); + SettableBeanProperty orig = _propsInOrder[i]; + SettableBeanProperty prop = _rename(ctxt, orig, transformer); + newProps.add(prop); } - // should we try to re-index? Ordering probably changed but caller probably doesn't want changes... // 26-Feb-2017, tatu: Probably SHOULD handle renaming wrt Aliases? - return new BeanPropertyMap(_caseInsensitive, newProps, _aliasDefs); + // NOTE: do NOT try reassigning indexes of properties; number doesn't change + + // !!! 18-Nov-2017, tatu: Should try recreating FieldNameMatcher here but... + return new BeanPropertyMap(_caseInsensitive, newProps, _aliasDefs, false) + .initMatcher(ctxt.getParserFactory()); } - /* - /********************************************************** - /* Public API, mutators - /********************************************************** - */ + private SettableBeanProperty _rename(DeserializationContext ctxt, + SettableBeanProperty prop, NameTransformer xf) + { + if (prop != null) { + String newName = xf.transform(prop.getName()); + newName = InternCache.instance.intern(newName); + prop = prop.withSimpleName(newName); + JsonDeserializer deser = prop.getValueDeserializer(); + if (deser != null) { + @SuppressWarnings("unchecked") + JsonDeserializer newDeser = (JsonDeserializer) + deser.unwrappingDeserializer(ctxt, xf); + if (newDeser != deser) { + prop = prop.withValueDeserializer(newDeser); + } + } + } + return prop; + } /** * Mutant factory method that will use this instance as the base, and * construct an instance that is otherwise same except for excluding * properties with specified names. - * - * @since 2.8 */ public BeanPropertyMap withoutProperties(Collection toExclude) { @@ -350,55 +215,30 @@ public BeanPropertyMap withoutProperties(Collection toExclude) for (int i = 0; i < len; ++i) { SettableBeanProperty prop = _propsInOrder[i]; - // 01-May-2015, tatu: Not 100% sure if existing `null`s should be retained; - // or, if entries to ignore should be retained as nulls. For now just - // prune them out - if (prop != null) { // may contain holes, too, check. - if (!toExclude.contains(prop.getName())) { - newProps.add(prop); - } + if (!toExclude.contains(prop.getName())) { + newProps.add(prop); } } // should we try to re-index? Apparently no need - return new BeanPropertyMap(_caseInsensitive, newProps, _aliasDefs); - } - - @Deprecated // in 2.9.4 -- must call method that takes old and new property to avoid mismatch - public void replace(SettableBeanProperty newProp) - { - String key = getPropertyName(newProp); - int ix = _findIndexInHash(key); - if (ix < 0) { - throw new NoSuchElementException("No entry '"+key+"' found, can't replace"); - } - SettableBeanProperty prop = (SettableBeanProperty) _hashArea[ix]; - _hashArea[ix] = newProp; - // also, replace in in-order - _propsInOrder[_findFromOrdered(prop)] = newProp; + // 17-Nov-2017, tatu: do NOT try to change indexes since this could lead to discrepancies + // (unless we actually copy property instances) + return new BeanPropertyMap(_caseInsensitive, newProps, _aliasDefs, false); } /** * Specialized method that can be used to replace an existing entry * (note: entry MUST exist; otherwise exception is thrown) with * specified replacement. - * - * @since 2.9.4 */ - public void replace(SettableBeanProperty origProp, SettableBeanProperty newProp) + public void replace(SettableBeanProperty oldProp, SettableBeanProperty newProp) { - int i = 1; - int end = _hashArea.length; - - for (;; i += 2) { - if (i > end) { - throw new NoSuchElementException("No entry '"+origProp.getName()+"' found, can't replace"); - } - if (_hashArea[i] == origProp) { - _hashArea[i] = newProp; - break; + for (int i = 0, end = _propsInOrder.length; i < end; ++i) { + if (_propsInOrder[i] == oldProp) { + _propsInOrder[i] = newProp; + return; } } - _propsInOrder[_findFromOrdered(origProp)] = newProp; + throw new NoSuchElementException("No entry '"+oldProp.getName()+"' found, can't replace"); } /** @@ -407,22 +247,13 @@ public void replace(SettableBeanProperty origProp, SettableBeanProperty newProp) */ public void remove(SettableBeanProperty propToRm) { - ArrayList props = new ArrayList(_size); - String key = getPropertyName(propToRm); + final String key = propToRm.getName(); + ArrayList props = new ArrayList(_propsInOrder.length); boolean found = false; - - for (int i = 1, end = _hashArea.length; i < end; i += 2) { - SettableBeanProperty prop = (SettableBeanProperty) _hashArea[i]; - if (prop == null) { - continue; - } + for (SettableBeanProperty prop : _propsInOrder) { if (!found) { - // 09-Jan-2017, tatu: Important: must check name slot and NOT property name, - // as only former is lower-case in case-insensitive case - found = key.equals(_hashArea[i-1]); - if (found) { - // need to leave a hole here - _propsInOrder[_findFromOrdered(prop)] = null; + String match = prop.getName(); + if (found = match.equals(key)) { continue; } } @@ -431,29 +262,65 @@ public void remove(SettableBeanProperty propToRm) if (!found) { throw new NoSuchElementException("No entry '"+propToRm.getName()+"' found, can't remove"); } - init(props); + _propsInOrder = props.toArray(new SettableBeanProperty[props.size()]); } /* /********************************************************** - /* Public API, simple accessors + /* Factory method(s) for helpers /********************************************************** */ - public int size() { return _size; } + public BeanPropertyMap initMatcher(TokenStreamFactory tsf) + { + List names; + if (_aliasDefs == null) { // simple case, no aliases + _propsWithAliases = _propsInOrder; + names = Arrays.asList(_propsInOrder); + } else { + // must make an actual copy (not just array-backed) as we'll append entries: + List allProps = new ArrayList<>(Arrays.asList(_propsInOrder)); + names = new ArrayList<>(allProps); + + // map aliases + for (int i = 0, end = _aliasDefs.length; i < end; ++i) { + PropertyName[] aliases = _aliasDefs[i]; + if (aliases != null) { + SettableBeanProperty primary = _propsInOrder[i]; + for (PropertyName alias : aliases) { + names.add(alias); + allProps.add(primary); + } + } + } + _propsWithAliases = allProps.toArray(new SettableBeanProperty[allProps.size()]); + } + // `true` -> yes, they are intern()ed alright + if (_caseInsensitive) { + _fieldMatcher = tsf.constructCIFieldNameMatcher(names, true); + } else { + _fieldMatcher = tsf.constructFieldNameMatcher(names, true); + } + return this; + } - /** - * @since 2.9 + public FieldNameMatcher getFieldMatcher() { return _fieldMatcher; } + public SettableBeanProperty[] getFieldMatcherProperties() { return _propsWithAliases; } + + /* + /********************************************************** + /* Public API, simple accessors + /********************************************************** */ + + public int size() { return _propsInOrder.length; } + public boolean isCaseInsensitive() { return _caseInsensitive; } - /** - * @since 2.9 - */ public boolean hasAliases() { - return !_aliasDefs.isEmpty(); + return _aliasDefs != null; } /** @@ -461,53 +328,28 @@ public boolean hasAliases() { */ @Override public Iterator iterator() { - return _properties().iterator(); + return Arrays.asList(_propsInOrder).iterator(); } - private List _properties() { - ArrayList p = new ArrayList(_size); - for (int i = 1, end = _hashArea.length; i < end; i += 2) { - SettableBeanProperty prop = (SettableBeanProperty) _hashArea[i]; - if (prop != null) { - p.add(prop); - } - } - return p; - } - /** * Method that will re-create initial insertion-ordering of * properties contained in this map. Note that if properties * have been removed, array may contain nulls; otherwise * it should be consecutive. - * - * @since 2.1 */ - public SettableBeanProperty[] getPropertiesInInsertionOrder() { + public SettableBeanProperty[] getPrimaryProperties() { return _propsInOrder; } - // Confining this case insensitivity to this function (and the find method) in case we want to - // apply a particular locale to the lower case function. For now, using the default. - protected final String getPropertyName(SettableBeanProperty prop) { - return _caseInsensitive ? prop.getName().toLowerCase() : prop.getName(); - } - /* /********************************************************** - /* Public API, property lookup + /* Public API, property definition lookup /********************************************************** */ - - /** - * @since 2.3 - */ - public SettableBeanProperty find(int index) + + public SettableBeanProperty findDefinition(int index) { - // note: will scan the whole area, including primary, secondary and - // possible spill-area - for (int i = 1, end = _hashArea.length; i < end; i += 2) { - SettableBeanProperty prop = (SettableBeanProperty) _hashArea[i]; + for (SettableBeanProperty prop : _propsInOrder) { if ((prop != null) && (index == prop.getPropertyIndex())) { return prop; } @@ -515,124 +357,23 @@ public SettableBeanProperty find(int index) return null; } - public SettableBeanProperty find(String key) + /** + * NOTE: does NOT do case-insensitive matching -- only to be used during construction + * and never during deserialization process -- nor alias expansion. + */ + public SettableBeanProperty findDefinition(String key) { if (key == null) { throw new IllegalArgumentException("Cannot pass null property name"); } - if (_caseInsensitive) { - key = key.toLowerCase(); - } - - // inlined `_hashCode(key)` - int slot = key.hashCode() & _hashMask; -// int h = key.hashCode(); -// int slot = (h + (h >> 13)) & _hashMask; - - int ix = (slot<<1); - Object match = _hashArea[ix]; - if ((match == key) || key.equals(match)) { - return (SettableBeanProperty) _hashArea[ix+1]; - } - return _find2(key, slot, match); - } - - private final SettableBeanProperty _find2(String key, int slot, Object match) - { - if (match == null) { - // 26-Feb-2017, tatu: Need to consider aliases - return _findWithAlias(_aliasMapping.get(key)); - } - // no? secondary? - int hashSize = _hashMask+1; - int ix = hashSize + (slot>>1) << 1; - match = _hashArea[ix]; - if (key.equals(match)) { - return (SettableBeanProperty) _hashArea[ix+1]; - } - if (match != null) { // _findFromSpill(...) - int i = (hashSize + (hashSize>>1)) << 1; - for (int end = i + _spillCount; i < end; i += 2) { - match = _hashArea[i]; - if ((match == key) || key.equals(match)) { - return (SettableBeanProperty) _hashArea[i+1]; - } - } - } - // 26-Feb-2017, tatu: Need to consider aliases - return _findWithAlias(_aliasMapping.get(key)); - } - - private SettableBeanProperty _findWithAlias(String keyFromAlias) - { - if (keyFromAlias == null) { - return null; - } - // NOTE: need to inline much of handling do avoid cyclic calls via alias - // first, inlined main `find(String)` - int slot = _hashCode(keyFromAlias); - int ix = (slot<<1); - Object match = _hashArea[ix]; - if (keyFromAlias.equals(match)) { - return (SettableBeanProperty) _hashArea[ix+1]; - } - if (match == null) { - return null; - } - return _find2ViaAlias(keyFromAlias, slot, match); - } - - private SettableBeanProperty _find2ViaAlias(String key, int slot, Object match) - { - // no? secondary? - int hashSize = _hashMask+1; - int ix = hashSize + (slot>>1) << 1; - match = _hashArea[ix]; - if (key.equals(match)) { - return (SettableBeanProperty) _hashArea[ix+1]; - } - if (match != null) { // _findFromSpill(...) - int i = (hashSize + (hashSize>>1)) << 1; - for (int end = i + _spillCount; i < end; i += 2) { - match = _hashArea[i]; - if ((match == key) || key.equals(match)) { - return (SettableBeanProperty) _hashArea[i+1]; - } + for (SettableBeanProperty prop : _propsInOrder) { + if (key.equals(prop.getName())) { + return prop; } } return null; } - /* - /********************************************************** - /* Public API, deserialization support - /********************************************************** - */ - - /** - * Convenience method that tries to find property with given name, and - * if it is found, call {@link SettableBeanProperty#deserializeAndSet} - * on it, and return true; or, if not found, return false. - * Note, too, that if deserialization is attempted, possible exceptions - * are wrapped if and as necessary, so caller need not handle those. - * - * @since 2.5 - */ - public boolean findDeserializeAndSet(JsonParser p, DeserializationContext ctxt, - Object bean, String key) throws IOException - { - final SettableBeanProperty prop = find(key); - if (prop == null) { - return false; - } - try { - prop.deserializeAndSet(p, ctxt, bean); - } catch (Exception e) { - wrapAndThrow(e, bean, key, ctxt); - } - return true; - } - /* /********************************************************** /* Std method overrides @@ -652,143 +393,12 @@ public String toString() if (count++ > 0) { sb.append(", "); } - sb.append(prop.getName()); - sb.append('('); - sb.append(prop.getType()); - sb.append(')'); + sb.append(String.format("%s(%s)", prop.getName(), prop.getType())); } sb.append(']'); - if (!_aliasDefs.isEmpty()) { - sb.append("(aliases: "); - sb.append(_aliasDefs); - sb.append(")"); + if (_aliasDefs != null) { + sb.append(String.format("(aliases: %s)", _aliasDefs.length)); } return sb.toString(); } - - /* - /********************************************************** - /* Helper methods - /********************************************************** - */ - - protected SettableBeanProperty _rename(SettableBeanProperty prop, NameTransformer xf) - { - if (prop == null) { - return prop; - } - String newName = xf.transform(prop.getName()); - prop = prop.withSimpleName(newName); - JsonDeserializer deser = prop.getValueDeserializer(); - if (deser != null) { - @SuppressWarnings("unchecked") - JsonDeserializer newDeser = (JsonDeserializer) - deser.unwrappingDeserializer(xf); - if (newDeser != deser) { - prop = prop.withValueDeserializer(newDeser); - } - } - return prop; - } - - protected void wrapAndThrow(Throwable t, Object bean, String fieldName, DeserializationContext ctxt) - throws IOException - { - // inlined 'throwOrReturnThrowable' - while (t instanceof InvocationTargetException && t.getCause() != null) { - t = t.getCause(); - } - // Errors to be passed as is - ClassUtil.throwIfError(t); - // StackOverflowErrors are tricky ones; need to be careful... - boolean wrap = (ctxt == null) || ctxt.isEnabled(DeserializationFeature.WRAP_EXCEPTIONS); - // Ditto for IOExceptions; except we may want to wrap JSON exceptions - if (t instanceof IOException) { - if (!wrap || !(t instanceof JsonProcessingException)) { - throw (IOException) t; - } - } else if (!wrap) { // allow disabling wrapping for unchecked exceptions - ClassUtil.throwIfRTE(t); - } - throw JsonMappingException.wrapWithPath(t, bean, fieldName); - } - - /** - * Helper method used to find exact location of a property with name - * given exactly, not subject to case changes, within hash area. - * Expectation is that such property SHOULD exist, although no - * exception is thrown. - * - * @since 2.7 - */ - private final int _findIndexInHash(String key) - { - final int slot = _hashCode(key); - int ix = (slot<<1); - - // primary match? - if (key.equals(_hashArea[ix])) { - return ix+1; - } - // no? secondary? - int hashSize = _hashMask+1; - ix = hashSize + (slot>>1) << 1; - if (key.equals(_hashArea[ix])) { - return ix+1; - } - // perhaps spill then - int i = (hashSize + (hashSize>>1)) << 1; - for (int end = i + _spillCount; i < end; i += 2) { - if (key.equals(_hashArea[i])) { - return i+1; - } - } - return -1; - } - - private final int _findFromOrdered(SettableBeanProperty prop) { - for (int i = 0, end = _propsInOrder.length; i < end; ++i) { - if (_propsInOrder[i] == prop) { - return i; - } - } - throw new IllegalStateException("Illegal state: property '"+prop.getName()+"' missing from _propsInOrder"); - } - - // Offlined version for convenience if we want to change hashing scheme - private final int _hashCode(String key) { - // This method produces better hash, fewer collisions... yet for some - // reason produces slightly worse performance. Very strange. - - // 05-Aug-2015, tatu: ... still true? - - /* - int h = key.hashCode(); - return (h + (h >> 13)) & _hashMask; - */ - return key.hashCode() & _hashMask; - } - - // @since 2.9 - private Map _buildAliasMapping(Map> defs) - { - if ((defs == null) || defs.isEmpty()) { - return Collections.emptyMap(); - } - Map aliases = new HashMap<>(); - for (Map.Entry> entry : defs.entrySet()) { - String key = entry.getKey(); - if (_caseInsensitive) { - key = key.toLowerCase(); - } - for (PropertyName pn : entry.getValue()) { - String mapped = pn.getSimpleName(); - if (_caseInsensitive) { - mapped = mapped.toLowerCase(); - } - aliases.put(mapped, key); - } - } - return aliases; - } } diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/CreatorCollector.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/CreatorCollector.java index 978c4086b1..525ba57e97 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/CreatorCollector.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/CreatorCollector.java @@ -2,7 +2,7 @@ import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Member; -import java.lang.reflect.Type; +import java.lang.reflect.Parameter; import java.util.*; import com.fasterxml.jackson.databind.*; @@ -17,8 +17,8 @@ * Container class for storing information on creators (based on annotations, * visibility), to be able to build actual instantiator later on. */ -public class CreatorCollector { - // Since 2.5 +public class CreatorCollector +{ protected final static int C_DEFAULT = 0; protected final static int C_STRING = 1; protected final static int C_INT = 2; @@ -38,15 +38,10 @@ public class CreatorCollector { final protected boolean _canFixAccess; - /** - * @since 2.7 - */ final protected boolean _forceAccess; /** * Set of creators we have collected so far - * - * @since 2.5 */ protected final AnnotatedWithParams[] _creators = new AnnotatedWithParams[9]; @@ -54,8 +49,6 @@ public class CreatorCollector { * Bitmask of creators that were explicitly marked as creators; false for * auto-detected (ones included base on naming and/or visibility, not * annotation) - * - * @since 2.5 */ protected int _explicitCreators = 0; @@ -253,7 +246,7 @@ private JavaType _computeDelegateType(DeserializationContext ctxt, AnnotatedParameter delegate = creator.getParameter(ix); // First: custom deserializer(s): - Object deserDef = intr.findDeserializer(delegate); + Object deserDef = intr.findDeserializer(config, delegate); if (deserDef != null) { JsonDeserializer deser = ctxt.deserializerInstance(delegate, deserDef); baseType = baseType.withValueHandler(deser); @@ -340,8 +333,6 @@ else if (newType.isAssignableFrom(oldType)) { /** * Helper method for recognizing `Enum.valueOf()` factory method - * - * @since 2.8.1 */ protected boolean _isEnumValueOf(AnnotatedWithParams creator) { return creator.getDeclaringClass().isEnum() @@ -426,9 +417,8 @@ public JavaType getParameterType(int index) { } @Override - @Deprecated - public Type getGenericParameterType(int index) { - return _base.getGenericParameterType(index); + public Parameter[] getNativeParameters() { + return _base.getNativeParameters(); } @Override 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 9ff1c0b9ed..f69e6388ca 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 @@ -215,7 +215,7 @@ public Object complete(JsonParser p, DeserializationContext ctxt, Object bean) // will be included. JsonToken t = tokens.firstToken(); if (t.isScalarValue()) { // can't be null as we never store empty buffers - JsonParser buffered = tokens.asParser(p); + JsonParser buffered = tokens.asParser(ctxt, p); buffered.nextToken(); SettableBeanProperty extProp = _properties[i].getProperty(); Object result = TypeDeserializer.deserializeIfNatural(buffered, ctxt, extProp.getType()); @@ -299,7 +299,7 @@ public Object complete(JsonParser p, DeserializationContext ctxt, if (typeProp.getType().hasRawClass(String.class)) { v = typeId; } else { - TokenBuffer tb = new TokenBuffer(p, ctxt); + TokenBuffer tb = TokenBuffer.forInputBuffering(p, ctxt); tb.writeString(typeId); v = typeProp.getValueDeserializer().deserialize(tb.asParserOnFirstToken(), ctxt); tb.close(); @@ -323,7 +323,7 @@ public Object complete(JsonParser p, DeserializationContext ctxt, protected final Object _deserialize(JsonParser p, DeserializationContext ctxt, int index, String typeId) throws IOException { - JsonParser p2 = _tokens[index].asParser(p); + JsonParser p2 = _tokens[index].asParser(ctxt, p); JsonToken t = p2.nextToken(); // 29-Sep-2015, tatu: As per [databind#942], nulls need special support if (t == JsonToken.VALUE_NULL) { @@ -336,7 +336,7 @@ protected final Object _deserialize(JsonParser p, DeserializationContext ctxt, merged.writeEndArray(); // needs to point to START_OBJECT (or whatever first token is) - JsonParser mp = merged.asParser(p); + JsonParser mp = merged.asParser(ctxt, p); mp.nextToken(); return _properties[index].getProperty().deserialize(mp, ctxt); } @@ -348,7 +348,7 @@ protected final void _deserializeAndSet(JsonParser p, DeserializationContext ctx /* 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. */ - JsonParser p2 = _tokens[index].asParser(p); + JsonParser p2 = _tokens[index].asParser(ctxt, p); JsonToken t = p2.nextToken(); // 29-Sep-2015, tatu: As per [databind#942], nulls need special support if (t == JsonToken.VALUE_NULL) { @@ -362,7 +362,7 @@ protected final void _deserializeAndSet(JsonParser p, DeserializationContext ctx merged.copyCurrentStructure(p2); merged.writeEndArray(); // needs to point to START_OBJECT (or whatever first token is) - JsonParser mp = merged.asParser(p); + JsonParser mp = merged.asParser(ctxt, p); mp.nextToken(); _properties[index].getProperty().deserializeAndSet(mp, ctxt, bean); } @@ -412,8 +412,6 @@ private void _addPropertyIndex(String name, Integer index) { * Method called after all external properties have been assigned, to further * link property with polymorphic value with possible property for type id * itself. This is needed to support type ids as Creator properties. - * - * @since 2.8 */ public ExternalTypeHandler build(BeanPropertyMap otherProps) { // 21-Jun-2016, tatu: as per [databind#999], may need to link type id property also @@ -422,7 +420,7 @@ public ExternalTypeHandler build(BeanPropertyMap otherProps) { for (int i = 0; i < len; ++i) { ExtTypedProperty extProp = _properties.get(i); String typePropId = extProp.getTypePropertyName(); - SettableBeanProperty typeProp = otherProps.find(typePropId); + SettableBeanProperty typeProp = otherProps.findDefinition(typePropId); if (typeProp != null) { extProp.linkTypeProperty(typeProp); } @@ -439,9 +437,6 @@ private final static class ExtTypedProperty private final TypeDeserializer _typeDeserializer; private final String _typePropertyName; - /** - * @since 2.8 - */ private SettableBeanProperty _typeProperty; public ExtTypedProperty(SettableBeanProperty property, TypeDeserializer typeDeser) @@ -485,9 +480,6 @@ public SettableBeanProperty getProperty() { return _property; } - /** - * @since 2.8 - */ public SettableBeanProperty getTypeProperty() { return _typeProperty; } diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/InnerClassProperty.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/InnerClassProperty.java index bdc72dd7ba..eece109ff4 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 @@ -72,7 +72,7 @@ protected SettableBeanProperty withDelegate(SettableBeanProperty d) { public void deserializeAndSet(JsonParser p, DeserializationContext ctxt, Object bean) throws IOException { - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); Object value; if (t == JsonToken.VALUE_NULL) { value = _valueDeserializer.getNullValue(ctxt); diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/JavaUtilCollectionsDeserializers.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/JavaUtilCollectionsDeserializers.java index ce27bbc590..4077a5207b 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/JavaUtilCollectionsDeserializers.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/JavaUtilCollectionsDeserializers.java @@ -6,7 +6,7 @@ import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.deser.std.StdDelegatingDeserializer; +import com.fasterxml.jackson.databind.deser.std.StdConvertingDeserializer; import com.fasterxml.jackson.databind.type.TypeFactory; import com.fasterxml.jackson.databind.util.Converter; @@ -76,7 +76,7 @@ public static JsonDeserializer findForCollection(DeserializationContext ctxt, } else { return null; } - return new StdDelegatingDeserializer(conv); + return new StdConvertingDeserializer(conv); } public static JsonDeserializer findForMap(DeserializationContext ctxt, @@ -93,7 +93,7 @@ public static JsonDeserializer findForMap(DeserializationContext ctxt, } else { return null; } - return new StdDelegatingDeserializer(conv); + return new StdConvertingDeserializer(conv); } static JavaUtilCollectionsConverter converter(int kind, 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 f1b62ab635..2278b7cdaf 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 @@ -86,12 +86,14 @@ public void deserializeAndSet(JsonParser p, DeserializationContext ctxt, Object } @Override - public Object deserializeSetAndReturn(JsonParser p, DeserializationContext ctxt, Object instance) throws IOException + public Object deserializeSetAndReturn(JsonParser p, DeserializationContext ctxt, + Object instance) throws IOException { try { return setAndReturn(instance, deserialize(p, ctxt)); } catch (UnresolvedForwardReference reference) { - boolean usingIdentityInfo = (_objectIdInfo != null) || (_valueDeserializer.getObjectIdReader() != null); + boolean usingIdentityInfo = (_objectIdInfo != null) + || (_valueDeserializer.getObjectIdReader(ctxt) != null); if (!usingIdentityInfo) { throw JsonMappingException.from(p, "Unresolved forward reference but no identity info", reference); } diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyBasedCreator.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyBasedCreator.java index 566b54f009..660974ebd2 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyBasedCreator.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyBasedCreator.java @@ -39,10 +39,9 @@ public final class PropertyBasedCreator protected final HashMap _propertyLookup; /** - * Array that contains properties that expect value to inject, if any; - * null if no injectable values are expected. + * Array that contains properties that match creator properties */ - protected final SettableBeanProperty[] _allProperties; + protected final SettableBeanProperty[] _propertiesInOrder; /* /********************************************************** @@ -64,7 +63,6 @@ protected PropertyBasedCreator(DeserializationContext ctxt, } final int len = creatorProps.length; _propertyCount = len; - _allProperties = new SettableBeanProperty[len]; // 26-Feb-2017, tatu: Let's start by aliases, so that there is no // possibility of accidental override of primary names @@ -82,9 +80,10 @@ protected PropertyBasedCreator(DeserializationContext ctxt, } } } + _propertiesInOrder = new SettableBeanProperty[len]; for (int i = 0; i < len; ++i) { SettableBeanProperty prop = creatorProps[i]; - _allProperties[i] = prop; + _propertiesInOrder[i] = prop; // 22-Jan-2018, tatu: ignorable entries should be skipped if (!prop.isIgnorable()) { _propertyLookup.put(prop.getName(), prop); @@ -95,8 +94,6 @@ protected PropertyBasedCreator(DeserializationContext ctxt, /** * Factory method used for building actual instances to be used with POJOS: * resolves deserializers, checks for "null values". - * - * @since 2.9 */ public static PropertyBasedCreator construct(DeserializationContext ctxt, ValueInstantiator valueInstantiator, SettableBeanProperty[] srcCreatorProps, @@ -121,8 +118,6 @@ public static PropertyBasedCreator construct(DeserializationContext ctxt, * Factory method used for building actual instances to be used with types * OTHER than POJOs. * resolves deserializers and checks for "null values". - * - * @since 2.9 */ public static PropertyBasedCreator construct(DeserializationContext ctxt, ValueInstantiator valueInstantiator, SettableBeanProperty[] srcCreatorProps, @@ -142,15 +137,6 @@ public static PropertyBasedCreator construct(DeserializationContext ctxt, caseInsensitive, false); } - @Deprecated // since 2.9 - public static PropertyBasedCreator construct(DeserializationContext ctxt, - ValueInstantiator valueInstantiator, SettableBeanProperty[] srcCreatorProps) - throws JsonMappingException - { - return construct(ctxt, valueInstantiator, srcCreatorProps, - ctxt.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)); - } - /* /********************************************************** /* Accessors @@ -182,8 +168,6 @@ public SettableBeanProperty findCreatorProperty(int propertyIndex) { /** * Method called when starting to build a bean instance. - * - * @since 2.1 (added ObjectIdReader parameter -- existed in previous versions without) */ public PropertyValueBuffer startBuilding(JsonParser p, DeserializationContext ctxt, ObjectIdReader oir) { @@ -193,7 +177,7 @@ public PropertyValueBuffer startBuilding(JsonParser p, DeserializationContext ct public Object build(DeserializationContext ctxt, PropertyValueBuffer buffer) throws IOException { Object bean = _valueInstantiator.createFromObjectWith(ctxt, - _allProperties, buffer); + _propertiesInOrder, buffer); // returning null isn't quite legal, but let's let caller deal with that if (bean != null) { // Object Id to handle? diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer.java index 23f75031c9..d397359105 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 @@ -21,9 +21,9 @@ public class PropertyValueBuffer { /* - /********************************************************** + /********************************************************************** /* Configuration - /********************************************************** + /********************************************************************** */ protected final JsonParser _parser; @@ -32,9 +32,9 @@ public class PropertyValueBuffer protected final ObjectIdReader _objectIdReader; /* - /********************************************************** + /********************************************************************** /* Accumulated properties, other stuff - /********************************************************** + /********************************************************************** */ /** @@ -77,9 +77,9 @@ public class PropertyValueBuffer protected Object _idValue; /* - /********************************************************** + /********************************************************************** /* Life-cycle - /********************************************************** + /********************************************************************** */ public PropertyValueBuffer(JsonParser p, DeserializationContext ctxt, int paramCount, @@ -100,8 +100,6 @@ public PropertyValueBuffer(JsonParser p, DeserializationContext ctxt, int paramC /** * Returns {@code true} if the given property was seen in the JSON source by * this buffer. - * - * @since 2.8 */ public final boolean hasParameter(SettableBeanProperty prop) { @@ -118,8 +116,6 @@ public final boolean hasParameter(SettableBeanProperty prop) * {@link #hasParameter(SettableBeanProperty)}) to let applications only * fetch the properties defined in the JSON source itself, and to have some * other customized behavior for missing properties. - * - * @since 2.8 */ public Object getParameter(SettableBeanProperty prop) throws JsonMappingException @@ -204,16 +200,14 @@ protected Object _findMissing(SettableBeanProperty prop) throws JsonMappingExcep } /* - /********************************************************** + /********************************************************************** /* Other methods - /********************************************************** + /********************************************************************** */ /** * Helper method called to see if given non-creator property is the "id property"; * and if so, handle appropriately. - * - * @since 2.1 */ public boolean readIdProperty(String propName) throws IOException { @@ -255,8 +249,6 @@ public Object handleIdValue(final DeserializationContext ctxt, Object bean) thro * we now have values for all (creator) properties that we expect to get values for. * * @return True if we have received all creator parameters - * - * @since 2.6 */ public boolean assignParameter(SettableBeanProperty prop, Object value) { 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 2ce045b64a..fd4a16f1c8 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 @@ -101,7 +101,7 @@ public A getAnnotation(Class acls) { public final void deserializeAndSet(JsonParser p, DeserializationContext ctxt, Object instance) throws IOException { - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == JsonToken.VALUE_NULL) { // Hmmh. Is this a problem? We won't be setting anything, so it's // equivalent of empty Collection/Map in this case diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/UnwrappedPropertyHandler.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/UnwrappedPropertyHandler.java index 890ff7b91a..877a28d8e3 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/UnwrappedPropertyHandler.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/UnwrappedPropertyHandler.java @@ -4,6 +4,7 @@ import java.util.*; import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.util.InternCache; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.deser.SettableBeanProperty; @@ -21,7 +22,8 @@ public class UnwrappedPropertyHandler public UnwrappedPropertyHandler() { _properties = new ArrayList(); - } + } + protected UnwrappedPropertyHandler(List props) { _properties = props; } @@ -30,17 +32,19 @@ public void addProperty(SettableBeanProperty property) { _properties.add(property); } - public UnwrappedPropertyHandler renameAll(NameTransformer transformer) + public UnwrappedPropertyHandler renameAll(DeserializationContext ctxt, + NameTransformer transformer) { ArrayList newProps = new ArrayList(_properties.size()); for (SettableBeanProperty prop : _properties) { String newName = transformer.transform(prop.getName()); + newName = InternCache.instance.intern(newName); prop = prop.withSimpleName(newName); JsonDeserializer deser = prop.getValueDeserializer(); if (deser != null) { @SuppressWarnings("unchecked") JsonDeserializer newDeser = (JsonDeserializer) - deser.unwrappingDeserializer(transformer); + deser.unwrappingDeserializer(ctxt, transformer); if (newDeser != deser) { prop = prop.withValueDeserializer(newDeser); } @@ -49,7 +53,13 @@ public UnwrappedPropertyHandler renameAll(NameTransformer transformer) } return new UnwrappedPropertyHandler(newProps); } - + + /* + public List getHandledProperties() { + return Collections.unmodifiableList(_properties); + } + */ + @SuppressWarnings("resource") public Object processUnwrapped(JsonParser originalParser, DeserializationContext ctxt, Object bean, TokenBuffer buffered) diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/ValueInjector.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ValueInjector.java index 148ba8f373..815d51cb84 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/ValueInjector.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ValueInjector.java @@ -28,17 +28,6 @@ public ValueInjector(PropertyName propName, JavaType type, _valueId = valueId; } - /** - * @deprecated in 2.9 (remove from 3.0) - */ - @Deprecated // see [databind#1835] - public ValueInjector(PropertyName propName, JavaType type, - com.fasterxml.jackson.databind.util.Annotations contextAnnotations, // removed from later versions - AnnotatedMember mutator, Object valueId) - { - this(propName, type, mutator, valueId); - } - public Object findValue(DeserializationContext context, Object beanInstance) throws JsonMappingException { diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/CollectionDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/CollectionDeserializer.java index c255d896fb..d9b2866d8f 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/CollectionDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/CollectionDeserializer.java @@ -27,7 +27,6 @@ @JacksonStdImpl public class CollectionDeserializer extends ContainerDeserializerBase> - implements ContextualDeserializer { private static final long serialVersionUID = -1L; // since 2.5 @@ -75,8 +74,6 @@ public CollectionDeserializer(JavaType collectionType, /** * Constructor used when creating contextualized instances. - * - * @since 2.9 */ protected CollectionDeserializer(JavaType collectionType, JsonDeserializer valueDeser, TypeDeserializer valueTypeDeser, @@ -105,15 +102,12 @@ protected CollectionDeserializer(CollectionDeserializer src) /** * Fluent-factory method call to construct contextual instance. - * - * @since 2.9 */ @SuppressWarnings("unchecked") protected CollectionDeserializer withResolved(JsonDeserializer dd, JsonDeserializer vd, TypeDeserializer vtd, NullValueProvider nuller, Boolean unwrapSingle) { -//if (true) throw new Error(); return new CollectionDeserializer(_containerType, (JsonDeserializer) vd, vtd, _valueInstantiator, (JsonDeserializer) dd, @@ -121,7 +115,7 @@ protected CollectionDeserializer withResolved(JsonDeserializer dd, } // Important: do NOT cache if polymorphic values - @Override // since 2.5 + @Override public boolean isCachable() { // 26-Mar-2015, tatu: As per [databind#735], need to be careful return (_valueDeserializer == null) @@ -269,7 +263,7 @@ public Collection deserialize(JsonParser p, DeserializationContext ctxt, JsonDeserializer valueDes = _valueDeserializer; // Let's offline handling of values with Object Ids (simplifies code here) - if (valueDes.getObjectIdReader() != null) { + if (valueDes.getObjectIdReader(ctxt) != null) { return _deserializeWithObjectId(p, ctxt, result); } final TypeDeserializer typeDeser = _valueTypeDeserializer; @@ -333,7 +327,7 @@ protected final Collection handleNonArray(JsonParser p, DeserializationC } JsonDeserializer valueDes = _valueDeserializer; final TypeDeserializer typeDeser = _valueTypeDeserializer; - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); Object value; 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 123cf6be9b..436a280a09 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 @@ -25,8 +25,6 @@ public abstract class ContainerDeserializerBase /** * Handler we need for dealing with nulls. - * - * @since 2.9 */ protected final NullValueProvider _nullProvider; @@ -34,16 +32,12 @@ public abstract class ContainerDeserializerBase * Specific override for this instance (from proper, or global per-type overrides) * to indicate whether single value may be taken to mean an unwrapped one-element array * or not. If null, left to global defaults. - * - * @since 2.9 (demoted from sub-classes where added in 2.7) */ protected final Boolean _unwrapSingle; /** * Marker flag set if the _nullProvider indicates that all null * content values should be skipped (instead of being possibly converted). - * - * @since 2.9 */ protected final boolean _skipNullValues; @@ -60,16 +54,10 @@ protected ContainerDeserializerBase(JavaType selfType) { this(selfType, null, null); } - /** - * @since 2.9 - */ protected ContainerDeserializerBase(ContainerDeserializerBase base) { this(base, base._nullProvider, base._unwrapSingle); } - /** - * @since 2.9 - */ protected ContainerDeserializerBase(ContainerDeserializerBase base, NullValueProvider nuller, Boolean unwrapSingle) { super(base._containerType); @@ -85,10 +73,10 @@ protected ContainerDeserializerBase(ContainerDeserializerBase base, /********************************************************** */ - @Override // since 2.9 + @Override public JavaType getValueType() { return _containerType; } - @Override // since 2.9 + @Override public Boolean supportsUpdate(DeserializationConfig config) { return Boolean.TRUE; } @@ -126,22 +114,19 @@ public JavaType getContentType() { */ public abstract JsonDeserializer getContentDeserializer(); - /** - * @since 2.9 - */ @Override public ValueInstantiator getValueInstantiator() { return null; } - @Override // since 2.9 + @Override public AccessPattern getEmptyAccessPattern() { // 02-Feb-2017, tatu: Empty containers are usually constructed as needed // and may not be shared; for some deserializers this may be further refined. return AccessPattern.DYNAMIC; } - @Override // since 2.9 + @Override public Object getEmptyValue(DeserializationContext ctxt) throws JsonMappingException { ValueInstantiator vi = getValueInstantiator(); if (vi == null || !vi.canCreateUsingDefault()) { diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/DateDeserializers.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/DateDeserializers.java index 14bddb3ba6..33580df17c 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/DateDeserializers.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/DateDeserializers.java @@ -13,7 +13,6 @@ import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; -import com.fasterxml.jackson.databind.deser.ContextualDeserializer; import com.fasterxml.jackson.databind.util.ClassUtil; import com.fasterxml.jackson.databind.util.StdDateFormat; @@ -68,7 +67,6 @@ public static JsonDeserializer find(Class rawType, String clsName) protected abstract static class DateBasedDeserializer extends StdScalarDeserializer - implements ContextualDeserializer { /** * Specific format to use, if non-null; if null will diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/DelegatingDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/DelegatingDeserializer.java index f4368780ea..ddbef1a4d1 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/DelegatingDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/DelegatingDeserializer.java @@ -15,12 +15,9 @@ * that mostly delegate functionality to another deserializer implementation * (possibly forming a chaing of deserializers delegating functionality * in some cases) - * - * @since 2.1 */ public abstract class DelegatingDeserializer extends StdDeserializer - implements ContextualDeserializer, ResolvableDeserializer { private static final long serialVersionUID = 1L; @@ -54,8 +51,8 @@ public DelegatingDeserializer(JsonDeserializer d) @Override public void resolve(DeserializationContext ctxt) throws JsonMappingException { - if (_delegatee instanceof ResolvableDeserializer) { - ((ResolvableDeserializer) _delegatee).resolve(ctxt); + if (_delegatee != null) { + _delegatee.resolve(ctxt); } } @@ -73,6 +70,12 @@ public JsonDeserializer createContextual(DeserializationContext ctxt, return newDelegatingInstance(del); } + @Override + public SettableBeanProperty findBackReference(String logicalName) { + // [databind#253]: Hope this works.... + return _delegatee.findBackReference(logicalName); + } + @Override public JsonDeserializer replaceDelegatee(JsonDeserializer delegatee) { @@ -118,25 +121,11 @@ public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, /********************************************************************** */ - @Override - public boolean isCachable() { return _delegatee.isCachable(); } - - @Override // since 2.9 - public Boolean supportsUpdate(DeserializationConfig config) { - return _delegatee.supportsUpdate(config); - } - @Override public JsonDeserializer getDelegatee() { return _delegatee; } - @Override - public SettableBeanProperty findBackReference(String logicalName) { - // [databind#253]: Hope this works.... - return _delegatee.findBackReference(logicalName); - } - @Override public AccessPattern getNullAccessPattern() { return _delegatee.getNullAccessPattern(); @@ -156,5 +145,17 @@ public Object getEmptyValue(DeserializationContext ctxt) throws JsonMappingExcep public Collection getKnownPropertyNames() { return _delegatee.getKnownPropertyNames(); } @Override - public ObjectIdReader getObjectIdReader() { return _delegatee.getObjectIdReader(); } + public ObjectIdReader getObjectIdReader(DeserializationContext ctxt) { + return _delegatee.getObjectIdReader(ctxt); + } + + @Override + public boolean isCachable() { + return (_delegatee != null) && _delegatee.isCachable(); + } + + @Override + public Boolean supportsUpdate(DeserializationConfig config) { + return _delegatee.supportsUpdate(config); + } } 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 a40ff4df29..29b4d5bd47 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 @@ -8,7 +8,6 @@ import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; -import com.fasterxml.jackson.databind.deser.ContextualDeserializer; import com.fasterxml.jackson.databind.deser.SettableBeanProperty; import com.fasterxml.jackson.databind.deser.ValueInstantiator; import com.fasterxml.jackson.databind.introspect.AnnotatedMethod; @@ -20,38 +19,26 @@ * Deserializer class that can deserialize instances of * specified Enum class from Strings and Integers. */ -@JacksonStdImpl // was missing until 2.6 +@JacksonStdImpl public class EnumDeserializer extends StdScalarDeserializer - implements ContextualDeserializer { private static final long serialVersionUID = 1L; protected Object[] _enumsByIndex; - - /** - * @since 2.8 - */ + private final Enum _enumDefaultValue; - /** - * @since 2.7.3 - */ protected final CompactStringObjectMap _lookupByName; /** * Alternatively, we may need a different lookup object if "use toString" * is defined. - * - * @since 2.7.3 */ protected CompactStringObjectMap _lookupByToString; protected final Boolean _caseInsensitive; - /** - * @since 2.9 - */ public EnumDeserializer(EnumResolver byNameResolver, Boolean caseInsensitive) { super(byNameResolver.getEnumClass()); @@ -61,9 +48,6 @@ public EnumDeserializer(EnumResolver byNameResolver, Boolean caseInsensitive) _caseInsensitive = caseInsensitive; } - /** - * @since 2.9 - */ protected EnumDeserializer(EnumDeserializer base, Boolean caseInsensitive) { super(base); @@ -73,30 +57,11 @@ protected EnumDeserializer(EnumDeserializer base, Boolean caseInsensitive) _caseInsensitive = caseInsensitive; } - /** - * @deprecated Since 2.9 - */ - @Deprecated - public EnumDeserializer(EnumResolver byNameResolver) { - this(byNameResolver, null); - } - - /** - * @deprecated Since 2.8 - */ - @Deprecated - public static JsonDeserializer deserializerForCreator(DeserializationConfig config, - Class enumClass, AnnotatedMethod factory) { - return deserializerForCreator(config, enumClass, factory, null, null); - } - /** * Factory method used when Enum instances are to be deserialized * using a creator (static factory method) * * @return Deserializer based on given factory method - * - * @since 2.8 */ public static JsonDeserializer deserializerForCreator(DeserializationConfig config, Class enumClass, AnnotatedMethod factory, @@ -116,8 +81,6 @@ public static JsonDeserializer deserializerForCreator(DeserializationConfig c * using a zero-/no-args factory method * * @return Deserializer based on given no-args factory method - * - * @since 2.8 */ public static JsonDeserializer deserializerForNoArgsCreator(DeserializationConfig config, Class enumClass, AnnotatedMethod factory) @@ -129,9 +92,6 @@ public static JsonDeserializer deserializerForNoArgsCreator(DeserializationCo return new FactoryBasedEnumDeserializer(enumClass, factory); } - /** - * @since 2.9 - */ public EnumDeserializer withResolved(Boolean caseInsensitive) { if (_caseInsensitive == caseInsensitive) { return this; @@ -139,7 +99,7 @@ public EnumDeserializer withResolved(Boolean caseInsensitive) { return new EnumDeserializer(this, caseInsensitive); } - @Override // since 2.9 + @Override public JsonDeserializer createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException { @@ -167,7 +127,7 @@ public JsonDeserializer createContextual(DeserializationContext ctxt, @Override public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - JsonToken curr = p.getCurrentToken(); + JsonToken curr = p.currentToken(); // Usually should just get string value: if (curr == JsonToken.VALUE_STRING || curr == JsonToken.FIELD_NAME) { @@ -233,9 +193,9 @@ private final Object _deserializeAltString(JsonParser p, DeserializationContext if (c >= '0' && c <= '9') { try { int index = Integer.parseInt(name); - if (!ctxt.isEnabled(MapperFeature.ALLOW_COERCION_OF_SCALARS)) { + if (!ctxt.isEnabled(DeserializationFeature.ALLOW_COERCION_OF_SCALARS)) { return ctxt.handleWeirdStringValue(_enumClass(), name, -"value looks like quoted Enum index, but `MapperFeature.ALLOW_COERCION_OF_SCALARS` prevents use" +"value looks like quoted Enum index, but `DeserializationFeature.ALLOW_COERCION_OF_SCALARS` prevents use" ); } if (index >= 0 && index < _enumsByIndex.length) { 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 63a31e7893..2512a71d48 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 @@ -5,11 +5,7 @@ import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.deser.ContextualDeserializer; -import com.fasterxml.jackson.databind.deser.NullValueProvider; -import com.fasterxml.jackson.databind.deser.ResolvableDeserializer; -import com.fasterxml.jackson.databind.deser.SettableBeanProperty; -import com.fasterxml.jackson.databind.deser.ValueInstantiator; +import com.fasterxml.jackson.databind.deser.*; import com.fasterxml.jackson.databind.deser.impl.PropertyBasedCreator; import com.fasterxml.jackson.databind.deser.impl.PropertyValueBuffer; import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; @@ -24,7 +20,6 @@ @SuppressWarnings({ "unchecked", "rawtypes" }) public class EnumMapDeserializer extends ContainerDeserializerBase> - implements ContextualDeserializer, ResolvableDeserializer { private static final long serialVersionUID = 1; @@ -41,10 +36,7 @@ public class EnumMapDeserializer protected final TypeDeserializer _valueTypeDeserializer; // // Instance construction settings: - - /** - * @since 2.9 - */ + protected final ValueInstantiator _valueInstantiator; /** @@ -100,13 +92,6 @@ protected EnumMapDeserializer(EnumMapDeserializer base, _propertyBasedCreator = base._propertyBasedCreator; } - @Deprecated // since 2.9 - public EnumMapDeserializer(JavaType mapType, KeyDeserializer keyDeser, - JsonDeserializer valueDeser, TypeDeserializer vtd) - { - this(mapType, null, keyDeser, valueDeser, vtd, null); - } - public EnumMapDeserializer withResolved(KeyDeserializer keyDeserializer, JsonDeserializer valueDeserializer, TypeDeserializer valueTypeDeser, NullValueProvider nuller) @@ -153,7 +138,7 @@ public void resolve(DeserializationContext ctxt) throws JsonMappingException } _delegateDeserializer = findDeserializer(ctxt, delegateType, null); } else if (_valueInstantiator.canCreateFromObjectWith()) { - SettableBeanProperty[] creatorProps = _valueInstantiator.getFromObjectArguments(ctxt.getConfig()); + SettableBeanProperty[] creatorProps = _valueInstantiator.getFromObjectArguments(ctxt); _propertyBasedCreator = PropertyBasedCreator.construct(ctxt, _valueInstantiator, creatorProps, ctxt.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)); } @@ -235,7 +220,7 @@ public EnumMap deserialize(JsonParser p, DeserializationContext ctxt) _delegateDeserializer.deserialize(p, ctxt)); } // Ok: must point to START_OBJECT - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if ((t != JsonToken.START_OBJECT) && (t != JsonToken.FIELD_NAME) && (t != JsonToken.END_OBJECT)) { // (empty) String may be ok however; or single-String-arg ctor if (t == JsonToken.VALUE_STRING) { @@ -263,14 +248,14 @@ public EnumMap deserialize(JsonParser p, DeserializationContext ctxt, if (p.isExpectedStartObjectToken()) { keyStr = p.nextFieldName(); } else { - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t != JsonToken.FIELD_NAME) { if (t == JsonToken.END_OBJECT) { return result; } ctxt.reportWrongTokenException(this, JsonToken.FIELD_NAME, null); } - keyStr = p.getCurrentName(); + keyStr = p.currentName(); } for (; keyStr != null; keyStr = p.nextFieldName()) { @@ -347,7 +332,7 @@ public EnumMap _deserializeUsingProperties(JsonParser p, DeserializationCon if (p.isExpectedStartObjectToken()) { keyName = p.nextFieldName(); } else if (p.hasToken(JsonToken.FIELD_NAME)) { - keyName = p.getCurrentName(); + keyName = p.currentName(); } else { keyName = null; } diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumSetDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumSetDeserializer.java index 08ceee8c1a..68682717a7 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumSetDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumSetDeserializer.java @@ -6,7 +6,6 @@ import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.deser.ContextualDeserializer; import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; /** @@ -18,7 +17,6 @@ @SuppressWarnings("rawtypes") public class EnumSetDeserializer extends StdDeserializer> - implements ContextualDeserializer { private static final long serialVersionUID = 1L; // since 2.5 @@ -32,8 +30,6 @@ public class EnumSetDeserializer * Specific override for this instance (from proper, or global per-type overrides) * to indicate whether single value may be taken to mean an unwrapped one-element array * or not. If null, left to global defaults. - * - * @since 2.7 */ protected final Boolean _unwrapSingle; @@ -57,9 +53,6 @@ public EnumSetDeserializer(JavaType enumType, JsonDeserializer deser) _unwrapSingle = null; } - /** - * @since 2.7 - */ @SuppressWarnings("unchecked" ) protected EnumSetDeserializer(EnumSetDeserializer base, JsonDeserializer deser, Boolean unwrapSingle) { diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/FactoryBasedEnumDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/FactoryBasedEnumDeserializer.java index 86a68d9dc5..d1e9c94fdf 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/FactoryBasedEnumDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/FactoryBasedEnumDeserializer.java @@ -7,7 +7,6 @@ import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.deser.ContextualDeserializer; import com.fasterxml.jackson.databind.deser.SettableBeanProperty; import com.fasterxml.jackson.databind.deser.ValueInstantiator; import com.fasterxml.jackson.databind.deser.impl.PropertyBasedCreator; @@ -19,12 +18,9 @@ /** * Deserializer that uses a single-String static factory method * for locating Enum values by String id. - * - * @since 2.8 (as stand-alone class; was static inner class of {@link EnumDeserializer} */ class FactoryBasedEnumDeserializer extends StdDeserializer - implements ContextualDeserializer { private static final long serialVersionUID = 1; @@ -110,7 +106,7 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOEx if (_deser != null) { value = _deser.deserialize(p, ctxt); } else if (_hasArgs) { - JsonToken curr = p.getCurrentToken(); + JsonToken curr = p.currentToken(); //There can be a JSON object passed for deserializing an Enum, //the below case handles it. if (curr == JsonToken.VALUE_STRING || curr == JsonToken.FIELD_NAME) { @@ -161,9 +157,9 @@ protected Object deserializeEnumUsingPropertyBased(final JsonParser p, final Des { PropertyValueBuffer buffer = creator.startBuilding(p, ctxt, null); - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) { - String propName = p.getCurrentName(); + String propName = p.currentName(); p.nextToken(); // to point to value SettableBeanProperty creatorProp = creator.findCreatorProperty(propName); diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/FromStringDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/FromStringDeserializer.java index 68187c1302..5d564fddbf 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/FromStringDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/FromStringDeserializer.java @@ -1,14 +1,22 @@ package com.fasterxml.jackson.databind.deser.std; +import static java.lang.Character.isLetter; + import java.io.*; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.MalformedURLException; import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; import java.nio.charset.Charset; +import java.nio.file.FileSystemNotFoundException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.spi.FileSystemProvider; import java.util.Currency; import java.util.Locale; +import java.util.ServiceLoader; import java.util.TimeZone; import java.util.regex.Pattern; @@ -58,6 +66,7 @@ public static Class[] types() { File.class, URL.class, URI.class, + Path.class, // since 3.0 Class.class, JavaType.class, Currency.class, @@ -70,11 +79,11 @@ public static Class[] types() { StringBuilder.class, }; } - + /* - /********************************************************** + /********************************************************************** /* Deserializer implementations - /********************************************************** + /********************************************************************** */ protected FromStringDeserializer(Class vc) { @@ -94,6 +103,8 @@ public static Std findDeserializer(Class rawType) kind = Std.STD_URL; } else if (rawType == URI.class) { kind = Std.STD_URI; + } else if (rawType == Path.class) { + kind = Std.STD_PATH; } else if (rawType == Class.class) { kind = Std.STD_CLASS; } else if (rawType == JavaType.class) { @@ -121,9 +132,9 @@ public static Std findDeserializer(Class rawType) } /* - /********************************************************** + /********************************************************************** /* Deserializer implementations - /********************************************************** + /********************************************************************** */ @SuppressWarnings("unchecked") @@ -135,7 +146,7 @@ public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOExcepti if (text != null) { // has String representation if (text.length() == 0 || (text = text.trim()).length() == 0) { // Usually should become null; but not always - return _deserializeFromEmptyString(); + return _deserializeFromEmptyString(ctxt); } Exception cause = null; try { @@ -158,7 +169,7 @@ public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOExcepti throw e; // nothing to do here, yet? We'll fail anyway } - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); // [databind#381] if (t == JsonToken.START_ARRAY) { return _deserializeFromArray(p, ctxt); @@ -183,25 +194,24 @@ protected T _deserializeEmbedded(Object ob, DeserializationContext ctxt) throws // default impl: error out ctxt.reportInputMismatch(this, "Don't know how to convert embedded Object of type %s into %s", - ob.getClass().getName(), _valueClass.getName()); + ClassUtil.classNameOf(ob), _valueClass.getName()); return null; } - protected T _deserializeFromEmptyString() throws IOException { - return null; + @SuppressWarnings("unchecked") + protected T _deserializeFromEmptyString(DeserializationContext ctxt) throws IOException { + return (T) _coerceEmptyString(ctxt, false); } /* - /********************************************************** + /********************************************************************** /* A general-purpose implementation - /********************************************************** + /********************************************************************** */ /** * "Chameleon" deserializer that works on simple types that are deserialized * from a simple String. - * - * @since 2.4 */ public static class Std extends FromStringDeserializer { @@ -210,16 +220,17 @@ public static class Std extends FromStringDeserializer public final static int STD_FILE = 1; public final static int STD_URL = 2; public final static int STD_URI = 3; - public final static int STD_CLASS = 4; - public final static int STD_JAVA_TYPE = 5; - public final static int STD_CURRENCY = 6; - public final static int STD_PATTERN = 7; - public final static int STD_LOCALE = 8; - public final static int STD_CHARSET = 9; - public final static int STD_TIME_ZONE = 10; - public final static int STD_INET_ADDRESS = 11; - public final static int STD_INET_SOCKET_ADDRESS = 12; - public final static int STD_STRING_BUILDER = 13; + public final static int STD_PATH = 4; + public final static int STD_CLASS = 5; + public final static int STD_JAVA_TYPE = 6; + public final static int STD_CURRENCY = 7; + public final static int STD_PATTERN = 8; + public final static int STD_LOCALE = 9; + public final static int STD_CHARSET = 10; + public final static int STD_TIME_ZONE = 11; + public final static int STD_INET_ADDRESS = 12; + public final static int STD_INET_SOCKET_ADDRESS = 13; + public final static int STD_STRING_BUILDER = 14; protected final int _kind; @@ -238,6 +249,9 @@ protected Object _deserialize(String value, DeserializationContext ctxt) throws return new URL(value); case STD_URI: return URI.create(value); + case STD_PATH: + // 06-Sep-2018, tatu: Offlined due to additions in [databind#2120] + return NioPathHelper.deserialize(ctxt, value); case STD_CLASS: try { return ctxt.findClass(value); @@ -305,7 +319,7 @@ protected Object _deserialize(String value, DeserializationContext ctxt) throws } @Override - protected Object _deserializeFromEmptyString() throws IOException { + protected Object _deserializeFromEmptyString(DeserializationContext ctxt) throws IOException { // As per [databind#398], URI requires special handling if (_kind == STD_URI) { return URI.create(""); @@ -317,7 +331,7 @@ protected Object _deserializeFromEmptyString() throws IOException { if (_kind == STD_STRING_BUILDER) { return new StringBuilder(); } - return super._deserializeFromEmptyString(); + return super._deserializeFromEmptyString(ctxt); } protected int _firstHyphenOrUnderscore(String str) @@ -331,4 +345,59 @@ protected int _firstHyphenOrUnderscore(String str) return -1; } } + + private static class NioPathHelper { + private static final boolean areWindowsFilePathsSupported; + static { + boolean isWindowsRootFound = false; + for (File file : File.listRoots()) { + String path = file.getPath(); + if (path.length() >= 2 && isLetter(path.charAt(0)) && path.charAt(1) == ':') { + isWindowsRootFound = true; + break; + } + } + areWindowsFilePathsSupported = isWindowsRootFound; + } + + public static Path deserialize(DeserializationContext ctxt, String value) throws IOException { + // If someone gives us an input with no : at all, treat as local path, instead of failing + // with invalid URI. + if (value.indexOf(':') < 0) { + return Paths.get(value); + } + + if (areWindowsFilePathsSupported) { + if (value.length() >= 2 && isLetter(value.charAt(0)) && value.charAt(1) == ':') { + return Paths.get(value); + } + } + + final URI uri; + try { + uri = new URI(value); + } catch (URISyntaxException e) { + return (Path) ctxt.handleInstantiationProblem(Path.class, value, e); + } + try { + return Paths.get(uri); + } catch (FileSystemNotFoundException cause) { + try { + final String scheme = uri.getScheme(); + // We want to use the current thread's context class loader, not system class loader that is used in Paths.get(): + for (FileSystemProvider provider : ServiceLoader.load(FileSystemProvider.class)) { + if (provider.getScheme().equalsIgnoreCase(scheme)) { + return provider.getPath(uri); + } + } + return (Path) ctxt.handleInstantiationProblem(Path.class, value, cause); + } catch (Throwable e) { + e.addSuppressed(cause); + return (Path) ctxt.handleInstantiationProblem(Path.class, value, e); + } + } catch (Throwable e) { + return (Path) ctxt.handleInstantiationProblem(Path.class, value, e); + } + } + } } diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/JsonLocationInstantiator.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/JsonLocationInstantiator.java index be32d08d27..1933fbc97f 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/JsonLocationInstantiator.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/JsonLocationInstantiator.java @@ -1,7 +1,6 @@ package com.fasterxml.jackson.databind.deser.std; import com.fasterxml.jackson.core.JsonLocation; -import com.fasterxml.jackson.databind.DeserializationConfig; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.PropertyMetadata; @@ -26,7 +25,7 @@ public JsonLocationInstantiator() { public boolean canCreateFromObjectWith() { return true; } @Override - public SettableBeanProperty[] getFromObjectArguments(DeserializationConfig config) { + public SettableBeanProperty[] getFromObjectArguments(DeserializationContext config) { JavaType intType = config.constructType(Integer.TYPE); JavaType longType = config.constructType(Long.TYPE); return new SettableBeanProperty[] { diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/JsonNodeDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/JsonNodeDeserializer.java index 01937fe8b4..6f524e2167 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/JsonNodeDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/JsonNodeDeserializer.java @@ -63,12 +63,12 @@ public JsonNode getNullValue(DeserializationContext ctxt) { @Override public JsonNode deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - switch (p.getCurrentTokenId()) { - case JsonTokenId.ID_START_OBJECT: + JsonToken t = p.currentToken(); + if (t == JsonToken.START_OBJECT) { return deserializeObject(p, ctxt, ctxt.getNodeFactory()); - case JsonTokenId.ID_START_ARRAY: + } + if (t == JsonToken.START_ARRAY) { return deserializeArray(p, ctxt, ctxt.getNodeFactory()); - default: } return deserializeAny(p, ctxt, ctxt.getNodeFactory()); } @@ -109,8 +109,6 @@ public ObjectNode deserialize(JsonParser p, DeserializationContext ctxt) throws /** * Variant needed to support both root-level `updateValue()` and merging. - * - * @since 2.9 */ @Override public ObjectNode deserialize(JsonParser p, DeserializationContext ctxt, @@ -145,8 +143,6 @@ public ArrayNode deserialize(JsonParser p, DeserializationContext ctxt) throws I /** * Variant needed to support both root-level `updateValue()` and merging. - * - * @since 2.9 */ @Override public ArrayNode deserialize(JsonParser p, DeserializationContext ctxt, @@ -243,8 +239,12 @@ protected final ObjectNode deserializeObject(JsonParser p, DeserializationContex final JsonNodeFactory nodeFactory) throws IOException { final ObjectNode node = nodeFactory.objectNode(); + + // 13-Dec-2017, tatu: Unrolling is a mysterious optimization. Looks like doing TWO + // operations per loop yields non-trivial +5% improvement. Yet doing more does not. + // So we'll go with 2... String key = p.nextFieldName(); - for (; key != null; key = p.nextFieldName()) { + while (key != null) { JsonNode value; JsonToken t = p.nextToken(); if (t == null) { // can this ever occur? @@ -257,8 +257,45 @@ protected final ObjectNode deserializeObject(JsonParser p, DeserializationContex case JsonTokenId.ID_START_ARRAY: value = deserializeArray(p, ctxt, nodeFactory); break; - case JsonTokenId.ID_EMBEDDED_OBJECT: - value = _fromEmbedded(p, ctxt, nodeFactory); + case JsonTokenId.ID_STRING: + value = nodeFactory.textNode(p.getText()); + break; + case JsonTokenId.ID_NUMBER_INT: + value = _fromInt(p, ctxt, nodeFactory); + break; + case JsonTokenId.ID_TRUE: + value = nodeFactory.booleanNode(true); + break; + case JsonTokenId.ID_FALSE: + value = nodeFactory.booleanNode(false); + break; + case JsonTokenId.ID_NULL: + value = nodeFactory.nullNode(); + break; + default: + value = deserializeAny(p, ctxt, nodeFactory); + } + { + JsonNode old = node.replace(key, value); + if (old != null) { + _handleDuplicateField(p, ctxt, nodeFactory, + key, node, old, value); + } + } + + if ((key = p.nextFieldName()) == null) { + break; + } + t = p.nextToken(); + if (t == null) { // can this ever occur? + t = JsonToken.NOT_AVAILABLE; // can this ever occur? + } + switch (t.id()) { + case JsonTokenId.ID_START_OBJECT: + value = deserializeObject(p, ctxt, nodeFactory); + break; + case JsonTokenId.ID_START_ARRAY: + value = deserializeArray(p, ctxt, nodeFactory); break; case JsonTokenId.ID_STRING: value = nodeFactory.textNode(p.getText()); @@ -278,11 +315,16 @@ protected final ObjectNode deserializeObject(JsonParser p, DeserializationContex default: value = deserializeAny(p, ctxt, nodeFactory); } - JsonNode old = node.replace(key, value); - if (old != null) { - _handleDuplicateField(p, ctxt, nodeFactory, - key, node, old, value); + { + JsonNode old = node.replace(key, value); + if (old != null) { + _handleDuplicateField(p, ctxt, nodeFactory, + key, node, old, value); + } } + + // for next round + key = p.nextFieldName(); } return node; } @@ -290,14 +332,12 @@ protected final ObjectNode deserializeObject(JsonParser p, DeserializationContex /** * Alternate deserialization method used when parser already points to first * FIELD_NAME and not START_OBJECT. - * - * @since 2.9 */ protected final ObjectNode deserializeObjectAtName(JsonParser p, DeserializationContext ctxt, final JsonNodeFactory nodeFactory) throws IOException { final ObjectNode node = nodeFactory.objectNode(); - String key = p.getCurrentName(); + String key = p.currentName(); for (; key != null; key = p.nextFieldName()) { JsonNode value; JsonToken t = p.nextToken(); @@ -311,9 +351,6 @@ protected final ObjectNode deserializeObjectAtName(JsonParser p, Deserialization case JsonTokenId.ID_START_ARRAY: value = deserializeArray(p, ctxt, nodeFactory); break; - case JsonTokenId.ID_EMBEDDED_OBJECT: - value = _fromEmbedded(p, ctxt, nodeFactory); - break; case JsonTokenId.ID_STRING: value = nodeFactory.textNode(p.getText()); break; @@ -344,8 +381,6 @@ protected final ObjectNode deserializeObjectAtName(JsonParser p, Deserialization /** * Alternate deserialization method that is to update existing {@link ObjectNode} * if possible. - * - * @since 2.9 */ protected final JsonNode updateObject(JsonParser p, DeserializationContext ctxt, final ObjectNode node) throws IOException @@ -357,8 +392,9 @@ protected final JsonNode updateObject(JsonParser p, DeserializationContext ctxt, if (!p.hasToken(JsonToken.FIELD_NAME)) { return deserialize(p, ctxt); } - key = p.getCurrentName(); + key = p.currentName(); } + final JsonNodeFactory nodeFactory = ctxt.getNodeFactory(); for (; key != null; key = p.nextFieldName()) { // If not, fall through to regular handling JsonToken t = p.nextToken(); @@ -385,7 +421,6 @@ protected final JsonNode updateObject(JsonParser p, DeserializationContext ctxt, t = JsonToken.NOT_AVAILABLE; } JsonNode value; - JsonNodeFactory nodeFactory = ctxt.getNodeFactory(); switch (t.id()) { case JsonTokenId.ID_START_OBJECT: value = deserializeObject(p, ctxt, nodeFactory); @@ -426,7 +461,11 @@ protected final JsonNode updateObject(JsonParser p, DeserializationContext ctxt, protected final ArrayNode deserializeArray(JsonParser p, DeserializationContext ctxt, final JsonNodeFactory nodeFactory) throws IOException { - ArrayNode node = nodeFactory.arrayNode(); + final ArrayNode node = nodeFactory.arrayNode(); + // 13-Dec-2017, tatu: Unrolling is a mysterious optimization. Looks like doing TWO + // operations per loop yields non-trivial +5% improvement. Yet doing more does not. + // So we'll go with 2... + while (true) { JsonToken t = p.nextToken(); switch (t.id()) { @@ -438,9 +477,36 @@ protected final ArrayNode deserializeArray(JsonParser p, DeserializationContext break; case JsonTokenId.ID_END_ARRAY: return node; - case JsonTokenId.ID_EMBEDDED_OBJECT: - node.add(_fromEmbedded(p, ctxt, nodeFactory)); + case JsonTokenId.ID_STRING: + node.add(nodeFactory.textNode(p.getText())); + break; + case JsonTokenId.ID_NUMBER_INT: + node.add(_fromInt(p, ctxt, nodeFactory)); + break; + case JsonTokenId.ID_TRUE: + node.add(nodeFactory.booleanNode(true)); break; + case JsonTokenId.ID_FALSE: + node.add(nodeFactory.booleanNode(false)); + break; + case JsonTokenId.ID_NULL: + node.add(nodeFactory.nullNode()); + break; + default: + node.add(deserializeAny(p, ctxt, nodeFactory)); + break; + } + + t = p.nextToken(); + switch (t.id()) { + case JsonTokenId.ID_START_OBJECT: + node.add(deserializeObject(p, ctxt, nodeFactory)); + break; + case JsonTokenId.ID_START_ARRAY: + node.add(deserializeArray(p, ctxt, nodeFactory)); + break; + case JsonTokenId.ID_END_ARRAY: + return node; case JsonTokenId.ID_STRING: node.add(nodeFactory.textNode(p.getText())); break; @@ -466,8 +532,6 @@ protected final ArrayNode deserializeArray(JsonParser p, DeserializationContext /** * Alternate deserialization method that is to update existing {@link ObjectNode} * if possible. - * - * @since 2.9 */ protected final JsonNode updateArray(JsonParser p, DeserializationContext ctxt, final ArrayNode node) throws IOException @@ -512,7 +576,7 @@ protected final JsonNode updateArray(JsonParser p, DeserializationContext ctxt, protected final JsonNode deserializeAny(JsonParser p, DeserializationContext ctxt, final JsonNodeFactory nodeFactory) throws IOException { - switch (p.getCurrentTokenId()) { + switch (p.currentTokenId()) { case JsonTokenId.ID_END_OBJECT: // for empty JSON Objects we may point to this? return nodeFactory.objectNode(); case JsonTokenId.ID_FIELD_NAME: 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 5e4bb35fb6..17de9343c6 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 @@ -29,7 +29,6 @@ @JacksonStdImpl public class MapDeserializer extends ContainerDeserializerBase> - implements ContextualDeserializer, ResolvableDeserializer { private static final long serialVersionUID = 1L; @@ -228,7 +227,7 @@ public void resolve(DeserializationContext ctxt) throws JsonMappingException _delegateDeserializer = findDeserializer(ctxt, delegateType, null); } if (_valueInstantiator.canCreateFromObjectWith()) { - SettableBeanProperty[] creatorProps = _valueInstantiator.getFromObjectArguments(ctxt.getConfig()); + SettableBeanProperty[] creatorProps = _valueInstantiator.getFromObjectArguments(ctxt); _propertyBasedCreator = PropertyBasedCreator.construct(ctxt, _valueInstantiator, creatorProps, ctxt.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)); } @@ -350,7 +349,7 @@ public Map deserialize(JsonParser p, DeserializationContext ctxt) "no default constructor found"); } // Ok: must point to START_OBJECT, FIELD_NAME or END_OBJECT - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t != JsonToken.START_OBJECT && t != JsonToken.FIELD_NAME && t != JsonToken.END_OBJECT) { // (empty) String may be ok however; or single-String-arg ctor if (t == JsonToken.VALUE_STRING) { @@ -378,7 +377,7 @@ public Map deserialize(JsonParser p, DeserializationContext ctxt, p.setCurrentValue(result); // Ok: must point to START_OBJECT or FIELD_NAME - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t != JsonToken.START_OBJECT && t != JsonToken.FIELD_NAME) { return (Map) ctxt.handleUnexpectedToken(getMapClass(), p); } @@ -425,7 +424,7 @@ protected final void _readAndBind(JsonParser p, DeserializationContext ctxt, final TypeDeserializer typeDeser = _valueTypeDeserializer; MapReferringAccumulator referringAccumulator = null; - boolean useObjectId = valueDes.getObjectIdReader() != null; + boolean useObjectId = valueDes.getObjectIdReader(ctxt) != null; if (useObjectId) { referringAccumulator = new MapReferringAccumulator(_containerType.getContentType().getRawClass(), result); @@ -435,14 +434,14 @@ protected final void _readAndBind(JsonParser p, DeserializationContext ctxt, if (p.isExpectedStartObjectToken()) { keyStr = p.nextFieldName(); } else { - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t != JsonToken.FIELD_NAME) { if (t == JsonToken.END_OBJECT) { return; } ctxt.reportWrongTokenException(this, JsonToken.FIELD_NAME, null); } - keyStr = p.getCurrentName(); + keyStr = p.currentName(); } for (; keyStr != null; keyStr = p.nextFieldName()) { @@ -490,7 +489,7 @@ protected final void _readAndBindStringKeyMap(JsonParser p, DeserializationConte final JsonDeserializer valueDes = _valueDeserializer; final TypeDeserializer typeDeser = _valueTypeDeserializer; MapReferringAccumulator referringAccumulator = null; - boolean useObjectId = (valueDes.getObjectIdReader() != null); + boolean useObjectId = (valueDes.getObjectIdReader(ctxt) != null); if (useObjectId) { referringAccumulator = new MapReferringAccumulator(_containerType.getContentType().getRawClass(), result); } @@ -499,14 +498,14 @@ protected final void _readAndBindStringKeyMap(JsonParser p, DeserializationConte if (p.isExpectedStartObjectToken()) { key = p.nextFieldName(); } else { - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == JsonToken.END_OBJECT) { return; } if (t != JsonToken.FIELD_NAME) { ctxt.reportWrongTokenException(this, JsonToken.FIELD_NAME, null); } - key = p.getCurrentName(); + key = p.currentName(); } for (; key != null; key = p.nextFieldName()) { @@ -556,7 +555,7 @@ public Map _deserializeUsingCreator(JsonParser p, Deserialization if (p.isExpectedStartObjectToken()) { key = p.nextFieldName(); } else if (p.hasToken(JsonToken.FIELD_NAME)) { - key = p.getCurrentName(); + key = p.currentName(); } else { key = null; } @@ -638,14 +637,14 @@ protected final void _readAndUpdate(JsonParser p, DeserializationContext ctxt, if (p.isExpectedStartObjectToken()) { keyStr = p.nextFieldName(); } else { - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == JsonToken.END_OBJECT) { return; } if (t != JsonToken.FIELD_NAME) { ctxt.reportWrongTokenException(this, JsonToken.FIELD_NAME, null); } - keyStr = p.getCurrentName(); + keyStr = p.currentName(); } for (; keyStr != null; keyStr = p.nextFieldName()) { @@ -703,14 +702,14 @@ protected final void _readAndUpdateStringKeyMap(JsonParser p, DeserializationCon if (p.isExpectedStartObjectToken()) { key = p.nextFieldName(); } else { - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == JsonToken.END_OBJECT) { return; } if (t != JsonToken.FIELD_NAME) { ctxt.reportWrongTokenException(this, JsonToken.FIELD_NAME, null); } - key = p.getCurrentName(); + key = p.currentName(); } for (; key != null; key = p.nextFieldName()) { diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/MapEntryDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/MapEntryDeserializer.java index 2805c6bf09..4d4168b328 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/MapEntryDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/MapEntryDeserializer.java @@ -21,7 +21,6 @@ @JacksonStdImpl public class MapEntryDeserializer extends ContainerDeserializerBase> - implements ContextualDeserializer { private static final long serialVersionUID = 1; @@ -167,7 +166,7 @@ public JsonDeserializer getContentDeserializer() { public Map.Entry deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { // Ok: must point to START_OBJECT, FIELD_NAME or END_OBJECT - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t != JsonToken.START_OBJECT && t != JsonToken.FIELD_NAME && t != JsonToken.END_OBJECT) { // String may be ok however: // slightly redundant (since String was passed above), but @@ -188,7 +187,7 @@ public Map.Entry deserialize(JsonParser p, DeserializationContext final JsonDeserializer valueDes = _valueDeserializer; final TypeDeserializer typeDeser = _valueTypeDeserializer; - final String keyStr = p.getCurrentName(); + final String keyStr = p.currentName(); Object key = keyDes.deserializeKey(keyStr, ctxt); Object value = null; // And then the value... @@ -212,7 +211,7 @@ public Map.Entry deserialize(JsonParser p, DeserializationContext if (t == JsonToken.FIELD_NAME) { // most likely ctxt.reportInputMismatch(this, "Problem binding JSON into Map.Entry: more than one entry in JSON (second field: '%s')", - p.getCurrentName()); + p.currentName()); } else { // how would this occur? ctxt.reportInputMismatch(this, diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/NullifyingDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/NullifyingDeserializer.java index bd58ecc5d9..64b337ab0c 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/NullifyingDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/NullifyingDeserializer.java @@ -57,7 +57,7 @@ public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, { // Not sure if we need to bother but: - switch (p.getCurrentTokenId()) { + switch (p.currentTokenId()) { case JsonTokenId.ID_START_ARRAY: case JsonTokenId.ID_START_OBJECT: case JsonTokenId.ID_FIELD_NAME: diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/NumberDeserializers.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/NumberDeserializers.java index 35ec9d4da3..280b4df10e 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/NumberDeserializers.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/NumberDeserializers.java @@ -189,7 +189,7 @@ public BooleanDeserializer(Class cls, Boolean nvl) @Override public Boolean deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == JsonToken.VALUE_TRUE) { return Boolean.TRUE; } @@ -206,7 +206,7 @@ public Boolean deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException { - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == JsonToken.VALUE_TRUE) { return Boolean.TRUE; } @@ -219,7 +219,7 @@ public Boolean deserializeWithType(JsonParser p, DeserializationContext ctxt, protected final Boolean _parseBoolean(JsonParser p, DeserializationContext ctxt) throws IOException { - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == JsonToken.VALUE_NULL) { return (Boolean) _coerceNullToken(ctxt, _primitive); } @@ -288,7 +288,7 @@ public Byte deserialize(JsonParser p, DeserializationContext ctxt) throws IOExce protected Byte _parseByte(JsonParser p, DeserializationContext ctxt) throws IOException { - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == JsonToken.VALUE_STRING) { // let's do implicit re-parse String text = p.getText().trim(); if (_hasTextualNull(text)) { @@ -358,7 +358,7 @@ public Short deserialize(JsonParser p, DeserializationContext ctxt) protected Short _parseShort(JsonParser p, DeserializationContext ctxt) throws IOException { - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == JsonToken.VALUE_NUMBER_INT) { return p.getShortValue(); } @@ -420,7 +420,7 @@ public CharacterDeserializer(Class cls, Character nvl) public Character deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - switch (p.getCurrentTokenId()) { + switch (p.currentTokenId()) { case JsonTokenId.ID_NUMBER_INT: // ok iff ascii value _verifyNumberForScalarCoercion(ctxt, p); int value = p.getIntValue(); @@ -488,7 +488,7 @@ public Integer deserializeWithType(JsonParser p, DeserializationContext ctxt, protected final Integer _parseInteger(JsonParser p, DeserializationContext ctxt) throws IOException { - switch (p.getCurrentTokenId()) { + switch (p.currentTokenId()) { // NOTE: caller assumed to usually check VALUE_NUMBER_INT in fast path case JsonTokenId.ID_NUMBER_INT: return Integer.valueOf(p.getIntValue()); @@ -559,7 +559,7 @@ public Long deserialize(JsonParser p, DeserializationContext ctxt) throws IOExce protected final Long _parseLong(JsonParser p, DeserializationContext ctxt) throws IOException { - switch (p.getCurrentTokenId()) { + switch (p.currentTokenId()) { // NOTE: caller assumed to usually check VALUE_NUMBER_INT in fast path case JsonTokenId.ID_NUMBER_INT: return p.getLongValue(); @@ -583,7 +583,6 @@ protected final Long _parseLong(JsonParser p, DeserializationContext ctxt) throw } catch (IllegalArgumentException iae) { } return (Long) ctxt.handleWeirdStringValue(_valueClass, text, "not a valid Long value"); - // fall-through case JsonTokenId.ID_NULL: return (Long) _coerceNullToken(ctxt, _primitive); case JsonTokenId.ID_START_ARRAY: @@ -617,7 +616,7 @@ protected final Float _parseFloat(JsonParser p, DeserializationContext ctxt) throws IOException { // We accept couple of different types; obvious ones first: - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == JsonToken.VALUE_NUMBER_FLOAT || t == JsonToken.VALUE_NUMBER_INT) { // coercing should work too return p.getFloatValue(); @@ -695,7 +694,7 @@ public Double deserializeWithType(JsonParser p, DeserializationContext ctxt, protected final Double _parseDouble(JsonParser p, DeserializationContext ctxt) throws IOException { - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == JsonToken.VALUE_NUMBER_INT || t == JsonToken.VALUE_NUMBER_FLOAT) { // coercing should work too return p.getDoubleValue(); } @@ -766,7 +765,7 @@ public NumberDeserializer() { @Override public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - switch (p.getCurrentTokenId()) { + switch (p.currentTokenId()) { case JsonTokenId.ID_NUMBER_INT: if (ctxt.hasSomeOfFeatures(F_MASK_INT_COERCIONS)) { return _coerceIntegral(p, ctxt); @@ -844,7 +843,7 @@ public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException { - switch (p.getCurrentTokenId()) { + switch (p.currentTokenId()) { case JsonTokenId.ID_NUMBER_INT: case JsonTokenId.ID_NUMBER_FLOAT: case JsonTokenId.ID_STRING: @@ -884,7 +883,7 @@ public Object getEmptyValue(DeserializationContext ctxt) { @Override public BigInteger deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - switch (p.getCurrentTokenId()) { + switch (p.currentTokenId()) { case JsonTokenId.ID_NUMBER_INT: switch (p.getNumberType()) { case INT: @@ -937,7 +936,7 @@ public Object getEmptyValue(DeserializationContext ctxt) { public BigDecimal deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - switch (p.getCurrentTokenId()) { + switch (p.currentTokenId()) { case JsonTokenId.ID_NUMBER_INT: case JsonTokenId.ID_NUMBER_FLOAT: return p.getDecimalValue(); 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 017317d5d4..797bab6e6c 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 @@ -9,7 +9,6 @@ import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; -import com.fasterxml.jackson.databind.deser.ContextualDeserializer; import com.fasterxml.jackson.databind.deser.NullValueProvider; import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; import com.fasterxml.jackson.databind.util.AccessPattern; @@ -21,7 +20,6 @@ @JacksonStdImpl public class ObjectArrayDeserializer extends ContainerDeserializerBase - implements ContextualDeserializer { private static final long serialVersionUID = 1L; @@ -321,7 +319,7 @@ protected Object[] handleNonArray(JsonParser p, DeserializationContext ctxt) ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)); if (!canWrap) { // One exception; byte arrays are generally serialized as base64, so that should be handled - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == JsonToken.VALUE_STRING // note: not `byte[]`, but `Byte[]` -- former is primitive array && _elementClass == Byte.class) { @@ -329,7 +327,7 @@ protected Object[] handleNonArray(JsonParser p, DeserializationContext ctxt) } return (Object[]) ctxt.handleUnexpectedToken(_containerType.getRawClass(), p); } - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); Object value; if (t == JsonToken.VALUE_NULL) { 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 175db71e0a..8e6795f57c 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 @@ -9,7 +9,6 @@ import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; -import com.fasterxml.jackson.databind.deser.ContextualDeserializer; import com.fasterxml.jackson.databind.deser.NullValueProvider; import com.fasterxml.jackson.databind.deser.impl.NullsConstantProvider; import com.fasterxml.jackson.databind.deser.impl.NullsFailProvider; @@ -23,8 +22,8 @@ * arrays that contain non-object java primitive types. */ @SuppressWarnings("serial") -public abstract class PrimitiveArrayDeserializers extends StdDeserializer - implements ContextualDeserializer // since 2.7 +public abstract class PrimitiveArrayDeserializers + extends StdDeserializer { /** * Specific override for this instance (from proper, or global per-type overrides) @@ -281,7 +280,7 @@ public char[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOEx * convert other tokens to Strings... but let's not bother * yet, doesn't seem to make sense) */ - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == JsonToken.VALUE_STRING) { // note: can NOT return shared internal buffer, must copy: char[] buffer = p.getTextCharacters(); @@ -468,7 +467,7 @@ protected byte[] _constructEmpty() { @Override public byte[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); // Most likely case: base64 encoded String? if (t == JsonToken.VALUE_STRING) { @@ -538,7 +537,7 @@ protected byte[] handleSingleElementUnwrapped(JsonParser p, DeserializationContext ctxt) throws IOException { byte value; - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == JsonToken.VALUE_NUMBER_INT || t == JsonToken.VALUE_NUMBER_FLOAT) { // should we catch overflow exceptions? value = p.getByteValue(); diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/ReferenceTypeDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/ReferenceTypeDeserializer.java index 56bf081b61..bec3101bce 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/ReferenceTypeDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/ReferenceTypeDeserializer.java @@ -6,7 +6,6 @@ import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.deser.ContextualDeserializer; import com.fasterxml.jackson.databind.deser.ValueInstantiator; import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; import com.fasterxml.jackson.databind.type.ReferenceType; @@ -16,14 +15,11 @@ * Base deserializer implementation for properties {@link ReferenceType} values. * Implements most of functionality, only leaving couple of abstract * methods for sub-classes to implement - * - * @since 2.8 */ public abstract class ReferenceTypeDeserializer extends StdDeserializer - implements ContextualDeserializer { - private static final long serialVersionUID = 2L; // 2.9 + private static final long serialVersionUID = 2L; /** * Full type of property (or root value) for which this deserializer @@ -53,13 +49,6 @@ public ReferenceTypeDeserializer(JavaType fullType, ValueInstantiator vi, _valueTypeDeserializer = typeDeser; } - @Deprecated // since 2.9 - public ReferenceTypeDeserializer(JavaType fullType, - TypeDeserializer typeDeser, JsonDeserializer deser) - { - this(fullType, null, typeDeser, deser); - } - @Override public JsonDeserializer createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException @@ -218,7 +207,7 @@ public T deserialize(JsonParser p, DeserializationContext ctxt, T reference) thr public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException { - final JsonToken t = p.getCurrentToken(); + final JsonToken t = p.currentToken(); if (t == JsonToken.VALUE_NULL) { // can this actually happen? return getNullValue(ctxt); } diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StackTraceElementDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StackTraceElementDeserializer.java index fcfba10297..872f2bf330 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StackTraceElementDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StackTraceElementDeserializer.java @@ -18,7 +18,7 @@ public class StackTraceElementDeserializer @Override public StackTraceElement deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); // Must get an Object if (t == JsonToken.START_OBJECT) { String className = "", methodName = "", fileName = ""; @@ -28,33 +28,46 @@ public StackTraceElement deserialize(JsonParser p, DeserializationContext ctxt) int lineNumber = -1; while ((t = p.nextValue()) != JsonToken.END_OBJECT) { - String propName = p.getCurrentName(); - // TODO: with Java 8, convert to switch - if ("className".equals(propName)) { + String propName = p.currentName(); + + switch (propName) { + case "className": className = p.getText(); - } else if ("classLoaderName".equals(propName)) { + break; + case "classLoaderName": classLoaderName = p.getText(); - } else if ("fileName".equals(propName)) { + break; + case "fileName": fileName = p.getText(); - } else if ("lineNumber".equals(propName)) { + break; + case "lineNumber": if (t.isNumeric()) { lineNumber = p.getIntValue(); } else { lineNumber = _parseIntPrimitive(p, ctxt); } - } else if ("methodName".equals(propName)) { + break; + case "methodName": methodName = p.getText(); - } else if ("nativeMethod".equals(propName)) { - // no setter, not passed via constructor: ignore - } else if ("moduleName".equals(propName)) { + break; + case "moduleName": moduleName = p.getText(); - } else if ("moduleVersion".equals(propName)) { + break; + case "moduleVersion": moduleVersion = p.getText(); - } else if ("declaringClass".equals(propName) - || "format".equals(propName)) { + break; + + // and then fluff we can't use: + + case "nativeMethod": + // no setter, not passed via constructor: ignore + case "declaringClass": // 01-Nov-2017: [databind#1794] Not sure if we should but... let's prune it for now - ; - } else { + case "format": + // 02-Feb-2018, tatu: Java 9 apparently adds "format" somehow... + break; + + default: handleUnknownProperty(p, ctxt, _valueClass, propName); } p.skipChildren(); // just in case we might get structured values @@ -72,18 +85,8 @@ public StackTraceElement deserialize(JsonParser p, DeserializationContext ctxt) return (StackTraceElement) ctxt.handleUnexpectedToken(_valueClass, p); } - @Deprecated // since 2.9 - protected StackTraceElement constructValue(DeserializationContext ctxt, - String className, String methodName, String fileName, int lineNumber, - String moduleName, String moduleVersion) { - return constructValue(ctxt, className, methodName, fileName, lineNumber, - moduleName, moduleVersion, null); - } - /** * Overridable factory method used for constructing {@link StackTraceElement}s. - * - * @since 2.8 */ protected StackTraceElement constructValue(DeserializationContext ctxt, String className, String methodName, String fileName, int lineNumber, diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDelegatingDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdConvertingDeserializer.java similarity index 81% rename from src/main/java/com/fasterxml/jackson/databind/deser/std/StdDelegatingDeserializer.java rename to src/main/java/com/fasterxml/jackson/databind/deser/std/StdConvertingDeserializer.java index 0b7e0ed080..ddf67b8b52 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDelegatingDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdConvertingDeserializer.java @@ -5,8 +5,6 @@ import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.deser.ContextualDeserializer; -import com.fasterxml.jackson.databind.deser.ResolvableDeserializer; import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; import com.fasterxml.jackson.databind.util.ClassUtil; import com.fasterxml.jackson.databind.util.Converter; @@ -22,19 +20,15 @@ * Note that although types (delegate, target) may be related, they must not be same; trying * to do this will result in an exception. *

    - * Since 2.5 There is {@link StdNodeBasedDeserializer} that is a simplified version - * for cases where intermediate type is {@link JsonNode} + * Also note that in Jackson 2.x, this class was named {@code StdDelegatingDeserializer} * * @param Target type to convert to, from delegate type * - * @since 2.1 - * * @see StdNodeBasedDeserializer * @see Converter */ -public class StdDelegatingDeserializer +public class StdConvertingDeserializer extends StdDeserializer - implements ContextualDeserializer, ResolvableDeserializer { private static final long serialVersionUID = 1L; @@ -54,13 +48,13 @@ public class StdDelegatingDeserializer protected final JsonDeserializer _delegateDeserializer; /* - /********************************************************** + /********************************************************************** /* Life-cycle - /********************************************************** + /********************************************************************** */ @SuppressWarnings("unchecked") - public StdDelegatingDeserializer(Converter converter) + public StdConvertingDeserializer(Converter converter) { super(Object.class); _converter = (Converter)converter; @@ -69,7 +63,7 @@ public StdDelegatingDeserializer(Converter converter) } @SuppressWarnings("unchecked") - public StdDelegatingDeserializer(Converter converter, + public StdConvertingDeserializer(Converter converter, JavaType delegateType, JsonDeserializer delegateDeserializer) { super(delegateType); @@ -78,10 +72,7 @@ public StdDelegatingDeserializer(Converter converter, _delegateDeserializer = (JsonDeserializer) delegateDeserializer; } - /** - * @since 2.5 - */ - protected StdDelegatingDeserializer(StdDelegatingDeserializer src) + protected StdConvertingDeserializer(StdConvertingDeserializer src) { super(src); _converter = src._converter; @@ -93,17 +84,17 @@ protected StdDelegatingDeserializer(StdDelegatingDeserializer src) * Method used for creating resolved contextual instances. Must be * overridden when sub-classing. */ - protected StdDelegatingDeserializer withDelegate(Converter converter, + protected StdConvertingDeserializer withDelegate(Converter converter, JavaType delegateType, JsonDeserializer delegateDeserializer) { - ClassUtil.verifyMustOverride(StdDelegatingDeserializer.class, this, "withDelegate"); - return new StdDelegatingDeserializer(converter, delegateType, delegateDeserializer); + ClassUtil.verifyMustOverride(StdConvertingDeserializer.class, this, "withDelegate"); + return new StdConvertingDeserializer(converter, delegateType, delegateDeserializer); } /* - /********************************************************** + /********************************************************************** /* Contextualization - /********************************************************** + /********************************************************************** */ // Note: unlikely to get called since most likely instances explicitly constructed; @@ -112,8 +103,8 @@ protected StdDelegatingDeserializer withDelegate(Converter converte public void resolve(DeserializationContext ctxt) throws JsonMappingException { - if (_delegateDeserializer != null && _delegateDeserializer instanceof ResolvableDeserializer) { - ((ResolvableDeserializer) _delegateDeserializer).resolve(ctxt); + if (_delegateDeserializer != null) { + _delegateDeserializer.resolve(ctxt); } } @@ -137,9 +128,9 @@ public JsonDeserializer createContextual(DeserializationContext ctxt, BeanPro } /* - /********************************************************** + /********************************************************************** /* Accessors - /********************************************************** + /********************************************************************** */ @Override @@ -152,17 +143,25 @@ public Class handledType() { return _delegateDeserializer.handledType(); } - @Override // since 2.9 + /** + * Let's assume that as long as delegate is cachable, we are too. + */ + @Override + public boolean isCachable() { + return (_delegateDeserializer != null) && _delegateDeserializer.isCachable(); + } + + @Override public Boolean supportsUpdate(DeserializationConfig config) { return _delegateDeserializer.supportsUpdate(config); } /* - /********************************************************** + /********************************************************************** /* Serialization - /********************************************************** + /********************************************************************** */ - + @Override public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { @@ -213,8 +212,6 @@ public T deserialize(JsonParser p, DeserializationContext ctxt, Object intoValue * an operation that can be permitted, and the default behavior is to throw exception. * Sub-classes may choose to try alternative approach if they have more information on * exact usage and constraints. - * - * @since 2.6 */ protected Object _handleIncompatibleUpdateValue(JsonParser p, DeserializationContext ctxt, Object intoValue) throws IOException @@ -223,11 +220,11 @@ protected Object _handleIncompatibleUpdateValue(JsonParser p, DeserializationCon ("Cannot update object of type %s (using deserializer for type %s)" +intoValue.getClass().getName(), _delegateType)); } - + /* - /********************************************************** + /********************************************************************** /* Overridable methods - /********************************************************** + /********************************************************************** */ /** 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 049f993a57..57a53311d4 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 @@ -5,15 +5,14 @@ import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.Nulls; + import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.exc.InputCoercionException; import com.fasterxml.jackson.core.io.NumberInput; + import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; -import com.fasterxml.jackson.databind.deser.BeanDeserializerBase; -import com.fasterxml.jackson.databind.deser.NullValueProvider; -import com.fasterxml.jackson.databind.deser.SettableBeanProperty; -import com.fasterxml.jackson.databind.deser.ValueInstantiator; +import com.fasterxml.jackson.databind.deser.*; import com.fasterxml.jackson.databind.deser.impl.NullsAsEmptyProvider; import com.fasterxml.jackson.databind.deser.impl.NullsConstantProvider; import com.fasterxml.jackson.databind.deser.impl.NullsFailProvider; @@ -38,14 +37,11 @@ public abstract class StdDeserializer * Bitmask that covers {@link DeserializationFeature#USE_BIG_INTEGER_FOR_INTS} * and {@link DeserializationFeature#USE_LONG_FOR_INTS}, used for more efficient * cheks when coercing integral values for untyped deserialization. - * - * @since 2.6 */ protected final static int F_MASK_INT_COERCIONS = DeserializationFeature.USE_BIG_INTEGER_FOR_INTS.getMask() | DeserializationFeature.USE_LONG_FOR_INTS.getMask(); - // @since 2.9 protected final static int F_MASK_ACCEPT_ARRAYS = DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS.getMask() | DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT.getMask(); @@ -60,45 +56,39 @@ public abstract class StdDeserializer final protected Class _valueClass; protected StdDeserializer(Class vc) { + if (vc == null) { + throw new IllegalArgumentException("`null` not accepted as handled type"); + } _valueClass = vc; } protected StdDeserializer(JavaType valueType) { - // 26-Sep-2017, tatu: [databind#1764] need to add null-check back until 3.x - _valueClass = (valueType == null) ? Object.class : valueType.getRawClass(); + this(ClassUtil.rawClass(valueType)); } /** * Copy-constructor for sub-classes to use, most often when creating - * new instances for {@link com.fasterxml.jackson.databind.deser.ContextualDeserializer}. - * - * @since 2.5 + * new instances via {@link com.fasterxml.jackson.databind.JsonDeserializer#createContextual}. */ protected StdDeserializer(StdDeserializer src) { _valueClass = src._valueClass; } /* - /********************************************************** + /********************************************************************** /* Accessors - /********************************************************** + /********************************************************************** */ @Override public Class handledType() { return _valueClass; } /* - /********************************************************** + /********************************************************************** /* Extended API - /********************************************************** + /********************************************************************** */ - /** - * @deprecated Since 2.3 use {@link #handledType} instead - */ - @Deprecated - public final Class getValueClass() { return _valueClass; } - /** * Exact structured type this deserializer handles, if known. *

    @@ -119,11 +109,11 @@ protected boolean isDefaultDeserializer(JsonDeserializer deserializer) { protected boolean isDefaultKeyDeserializer(KeyDeserializer keyDeser) { return ClassUtil.isJacksonStdImpl(keyDeser); } - + /* - /********************************************************** - /* Partial JsonDeserializer implementation - /********************************************************** + /********************************************************************** + /* Partial deserialize method implementation + /********************************************************************** */ /** @@ -138,16 +128,16 @@ public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, } /* - /********************************************************** + /********************************************************************** /* Helper methods for sub-classes, parsing: while mostly /* useful for numeric types, can be also useful for dealing /* with things serialized as numbers (such as Dates). - /********************************************************** + /********************************************************************** */ protected final boolean _parseBooleanPrimitive(JsonParser p, DeserializationContext ctxt) throws IOException { - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == JsonToken.VALUE_TRUE) return true; if (t == JsonToken.VALUE_FALSE) return false; if (t == JsonToken.VALUE_NULL) { @@ -233,7 +223,7 @@ protected final int _parseIntPrimitive(JsonParser p, DeserializationContext ctxt if (p.hasToken(JsonToken.VALUE_NUMBER_INT)) { return p.getIntValue(); } - switch (p.getCurrentTokenId()) { + switch (p.currentTokenId()) { case JsonTokenId.ID_STRING: String text = p.getText().trim(); if (_isEmptyOrTextualNull(text)) { @@ -263,9 +253,6 @@ protected final int _parseIntPrimitive(JsonParser p, DeserializationContext ctxt return ((Number) ctxt.handleUnexpectedToken(_valueClass, p)).intValue(); } - /** - * @since 2.9 - */ protected final int _parseIntPrimitive(DeserializationContext ctxt, String text) throws IOException { try { @@ -293,7 +280,7 @@ protected final long _parseLongPrimitive(JsonParser p, DeserializationContext ct if (p.hasToken(JsonToken.VALUE_NUMBER_INT)) { return p.getLongValue(); } - switch (p.getCurrentTokenId()) { + switch (p.currentTokenId()) { case JsonTokenId.ID_STRING: String text = p.getText().trim(); if (_isEmptyOrTextualNull(text)) { @@ -321,9 +308,6 @@ protected final long _parseLongPrimitive(JsonParser p, DeserializationContext ct return ((Number) ctxt.handleUnexpectedToken(_valueClass, p)).longValue(); } - /** - * @since 2.9 - */ protected final long _parseLongPrimitive(DeserializationContext ctxt, String text) throws IOException { try { @@ -342,7 +326,7 @@ protected final float _parseFloatPrimitive(JsonParser p, DeserializationContext if (p.hasToken(JsonToken.VALUE_NUMBER_FLOAT)) { return p.getFloatValue(); } - switch (p.getCurrentTokenId()) { + switch (p.currentTokenId()) { case JsonTokenId.ID_STRING: String text = p.getText().trim(); if (_isEmptyOrTextualNull(text)) { @@ -403,7 +387,7 @@ protected final double _parseDoublePrimitive(JsonParser p, DeserializationContex if (p.hasToken(JsonToken.VALUE_NUMBER_FLOAT)) { return p.getDoubleValue(); } - switch (p.getCurrentTokenId()) { + switch (p.currentTokenId()) { case JsonTokenId.ID_STRING: String text = p.getText().trim(); if (_isEmptyOrTextualNull(text)) { @@ -463,7 +447,7 @@ protected final double _parseDoublePrimitive(DeserializationContext ctxt, String protected java.util.Date _parseDate(JsonParser p, DeserializationContext ctxt) throws IOException { - switch (p.getCurrentTokenId()) { + switch (p.currentTokenId()) { case JsonTokenId.ID_STRING: return _parseDate(p.getText().trim(), ctxt); case JsonTokenId.ID_NUMBER_INT: @@ -506,7 +490,7 @@ protected java.util.Date _parseDateFromArray(JsonParser p, DeserializationContex return parsed; } } else { - t = p.getCurrentToken(); + t = p.currentToken(); } return (java.util.Date) ctxt.handleUnexpectedToken(_valueClass, t, p, null); } @@ -551,7 +535,7 @@ protected final static double parseDouble(String numStr) throws NumberFormatExce */ protected final String _parseString(JsonParser p, DeserializationContext ctxt) throws IOException { - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == JsonToken.VALUE_STRING) { return p.getText(); } @@ -576,14 +560,12 @@ protected final String _parseString(JsonParser p, DeserializationContext ctxt) t /** * 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 */ @SuppressWarnings("unchecked") protected T _deserializeFromEmpty(JsonParser p, DeserializationContext ctxt) throws IOException { - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == JsonToken.START_ARRAY) { if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT)) { t = p.nextToken(); @@ -632,10 +614,9 @@ protected final boolean _isPosInf(String text) { protected final boolean _isNaN(String text) { return "NaN".equals(text); } /* - /********************************************************** - /* Helper methods for sub-classes regarding decoding from - /* alternate representations - /********************************************************** + /********************************************************************** + /* Helper methods for sub-classes regarding decoding from alternate representations + /********************************************************************** */ /** @@ -651,8 +632,6 @@ protected final boolean _isPosInf(String text) { * NOTE: in case of unwrapped single element, will handle actual decoding * by calling {@link #_deserializeWrappedValue}, which by default calls * {@link #deserialize(JsonParser, DeserializationContext)}. - * - * @since 2.9 */ protected T _deserializeFromArray(JsonParser p, DeserializationContext ctxt) throws IOException { @@ -672,7 +651,7 @@ protected T _deserializeFromArray(JsonParser p, DeserializationContext ctxt) thr return parsed; } } else { - t = p.getCurrentToken(); + t = p.currentToken(); } @SuppressWarnings("unchecked") T result = (T) ctxt.handleUnexpectedToken(_valueClass, t, p, null); @@ -697,16 +676,16 @@ protected T _deserializeWrappedValue(JsonParser p, DeserializationContext ctxt) ClassUtil.nameOf(_valueClass), JsonToken.START_ARRAY, "DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS"); @SuppressWarnings("unchecked") - T result = (T) ctxt.handleUnexpectedToken(_valueClass, p.getCurrentToken(), p, msg); + T result = (T) ctxt.handleUnexpectedToken(_valueClass, p.currentToken(), p, msg); return result; } return (T) deserialize(p, ctxt); } /* - /**************************************************** + /********************************************************************** /* Helper methods for sub-classes, coercions - /**************************************************** + /********************************************************************** */ protected void _failDoubleToIntCoercion(JsonParser p, DeserializationContext ctxt, @@ -725,8 +704,6 @@ protected void _failDoubleToIntCoercion(JsonParser p, DeserializationContext ctx * * @see DeserializationFeature#USE_BIG_INTEGER_FOR_INTS * @see DeserializationFeature#USE_LONG_FOR_INTS - * - * @since 2.6 */ protected Object _coerceIntegral(JsonParser p, DeserializationContext ctxt) throws IOException { @@ -764,8 +741,8 @@ protected Object _coerceTextualNull(DeserializationContext ctxt, boolean isPrimi Enum feat; boolean enable; - if (!ctxt.isEnabled(MapperFeature.ALLOW_COERCION_OF_SCALARS)) { - feat = MapperFeature.ALLOW_COERCION_OF_SCALARS; + if (!ctxt.isEnabled(DeserializationFeature.ALLOW_COERCION_OF_SCALARS)) { + feat = DeserializationFeature.ALLOW_COERCION_OF_SCALARS; enable = true; } else if (isPrimitive && ctxt.isEnabled(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)) { feat = DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES; @@ -787,8 +764,8 @@ protected Object _coerceEmptyString(DeserializationContext ctxt, boolean isPrimi Enum feat; boolean enable; - if (!ctxt.isEnabled(MapperFeature.ALLOW_COERCION_OF_SCALARS)) { - feat = MapperFeature.ALLOW_COERCION_OF_SCALARS; + if (!ctxt.isEnabled(DeserializationFeature.ALLOW_COERCION_OF_SCALARS)) { + feat = DeserializationFeature.ALLOW_COERCION_OF_SCALARS; enable = true; } else if (isPrimitive && ctxt.isEnabled(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)) { feat = DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES; @@ -817,8 +794,8 @@ protected final void _verifyNullForPrimitiveCoercion(DeserializationContext ctxt Enum feat; boolean enable; - if (!ctxt.isEnabled(MapperFeature.ALLOW_COERCION_OF_SCALARS)) { - feat = MapperFeature.ALLOW_COERCION_OF_SCALARS; + if (!ctxt.isEnabled(DeserializationFeature.ALLOW_COERCION_OF_SCALARS)) { + feat = DeserializationFeature.ALLOW_COERCION_OF_SCALARS; enable = true; } else if (ctxt.isEnabled(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)) { feat = DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES; @@ -834,16 +811,16 @@ protected final void _verifyNullForPrimitiveCoercion(DeserializationContext ctxt // @since 2.9 protected final void _verifyNullForScalarCoercion(DeserializationContext ctxt, String str) throws JsonMappingException { - if (!ctxt.isEnabled(MapperFeature.ALLOW_COERCION_OF_SCALARS)) { + if (!ctxt.isEnabled(DeserializationFeature.ALLOW_COERCION_OF_SCALARS)) { String strDesc = str.isEmpty() ? "empty String (\"\")" : String.format("String \"%s\"", str); - _reportFailedNullCoerce(ctxt, true, MapperFeature.ALLOW_COERCION_OF_SCALARS, strDesc); + _reportFailedNullCoerce(ctxt, true, DeserializationFeature.ALLOW_COERCION_OF_SCALARS, strDesc); } } // @since 2.9 protected void _verifyStringForScalarCoercion(DeserializationContext ctxt, String str) throws JsonMappingException { - MapperFeature feat = MapperFeature.ALLOW_COERCION_OF_SCALARS; + DeserializationFeature feat = DeserializationFeature.ALLOW_COERCION_OF_SCALARS; if (!ctxt.isEnabled(feat)) { ctxt.reportInputMismatch(this, "Cannot coerce String \"%s\" %s (enable `%s.%s` to allow)", str, _coercedTypeDesc(), feat.getClass().getSimpleName(), feat.name()); @@ -853,7 +830,7 @@ protected void _verifyStringForScalarCoercion(DeserializationContext ctxt, Strin // @since 2.9 protected void _verifyNumberForScalarCoercion(DeserializationContext ctxt, JsonParser p) throws IOException { - MapperFeature feat = MapperFeature.ALLOW_COERCION_OF_SCALARS; + DeserializationFeature feat = DeserializationFeature.ALLOW_COERCION_OF_SCALARS; if (!ctxt.isEnabled(feat)) { // 31-Mar-2017, tatu: Since we don't know (or this deep, care) about exact type, // access as a String: may require re-encoding by parser which should be fine @@ -877,8 +854,6 @@ protected void _reportFailedNullCoerce(DeserializationContext ctxt, boolean stat * on coerce failure. * * @return Message with backtick-enclosed name of type this deserializer supports - * - * @since 2.9 */ protected String _coercedTypeDesc() { boolean structured; @@ -902,9 +877,9 @@ protected String _coercedTypeDesc() { } /* - /**************************************************** + /********************************************************************** /* Helper methods for sub-classes, resolving dependencies - /**************************************************** + /********************************************************************** */ /** @@ -946,9 +921,9 @@ protected final boolean _isIntNumber(String text) } /* - /********************************************************** + /********************************************************************** /* Helper methods for: deserializer construction - /********************************************************** + /********************************************************************** */ /** @@ -958,8 +933,6 @@ protected final boolean _isIntNumber(String text) * * @param existingDeserializer (optional) configured content * serializer if one already exists. - * - * @since 2.2 */ protected JsonDeserializer findConvertingContentDeserializer(DeserializationContext ctxt, BeanProperty prop, JsonDeserializer existingDeserializer) @@ -969,14 +942,14 @@ protected JsonDeserializer findConvertingContentDeserializer(DeserializationC if (_neitherNull(intr, prop)) { AnnotatedMember member = prop.getMember(); if (member != null) { - Object convDef = intr.findDeserializationContentConverter(member); + Object convDef = intr.findDeserializationContentConverter(ctxt.getConfig(), member); if (convDef != null) { Converter conv = ctxt.converterInstance(prop.getMember(), convDef); JavaType delegateType = conv.getInputType(ctxt.getTypeFactory()); if (existingDeserializer == null) { existingDeserializer = ctxt.findContextualValueDeserializer(delegateType, prop); } - return new StdDelegatingDeserializer(conv, delegateType, existingDeserializer); + return new StdConvertingDeserializer(conv, delegateType, existingDeserializer); } } } @@ -984,9 +957,9 @@ protected JsonDeserializer findConvertingContentDeserializer(DeserializationC } /* - /********************************************************** + /********************************************************************** /* Helper methods for: accessing contextual config settings - /********************************************************** + /********************************************************************** */ /** @@ -995,8 +968,6 @@ protected JsonDeserializer findConvertingContentDeserializer(DeserializationC * defaulting. * * @param typeForDefaults Type (erased) used for finding default format settings, if any - * - * @since 2.7 */ protected JsonFormat.Value findFormatOverrides(DeserializationContext ctxt, BeanProperty prop, Class typeForDefaults) @@ -1015,8 +986,6 @@ protected JsonFormat.Value findFormatOverrides(DeserializationContext ctxt, * to find whether that feature has been specifically marked as enabled or disabled. * * @param typeForDefaults Type (erased) used for finding default format settings, if any - * - * @since 2.7 */ protected Boolean findFormatFeature(DeserializationContext ctxt, BeanProperty prop, Class typeForDefaults, JsonFormat.Feature feat) @@ -1032,8 +1001,6 @@ protected Boolean findFormatFeature(DeserializationContext ctxt, * Method called to find {@link NullValueProvider} for a primary property, using * "value nulls" setting. If no provider found (not defined, or is "skip"), * will return `null`. - * - * @since 2.9 */ protected final NullValueProvider findValueNullProvider(DeserializationContext ctxt, SettableBeanProperty prop, PropertyMetadata propMetadata) @@ -1051,8 +1018,6 @@ protected final NullValueProvider findValueNullProvider(DeserializationContext c * primary property (Collection, Map, array), using * "content nulls" setting. If no provider found (not defined), * will return given value deserializer (which is a null value provider itself). - * - * @since 2.9 */ protected NullValueProvider findContentNullProvider(DeserializationContext ctxt, BeanProperty prop, JsonDeserializer valueDeser) @@ -1078,7 +1043,6 @@ protected Nulls findContentNullStyle(DeserializationContext ctxt, BeanProperty p return null; } - // @since 2.9 protected final NullValueProvider _findNullProvider(DeserializationContext ctxt, BeanProperty prop, Nulls nulls, JsonDeserializer valueDeser) throws JsonMappingException @@ -1126,9 +1090,9 @@ protected final NullValueProvider _findNullProvider(DeserializationContext ctxt, } /* - /********************************************************** + /********************************************************************** /* Helper methods for sub-classes, problem reporting - /********************************************************** + /********************************************************************** */ /** @@ -1142,7 +1106,7 @@ protected final NullValueProvider _findNullProvider(DeserializationContext ctxt, * error reporting functionality * @param instanceOrClass Instance that is being populated by this * deserializer, or if not known, Class that would be instantiated. - * If null, will assume type is what {@link #getValueClass} returns. + * If null, will assume type is what {@link #handledType} returns. * @param propName Name of the property that cannot be mapped */ protected void handleUnknownProperty(JsonParser p, DeserializationContext ctxt, @@ -1156,9 +1120,8 @@ protected void handleUnknownProperty(JsonParser p, DeserializationContext ctxt, if (ctxt.handleUnknownProperty(p, this, instanceOrClass, propName)) { return; } - /* But if we do get this far, need to skip whatever value we - * are pointing to now (although handler is likely to have done that already) - */ + // But if we do get this far, need to skip whatever value we + // are pointing to now (although handler is likely to have done that already) p.skipChildren(); } @@ -1181,44 +1144,29 @@ protected void _verifyEndArrayForSingle(JsonParser p, DeserializationContext ctx } /* - /********************************************************** + /********************************************************************** /* Helper methods, other - /********************************************************** + /********************************************************************** */ - /** - * @since 2.9 - */ protected final static boolean _neitherNull(Object a, Object b) { return (a != null) && (b != null); } - /** - * @since 2.9 - */ protected final boolean _byteOverflow(int value) { // 07-nov-2016, tatu: We support "unsigned byte" as well // as Java signed range since that's relatively common usage return (value < Byte.MIN_VALUE || value > 255); } - - /** - * @since 2.9 - */ + protected final boolean _shortOverflow(int value) { return (value < Short.MIN_VALUE || value > Short.MAX_VALUE); } - /** - * @since 2.9 - */ protected final boolean _intOverflow(long value) { return (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE); } - /** - * @since 2.9 - */ protected Number _nonNullNumber(Number n) { if (n == null) { n = Integer.valueOf(0); diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/JdkDeserializers.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdJdkDeserializers.java similarity index 77% rename from src/main/java/com/fasterxml/jackson/databind/deser/std/JdkDeserializers.java rename to src/main/java/com/fasterxml/jackson/databind/deser/std/StdJdkDeserializers.java index 7743deae26..abdc636b3a 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/JdkDeserializers.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdJdkDeserializers.java @@ -10,20 +10,19 @@ * Container class that contains serializers for JDK types that * require special handling for some reason. */ -public class JdkDeserializers +public class StdJdkDeserializers { private final static HashSet _classNames = new HashSet(); static { // note: can skip primitive types; other ways to check them: - Class[] types = new Class[] { - UUID.class, - AtomicBoolean.class, - StackTraceElement.class, - ByteBuffer.class, - Void.class - }; - for (Class cls : types) { _classNames.add(cls.getName()); } - for (Class cls : FromStringDeserializer.types()) { _classNames.add(cls.getName()); } + _classNames.add(UUID.class.getName()); + _classNames.add(AtomicBoolean.class.getName()); + _classNames.add(StackTraceElement.class.getName()); + _classNames.add(ByteBuffer.class.getName()); + _classNames.add(Void.class.getName()); + for (Class cls : FromStringDeserializer.types()) { + _classNames.add(cls.getName()); + } } public static JsonDeserializer find(Class rawType, String clsName) 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 5b12270391..c2ddab4be4 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 @@ -45,16 +45,18 @@ public static KeyDeserializer constructDelegatingKeyDeserializer(Deserialization return new StdKeyDeserializer.DelegatingKD(type.getRawClass(), deser); } - public static KeyDeserializer findStringBasedKeyDeserializer(DeserializationConfig config, + public static KeyDeserializer findStringBasedKeyDeserializer(DeserializationContext ctxt, JavaType type) + throws JsonMappingException { // We don't need full deserialization information, just need to know creators. - BeanDescription beanDesc = config.introspect(type); + BeanDescription beanDesc = ctxt.introspect(type); + // Ok, so: can we find T(String) constructor? Constructor ctor = beanDesc.findSingleArgConstructor(String.class); if (ctor != null) { - if (config.canOverrideAccessModifiers()) { - ClassUtil.checkAndFixAccess(ctor, config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); + if (ctxt.canOverrideAccessModifiers()) { + ClassUtil.checkAndFixAccess(ctor, ctxt.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); } return new StdKeyDeserializer.StringCtorKeyDeserializer(ctor); } @@ -63,8 +65,8 @@ public static KeyDeserializer findStringBasedKeyDeserializer(DeserializationConf */ Method m = beanDesc.findFactoryMethod(String.class); if (m != null){ - if (config.canOverrideAccessModifiers()) { - ClassUtil.checkAndFixAccess(m, config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); + if (ctxt.canOverrideAccessModifiers()) { + ClassUtil.checkAndFixAccess(m, ctxt.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); } return new StdKeyDeserializer.StringFactoryKeyDeserializer(m); } diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdNodeBasedDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdNodeBasedDeserializer.java index b48340c962..d08a2c5acb 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdNodeBasedDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdNodeBasedDeserializer.java @@ -4,31 +4,27 @@ import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.deser.ResolvableDeserializer; import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; /** * Convenience deserializer that may be used to deserialize values given an * intermediate tree representation ({@link JsonNode}). - * Note that this is a slightly simplified alternative to {@link StdDelegatingDeserializer}). + * Note that this is a slightly simplified alternative to {@link StdConvertingDeserializer}). * * @param Target type of this deserializer; that is, type of values that * input data is deserialized into. - * - * @since 2.5 */ public abstract class StdNodeBasedDeserializer extends StdDeserializer - implements ResolvableDeserializer { private static final long serialVersionUID = 1L; protected JsonDeserializer _treeDeserializer; /* - /********************************************************** + /********************************************************************** /* Life-cycle - /********************************************************** + /********************************************************************** */ protected StdNodeBasedDeserializer(JavaType targetType) { @@ -41,7 +37,7 @@ protected StdNodeBasedDeserializer(Class targetType) { /** * "Copy-constructor" used when creating a modified copies, most often - * if sub-class implements {@link com.fasterxml.jackson.databind.deser.ContextualDeserializer}. + * if sub-class overrides {@link com.fasterxml.jackson.databind.JsonDeserializer#createContextual}. */ protected StdNodeBasedDeserializer(StdNodeBasedDeserializer src) { super(src); @@ -54,34 +50,33 @@ public void resolve(DeserializationContext ctxt) throws JsonMappingException { } /* - /********************************************************** + /********************************************************************** /* Abstract methods for sub-classes - /********************************************************** + /********************************************************************** */ public abstract T convert(JsonNode root, DeserializationContext ctxt) throws IOException; /* - /********************************************************** + /********************************************************************** /* JsonDeserializer impl - /********************************************************** + /********************************************************************** */ @Override - public T deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { - JsonNode n = (JsonNode) _treeDeserializer.deserialize(jp, ctxt); + public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + JsonNode n = (JsonNode) _treeDeserializer.deserialize(p, ctxt); return convert(n, ctxt); } @Override - public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, + public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer td) - throws IOException, JsonProcessingException + throws IOException { - /* 19-Nov-2014, tatu: Quite likely we'd have some issues but... let's - * try, just in case. - */ - JsonNode n = (JsonNode) _treeDeserializer.deserializeWithType(jp, ctxt, td); + // 19-Nov-2014, tatu: Quite likely we'd have some issues but... let's + // try, just in case. + JsonNode n = (JsonNode) _treeDeserializer.deserializeWithType(p, ctxt, td); return convert(n, ctxt); } } 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 e4ac0fd6fd..6f8eee3f1f 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 @@ -6,9 +6,7 @@ import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; import com.fasterxml.jackson.databind.deser.*; -import com.fasterxml.jackson.databind.introspect.AnnotatedParameter; import com.fasterxml.jackson.databind.introspect.AnnotatedWithParams; -import com.fasterxml.jackson.databind.util.ClassUtil; /** * Default {@link ValueInstantiator} implementation, which supports @@ -28,9 +26,6 @@ public class StdValueInstantiator */ protected final String _valueTypeDesc; - /** - * @since 2.8 - */ protected final Class _valueClass; // // // Default (no-args) construction @@ -66,27 +61,20 @@ public class StdValueInstantiator protected AnnotatedWithParams _fromDoubleCreator; protected AnnotatedWithParams _fromBooleanCreator; - // // // Incomplete creator - protected AnnotatedParameter _incompleteParameter; - /* - /********************************************************** + /********************************************************************** /* Life-cycle - /********************************************************** - */ - - /** - * @deprecated Since 2.7 use constructor that takes {@link JavaType} instead + /********************************************************************** */ - @Deprecated - public StdValueInstantiator(DeserializationConfig config, Class valueType) { - _valueTypeDesc = ClassUtil.nameOf(valueType); - _valueClass = (valueType == null) ? Object.class : valueType; - } public StdValueInstantiator(DeserializationConfig config, JavaType valueType) { - _valueTypeDesc = (valueType == null) ? "UNKNOWN TYPE" : valueType.toString(); - _valueClass = (valueType == null) ? Object.class : valueType.getRawClass(); + if (valueType == null) { + _valueTypeDesc = "UNKNOWN TYPE"; + _valueClass = Object.class; + } else { + _valueTypeDesc = valueType.toString(); + _valueClass = valueType.getRawClass(); + } } /** @@ -165,14 +153,10 @@ public void configureFromBooleanCreator(AnnotatedWithParams creator) { _fromBooleanCreator = creator; } - public void configureIncompleteParameter(AnnotatedParameter parameter) { - _incompleteParameter = parameter; - } - /* - /********************************************************** + /********************************************************************** /* Public API implementation; metadata - /********************************************************** + /********************************************************************** */ @Override @@ -250,14 +234,14 @@ public JavaType getArrayDelegateType(DeserializationConfig config) { } @Override - public SettableBeanProperty[] getFromObjectArguments(DeserializationConfig config) { + public SettableBeanProperty[] getFromObjectArguments(DeserializationContext ctxt) { return _constructorArguments; } - + /* - /********************************************************** + /********************************************************************** /* Public API implementation; instantiation from JSON Object - /********************************************************** + /********************************************************************** */ @Override @@ -311,9 +295,9 @@ public Object createUsingArrayDelegate(DeserializationContext ctxt, Object deleg } /* - /********************************************************** + /********************************************************************** /* Public API implementation; instantiation from JSON scalars - /********************************************************** + /********************************************************************** */ @Override @@ -402,9 +386,9 @@ public Object createFromBoolean(DeserializationContext ctxt, boolean value) thro } /* - /********************************************************** + /********************************************************************** /* Extended API: configuration mutators, accessors - /********************************************************** + /********************************************************************** */ @Override @@ -427,59 +411,17 @@ public AnnotatedWithParams getWithArgsCreator() { return _withArgsCreator; } - @Override - public AnnotatedParameter getIncompleteParameter() { - return _incompleteParameter; - } - /* - /********************************************************** + /********************************************************************** /* Internal methods - /********************************************************** + /********************************************************************** */ - /** - * @deprecated Since 2.7 call either {@link #rewrapCtorProblem} or - * {@link #wrapAsJsonMappingException} - */ - @Deprecated // since 2.7 - protected JsonMappingException wrapException(Throwable t) - { - // 05-Nov-2015, tatu: This used to always unwrap the whole exception, but now only - // does so if and until `JsonMappingException` is found. - for (Throwable curr = t; curr != null; curr = curr.getCause()) { - if (curr instanceof JsonMappingException) { - return (JsonMappingException) curr; - } - } - return new JsonMappingException(null, - "Instantiation of "+getValueTypeDesc()+" value failed: "+ClassUtil.exceptionMessage(t), t); - } - - /** - * @deprecated Since 2.7 call either {@link #rewrapCtorProblem} or - * {@link #wrapAsJsonMappingException} - */ - @Deprecated // since 2.10 - protected JsonMappingException unwrapAndWrapException(DeserializationContext ctxt, Throwable t) - { - // 05-Nov-2015, tatu: This used to always unwrap the whole exception, but now only - // does so if and until `JsonMappingException` is found. - for (Throwable curr = t; curr != null; curr = curr.getCause()) { - if (curr instanceof JsonMappingException) { - return (JsonMappingException) curr; - } - } - return ctxt.instantiationException(getValueClass(), t); - } - /** * Helper method that will return given {@link Throwable} case as * a {@link JsonMappingException} (if it is of that type), or call * {@link DeserializationContext#instantiationException(Class, Throwable)} to * produce and return suitable {@link JsonMappingException}. - * - * @since 2.7 */ protected JsonMappingException wrapAsJsonMappingException(DeserializationContext ctxt, Throwable t) @@ -495,8 +437,6 @@ protected JsonMappingException wrapAsJsonMappingException(DeserializationContext * Method that subclasses may call for standard handling of an exception thrown when * calling constructor or factory method. Will unwrap {@link ExceptionInInitializerError} * and {@link InvocationTargetException}s, then call {@link #wrapAsJsonMappingException}. - * - * @since 2.7 */ protected JsonMappingException rewrapCtorProblem(DeserializationContext ctxt, Throwable t) @@ -515,16 +455,15 @@ protected JsonMappingException rewrapCtorProblem(DeserializationContext ctxt, } /* - /********************************************************** + /********************************************************************** /* Helper methods - /********************************************************** + /********************************************************************** */ private Object _createUsingDelegate(AnnotatedWithParams delegateCreator, SettableBeanProperty[] delegateArguments, - DeserializationContext ctxt, - Object delegate) - throws IOException + DeserializationContext ctxt, Object delegate) + throws IOException { if (delegateCreator == null) { // sanity-check; caller should check throw new IllegalStateException("No delegate constructor for "+getValueTypeDesc()); 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 a348a4019a..dcefad1e42 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 @@ -6,7 +6,6 @@ import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; -import com.fasterxml.jackson.databind.deser.ContextualDeserializer; import com.fasterxml.jackson.databind.deser.NullValueProvider; import com.fasterxml.jackson.databind.deser.impl.NullsConstantProvider; import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; @@ -24,7 +23,6 @@ public final class StringArrayDeserializer // extends ContainerDeserializerBase // but for now won't: extends StdDeserializer - implements ContextualDeserializer { private static final long serialVersionUID = 2L; @@ -144,7 +142,7 @@ public String[] deserialize(JsonParser p, DeserializationContext ctxt) throws IO while (true) { String value = p.nextTextValue(); if (value == null) { - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == JsonToken.END_ARRAY) { break; } @@ -200,7 +198,7 @@ protected final String[] _deserializeCustom(JsonParser p, DeserializationContext */ String value; if (p.nextTextValue() == null) { - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == JsonToken.END_ARRAY) { break; } @@ -264,7 +262,7 @@ public String[] deserialize(JsonParser p, DeserializationContext ctxt, while (true) { String value = p.nextTextValue(); if (value == null) { - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == JsonToken.END_ARRAY) { break; } diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer.java index 321df6f294..2c82ef8b6f 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer.java @@ -7,7 +7,6 @@ import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; -import com.fasterxml.jackson.databind.deser.ContextualDeserializer; import com.fasterxml.jackson.databind.deser.NullValueProvider; import com.fasterxml.jackson.databind.deser.ValueInstantiator; import com.fasterxml.jackson.databind.introspect.AnnotatedWithParams; @@ -21,7 +20,6 @@ @JacksonStdImpl public final class StringCollectionDeserializer extends ContainerDeserializerBase> - implements ContextualDeserializer { private static final long serialVersionUID = 1L; @@ -190,7 +188,7 @@ public Collection deserialize(JsonParser p, DeserializationContext ctxt, result.add(value); continue; } - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == JsonToken.END_ARRAY) { break; } @@ -221,7 +219,7 @@ private Collection deserializeUsingCustom(JsonParser p, DeserializationC */ String value; if (p.nextTextValue() == null) { - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == JsonToken.END_ARRAY) { break; } @@ -267,7 +265,7 @@ private final Collection handleNonArray(JsonParser p, DeserializationCon } // Strings are one of "native" (intrinsic) types, so there's never type deserializer involved JsonDeserializer valueDes = _valueDeserializer; - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); String value; diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StringDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StringDeserializer.java index c563f4bddb..a4b25c060b 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StringDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StringDeserializer.java @@ -34,7 +34,7 @@ public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOEx if (p.hasToken(JsonToken.VALUE_STRING)) { return p.getText(); } - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); // [databind#381] if (t == JsonToken.START_ARRAY) { return _deserializeFromArray(p, ctxt); diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/ThrowableDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/ThrowableDeserializer.java index d2e8c6782f..f51f2b9b2d 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/ThrowableDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/ThrowableDeserializer.java @@ -3,10 +3,12 @@ import java.io.IOException; import com.fasterxml.jackson.core.*; - +import com.fasterxml.jackson.core.sym.FieldNameMatcher; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.deser.BeanDeserializer; import com.fasterxml.jackson.databind.deser.SettableBeanProperty; +import com.fasterxml.jackson.databind.deser.impl.BeanPropertyMap; +import com.fasterxml.jackson.databind.deser.impl.UnwrappedPropertyHandler; import com.fasterxml.jackson.databind.util.NameTransformer; /** @@ -35,20 +37,29 @@ public ThrowableDeserializer(BeanDeserializer baseDeserializer) { /** * Alternative constructor used when creating "unwrapping" deserializers */ - protected ThrowableDeserializer(BeanDeserializer src, NameTransformer unwrapper) { - super(src, unwrapper); + protected ThrowableDeserializer(BeanDeserializer src, + UnwrappedPropertyHandler unwrapHandler, BeanPropertyMap renamedProperties, + boolean ignoreAllUnknown) { + super(src, unwrapHandler, renamedProperties, ignoreAllUnknown); } @Override - public JsonDeserializer unwrappingDeserializer(NameTransformer unwrapper) { + public JsonDeserializer unwrappingDeserializer(DeserializationContext ctxt, + NameTransformer transformer) + { if (getClass() != ThrowableDeserializer.class) { return this; } - /* main thing really is to just enforce ignoring of unknown - * properties; since there may be multiple unwrapped values - * and properties for all may be interleaved... - */ - return new ThrowableDeserializer(this, unwrapper); + // main thing really is to just enforce ignoring of unknown properties; since + // there may be multiple unwrapped values and properties for all may be interleaved... + UnwrappedPropertyHandler uwHandler = _unwrappedPropertyHandler; + // delegate further unwraps, if any + if (uwHandler != null) { + uwHandler = uwHandler.renameAll(ctxt, transformer); + } + // and handle direct unwrapping as well: + return new ThrowableDeserializer(this, uwHandler, + _beanProperties.renameAll(ctxt, transformer), true); } /* @@ -84,12 +95,11 @@ public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) t Object[] pending = null; int pendingIx = 0; - for (; p.getCurrentToken() != JsonToken.END_OBJECT; p.nextToken()) { - String propName = p.getCurrentName(); - SettableBeanProperty prop = _beanProperties.find(propName); - p.nextToken(); // to point to field value - - if (prop != null) { // normal case + int ix = p.currentFieldName(_fieldMatcher); + for (; ; ix = p.nextFieldName(_fieldMatcher)) { + if (ix >= 0) { + p.nextToken(); + SettableBeanProperty prop = _fieldsByIndex[ix]; if (throwable != null) { prop.deserializeAndSet(p, ctxt, throwable); continue; @@ -103,8 +113,15 @@ public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) t pending[pendingIx++] = prop.deserialize(p, ctxt); continue; } - + if (ix != FieldNameMatcher.MATCH_UNKNOWN_NAME) { + if (ix == FieldNameMatcher.MATCH_END_OBJECT) { + break; + } + return _handleUnexpectedWithin(p, ctxt, throwable); + } // Maybe it's "message"? + String propName = p.currentName(); + p.nextToken(); final boolean isMessage = PROP_NAME_MESSAGE.equals(propName); if (isMessage) { if (hasStringCreator) { @@ -112,7 +129,7 @@ public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) t // any pending values? if (pending != null) { for (int i = 0, len = pendingIx; i < len; i += 2) { - prop = (SettableBeanProperty)pending[i]; + SettableBeanProperty prop = (SettableBeanProperty)pending[i]; prop.set(throwable, pending[i+1]); } pending = null; @@ -137,11 +154,11 @@ public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) t } // Sanity check: did we find "message"? if (throwable == null) { - /* 15-Oct-2010, tatu: Can't assume missing message is an error, since it may be - * suppressed during serialization, as per [JACKSON-388]. - * - * Should probably allow use of default constructor, too... - */ + // 15-Oct-2010, tatu: Can't assume missing message is an error, since it may be + // suppressed during serialization, as per [JACKSON-388]. + // + // Should probably allow use of default constructor, too... + //throw new JsonMappingException("No 'message' property found: could not deserialize "+_beanType); if (hasStringCreator) { throwable = _valueInstantiator.createFromString(ctxt, null); diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/TokenBufferDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/TokenBufferDeserializer.java index 5435e97f48..128299cb1d 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/TokenBufferDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/TokenBufferDeserializer.java @@ -32,6 +32,6 @@ public TokenBuffer deserialize(JsonParser p, DeserializationContext ctxt) throws } protected TokenBuffer createBufferInstance(JsonParser p) { - return new TokenBuffer(p); + return new TokenBuffer(p, null); } -} \ No newline at end of file +} diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/UUIDDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/UUIDDeserializer.java index 797569e6e5..7686292a33 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/UUIDDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/UUIDDeserializer.java @@ -32,9 +32,8 @@ protected UUID _deserialize(String id, DeserializationContext ctxt) throws IOExc // Adapted from java-uuid-generator (https://github.com/cowtowncoder/java-uuid-generator) // which is 5x faster than UUID.fromString(value), as oper "ManualReadPerfWithUUID" if (id.length() != 36) { - /* 14-Sep-2013, tatu: One trick we do allow, Base64-encoding, since we know - * length it must have... - */ + // 14-Sep-2013, tatu: One trick we do allow, Base64-encoding, since we know + // length it must have... if (id.length() == 24) { byte[] stuff = Base64Variants.getDefaultVariant().decode(id); return _fromBytes(stuff, ctxt); diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/UntypedObjectDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/UntypedObjectDeserializer.java index 67be23847a..8b17c0f2c7 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/UntypedObjectDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/UntypedObjectDeserializer.java @@ -4,16 +4,8 @@ import java.util.*; import com.fasterxml.jackson.core.*; -import com.fasterxml.jackson.databind.BeanProperty; -import com.fasterxml.jackson.databind.DeserializationConfig; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; -import com.fasterxml.jackson.databind.deser.ContextualDeserializer; -import com.fasterxml.jackson.databind.deser.ResolvableDeserializer; import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; import com.fasterxml.jackson.databind.type.TypeFactory; import com.fasterxml.jackson.databind.util.ClassUtil; @@ -32,7 +24,6 @@ @JacksonStdImpl public class UntypedObjectDeserializer extends StdDeserializer - implements ResolvableDeserializer, ContextualDeserializer { private static final long serialVersionUID = 1L; @@ -63,23 +54,10 @@ public class UntypedObjectDeserializer /** * If {@link java.util.Map} has been mapped to non-default implementation, * we'll store type here - * - * @since 2.6 */ protected JavaType _mapType; - /** - * @since 2.9 - */ protected final boolean _nonMerging; - - /** - * @deprecated Since 2.6 use variant takes type arguments - */ - @Deprecated - public UntypedObjectDeserializer() { - this(null, null); - } public UntypedObjectDeserializer(JavaType listType, JavaType mapType) { super(Object.class); @@ -103,9 +81,6 @@ public UntypedObjectDeserializer(UntypedObjectDeserializer base, _nonMerging = base._nonMerging; } - /** - * @since 2.9 - */ protected UntypedObjectDeserializer(UntypedObjectDeserializer base, boolean nonMerging) { @@ -236,7 +211,7 @@ public Boolean supportsUpdate(DeserializationConfig config) { @Override public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - switch (p.getCurrentTokenId()) { + switch (p.currentTokenId()) { case JsonTokenId.ID_START_OBJECT: case JsonTokenId.ID_FIELD_NAME: // 28-Oct-2015, tatu: [databind#989] We may also be given END_OBJECT (similar to FIELD_NAME), @@ -303,7 +278,7 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOEx public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException { - switch (p.getCurrentTokenId()) { + switch (p.currentTokenId()) { // First: does it look like we had type id wrapping of some kind? case JsonTokenId.ID_START_ARRAY: case JsonTokenId.ID_START_OBJECT: @@ -363,7 +338,7 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt, Object into return deserialize(p, ctxt); } - switch (p.getCurrentTokenId()) { + switch (p.currentTokenId()) { case JsonTokenId.ID_START_OBJECT: case JsonTokenId.ID_FIELD_NAME: // 28-Oct-2015, tatu: [databind#989] We may also be given END_OBJECT (similar to FIELD_NAME), @@ -493,12 +468,12 @@ protected Object mapObject(JsonParser p, DeserializationContext ctxt) throws IOE { String key1; - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == JsonToken.START_OBJECT) { key1 = p.nextFieldName(); } else if (t == JsonToken.FIELD_NAME) { - key1 = p.getCurrentName(); + key1 = p.currentName(); } else { if (t != JsonToken.END_OBJECT) { return ctxt.handleUnexpectedToken(handledType(), p); @@ -571,7 +546,7 @@ protected Object[] mapArrayToArray(JsonParser p, DeserializationContext ctxt) th protected Object mapObject(JsonParser p, DeserializationContext ctxt, Map m) throws IOException { - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == JsonToken.START_OBJECT) { t = p.nextToken(); } @@ -579,7 +554,7 @@ protected Object mapObject(JsonParser p, DeserializationContext ctxt, return m; } // NOTE: we are guaranteed to point to FIELD_NAME - String key = p.getCurrentName(); + String key = p.currentName(); do { p.nextToken(); // and possibly recursive merge here @@ -642,7 +617,7 @@ public Boolean supportsUpdate(DeserializationConfig config) { @Override public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - switch (p.getCurrentTokenId()) { + switch (p.currentTokenId()) { case JsonTokenId.ID_START_OBJECT: { JsonToken t = p.nextToken(); @@ -705,7 +680,7 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOEx @Override public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException { - switch (p.getCurrentTokenId()) { + switch (p.currentTokenId()) { case JsonTokenId.ID_START_ARRAY: case JsonTokenId.ID_START_OBJECT: case JsonTokenId.ID_FIELD_NAME: @@ -749,7 +724,7 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt, Object into return deserialize(p, ctxt); } - switch (p.getCurrentTokenId()) { + switch (p.currentTokenId()) { case JsonTokenId.ID_END_OBJECT: case JsonTokenId.ID_END_ARRAY: return intoValue; @@ -764,7 +739,7 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt, Object into if (intoValue instanceof Map) { Map m = (Map) intoValue; // NOTE: we are guaranteed to point to FIELD_NAME - String key = p.getCurrentName(); + String key = p.currentName(); do { p.nextToken(); // and possibly recursive merge here diff --git a/src/main/java/com/fasterxml/jackson/databind/exc/IgnoredPropertyException.java b/src/main/java/com/fasterxml/jackson/databind/exc/IgnoredPropertyException.java index e78ad5216c..47396ede2c 100644 --- a/src/main/java/com/fasterxml/jackson/databind/exc/IgnoredPropertyException.java +++ b/src/main/java/com/fasterxml/jackson/databind/exc/IgnoredPropertyException.java @@ -10,17 +10,12 @@ * Specialized {@link JsonMappingException} sub-class used to indicate * case where an explicitly ignored property is encountered, and mapper * is configured to consider this an error. - * - * @since 2.3 */ public class IgnoredPropertyException extends PropertyBindingException { private static final long serialVersionUID = 1L; - /** - * @since 2.7 - */ public IgnoredPropertyException(JsonParser p, String msg, JsonLocation loc, Class referringClass, String propName, Collection propertyIds) @@ -28,17 +23,6 @@ public IgnoredPropertyException(JsonParser p, String msg, JsonLocation loc, super(p, msg, loc, referringClass, propName, propertyIds); } - /** - * @deprecated Since 2.7 - */ - @Deprecated - public IgnoredPropertyException(String msg, JsonLocation loc, - Class referringClass, String propName, - Collection propertyIds) - { - super(msg, loc, referringClass, propName, propertyIds); - } - /** * Factory method used for constructing instances of this exception type. * diff --git a/src/main/java/com/fasterxml/jackson/databind/exc/InvalidFormatException.java b/src/main/java/com/fasterxml/jackson/databind/exc/InvalidFormatException.java index b1b8b105c2..3d0921adff 100644 --- a/src/main/java/com/fasterxml/jackson/databind/exc/InvalidFormatException.java +++ b/src/main/java/com/fasterxml/jackson/databind/exc/InvalidFormatException.java @@ -1,19 +1,16 @@ package com.fasterxml.jackson.databind.exc; -import com.fasterxml.jackson.core.JsonLocation; import com.fasterxml.jackson.core.JsonParser; /** * Specialized sub-class of {@link MismatchedInputException} * that is used when the underlying problem appears to be that * of bad formatting of a value to deserialize. - * - * @since 2.1 */ public class InvalidFormatException - extends MismatchedInputException // since 2.9 + extends MismatchedInputException { - private static final long serialVersionUID = 1L; // silly Eclipse, warnings + private static final long serialVersionUID = 1L; /** * Underlying value that could not be deserialized into @@ -27,33 +24,6 @@ public class InvalidFormatException /********************************************************** */ - /** - * @deprecated Since 2.7 Use variant that takes {@link JsonParser} - */ - @Deprecated // since 2.7 - public InvalidFormatException(String msg, - Object value, Class targetType) - { - super(null, msg); - _value = value; - _targetType = targetType; - } - - /** - * @deprecated Since 2.7 Use variant that takes {@link JsonParser} - */ - @Deprecated // since 2.7 - public InvalidFormatException(String msg, JsonLocation loc, - Object value, Class targetType) - { - super(null, msg, loc); - _value = value; - _targetType = targetType; - } - - /** - * @since 2.7 - */ public InvalidFormatException(JsonParser p, String msg, Object value, Class targetType) { diff --git a/src/main/java/com/fasterxml/jackson/databind/exc/MismatchedInputException.java b/src/main/java/com/fasterxml/jackson/databind/exc/MismatchedInputException.java index 183831a90c..90dfb33077 100644 --- a/src/main/java/com/fasterxml/jackson/databind/exc/MismatchedInputException.java +++ b/src/main/java/com/fasterxml/jackson/databind/exc/MismatchedInputException.java @@ -19,8 +19,6 @@ * NOTE: name chosen to differ from `java.util.InputMismatchException` since while that * would have been better name, use of same overlapping name causes nasty issues * with IDE auto-completion, so slightly less optimal chosen. - * - * @since 2.9 */ @SuppressWarnings("serial") public class MismatchedInputException @@ -49,12 +47,6 @@ protected MismatchedInputException(JsonParser p, String msg, JavaType targetType _targetType = ClassUtil.rawClass(targetType); } - // Only to prevent super-class static method from getting called - @Deprecated // as of 2.9 - public static MismatchedInputException from(JsonParser p, String msg) { - return from(p, (Class) null, msg); - } - public static MismatchedInputException from(JsonParser p, JavaType targetType, String msg) { return new MismatchedInputException(p, msg, targetType); } diff --git a/src/main/java/com/fasterxml/jackson/databind/exc/PropertyBindingException.java b/src/main/java/com/fasterxml/jackson/databind/exc/PropertyBindingException.java index 7e5c63077e..fc8638fbea 100644 --- a/src/main/java/com/fasterxml/jackson/databind/exc/PropertyBindingException.java +++ b/src/main/java/com/fasterxml/jackson/databind/exc/PropertyBindingException.java @@ -11,8 +11,6 @@ /** * Base class for {@link JsonMappingException}s that are specifically related * to problems related to binding an individual property. - * - * @since 2.3 */ @SuppressWarnings("serial") public abstract class PropertyBindingException @@ -42,9 +40,6 @@ public abstract class PropertyBindingException */ protected transient String _propertiesAsString; - /** - * @since 2.7 - */ protected PropertyBindingException(JsonParser p, String msg, JsonLocation loc, Class referringClass, String propName, Collection propertyIds) @@ -55,17 +50,6 @@ protected PropertyBindingException(JsonParser p, String msg, JsonLocation loc, _propertyIds = propertyIds; } - /** - * @deprecated Since 2.7 - */ - @Deprecated // since 2.7 - protected PropertyBindingException(String msg, JsonLocation loc, - Class referringClass, String propName, - Collection propertyIds) - { - this(null, msg, loc, referringClass, propName, propertyIds); - } - /* /********************************************************** /* Overrides diff --git a/src/main/java/com/fasterxml/jackson/databind/RuntimeJsonMappingException.java b/src/main/java/com/fasterxml/jackson/databind/exc/RuntimeJsonMappingException.java similarity index 82% rename from src/main/java/com/fasterxml/jackson/databind/RuntimeJsonMappingException.java rename to src/main/java/com/fasterxml/jackson/databind/exc/RuntimeJsonMappingException.java index 22508ac9d1..69505d889c 100644 --- a/src/main/java/com/fasterxml/jackson/databind/RuntimeJsonMappingException.java +++ b/src/main/java/com/fasterxml/jackson/databind/exc/RuntimeJsonMappingException.java @@ -1,4 +1,6 @@ -package com.fasterxml.jackson.databind; +package com.fasterxml.jackson.databind.exc; + +import com.fasterxml.jackson.databind.JsonMappingException; /** * Wrapper used when interface does not allow throwing a checked diff --git a/src/main/java/com/fasterxml/jackson/databind/exc/UnrecognizedPropertyException.java b/src/main/java/com/fasterxml/jackson/databind/exc/UnrecognizedPropertyException.java index fda5674b5d..21671dafc1 100644 --- a/src/main/java/com/fasterxml/jackson/databind/exc/UnrecognizedPropertyException.java +++ b/src/main/java/com/fasterxml/jackson/databind/exc/UnrecognizedPropertyException.java @@ -24,17 +24,6 @@ public UnrecognizedPropertyException(JsonParser p, String msg, JsonLocation loc, super(p, msg, loc, referringClass, propName, propertyIds); } - /** - * @deprecated Since 2.7 - */ - @Deprecated // since 2.7 - public UnrecognizedPropertyException(String msg, JsonLocation loc, - Class referringClass, String propName, - Collection propertyIds) - { - super(msg, loc, referringClass, propName, propertyIds); - } - /** * Factory method used for constructing instances of this exception type. * diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/CoreXMLDeserializers.java b/src/main/java/com/fasterxml/jackson/databind/ext/CoreXMLDeserializers.java index 64a06d8113..3690634b9b 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ext/CoreXMLDeserializers.java +++ b/src/main/java/com/fasterxml/jackson/databind/ext/CoreXMLDeserializers.java @@ -9,7 +9,6 @@ import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.deser.Deserializers; import com.fasterxml.jackson.databind.deser.std.FromStringDeserializer; /** @@ -17,7 +16,7 @@ * JDK 1.5. Types are directly needed by JAXB, but may be unavailable on some * limited platforms; hence separate out from basic deserializer factory. */ -public class CoreXMLDeserializers extends Deserializers.Base +public class CoreXMLDeserializers { /** * Data type factories are thread-safe after instantiation (and @@ -33,8 +32,7 @@ public class CoreXMLDeserializers extends Deserializers.Base } } - @Override - public JsonDeserializer findBeanDeserializer(JavaType type, + public static JsonDeserializer findBeanDeserializer(JavaType type, DeserializationConfig config, BeanDescription beanDesc) { Class raw = type.getRawClass(); diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/CoreXMLSerializers.java b/src/main/java/com/fasterxml/jackson/databind/ext/CoreXMLSerializers.java deleted file mode 100644 index 56dd43fda2..0000000000 --- a/src/main/java/com/fasterxml/jackson/databind/ext/CoreXMLSerializers.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.fasterxml.jackson.databind.ext; - -import java.io.IOException; -import java.util.Calendar; - -import javax.xml.datatype.Duration; -import javax.xml.datatype.XMLGregorianCalendar; -import javax.xml.namespace.QName; - -import com.fasterxml.jackson.core.*; -import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; -import com.fasterxml.jackson.databind.jsontype.TypeSerializer; -import com.fasterxml.jackson.databind.ser.ContextualSerializer; -import com.fasterxml.jackson.databind.ser.Serializers; -import com.fasterxml.jackson.databind.ser.std.CalendarSerializer; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; -import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; - -/** - * Provider for serializers of XML types that are part of full JDK 1.5, but - * that some alleged 1.5 platforms are missing (Android, GAE). - * And for this reason these are added using more dynamic mechanism. - *

    - * Note: since many of classes defined are abstract, caller must take - * care not to just use straight equivalency check but rather consider - * subclassing as well. - */ -public class CoreXMLSerializers extends Serializers.Base -{ - @Override - public JsonSerializer findSerializer(SerializationConfig config, - JavaType type, BeanDescription beanDesc) - { - Class raw = type.getRawClass(); - if (Duration.class.isAssignableFrom(raw) || QName.class.isAssignableFrom(raw)) { - return ToStringSerializer.instance; - } - if (XMLGregorianCalendar.class.isAssignableFrom(raw)) { - return XMLGregorianCalendarSerializer.instance; - } - return null; - } - - @SuppressWarnings("serial") - public static class XMLGregorianCalendarSerializer - extends StdSerializer - implements ContextualSerializer - { - final static XMLGregorianCalendarSerializer instance = new XMLGregorianCalendarSerializer(); - - final JsonSerializer _delegate; - - public XMLGregorianCalendarSerializer() { - this(CalendarSerializer.instance); - } - - @SuppressWarnings("unchecked") - protected XMLGregorianCalendarSerializer(JsonSerializer del) { - super(XMLGregorianCalendar.class); - _delegate = (JsonSerializer) del; - } - - @Override - public JsonSerializer getDelegatee() { - return _delegate; - } - - @Override - public boolean isEmpty(SerializerProvider provider, XMLGregorianCalendar value) { - return _delegate.isEmpty(provider, _convert(value)); - } - - @Override - public void serialize(XMLGregorianCalendar value, JsonGenerator gen, SerializerProvider provider) - throws IOException { - _delegate.serialize(_convert(value), gen, provider); - } - - @Override - public void serializeWithType(XMLGregorianCalendar value, JsonGenerator gen, SerializerProvider provider, - TypeSerializer typeSer) throws IOException - { - _delegate.serializeWithType(_convert(value), gen, provider, typeSer); - } - - @Override - public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { - _delegate.acceptJsonFormatVisitor(visitor, null); - } - - @Override - public JsonSerializer createContextual(SerializerProvider prov, BeanProperty property) - throws JsonMappingException { - JsonSerializer ser = prov.handlePrimaryContextualization(_delegate, property); - if (ser != _delegate) { - return new XMLGregorianCalendarSerializer(ser); - } - return this; - } - - protected Calendar _convert(XMLGregorianCalendar input) { - return (input == null) ? null : input.toGregorianCalendar(); - } - } -} diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/DOMSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ext/DOMSerializer.java index 9095b32c17..ce931e7b0c 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ext/DOMSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ext/DOMSerializer.java @@ -10,7 +10,6 @@ import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import com.fasterxml.jackson.databind.ser.std.StdSerializer; @@ -40,12 +39,6 @@ public void serialize(Node value, JsonGenerator jgen, SerializerProvider provide jgen.writeString(writer.writeToString(value)); } - @Override - public JsonNode getSchema(SerializerProvider provider, java.lang.reflect.Type typeHint) { - // Well... it is serialized as String - return createSchemaNode("string", true); - } - @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { if (visitor != null) visitor.expectAnyFormat(typeHint); diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/Java7Support.java b/src/main/java/com/fasterxml/jackson/databind/ext/Java7Support.java deleted file mode 100644 index 051f5709e3..0000000000 --- a/src/main/java/com/fasterxml/jackson/databind/ext/Java7Support.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.fasterxml.jackson.databind.ext; - -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.PropertyName; -import com.fasterxml.jackson.databind.introspect.Annotated; -import com.fasterxml.jackson.databind.introspect.AnnotatedParameter; -import com.fasterxml.jackson.databind.util.ClassUtil; - -/** - * To support Java7-incomplete platforms, we will offer support for JDK 7 - * annotations through this class, loaded dynamically; if loading fails, - * support will be missing. This class is the non-JDK-7-dependent API, - * and {@link Java7SupportImpl} is JDK7-dependent implementation of - * functionality. - */ -public abstract class Java7Support -{ - private final static Java7Support IMPL; - - static { - Java7Support impl = null; - try { - Class cls = Class.forName("com.fasterxml.jackson.databind.ext.Java7SupportImpl"); - impl = (Java7Support) ClassUtil.createInstance(cls, false); - } catch (Throwable t) { - // 24-Nov-2015, tatu: Should we log or not? - java.util.logging.Logger.getLogger(Java7Support.class.getName()) - .warning("Unable to load JDK7 types (annotations, java.nio.file.Path): no Java7 support added"); - } - IMPL = impl; - } - - public static Java7Support instance() { - return IMPL; - } - - public abstract Boolean findTransient(Annotated a); - - public abstract Boolean hasCreatorAnnotation(Annotated a); - - public abstract PropertyName findConstructorName(AnnotatedParameter p); - - public abstract Class getClassJavaNioFilePath(); - - public abstract JsonDeserializer getDeserializerForJavaNioFilePath(Class rawType); - - public abstract JsonSerializer getSerializerForJavaNioFilePath(Class rawType); -} diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/Java7SupportImpl.java b/src/main/java/com/fasterxml/jackson/databind/ext/Java7SupportImpl.java deleted file mode 100644 index 4546d032a4..0000000000 --- a/src/main/java/com/fasterxml/jackson/databind/ext/Java7SupportImpl.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.fasterxml.jackson.databind.ext; - -import java.beans.ConstructorProperties; -import java.beans.Transient; -import java.nio.file.Path; - -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.PropertyName; -import com.fasterxml.jackson.databind.introspect.Annotated; -import com.fasterxml.jackson.databind.introspect.AnnotatedParameter; -import com.fasterxml.jackson.databind.introspect.AnnotatedWithParams; - -/** - * @since 2.8 - */ -public class Java7SupportImpl extends Java7Support -{ - @SuppressWarnings("unused") // compiler warns, just needed side-effects - private final Class _bogus; - - public Java7SupportImpl() { - // Trigger loading of annotations that only JDK 7 has... - Class cls = Transient.class; - cls = ConstructorProperties.class; - _bogus = cls; - } - - @Override - public Class getClassJavaNioFilePath() { - return Path.class; - } - - @Override - public JsonDeserializer getDeserializerForJavaNioFilePath(Class rawType) { - if (rawType == Path.class) { - return new NioPathDeserializer(); - } - return null; - } - - @Override - public JsonSerializer getSerializerForJavaNioFilePath(Class rawType) { - if (Path.class.isAssignableFrom(rawType)) { - return new NioPathSerializer(); - } - return null; - } - - @Override - public Boolean findTransient(Annotated a) { - Transient t = a.getAnnotation(Transient.class); - if (t != null) { - return t.value(); - } - return null; - } - - @Override - public Boolean hasCreatorAnnotation(Annotated a) { - ConstructorProperties props = a.getAnnotation(ConstructorProperties.class); - // 08-Nov-2015, tatu: One possible check would be to ensure there is at least - // one name iff constructor has arguments. But seems unnecessary for now. - if (props != null) { - return Boolean.TRUE; - } - return null; - } - - @Override - public PropertyName findConstructorName(AnnotatedParameter p) - { - AnnotatedWithParams ctor = p.getOwner(); - if (ctor != null) { - ConstructorProperties props = ctor.getAnnotation(ConstructorProperties.class); - if (props != null) { - String[] names = props.value(); - int ix = p.getIndex(); - if (ix < names.length) { - return PropertyName.construct(names[ix]); - } - } - } - return null; - } -} diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/NioPathDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/ext/NioPathDeserializer.java deleted file mode 100644 index 20e9d5cf12..0000000000 --- a/src/main/java/com/fasterxml/jackson/databind/ext/NioPathDeserializer.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.fasterxml.jackson.databind.ext; - -import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.file.FileSystemNotFoundException; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.spi.FileSystemProvider; -import java.util.ServiceLoader; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer; - -import static java.lang.Character.isLetter; - -/** - * @since 2.8 - */ -public class NioPathDeserializer extends StdScalarDeserializer -{ - private static final long serialVersionUID = 1; - - private static final boolean areWindowsFilePathsSupported; - static { - boolean isWindowsRootFound = false; - for (File file : File.listRoots()) { - String path = file.getPath(); - if (path.length() >= 2 && isLetter(path.charAt(0)) && path.charAt(1) == ':') { - isWindowsRootFound = true; - break; - } - } - areWindowsFilePathsSupported = isWindowsRootFound; - } - - public NioPathDeserializer() { super(Path.class); } - - @Override - public Path deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - if (!p.hasToken(JsonToken.VALUE_STRING)) { - return (Path) ctxt.handleUnexpectedToken(Path.class, p); - } - - final String value = p.getText(); - - // If someone gives us an input with no : at all, treat as local path, instead of failing - // with invalid URI. - if (value.indexOf(':') < 0) { - return Paths.get(value); - } - - if (areWindowsFilePathsSupported) { - if (value.length() >= 2 && isLetter(value.charAt(0)) && value.charAt(1) == ':') { - return Paths.get(value); - } - } - - final URI uri; - try { - uri = new URI(value); - } catch (URISyntaxException e) { - return (Path) ctxt.handleInstantiationProblem(handledType(), value, e); - } - try { - return Paths.get(uri); - } catch (FileSystemNotFoundException cause) { - try { - final String scheme = uri.getScheme(); - // We want to use the current thread's context class loader, not system class loader that is used in Paths.get(): - for (FileSystemProvider provider : ServiceLoader.load(FileSystemProvider.class)) { - if (provider.getScheme().equalsIgnoreCase(scheme)) { - return provider.getPath(uri); - } - } - return (Path) ctxt.handleInstantiationProblem(handledType(), value, cause); - } catch (Throwable e) { - e.addSuppressed(cause); - return (Path) ctxt.handleInstantiationProblem(handledType(), value, e); - } - } catch (Throwable e) { - return (Path) ctxt.handleInstantiationProblem(handledType(), value, e); - } - } -} diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/NioPathSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ext/NioPathSerializer.java deleted file mode 100644 index b8c23a5911..0000000000 --- a/src/main/java/com/fasterxml/jackson/databind/ext/NioPathSerializer.java +++ /dev/null @@ -1,42 +0,0 @@ - -package com.fasterxml.jackson.databind.ext; - -import java.io.IOException; -import java.nio.file.Path; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.core.type.WritableTypeId; - -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.jsontype.TypeSerializer; -import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer; - -/** - * @since 2.8 - */ -public class NioPathSerializer extends StdScalarSerializer -{ - private static final long serialVersionUID = 1; - - public NioPathSerializer() { super(Path.class); } - - @Override - public void serialize(Path value, JsonGenerator gen, SerializerProvider serializers) throws IOException { - // write the Path as a URI, always. - gen.writeString(value.toUri().toString()); - } - - // [databind#1688]: Not sure this is 100% ok, considering there are legitimately different - // impls... but has to do - @Override - public void serializeWithType(Path value, JsonGenerator g, - SerializerProvider provider, TypeSerializer typeSer) throws IOException - { - // Better ensure we don't use specific sub-classes: - WritableTypeId typeIdDef = typeSer.writeTypePrefix(g, - typeSer.typeId(value, Path.class, JsonToken.VALUE_STRING)); - serialize(value, g, provider); - typeSer.writeTypeSuffix(g, typeIdDef); - } -} diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/OptionalHandlerFactory.java b/src/main/java/com/fasterxml/jackson/databind/ext/OptionalHandlerFactory.java index 011c31f830..8297e37193 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ext/OptionalHandlerFactory.java +++ b/src/main/java/com/fasterxml/jackson/databind/ext/OptionalHandlerFactory.java @@ -1,24 +1,18 @@ package com.fasterxml.jackson.databind.ext; -import java.util.logging.Logger; -import java.util.logging.Level; +import javax.xml.datatype.Duration; +import javax.xml.datatype.XMLGregorianCalendar; +import javax.xml.namespace.QName; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.deser.Deserializers; -import com.fasterxml.jackson.databind.ser.Serializers; -import com.fasterxml.jackson.databind.util.ClassUtil; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; /** * Helper class used for isolating details of handling optional+external types * (javax.xml classes) from standard factories that offer them. *

    - * Note that 2.7 changed handling to slightly less dynamic, to avoid having to - * traverse class hierarchy, which turned to be a performance issue in - * certain cases. Since DOM classes are assumed to exist on all Java 1.6 - * environments (yes, even on Android/GAE), this part could be simplified by - * slightly less dynamic lookups. - *

    - * Also with 2.7 we are supporting JDK 1.7/Java 7 type(s). + * Note that with 3.0 need for separate class has been reduced somewhat + * and this class may be eliminated. */ public class OptionalHandlerFactory implements java.io.Serializable { @@ -30,49 +24,12 @@ public class OptionalHandlerFactory implements java.io.Serializable */ private final static String PACKAGE_PREFIX_JAVAX_XML = "javax.xml."; - private final static String SERIALIZERS_FOR_JAVAX_XML = "com.fasterxml.jackson.databind.ext.CoreXMLSerializers"; - private final static String DESERIALIZERS_FOR_JAVAX_XML = "com.fasterxml.jackson.databind.ext.CoreXMLDeserializers"; - - // Plus we also have a single serializer for DOM Node: -// private final static String CLASS_NAME_DOM_NODE = "org.w3c.dom.Node"; -// private final static String CLASS_NAME_DOM_DOCUMENT = "org.w3c.dom.Document"; - private final static String SERIALIZER_FOR_DOM_NODE = "com.fasterxml.jackson.databind.ext.DOMSerializer"; - private final static String DESERIALIZER_FOR_DOM_DOCUMENT = "com.fasterxml.jackson.databind.ext.DOMDeserializer$DocumentDeserializer"; - private final static String DESERIALIZER_FOR_DOM_NODE = "com.fasterxml.jackson.databind.ext.DOMDeserializer$NodeDeserializer"; - // // Since 2.7, we will assume DOM classes are always found, both due to JDK 1.6 minimum // // and because Android (and presumably GAE) have these classes - private final static Class CLASS_DOM_NODE; - private final static Class CLASS_DOM_DOCUMENT; - - static { - Class doc = null, node = null; - try { - node = org.w3c.dom.Node.class; - doc = org.w3c.dom.Document.class; - } catch (Exception e) { - // not optimal but will do - Logger.getLogger(OptionalHandlerFactory.class.getName()) - .log(Level.INFO, "Could not load DOM `Node` and/or `Document` classes: no DOM support"); - } - CLASS_DOM_NODE = node; - CLASS_DOM_DOCUMENT = doc; - } - - // // But Java7 type(s) may or may not be; dynamic lookup should be fine, still - // // (note: also assume it comes from JDK so that ClassLoader issues with OSGi - // // can, I hope, be avoided?) + private final static Class CLASS_DOM_NODE = org.w3c.dom.Node.class; + private final static Class CLASS_DOM_DOCUMENT = org.w3c.dom.Document.class; - private static final Java7Support _jdk7Helper; - static { - Java7Support x = null; - try { - x = Java7Support.instance(); - } catch (Throwable t) { } - _jdk7Helper = x; - } - public final static OptionalHandlerFactory instance = new OptionalHandlerFactory(); protected OptionalHandlerFactory() { } @@ -87,29 +44,20 @@ public JsonSerializer findSerializer(SerializationConfig config, JavaType typ BeanDescription beanDesc) { final Class rawType = type.getRawClass(); - - if (_jdk7Helper != null) { - JsonSerializer ser = _jdk7Helper.getSerializerForJavaNioFilePath(rawType); - if (ser != null) { - return ser; - } - } if ((CLASS_DOM_NODE != null) && CLASS_DOM_NODE.isAssignableFrom(rawType)) { - return (JsonSerializer) instantiate(SERIALIZER_FOR_DOM_NODE); + return new DOMSerializer(); } + String className = rawType.getName(); - String factoryName; if (className.startsWith(PACKAGE_PREFIX_JAVAX_XML) || hasSuperClassStartingWith(rawType, PACKAGE_PREFIX_JAVAX_XML)) { - factoryName = SERIALIZERS_FOR_JAVAX_XML; - } else { - return null; - } - - Object ob = instantiate(factoryName); - if (ob == null) { // could warn, if we had logging system (j.u.l?) - return null; + if (Duration.class.isAssignableFrom(rawType) || QName.class.isAssignableFrom(rawType)) { + return ToStringSerializer.instance; + } + if (XMLGregorianCalendar.class.isAssignableFrom(rawType)) { + return XMLGregorianCalendarSerializer.instance; + } } - return ((Serializers) ob).findSerializer(config, type, beanDesc); + return null; } public JsonDeserializer findDeserializer(JavaType type, DeserializationConfig config, @@ -117,32 +65,19 @@ public JsonDeserializer findDeserializer(JavaType type, DeserializationConfig throws JsonMappingException { final Class rawType = type.getRawClass(); - - if (_jdk7Helper != null) { - JsonDeserializer deser = _jdk7Helper.getDeserializerForJavaNioFilePath(rawType); - if (deser != null) { - return deser; - } - } if ((CLASS_DOM_NODE != null) && CLASS_DOM_NODE.isAssignableFrom(rawType)) { - return (JsonDeserializer) instantiate(DESERIALIZER_FOR_DOM_NODE); + return new DOMDeserializer.NodeDeserializer(); } if ((CLASS_DOM_DOCUMENT != null) && CLASS_DOM_DOCUMENT.isAssignableFrom(rawType)) { - return (JsonDeserializer) instantiate(DESERIALIZER_FOR_DOM_DOCUMENT); + return new DOMDeserializer.DocumentDeserializer(); } String className = rawType.getName(); - String factoryName; + if (className.startsWith(PACKAGE_PREFIX_JAVAX_XML) || hasSuperClassStartingWith(rawType, PACKAGE_PREFIX_JAVAX_XML)) { - factoryName = DESERIALIZERS_FOR_JAVAX_XML; - } else { - return null; - } - Object ob = instantiate(factoryName); - if (ob == null) { // could warn, if we had logging system (j.u.l?) - return null; + return CoreXMLDeserializers.findBeanDeserializer(type, config, beanDesc); } - return ((Deserializers) ob).findBeanDeserializer(type, config, beanDesc); + return null; } /* @@ -151,23 +86,11 @@ public JsonDeserializer findDeserializer(JavaType type, DeserializationConfig /********************************************************** */ - private Object instantiate(String className) - { - try { - return ClassUtil.createInstance(Class.forName(className), false); - } catch (LinkageError e) { } - // too many different kinds to enumerate here: - catch (Exception e) { } - return null; - } - /** * Since 2.7 we only need to check for class extension, as all implemented * types are classes, not interfaces. This has performance implications for * some cases, as we do not need to go over interfaces implemented, just - * superclasses - * - * @since 2.7 + * super classes */ private boolean hasSuperClassStartingWith(Class rawType, String prefix) { diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/XMLGregorianCalendarSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ext/XMLGregorianCalendarSerializer.java new file mode 100644 index 0000000000..8b814fe983 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/ext/XMLGregorianCalendarSerializer.java @@ -0,0 +1,78 @@ +package com.fasterxml.jackson.databind.ext; + +import java.io.IOException; +import java.util.Calendar; + +import javax.xml.datatype.XMLGregorianCalendar; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.std.CalendarSerializer; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +@SuppressWarnings("serial") +public class XMLGregorianCalendarSerializer + extends StdSerializer +{ + final static XMLGregorianCalendarSerializer instance = new XMLGregorianCalendarSerializer(); + + final JsonSerializer _delegate; + + public XMLGregorianCalendarSerializer() { + this(CalendarSerializer.instance); + } + + @SuppressWarnings("unchecked") + protected XMLGregorianCalendarSerializer(JsonSerializer del) { + super(XMLGregorianCalendar.class); + _delegate = (JsonSerializer) del; + } + + @Override + public JsonSerializer getDelegatee() { + return _delegate; + } + + @Override + public boolean isEmpty(SerializerProvider provider, XMLGregorianCalendar value) throws IOException { + return _delegate.isEmpty(provider, _convert(value)); + } + + @Override + public void serialize(XMLGregorianCalendar value, JsonGenerator gen, SerializerProvider provider) + throws IOException { + _delegate.serialize(_convert(value), gen, provider); + } + + @Override + public void serializeWithType(XMLGregorianCalendar value, JsonGenerator gen, SerializerProvider provider, + TypeSerializer typeSer) throws IOException + { + _delegate.serializeWithType(_convert(value), gen, provider, typeSer); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { + _delegate.acceptJsonFormatVisitor(visitor, null); + } + + @Override + public JsonSerializer createContextual(SerializerProvider prov, BeanProperty property) + throws JsonMappingException { + JsonSerializer ser = prov.handlePrimaryContextualization(_delegate, property); + if (ser != _delegate) { + return new XMLGregorianCalendarSerializer(ser); + } + return this; + } + + protected Calendar _convert(XMLGregorianCalendar input) { + return (input == null) ? null : input.toGregorianCalendar(); + } +} \ No newline at end of file diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/BaseScalarOptionalDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/BaseScalarOptionalDeserializer.java new file mode 100644 index 0000000000..4c276a0150 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/BaseScalarOptionalDeserializer.java @@ -0,0 +1,21 @@ +package com.fasterxml.jackson.databind.ext.jdk8; + +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer; + +@SuppressWarnings("serial") +public abstract class BaseScalarOptionalDeserializer + extends StdScalarDeserializer +{ + protected final T _empty; + + protected BaseScalarOptionalDeserializer(Class cls, T empty) { + super(cls); + _empty = empty; + } + + @Override + public T getNullValue(DeserializationContext ctxt) { + return _empty; + } +} diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/DoubleStreamSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/DoubleStreamSerializer.java new file mode 100644 index 0000000000..e1cc938d39 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/DoubleStreamSerializer.java @@ -0,0 +1,51 @@ +package com.fasterxml.jackson.databind.ext.jdk8; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.stream.DoubleStream; + +/** + * {@link DoubleStream} serializer + *

    + * Unfortunately there to common ancestor between number base stream, + * so we need to define each in a specific class + *

    + */ +public class DoubleStreamSerializer extends StdSerializer +{ + private static final long serialVersionUID = 1L; + + /** + * Singleton instance + */ + public static final DoubleStreamSerializer INSTANCE = new DoubleStreamSerializer(); + + /** + * Constructor + */ + private DoubleStreamSerializer() { + super(DoubleStream.class); + } + + @Override + public void serialize(DoubleStream stream, JsonGenerator g, SerializerProvider provider) throws IOException { + + try(DoubleStream ds = stream) { + g.writeStartArray(); + ds.forEach(value -> { + try { + g.writeNumber(value); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + g.writeEndArray(); + } catch (UncheckedIOException e) { + throw e.getCause(); + } + } +} diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/IntStreamSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/IntStreamSerializer.java new file mode 100644 index 0000000000..f05b921607 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/IntStreamSerializer.java @@ -0,0 +1,52 @@ +package com.fasterxml.jackson.databind.ext.jdk8; + + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.stream.IntStream; + +/** + * {@link IntStream} serializer + *

    + * Unfortunately there to common ancestor between number base stream, so we need to define each in a specific class + *

    + */ +public class IntStreamSerializer extends StdSerializer +{ + private static final long serialVersionUID = 1L; + + /** + * Singleton instance + */ + public static final IntStreamSerializer INSTANCE = new IntStreamSerializer(); + + /** + * Constructor + */ + private IntStreamSerializer() { + super(IntStream.class); + } + + @Override + public void serialize(IntStream stream, JsonGenerator g, SerializerProvider provider) throws IOException { + + try(IntStream is = stream) { + g.writeStartArray(); + is.forEach(value -> { + try { + g.writeNumber(value); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + + g.writeEndArray(); + } catch (UncheckedIOException e) { + throw e.getCause(); + } + } +} diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/Jdk8OptionalDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/Jdk8OptionalDeserializer.java new file mode 100644 index 0000000000..dfcb4876f2 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/Jdk8OptionalDeserializer.java @@ -0,0 +1,62 @@ +package com.fasterxml.jackson.databind.ext.jdk8; + +import java.util.Optional; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.ValueInstantiator; +import com.fasterxml.jackson.databind.deser.std.ReferenceTypeDeserializer; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; + +public class Jdk8OptionalDeserializer + extends ReferenceTypeDeserializer> +{ + private static final long serialVersionUID = 1L; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + public Jdk8OptionalDeserializer(JavaType fullType, ValueInstantiator inst, + TypeDeserializer typeDeser, JsonDeserializer deser) + { + super(fullType, inst, typeDeser, deser); + } + + /* + /********************************************************** + /* Abstract method implementations + /********************************************************** + */ + + @Override + public Jdk8OptionalDeserializer withResolved(TypeDeserializer typeDeser, JsonDeserializer valueDeser) { + return new Jdk8OptionalDeserializer(_fullType, _valueInstantiator, + typeDeser, valueDeser); + } + + @Override + public Optional getNullValue(DeserializationContext ctxt) { + return Optional.empty(); + } + + @Override + public Optional referenceValue(Object contents) { + return Optional.ofNullable(contents); + } + + @Override + public Object getReferenced(Optional reference) { + return reference.get(); + } + + @Override // since 2.9 + public Optional updateReference(Optional reference, Object contents) { + return Optional.ofNullable(contents); + } + + // Default ought to be fine: +// public Boolean supportsUpdate(DeserializationConfig config) { } + +} diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/Jdk8OptionalSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/Jdk8OptionalSerializer.java new file mode 100644 index 0000000000..d09babcce0 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/Jdk8OptionalSerializer.java @@ -0,0 +1,74 @@ +package com.fasterxml.jackson.databind.ext.jdk8; + +import java.util.Optional; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.std.ReferenceTypeSerializer; +import com.fasterxml.jackson.databind.type.ReferenceType; +import com.fasterxml.jackson.databind.util.NameTransformer; + +public class Jdk8OptionalSerializer + extends ReferenceTypeSerializer> +{ + private static final long serialVersionUID = 1L; + + /* + /********************************************************** + /* Constructors, factory methods + /********************************************************** + */ + + public Jdk8OptionalSerializer(ReferenceType fullType, boolean staticTyping, + TypeSerializer vts, JsonSerializer ser) + { + super(fullType, staticTyping, vts, ser); + } + + protected Jdk8OptionalSerializer(Jdk8OptionalSerializer base, BeanProperty property, + TypeSerializer vts, JsonSerializer valueSer, NameTransformer unwrapper, + Object suppressableValue, boolean suppressNulls) + { + super(base, property, vts, valueSer, unwrapper, + suppressableValue, suppressNulls); + } + + @Override + protected ReferenceTypeSerializer> withResolved(BeanProperty prop, + TypeSerializer vts, JsonSerializer valueSer, + NameTransformer unwrapper) + { + return new Jdk8OptionalSerializer(this, prop, vts, valueSer, unwrapper, + _suppressableValue, _suppressNulls); + } + + @Override + public ReferenceTypeSerializer> withContentInclusion(Object suppressableValue, + boolean suppressNulls) + { + return new Jdk8OptionalSerializer(this, _property, _valueTypeSerializer, + _valueSerializer, _unwrapper, + suppressableValue, suppressNulls); + } + + /* + /********************************************************** + /* Abstract method impls + /********************************************************** + */ + + @Override + protected boolean _isValuePresent(Optional value) { + return value.isPresent(); + } + + @Override + protected Object _getReferenced(Optional value) { + return value.get(); + } + + @Override + protected Object _getReferencedIfPresent(Optional value) { + return value.orElse(null); + } +} diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/Jdk8StreamSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/Jdk8StreamSerializer.java new file mode 100644 index 0000000000..c8e6904058 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/Jdk8StreamSerializer.java @@ -0,0 +1,89 @@ +package com.fasterxml.jackson.databind.ext.jdk8; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.stream.Stream; + +/** + * Common typed stream serializer + * + */ +public class Jdk8StreamSerializer extends StdSerializer> +{ + private static final long serialVersionUID = 1L; + + /** + * Stream elements type (matching T) + */ + private final JavaType elemType; + + /** + * element specific serializer, if any + */ + private transient final JsonSerializer elemSerializer; + + /** + * Constructor + * + * @param streamType Stream type + * @param elemType Stream elements type (matching T) + */ + public Jdk8StreamSerializer(JavaType streamType, JavaType elemType) { + this(streamType, elemType, null); + } + + /** + * Constructor with custom serializer + * + * @param streamType Stream type + * @param elemType Stream elements type (matching T) + * @param elemSerializer Custom serializer to use for element type + */ + public Jdk8StreamSerializer(JavaType streamType, JavaType elemType, JsonSerializer elemSerializer) { + super(streamType); + this.elemType = elemType; + this.elemSerializer = elemSerializer; + } + + @Override + public JsonSerializer createContextual(SerializerProvider provider, BeanProperty property) + throws JsonMappingException + { + if (!elemType.hasRawClass(Object.class) + && (provider.isEnabled(MapperFeature.USE_STATIC_TYPING) || elemType.isFinal())) { + return new Jdk8StreamSerializer( + provider.getTypeFactory().constructParametricType(Stream.class, elemType), + elemType, + provider.findSecondaryPropertySerializer(elemType, property)); + } + return this; + } + + @Override + public void serialize(Stream stream, JsonGenerator g, SerializerProvider provider) + throws IOException + { + try(Stream s = stream) { + g.writeStartArray(); + + s.forEach(elem -> { + try { + if (elemSerializer == null) { + provider.writeValue(g, elem); + } else { + elemSerializer.serialize(elem, g, provider); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + g.writeEndArray(); + } catch (UncheckedIOException e) { + throw e.getCause(); + } + } +} diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/LongStreamSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/LongStreamSerializer.java new file mode 100644 index 0000000000..3e210a968d --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/LongStreamSerializer.java @@ -0,0 +1,49 @@ +package com.fasterxml.jackson.databind.ext.jdk8; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.stream.LongStream; + +/** + * {@link LongStream} serializer + *

    + * Unfortunately there to common ancestor between number base stream, so we need to define each in a specific class + *

    + */ +public class LongStreamSerializer extends StdSerializer +{ + private static final long serialVersionUID = 1L; + + /** + * Singleton instance + */ + public static final LongStreamSerializer INSTANCE = new LongStreamSerializer(); + + /** + * Constructor + */ + private LongStreamSerializer() { + super(LongStream.class); + } + + @Override + public void serialize(LongStream stream, JsonGenerator g, SerializerProvider provider) throws IOException { + try (LongStream ls = stream) { + g.writeStartArray(); + ls.forEach(value -> { + try { + g.writeNumber(value); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + g.writeEndArray(); + } catch (UncheckedIOException e) { + throw e.getCause(); + } + } +} diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalDoubleDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalDoubleDeserializer.java new file mode 100644 index 0000000000..e6e901b337 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalDoubleDeserializer.java @@ -0,0 +1,55 @@ +package com.fasterxml.jackson.databind.ext.jdk8; + +import java.io.IOException; +import java.util.OptionalDouble; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.core.JsonTokenId; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.DeserializationFeature; + +public class OptionalDoubleDeserializer extends BaseScalarOptionalDeserializer +{ + private static final long serialVersionUID = 1L; + + static final OptionalDoubleDeserializer INSTANCE = new OptionalDoubleDeserializer(); + + public OptionalDoubleDeserializer() { + super(OptionalDouble.class, OptionalDouble.empty()); + } + + @Override + public OptionalDouble deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + // minor optimization, first, for common case + if (p.hasToken(JsonToken.VALUE_NUMBER_FLOAT)) { + return OptionalDouble.of(p.getDoubleValue()); + } + switch (p.currentTokenId()) { + case JsonTokenId.ID_STRING: + String text = p.getText().trim(); + if ((text.length() == 0)) { + _coerceEmptyString(ctxt, false); + return _empty; + } + if (_hasTextualNull(text)) { + _coerceTextualNull(ctxt, false); + return _empty; + } + return OptionalDouble.of(_parseDoublePrimitive(ctxt, text)); + case JsonTokenId.ID_NUMBER_INT: // coercion here should be fine + return OptionalDouble.of(p.getDoubleValue()); + case JsonTokenId.ID_NULL: + return _empty; + case JsonTokenId.ID_START_ARRAY: + if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { + p.nextToken(); + final OptionalDouble parsed = deserialize(p, ctxt); + _verifyEndArrayForSingle(p, ctxt); + return parsed; + } + break; + } + return (OptionalDouble) ctxt.handleUnexpectedToken(_valueClass, p); + } +} diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalDoubleSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalDoubleSerializer.java new file mode 100644 index 0000000000..db94a0683e --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalDoubleSerializer.java @@ -0,0 +1,49 @@ +package com.fasterxml.jackson.databind.ext.jdk8; + +import java.io.IOException; +import java.util.OptionalDouble; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonNumberFormatVisitor; +import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer; + +public class OptionalDoubleSerializer extends StdScalarSerializer +{ + private static final long serialVersionUID = 1L; + + static final OptionalDoubleSerializer INSTANCE = new OptionalDoubleSerializer(); + + public OptionalDoubleSerializer() { + super(OptionalDouble.class); + } + + @Override + public boolean isEmpty(SerializerProvider provider, OptionalDouble value) { + return (value == null) || !value.isPresent(); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, + JavaType typeHint) throws JsonMappingException { + JsonNumberFormatVisitor v2 = visitor.expectNumberFormat(typeHint); + if (v2 != null) { + v2.numberType(JsonParser.NumberType.DOUBLE); + } + } + + @Override + public void serialize(OptionalDouble value, JsonGenerator gen, SerializerProvider provider) + throws IOException + { + if (value.isPresent()) { + gen.writeNumber(value.getAsDouble()); + } else { + gen.writeNull(); + } + } +} diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalIntDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalIntDeserializer.java new file mode 100644 index 0000000000..bf75e14912 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalIntDeserializer.java @@ -0,0 +1,59 @@ +package com.fasterxml.jackson.databind.ext.jdk8; + +import java.io.IOException; +import java.util.OptionalInt; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.core.JsonTokenId; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.DeserializationFeature; + +public class OptionalIntDeserializer extends BaseScalarOptionalDeserializer +{ + private static final long serialVersionUID = 1L; + + static final OptionalIntDeserializer INSTANCE = new OptionalIntDeserializer(); + + public OptionalIntDeserializer() { + super(OptionalInt.class, OptionalInt.empty()); + } + + @Override + public OptionalInt deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + // minor optimization, first, for common case + if (p.hasToken(JsonToken.VALUE_NUMBER_INT)) { + return OptionalInt.of(p.getIntValue()); + } + switch (p.currentTokenId()) { + case JsonTokenId.ID_STRING: + String text = p.getText().trim(); + if ((text.length() == 0)) { + _coerceEmptyString(ctxt, false); + return _empty; + } + if (_hasTextualNull(text)) { + _coerceTextualNull(ctxt, false); + return _empty; + } + return OptionalInt.of(_parseIntPrimitive(ctxt, text)); + case JsonTokenId.ID_NUMBER_FLOAT: + if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_FLOAT_AS_INT)) { + _failDoubleToIntCoercion(p, ctxt, "int"); + } + return OptionalInt.of(p.getValueAsInt()); + case JsonTokenId.ID_NULL: + return _empty; + case JsonTokenId.ID_START_ARRAY: + if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { + p.nextToken(); + final OptionalInt parsed = deserialize(p, ctxt); + _verifyEndArrayForSingle(p, ctxt); + return parsed; + } + break; + default: + } + return (OptionalInt) ctxt.handleUnexpectedToken(_valueClass, p); + } +} diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalIntSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalIntSerializer.java new file mode 100644 index 0000000000..3c3345b537 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalIntSerializer.java @@ -0,0 +1,48 @@ +package com.fasterxml.jackson.databind.ext.jdk8; + +import java.io.IOException; +import java.util.OptionalInt; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonIntegerFormatVisitor; +import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer; + +public class OptionalIntSerializer extends StdScalarSerializer +{ + private static final long serialVersionUID = 1L; + + public OptionalIntSerializer() { + super(OptionalInt.class); + } + + @Override + public boolean isEmpty(SerializerProvider provider, OptionalInt value) { + return (value == null) || !value.isPresent(); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, + JavaType typeHint) throws JsonMappingException { + JsonIntegerFormatVisitor v2 = visitor + .expectIntegerFormat(typeHint); + if (v2 != null) { + v2.numberType(JsonParser.NumberType.INT); + } + } + + @Override + public void serialize(OptionalInt value, JsonGenerator gen, SerializerProvider provider) + throws IOException + { + if (value.isPresent()) { + gen.writeNumber(value.getAsInt()); + } else { + gen.writeNull(); + } + } +} diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalLongDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalLongDeserializer.java new file mode 100644 index 0000000000..6fe66d40ec --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalLongDeserializer.java @@ -0,0 +1,59 @@ +package com.fasterxml.jackson.databind.ext.jdk8; + +import java.io.IOException; +import java.util.OptionalLong; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.core.JsonTokenId; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.DeserializationFeature; + +public class OptionalLongDeserializer extends BaseScalarOptionalDeserializer +{ + private static final long serialVersionUID = 1L; + + static final OptionalLongDeserializer INSTANCE = new OptionalLongDeserializer(); + + public OptionalLongDeserializer() { + super(OptionalLong.class, OptionalLong.empty()); + } + + @Override + public OptionalLong deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + { + // minor optimization, first, for common case + if (p.hasToken(JsonToken.VALUE_NUMBER_INT)) { + return OptionalLong.of(p.getLongValue()); + } + switch (p.currentTokenId()) { + case JsonTokenId.ID_STRING: + String text = p.getText().trim(); + if ((text.length() == 0)) { + _coerceEmptyString(ctxt, false); + return _empty; + } + if (_hasTextualNull(text)) { + _coerceTextualNull(ctxt, false); + return _empty; + } + return OptionalLong.of(_parseLongPrimitive(ctxt, text)); + case JsonTokenId.ID_NUMBER_FLOAT: + if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_FLOAT_AS_INT)) { + _failDoubleToIntCoercion(p, ctxt, "long"); + } + return OptionalLong.of(p.getValueAsLong()); + case JsonTokenId.ID_NULL: + return _empty; + case JsonTokenId.ID_START_ARRAY: + if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { + p.nextToken(); + final OptionalLong parsed = deserialize(p, ctxt); + _verifyEndArrayForSingle(p, ctxt); + return parsed; + } + break; + } + return (OptionalLong) ctxt.handleUnexpectedToken(_valueClass, p); + } +} diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalLongSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalLongSerializer.java new file mode 100644 index 0000000000..6cea35516a --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalLongSerializer.java @@ -0,0 +1,51 @@ +package com.fasterxml.jackson.databind.ext.jdk8; + +import java.io.IOException; +import java.util.OptionalLong; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonIntegerFormatVisitor; +import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer; + +public class OptionalLongSerializer extends StdScalarSerializer +{ + private static final long serialVersionUID = 1L; + + static final OptionalLongSerializer INSTANCE = new OptionalLongSerializer(); + + public OptionalLongSerializer() { + super(OptionalLong.class); + } + + // @since 2.6 + @Override + public boolean isEmpty(SerializerProvider provider, OptionalLong value) { + return (value == null) || !value.isPresent(); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, + JavaType typeHint) throws JsonMappingException { + JsonIntegerFormatVisitor v2 = visitor + .expectIntegerFormat(typeHint); + if (v2 != null) { + v2.numberType(JsonParser.NumberType.LONG); + } + } + + @Override + public void serialize(OptionalLong value, JsonGenerator jgen, SerializerProvider provider) + throws IOException + { + if (value.isPresent()) { + jgen.writeNumber(value.getAsLong()); + } else { // should we get here? + jgen.writeNull(); + } + } +} diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/package-info.java b/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/package-info.java new file mode 100644 index 0000000000..e488ad6dfa --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/ext/jdk8/package-info.java @@ -0,0 +1,9 @@ +/** +Package that contains handlers specific to datatypes introduced in Java 8. +Previously these were added via additional external modules, but with Jackson 3 +most (if not all) types are included directly in databind. + +@since 3.0 +*/ + +package com.fasterxml.jackson.databind.ext.jdk8; 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 fce7b9e303..8e716b2697 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/Annotated.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/Annotated.java @@ -3,10 +3,8 @@ import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Modifier; -import java.lang.reflect.Type; import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.type.TypeBindings; /** * Shared base class used for anything on which annotations (included @@ -20,9 +18,6 @@ protected Annotated() { } public abstract boolean hasAnnotation(Class acls); - /** - * @since 2.7 - */ public abstract boolean hasOneOf(Class[] annoClasses); /** @@ -43,49 +38,15 @@ public boolean isPublic() { /** * Full generic type of the annotated element; definition * of what exactly this means depends on sub-class. - * - * @since 2.7 */ public abstract JavaType getType(); - /** - * @deprecated Since 2.7 Use {@link #getType()} instead. To be removed from 2.9 - */ - @Deprecated - public final JavaType getType(TypeBindings bogus) { - return getType(); - } - - /** - * JDK declared generic type of the annotated element; definition - * of what exactly this means depends on sub-class. Note that such type - * cannot be reliably resolved without {@link TypeResolutionContext}, and - * as a result use of this method was deprecated in Jackson 2.7: see - * {@link #getType} for replacement. - * - * @deprecated Since 2.7 should instead use {@link #getType()}. To be removed from 2.9 - */ - @Deprecated - public Type getGenericType() { - return getRawType(); - } - /** * "Raw" type (type-erased class) of the annotated element; definition * of what exactly this means depends on sub-class. */ public abstract Class getRawType(); - /** - * Accessor that can be used to iterate over all the annotations - * associated with annotated component. - * - * @since 2.3 - * @deprecated Since 2.9 should instead use {@link #getAnnotated()} - */ - @Deprecated - public abstract Iterable annotations(); - // Also: ensure we can use #equals, #hashCode @Override 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 2c8b733e82..4b640194a3 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java @@ -6,8 +6,6 @@ import com.fasterxml.jackson.databind.AnnotationIntrospector; import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.cfg.MapperConfig; -import com.fasterxml.jackson.databind.introspect.ClassIntrospector.MixInResolver; import com.fasterxml.jackson.databind.type.TypeBindings; import com.fasterxml.jackson.databind.type.TypeFactory; import com.fasterxml.jackson.databind.util.Annotations; @@ -27,9 +25,6 @@ public final class AnnotatedClass /********************************************************** */ - /** - * @since 2.7 - */ final protected JavaType _type; /** @@ -40,8 +35,6 @@ public final class AnnotatedClass /** * Type bindings to use for members of {@link #_class}. - * - * @since 2.7 */ final protected TypeBindings _bindings; @@ -58,9 +51,6 @@ public final class AnnotatedClass */ final protected AnnotationIntrospector _annotationIntrospector; - /** - * @since 2.7 - */ final protected TypeFactory _typeFactory; /** @@ -88,9 +78,6 @@ public final class AnnotatedClass */ final protected Annotations _classAnnotations; - /** - * @since 2.9 - */ protected Creators _creators; /** @@ -108,8 +95,6 @@ public final class AnnotatedClass /** * Lazily determined property to see if this is a non-static inner * class. - * - * @since 2.8.7 */ protected transient Boolean _nonStaticInnerClass; @@ -145,8 +130,6 @@ public final class AnnotatedClass /** * Constructor (only) used for creating primordial simple types (during bootstrapping) * and array type placeholders where no fields or methods are needed. - * - * @since 2.9 */ AnnotatedClass(Class rawType) { _type = null; @@ -160,47 +143,6 @@ public final class AnnotatedClass _typeFactory = null; } - /** - * @deprecated Since 2.9, use methods in {@link AnnotatedClassResolver} instead. - */ - @Deprecated - public static AnnotatedClass construct(JavaType type, MapperConfig config) { - return construct(type, config, (MixInResolver) config); - } - - /** - * @deprecated Since 2.9, use methods in {@link AnnotatedClassResolver} instead. - */ - @Deprecated - public static AnnotatedClass construct(JavaType type, MapperConfig config, - MixInResolver mir) - { - return AnnotatedClassResolver.resolve(config, type, mir); - } - - /** - * Method similar to {@link #construct}, but that will NOT include - * information from supertypes; only class itself and any direct - * mix-ins it may have. - */ - /** - * @deprecated Since 2.9, use methods in {@link AnnotatedClassResolver} instead. - */ - @Deprecated - public static AnnotatedClass constructWithoutSuperTypes(Class raw, MapperConfig config) { - return constructWithoutSuperTypes(raw, config, config); - } - - /** - * @deprecated Since 2.9, use methods in {@link AnnotatedClassResolver} instead. - */ - @Deprecated - public static AnnotatedClass constructWithoutSuperTypes(Class raw, MapperConfig config, - MixInResolver mir) - { - return AnnotatedClassResolver.resolveWithoutSuperTypes(config, raw, mir); - } - /* /********************************************************** /* TypeResolutionContext implementation @@ -247,18 +189,6 @@ public Class getRawType() { return _class; } - @Override - @Deprecated - public Iterable annotations() { - if (_classAnnotations instanceof AnnotationMap) { - return ((AnnotationMap) _classAnnotations).annotations(); - } else if (_classAnnotations instanceof AnnotationCollector.OneAnnotation || - _classAnnotations instanceof AnnotationCollector.TwoAnnotations) { - throw new UnsupportedOperationException("please use getAnnotations/ hasAnnotation to check for Annotations"); - } - return Collections.emptyList(); - } - @Override public JavaType getType() { return _type; @@ -286,21 +216,10 @@ public List getConstructors() { return _creators().constructors; } - /** - * @since 2.9 - */ public List getFactoryMethods() { return _creators().creatorMethods; } - /** - * @deprecated Since 2.9; use {@link #getFactoryMethods} instead. - */ - @Deprecated - public List getStaticMethods() { - return getFactoryMethods(); - } - public Iterable memberMethods() { return _methods(); } @@ -321,9 +240,6 @@ public Iterable fields() { return _fields(); } - /** - * @since 2.9 - */ public boolean isNonStaticInnerClass() { Boolean B = _nonStaticInnerClass; @@ -347,7 +263,8 @@ private final List _fields() { f = Collections.emptyList(); } else { f = AnnotatedFieldCollector.collectFields(_annotationIntrospector, - this, _mixInResolver, _typeFactory, _type); + this, _mixInResolver, _typeFactory, + _type, _primaryMixIn); } _fields = f; } @@ -363,8 +280,7 @@ private final AnnotatedMethodMap _methods() { m = new AnnotatedMethodMap(); } else { m = AnnotatedMethodCollector.collectMethods(_annotationIntrospector, - this, - _mixInResolver, _typeFactory, + this, _mixInResolver, _typeFactory, _type, _superTypes, _primaryMixIn); } _memberMethods = m; diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClassResolver.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClassResolver.java index 65326e23be..544be43ec1 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClassResolver.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClassResolver.java @@ -9,7 +9,6 @@ import com.fasterxml.jackson.databind.AnnotationIntrospector; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.cfg.MapperConfig; -import com.fasterxml.jackson.databind.introspect.ClassIntrospector.MixInResolver; import com.fasterxml.jackson.databind.type.TypeBindings; import com.fasterxml.jackson.databind.util.Annotations; import com.fasterxml.jackson.databind.util.ClassUtil; @@ -17,8 +16,6 @@ /** * Helper class that contains logic for resolving annotations to construct * {@link AnnotatedClass} instances. - * - * @since 2.9 */ public class AnnotatedClassResolver { @@ -128,9 +125,9 @@ AnnotatedClass resolveWithoutSuperTypes() { } /* - /********************************************************** + /********************************************************************** /* Class annotation resolution - /********************************************************** + /********************************************************************** */ /** 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 4e17a00d35..6f7619d84e 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedConstructor.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedConstructor.java @@ -13,10 +13,7 @@ public final class AnnotatedConstructor protected final Constructor _constructor; /** - * Field that is used to make JDK serialization work with this - * object. - * - * @since 2.1 + * Field that is used to make JDK serialization work with this object. */ protected Serialization _serialization; @@ -38,7 +35,6 @@ public AnnotatedConstructor(TypeResolutionContext ctxt, Constructor construct /** * Method used for JDK serialization support - * @since 2.1 */ protected AnnotatedConstructor(Serialization ser) { @@ -105,15 +101,10 @@ public JavaType getParameterType(int index) { } @Override - @Deprecated // since 2.7 - public Type getGenericParameterType(int index) { - Type[] types = _constructor.getGenericParameterTypes(); - if (index >= types.length) { - return null; - } - return types[index]; + public Parameter[] getNativeParameters() { + return _constructor.getParameters(); } - + @Override public final Object call() throws Exception { return _constructor.newInstance(); @@ -128,7 +119,7 @@ public final Object call(Object[] args) throws Exception { public final Object call1(Object arg) throws Exception { return _constructor.newInstance(arg); } - + /* /********************************************************** /* AnnotatedMember impl 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 b81c51ec4e..e905f95f8d 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedField.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedField.java @@ -75,12 +75,6 @@ public Class getRawType() { return _field.getType(); } - @Deprecated - @Override - public Type getGenericType() { - return _field.getGenericType(); - } - @Override public JavaType getType() { return _typeContext.resolveType(_field.getGenericType()); diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedFieldCollector.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedFieldCollector.java index 0dba88bba3..45391eafcc 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedFieldCollector.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedFieldCollector.java @@ -6,39 +6,32 @@ import com.fasterxml.jackson.databind.AnnotationIntrospector; import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.introspect.ClassIntrospector.MixInResolver; import com.fasterxml.jackson.databind.type.TypeFactory; import com.fasterxml.jackson.databind.util.ClassUtil; public class AnnotatedFieldCollector extends CollectorBase { - // // // Configuration - - private final TypeFactory _typeFactory; private final MixInResolver _mixInResolver; - // // // Collected state - - AnnotatedFieldCollector(AnnotationIntrospector intr, - TypeFactory types, MixInResolver mixins) + AnnotatedFieldCollector(AnnotationIntrospector intr, MixInResolver mixins) { super(intr); - _typeFactory = types; _mixInResolver = (intr == null) ? null : mixins; } public static List collectFields(AnnotationIntrospector intr, - TypeResolutionContext tc, - MixInResolver mixins, TypeFactory types, - JavaType type) + TypeResolutionContext tc, MixInResolver mixins, TypeFactory typeFactory, + JavaType type, Class primaryMixIn) { - return new AnnotatedFieldCollector(intr, types, mixins).collect(tc, type); + return new AnnotatedFieldCollector(intr, mixins).collect(tc, typeFactory, + type, primaryMixIn); } - List collect(TypeResolutionContext tc, JavaType type) + List collect(TypeResolutionContext tc, TypeFactory typeFactory, + JavaType type, Class primaryMixIn) { - Map foundFields = _findFields(tc, type, null); + Map foundFields = _findFields(tc, typeFactory, type, primaryMixIn, null); if (foundFields == null) { return Collections.emptyList(); } @@ -49,21 +42,26 @@ List collect(TypeResolutionContext tc, JavaType type) return result; } - private Map _findFields(TypeResolutionContext tc, - JavaType type, Map fields) + private Map _findFields(TypeResolutionContext tc, TypeFactory typeFactory, + JavaType type, Class mixin, + Map fields) { // First, a quick test: we only care for regular classes (not interfaces, //primitive types etc), except for Object.class. A simple check to rule out // other cases is to see if there is a super class or not. - JavaType parent = type.getSuperClass(); - if (parent == null) { + final JavaType parentType = type.getSuperClass(); + if (parentType == null) { return fields; } - final Class cls = type.getRawClass(); // Let's add super-class' fields first, then ours. - fields = _findFields(new TypeResolutionContext.Basic(_typeFactory, parent.getBindings()), - parent, fields); - for (Field f : ClassUtil.getDeclaredFields(cls)) { + { + Class parentMixin = (_mixInResolver == null) ? null + : _mixInResolver.findMixInClassFor(parentType.getRawClass()); + fields = _findFields(new TypeResolutionContext.Basic(typeFactory, parentType.getBindings()), + typeFactory, parentType, parentMixin, fields); + } + final Class rawType = type.getRawClass(); + for (Field f : ClassUtil.getDeclaredFields(rawType)) { // static fields not included (transients are at this point, filtered out later) if (!_isIncludableField(f)) { continue; @@ -81,11 +79,8 @@ private Map _findFields(TypeResolutionContext tc, fields.put(f.getName(), b); } // And then... any mix-in overrides? - if (_mixInResolver != null) { - Class mixin = _mixInResolver.findMixInClassFor(cls); - if (mixin != null) { - _addFieldMixIns(mixin, cls, fields); - } + if (mixin != null) { + _addFieldMixIns(mixin, rawType, fields); } return fields; } 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 0f7f3d2928..6b57aafc3d 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMember.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMember.java @@ -2,7 +2,6 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Member; -import java.util.Collections; import com.fasterxml.jackson.databind.util.ClassUtil; @@ -23,8 +22,6 @@ public abstract class AnnotatedMember /** * Context object needed for resolving generic type associated with this * member (method parameter or return value, or field type). - * - * @since 2.7 */ protected final transient TypeResolutionContext _typeContext; @@ -40,8 +37,6 @@ protected AnnotatedMember(TypeResolutionContext ctxt, AnnotationMap annotations) /** * Copy-constructor. - * - * @since 2.5 */ protected AnnotatedMember(AnnotatedMember base) { _typeContext = base._typeContext; @@ -51,8 +46,6 @@ protected AnnotatedMember(AnnotatedMember base) { /** * Fluent factory method that will construct a new instance that uses specified * instance annotations instead of currently configured ones. - * - * @since 2.9 (promoted from `Annotated`) */ public abstract Annotated withAnnotations(AnnotationMap fallback); @@ -67,19 +60,6 @@ public String getFullName() { return getDeclaringClass().getName() + "#" + getName(); } - /** - * Accessor for {@link TypeResolutionContext} that is used for resolving - * full generic type of this member. - * - * @since 2.7 - * - * @deprecated Since 2.9 - */ - @Deprecated - public TypeResolutionContext getTypeContext() { - return _typeContext; - } - @Override public final A getAnnotation(Class acls) { if (_annotations == null) { @@ -104,19 +84,10 @@ public boolean hasOneOf(Class[] annoClasses) { return _annotations.hasOneOf(annoClasses); } - @Override - @Deprecated - public Iterable annotations() { - if (_annotations == null) { - return Collections.emptyList(); - } - return _annotations.annotations(); - } - /** - *

    - * NOTE: promoted in 2.9 from `Annotated` up + * @deprecated Since 3.0 */ + @Deprecated public AnnotationMap getAllAnnotations() { // alas, used by at least one module, hence public return _annotations; } @@ -130,8 +101,6 @@ public AnnotationMap getAllAnnotations() { // alas, used by at least one module, * {@link com.fasterxml.jackson.databind.MapperFeature#CAN_OVERRIDE_ACCESS_MODIFIERS} * is enabled before calling this method; as well as pass * force flag appropriately. - * - * @since 2.7 */ public final void fixAccess(boolean force) { Member m = getMember(); 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 2338987129..22f93a7c02 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethod.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethod.java @@ -20,8 +20,6 @@ public final class AnnotatedMethod /** * Field that is used to make JDK serialization work with this * object. - * - * @since 2.1 */ protected Serialization _serialization; @@ -43,7 +41,6 @@ public AnnotatedMethod(TypeResolutionContext ctxt, Method method, /** * Method used for JDK serialization support - * @since 2.1 */ protected AnnotatedMethod(Serialization ser) { @@ -87,12 +84,6 @@ public Class getRawType() { return _method.getReturnType(); } - @Deprecated - @Override - public Type getGenericType() { - return _method.getGenericReturnType(); - } - /* /***************************************************** /* AnnotatedWithParams @@ -150,15 +141,10 @@ public JavaType getParameterType(int index) { } @Override - @Deprecated // since 2.7 - public Type getGenericParameterType(int index) { - Type[] types = getGenericParameterTypes(); - if (index >= types.length) { - return null; - } - return types[index]; + public Parameter[] getNativeParameters() { + return _method.getParameters(); } - + @Override public Class getDeclaringClass() { return _method.getDeclaringClass(); } @@ -206,11 +192,6 @@ public Class[] getRawParameterTypes() return _paramClasses; } - @Deprecated // since 2.7 - public Type[] getGenericParameterTypes() { - return _method.getGenericParameterTypes(); - } - public Class getRawReturnType() { return _method.getReturnType(); } @@ -219,8 +200,6 @@ public Class getRawReturnType() { * Helper method that can be used to check whether method returns * a value or not; if return type declared as void, returns * false, otherwise true - * - * @since 2.4 */ public boolean hasReturnType() { Class rt = getRawReturnType(); diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethodCollector.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethodCollector.java index 0341e3a2af..894052036b 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethodCollector.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethodCollector.java @@ -7,7 +7,6 @@ import com.fasterxml.jackson.databind.AnnotationIntrospector; import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.introspect.ClassIntrospector.MixInResolver; import com.fasterxml.jackson.databind.type.TypeFactory; import com.fasterxml.jackson.databind.util.ClassUtil; @@ -16,24 +15,22 @@ public class AnnotatedMethodCollector { private final MixInResolver _mixInResolver; - AnnotatedMethodCollector(AnnotationIntrospector intr, - MixInResolver mixins) + AnnotatedMethodCollector(AnnotationIntrospector intr, MixInResolver mixins) { super(intr); _mixInResolver = (intr == null) ? null : mixins; } public static AnnotatedMethodMap collectMethods(AnnotationIntrospector intr, - TypeResolutionContext tc, - MixInResolver mixins, TypeFactory types, + TypeResolutionContext tc, MixInResolver mixins, TypeFactory typeFactory, JavaType type, List superTypes, Class primaryMixIn) { // Constructor also always members of resolved class, parent == resolution context return new AnnotatedMethodCollector(intr, mixins) - .collect(types, tc, type, superTypes, primaryMixIn); + .collect(tc, typeFactory, type, superTypes, primaryMixIn); } - AnnotatedMethodMap collect(TypeFactory typeFactory, TypeResolutionContext tc, + AnnotatedMethodMap collect(TypeResolutionContext tc, TypeFactory typeFactory, JavaType mainType, List superTypes, Class primaryMixIn) { Map methods = new LinkedHashMap<>(); 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 9835c35852..f38ab57fa6 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedParameter.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedParameter.java @@ -93,12 +93,6 @@ public JavaType getType() { return _type; } - @Deprecated - @Override - public Type getGenericType() { - return _owner.getGenericParameterType(_index); - } - /* /********************************************************** /* AnnotatedMember extras @@ -112,9 +106,8 @@ public Class getDeclaringClass() { @Override public Member getMember() { - /* This is bit tricky: since there is no JDK equivalent; can either - * return null or owner... let's do latter, for now. - */ + // This is bit tricky: since there is no JDK equivalent; can either + // return null or owner... let's do latter, for now. return _owner.getMember(); } diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedWithParams.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedWithParams.java index 4f415a68e3..7836edd9a8 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedWithParams.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedWithParams.java @@ -1,7 +1,7 @@ package com.fasterxml.jackson.databind.introspect; -import java.lang.annotation.Annotation; -import java.lang.reflect.Type; +import java.lang.reflect.Modifier; +import java.lang.reflect.Parameter; import com.fasterxml.jackson.databind.JavaType; @@ -32,9 +32,6 @@ protected AnnotatedWithParams(TypeResolutionContext ctxt, AnnotationMap annotati _paramAnnotations = paramAnnotations; } - /** - * @since 2.8.1 - */ protected AnnotatedWithParams(AnnotatedWithParams base, AnnotationMap[] paramAnnotations) { super(base); _paramAnnotations = paramAnnotations; @@ -45,7 +42,8 @@ protected AnnotatedWithParams(AnnotatedWithParams base, AnnotationMap[] paramAnn * usually due to a mix-in * annotation masking or overriding an annotation 'real' method * has. - */ + * + @Deprecated // since 3.0 public final void addOrOverrideParam(int paramIndex, Annotation a) { AnnotationMap old = _paramAnnotations[paramIndex]; @@ -55,6 +53,7 @@ public final void addOrOverrideParam(int paramIndex, Annotation a) } old.add(a); } + */ /** * Method called by parameter object when an augmented instance is created; @@ -88,19 +87,20 @@ public final AnnotatedParameter getParameter(int index) { } public abstract int getParameterCount(); - public abstract Class getRawParameterType(int index); + public abstract JavaType getParameterType(int index); /** - * @since 2.7 + * @since 3.0 */ - public abstract JavaType getParameterType(int index); + public abstract Parameter[] getNativeParameters(); /** - * @deprecated Since 2.7, remove in 2.9 + * @since 3.0 */ - @Deprecated - public abstract Type getGenericParameterType(int index); + public boolean isStatic() { + return Modifier.isStatic(getModifiers()); + } public final int getAnnotationCount() { return _annotations.size(); } diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationCollector.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationCollector.java index 940d435f7c..0a741873c2 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationCollector.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationCollector.java @@ -10,8 +10,6 @@ /** * Helper class used to collect annotations to be stored as * {@link com.fasterxml.jackson.databind.util.Annotations} (like {@link AnnotationMap}). - * - * @since 2.9 */ public abstract class AnnotationCollector { @@ -149,11 +147,7 @@ public Annotations asAnnotations() { @Override public AnnotationMap asAnnotationMap() { - AnnotationMap result = new AnnotationMap(); - for (Annotation ann : _annotations.values()) { - result.add(ann); - } - return result; + return AnnotationMap.of(_annotations.values()); } @Override 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 aa288b9e17..d20916eb33 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair.java @@ -5,20 +5,15 @@ import java.util.Collection; import java.util.List; -import com.fasterxml.jackson.annotation.JacksonInject; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonFormat; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.*; + import com.fasterxml.jackson.core.Version; + import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.cfg.MapperConfig; import com.fasterxml.jackson.databind.jsontype.NamedType; -import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; import com.fasterxml.jackson.databind.util.ClassUtil; import com.fasterxml.jackson.databind.util.NameTransformer; @@ -33,11 +28,6 @@ * longer chains of introspectors by linking multiple pairs. * Currently most likely combination is that of using the default * Jackson provider, along with JAXB annotation introspector. - *

    - * Note: up until 2.0, this class was an inner class of - * {@link AnnotationIntrospector}; moved here for convenience. - * - * @since 2.1 */ public class AnnotationIntrospectorPair extends AnnotationIntrospector @@ -96,9 +86,9 @@ public boolean isAnnotationBundle(Annotation ann) { } /* - /****************************************************** + /********************************************************************** /* General class annotations - /****************************************************** + /********************************************************************** */ @Override @@ -164,98 +154,88 @@ public String findClassDescription(AnnotatedClass ac) { return str; } - @Override - @Deprecated // since 2.6 - public String[] findPropertiesToIgnore(Annotated ac) { - String[] result = _primary.findPropertiesToIgnore(ac); - if (result == null) { - result = _secondary.findPropertiesToIgnore(ac); - } - return result; - } - - @Override - @Deprecated // since 2.8 - public String[] findPropertiesToIgnore(Annotated ac, boolean forSerialization) { - String[] result = _primary.findPropertiesToIgnore(ac, forSerialization); - if (result == null) { - result = _secondary.findPropertiesToIgnore(ac, forSerialization); - } - return result; - } - - @Override - @Deprecated // since 2.8 - public Boolean findIgnoreUnknownProperties(AnnotatedClass ac) - { - Boolean result = _primary.findIgnoreUnknownProperties(ac); - if (result == null) { - result = _secondary.findIgnoreUnknownProperties(ac); - } - return result; - } - /* - /****************************************************** + /********************************************************************** /* Property auto-detection - /****************************************************** - */ + /********************************************************************** + */ @Override - public VisibilityChecker findAutoDetectVisibility(AnnotatedClass ac, - VisibilityChecker checker) + public VisibilityChecker findAutoDetectVisibility(MapperConfig config, + AnnotatedClass ac, VisibilityChecker checker) { /* Note: to have proper priorities, we must actually call delegatees * in reverse order: */ - checker = _secondary.findAutoDetectVisibility(ac, checker); - return _primary.findAutoDetectVisibility(ac, checker); + checker = _secondary.findAutoDetectVisibility(config, ac, checker); + return _primary.findAutoDetectVisibility(config, ac, checker); } /* - /****************************************************** + /********************************************************************** /* Type handling - /****************************************************** + /********************************************************************** */ @Override - public TypeResolverBuilder findTypeResolver(MapperConfig config, - AnnotatedClass ac, JavaType baseType) + public JsonTypeInfo.Value findPolymorphicTypeInfo(MapperConfig config, + Annotated ann) { - TypeResolverBuilder b = _primary.findTypeResolver(config, ac, baseType); + JsonTypeInfo.Value v = _primary.findPolymorphicTypeInfo(config, ann); + if (v == null) { + v = _secondary.findPolymorphicTypeInfo(config, ann); + } + return v; + } + + @Override + public Object findTypeResolverBuilder(MapperConfig config, + Annotated ann) { + Object b = _primary.findTypeResolverBuilder(config, ann); if (b == null) { - b = _secondary.findTypeResolver(config, ac, baseType); + b = _secondary.findTypeResolverBuilder(config, ann); } return b; } + @Override + public Object findTypeIdResolver(MapperConfig config, Annotated ann) { + Object b = _primary.findTypeIdResolver(config, ann); + if (b == null) { + b = _secondary.findTypeIdResolver(config, ann); + } + return b; + } + + /* @Override public TypeResolverBuilder findPropertyTypeResolver(MapperConfig config, - AnnotatedMember am, JavaType baseType) + Annotated ann, JavaType baseType, JsonTypeInfo.Value typeInfo) { - TypeResolverBuilder b = _primary.findPropertyTypeResolver(config, am, baseType); + TypeResolverBuilder b = _primary.findPropertyTypeResolver(config, ann, baseType, typeInfo); if (b == null) { - b = _secondary.findPropertyTypeResolver(config, am, baseType); + b = _secondary.findPropertyTypeResolver(config, ann, baseType, typeInfo); } return b; } @Override public TypeResolverBuilder findPropertyContentTypeResolver(MapperConfig config, - AnnotatedMember am, JavaType baseType) + Annotated ann, JavaType baseType, JsonTypeInfo.Value typeInfo) { - TypeResolverBuilder b = _primary.findPropertyContentTypeResolver(config, am, baseType); + TypeResolverBuilder b = _primary.findPropertyContentTypeResolver(config, ann, baseType, typeInfo); if (b == null) { - b = _secondary.findPropertyContentTypeResolver(config, am, baseType); + b = _secondary.findPropertyContentTypeResolver(config, ann, baseType, typeInfo); } return b; } + */ @Override - public List findSubtypes(Annotated a) + public List findSubtypes(MapperConfig config, Annotated a) { - List types1 = _primary.findSubtypes(a); - List types2 = _secondary.findSubtypes(a); + List types1 = _primary.findSubtypes(config, a); + List types2 = _secondary.findSubtypes(config, a); if (types1 == null || types1.isEmpty()) return types2; if (types2 == null || types2.isEmpty()) return types1; ArrayList result = new ArrayList(types1.size() + types2.size()); @@ -265,18 +245,19 @@ public List findSubtypes(Annotated a) } @Override - public String findTypeName(AnnotatedClass ac) + public String findTypeName(MapperConfig config, AnnotatedClass ac) { - String name = _primary.findTypeName(ac); + String name = _primary.findTypeName(config, ac); if (name == null || name.length() == 0) { - name = _secondary.findTypeName(ac); + name = _secondary.findTypeName(config, ac); } return name; } + /* - /****************************************************** + /********************************************************************** /* General member (field, method/constructor) annotations - /****************************************************** + /********************************************************************** */ @Override @@ -308,81 +289,53 @@ public Boolean hasRequiredMarker(AnnotatedMember m) { return (r == null) ? _secondary.hasRequiredMarker(m) : r; } - @Override - @Deprecated // since 2.9 - public Object findInjectableValueId(AnnotatedMember m) { - Object r = _primary.findInjectableValueId(m); - return (r == null) ? _secondary.findInjectableValueId(m) : r; - } - // // // Serialization: general annotations @Override - public Object findSerializer(Annotated am) { - Object r = _primary.findSerializer(am); + public Object findSerializer(MapperConfig config, Annotated am) { + Object r = _primary.findSerializer(config, am); if (_isExplicitClassOrOb(r, JsonSerializer.None.class)) { return r; } - return _explicitClassOrOb(_secondary.findSerializer(am), + return _explicitClassOrOb(_secondary.findSerializer(config, am), JsonSerializer.None.class); } @Override - public Object findKeySerializer(Annotated a) { - Object r = _primary.findKeySerializer(a); + public Object findKeySerializer(MapperConfig config, Annotated a) { + Object r = _primary.findKeySerializer(config, a); if (_isExplicitClassOrOb(r, JsonSerializer.None.class)) { return r; } - return _explicitClassOrOb(_secondary.findKeySerializer(a), + return _explicitClassOrOb(_secondary.findKeySerializer(config, a), JsonSerializer.None.class); } @Override - public Object findContentSerializer(Annotated a) { - Object r = _primary.findContentSerializer(a); + public Object findContentSerializer(MapperConfig config, Annotated a) { + Object r = _primary.findContentSerializer(config, a); if (_isExplicitClassOrOb(r, JsonSerializer.None.class)) { return r; } - return _explicitClassOrOb(_secondary.findContentSerializer(a), + return _explicitClassOrOb(_secondary.findContentSerializer(config, a), JsonSerializer.None.class); } @Override - public Object findNullSerializer(Annotated a) { - Object r = _primary.findNullSerializer(a); + public Object findNullSerializer(MapperConfig config, Annotated a) { + Object r = _primary.findNullSerializer(config, a); if (_isExplicitClassOrOb(r, JsonSerializer.None.class)) { return r; } - return _explicitClassOrOb(_secondary.findNullSerializer(a), + return _explicitClassOrOb(_secondary.findNullSerializer(config, a), JsonSerializer.None.class); } - - @Deprecated - @Override - public JsonInclude.Include findSerializationInclusion(Annotated a, - JsonInclude.Include defValue) - { - // note: call secondary first, to give lower priority - defValue = _secondary.findSerializationInclusion(a, defValue); - defValue = _primary.findSerializationInclusion(a, defValue); - return defValue; - } - - @Deprecated - @Override - public JsonInclude.Include findSerializationInclusionForContent(Annotated a, JsonInclude.Include defValue) - { - // note: call secondary first, to give lower priority - defValue = _secondary.findSerializationInclusionForContent(a, defValue); - defValue = _primary.findSerializationInclusionForContent(a, defValue); - return defValue; - } @Override - public JsonInclude.Value findPropertyInclusion(Annotated a) + public JsonInclude.Value findPropertyInclusion(MapperConfig config, Annotated a) { - JsonInclude.Value v2 = _secondary.findPropertyInclusion(a); - JsonInclude.Value v1 = _primary.findPropertyInclusion(a); + JsonInclude.Value v2 = _secondary.findPropertyInclusion(config, a); + JsonInclude.Value v1 = _primary.findPropertyInclusion(config, a); if (v2 == null) { // shouldn't occur but return v1; @@ -391,21 +344,21 @@ public JsonInclude.Value findPropertyInclusion(Annotated a) } @Override - public JsonSerialize.Typing findSerializationTyping(Annotated a) { - JsonSerialize.Typing r = _primary.findSerializationTyping(a); - return (r == null) ? _secondary.findSerializationTyping(a) : r; + public JsonSerialize.Typing findSerializationTyping(MapperConfig config, Annotated a) { + JsonSerialize.Typing r = _primary.findSerializationTyping(config, a); + return (r == null) ? _secondary.findSerializationTyping(config, a) : r; } @Override - public Object findSerializationConverter(Annotated a) { - Object r = _primary.findSerializationConverter(a); - return (r == null) ? _secondary.findSerializationConverter(a) : r; + public Object findSerializationConverter(MapperConfig config, Annotated a) { + Object r = _primary.findSerializationConverter(config, a); + return (r == null) ? _secondary.findSerializationConverter(config, a) : r; } @Override - public Object findSerializationContentConverter(AnnotatedMember a) { - Object r = _primary.findSerializationContentConverter(a); - return (r == null) ? _secondary.findSerializationContentConverter(a) : r; + public Object findSerializationContentConverter(MapperConfig config, AnnotatedMember a) { + Object r = _primary.findSerializationContentConverter(config, a); + return (r == null) ? _secondary.findSerializationContentConverter(config, a) : r; } @Override @@ -422,22 +375,23 @@ public Class[] findViews(Annotated a) { } @Override - public Boolean isTypeId(AnnotatedMember member) { - Boolean b = _primary.isTypeId(member); - return (b == null) ? _secondary.isTypeId(member) : b; + public Boolean isTypeId(MapperConfig config, AnnotatedMember member) { + Boolean b = _primary.isTypeId(config, member); + return (b == null) ? _secondary.isTypeId(config, member) : b; } @Override - public ObjectIdInfo findObjectIdInfo(Annotated ann) { - ObjectIdInfo r = _primary.findObjectIdInfo(ann); - return (r == null) ? _secondary.findObjectIdInfo(ann) : r; + public ObjectIdInfo findObjectIdInfo(MapperConfig config, Annotated ann) { + ObjectIdInfo r = _primary.findObjectIdInfo(config, ann); + return (r == null) ? _secondary.findObjectIdInfo(config, ann) : r; } @Override - public ObjectIdInfo findObjectReferenceInfo(Annotated ann, ObjectIdInfo objectIdInfo) { + public ObjectIdInfo findObjectReferenceInfo(MapperConfig config, + Annotated ann, ObjectIdInfo objectIdInfo) { // to give precedence for primary, must start with secondary: - objectIdInfo = _secondary.findObjectReferenceInfo(ann, objectIdInfo); - objectIdInfo = _primary.findObjectReferenceInfo(ann, objectIdInfo); + objectIdInfo = _secondary.findObjectReferenceInfo(config, ann, objectIdInfo); + objectIdInfo = _primary.findObjectReferenceInfo(config, ann, objectIdInfo); return objectIdInfo; } @@ -509,7 +463,7 @@ public JsonProperty.Access findPropertyAccess(Annotated ann) { return JsonProperty.Access.AUTO; } - @Override // since 2.7 + @Override public AnnotatedMethod resolveSetterConflict(MapperConfig config, AnnotatedMethod setter1, AnnotatedMethod setter2) { @@ -522,35 +476,14 @@ public AnnotatedMethod resolveSetterConflict(MapperConfig config, // // // Serialization: type refinements - @Override // since 2.7 + @Override public JavaType refineSerializationType(MapperConfig config, Annotated a, JavaType baseType) throws JsonMappingException { JavaType t = _secondary.refineSerializationType(config, a, baseType); return _primary.refineSerializationType(config, a, t); } - - @Override - @Deprecated - public Class findSerializationType(Annotated a) { - Class r = _primary.findSerializationType(a); - return (r == null) ? _secondary.findSerializationType(a) : r; - } - - @Override - @Deprecated - public Class findSerializationKeyType(Annotated am, JavaType baseType) { - Class r = _primary.findSerializationKeyType(am, baseType); - return (r == null) ? _secondary.findSerializationKeyType(am, baseType) : r; - } - @Override - @Deprecated - public Class findSerializationContentType(Annotated am, JavaType baseType) { - Class r = _primary.findSerializationContentType(am, baseType); - return (r == null) ? _secondary.findSerializationContentType(am, baseType) : r; - } - // // // Serialization: class annotations @Override @@ -622,73 +555,53 @@ public Enum findDefaultEnumValue(Class> enumCls) { return (en == null) ? _secondary.findDefaultEnumValue(enumCls) : en; } - @Override - @Deprecated // since 2.8 - public String findEnumValue(Enum value) { - String r = _primary.findEnumValue(value); - return (r == null) ? _secondary.findEnumValue(value) : r; - } - - @Override - @Deprecated // since 2.9 - public boolean hasAsValueAnnotation(AnnotatedMethod am) { - return _primary.hasAsValueAnnotation(am) || _secondary.hasAsValueAnnotation(am); - } - - @Override - @Deprecated // since 2.9 - public boolean hasAnyGetterAnnotation(AnnotatedMethod am) { - return _primary.hasAnyGetterAnnotation(am) || _secondary.hasAnyGetterAnnotation(am); - } - // // // Deserialization: general annotations @Override - public Object findDeserializer(Annotated a) { - Object r = _primary.findDeserializer(a); + public Object findDeserializer(MapperConfig config, Annotated a) { + Object r = _primary.findDeserializer(config, a); if (_isExplicitClassOrOb(r, JsonDeserializer.None.class)) { return r; } - return _explicitClassOrOb(_secondary.findDeserializer(a), + return _explicitClassOrOb(_secondary.findDeserializer(config, a), JsonDeserializer.None.class); } @Override - public Object findKeyDeserializer(Annotated a) { - Object r = _primary.findKeyDeserializer(a); + public Object findKeyDeserializer(MapperConfig config, Annotated a) { + Object r = _primary.findKeyDeserializer(config, a); if (_isExplicitClassOrOb(r, KeyDeserializer.None.class)) { return r; } - return _explicitClassOrOb(_secondary.findKeyDeserializer(a), + return _explicitClassOrOb(_secondary.findKeyDeserializer(config, a), KeyDeserializer.None.class); } @Override - public Object findContentDeserializer(Annotated am) { - Object r = _primary.findContentDeserializer(am); + public Object findContentDeserializer(MapperConfig config, Annotated am) { + Object r = _primary.findContentDeserializer(config, am); if (_isExplicitClassOrOb(r, JsonDeserializer.None.class)) { return r; } - return _explicitClassOrOb(_secondary.findContentDeserializer(am), + return _explicitClassOrOb(_secondary.findContentDeserializer(config, am), JsonDeserializer.None.class); } @Override - public Object findDeserializationConverter(Annotated a) { - Object ob = _primary.findDeserializationConverter(a); - return (ob == null) ? _secondary.findDeserializationConverter(a) : ob; + public Object findDeserializationConverter(MapperConfig config, Annotated a) { + Object ob = _primary.findDeserializationConverter(config, a); + return (ob == null) ? _secondary.findDeserializationConverter(config, a) : ob; } @Override - public Object findDeserializationContentConverter(AnnotatedMember a) { - Object ob = _primary.findDeserializationContentConverter(a); - return (ob == null) ? _secondary.findDeserializationContentConverter(a) : ob; + public Object findDeserializationContentConverter(MapperConfig config, AnnotatedMember a) { + Object ob = _primary.findDeserializationContentConverter(config, a); + return (ob == null) ? _secondary.findDeserializationContentConverter(config, a) : ob; } // // // Deserialization: type refinements - // since 2.7 @Override public JavaType refineDeserializationType(MapperConfig config, Annotated a, JavaType baseType) throws JsonMappingException @@ -696,46 +609,25 @@ public JavaType refineDeserializationType(MapperConfig config, JavaType t = _secondary.refineDeserializationType(config, a, baseType); return _primary.refineDeserializationType(config, a, t); } - - @Override - @Deprecated - public Class findDeserializationType(Annotated am, JavaType baseType) { - Class r = _primary.findDeserializationType(am, baseType); - return (r != null) ? r : _secondary.findDeserializationType(am, baseType); - } - @Override - @Deprecated - public Class findDeserializationKeyType(Annotated am, JavaType baseKeyType) { - Class result = _primary.findDeserializationKeyType(am, baseKeyType); - return (result == null) ? _secondary.findDeserializationKeyType(am, baseKeyType) : result; - } - - @Override - @Deprecated - public Class findDeserializationContentType(Annotated am, JavaType baseContentType) { - Class result = _primary.findDeserializationContentType(am, baseContentType); - return (result == null) ? _secondary.findDeserializationContentType(am, baseContentType) : result; - } - // // // Deserialization: class annotations @Override - public Object findValueInstantiator(AnnotatedClass ac) { - Object result = _primary.findValueInstantiator(ac); - return (result == null) ? _secondary.findValueInstantiator(ac) : result; + public Object findValueInstantiator(MapperConfig config, AnnotatedClass ac) { + Object result = _primary.findValueInstantiator(config, ac); + return (result == null) ? _secondary.findValueInstantiator(config, ac) : result; } @Override - public Class findPOJOBuilder(AnnotatedClass ac) { - Class result = _primary.findPOJOBuilder(ac); - return (result == null) ? _secondary.findPOJOBuilder(ac) : result; + public Class findPOJOBuilder(MapperConfig config, AnnotatedClass ac) { + Class result = _primary.findPOJOBuilder(config, ac); + return (result == null) ? _secondary.findPOJOBuilder(config, ac) : result; } @Override - public JsonPOJOBuilder.Value findPOJOBuilderConfig(AnnotatedClass ac) { - JsonPOJOBuilder.Value result = _primary.findPOJOBuilderConfig(ac); - return (result == null) ? _secondary.findPOJOBuilderConfig(ac) : result; + public JsonPOJOBuilder.Value findPOJOBuilderConfig(MapperConfig config, AnnotatedClass ac) { + JsonPOJOBuilder.Value result = _primary.findPOJOBuilderConfig(config, ac); + return (result == null) ? _secondary.findPOJOBuilderConfig(config, ac) : result; } // // // Deserialization: method annotations @@ -773,7 +665,7 @@ public JsonSetter.Value findSetterInfo(Annotated a) { ? v1 : v2.withOverrides(v1); } - @Override // since 2.9 + @Override public Boolean findMergeInfo(Annotated a) { Boolean b = _primary.findMergeInfo(a); if (b == null) { @@ -782,34 +674,12 @@ public Boolean findMergeInfo(Annotated a) { return b; } - @Override - @Deprecated // since 2.9 - public boolean hasCreatorAnnotation(Annotated a) { - return _primary.hasCreatorAnnotation(a) || _secondary.hasCreatorAnnotation(a); - } - - @Override - @Deprecated // since 2.9 - public JsonCreator.Mode findCreatorBinding(Annotated a) { - JsonCreator.Mode mode = _primary.findCreatorBinding(a); - if (mode != null) { - return mode; - } - return _secondary.findCreatorBinding(a); - } - @Override public JsonCreator.Mode findCreatorAnnotation(MapperConfig config, Annotated a) { JsonCreator.Mode mode = _primary.findCreatorAnnotation(config, a); return (mode == null) ? _secondary.findCreatorAnnotation(config, a) : mode; } - @Override - @Deprecated // since 2.9 - public boolean hasAnySetterAnnotation(AnnotatedMethod am) { - return _primary.hasAnySetterAnnotation(am) || _secondary.hasAnySetterAnnotation(am); - } - protected boolean _isExplicitClassOrOb(Object maybeCls, Class implicit) { if ((maybeCls == null) || (maybeCls == implicit)) { return false; @@ -820,7 +690,6 @@ protected boolean _isExplicitClassOrOb(Object maybeCls, Class implicit) { return true; } - // @since 2.9 protected Object _explicitClassOrOb(Object maybeCls, Class implicit) { if ((maybeCls == null) || (maybeCls == implicit)) { return null; 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 8ae39b655b..d8060a213b 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationMap.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationMap.java @@ -13,18 +13,32 @@ */ public final class AnnotationMap implements Annotations { - protected HashMap,Annotation> _annotations; + protected Map,Annotation> _annotations; + /* + /********************************************************** + /* Construction + /********************************************************** + */ + public AnnotationMap() { } + public AnnotationMap(Map,Annotation> a) { + _annotations = a; + } + public static AnnotationMap of(Class type, Annotation value) { - HashMap,Annotation> ann = new HashMap<>(4); + Map,Annotation> ann = new HashMap<>(4); ann.put(type, value); return new AnnotationMap(ann); } - AnnotationMap(HashMap,Annotation> a) { - _annotations = a; + public static AnnotationMap of(Collection rawAnnotations) { + Map,Annotation> map = new HashMap<>(rawAnnotations.size()); + for (Annotation raw : rawAnnotations) { + map.put(raw.annotationType(), raw); + } + return new AnnotationMap(map); } /* @@ -55,8 +69,6 @@ public boolean has(Class cls) /** * Helper method that can be used for a "bulk" check to see if at least * one of given annotation types is included within this map. - * - * @since 2.7 */ @Override public boolean hasOneOf(Class[] annoClasses) { @@ -75,10 +87,7 @@ public boolean hasOneOf(Class[] annoClasses) { /* Other API /********************************************************** */ - - /** - * @since 2.3 - */ + public Iterable annotations() { if (_annotations == null || _annotations.size() == 0) { return Collections.emptyList(); @@ -111,29 +120,6 @@ public int size() { return (_annotations == null) ? 0 : _annotations.size(); } - /** - * Method called to add specified annotation in the Map, but - * only if it didn't yet exist. - */ - 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. - * - * @return True if the addition changed the contents, that is, this map did not - * already have specified annotation - */ - public boolean add(Annotation ann) { - return _add(ann); - } - @Override public String toString() { if (_annotations == null) { @@ -141,18 +127,4 @@ public String toString() { } return _annotations.toString(); } - - /* - /********************************************************** - /* Helper methods - /********************************************************** - */ - - protected final boolean _add(Annotation ann) { - if (_annotations == null) { - _annotations = new HashMap,Annotation>(); - } - 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 139f73a1c7..04e3a5bbb6 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/BasicBeanDescription.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/BasicBeanDescription.java @@ -12,7 +12,6 @@ import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; import com.fasterxml.jackson.databind.cfg.HandlerInstantiator; import com.fasterxml.jackson.databind.cfg.MapperConfig; -import com.fasterxml.jackson.databind.type.TypeBindings; import com.fasterxml.jackson.databind.util.Annotations; import com.fasterxml.jackson.databind.util.ClassUtil; import com.fasterxml.jackson.databind.util.Converter; @@ -27,13 +26,12 @@ */ public class BasicBeanDescription extends BeanDescription { - // since 2.9 private final static Class[] NO_VIEWS = new Class[0]; /* - /********************************************************** + /********************************************************************** /* General configuration - /********************************************************** + /********************************************************************** */ /** @@ -48,9 +46,9 @@ public class BasicBeanDescription extends BeanDescription final protected AnnotationIntrospector _annotationIntrospector; /* - /********************************************************** + /********************************************************************** /* Information about type itself - /********************************************************** + /********************************************************************** */ /** @@ -58,20 +56,14 @@ public class BasicBeanDescription extends BeanDescription */ final protected AnnotatedClass _classInfo; - /** - * @since 2.9 - */ protected Class[] _defaultViews; - /** - * @since 2.9 - */ protected boolean _defaultViewsResolved; /* - /********************************************************** + /********************************************************************** /* Member information - /********************************************************** + /********************************************************************** */ /** @@ -85,9 +77,22 @@ public class BasicBeanDescription extends BeanDescription protected ObjectIdInfo _objectIdInfo; /* - /********************************************************** + /********************************************************************** + /* Lazily accessed results of introspection, cached for reuse + /********************************************************************** + */ + + /** + * Results of introspecting `@JsonFormat` configuration for class, if any. + * + * @since 3.0 + */ + protected transient JsonFormat.Value _classFormat; + + /* + /********************************************************************** /* Life-cycle - /********************************************************** + /********************************************************************** */ protected BasicBeanDescription(POJOPropertiesCollector coll, @@ -97,11 +102,8 @@ protected BasicBeanDescription(POJOPropertiesCollector coll, _propCollector = coll; _config = coll.getConfig(); // NOTE: null config only for some pre-constructed types - if (_config == null) { - _annotationIntrospector = null; - } else { - _annotationIntrospector = _config.getAnnotationIntrospector(); - } + _annotationIntrospector = (_config == null) ? NopAnnotationIntrospector.nopInstance() + : _config.getAnnotationIntrospector(); _classInfo = classDef; } @@ -116,15 +118,12 @@ protected BasicBeanDescription(MapperConfig config, _propCollector = null; _config = config; // NOTE: null config only for some pre-constructed types - if (_config == null) { - _annotationIntrospector = null; - } else { - _annotationIntrospector = _config.getAnnotationIntrospector(); - } + _annotationIntrospector = (_config == null) ? NopAnnotationIntrospector.nopInstance() + : _config.getAnnotationIntrospector(); _classInfo = classDef; _properties = props; } - + protected BasicBeanDescription(POJOPropertiesCollector coll) { this(coll, coll.getType(), coll.getClassDef()); @@ -167,17 +166,15 @@ protected List _properties() { } /* - /********************************************************** + /********************************************************************** /* Limited modifications by core databind functionality - /********************************************************** + /********************************************************************** */ /** * Method that can be used to prune unwanted properties, during * construction of serializers and deserializers. * Use with utmost care, if at all... - * - * @since 2.1 */ public boolean removeProperty(String propName) { @@ -201,17 +198,11 @@ public boolean addProperty(BeanPropertyDefinition def) _properties().add(def); return true; } - - /** - * @since 2.6 - */ + public boolean hasProperty(PropertyName name) { return findProperty(name) != null; } - - /** - * @since 2.6 - */ + public BeanPropertyDefinition findProperty(PropertyName name) { for (BeanPropertyDefinition prop : _properties()) { @@ -221,11 +212,11 @@ public BeanPropertyDefinition findProperty(PropertyName name) } return null; } - + /* - /********************************************************** + /********************************************************************** /* Simple accessors from BeanDescription - /********************************************************** + /********************************************************************** */ @Override @@ -239,13 +230,6 @@ public List findProperties() { return _properties(); } - @Override - @Deprecated // since 2.9 - public AnnotatedMethod findJsonValueMethod() { - return (_propCollector == null) ? null - : _propCollector.getJsonValueMethod(); - } - @Override // since 2.9 public AnnotatedMember findJsonValueAccessor() { return (_propCollector == null) ? null @@ -272,21 +256,6 @@ public Annotations getClassAnnotations() { return _classInfo.getAnnotations(); } - @Override - @Deprecated // since 2.7 - public TypeBindings bindingsForBeanType() { - return _type.getBindings(); - } - - @Override - @Deprecated // since 2.8 - public JavaType resolveType(java.lang.reflect.Type jdkType) { - if (jdkType == null) { - return null; - } - return _config.getTypeFactory().constructType(jdkType, _type.getBindings()); - } - @Override public AnnotatedConstructor findDefaultConstructor() { return _classInfo.getDefaultConstructor(); @@ -359,16 +328,15 @@ public Object instantiateBean(boolean fixAccess) { } ClassUtil.throwIfError(t); ClassUtil.throwIfRTE(t); - throw new IllegalArgumentException("Failed to instantiate bean of type " - +_classInfo.getAnnotated().getName()+": ("+t.getClass().getName()+") " - +ClassUtil.exceptionMessage(t), t); + throw new IllegalArgumentException("Failed to instantiate bean of type "+ClassUtil.nameOf(_classInfo.getAnnotated()) + +": ("+t.getClass().getName()+") "+ClassUtil.exceptionMessage(t), t); } } /* - /********************************************************** + /********************************************************************** /* Simple accessors, extended - /********************************************************** + /********************************************************************** */ @Override @@ -377,44 +345,55 @@ public AnnotatedMethod findMethod(String name, Class[] paramTypes) { } /* - /********************************************************** + /********************************************************************** /* General per-class annotation introspection - /********************************************************** + /********************************************************************** */ + @Deprecated // since 3.0 @Override - public JsonFormat.Value findExpectedFormat(JsonFormat.Value defValue) + public JsonFormat.Value findExpectedFormat() { - // 15-Apr-2016, tatu: Let's check both per-type defaults and annotations; per-type - // defaults having higher precedence, so start with that - if (_annotationIntrospector != null) { - JsonFormat.Value v = _annotationIntrospector.findFormat(_classInfo); - if (v != null) { - if (defValue == null) { - defValue = v; - } else { - defValue = defValue.withOverrides(v); - } + JsonFormat.Value v = _classFormat; + if (v == null) { + // 18-Apr-2018, tatu: Bit unclean but apparently `_config` is `null` for + // a small set of pre-discovered simple types that `BasicClassIntrospector` + // may expose. If so, nothing we can do + v = (_config == null) ? null + : _annotationIntrospector.findFormat(_classInfo); + if (v == null) { + v = JsonFormat.Value.empty(); } + _classFormat = v; } - JsonFormat.Value v = _config.getDefaultPropertyFormat(_classInfo.getRawType()); - if (v != null) { - if (defValue == null) { - defValue = v; - } else { - defValue = defValue.withOverrides(v); + return v; + } + + @Override + public JsonFormat.Value findExpectedFormat(Class baseType) + { + JsonFormat.Value v0 = _classFormat; + if (v0 == null) { // copied from above + v0 = (_config == null) ? null + : _annotationIntrospector.findFormat(_classInfo); + if (v0 == null) { + v0 = JsonFormat.Value.empty(); } + _classFormat = v0; } - return defValue; + JsonFormat.Value v1 = _config.getDefaultPropertyFormat(baseType); + if (v1 == null) { + return v0; + } + return JsonFormat.Value.merge(v0, v1); } - @Override // since 2.9 + @Override public Class[] findDefaultViews() { if (!_defaultViewsResolved) { _defaultViewsResolved = true; - Class[] def = (_annotationIntrospector == null) ? null - : _annotationIntrospector.findViews(_classInfo); + Class[] def = _annotationIntrospector.findViews(_classInfo); // one more twist: if default inclusion disabled, need to force empty set of views if (def == null) { if (!_config.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION)) { @@ -427,18 +406,15 @@ public Class[] findDefaultViews() } /* - /********************************************************** + /********************************************************************** /* Introspection for serialization - /********************************************************** + /********************************************************************** */ @Override public Converter findSerializationConverter() { - if (_annotationIntrospector == null) { - return null; - } - return _createConverter(_annotationIntrospector.findSerializationConverter(_classInfo)); + return _createConverter(_annotationIntrospector.findSerializationConverter(_config, _classInfo)); } /** @@ -449,11 +425,9 @@ public Converter findSerializationConverter() */ @Override public JsonInclude.Value findPropertyInclusion(JsonInclude.Value defValue) { - if (_annotationIntrospector != null) { - JsonInclude.Value incl = _annotationIntrospector.findPropertyInclusion(_classInfo); - if (incl != null) { - return (defValue == null) ? incl : defValue.withOverrides(incl); - } + JsonInclude.Value incl = _annotationIntrospector.findPropertyInclusion(_config, _classInfo); + if (incl != null) { + return (defValue == null) ? incl : defValue.withOverrides(incl); } return defValue; } @@ -506,25 +480,10 @@ public List findBackReferences() return result; } - @Deprecated // since 2.9 - @Override - public Map findBackReferenceProperties() - { - List props = findBackReferences(); - if (props == null) { - return null; - } - Map result = new HashMap<>(); - for (BeanPropertyDefinition prop : props) { - result.put(prop.getName(), prop.getMutator()); - } - return result; - } - /* - /********************************************************** + /********************************************************************** /* Introspection for deserialization, factories - /********************************************************** + /********************************************************************** */ @Override @@ -625,98 +584,39 @@ protected boolean isFactoryMethod(AnnotatedMethod am) return false; } - /** - * @deprecated since 2.8 - */ - @Deprecated // since 2.8, not used at least since 2.7 - protected PropertyName _findCreatorPropertyName(AnnotatedParameter param) - { - PropertyName name = _annotationIntrospector.findNameForDeserialization(param); - if (name == null || name.isEmpty()) { - String str = _annotationIntrospector.findImplicitPropertyName(param); - if (str != null && !str.isEmpty()) { - name = PropertyName.construct(str); - } - } - return name; - } - /* - /********************************************************** + /********************************************************************** /* Introspection for deserialization, other - /********************************************************** + /********************************************************************** */ @Override public Class findPOJOBuilder() { - return (_annotationIntrospector == null) ? - null : _annotationIntrospector.findPOJOBuilder(_classInfo); + return _annotationIntrospector.findPOJOBuilder(_config, _classInfo); } @Override public JsonPOJOBuilder.Value findPOJOBuilderConfig() { - return (_annotationIntrospector == null) ? - null : _annotationIntrospector.findPOJOBuilderConfig(_classInfo); + return _annotationIntrospector.findPOJOBuilderConfig(_config, _classInfo); } @Override public Converter findDeserializationConverter() { - if (_annotationIntrospector == null) { - return null; - } - return _createConverter(_annotationIntrospector.findDeserializationConverter(_classInfo)); + return _createConverter(_annotationIntrospector + .findDeserializationConverter(_config, _classInfo)); } @Override public String findClassDescription() { - return (_annotationIntrospector == null) ? - null : _annotationIntrospector.findClassDescription(_classInfo); - } - - /* - /********************************************************** - /* Helper methods for field introspection - /********************************************************** - */ - - /** - * @param ignoredProperties (optional) names of properties to ignore; - * any fields that would be recognized as one of these properties - * is ignored. - * @param forSerialization If true, will collect serializable property - * fields; if false, deserializable - * - * @return Ordered Map with logical property name as key, and - * matching field as value. - * - * @deprecated Since 2.7.2, does not seem to be used? - */ - @Deprecated - public LinkedHashMap _findPropertyFields( - Collection ignoredProperties, boolean forSerialization) - { - LinkedHashMap results = new LinkedHashMap(); - for (BeanPropertyDefinition property : _properties()) { - AnnotatedField f = property.getField(); - if (f != null) { - String name = property.getName(); - if (ignoredProperties != null) { - if (ignoredProperties.contains(name)) { - continue; - } - } - results.put(name, f); - } - } - return results; + return _annotationIntrospector.findClassDescription(_classInfo); } /* - /********************************************************** + /********************************************************************** /* Helper methods, other - /********************************************************** + /********************************************************************** */ @SuppressWarnings("unchecked") 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 f511efb90d..d78029da3f 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/BasicClassIntrospector.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/BasicClassIntrospector.java @@ -11,21 +11,19 @@ import com.fasterxml.jackson.databind.cfg.MapperConfig; import com.fasterxml.jackson.databind.type.SimpleType; import com.fasterxml.jackson.databind.util.ClassUtil; -import com.fasterxml.jackson.databind.util.LRUMap; +import com.fasterxml.jackson.databind.util.SimpleLookupCache; public class BasicClassIntrospector extends ClassIntrospector implements java.io.Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 3L; /* We keep a small set of pre-constructed descriptions to use for * common non-structured values, such as Numbers and Strings. * 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; static { @@ -49,33 +47,38 @@ public class BasicClassIntrospector } /* - /********************************************************** + /********************************************************************** /* Life cycle - /********************************************************** + /********************************************************************** */ + // Looks like 'forClassAnnotations()' gets called so frequently that we + // should consider caching to avoid some of the lookups. + /** - * Looks like 'forClassAnnotations()' gets called so frequently that we - * should consider caching to avoid some of the lookups. - * - * @since 2.5 + * Transient cache: note that we do NOT have to add `readResolve()` for JDK serialization + * because {@link #forMapper(Object)} initializes it properly, when mapper get + * constructed. */ - protected final LRUMap _cachedFCA; + protected final transient SimpleLookupCache _cachedFCA; public BasicClassIntrospector() { - // a small cache should go a long way here - _cachedFCA = new LRUMap(16, 64); + this(null); + } + + protected BasicClassIntrospector(SimpleLookupCache cache) { + _cachedFCA = cache; } @Override - public ClassIntrospector copy() { - return new BasicClassIntrospector(); + public ClassIntrospector forMapper(Object mapper) { + return new BasicClassIntrospector(new SimpleLookupCache(16, 64)); } /* - /********************************************************** + /********************************************************************** /* Factory method impls - /********************************************************** + /********************************************************************** */ @Override @@ -179,9 +182,9 @@ public BasicBeanDescription forDirectClassAnnotations(MapperConfig config, } /* - /********************************************************** + /********************************************************************** /* Overridable helper methods - /********************************************************** + /********************************************************************** */ protected POJOPropertiesCollector collectProperties(MapperConfig config, @@ -198,7 +201,8 @@ protected POJOPropertiesCollector collectPropertiesWithBuilder(MapperConfig c { AnnotatedClass ac = _resolveAnnotatedClass(config, type, r); AnnotationIntrospector ai = config.isAnnotationProcessingEnabled() ? config.getAnnotationIntrospector() : null; - JsonPOJOBuilder.Value builderConfig = (ai == null) ? null : ai.findPOJOBuilderConfig(ac); + JsonPOJOBuilder.Value builderConfig = (ai == null) ? null + : ai.findPOJOBuilderConfig(config, ac); String mutatorPrefix = (builderConfig == null) ? JsonPOJOBuilder.DEFAULT_WITH_PREFIX : builderConfig.withPrefix; return constructPropertyCollector(config, ac, type, forSerialization, mutatorPrefix); } @@ -253,9 +257,8 @@ protected boolean _isStdJDKCollection(JavaType type) if (pkgName != null) { 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. - */ + // 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; @@ -274,17 +277,11 @@ protected BasicBeanDescription _findStdJdkCollectionDesc(MapperConfig cfg, Ja return null; } - /** - * @since 2.9 - */ protected AnnotatedClass _resolveAnnotatedClass(MapperConfig config, JavaType type, MixInResolver r) { return AnnotatedClassResolver.resolve(config, type, r); } - /** - * @since 2.9 - */ protected AnnotatedClass _resolveAnnotatedWithoutSuperTypes(MapperConfig config, JavaType type, MixInResolver r) { return AnnotatedClassResolver.resolveWithoutSuperTypes(config, type, r); 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 caa2663c69..32aac360ad 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/BeanPropertyDefinition.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/BeanPropertyDefinition.java @@ -6,7 +6,7 @@ import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.util.ClassUtil; -import com.fasterxml.jackson.databind.util.Named; +import com.fasterxml.jackson.databind.util.FullyNamed; /** * Simple value classes that contain definitions of properties, @@ -18,7 +18,7 @@ * {@link BeanProperty} instances. */ public abstract class BeanPropertyDefinition - implements Named + implements FullyNamed { protected final static JsonInclude.Value EMPTY_INCLUDE = JsonInclude.Value.empty(); @@ -48,24 +48,19 @@ public abstract class BeanPropertyDefinition /* /********************************************************** - /* Property name information + /* Property name information, `FullyNamed` /********************************************************** */ - /** - * Accessor for name used for external representation (in JSON). - */ - @Override // from Named - public abstract String getName(); - - public abstract PropertyName getFullName(); +// public abstract String getName(); +// public abstract PropertyName getFullName(); +// public boolean hasName(PropertyName name); - /** - * @since 2.6 + /* + /********************************************************** + /* Property name information, other + /********************************************************** */ - public boolean hasName(PropertyName name) { - return getFullName().equals(name); - } /** * Accessor that can be used to determine implicit name from underlying @@ -77,8 +72,6 @@ public boolean hasName(PropertyName name) { /** * Accessor for finding wrapper name to use for property (if any). - * - * @since 2.2 */ public abstract PropertyName getWrapperName(); diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/ClassIntrospector.java b/src/main/java/com/fasterxml/jackson/databind/introspect/ClassIntrospector.java index 6a5dbbf9cd..33553028d1 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/ClassIntrospector.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/ClassIntrospector.java @@ -15,49 +15,24 @@ */ public abstract class ClassIntrospector { - /* - /********************************************************** - /* Helper interfaces - /********************************************************** - */ - - /** - * Interface used for decoupling details of how mix-in annotation - * definitions are accessed (via this interface), and how - * they are stored (defined by classes that implement the interface) - */ - public interface MixInResolver - { - /** - * Method that will check if there are "mix-in" classes (with mix-in - * annotations) for given class - */ - public Class findMixInClassFor(Class cls); - - /** - * Method called to create a new, non-shared copy, to be used by different - * ObjectMapper instance, and one that should not be connected - * to this instance, if resolver has mutable state. - * If resolver is immutable may simply return `this`. - * - * @since 2.6 - */ - public MixInResolver copy(); - } - protected ClassIntrospector() { } /** - * Method that may be needed when `copy()`ing `ObjectMapper` instances. + * Method called to create an instance to be exclusive used by specified + * mapper. Needed to ensure that no sharing through cache occurs. + * + * @param mapper "owner" of this instance: always of type + * {@link com.fasterxml.jackson.databind.ObjectMapper}, but not fully + * typed to avoid compile dependency * - * @since 2.9.6 + * @since 3.0 */ - public abstract ClassIntrospector copy(); + public abstract ClassIntrospector forMapper(Object mapper); /* - /********************************************************** + /********************************************************************** /* Public API: factory methods - /********************************************************** + /********************************************************************** */ /** diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/CollectorBase.java b/src/main/java/com/fasterxml/jackson/databind/introspect/CollectorBase.java index e52dcb4538..52b0e8c835 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/CollectorBase.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/CollectorBase.java @@ -7,7 +7,6 @@ import com.fasterxml.jackson.databind.AnnotationIntrospector; import com.fasterxml.jackson.databind.util.ClassUtil; -// @since 2.9 class CollectorBase { protected final static AnnotationMap[] NO_ANNOTATION_MAPS = new AnnotationMap[0]; diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/ConcreteBeanPropertyBase.java b/src/main/java/com/fasterxml/jackson/databind/introspect/ConcreteBeanPropertyBase.java index 7d2bc48920..9f7a2f3035 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/ConcreteBeanPropertyBase.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/ConcreteBeanPropertyBase.java @@ -14,8 +14,6 @@ /** * Intermediate {@link BeanProperty} class shared by concrete readable- and * writable property implementations for sharing common functionality. - * - * @since 2.7 */ public abstract class ConcreteBeanPropertyBase implements BeanProperty, java.io.Serializable @@ -24,21 +22,14 @@ public abstract class ConcreteBeanPropertyBase /** * Additional information about property - * - * @since 2.3 */ protected final PropertyMetadata _metadata; /** * Lazily accessed value for per-property format override definition. - * - * @since 2.8 */ protected transient JsonFormat.Value _propertyFormat; - /** - * @since 2.9 - */ protected transient List _aliases; protected ConcreteBeanPropertyBase(PropertyMetadata md) { @@ -60,19 +51,15 @@ protected ConcreteBeanPropertyBase(ConcreteBeanPropertyBase src) { public boolean isVirtual() { return false; } @Override - @Deprecated - public final JsonFormat.Value findFormatOverrides(AnnotationIntrospector intr) { - JsonFormat.Value f = null; + public JsonFormat.Value findFormatOverrides(MapperConfig config) { + AnnotationIntrospector intr = config.getAnnotationIntrospector(); if (intr != null) { AnnotatedMember member = getMember(); if (member != null) { - f = intr.findFormat(member); + return intr.findFormat(member); } } - if (f == null) { - f = EMPTY_FORMAT; - } - return f; + return null; } @Override @@ -83,14 +70,7 @@ public JsonFormat.Value findPropertyFormat(MapperConfig config, Class base JsonFormat.Value v = _propertyFormat; if (v == null) { JsonFormat.Value v1 = config.getDefaultPropertyFormat(baseType); - JsonFormat.Value v2 = null; - AnnotationIntrospector intr = config.getAnnotationIntrospector(); - if (intr != null) { - AnnotatedMember member = getMember(); - if (member != null) { - v2 = intr.findFormat(member); - } - } + JsonFormat.Value v2 = findFormatOverrides(config); if (v1 == null) { v = (v2 == null) ? EMPTY_FORMAT : v2; } else { @@ -114,7 +94,7 @@ public JsonInclude.Value findPropertyInclusion(MapperConfig config, Class if (intr == null) { return v0; } - JsonInclude.Value v = intr.findPropertyInclusion(member); + JsonInclude.Value v = intr.findPropertyInclusion(config, member); if (v0 == null) { return v; } 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 823deb622f..658aa58f2d 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java @@ -1,20 +1,22 @@ package com.fasterxml.jackson.databind.introspect; +import java.beans.ConstructorProperties; +import java.beans.Transient; import java.lang.annotation.Annotation; import java.lang.reflect.Field; +import java.lang.reflect.MalformedParametersException; +import java.lang.reflect.Parameter; import java.util.*; import com.fasterxml.jackson.annotation.*; + import com.fasterxml.jackson.core.Version; + import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.*; import com.fasterxml.jackson.databind.cfg.HandlerInstantiator; import com.fasterxml.jackson.databind.cfg.MapperConfig; -import com.fasterxml.jackson.databind.ext.Java7Support; import com.fasterxml.jackson.databind.jsontype.NamedType; -import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; -import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; -import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder; import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; import com.fasterxml.jackson.databind.ser.VirtualBeanPropertyWriter; import com.fasterxml.jackson.databind.ser.impl.AttributePropertyWriter; @@ -59,32 +61,19 @@ public class JacksonAnnotationIntrospector JsonMerge.class // since 2.9 }; - // NOTE: loading of Java7 dependencies is encapsulated by handlers in Java7Support, - // here we do not really need any handling; but for extra-safety use try-catch - private static final Java7Support _java7Helper; - static { - Java7Support x = null; - try { - x = Java7Support.instance(); - } catch (Throwable t) { } - _java7Helper = x; - } - /** * Since introspection of annotation types is a performance issue in some * use cases (rare, but do exist), let's try a simple cache to reduce * need for actual meta-annotation introspection. *

    * Non-final only because it needs to be re-created after deserialization. - * - * @since 2.7 */ - protected transient LRUMap,Boolean> _annotationsInside = new LRUMap,Boolean>(48, 48); + protected transient SimpleLookupCache,Boolean> _annotationsInside = new SimpleLookupCache,Boolean>(48, 96); /* - /********************************************************** + /********************************************************************** /* Local configuration settings - /********************************************************** + /********************************************************************** */ /** @@ -92,15 +81,13 @@ public class JacksonAnnotationIntrospector * explanation. *

    * Defaults to true. - * - * @since 2.7.4 */ protected boolean _cfgConstructorPropertiesImpliesCreator = true; /* - /********************************************************** + /********************************************************************** /* Life-cycle - /********************************************************** + /********************************************************************** */ public JacksonAnnotationIntrospector() { } @@ -112,15 +99,15 @@ public Version version() { protected Object readResolve() { if (_annotationsInside == null) { - _annotationsInside = new LRUMap,Boolean>(48, 48); + _annotationsInside = new SimpleLookupCache,Boolean>(48, 48); } return this; } /* - /********************************************************** + /********************************************************************** /* Configuration - /********************************************************** + /********************************************************************** */ /** @@ -130,8 +117,6 @@ protected Object readResolve() { * without explicit use of JsonCreator annotation. *

    * Default setting is `true` - * - * @since 2.7.4 */ public JacksonAnnotationIntrospector setConstructorPropertiesImpliesCreator(boolean b) { @@ -140,9 +125,9 @@ public JacksonAnnotationIntrospector setConstructorPropertiesImpliesCreator(bool } /* - /********************************************************** + /********************************************************************** /* General annotation properties - /********************************************************** + /********************************************************************** */ /** @@ -165,42 +150,11 @@ public boolean isAnnotationBundle(Annotation ann) { } /* - /********************************************************** + /********************************************************************** /* General annotations - /********************************************************** + /********************************************************************** */ - /** - * Since 2.6, we have supported use of {@link JsonProperty} for specifying - * explicit serialized name - */ - @Override - @Deprecated // since 2.8 - public String findEnumValue(Enum value) - { - // 11-Jun-2015, tatu: As per [databind#677], need to allow explicit naming. - // Unfortunately cannot quite use standard AnnotatedClass here (due to various - // reasons, including odd representation JVM uses); has to do for now - try { - // We know that values are actually static fields with matching name so: - Field f = value.getClass().getField(value.name()); - if (f != null) { - JsonProperty prop = f.getAnnotation(JsonProperty.class); - if (prop != null) { - String n = prop.value(); - if (n != null && !n.isEmpty()) { - return n; - } - } - } - } catch (SecurityException e) { - // 17-Sep-2015, tatu: Anything we could/should do here? - } catch (NoSuchFieldException e) { - // 17-Sep-2015, tatu: should not really happen. But... can we do anything? - } - return value.name(); - } - @Override // since 2.7 public String[] findEnumValues(Class enumType, Enum[] enumValues, String[] names) { HashMap expl = null; @@ -249,9 +203,9 @@ public Enum findDefaultEnumValue(Class> enumCls) { } /* - /********************************************************** + /********************************************************************** /* General class annotations - /********************************************************** + /********************************************************************** */ @Override @@ -311,29 +265,76 @@ public String findClassDescription(AnnotatedClass ac) { } /* - /********************************************************** + /********************************************************************** /* Property auto-detection - /********************************************************** + /********************************************************************** */ @Override - public VisibilityChecker findAutoDetectVisibility(AnnotatedClass ac, - VisibilityChecker checker) + public VisibilityChecker findAutoDetectVisibility(MapperConfig config, + AnnotatedClass ac, VisibilityChecker checker) { JsonAutoDetect ann = _findAnnotation(ac, JsonAutoDetect.class); - return (ann == null) ? checker : checker.with(ann); + if (ann == null) { + return checker; + } + return checker.withOverrides(JsonAutoDetect.Value.from(ann)); } /* - /********************************************************** + /********************************************************************** /* General member (field, method/constructor) annotations - /********************************************************** + /********************************************************************** */ @Override - public String findImplicitPropertyName(AnnotatedMember m) { - PropertyName n = _findConstructorName(m); - return (n == null) ? null : n.getSimpleName(); + public String findImplicitPropertyName(AnnotatedMember m) + { + // Always get name for fields so why not + if (m instanceof AnnotatedField) { + return m.getName(); + } + if (m instanceof AnnotatedParameter) { + AnnotatedParameter p = (AnnotatedParameter) m; + AnnotatedWithParams owner = p.getOwner(); + if (owner instanceof AnnotatedConstructor) { + // 17-Sep-2017, tatu: Two possibilities; either `@ConstructorProperties` (JDK6) + ConstructorProperties props = owner.getAnnotation(ConstructorProperties.class); + if (props != null) { + String[] names = props.value(); + int ix = p.getIndex(); + if (ix < names.length) { + return names[ix]; + } + } + // ... or parameter names from bytecode (JDK8) + return _findImplicitName(owner, p.getIndex()); + } + if (owner instanceof AnnotatedMethod) { + // For now let's only bother discovering names for static methods as they + // (only) may be creators + if (owner.isStatic()) { + return _findImplicitName(owner, p.getIndex()); + } + } + } + return null; + } + + protected String _findImplicitName(AnnotatedWithParams m, int index) + { + try { + Parameter[] params = m.getNativeParameters(); + Parameter p = params[index]; + if (p.isNamePresent()) { + return p.getName(); + } + } catch (MalformedParametersException e) { + // 17-Sep-2017, tatu: I don't usually add defensive handling like this without + // having clear examples of problems, but this seems like something that + // can still crop up unexpectedly and be a PITA so... + } + return null; } @Override @@ -388,10 +389,10 @@ public String findPropertyDescription(Annotated ann) { 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); - } + int ix = prop.index(); + if (ix != JsonProperty.INDEX_UNKNOWN) { + return Integer.valueOf(ix); + } } return null; } @@ -467,13 +468,6 @@ public JacksonInject.Value findInjectableValue(AnnotatedMember m) { return v; } - @Override - @Deprecated // since 2.9 - public Object findInjectableValueId(AnnotatedMember m) { - JacksonInject.Value v = findInjectableValue(m); - return (v == null) ? null : v.getId(); - } - @Override public Class[] findViews(Annotated a) { @@ -481,7 +475,7 @@ public Class[] findViews(Annotated a) return (ann == null) ? null : ann.value(); } - @Override // since 2.7 + @Override public AnnotatedMethod resolveSetterConflict(MapperConfig config, AnnotatedMethod setter1, AnnotatedMethod setter2) { @@ -510,48 +504,63 @@ public AnnotatedMethod resolveSetterConflict(MapperConfig config, } /* - /********************************************************** + /********************************************************************** /* Annotations for Polymorphic Type handling - /********************************************************** + /********************************************************************** */ @Override - public TypeResolverBuilder findTypeResolver(MapperConfig config, - AnnotatedClass ac, JavaType baseType) + public JsonTypeInfo.Value findPolymorphicTypeInfo(MapperConfig config, + Annotated ann) { - return _findTypeResolver(config, ac, baseType); + JsonTypeInfo t = _findAnnotation(ann, JsonTypeInfo.class); + return (t == null) ? null : JsonTypeInfo.Value.from(t); + } + + @Override + public Object findTypeResolverBuilder(MapperConfig config, + Annotated ann) { + JsonTypeResolver a = _findAnnotation(ann, JsonTypeResolver.class); + return (a == null) ? a : a.value(); } + @Override + public Object findTypeIdResolver(MapperConfig config, Annotated ann) { + JsonTypeIdResolver a = _findAnnotation(ann, JsonTypeIdResolver.class); + return (a == null) ? a : a.value(); + } + + /* @Override public TypeResolverBuilder findPropertyTypeResolver(MapperConfig config, - AnnotatedMember am, JavaType baseType) + Annotated ann, JavaType baseType, JsonTypeInfo.Value typeInfo) { - /* As per definition of @JsonTypeInfo, should only apply to contents of container - * (collection, map) types, not container types themselves: - */ - // 17-Apr-2016, tatu: For 2.7.4 make sure ReferenceType also included + // As per definition of @JsonTypeInfo, should only apply to contents of container + // (collection, map) types, not container types themselves: if (baseType.isContainerType() || baseType.isReferenceType()) { return null; } // No per-member type overrides (yet) - return _findTypeResolver(config, am, baseType); + return _findTypeResolver(config, ann, baseType, typeInfo); } + */ + /* @Override public TypeResolverBuilder findPropertyContentTypeResolver(MapperConfig config, - AnnotatedMember am, JavaType containerType) + Annotated ann, JavaType containerType, JsonTypeInfo.Value typeInfo) { - /* First: let's ensure property is a container type: caller should have - * verified but just to be sure - */ + // First: let's ensure property is a container type: caller should have + // verified but just to be sure if (containerType.getContentType() == null) { throw new IllegalArgumentException("Must call method with a container or reference type (got "+containerType+")"); } - return _findTypeResolver(config, am, containerType); + return _findTypeResolver(config, ann, containerType, typeInfo); } - + */ + @Override - public List findSubtypes(Annotated a) + public List findSubtypes(MapperConfig config, Annotated a) { JsonSubTypes t = _findAnnotation(a, JsonSubTypes.class); if (t == null) return null; @@ -564,14 +573,14 @@ public List findSubtypes(Annotated a) } @Override - public String findTypeName(AnnotatedClass ac) + public String findTypeName(MapperConfig config, AnnotatedClass ac) { JsonTypeName tn = _findAnnotation(ac, JsonTypeName.class); return (tn == null) ? null : tn.value(); } @Override - public Boolean isTypeId(AnnotatedMember member) { + public Boolean isTypeId(MapperConfig config, AnnotatedMember member) { return _hasAnnotation(member, JsonTypeId.class); } @@ -582,7 +591,7 @@ public Boolean isTypeId(AnnotatedMember member) { */ @Override - public ObjectIdInfo findObjectIdInfo(Annotated ann) { + public ObjectIdInfo findObjectIdInfo(MapperConfig config, Annotated ann) { JsonIdentityInfo info = _findAnnotation(ann, JsonIdentityInfo.class); if (info == null || info.generator() == ObjectIdGenerators.None.class) { return null; @@ -593,7 +602,8 @@ public ObjectIdInfo findObjectIdInfo(Annotated ann) { } @Override - public ObjectIdInfo findObjectReferenceInfo(Annotated ann, ObjectIdInfo objectIdInfo) { + public ObjectIdInfo findObjectReferenceInfo(MapperConfig config, + Annotated ann, ObjectIdInfo objectIdInfo) { JsonIdentityReference ref = _findAnnotation(ann, JsonIdentityReference.class); if (ref == null) { return objectIdInfo; @@ -611,7 +621,7 @@ public ObjectIdInfo findObjectReferenceInfo(Annotated ann, ObjectIdInfo objectId */ @Override - public Object findSerializer(Annotated a) + public Object findSerializer(MapperConfig config, Annotated a) { JsonSerialize ann = _findAnnotation(a, JsonSerialize.class); if (ann != null) { @@ -636,7 +646,7 @@ public Object findSerializer(Annotated a) } @Override - public Object findKeySerializer(Annotated a) + public Object findKeySerializer(MapperConfig config, Annotated a) { JsonSerialize ann = _findAnnotation(a, JsonSerialize.class); if (ann != null) { @@ -650,7 +660,7 @@ public Object findKeySerializer(Annotated a) } @Override - public Object findContentSerializer(Annotated a) + public Object findContentSerializer(MapperConfig config, Annotated a) { JsonSerialize ann = _findAnnotation(a, JsonSerialize.class); if (ann != null) { @@ -664,7 +674,7 @@ public Object findContentSerializer(Annotated a) } @Override - public Object findNullSerializer(Annotated a) + public Object findNullSerializer(MapperConfig config, Annotated a) { JsonSerialize ann = _findAnnotation(a, JsonSerialize.class); if (ann != null) { @@ -678,53 +688,28 @@ public Object findNullSerializer(Annotated a) } @Override - public JsonInclude.Value findPropertyInclusion(Annotated a) + public JsonInclude.Value findPropertyInclusion(MapperConfig config, Annotated a) { JsonInclude inc = _findAnnotation(a, JsonInclude.class); JsonInclude.Value value = (inc == null) ? JsonInclude.Value.empty() : JsonInclude.Value.from(inc); - - // only consider deprecated variant if we didn't have non-deprecated one: - if (value.getValueInclusion() == JsonInclude.Include.USE_DEFAULTS) { - value = _refinePropertyInclusion(a, value); - } - return value; - } - - @SuppressWarnings("deprecation") - private JsonInclude.Value _refinePropertyInclusion(Annotated a, JsonInclude.Value value) { - JsonSerialize ann = _findAnnotation(a, JsonSerialize.class); - if (ann != null) { - switch (ann.include()) { - case ALWAYS: - return value.withValueInclusion(JsonInclude.Include.ALWAYS); - case NON_NULL: - return value.withValueInclusion(JsonInclude.Include.NON_NULL); - case NON_DEFAULT: - return value.withValueInclusion(JsonInclude.Include.NON_DEFAULT); - case NON_EMPTY: - return value.withValueInclusion(JsonInclude.Include.NON_EMPTY); - case DEFAULT_INCLUSION: - default: - } - } return value; } @Override - public JsonSerialize.Typing findSerializationTyping(Annotated a) + public JsonSerialize.Typing findSerializationTyping(MapperConfig config, Annotated a) { JsonSerialize ann = _findAnnotation(a, JsonSerialize.class); return (ann == null) ? null : ann.typing(); } @Override - public Object findSerializationConverter(Annotated a) { + public Object findSerializationConverter(MapperConfig config, Annotated a) { JsonSerialize ann = _findAnnotation(a, JsonSerialize.class); return (ann == null) ? null : _classIfExplicit(ann.converter(), Converter.None.class); } @Override - public Object findSerializationContentConverter(AnnotatedMember a) { + public Object findSerializationContentConverter(MapperConfig config, AnnotatedMember a) { JsonSerialize ann = _findAnnotation(a, JsonSerialize.class); return (ann == null) ? null : _classIfExplicit(ann.contentConverter(), Converter.None.class); } @@ -734,7 +719,7 @@ public Object findSerializationContentConverter(AnnotatedMember a) { /* Serialization: type refinements /********************************************************** */ - + @Override public JavaType refineSerializationType(final MapperConfig config, final Annotated a, final JavaType baseType) throws JsonMappingException @@ -853,24 +838,6 @@ public JavaType refineSerializationType(final MapperConfig config, return type; } - @Override - @Deprecated // since 2.7 - public Class findSerializationType(Annotated am) { - return null; - } - - @Override - @Deprecated // since 2.7 - public Class findSerializationKeyType(Annotated am, JavaType baseType) { - return null; - } - - @Override - @Deprecated // since 2.7 - public Class findSerializationContentType(Annotated am, JavaType baseType) { - return null; - } - /* /********************************************************** /* Serialization: class annotations @@ -1017,7 +984,7 @@ public PropertyName findNameForSerialization(Annotated a) return null; } - @Override // since 2.9 + @Override public Boolean hasAsValue(Annotated a) { JsonValue ann = _findAnnotation(a, JsonValue.class); if (ann == null) { @@ -1026,7 +993,7 @@ public Boolean hasAsValue(Annotated a) { return ann.value(); } - @Override // since 2.9 + @Override public Boolean hasAnyGetter(Annotated a) { JsonAnyGetter ann = _findAnnotation(a, JsonAnyGetter.class); if (ann == null) { @@ -1035,21 +1002,6 @@ public Boolean hasAnyGetter(Annotated a) { return ann.enabled(); } - @Override - @Deprecated // since 2.9 - public boolean hasAnyGetterAnnotation(AnnotatedMethod am) { - // No dedicated disabling; regular @JsonIgnore used if needs to be ignored (handled separately) - return _hasAnnotation(am, JsonAnyGetter.class); - } - - @Override - @Deprecated // since 2.9 - public boolean hasAsValueAnnotation(AnnotatedMethod am) { - JsonValue ann = _findAnnotation(am, JsonValue.class); - // value of 'false' means disabled... - return (ann != null) && ann.value(); - } - /* /********************************************************** /* Deserialization: general annotations @@ -1057,7 +1009,7 @@ public boolean hasAsValueAnnotation(AnnotatedMethod am) { */ @Override - public Object findDeserializer(Annotated a) + public Object findDeserializer(MapperConfig config, Annotated a) { JsonDeserialize ann = _findAnnotation(a, JsonDeserialize.class); if (ann != null) { @@ -1071,7 +1023,7 @@ public Object findDeserializer(Annotated a) } @Override - public Object findKeyDeserializer(Annotated a) + public Object findKeyDeserializer(MapperConfig config, Annotated a) { JsonDeserialize ann = _findAnnotation(a, JsonDeserialize.class); if (ann != null) { @@ -1084,7 +1036,7 @@ public Object findKeyDeserializer(Annotated a) } @Override - public Object findContentDeserializer(Annotated a) + public Object findContentDeserializer(MapperConfig config, Annotated a) { JsonDeserialize ann = _findAnnotation(a, JsonDeserialize.class); if (ann != null) { @@ -1098,14 +1050,14 @@ public Object findContentDeserializer(Annotated a) } @Override - public Object findDeserializationConverter(Annotated a) + public Object findDeserializationConverter(MapperConfig config, Annotated a) { JsonDeserialize ann = _findAnnotation(a, JsonDeserialize.class); return (ann == null) ? null : _classIfExplicit(ann.converter(), Converter.None.class); } @Override - public Object findDeserializationContentConverter(AnnotatedMember a) + public Object findDeserializationContentConverter(MapperConfig config, AnnotatedMember a) { JsonDeserialize ann = _findAnnotation(a, JsonDeserialize.class); return (ann == null) ? null : _classIfExplicit(ann.contentConverter(), Converter.None.class); @@ -1178,24 +1130,6 @@ public JavaType refineDeserializationType(final MapperConfig config, return type; } - @Override - @Deprecated // since 2.7 - public Class findDeserializationContentType(Annotated am, JavaType baseContentType) { - return null; - } - - @Override - @Deprecated // since 2.7 - public Class findDeserializationType(Annotated am, JavaType baseType) { - return null; - } - - @Override - @Deprecated // since 2.7 - public Class findDeserializationKeyType(Annotated am, JavaType baseKeyType) { - return null; - } - /* /********************************************************** /* Deserialization: Class annotations @@ -1203,7 +1137,7 @@ public Class findDeserializationKeyType(Annotated am, JavaType baseKeyType) { */ @Override - public Object findValueInstantiator(AnnotatedClass ac) + public Object findValueInstantiator(MapperConfig config, AnnotatedClass ac) { JsonValueInstantiator ann = _findAnnotation(ac, JsonValueInstantiator.class); // no 'null' marker yet, so: @@ -1211,14 +1145,14 @@ public Object findValueInstantiator(AnnotatedClass ac) } @Override - public Class findPOJOBuilder(AnnotatedClass ac) + public Class findPOJOBuilder(MapperConfig config, AnnotatedClass ac) { JsonDeserialize ann = _findAnnotation(ac, JsonDeserialize.class); return (ann == null) ? null : _classIfExplicit(ann.builder()); } @Override - public JsonPOJOBuilder.Value findPOJOBuilderConfig(AnnotatedClass ac) + public JsonPOJOBuilder.Value findPOJOBuilderConfig(MapperConfig config, AnnotatedClass ac) { JsonPOJOBuilder ann = _findAnnotation(ac, JsonPOJOBuilder.class); return (ann == null) ? null : new JsonPOJOBuilder.Value(ann); @@ -1267,51 +1201,12 @@ public JsonSetter.Value findSetterInfo(Annotated a) { return JsonSetter.Value.from(_findAnnotation(a, JsonSetter.class)); } - @Override // since 2.9 + @Override public Boolean findMergeInfo(Annotated a) { JsonMerge ann = _findAnnotation(a, JsonMerge.class); return (ann == null) ? null : ann.value().asBoolean(); } - @Override - @Deprecated // since 2.9 - public boolean hasAnySetterAnnotation(AnnotatedMethod am) { - return _hasAnnotation(am, JsonAnySetter.class); - } - - @Override - @Deprecated // since 2.9 - public boolean hasCreatorAnnotation(Annotated a) - { - /* No dedicated disabling; regular @JsonIgnore used if needs to be - * ignored (and if so, is handled prior to this method getting called) - */ - JsonCreator ann = _findAnnotation(a, JsonCreator.class); - if (ann != null) { - return (ann.mode() != JsonCreator.Mode.DISABLED); - } - // 19-Apr-2016, tatu: As per [databind#1197], [databind#1122] (and some related), - // may or may not consider it a creator - if (_cfgConstructorPropertiesImpliesCreator ) { - if (a instanceof AnnotatedConstructor) { - if (_java7Helper != null) { - Boolean b = _java7Helper.hasCreatorAnnotation(a); - if (b != null) { - return b.booleanValue(); - } - } - } - } - return false; - } - - @Override - @Deprecated // since 2.9 - public JsonCreator.Mode findCreatorBinding(Annotated a) { - JsonCreator ann = _findAnnotation(a, JsonCreator.class); - return (ann == null) ? null : ann.mode(); - } - @Override public JsonCreator.Mode findCreatorAnnotation(MapperConfig config, Annotated a) { JsonCreator ann = _findAnnotation(a, JsonCreator.class); @@ -1321,15 +1216,10 @@ public JsonCreator.Mode findCreatorAnnotation(MapperConfig config, Annotated if (_cfgConstructorPropertiesImpliesCreator && config.isEnabled(MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES) ) { - if (a instanceof AnnotatedConstructor) { - if (_java7Helper != null) { - Boolean b = _java7Helper.hasCreatorAnnotation(a); - if ((b != null) && b.booleanValue()) { - // 13-Sep-2016, tatu: Judgment call, but I don't think JDK ever implies - // use of delegate; assumes as-properties implicitly - return JsonCreator.Mode.PROPERTIES; - } - } + if (_hasAnnotation(a, ConstructorProperties.class)) { + // 13-Sep-2016, tatu: Judgment call, but I don't think JDK ever implies + // use of delegate; assumes as-properties implicitly + return JsonCreator.Mode.PROPERTIES; } } return null; @@ -1347,11 +1237,10 @@ protected boolean _isIgnorable(Annotated a) if (ann != null) { return ann.value(); } - if (_java7Helper != null) { - Boolean b = _java7Helper.findTransient(a); - if (b != null) { - return b.booleanValue(); - } + // From JDK 7: + Transient t = a.getAnnotation(Transient.class); + if (t != null) { + return t.value(); } return false; } @@ -1378,103 +1267,6 @@ protected PropertyName _propertyName(String localName, String namespace) { return PropertyName.construct(localName, namespace); } - protected PropertyName _findConstructorName(Annotated a) - { - if (a instanceof AnnotatedParameter) { - AnnotatedParameter p = (AnnotatedParameter) a; - AnnotatedWithParams ctor = p.getOwner(); - - if (ctor != null) { - if (_java7Helper != null) { - PropertyName name = _java7Helper.findConstructorName(p); - if (name != null) { - return name; - } - } - } - } - return null; - } - - /** - * 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 = _findAnnotation(ann, JsonTypeInfo.class); - JsonTypeResolver resAnn = _findAnnotation(ann, JsonTypeResolver.class); - - if (resAnn != null) { - if (info == null) { - return null; - } - /* let's not try to force access override (would need to pass - * settings through if we did, since that's not doable on some - * platforms) - */ - b = config.typeResolverBuilderInstance(ann, resAnn.value()); - } else { // if not, use standard one, if indicated by annotations - if (info == null) { - return null; - } - // bit special; must return 'marker' to block use of default typing: - if (info.use() == JsonTypeInfo.Id.NONE) { - return _constructNoTypeResolverBuilder(); - } - b = _constructStdTypeResolverBuilder(); - } - // Does it define a custom type id resolver? - JsonTypeIdResolver idResInfo = _findAnnotation(ann, JsonTypeIdResolver.class); - TypeIdResolver idRes = (idResInfo == null) ? null - : config.typeIdResolverInstance(ann, idResInfo.value()); - if (idRes != null) { - idRes.init(baseType); - } - b = b.init(info.use(), idRes); - /* 13-Aug-2011, tatu: One complication; external id - * only works for properties; so if declared for a Class, we will need - * to map it to "PROPERTY" instead of "EXTERNAL_PROPERTY" - */ - JsonTypeInfo.As inclusion = info.include(); - if (inclusion == JsonTypeInfo.As.EXTERNAL_PROPERTY && (ann instanceof AnnotatedClass)) { - inclusion = JsonTypeInfo.As.PROPERTY; - } - b = b.inclusion(inclusion); - b = b.typeProperty(info.property()); - Class defaultImpl = info.defaultImpl(); - - // 08-Dec-2014, tatu: To deprecate `JsonTypeInfo.None` we need to use other placeholder(s); - // and since `java.util.Void` has other purpose (to indicate "deser as null"), we'll instead - // use `JsonTypeInfo.class` itself. But any annotation type will actually do, as they have no - // valid use (cannot instantiate as default) - if (defaultImpl != JsonTypeInfo.None.class && !defaultImpl.isAnnotation()) { - b = b.defaultImpl(defaultImpl); - } - b = b.typeIdVisibility(info.visible()); - return b; - } - - /** - * Helper method for constructing standard {@link TypeResolverBuilder} - * implementation. - */ - protected StdTypeResolverBuilder _constructStdTypeResolverBuilder() { - return new StdTypeResolverBuilder(); - } - - /** - * Helper method for dealing with "no type info" marker; can't be null - * (as it'd be replaced by default typing) - */ - protected StdTypeResolverBuilder _constructNoTypeResolverBuilder() { - return StdTypeResolverBuilder.noTypeInfoBuilder(); - } - private boolean _primitiveAndWrapper(Class baseType, Class refinement) { if (baseType.isPrimitive()) { diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/MixInHandler.java b/src/main/java/com/fasterxml/jackson/databind/introspect/MixInHandler.java new file mode 100644 index 0000000000..c5a677b324 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/MixInHandler.java @@ -0,0 +1,131 @@ +package com.fasterxml.jackson.databind.introspect; + +import java.util.HashMap; +import java.util.Map; + +import com.fasterxml.jackson.core.util.Snapshottable; +import com.fasterxml.jackson.databind.type.ClassKey; + +/** + * Basic {@link MixInResolver} implementation that both allows simple "local" + * override definitions (with simple Mix-in class over Target class mapping) + * and allows optional custom overrides for lookup. + *

    + * Implementation is only thread-safe after initialization (that is, + * when underlying Map is not modified but only read). + */ +public class MixInHandler + implements MixInResolver, + java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + /** + * External resolver that gets called before looking at any locally defined + * mix-in target classes. + */ + protected final MixInResolver _overrides; + + /** + * Simple mix-in targets defined locally. + */ + protected Map> _localMixIns; + + /* + /********************************************************************** + /* Construction, mutant factories + /********************************************************************** + */ + + public MixInHandler(MixInResolver overrides) { + _overrides = overrides; + } + + protected MixInHandler(MixInResolver overrides, + Map> mixins) { + _overrides = overrides; + _localMixIns = mixins; + } + + /** + * Mutant factory for constructor a new resolver instance with given + * mix-in resolver override. + */ + public MixInHandler withOverrides(MixInResolver overrides) { + return new MixInHandler(overrides, _localMixIns); + } + + + /** + * Mutant factory method that constructs a new instance that has no locally + * defined mix-in/target mappings. + */ + public MixInHandler withoutLocalDefinitions() { + return new MixInHandler(_overrides, null); + } + + /* + /********************************************************************** + /* Mutators + /********************************************************************** + */ + + public MixInHandler addLocalDefinitions(Map, Class> sourceMixins) { + if (!sourceMixins.isEmpty()) { + if (_localMixIns == null) { + _localMixIns = new HashMap<>(sourceMixins.size()); + } + for (Map.Entry,Class> en : sourceMixins.entrySet()) { + _localMixIns.put(new ClassKey(en.getKey()), en.getValue()); + } + } + return this; + } + + public MixInHandler addLocalDefinition(Class target, Class mixinSource) { + if (_localMixIns == null) { + _localMixIns = new HashMap>(); + } + _localMixIns.put(new ClassKey(target), mixinSource); + return this; + } + + public MixInHandler clearLocalDefinitions(Map, Class> sourceMixins) { + _localMixIns = null; + return this; + } + + /* + /********************************************************************** + /* MixInResolver API implementation + /********************************************************************** + */ + + @Override + public MixInHandler snapshot() { + MixInResolver overrides = Snapshottable.takeSnapshot(_overrides); + Map> mixIns = (_localMixIns == null) + ? null : new HashMap>(_localMixIns); + return new MixInHandler(overrides, mixIns); + } + + @Override + public Class findMixInClassFor(Class cls) + { + Class mixin = (_overrides == null) ? null : _overrides.findMixInClassFor(cls); + if (mixin == null && (_localMixIns != null)) { + mixin = _localMixIns.get(new ClassKey(cls)); + } + return mixin; + } + + /* + /********************************************************************** + /* Other + /********************************************************************** + */ + + public int localSize() { // for tests + return (_localMixIns == null) ? 0 : _localMixIns.size(); + } +} diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/MixInResolver.java b/src/main/java/com/fasterxml/jackson/databind/introspect/MixInResolver.java new file mode 100644 index 0000000000..508691ec8e --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/MixInResolver.java @@ -0,0 +1,29 @@ +package com.fasterxml.jackson.databind.introspect; + +import com.fasterxml.jackson.core.util.Snapshottable; + +/** + * Interface used for decoupling details of how mix-in annotation + * definitions are accessed (via this interface), and how + * they are stored (defined by classes that implement the interface) + * + * @since 3.0 (in 2.x was nested type of {@link ClassIntrospector}) + */ +public interface MixInResolver + extends Snapshottable +{ + /** + * Method that will check if there are "mix-in" classes (with mix-in + * annotations) for given class + */ + public Class findMixInClassFor(Class cls); + + /** + * Method called to create a new, non-shared copy, to be used by different + * ObjectMapper instance, and one that should not be connected + * to this instance, if resolver has mutable state. + * If resolver is immutable may simply return `this`. + */ + @Override + public MixInResolver snapshot(); +} \ No newline at end of file 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 4612cbd9da..8fa68ff501 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java @@ -37,11 +37,6 @@ public class POJOPropertiesCollector */ protected final boolean _forSerialization; - /** - * @since 2.5 - */ - protected final boolean _stdBeanNaming; - /** * Type of POJO for which properties are being collected. */ @@ -52,13 +47,10 @@ public class POJOPropertiesCollector */ protected final AnnotatedClass _classDef; - protected final VisibilityChecker _visibilityChecker; + protected final VisibilityChecker _visibilityChecker; protected final AnnotationIntrospector _annotationIntrospector; - /** - * @since 2.9 - */ protected final boolean _useAnnotations; /** @@ -127,7 +119,6 @@ protected POJOPropertiesCollector(MapperConfig config, boolean forSerializati JavaType type, AnnotatedClass classDef, String mutatorPrefix) { _config = config; - _stdBeanNaming = config.isEnabled(MapperFeature.USE_STD_BEAN_NAMING); _forSerialization = forSerialization; _type = type; _classDef = classDef; @@ -178,18 +169,6 @@ public Map getInjectables() { return _injectables; } - @Deprecated // since 2.9 - public AnnotatedMethod getJsonValueMethod() { - AnnotatedMember m = getJsonValueAccessor(); - if (m instanceof AnnotatedMethod) { - return (AnnotatedMethod) m; - } - return null; - } - - /** - * @since 2.9 - */ public AnnotatedMember getJsonValueAccessor() { if (!_collected) { @@ -267,9 +246,9 @@ public Set getIgnoredPropertyNames() { */ public ObjectIdInfo getObjectIdInfo() { - ObjectIdInfo info = _annotationIntrospector.findObjectIdInfo(_classDef); - if (info != null) { // 2.1: may also have different defaults for refs: - info = _annotationIntrospector.findObjectReferenceInfo(_classDef, info); + ObjectIdInfo info = _annotationIntrospector.findObjectIdInfo(_config, _classDef); + if (info != null) { + info = _annotationIntrospector.findObjectReferenceInfo(_config, _classDef, info); } return info; } @@ -278,7 +257,7 @@ public ObjectIdInfo getObjectIdInfo() * Method for finding Class to use as POJO builder, if any. */ public Class findPOJOBuilderClass() { - return _annotationIntrospector.findPOJOBuilder(_classDef); + return _annotationIntrospector.findPOJOBuilder(_config, _classDef); } // for unit tests: @@ -297,8 +276,6 @@ protected Map getPropertyMap() { /** * Internal method that will collect actual property information. - * - * @since 2.6 */ protected void collectAll() { @@ -319,34 +296,28 @@ protected void collectAll() _removeUnwantedProperties(props); // and then remove unneeded accessors (wrt read-only, read-write) _removeUnwantedAccessor(props); - // Rename remaining properties _renameProperties(props); - - // then merge annotations, to simplify further processing - // 26-Sep-2017, tatu: Before 2.9.2 was done earlier but that prevented some of - // annotations from getting properly merged + // then merge annotations, to simplify further processing: has to be done AFTER + // preceding renaming step to get right propagation for (POJOPropertyBuilder property : props.values()) { property.mergeAnnotations(_forSerialization); } - // And use custom naming strategy, if applicable... PropertyNamingStrategy naming = _findNamingStrategy(); if (naming != null) { _renameUsing(props, naming); } - /* Sort by visibility (explicit over implicit); drop all but first - * of member type (getter, setter etc) if there is visibility - * difference - */ + // Sort by visibility (explicit over implicit); drop all but first + // of member type (getter, setter etc) if there is visibility + // difference for (POJOPropertyBuilder property : props.values()) { property.trimByVisibility(); } - /* and, if required, apply wrapper name: note, MUST be done after - * annotations are merged. - */ + // and, if required, apply wrapper name: note, MUST be done after + // annotations are merged. if (_config.isEnabled(MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME)) { _renameWithWrappers(props); } @@ -362,7 +333,7 @@ protected void collectAll() /* Overridable internal methods, adding members /********************************************************** */ - + /** * Method for collecting basic information on all fields found */ @@ -377,7 +348,6 @@ protected void _addFields(Map props) final boolean transientAsIgnoral = _config.isEnabled(MapperFeature.PROPAGATE_TRANSIENT_MARKER); for (AnnotatedField f : _classDef.fields()) { - String implName = ai.findImplicitPropertyName(f); // @JsonValue? if (Boolean.TRUE.equals(ai.hasAsValue(f))) { if (_jsonValueAccessors == null) { @@ -394,6 +364,7 @@ protected void _addFields(Map props) _anySetterField.add(f); continue; } + String implName = ai.findImplicitPropertyName(f); if (implName == null) { implName = f.getName(); } @@ -475,9 +446,6 @@ protected void _addCreators(Map props) } } - /** - * @since 2.4 - */ protected void _addCreatorParam(Map props, AnnotatedParameter param) { @@ -580,10 +548,10 @@ protected void _addGetterMethod(Map props, if (!nameExplicit) { // no explicit name; must consider implicit implName = ai.findImplicitPropertyName(m); if (implName == null) { - implName = BeanUtil.okNameForRegularGetter(m, m.getName(), _stdBeanNaming); + implName = BeanUtil.okNameForRegularGetter(m, m.getName()); } if (implName == null) { // if not, must skip - implName = BeanUtil.okNameForIsGetter(m, m.getName(), _stdBeanNaming); + implName = BeanUtil.okNameForIsGetter(m, m.getName()); if (implName == null) { return; } @@ -595,7 +563,7 @@ protected void _addGetterMethod(Map props, // we still need implicit name to link with other pieces implName = ai.findImplicitPropertyName(m); if (implName == null) { - implName = BeanUtil.okNameForGetter(m, _stdBeanNaming); + implName = BeanUtil.okNameForGetter(m); } // if not regular getter name, use method name as is if (implName == null) { @@ -622,7 +590,7 @@ protected void _addSetterMethod(Map props, if (!nameExplicit) { // no explicit name; must follow naming convention implName = (ai == null) ? null : ai.findImplicitPropertyName(m); if (implName == null) { - implName = BeanUtil.okNameForMutator(m, _mutatorPrefix, _stdBeanNaming); + implName = BeanUtil.okNameForMutator(m, _mutatorPrefix); } if (implName == null) { // if not, must skip return; @@ -632,7 +600,7 @@ protected void _addSetterMethod(Map props, // we still need implicit name to link with other pieces implName = (ai == null) ? null : ai.findImplicitPropertyName(m); if (implName == null) { - implName = BeanUtil.okNameForMutator(m, _mutatorPrefix, _stdBeanNaming); + implName = BeanUtil.okNameForMutator(m, _mutatorPrefix); } // if not regular getter name, use method name as is if (implName == null) { @@ -933,8 +901,8 @@ protected void _renameWithWrappers(Map props) /********************************************************** */ - /* First, order by [JACKSON-90] (explicit ordering and/or alphabetic) - * and then for [JACKSON-170] (implicitly order creator properties before others) + /* First, explicit ordering and/or alphabetic + * and then implicitly order creator properties before others. */ protected void _sortProperties(Map props) { @@ -1043,7 +1011,7 @@ protected POJOPropertyBuilder _property(Map props, } return prop; } - + // !!! TODO: deprecate, require use of PropertyName protected POJOPropertyBuilder _property(Map props, String implName) @@ -1066,9 +1034,8 @@ private PropertyNamingStrategy _findNamingStrategy() if (namingDef instanceof PropertyNamingStrategy) { return (PropertyNamingStrategy) namingDef; } - /* Alas, there's no way to force return type of "either class - * X or Y" -- need to throw an exception after the fact - */ + // Alas, there's no way to force return type of "either class + // X or Y" -- need to throw an exception after the fact if (!(namingDef instanceof Class)) { throw new IllegalStateException("AnnotationIntrospector returned PropertyNamingStrategy definition of type " +namingDef.getClass().getName()+"; expected type PropertyNamingStrategy or Class instead"); 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 dc37e282fc..5c682b3ecb 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertyBuilder.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertyBuilder.java @@ -23,8 +23,6 @@ public class POJOPropertyBuilder /** * Marker value used to denote that no reference-property information found for * this property - * - * @since 2.9 */ private final static AnnotationIntrospector.ReferenceProperty NOT_REFEFERENCE_PROP = AnnotationIntrospector.ReferenceProperty.managed(""); @@ -85,7 +83,6 @@ protected POJOPropertyBuilder(MapperConfig config, AnnotationIntrospector ai, _forSerialization = forSerialization; } - // protected since 2.9 (was public before) protected POJOPropertyBuilder(POJOPropertyBuilder src, PropertyName newName) { _config = src._config; @@ -136,9 +133,7 @@ public int compareTo(POJOPropertyBuilder other) } else if (other._ctorParameters != null) { return 1; } - /* otherwise sort by external name (including sorting of - * ctor parameters) - */ + // otherwise sort by external name (including sorting of ctor parameters) return getName().compareTo(other.getName()); } @@ -174,7 +169,7 @@ public PropertyName getWrapperName() { * occur, try commenting out full traversal code */ AnnotatedMember member = getPrimaryMember(); - return (member == null || _annotationIntrospector == null) ? null + return (member == null) ? null : _annotationIntrospector.findWrapperName(member); /* return fromMemberAnnotations(new WithMember() { @@ -273,13 +268,13 @@ protected PropertyMetadata _getSetterInfo(PropertyMetadata metadata) if (needMerge || (valueNulls == null) || (contentNulls == null)) { Class rawType = getRawPrimaryType(); ConfigOverride co = _config.getConfigOverride(rawType); - JsonSetter.Value setterInfo = co.getSetterInfo(); - if (setterInfo != null) { + JsonSetter.Value nullHandling = co.getNullHandling(); + if (nullHandling != null) { if (valueNulls == null) { - valueNulls = setterInfo.nonDefaultValueNulls(); + valueNulls = nullHandling.nonDefaultValueNulls(); } if (contentNulls == null) { - contentNulls = setterInfo.nonDefaultContentNulls(); + contentNulls = nullHandling.nonDefaultContentNulls(); } } if (needMerge && (acc != null)) { @@ -294,7 +289,7 @@ protected PropertyMetadata _getSetterInfo(PropertyMetadata metadata) } } if (needMerge || (valueNulls == null) || (contentNulls == null)) { - JsonSetter.Value setterInfo = _config.getDefaultSetterInfo(); + JsonSetter.Value setterInfo = _config.getDefaultNullHandling(); if (valueNulls == null) { valueNulls = setterInfo.nonDefaultValueNulls(); } @@ -400,9 +395,8 @@ public AnnotatedMethod getGetter() } // But if multiple, verify that they do not conflict... for (; next != null; next = next.next) { - /* [JACKSON-255] Allow masking, i.e. do not report exception if one - * is in super-class from the other - */ + // Allow masking, i.e. do not report exception if one + // is in super-class from the other Class currClass = curr.value.getDeclaringClass(); Class nextClass = next.value.getDeclaringClass(); if (currClass != nextClass) { @@ -608,12 +602,7 @@ protected int _setterPriority(AnnotatedMethod m) @Override public Class[] findViews() { - return fromMemberAnnotations(new WithMember[]>() { - @Override - public Class[] withMember(AnnotatedMember member) { - return _annotationIntrospector.findViews(member); - } - }); + return _annotationIntrospector.findViews(getPrimaryMember()); } @Override @@ -627,75 +616,58 @@ public AnnotationIntrospector.ReferenceProperty findReferenceType() { } return result; } - result = fromMemberAnnotations(new WithMember() { - @Override - public AnnotationIntrospector.ReferenceProperty withMember(AnnotatedMember member) { - return _annotationIntrospector.findReferenceType(member); - } - }); + AnnotatedMember m = getPrimaryMember(); + result = (m == null) ? null : _annotationIntrospector.findReferenceType(m); _referenceInfo = (result == null) ? NOT_REFEFERENCE_PROP : result; return result; } @Override public boolean isTypeId() { - Boolean b = fromMemberAnnotations(new WithMember() { - @Override - public Boolean withMember(AnnotatedMember member) { - return _annotationIntrospector.isTypeId(member); + AnnotatedMember m = getPrimaryMember(); + if (m != null) { + Boolean b = _annotationIntrospector.isTypeId(_config, m); + if (b != null) { + return b.booleanValue(); } - }); - return (b != null) && b.booleanValue(); + } + return false; } protected Boolean _findRequired() { - return fromMemberAnnotations(new WithMember() { - @Override - public Boolean withMember(AnnotatedMember member) { - return _annotationIntrospector.hasRequiredMarker(member); - } - }); + AnnotatedMember m = getPrimaryMember(); + return (m == null) ? null + : _annotationIntrospector.hasRequiredMarker(m); } protected String _findDescription() { - return fromMemberAnnotations(new WithMember() { - @Override - public String withMember(AnnotatedMember member) { - return _annotationIntrospector.findPropertyDescription(member); - } - }); + AnnotatedMember m = getPrimaryMember(); + return (m == null) ? null + : _annotationIntrospector.findPropertyDescription(m); } protected Integer _findIndex() { - return fromMemberAnnotations(new WithMember() { - @Override - public Integer withMember(AnnotatedMember member) { - return _annotationIntrospector.findPropertyIndex(member); - } - }); + AnnotatedMember m = getPrimaryMember(); + return (m == null) ? null + : _annotationIntrospector.findPropertyIndex(m); } protected String _findDefaultValue() { - return fromMemberAnnotations(new WithMember() { - @Override - public String withMember(AnnotatedMember member) { - return _annotationIntrospector.findPropertyDefaultValue(member); - } - }); + AnnotatedMember m = getPrimaryMember(); + return (m == null) ? null + : _annotationIntrospector.findPropertyDefaultValue(m); } - + @Override public ObjectIdInfo findObjectIdInfo() { - return fromMemberAnnotations(new WithMember() { - @Override - public ObjectIdInfo withMember(AnnotatedMember member) { - ObjectIdInfo info = _annotationIntrospector.findObjectIdInfo(member); - if (info != null) { - info = _annotationIntrospector.findObjectReferenceInfo(member, info); - } - return info; + AnnotatedMember m = getPrimaryMember(); + if (m != null) { + ObjectIdInfo info = _annotationIntrospector.findObjectIdInfo(_config, m); + if (info != null) { + return _annotationIntrospector.findObjectReferenceInfo(_config, m, info); } - }); + } + return null; } @Override @@ -705,12 +677,13 @@ public JsonInclude.Value findInclusion() { // 17-Aug-2016, tatu: Do NOT include global, or per-type defaults, because // not all of this information (specifically, enclosing type's settings) // is available here - JsonInclude.Value v = (_annotationIntrospector == null) ? - null : _annotationIntrospector.findPropertyInclusion(a); + JsonInclude.Value v = _annotationIntrospector.findPropertyInclusion(_config, a); return (v == null) ? JsonInclude.Value.empty() : v; } public JsonProperty.Access findAccess() { + // 25-Sep-2017, tatu: IMPORTANT! Called BEFORE merge occurs so MUST traverse + // accessors separately return fromMemberAnnotationsExcept(new WithMember() { @Override public JsonProperty.Access withMember(AnnotatedMember member) { @@ -850,53 +823,58 @@ public void trimByVisibility() _ctorParameters = _trimByVisibility(_ctorParameters); } - @SuppressWarnings("unchecked") public void mergeAnnotations(boolean forSerialization) { if (forSerialization) { if (_getters != null) { - AnnotationMap ann = _mergeAnnotations(0, _getters, _fields, _ctorParameters, _setters); + AnnotationMap ann = _mergeAnnotations(_getters, + _mergeAnnotations(_fields, + _mergeAnnotations(_ctorParameters, _setters))); _getters = _applyAnnotations(_getters, ann); } else if (_fields != null) { - AnnotationMap ann = _mergeAnnotations(0, _fields, _ctorParameters, _setters); + AnnotationMap ann = _mergeAnnotations(_fields, + _mergeAnnotations(_ctorParameters, _setters)); _fields = _applyAnnotations(_fields, ann); } } else { // for deserialization if (_ctorParameters != null) { - AnnotationMap ann = _mergeAnnotations(0, _ctorParameters, _setters, _fields, _getters); + AnnotationMap ann = _mergeAnnotations(_ctorParameters, + _mergeAnnotations(_setters, + _mergeAnnotations(_fields, _getters))); _ctorParameters = _applyAnnotations(_ctorParameters, ann); } else if (_setters != null) { - AnnotationMap ann = _mergeAnnotations(0, _setters, _fields, _getters); + AnnotationMap ann = _mergeAnnotations(_setters, + _mergeAnnotations(_fields, _getters)); _setters = _applyAnnotations(_setters, ann); } else if (_fields != null) { - AnnotationMap ann = _mergeAnnotations(0, _fields, _getters); + AnnotationMap ann = _mergeAnnotations(_fields, _getters); _fields = _applyAnnotations(_fields, ann); } } } - private AnnotationMap _mergeAnnotations(int index, - Linked... nodes) + private AnnotationMap _mergeAnnotations(Linked node1, + Linked node2) { - AnnotationMap ann = _getAllAnnotations(nodes[index]); - while (++index < nodes.length) { - if (nodes[index] != null) { - return AnnotationMap.merge(ann, _mergeAnnotations(index, nodes)); - } - } - return ann; + return AnnotationMap.merge(_getAllAnnotations(node1), + _getAllAnnotations(node2)); + } + + private AnnotationMap _mergeAnnotations(Linked node1, + AnnotationMap secondary) + { + return AnnotationMap.merge(_getAllAnnotations(node1), secondary); } /** * Replacement, as per [databind#868], of simple access to annotations, which - * does "deep merge" if an as necessary. - *

    -     * nodes[index].value.getAllAnnotations()
    -     *
    - * - * @since 2.6 + * does "deep merge" if an as necessary, across alternate accessors of same type: + * most importantly, "is-getter vs regular getter" */ - private AnnotationMap _getAllAnnotations(Linked node) { + private AnnotationMap _getAllAnnotations(Linked node) { + if (node == null) { + return null; + } AnnotationMap ann = node.value.getAllAnnotations(); if (node.next != null) { ann = AnnotationMap.merge(ann, _getAllAnnotations(node.next)); @@ -910,8 +888,6 @@ private AnnotationMap _getAllAnnotations(Linked n * and secondary accessors are pruned later on. *

    * See [databind#868] for more information. - * - * @since 2.6 */ private Linked _applyAnnotations(Linked node, AnnotationMap ann) { @SuppressWarnings("unchecked") @@ -1159,10 +1135,6 @@ protected T fromMemberAnnotations(WithMember func) protected T fromMemberAnnotationsExcept(WithMember func, T defaultValue) { - if (_annotationIntrospector == null) { - return null; - } - // NOTE: here we must ask ALL accessors, but the order varies between // serialization, deserialization if (_forSerialization) { diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/SimpleMixInResolver.java b/src/main/java/com/fasterxml/jackson/databind/introspect/SimpleMixInResolver.java deleted file mode 100644 index a3c630a882..0000000000 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/SimpleMixInResolver.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.fasterxml.jackson.databind.introspect; - -import java.util.HashMap; -import java.util.Map; - -import com.fasterxml.jackson.databind.type.ClassKey; - -/** - * Simple implementation of {@link ClassIntrospector.MixInResolver} - * that just uses a {@link java.util.Map} for containing mapping - * from target to mix-in classes. - *

    - * Implementation is only thread-safe after initialization (that is, - * when underlying Map is not modified but only read). - * - * @since 2.6 - */ -public class SimpleMixInResolver - implements ClassIntrospector.MixInResolver, - java.io.Serializable -{ - private static final long serialVersionUID = 1L; - - /** - * External resolver that gets called before looking at any locally defined - * mix-in target classes. - */ - protected final ClassIntrospector.MixInResolver _overrides; - - /** - * Simple mix-in targets defined locally. - */ - protected Map> _localMixIns; - - public SimpleMixInResolver(ClassIntrospector.MixInResolver overrides) { - _overrides = overrides; - } - - protected SimpleMixInResolver(ClassIntrospector.MixInResolver overrides, - Map> mixins) { - _overrides = overrides; - _localMixIns = mixins; - } - - /** - * Mutant factory for constructor a new resolver instance with given - * mix-in resolver override. - */ - public SimpleMixInResolver withOverrides(ClassIntrospector.MixInResolver overrides) { - return new SimpleMixInResolver(overrides, _localMixIns); - } - - /** - * Mutant factory method that constructs a new instance that has no locally - * defined mix-in/target mappings. - */ - public SimpleMixInResolver withoutLocalDefinitions() { - return new SimpleMixInResolver(_overrides, null); - } - - public void setLocalDefinitions(Map, Class> sourceMixins) { - if (sourceMixins == null || sourceMixins.isEmpty()) { - _localMixIns = null; - } else { - Map> mixIns = new HashMap>(sourceMixins.size()); - for (Map.Entry,Class> en : sourceMixins.entrySet()) { - mixIns.put(new ClassKey(en.getKey()), en.getValue()); - } - _localMixIns = mixIns; - } - } - - public void addLocalDefinition(Class target, Class mixinSource) { - if (_localMixIns == null) { - _localMixIns = new HashMap>(); - } - _localMixIns.put(new ClassKey(target), mixinSource); - } - - @Override - public SimpleMixInResolver copy() { - ClassIntrospector.MixInResolver overrides = (_overrides == null) - ? null : _overrides.copy(); - Map> mixIns = (_localMixIns == null) - ? null : new HashMap>(_localMixIns); - return new SimpleMixInResolver(overrides, mixIns); - } - - @Override - public Class findMixInClassFor(Class cls) - { - Class mixin = (_overrides == null) ? null : _overrides.findMixInClassFor(cls); - if (mixin == null && (_localMixIns != null)) { - mixin = _localMixIns.get(new ClassKey(cls)); - } - return mixin; - } - - public int localSize() { - return (_localMixIns == null) ? 0 : _localMixIns.size(); - } -} 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 0cb7766746..bf1add7ea4 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/VisibilityChecker.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/VisibilityChecker.java @@ -1,10 +1,5 @@ package com.fasterxml.jackson.databind.introspect; -import java.lang.reflect.Field; -import java.lang.reflect.Member; -import java.lang.reflect.Method; - - import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; @@ -13,30 +8,125 @@ * Interface for object used for determine which property elements * (methods, fields, constructors) can be auto-detected, with respect * to their visibility modifiers. - *

    - * Note on type declaration: funky recursive type is necessary to - * support builder/fluent pattern. */ -public interface VisibilityChecker> +public class VisibilityChecker + implements java.io.Serializable { - // // Builder methods + private static final long serialVersionUID = 1; /** - * Builder method that will return an instance that has same - * settings as this instance has, except for values that - * given annotation overrides. + * This is the canonical base instance, configured with default + * visibility values */ - public T with(JsonAutoDetect ann); + protected final static VisibilityChecker DEFAULT = new VisibilityChecker( + Visibility.PUBLIC_ONLY, // field + Visibility.PUBLIC_ONLY, // getter + Visibility.PUBLIC_ONLY, // is-getter + Visibility.ANY, // setter + Visibility.PUBLIC_ONLY, // creator -- NOTE: was `ANY` for 2.x + Visibility.NON_PRIVATE // scalar-constructor (new in 3.x) + ); + + protected final Visibility _fieldMinLevel; + protected final Visibility _getterMinLevel; + protected final Visibility _isGetterMinLevel; + protected final Visibility _setterMinLevel; + protected final Visibility _creatorMinLevel; + protected final Visibility _scalarConstructorMinLevel; /** - * Method that can be used for merging default values from `this` - * instance with specified overrides; and either return `this` - * if overrides had no effect (that is, result would be equal), - * or a new instance with merged visibility settings. - * - * @since 2.9 + * Constructor used for building instance that has minumum visibility + * levels as indicated by given annotation instance + * + * @param ann Annotations to use for determining minimum visibility levels */ - public T withOverrides(JsonAutoDetect.Value vis); + public VisibilityChecker(JsonAutoDetect ann) + { + // let's combine checks for enabled/disabled, with minimum level checks: + _fieldMinLevel = ann.fieldVisibility(); + _getterMinLevel = ann.getterVisibility(); + _isGetterMinLevel = ann.isGetterVisibility(); + _setterMinLevel = ann.setterVisibility(); + _creatorMinLevel = ann.creatorVisibility(); + _scalarConstructorMinLevel = ann.scalarConstructorVisibility(); + } + + /** + * Constructor that allows directly specifying minimum visibility levels to use + */ + public VisibilityChecker(Visibility field, + Visibility getter, Visibility isGetter, Visibility setter, + Visibility creator, Visibility scalarConstructor) + { + + _getterMinLevel = getter; + _isGetterMinLevel = isGetter; + _setterMinLevel = setter; + _creatorMinLevel = creator; + _fieldMinLevel = field; + _scalarConstructorMinLevel = scalarConstructor; + } + + /** + * Constructor that will assign given visibility value for all + * properties. + * + * @param v level to use for all property types + */ + public VisibilityChecker(Visibility v) + { + // typically we shouldn't get this value; but let's handle it if we do: + if (v == Visibility.DEFAULT) { + _getterMinLevel = DEFAULT._getterMinLevel; + _isGetterMinLevel = DEFAULT._isGetterMinLevel; + _setterMinLevel = DEFAULT._setterMinLevel; + _creatorMinLevel = DEFAULT._creatorMinLevel; + _fieldMinLevel = DEFAULT._fieldMinLevel; + _scalarConstructorMinLevel = DEFAULT._scalarConstructorMinLevel; + } else { + _getterMinLevel = v; + _isGetterMinLevel = v; + _setterMinLevel = v; + _creatorMinLevel = v; + _fieldMinLevel = v; + _scalarConstructorMinLevel = v; + } + } + + public static VisibilityChecker construct(JsonAutoDetect.Value vis) { + return DEFAULT.withOverrides(vis); + } + + + public static VisibilityChecker defaultInstance() { return DEFAULT; } + + /* + /********************************************************************** + /* Mutant factories + /********************************************************************** + */ + + public VisibilityChecker withOverrides(JsonAutoDetect.Value vis) + { + if (vis == null) { + return this; + } + return _with( + _defaultOrOverride(_fieldMinLevel, vis.getFieldVisibility()), + _defaultOrOverride(_getterMinLevel, vis.getGetterVisibility()), + _defaultOrOverride(_isGetterMinLevel, vis.getIsGetterVisibility()), + _defaultOrOverride(_setterMinLevel, vis.getSetterVisibility()), + _defaultOrOverride(_creatorMinLevel, vis.getCreatorVisibility()), + _defaultOrOverride(_scalarConstructorMinLevel, vis.getScalarConstructorVisibility()) + ); + } + + private Visibility _defaultOrOverride(Visibility defaults, Visibility override) { + if (override == Visibility.DEFAULT) { + return defaults; + } + return override; + } /** * Builder method that will create and return an instance that has specified @@ -48,7 +138,13 @@ public interface VisibilityChecker> * * (which would basically disable all auto-detection) */ - public T with(Visibility v); + public VisibilityChecker with(Visibility v) + { + if (v == Visibility.DEFAULT) { + return DEFAULT; + } + return new VisibilityChecker(v); + } /** * Builder method that will create and return an instance that has specified @@ -60,375 +156,187 @@ public interface VisibilityChecker> * * (which would basically enable auto-detection for all member fields) */ - public T withVisibility(PropertyAccessor method, Visibility v); + public VisibilityChecker 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; + } + } + /** + * Builder method that will return a checker instance that has + * specified minimum visibility level for fields. + */ + public VisibilityChecker withFieldVisibility(Visibility v) { + if (v == Visibility.DEFAULT) v = DEFAULT._fieldMinLevel; + if (_fieldMinLevel == v) return this; + return new VisibilityChecker(v, _getterMinLevel, _isGetterMinLevel, _setterMinLevel, + _creatorMinLevel, _scalarConstructorMinLevel); + } + /** * Builder method that will return a checker instance that has * specified minimum visibility level for regular ("getXxx") getters. */ - public T withGetterVisibility(Visibility v); + public VisibilityChecker withGetterVisibility(Visibility v) { + if (v == Visibility.DEFAULT) v = DEFAULT._getterMinLevel; + if (_getterMinLevel == v) return this; + return new VisibilityChecker(_fieldMinLevel, v, _isGetterMinLevel, _setterMinLevel, + _creatorMinLevel, _scalarConstructorMinLevel); + } /** * Builder method that will return a checker instance that has * specified minimum visibility level for "is-getters" ("isXxx"). */ - public T withIsGetterVisibility(Visibility v); - + public VisibilityChecker withIsGetterVisibility(Visibility v) { + if (v == Visibility.DEFAULT) v = DEFAULT._isGetterMinLevel; + if (_isGetterMinLevel == v) return this; + return new VisibilityChecker(_fieldMinLevel, _getterMinLevel, v, _setterMinLevel, + _creatorMinLevel, _scalarConstructorMinLevel); + } + /** * Builder method that will return a checker instance that has * specified minimum visibility level for setters. */ - public T withSetterVisibility(Visibility v); + public VisibilityChecker withSetterVisibility(Visibility v) { + if (v == Visibility.DEFAULT) v = DEFAULT._setterMinLevel; + if (_setterMinLevel == v) return this; + return new VisibilityChecker(_fieldMinLevel, _getterMinLevel, _isGetterMinLevel, v, + _creatorMinLevel, _scalarConstructorMinLevel); + } /** * Builder method that will return a checker instance that has * specified minimum visibility level for creator methods * (constructors, factory methods) */ - public T withCreatorVisibility(Visibility v); + public VisibilityChecker withCreatorVisibility(Visibility v) { + if (v == Visibility.DEFAULT) v = DEFAULT._creatorMinLevel; + if (_creatorMinLevel == v) return this; + return new VisibilityChecker(_fieldMinLevel, _getterMinLevel, _isGetterMinLevel, _setterMinLevel, + v, _scalarConstructorMinLevel); + } /** - * Builder method that will return a checker instance that has - * specified minimum visibility level for fields. + * @since 3.0 + */ + public VisibilityChecker withScalarConstructorVisibility(Visibility v) { + if (v == Visibility.DEFAULT) v = DEFAULT._scalarConstructorMinLevel; + if (_scalarConstructorMinLevel == v) return this; + return new VisibilityChecker(_fieldMinLevel, _getterMinLevel, _isGetterMinLevel, _setterMinLevel, + _creatorMinLevel, v); + } + + /** + * Method that can be used for merging default values from `this` + * instance with specified overrides; and either return `this` + * if overrides had no effect (that is, result would be equal), + * or a new instance with merged visibility settings. + */ + protected VisibilityChecker _with(Visibility f, Visibility g, Visibility isG, Visibility s, + Visibility cr, Visibility scalarCr) { + if ((f == _fieldMinLevel) + && (g == _getterMinLevel) + && (isG == _isGetterMinLevel) + && (s == _setterMinLevel) + && (cr == _creatorMinLevel) + && (scalarCr == _scalarConstructorMinLevel)) { + return this; + } + return new VisibilityChecker(f, g, isG, s, cr, scalarCr); + } + + /* + /********************************************************************** + /* Accessors + /********************************************************************** + */ + + /** + * Method for checking whether given field is auto-detectable + * as property, with respect to its visibility (not considering + * method signature or name, just visibility) */ - public T withFieldVisibility(Visibility v); - - // // Accessors - + public boolean isFieldVisible(AnnotatedField f) { + return _fieldMinLevel.isVisible(f.getAnnotated()); + } + /** * Method for checking whether given method is auto-detectable * as regular getter, with respect to its visibility (not considering * method signature or name, just visibility) */ - public boolean isGetterVisible(Method m); - public boolean isGetterVisible(AnnotatedMethod m); + public boolean isGetterVisible(AnnotatedMethod m) { + return _getterMinLevel.isVisible(m.getAnnotated()); + } /** * Method for checking whether given method is auto-detectable * as is-getter, with respect to its visibility (not considering * method signature or name, just visibility) */ - public boolean isIsGetterVisible(Method m); - public boolean isIsGetterVisible(AnnotatedMethod m); - + public boolean isIsGetterVisible(AnnotatedMethod m) { + return _isGetterMinLevel.isVisible(m.getAnnotated()); + } + /** * Method for checking whether given method is auto-detectable * as setter, with respect to its visibility (not considering * method signature or name, just visibility) */ - public boolean isSetterVisible(Method m); - public boolean isSetterVisible(AnnotatedMethod m); + public boolean isSetterVisible(AnnotatedMethod m) { + return _setterMinLevel.isVisible(m.getAnnotated()); + } /** - * Method for checking whether given method is auto-detectable - * as Creator, with respect to its visibility (not considering - * method signature or name, just visibility) + * Method for checking whether given creator (other than "scalar constructor", + * see {@link #isScalarConstructorVisible}) is auto-detectable + * as Creator, with respect to its visibility + * (not considering signature, just visibility) */ - public boolean isCreatorVisible(Member m); - public boolean isCreatorVisible(AnnotatedMember m); + public boolean isCreatorVisible(AnnotatedMember m) { + return _creatorMinLevel.isVisible(m.getMember()); + } /** - * Method for checking whether given field is auto-detectable - * as property, with respect to its visibility (not considering - * method signature or name, just visibility) + * Method for checking whether given single-scalar-argument + * constructor is auto-detectable + * as delegating Creator, with respect to its visibility + * (not considering signature, just visibility) + * + * @since 3.0 */ - public boolean isFieldVisible(Field f); - public boolean isFieldVisible(AnnotatedField f); + public boolean isScalarConstructorVisible(AnnotatedMember m) { + return _scalarConstructorMinLevel.isVisible(m.getMember()); + } /* /******************************************************** - /* Standard implementation suitable for basic use + /* Standard methods /******************************************************** - */ - - /** - * Default standard implementation is purely based on visibility - * modifier of given class members, and its configured minimum - * levels. - * Implemented using "builder" (or "Fluent") pattern, whereas instances - * are immutable, and configuration is achieved by chainable factory - * methods. As a result, type is declared is funky recursive generic - * type, to allow for sub-classing of build methods with property type - * co-variance. - */ - public static class Std - implements VisibilityChecker, - java.io.Serializable - { - private static final long serialVersionUID = 1; - - /** - * This is the canonical base instance, configured with default - * visibility values - */ - protected final static Std DEFAULT = new Std( - Visibility.PUBLIC_ONLY, // getter - Visibility.PUBLIC_ONLY, // is-getter - Visibility.ANY, // setter - Visibility.ANY, // creator -- legacy, to support single-arg ctors - Visibility.PUBLIC_ONLY // field - ); - - protected final Visibility _getterMinLevel; - protected final Visibility _isGetterMinLevel; - protected final Visibility _setterMinLevel; - protected final Visibility _creatorMinLevel; - protected final Visibility _fieldMinLevel; - - public static Std defaultInstance() { return DEFAULT; } - - /** - * Constructor used for building instance that has minumum visibility - * levels as indicated by given annotation instance - * - * @param ann Annotations to use for determining minimum visibility levels - */ - public Std(JsonAutoDetect ann) - { - // let's combine checks for enabled/disabled, with minimum level checks: - _getterMinLevel = ann.getterVisibility(); - _isGetterMinLevel = ann.isGetterVisibility(); - _setterMinLevel = ann.setterVisibility(); - _creatorMinLevel = ann.creatorVisibility(); - _fieldMinLevel = ann.fieldVisibility(); - } - - /** - * Constructor that allows directly specifying minimum visibility levels to use - */ - public Std(Visibility getter, Visibility isGetter, Visibility setter, - Visibility creator, Visibility field) - { - _getterMinLevel = getter; - _isGetterMinLevel = isGetter; - _setterMinLevel = setter; - _creatorMinLevel = creator; - _fieldMinLevel = field; - } - - /** - * Constructor that will assign given visibility value for all - * properties. - * - * @param v level to use for all property types - */ - public Std(Visibility v) - { - // typically we shouldn't get this value; but let's handle it if we do: - if (v == Visibility.DEFAULT) { - _getterMinLevel = DEFAULT._getterMinLevel; - _isGetterMinLevel = DEFAULT._isGetterMinLevel; - _setterMinLevel = DEFAULT._setterMinLevel; - _creatorMinLevel = DEFAULT._creatorMinLevel; - _fieldMinLevel = DEFAULT._fieldMinLevel; - } else { - _getterMinLevel = v; - _isGetterMinLevel = v; - _setterMinLevel = v; - _creatorMinLevel = v; - _fieldMinLevel = v; - } - } - - /** - * @since 2.9 - */ - public static Std construct(JsonAutoDetect.Value vis) { - return DEFAULT.withOverrides(vis); - } - - /* - /******************************************************** - /* Builder/fluent methods for instantiating configured - /* instances - /******************************************************** - */ - - protected Std _with(Visibility g, Visibility isG, Visibility s, - Visibility cr, Visibility f) { - if ((g == _getterMinLevel) - && (isG == _isGetterMinLevel) - && (s == _setterMinLevel) - && (cr == _creatorMinLevel) - && (f == _fieldMinLevel) - ) { - return this; - } - return new Std(g, isG, s, cr, f); - } - - @Override - public Std with(JsonAutoDetect ann) - { - Std curr = this; - if (ann != null) { - return _with( - _defaultOrOverride(_getterMinLevel, ann.getterVisibility()), - _defaultOrOverride(_isGetterMinLevel, ann.isGetterVisibility()), - _defaultOrOverride(_setterMinLevel, ann.setterVisibility()), - _defaultOrOverride(_creatorMinLevel, ann.creatorVisibility()), - _defaultOrOverride(_fieldMinLevel, ann.fieldVisibility()) - ); - } - return curr; - } - - @Override // since 2.9 - public Std withOverrides(JsonAutoDetect.Value vis) - { - Std curr = this; - if (vis != null) { - return _with( - _defaultOrOverride(_getterMinLevel, vis.getGetterVisibility()), - _defaultOrOverride(_isGetterMinLevel, vis.getIsGetterVisibility()), - _defaultOrOverride(_setterMinLevel, vis.getSetterVisibility()), - _defaultOrOverride(_creatorMinLevel, vis.getCreatorVisibility()), - _defaultOrOverride(_fieldMinLevel, vis.getFieldVisibility()) - ); - } - return curr; - } - - private Visibility _defaultOrOverride(Visibility defaults, Visibility override) { - if (override == Visibility.DEFAULT) { - return defaults; - } - return override; - } - - @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; - } - } - - @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); - } - - @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); - } - - /* - /******************************************************** - /* 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 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 isSetterVisible(AnnotatedMethod m) { - return isSetterVisible(m.getAnnotated()); - } + */ - /* - /******************************************************** - /* Standard methods - /******************************************************** - */ - - @Override - public String toString() { - return String.format("[Visibility: getter=%s,isGetter=%s,setter=%s,creator=%s,field=%s]", - _getterMinLevel, _isGetterMinLevel, _setterMinLevel, _creatorMinLevel, _fieldMinLevel); - } + @Override + public String toString() { + return String.format("[Visibility: field=%s,getter=%s,isGetter=%s,setter=%s,creator=%s,scalarConstructor=%s]", + _fieldMinLevel, _getterMinLevel, _isGetterMinLevel, _setterMinLevel, + _creatorMinLevel, _scalarConstructorMinLevel); } } diff --git a/src/main/java/com/fasterxml/jackson/databind/json/JsonMapper.java b/src/main/java/com/fasterxml/jackson/databind/json/JsonMapper.java index 2a77252018..ed2e996ab7 100644 --- a/src/main/java/com/fasterxml/jackson/databind/json/JsonMapper.java +++ b/src/main/java/com/fasterxml/jackson/databind/json/JsonMapper.java @@ -1,18 +1,16 @@ package com.fasterxml.jackson.databind.json; -import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.Version; +import com.fasterxml.jackson.core.json.JsonFactory; import com.fasterxml.jackson.core.json.JsonReadFeature; import com.fasterxml.jackson.core.json.JsonWriteFeature; - import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.cfg.MapperBuilder; +import com.fasterxml.jackson.databind.cfg.MapperBuilderState; import com.fasterxml.jackson.databind.cfg.PackageVersion; /** - * JSON-format specific {@link ObjectMapper} implementation. - * - * @since 2.10 + * JSON-specific {@link ObjectMapper} implementation. */ public class JsonMapper extends ObjectMapper { @@ -21,68 +19,103 @@ public class JsonMapper extends ObjectMapper /** * Base implementation for "Vanilla" {@link ObjectMapper}, used with * JSON dataformat backend. - * - * @since 2.10 */ public static class Builder extends MapperBuilder { - public Builder(JsonMapper m) { - super(m); + public Builder(JsonFactory f) { + super(f); + } + + public Builder(StateImpl state) { + super(state); } - public Builder enable(JsonReadFeature... features) { + @Override + public JsonMapper build() { + return new JsonMapper(this); + } + + @Override + protected MapperBuilderState _saveState() { + return new StateImpl(this); + } + + /* + /****************************************************************** + /* Format features + /****************************************************************** + */ + + public Builder enable(JsonReadFeature... features) { for (JsonReadFeature f : features) { - _mapper.enable(f.mappedFeature()); + _formatReadFeatures |= f.getMask(); } return this; } public Builder disable(JsonReadFeature... features) { for (JsonReadFeature f : features) { - _mapper.disable(f.mappedFeature()); + _formatReadFeatures &= ~f.getMask(); } return this; } - public Builder configure(JsonReadFeature f, boolean state) + public Builder configure(JsonReadFeature feature, boolean state) { if (state) { - _mapper.enable(f.mappedFeature()); + _formatReadFeatures |= feature.getMask(); } else { - _mapper.disable(f.mappedFeature()); + _formatReadFeatures &= ~feature.getMask(); } return this; } - public Builder enable(JsonWriteFeature... features) { + public Builder enable(JsonWriteFeature... features) { for (JsonWriteFeature f : features) { - _mapper.enable(f.mappedFeature()); + _formatWriteFeatures |= f.getMask(); } return this; } public Builder disable(JsonWriteFeature... features) { for (JsonWriteFeature f : features) { - _mapper.disable(f.mappedFeature()); + _formatWriteFeatures &= ~f.getMask(); } return this; } - public Builder configure(JsonWriteFeature f, boolean state) + public Builder configure(JsonWriteFeature feature, boolean state) { if (state) { - _mapper.enable(f.mappedFeature()); + _formatWriteFeatures |= feature.getMask(); } else { - _mapper.disable(f.mappedFeature()); + _formatWriteFeatures &= ~feature.getMask(); } return this; } + + protected static class StateImpl extends MapperBuilderState + implements java.io.Serializable // important! + { + private static final long serialVersionUID = 3L; + + public StateImpl(Builder src) { + super(src); + } + + // We also need actual instance of state as base class can not implement logic + // for reinstating mapper (via mapper builder) from state. + @Override + protected Object readResolve() { + return new Builder(this).build(); + } + } } /* - /********************************************************** + /********************************************************************** /* Life-cycle, constructors - /********************************************************** + /********************************************************************** */ public JsonMapper() { @@ -90,44 +123,53 @@ public JsonMapper() { } public JsonMapper(JsonFactory f) { - super(f); + this(new Builder(f)); } - protected JsonMapper(JsonMapper src) { - super(src); - } - - @Override - public JsonMapper copy() - { - _checkInvalidCopy(JsonMapper.class); - return new JsonMapper(this); + public JsonMapper(Builder b) { + super(b); } /* - /********************************************************** + /********************************************************************** /* Life-cycle, builders - /********************************************************** + /********************************************************************** */ public static JsonMapper.Builder builder() { - return new Builder(new JsonMapper()); + return new Builder(new JsonFactory()); } public static Builder builder(JsonFactory streamFactory) { - return new Builder(new JsonMapper(streamFactory)); + return new Builder(streamFactory); } - public JsonMapper.Builder rebuild() { - // 09-Dec-2018, tatu: Not as good as what 3.0 has wrt immutability, but best approximation - // we have for 2.x - return new Builder(this.copy()); + @SuppressWarnings("unchecked") + @Override + public JsonMapper.Builder rebuild() { + return new Builder((Builder.StateImpl)_savedBuilderState); } /* - /********************************************************** + /********************************************************************** + /* Life-cycle, shared "vanilla" (default configuration) instance + /********************************************************************** + */ + + /** + * Accessor method for getting globally shared "default" {@link JsonMapper} + * instance: one that has default configuration, no modules registered, no + * config overrides. Usable mostly when dealing "untyped" or Tree-style + * content reading and writing. + */ + public static JsonMapper shared() { + return SharedWrapper.wrapped(); + } + + /* + /********************************************************************** /* Standard method overrides - /********************************************************** + /********************************************************************** */ @Override @@ -136,24 +178,37 @@ public Version version() { } @Override - public JsonFactory getFactory() { - return _jsonFactory; + public JsonFactory tokenStreamFactory() { + return (JsonFactory) _streamFactory; } /* /********************************************************** - /* JSON-specific accessors, mutators + /* Format-specific /********************************************************** */ - // // // 25-Oct-2018, tatu: Since for 2.x these will simply map to legacy settings, - // // // we will fake them - public boolean isEnabled(JsonReadFeature f) { - return isEnabled(f.mappedFeature()); + return _deserializationConfig.hasFormatFeature(f); } public boolean isEnabled(JsonWriteFeature f) { - return isEnabled(f.mappedFeature()); + return _serializationConfig.hasFormatFeature(f); + } + + /* + /********************************************************** + /* Helper class(es) + /********************************************************** + */ + + /** + * Helper class to contain dynamically constructed "shared" instance of + * mapper, should one be needed via {@link #shared}. + */ + private final static class SharedWrapper { + private final static JsonMapper MAPPER = JsonMapper.builder().build(); + + public static JsonMapper wrapped() { return MAPPER; } } } diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/JsonSchema.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/JsonSchema.java deleted file mode 100644 index f442be42a3..0000000000 --- a/src/main/java/com/fasterxml/jackson/databind/jsonschema/JsonSchema.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.fasterxml.jackson.databind.jsonschema; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonValue; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.JsonNodeFactory; -import com.fasterxml.jackson.databind.node.ObjectNode; - -/** - * Container for a logical JSON Schema instance. - * Internally schema data is stored as a JSON Tree - * (instance of {@link JsonNode} is the root - * of schema document) - * - * @author Ryan Heaton - * @see JSON Schema - * - * @deprecated Since 2.2, we recommend use of external - * JSON Schema generator module - */ -@Deprecated -public class JsonSchema -{ - private final ObjectNode schema; - - /** - * Main constructor for schema instances. - *

    - * This is the creator constructor used by Jackson itself when - * deserializing instances. It is so-called delegating creator, - * meaning that its argument will be bound by Jackson before - * constructor gets called. - */ - @JsonCreator - public JsonSchema(ObjectNode schema) - { - this.schema = schema; - } - - /** - * Method for accessing root JSON object of the contained schema. - *

    - * Note: this method is specified with {@link JsonValue} annotation - * to represent serialization to use; same as if explicitly - * serializing returned object. - * - * @return Root node of the schema tree - */ - @JsonValue - public ObjectNode getSchemaNode() - { - return schema; - } - - @Override - public String toString() - { - return this.schema.toString(); - } - - @Override - public int hashCode() - { - return schema.hashCode(); - } - - @Override - public boolean equals(Object o) - { - if (o == this) return true; - if (o == null) return false; - if (!(o instanceof JsonSchema)) return false; - - JsonSchema other = (JsonSchema) o; - if (schema == null) { - return other.schema == null; - } - return schema.equals(other.schema); - } - - /** - * Get the default schema node. - * - * @return The default schema node. - */ - public static JsonNode getDefaultSchemaNode() - { - ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); - objectNode.put("type", "any"); - // "required" is false by default, no need to include - //objectNode.put("required", false); - return objectNode; - } - -} diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/JsonSerializableSchema.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/JsonSerializableSchema.java deleted file mode 100644 index b34ce6d355..0000000000 --- a/src/main/java/com/fasterxml/jackson/databind/jsonschema/JsonSerializableSchema.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.fasterxml.jackson.databind.jsonschema; - -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Retention; -import java.lang.annotation.ElementType; -import java.lang.annotation.Target; - -import com.fasterxml.jackson.annotation.JacksonAnnotation; - -/** - * Annotation that can be used to define JSON Schema definition for - * the annotated class. - *

    - * Note that annotation is often not needed: for example, regular - * Jackson beans that Jackson can introspect can be used without - * annotations, to produce JSON schema definition. - * - * @author Ryan Heaton - * @author Tatu Saloranta - */ -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -@JacksonAnnotation -public @interface JsonSerializableSchema -{ - /** - * Marker value used to indicate that property has "no value"; - * needed because annotations cannot have null as default - * value. - */ - public final static String NO_VALUE = "##irrelevant"; - - /** - * Property that can be used to indicate id of the type when - * generating JSON Schema; empty String indicates that no id - * is defined. - */ - public String id() default ""; - - /** - * The schema type for this JsonSerializable instance. - * Possible values: "string", "number", "boolean", "object", "array", "null", "any" - * - * @return The schema type for this JsonSerializable instance. - */ - public String schemaType() default "any"; - - /** - * If the schema type is "object", JSON definition of properties of the object as - * a String. - * - * @return The node representing the schema properties, or "##irrelevant" if irrelevant. - * - * @deprecated (since 2.1) -- support will be dropped in future, since JSON-as-String is - * fundamentally bad way for customizing anything. No direct replacements offered. - */ - @Deprecated - public String schemaObjectPropertiesDefinition() default NO_VALUE; - - /** - * If the schema type if "array", JSON definition of the schema for item types contained. - * - * @return The schema for the items in the array, or "##irrelevant" if irrelevant. - * - * @deprecated (since 2.1) -- support will be dropped in future, since JSON-as-String is - * fundamentally bad way for customizing anything. No direct replacements offered. - */ - @Deprecated - public String schemaItemDefinition() default NO_VALUE; -} diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/SchemaAware.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/SchemaAware.java deleted file mode 100644 index f3505cd37d..0000000000 --- a/src/main/java/com/fasterxml/jackson/databind/jsonschema/SchemaAware.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.fasterxml.jackson.databind.jsonschema; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.SerializerProvider; - -import java.lang.reflect.Type; - -/** - * Marker interface for schema-aware serializers. - */ -public interface SchemaAware -{ - /** - * Get the representation of the schema to which this serializer will conform. - * - * @param provider The serializer provider. - * @param typeHint A hint about the type. - * @return Json-schema for this serializer. - */ - public JsonNode getSchema(SerializerProvider provider, Type typeHint) - throws JsonMappingException; - - /** - * Get the representation of the schema to which this serializer will conform. - * - * @param provider The serializer provider. - * @param isOptional Is the type optional - * @param typeHint A hint about the type. - * @return Json-schema for this serializer. - */ - public JsonNode getSchema(SerializerProvider provider, Type typeHint, boolean isOptional) - throws JsonMappingException; -} diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/package-info.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/package-info.java deleted file mode 100644 index 443dcf48f9..0000000000 --- a/src/main/java/com/fasterxml/jackson/databind/jsonschema/package-info.java +++ /dev/null @@ -1,5 +0,0 @@ -/** - * Classes needed for JSON schema support (currently just ability - * to generate schemas using serialization part of data mapping) - */ -package com.fasterxml.jackson.databind.jsonschema; diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/SubtypeResolver.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/SubtypeResolver.java index 0f6c50b6ee..a4f9c61d80 100644 --- a/src/main/java/com/fasterxml/jackson/databind/jsontype/SubtypeResolver.java +++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/SubtypeResolver.java @@ -2,7 +2,8 @@ import java.util.Collection; -import com.fasterxml.jackson.databind.AnnotationIntrospector; +import com.fasterxml.jackson.core.util.Snapshottable; + import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.cfg.MapperConfig; import com.fasterxml.jackson.databind.introspect.AnnotatedClass; @@ -13,11 +14,28 @@ * to sub-types. */ public abstract class SubtypeResolver + implements Snapshottable { /* - /********************************************************** + /********************************************************************** + /* Snapshottable + /********************************************************************** + */ + + /** + * Method that has to create a new instance that contains + * same registration information as this instance, but is not + * linked to this instance. + * + * @since 3.0 + */ + @Override + public abstract SubtypeResolver snapshot(); + + /* + /********************************************************************** /* Methods for registering external subtype definitions - /********************************************************** + /********************************************************************** */ /** @@ -25,19 +43,16 @@ public abstract class SubtypeResolver * names); for type entries without name, non-qualified class name * as used as name (unless overridden by annotation). */ - public abstract void registerSubtypes(NamedType... types); + public abstract SubtypeResolver registerSubtypes(NamedType... types); - public abstract void registerSubtypes(Class... classes); + public abstract SubtypeResolver registerSubtypes(Class... classes); + + public abstract SubtypeResolver registerSubtypes(Collection> subtypes); - /** - * @since 2.9 - */ - public abstract void registerSubtypes(Collection> subtypes); - /* - /********************************************************** + /********************************************************************** /* Subtype resolution - /********************************************************** + /********************************************************************** */ /** @@ -49,15 +64,9 @@ public abstract class SubtypeResolver * @param baseType Effective property base type to use; may differ from * actual type of property; for structured types it is content (value) type and NOT * structured type. - * - * @since 2.6 */ - public Collection collectAndResolveSubtypesByClass(MapperConfig config, - AnnotatedMember property, JavaType baseType) { - // for backwards compatibility... - return collectAndResolveSubtypes(property, config, - config.getAnnotationIntrospector(), baseType); - } + public abstract Collection collectAndResolveSubtypesByClass(MapperConfig config, + AnnotatedMember property, JavaType baseType); /** * Method for finding out all reachable subtypes for given type, @@ -67,14 +76,9 @@ public Collection collectAndResolveSubtypesByClass(MapperConfig co * @param baseType Effective property base type to use; may differ from * actual type of property; for structured types it is content (value) type and NOT * structured type. - * - * @since 2.6 */ - public Collection collectAndResolveSubtypesByClass(MapperConfig config, - AnnotatedClass baseType) { - // for backwards compatibility... - return collectAndResolveSubtypes(baseType, config, config.getAnnotationIntrospector()); - } + public abstract Collection collectAndResolveSubtypesByClass(MapperConfig config, + AnnotatedClass baseType); /** * Method for finding out all reachable subtypes for a property specified @@ -85,15 +89,9 @@ public Collection collectAndResolveSubtypesByClass(MapperConfig co * @param baseType Effective property base type to use; may differ from * actual type of property; for structured types it is content (value) type and NOT * structured type. - * - * @since 2.6 */ - public Collection collectAndResolveSubtypesByTypeId(MapperConfig config, - AnnotatedMember property, JavaType baseType) { - // for backwards compatibility... - return collectAndResolveSubtypes(property, config, - config.getAnnotationIntrospector(), baseType); - } + public abstract Collection collectAndResolveSubtypesByTypeId(MapperConfig config, + AnnotatedMember property, JavaType baseType); /** * Method for finding out all reachable subtypes for given type, @@ -103,42 +101,7 @@ public Collection collectAndResolveSubtypesByTypeId(MapperConfig c * @param baseType Effective property base type to use; may differ from * actual type of property; for structured types it is content (value) type and NOT * structured type. - * - * @since 2.6 - */ - public Collection collectAndResolveSubtypesByTypeId(MapperConfig config, - AnnotatedClass baseType) { - // for backwards compatibility... - return collectAndResolveSubtypes(baseType, config, config.getAnnotationIntrospector()); - } - - /* - /********************************************************** - /* Deprecated methods - /********************************************************** - */ - - /** - * @deprecated Since 2.6 Use either - * {@link #collectAndResolveSubtypesByClass(MapperConfig, AnnotatedMember, JavaType)} - * or {@link #collectAndResolveSubtypesByTypeId(MapperConfig, AnnotatedMember, JavaType)} - * instead. - */ - @Deprecated - public Collection collectAndResolveSubtypes(AnnotatedMember property, - MapperConfig config, AnnotationIntrospector ai, JavaType baseType) { - return collectAndResolveSubtypesByClass(config, property, baseType); - } - - /** - * @deprecated Since 2.6 Use either - * {@link #collectAndResolveSubtypesByClass(MapperConfig, AnnotatedClass)} - * or {@link #collectAndResolveSubtypesByTypeId(MapperConfig, AnnotatedClass)} - * instead. */ - @Deprecated - public Collection collectAndResolveSubtypes(AnnotatedClass baseType, - MapperConfig config, AnnotationIntrospector ai) { - return collectAndResolveSubtypesByClass(config, baseType); - } + public abstract Collection collectAndResolveSubtypesByTypeId(MapperConfig config, + AnnotatedClass baseType); } diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeDeserializer.java index b5e265fd64..b244266050 100644 --- a/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeDeserializer.java @@ -141,7 +141,7 @@ public static Object deserializeIfNatural(JsonParser p, DeserializationContext c public static Object deserializeIfNatural(JsonParser p, DeserializationContext ctxt, Class base) throws IOException { - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == null) { return null; } diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeIdResolver.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeIdResolver.java index a7e7e94b45..9ef5c655e2 100644 --- a/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeIdResolver.java +++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeIdResolver.java @@ -69,15 +69,11 @@ public interface TypeIdResolver /** * Method called to resolve type from given type identifier. - * - * @since 2.5 (throws clause added in 2.8) */ public JavaType typeFromId(DatabindContext context, String id) throws IOException; /** * Method called for error-reporting and diagnostics purposes. - * - * @since 2.7 -- but since 2.5 has existed in {@link com.fasterxml.jackson.databind.jsontype.impl.TypeIdResolverBase} */ public String getDescForKnownTypeIds(); diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeResolverBuilder.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeResolverBuilder.java index 7878dbaa16..cd7a91ad0e 100644 --- a/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeResolverBuilder.java +++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeResolverBuilder.java @@ -3,9 +3,10 @@ import java.util.Collection; import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.JsonTypeInfo.As; + import com.fasterxml.jackson.databind.DeserializationConfig; import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.SerializationConfig; /** @@ -15,8 +16,7 @@ * handling type information embedded in JSON to allow for safe * polymorphic type handling. *

    - * Builder is first initialized by calling {@link #init} method, and then - * configured using 'set' methods like {@link #inclusion}. + * Builder is first initialized by calling {@link #init} method. * Finally, after calling all configuration methods, * {@link #buildTypeSerializer} or {@link #buildTypeDeserializer} * will be called to get actual type resolver constructed @@ -40,9 +40,9 @@ public interface TypeResolverBuilder> { /* - /********************************************************** + /********************************************************************** /* Accessors - /********************************************************** + /********************************************************************** */ /** @@ -53,9 +53,9 @@ public interface TypeResolverBuilder> public Class getDefaultImpl(); /* - /********************************************************** + /********************************************************************** /* Actual builder methods - /********************************************************** + /********************************************************************** */ /** @@ -66,7 +66,8 @@ public interface TypeResolverBuilder> * handle; super type of all types it will be used for. */ public TypeSerializer buildTypeSerializer(SerializationConfig config, - JavaType baseType, Collection subtypes); + JavaType baseType, Collection subtypes) + throws JsonMappingException; /** * Method for building type deserializer based on current configuration @@ -77,61 +78,32 @@ public TypeSerializer buildTypeSerializer(SerializationConfig config, * @param subtypes Known subtypes of the base type. */ public TypeDeserializer buildTypeDeserializer(DeserializationConfig config, - JavaType baseType, Collection subtypes); + JavaType baseType, Collection subtypes) + throws JsonMappingException; /* - /********************************************************** - /* Initialization method(s) that must be called before other - /* configuration - /********************************************************** + /********************************************************************** + /* Initialization method(s) that must be called before other configuration + /********************************************************************** */ /** * Initialization method that is called right after constructing - * the builder instance. + * the builder instance, in cases where information could not be + * passed directly (for example when instantiated for an annotation) * - * @param idType Which type metadata is used - * @param res (optional) Custom type id resolver used, if any + * @param settings Configuration settings to apply. * * @return Resulting builder instance (usually this builder, * but not necessarily) */ - public T init(JsonTypeInfo.Id idType, TypeIdResolver res); - + public T init(JsonTypeInfo.Value settings, TypeIdResolver res); + /* - /********************************************************** + /********************************************************************** /* Methods for configuring resolver to build - /********************************************************** - */ - - /** - * Method for specifying mechanism to use for including type metadata - * in JSON. - * If not explicitly called, setting defaults to - * {@link As#PROPERTY}. - * - * @param includeAs Mechanism used for including type metadata in JSON - * - * @return Resulting builder instance (usually this builder, - * but may be a newly constructed instance for immutable builders} - */ - public T inclusion(As includeAs); - - /** - * Method for specifying name of property used for including type - * information. Not used for all inclusion mechanisms; - * usually only used with {@link As#PROPERTY}. - *

    - * If not explicitly called, name of property to use is based on - * defaults for {@link com.fasterxml.jackson.annotation.JsonTypeInfo.Id} configured. - * - * @param propName Name of JSON property to use for including - * type information - * - * @return Resulting builder instance (usually this builder, - * but may be a newly constructed instance for immutable builders} + /********************************************************************** */ - public T typeProperty(String propName); /** * Method for specifying default implementation to use if type id @@ -141,15 +113,4 @@ public TypeDeserializer buildTypeDeserializer(DeserializationConfig config, * but may be a newly constructed instance for immutable builders} */ public T defaultImpl(Class defaultImpl); - - /** - * Method for specifying whether type id should be visible to - * {@link com.fasterxml.jackson.databind.JsonDeserializer}s or not. - * - * @return Resulting builder instance (usually this builder, - * but may be a newly constructed instance for immutable builders} - * - * @since 2.0 - */ - public T typeIdVisibility(boolean isVisible); } diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeResolverProvider.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeResolverProvider.java new file mode 100644 index 0000000000..0f6f3508ea --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeResolverProvider.java @@ -0,0 +1,275 @@ +package com.fasterxml.jackson.databind.jsontype; + +import java.util.Collection; + +import com.fasterxml.jackson.annotation.*; + +import com.fasterxml.jackson.databind.*; + +import com.fasterxml.jackson.databind.cfg.MapperConfig; +import com.fasterxml.jackson.databind.introspect.Annotated; +import com.fasterxml.jackson.databind.introspect.AnnotatedClass; +import com.fasterxml.jackson.databind.introspect.AnnotatedMember; +import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder; + +/** + * Abstraction used for allowing construction and registration of custom + * {@link TypeResolverBuilder}s, used in turn for actual construction of + * {@link com.fasterxml.jackson.databind.jsontype.TypeSerializer}s + * and {@link com.fasterxml.jackson.databind.jsontype.TypeDeserializer}s + * for Polymorphic type handling. + * At this point contains both API and default implementation. + * + * @since 3.0 + */ +public class TypeResolverProvider + implements java.io.Serializable +{ + private static final long serialVersionUID = 3L; + + protected final static StdTypeResolverBuilder NO_RESOLVER = StdTypeResolverBuilder.noTypeInfoBuilder(); + + /* + /********************************************************************** + /* Public API, for class + /********************************************************************** + */ + + /** + * Method for checking if given class has annotations that indicate + * that specific type resolver is to be used for handling instances of given type. + * This includes not only + * instantiating resolver builder, but also configuring it based on + * relevant annotations (not including ones checked with a call to + * {@code findSubtypes()} + * + * @param baseType Base java type of value for which resolver is to be found + * @param classInfo Introspected annotation information for the class (type) + * + * @return Type resolver builder for given type, if one found; null if none + */ + public TypeSerializer findTypeSerializer(SerializerProvider ctxt, + JavaType baseType, AnnotatedClass classInfo) + throws JsonMappingException + { + final SerializationConfig config = ctxt.getConfig(); + TypeResolverBuilder b = _findTypeResolver(config, classInfo, baseType); + // Ok: if there is no explicit type info handler, we may want to + // use a default. If so, config object knows what to use. + Collection subtypes = null; + if (b == null) { + b = config.getDefaultTyper(baseType); + } else { + subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByClass(config, classInfo); + } + if (b == null) { + return null; + } + // 10-Jun-2015, tatu: Since not created for Bean Property, no need for post-processing + // wrt EXTERNAL_PROPERTY + return b.buildTypeSerializer(config, baseType, subtypes); + } + + public TypeDeserializer findTypeDeserializer(DeserializationContext ctxt, + JavaType baseType, AnnotatedClass classInfo) + throws JsonMappingException + { + final DeserializationConfig config = ctxt.getConfig(); + TypeResolverBuilder b = _findTypeResolver(config, classInfo, baseType); + + // Ok: if there is no explicit type info handler, we may want to + // use a default. If so, config object knows what to use. + Collection subtypes = null; + if (b == null) { + b = config.getDefaultTyper(baseType); + if (b == null) { + return null; + } + } else { + subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByTypeId(config, classInfo); + } + // May need to figure out default implementation, if none found yet + // (note: check for abstract type is not 100% mandatory, more of an optimization) + if ((b.getDefaultImpl() == null) && baseType.isAbstract()) { + JavaType defaultType = config.mapAbstractType(baseType); + if ((defaultType != null) && !defaultType.hasRawClass(baseType.getRawClass())) { + b = b.defaultImpl(defaultType.getRawClass()); + } + } + return b.buildTypeDeserializer(config, baseType, subtypes); + } + + /* + /********************************************************************** + /* Public API, for property + /********************************************************************** + */ + + public TypeSerializer findPropertyTypeSerializer(SerializerProvider ctxt, + AnnotatedMember accessor, JavaType baseType) + throws JsonMappingException + { + TypeResolverBuilder b = null; + final SerializationConfig config = ctxt.getConfig(); + // As per definition of @JsonTypeInfo, check for annotation only for non-container types + if (!baseType.isContainerType() && !baseType.isReferenceType()) { + b = _findTypeResolver(config, accessor, baseType); + } + // No annotation on property? Then base it on actual type (and further, default typing if need be) + if (b == null) { + BeanDescription bean = ctxt.introspectClassAnnotations(baseType); + return findTypeSerializer(ctxt, baseType, bean.getClassInfo()); + } + Collection subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByClass( + config, accessor, baseType); + // 10-Jun-2015, tatu: Since not created for Bean Property, no need for post-processing + // wrt EXTERNAL_PROPERTY + return b.buildTypeSerializer(config, baseType, subtypes); + } + + public TypeDeserializer findPropertyTypeDeserializer(DeserializationContext ctxt, + AnnotatedMember accessor, JavaType baseType) + throws JsonMappingException + { + TypeResolverBuilder b = null; + final DeserializationConfig config = ctxt.getConfig(); + // As per definition of @JsonTypeInfo, check for annotation only for non-container types + if (!baseType.isContainerType() && !baseType.isReferenceType()) { + b = _findTypeResolver(config, accessor, baseType); + } + // No annotation on property? Then base it on actual type (and further, default typing if need be) + if (b == null) { + BeanDescription bean = ctxt.introspectClassAnnotations(baseType); + return findTypeDeserializer(ctxt, baseType, bean.getClassInfo()); + } + Collection subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByTypeId(config, + accessor, baseType); + // May need to figure out default implementation, if none found yet + // (note: check for abstract type is not 100% mandatory, more of an optimization) + if ((b.getDefaultImpl() == null) && baseType.isAbstract()) { + JavaType defaultType = config.mapAbstractType(baseType); + if ((defaultType != null) && !defaultType.hasRawClass(baseType.getRawClass())) { + b = b.defaultImpl(defaultType.getRawClass()); + } + } + return b.buildTypeDeserializer(config, baseType, subtypes); + } + + public TypeSerializer findPropertyContentTypeSerializer(SerializerProvider ctxt, + AnnotatedMember accessor, JavaType containerType) + throws JsonMappingException + { + final JavaType contentType = containerType.getContentType(); + // First: let's ensure property is a container type: caller should have + // verified but just to be sure + if (contentType == null) { + throw new IllegalArgumentException("Must call method with a container or reference type (got "+containerType+")"); + } + final SerializationConfig config = ctxt.getConfig(); + TypeResolverBuilder b = _findTypeResolver(config, accessor, containerType); + // No annotation on property? Then base it on actual type (and further, default typing if need be) + if (b == null) { + BeanDescription beanDesc = ctxt.introspectClassAnnotations(contentType.getRawClass()); + return findTypeSerializer(ctxt, contentType, beanDesc.getClassInfo()); + } + Collection subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByClass( + config, accessor, contentType); + return b.buildTypeSerializer(config, contentType, subtypes); + } + + public TypeDeserializer findPropertyContentTypeDeserializer(DeserializationContext ctxt, + AnnotatedMember accessor, JavaType containerType) + throws JsonMappingException + { + final JavaType contentType = containerType.getContentType(); + final DeserializationConfig config = ctxt.getConfig(); + // First: let's ensure property is a container type: caller should have + // verified but just to be sure + if (contentType == null) { + throw new IllegalArgumentException("Must call method with a container or reference type (got "+containerType+")"); + } + TypeResolverBuilder b = _findTypeResolver(config, accessor, containerType); + if (b == null) { + BeanDescription beanDesc = ctxt.introspectClassAnnotations(contentType); + return findTypeDeserializer(ctxt, contentType, beanDesc.getClassInfo()); + } + Collection subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByTypeId(config, + accessor, contentType); + // May need to figure out default implementation, if none found yet + // (note: check for abstract type is not 100% mandatory, more of an optimization) + if ((b.getDefaultImpl() == null) && contentType.isAbstract()) { + JavaType defaultType = config.mapAbstractType(contentType); + if ((defaultType != null) && !defaultType.hasRawClass(contentType.getRawClass())) { + b = b.defaultImpl(defaultType.getRawClass()); + } + } + return b.buildTypeDeserializer(config, contentType, subtypes); + } + + /* + /********************************************************************** + /* Helper methods + /********************************************************************** + */ + + protected TypeResolverBuilder _findTypeResolver(MapperConfig config, + Annotated ann, JavaType baseType) + { + final AnnotationIntrospector ai = config.getAnnotationIntrospector(); + JsonTypeInfo.Value typeInfo = ai.findPolymorphicTypeInfo(config, ann); + + // First: maybe we have explicit type resolver? + TypeResolverBuilder b; + Object customResolverOb = ai.findTypeResolverBuilder(config, ann); + if (customResolverOb != null) { + // 08-Mar-2018, tatu: Should `NONE` block custom one? Or not? + if ((typeInfo != null) && (typeInfo.getIdType() == JsonTypeInfo.Id.NONE)) { + return null; + } + if (customResolverOb instanceof Class) { + @SuppressWarnings("unchecked") + Class> cls = (Class>) customResolverOb; + b = config.typeResolverBuilderInstance(ann, cls); + } else { + b = (TypeResolverBuilder) customResolverOb; + } + } else { // if not, use standard one, but only if indicated by annotations + if (typeInfo == null) { + return null; + } + // bit special; must return 'marker' to block use of default typing: + if (typeInfo.getIdType() == JsonTypeInfo.Id.NONE) { + return NO_RESOLVER; + } + // 13-Aug-2011, tatu: One complication; external id + // only works for properties; so if declared for a Class, we will need + // to map it to "PROPERTY" instead of "EXTERNAL_PROPERTY" + if (ann instanceof AnnotatedClass) { + JsonTypeInfo.As inclusion = typeInfo.getInclusionType(); + if (inclusion == JsonTypeInfo.As.EXTERNAL_PROPERTY && (ann instanceof AnnotatedClass)) { + typeInfo = typeInfo.withInclusionType(JsonTypeInfo.As.PROPERTY); + } + } + b = _constructStdTypeResolverBuilder(config, typeInfo, baseType); + } + // Does it define a custom type id resolver? + Object customIdResolverOb = ai.findTypeIdResolver(config, ann); + TypeIdResolver idResolver = null; + + if (customIdResolverOb != null) { + if (customIdResolverOb instanceof Class) { + @SuppressWarnings("unchecked") + Class cls = (Class) customIdResolverOb; + idResolver = config.typeIdResolverInstance(ann, cls); + idResolver.init(baseType); + } + } + b = b.init(typeInfo, idResolver); + return b; + } + + protected TypeResolverBuilder _constructStdTypeResolverBuilder(MapperConfig config, + JsonTypeInfo.Value typeInfo, JavaType baseType) { + return new StdTypeResolverBuilder(typeInfo); + } +} diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeSerializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeSerializer.java index 9b6e6021ac..dc82f7baff 100644 --- a/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeSerializer.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.core.type.WritableTypeId; import com.fasterxml.jackson.core.util.VersionUtil; import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.annotation.JsonTypeInfo; /** @@ -15,19 +16,13 @@ * {@link com.fasterxml.jackson.databind.JsonSerializer}s using proper contextual * calls, to add type information using mechanism type serializer was * configured with. - *

    - * NOTE: version 2.9 contains significant attempt at simplifying interface, - * as well as giving format implementation (via {@link JsonGenerator}) more - * control over actual serialization details. Minor changes are required to change - * call pattern so that return value of "prefix" write needs to be passed to "suffix" - * write. */ public abstract class TypeSerializer { /* - /********************************************************** + /********************************************************************** /* Initialization - /********************************************************** + /********************************************************************** */ /** @@ -36,15 +31,16 @@ public abstract class TypeSerializer * (as is the case for bean properties), or values contained * (for {@link java.util.Collection} or {@link java.util.Map} * valued properties). - * - * @since 2.0 + *

    + * NOTE: since 3.0 has received context object as first argument. */ - public abstract TypeSerializer forProperty(BeanProperty prop); + public abstract TypeSerializer forProperty(SerializerProvider ctxt, + BeanProperty prop); /* - /********************************************************** + /********************************************************************** /* Introspection - /********************************************************** + /********************************************************************** */ /** @@ -67,9 +63,9 @@ public abstract class TypeSerializer public abstract TypeIdResolver getTypeIdResolver(); /* - /********************************************************** - /* Type serialization methods: new (2.9) - /********************************************************** + /********************************************************************** + /* Type serialization methods + /********************************************************************** */ /** @@ -130,229 +126,10 @@ public WritableTypeId typeId(Object value, Class typeForId, * * @param g Generator to use for outputting type id and possible wrapping * @param typeId Details of what type id is to be written, how. - * - * @since 2.9 */ public abstract WritableTypeId writeTypePrefix(JsonGenerator g, WritableTypeId typeId) throws IOException; - /** - * Method that should be called after {@link #writeTypePrefix(JsonGenerator, WritableTypeId)} - * and matching value write have called, passing {@link WritableTypeId} returned. - * Usual idiom is: - *

    -     * // Indicator generator that type identifier may be needed; generator may write
    -     * // one as suggested, modify information, or take some other action 
    -     * // NOTE! For Object/Array types, this will ALSO write start marker!
    -     * WritableTypeId typeIdDef = typeSer.writeTypePrefix(gen,
    -     *          typeSer.typeId(value, JsonToken.START_OBJECT));
    -     *
    -     * // serializing actual value for which TypeId may have been written... like
    -     * // NOTE: do NOT write START_OBJECT before OR END_OBJECT after:
    -     * g.writeStringField("message", "Hello, world!"
    -     *
    -     * // matching type suffix call to let generator chance to add suffix, if any
    -     * // NOTE! For Object/Array types, this will ALSO write end marker!
    -     * typeSer.writeTypeSuffix(gen, typeIdDef);
    -     *
    - * - * @since 2.9 - */ public abstract WritableTypeId writeTypeSuffix(JsonGenerator g, WritableTypeId typeId) throws IOException; - - /* - /********************************************************** - /* Legacy type serialization methods - /********************************************************** - */ - - /** - * DEPRECATED: now equivalent to: - *{@code writeTypePrefix(g, typeId(value, JsonToken.VALUE_STRING));}. - * See {@link #writeTypePrefix} for more info. - * - * @deprecated Since 2.9 use {@link #writeTypePrefix(JsonGenerator, WritableTypeId)} instead - */ - @Deprecated // since 2.9 - public void writeTypePrefixForScalar(Object value, JsonGenerator g) throws IOException { - writeTypePrefix(g, typeId(value, JsonToken.VALUE_STRING)); - } - - /** - * DEPRECATED: now equivalent to: - *{@code writeTypePrefix(g, typeId(value, JsonToken.START_OBJECT));}. - * See {@link #writeTypePrefix} for more info. - * - * @deprecated Since 2.9 use {@link #writeTypePrefix(JsonGenerator, WritableTypeId)} instead - */ - @Deprecated // since 2.9 - public void writeTypePrefixForObject(Object value, JsonGenerator g) throws IOException { - writeTypePrefix(g, typeId(value, JsonToken.START_OBJECT)); - } - - /** - * DEPRECATED: now equivalent to: - *{@code writeTypePrefix(g, typeId(value, JsonToken.START_ARRAY));}. - * See {@link #writeTypePrefix} for more info. - * - * @deprecated Since 2.9 use {@link #writeTypePrefix(JsonGenerator, WritableTypeId)} instead - */ - @Deprecated // since 2.9 - public void writeTypePrefixForArray(Object value, JsonGenerator g) throws IOException { - writeTypePrefix(g, typeId(value, JsonToken.START_ARRAY)); - } - - /** - * DEPRECATED: now equivalent to: - *{@code writeTypeSuffix(g, typeId(value, JsonToken.VALUE_STRING));}. - * See {@link #writeTypeSuffix} for more info. - * - * @deprecated Since 2.9 use {@link #writeTypeSuffix(JsonGenerator, WritableTypeId)} instead - */ - @Deprecated // since 2.9 - public void writeTypeSuffixForScalar(Object value, JsonGenerator g) throws IOException { - _writeLegacySuffix(g, typeId(value, JsonToken.VALUE_STRING)); - } - - /** - * DEPRECATED: now equivalent to: - *{@code writeTypeSuffix(g, typeId(value, JsonToken.START_OBJECT));}. - * See {@link #writeTypeSuffix} for more info. - * - * @deprecated Since 2.9 use {@link #writeTypeSuffix(JsonGenerator, WritableTypeId)} instead - */ - @Deprecated // since 2.9 - public void writeTypeSuffixForObject(Object value, JsonGenerator g) throws IOException { - _writeLegacySuffix(g, typeId(value, JsonToken.START_OBJECT)); - } - - /** - * DEPRECATED: now equivalent to: - *{@code writeTypeSuffix(g, typeId(value, JsonToken.START_ARRAY));}. - * See {@link #writeTypeSuffix} for more info. - * - * @deprecated Since 2.9 use {@link #writeTypeSuffix(JsonGenerator, WritableTypeId)} instead - */ - @Deprecated // since 2.9 - public void writeTypeSuffixForArray(Object value, JsonGenerator g) throws IOException { - _writeLegacySuffix(g, typeId(value, JsonToken.START_ARRAY)); - } - - /** - * DEPRECATED: now equivalent to: - *{@code writeTypePrefix(g, typeId(value, type, JsonToken.VALUE_STRING));}. - * See {@link #writeTypePrefix} for more info. - * - * @deprecated Since 2.9 use {@link #writeTypePrefix(JsonGenerator, WritableTypeId)} instead - */ - @Deprecated // since 2.9 - public void writeTypePrefixForScalar(Object value, JsonGenerator g, Class type) throws IOException { - writeTypePrefix(g, typeId(value, type, JsonToken.VALUE_STRING)); - } - - /** - * DEPRECATED: now equivalent to: - *{@code writeTypePrefix(g, typeId(value, type, JsonToken.START_OBJECT));}. - * See {@link #writeTypePrefix} for more info. - * - * @deprecated Since 2.9 use {@link #writeTypePrefix(JsonGenerator, WritableTypeId)} instead - */ - @Deprecated // since 2.9 - public void writeTypePrefixForObject(Object value, JsonGenerator g, Class type) throws IOException { - writeTypePrefix(g, typeId(value, type, JsonToken.START_OBJECT)); - } - - /** - * DEPRECATED: now equivalent to: - *{@code writeTypePrefix(g, typeId(value, type, JsonToken.START_ARRAY));}. - * See {@link #writeTypePrefix} for more info. - * - * @deprecated Since 2.9 use {@link #writeTypePrefix(JsonGenerator, WritableTypeId)} instead - */ - @Deprecated // since 2.9 - public void writeTypePrefixForArray(Object value, JsonGenerator g, Class type) throws IOException { - writeTypePrefix(g, typeId(value, type, JsonToken.START_ARRAY)); - } - - /* - /********************************************************** - /* Type serialization methods with type id override - /********************************************************** - */ - - /** - * DEPRECATED: now equivalent to: - *{@code writeTypePrefix(g, typeId(value, JsonToken.VALUE_STRING, typeId));}. - * See {@link #writeTypePrefix} for more info. - * - * @deprecated Since 2.9 use {@link #writeTypePrefix(JsonGenerator, WritableTypeId)} instead - */ - @Deprecated // since 2.9 - public void writeCustomTypePrefixForScalar(Object value, JsonGenerator g, String typeId) throws IOException { - writeTypePrefix(g, typeId(value, JsonToken.VALUE_STRING, typeId)); - } - - /** - * DEPRECATED: now equivalent to: - *{@code writeTypePrefix(g, typeId(value, JsonToken.START_OBJECT, typeId));}. - * See {@link #writeTypePrefix} for more info. - * - * @deprecated Since 2.9 use {@link #writeTypePrefix(JsonGenerator, WritableTypeId)} instead - */ - @Deprecated // since 2.9 - public void writeCustomTypePrefixForObject(Object value, JsonGenerator g, String typeId) throws IOException { - writeTypePrefix(g, typeId(value, JsonToken.START_OBJECT, typeId)); - } - - /** - * DEPRECATED: now equivalent to: - *{@code writeTypePrefix(g, typeId(value, JsonToken.START_ARRAY, typeId));}. - * See {@link #writeTypePrefix} for more info. - * - * @deprecated Since 2.9 use {@link #writeTypePrefix(JsonGenerator, WritableTypeId)} instead - */ - @Deprecated // since 2.9 - public void writeCustomTypePrefixForArray(Object value, JsonGenerator g, String typeId) throws IOException { - writeTypePrefix(g, typeId(value, JsonToken.START_ARRAY, typeId)); - } - - /** - * @deprecated Since 2.9 use {@link #writeTypeSuffix(JsonGenerator, WritableTypeId)} instead - */ - @Deprecated // since 2.9 - public void writeCustomTypeSuffixForScalar(Object value, JsonGenerator g, String typeId) throws IOException { - _writeLegacySuffix(g, typeId(value, JsonToken.VALUE_STRING, typeId)); - } - - /** - * @deprecated Since 2.9 use {@link #writeTypeSuffix(JsonGenerator, WritableTypeId)} instead - */ - @Deprecated // since 2.9 - public void writeCustomTypeSuffixForObject(Object value, JsonGenerator g, String typeId) throws IOException { - _writeLegacySuffix(g, typeId(value, JsonToken.START_OBJECT, typeId)); - } - - /** - * @deprecated Since 2.9 use {@link #writeTypeSuffix(JsonGenerator, WritableTypeId)} instead - */ - @Deprecated // since 2.9 - public void writeCustomTypeSuffixForArray(Object value, JsonGenerator g, String typeId) throws IOException { - _writeLegacySuffix(g, typeId(value, JsonToken.START_ARRAY, typeId)); - } - - /** - * Helper method needed for backwards compatibility: since original type id - * can not be routed through completely, we have to reverse-engineer likely - * setting before calling suffix. - * - * @since 2.9 - */ - protected final void _writeLegacySuffix(JsonGenerator g, - WritableTypeId typeId) throws IOException - { - // most likely logic within generator is this: - typeId.wrapperWritten = !g.canWriteTypeId(); - writeTypeSuffix(g, typeId); - } } 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 250db2bd4a..4f27617821 100644 --- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeDeserializer.java @@ -23,9 +23,6 @@ public class AsArrayTypeDeserializer { private static final long serialVersionUID = 1L; - /** - * @since 2.8 - */ public AsArrayTypeDeserializer(JavaType bt, TypeIdResolver idRes, String typePropertyName, boolean typeIdVisible, JavaType defaultImpl) { @@ -101,16 +98,16 @@ protected Object _deserialize(JsonParser p, DeserializationContext ctxt) throws // internal and external properties // TODO: but does it need to be injected in external case? Why not? && !_usesExternalId() - && p.getCurrentToken() == JsonToken.START_OBJECT) { + && p.isExpectedStartObjectToken()) { // but what if there's nowhere to add it in? Error? Or skip? For now, skip. - TokenBuffer tb = new TokenBuffer(null, false); + TokenBuffer tb = TokenBuffer.forInputBuffering(p, ctxt); tb.writeStartObject(); // recreate START_OBJECT tb.writeFieldName(_typePropertyName); tb.writeString(typeId); - // 02-Jul-2016, tatu: Depending on for JsonParserSequence is initialized it may + // 02-Jul-2016, tatu: Depending on how JsonParserSequence is initialized it may // try to access current token; ensure there isn't one p.clearCurrentToken(); - p = JsonParserSequence.createFlattened(false, tb.asParser(p), p); + p = JsonParserSequence.createFlattened(false, tb.asParser(ctxt, p), p); p.nextToken(); } Object value = deser.deserialize(p, ctxt); diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeSerializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeSerializer.java index 1ee6f2b8a7..18c706f9b0 100644 --- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeSerializer.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo.As; import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; /** @@ -16,7 +17,8 @@ public AsArrayTypeSerializer(TypeIdResolver idRes, BeanProperty property) { } @Override - public AsArrayTypeSerializer forProperty(BeanProperty prop) { + public AsArrayTypeSerializer forProperty(SerializerProvider ctxt, + BeanProperty prop) { return (_property == prop) ? this : new AsArrayTypeSerializer(_idResolver, prop); } diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsExistingPropertyTypeSerializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsExistingPropertyTypeSerializer.java index ec8ddaacd7..0b3deef54a 100644 --- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsExistingPropertyTypeSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsExistingPropertyTypeSerializer.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo.As; import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; /** @@ -19,7 +20,8 @@ public AsExistingPropertyTypeSerializer(TypeIdResolver idRes, } @Override - public AsExistingPropertyTypeSerializer forProperty(BeanProperty prop) { + public AsExistingPropertyTypeSerializer forProperty(SerializerProvider ctxt, + BeanProperty prop) { return (_property == prop) ? this : new AsExistingPropertyTypeSerializer(_idResolver, prop, _typePropertyName); } diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsExternalTypeSerializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsExternalTypeSerializer.java index 821b6f7aa5..3ead09bc6e 100644 --- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsExternalTypeSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsExternalTypeSerializer.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo.As; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; /** @@ -31,7 +32,8 @@ public AsExternalTypeSerializer(TypeIdResolver idRes, BeanProperty property, Str } @Override - public AsExternalTypeSerializer forProperty(BeanProperty prop) { + public AsExternalTypeSerializer forProperty(SerializerProvider ctxt, + BeanProperty prop) { return (_property == prop) ? this : new AsExternalTypeSerializer(_idResolver, prop, _typePropertyName); } 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 9bfab808b4..915b5a02b0 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 @@ -74,7 +74,7 @@ public Object deserializeTypedFromObject(JsonParser p, DeserializationContext ct } // but first, sanity check to ensure we have START_OBJECT or FIELD_NAME - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == JsonToken.START_OBJECT) { t = p.nextToken(); } else if (/*t == JsonToken.START_ARRAY ||*/ t != JsonToken.FIELD_NAME) { @@ -91,7 +91,7 @@ public Object deserializeTypedFromObject(JsonParser p, DeserializationContext ct TokenBuffer tb = null; for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) { - String name = p.getCurrentName(); + String name = p.currentName(); p.nextToken(); // to point to the value if (name.equals(_typePropertyName)) { // gotcha! return _deserializeTypedForId(p, ctxt, tb); @@ -113,16 +113,16 @@ protected Object _deserializeTypedForId(JsonParser p, DeserializationContext ctx JsonDeserializer deser = _findDeserializer(ctxt, typeId); if (_typeIdVisible) { // need to merge id back in JSON input? if (tb == null) { - tb = new TokenBuffer(p, ctxt); + tb = TokenBuffer.forInputBuffering(p, ctxt); } - tb.writeFieldName(p.getCurrentName()); + tb.writeFieldName(p.currentName()); tb.writeString(typeId); } if (tb != null) { // need to put back skipped properties? // 02-Jul-2016, tatu: Depending on for JsonParserSequence is initialized it may // try to access current token; ensure there isn't one p.clearCurrentToken(); - p = JsonParserSequence.createFlattened(false, tb.asParser(p), p); + p = JsonParserSequence.createFlattened(false, tb.asParser(ctxt, p), p); } // Must point to the next value; tb had no current, jp pointed to VALUE_STRING: p.nextToken(); // to skip past String value @@ -171,7 +171,7 @@ protected Object _deserializeTypedUsingDefaultImpl(JsonParser p, } if (tb != null) { tb.writeEndObject(); - p = tb.asParser(p); + p = tb.asParser(ctxt, p); // must move to point to the first token: p.nextToken(); } @@ -187,7 +187,7 @@ public Object deserializeTypedFromAny(JsonParser p, DeserializationContext ctxt) /* Sometimes, however, we get an array wrapper; specifically * when an array or list has been serialized with type information. */ - if (p.getCurrentToken() == JsonToken.START_ARRAY) { + if (p.hasToken(JsonToken.START_ARRAY)) { return super.deserializeTypedFromArray(p, ctxt); } return deserializeTypedFromObject(p, ctxt); diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsPropertyTypeSerializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsPropertyTypeSerializer.java index 3b56f915f1..25b2f5610e 100644 --- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsPropertyTypeSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsPropertyTypeSerializer.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo.As; import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; /** @@ -24,7 +25,8 @@ public AsPropertyTypeSerializer(TypeIdResolver idRes, BeanProperty property, Str } @Override - public AsPropertyTypeSerializer forProperty(BeanProperty prop) { + public AsPropertyTypeSerializer forProperty(SerializerProvider ctxt, + BeanProperty prop) { return (_property == prop) ? this : new AsPropertyTypeSerializer(this._idResolver, prop, this._typePropertyName); } diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeDeserializer.java index 643f921c97..57332c8449 100644 --- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeDeserializer.java @@ -89,7 +89,7 @@ protected Object _deserialize(JsonParser p, DeserializationContext ctxt) throws } } // first, sanity checks - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == JsonToken.START_OBJECT) { // should always get field name, but just in case... if (p.nextToken() != JsonToken.FIELD_NAME) { @@ -105,19 +105,19 @@ protected Object _deserialize(JsonParser p, DeserializationContext ctxt) throws p.nextToken(); // Minor complication: we may need to merge type id in? - if (_typeIdVisible && p.getCurrentToken() == JsonToken.START_OBJECT) { + if (_typeIdVisible && p.isExpectedStartObjectToken()) { // but what if there's nowhere to add it in? Error? Or skip? For now, skip. - TokenBuffer tb = new TokenBuffer(null, false); + TokenBuffer tb = TokenBuffer.forInputBuffering(p, ctxt); tb.writeStartObject(); // recreate START_OBJECT tb.writeFieldName(_typePropertyName); tb.writeString(typeId); // 02-Jul-2016, tatu: Depending on for JsonParserSequence is initialized it may // try to access current token; ensure there isn't one p.clearCurrentToken(); - p = JsonParserSequence.createFlattened(false, tb.asParser(p), p); + p = JsonParserSequence.createFlattened(false, tb.asParser(ctxt, p), p); p.nextToken(); } - + Object value = deser.deserialize(p, ctxt); // And then need the closing END_OBJECT if (p.nextToken() != JsonToken.END_OBJECT) { diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeSerializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeSerializer.java index 8f12499666..c9453d8fdb 100644 --- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeSerializer.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; import com.fasterxml.jackson.databind.util.ClassUtil; @@ -24,7 +25,8 @@ public AsWrapperTypeSerializer(TypeIdResolver idRes, BeanProperty property) { } @Override - public AsWrapperTypeSerializer forProperty(BeanProperty prop) { + public AsWrapperTypeSerializer forProperty(SerializerProvider ctxt, + BeanProperty prop) { return (_property == prop) ? this : new AsWrapperTypeSerializer(_idResolver, prop); } @@ -40,14 +42,11 @@ public AsWrapperTypeSerializer forProperty(BeanProperty prop) { /** * Helper method used to ensure that intended type id is output as something that is valid: * currently only used to ensure that `null` output is converted to an empty String. - * - * @since 2.6 */ protected String _validTypeId(String typeId) { return ClassUtil.nonNullString(typeId); } - // @since 2.9 protected final void _writeTypeId(JsonGenerator g, String typeId) throws IOException { if (typeId != null) { diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/DefaultTypeResolverBuilder.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/DefaultTypeResolverBuilder.java new file mode 100644 index 0000000000..e88598c4df --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/DefaultTypeResolverBuilder.java @@ -0,0 +1,130 @@ +package com.fasterxml.jackson.databind.jsontype.impl; + +import java.util.Collection; + +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +import com.fasterxml.jackson.core.TreeNode; + +import com.fasterxml.jackson.databind.DefaultTyping; +import com.fasterxml.jackson.databind.DeserializationConfig; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.SerializationConfig; +import com.fasterxml.jackson.databind.jsontype.NamedType; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; + +/** + * Customized {@link TypeResolverBuilder} that provides type resolver builders + * used with so-called "default typing" + * (see MapperBuilder.enableDefaultTyping() for details). + *

    + * Type resolver construction is based on configuration: implementation takes care + * of only providing builders in cases where type information should be applied. + * This is important since build calls may be sent for any and all types, and + * type information should NOT be applied to all of them. + */ +public class DefaultTypeResolverBuilder + extends StdTypeResolverBuilder + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + /** + * Definition of what types is this default typer valid for. + */ + protected final DefaultTyping _appliesFor; + + public DefaultTypeResolverBuilder(DefaultTyping t, JsonTypeInfo.As includeAs) { + _appliesFor = t; + _idType = JsonTypeInfo.Id.CLASS; + _includeAs = includeAs; + _typeProperty = _idType.getDefaultPropertyName(); + } + + public DefaultTypeResolverBuilder(DefaultTyping t, String propertyName) { + _appliesFor = t; + _idType = JsonTypeInfo.Id.CLASS; + _includeAs = JsonTypeInfo.As.PROPERTY; + _typeProperty = propertyName; + } + + public DefaultTypeResolverBuilder(DefaultTyping t, JsonTypeInfo.As includeAs, + JsonTypeInfo.Id idType, String propertyName) { + _appliesFor = t; + _idType = idType; + _includeAs = includeAs; + if (propertyName == null) { + propertyName = _idType.getDefaultPropertyName(); + } + _typeProperty = propertyName; + } + + @Override + public TypeDeserializer buildTypeDeserializer(DeserializationConfig config, + JavaType baseType, Collection subtypes) + { + return useForType(baseType) ? super.buildTypeDeserializer(config, baseType, subtypes) : null; + } + + @Override + public TypeSerializer buildTypeSerializer(SerializationConfig config, + JavaType baseType, Collection subtypes) + { + return useForType(baseType) ? super.buildTypeSerializer(config, baseType, subtypes) : null; + } + + public DefaultTypeResolverBuilder typeIdVisibility(boolean isVisible) { + _typeIdVisible = isVisible; + return this; + } + + /** + * Method called to check if the default type handler should be + * used for given type. + * Note: "natural types" (String, Boolean, Integer, Double) will never + * use typing; that is both due to them being concrete and final, + * and since actual serializers and deserializers will also ignore any + * attempts to enforce typing. + */ + public boolean useForType(JavaType t) + { + // 03-Oct-2016, tatu: As per [databind#1395], need to skip + // primitive types too, regardless + if (t.isPrimitive()) { + return false; + } + + switch (_appliesFor) { + case NON_CONCRETE_AND_ARRAYS: + while (t.isArrayType()) { + t = t.getContentType(); + } + // fall through + case OBJECT_AND_NON_CONCRETE: + // 19-Apr-2016, tatu: ReferenceType like Optional also requires similar handling: + while (t.isReferenceType()) { + t = t.getReferencedType(); + } + return t.isJavaLangObject() + || (!t.isConcrete() + // [databind#88] Should not apply to JSON tree models: + && !TreeNode.class.isAssignableFrom(t.getRawClass())); + + case NON_FINAL: + while (t.isArrayType()) { + t = t.getContentType(); + } + // 19-Apr-2016, tatu: ReferenceType like Optional also requires similar handling: + while (t.isReferenceType()) { + t = t.getReferencedType(); + } + // [databind#88] Should not apply to JSON tree models: + return !t.isFinal() && !TreeNode.class.isAssignableFrom(t.getRawClass()); + default: + //case JAVA_LANG_OBJECT: + return t.isJavaLangObject(); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/StdSubtypeResolver.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/StdSubtypeResolver.java index b705511f65..1c9384e7e0 100644 --- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/StdSubtypeResolver.java +++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/StdSubtypeResolver.java @@ -19,10 +19,22 @@ public class StdSubtypeResolver { private static final long serialVersionUID = 1L; - protected LinkedHashSet _registeredSubtypes; + protected Set _registeredSubtypes; public StdSubtypeResolver() { } + protected StdSubtypeResolver(Set reg) { + _registeredSubtypes = reg; + } + + @Override + public SubtypeResolver snapshot() { + if (_registeredSubtypes == null) { + return new StdSubtypeResolver(); + } + return new StdSubtypeResolver(new LinkedHashSet<>(_registeredSubtypes)); + } + /* /********************************************************** /* Subtype registration @@ -30,26 +42,28 @@ public StdSubtypeResolver() { } */ @Override - public void registerSubtypes(NamedType... types) { + public StdSubtypeResolver registerSubtypes(NamedType... types) { if (_registeredSubtypes == null) { - _registeredSubtypes = new LinkedHashSet(); + _registeredSubtypes = new LinkedHashSet<>(); } for (NamedType type : types) { _registeredSubtypes.add(type); } + return this; } @Override - public void registerSubtypes(Class... classes) { + public StdSubtypeResolver registerSubtypes(Class... classes) { NamedType[] types = new NamedType[classes.length]; for (int i = 0, len = classes.length; i < len; ++i) { types[i] = new NamedType(classes[i]); } registerSubtypes(types); + return this; } - @Override // since 2.9 - public void registerSubtypes(Collection> subtypes) { + @Override + public StdSubtypeResolver registerSubtypes(Collection> subtypes) { int len = subtypes.size(); NamedType[] types = new NamedType[len]; int i = 0; @@ -57,6 +71,7 @@ public void registerSubtypes(Collection> subtypes) { types[i++] = new NamedType(subtype); } registerSubtypes(types); + return this; } /* @@ -73,7 +88,7 @@ public Collection collectAndResolveSubtypesByClass(MapperConfig co // for backwards compatibility, must allow null here: Class rawBase = (baseType == null) ? property.getRawType() : baseType.getRawClass(); - HashMap collected = new HashMap(); + HashMap collected = new HashMap<>(); // start with registered subtypes (which have precedence) if (_registeredSubtypes != null) { for (NamedType subtype : _registeredSubtypes) { @@ -81,19 +96,19 @@ public Collection collectAndResolveSubtypesByClass(MapperConfig co if (rawBase.isAssignableFrom(subtype.getType())) { // yes AnnotatedClass curr = AnnotatedClassResolver.resolveWithoutSuperTypes(config, subtype.getType()); - _collectAndResolve(curr, subtype, config, ai, collected); + _collectAndResolve(config, curr, subtype, ai, collected); } } } // then annotated types for property itself if (property != null) { - Collection st = ai.findSubtypes(property); + Collection st = ai.findSubtypes(config, property); if (st != null) { for (NamedType nt : st) { AnnotatedClass ac = AnnotatedClassResolver.resolveWithoutSuperTypes(config, nt.getType()); - _collectAndResolve(ac, nt, config, ai, collected); + _collectAndResolve(config, ac, nt, ai, collected); } } } @@ -102,7 +117,7 @@ public Collection collectAndResolveSubtypesByClass(MapperConfig co AnnotatedClass ac = AnnotatedClassResolver.resolveWithoutSuperTypes(config, rawBase); // and finally subtypes via annotations from base type (recursively) - _collectAndResolve(ac, rootType, config, ai, collected); + _collectAndResolve(config, ac, rootType, ai, collected); return new ArrayList(collected.values()); } @@ -112,7 +127,7 @@ public Collection collectAndResolveSubtypesByClass(MapperConfig co AnnotatedClass type) { final AnnotationIntrospector ai = config.getAnnotationIntrospector(); - HashMap subtypes = new HashMap(); + HashMap subtypes = new HashMap<>(); // then consider registered subtypes (which have precedence over annotations) if (_registeredSubtypes != null) { Class rawBase = type.getRawType(); @@ -121,14 +136,14 @@ public Collection collectAndResolveSubtypesByClass(MapperConfig co if (rawBase.isAssignableFrom(subtype.getType())) { // yes AnnotatedClass curr = AnnotatedClassResolver.resolveWithoutSuperTypes(config, subtype.getType()); - _collectAndResolve(curr, subtype, config, ai, subtypes); + _collectAndResolve(config, curr, subtype, ai, subtypes); } } } // and then check subtypes via annotations from base type (recursively) NamedType rootType = new NamedType(type.getRawType(), null); - _collectAndResolve(type, rootType, config, ai, subtypes); - return new ArrayList(subtypes.values()); + _collectAndResolve(config, type, rootType, ai, subtypes); + return new ArrayList<>(subtypes.values()); } /* @@ -145,22 +160,22 @@ public Collection collectAndResolveSubtypesByTypeId(MapperConfig c Class rawBase = baseType.getRawClass(); // Need to keep track of classes that have been handled already - Set> typesHandled = new HashSet>(); - Map byName = new LinkedHashMap(); + Set> typesHandled = new HashSet<>(); + Map byName = new LinkedHashMap<>(); // start with lowest-precedence, which is from type hierarchy NamedType rootType = new NamedType(rawBase, null); AnnotatedClass ac = AnnotatedClassResolver.resolveWithoutSuperTypes(config, rawBase); - _collectAndResolveByTypeId(ac, rootType, config, typesHandled, byName); + _collectAndResolveByTypeId(config, ac, rootType, typesHandled, byName); // then with definitions from property if (property != null) { - Collection st = ai.findSubtypes(property); + Collection st = ai.findSubtypes(config, property); if (st != null) { for (NamedType nt : st) { ac = AnnotatedClassResolver.resolveWithoutSuperTypes(config, nt.getType()); - _collectAndResolveByTypeId(ac, nt, config, typesHandled, byName); + _collectAndResolveByTypeId(config, ac, nt, typesHandled, byName); } } } @@ -171,7 +186,7 @@ public Collection collectAndResolveSubtypesByTypeId(MapperConfig c if (rawBase.isAssignableFrom(subtype.getType())) { // yes AnnotatedClass curr = AnnotatedClassResolver.resolveWithoutSuperTypes(config, subtype.getType()); - _collectAndResolveByTypeId(curr, subtype, config, typesHandled, byName); + _collectAndResolveByTypeId(config, curr, subtype, typesHandled, byName); } } } @@ -183,11 +198,11 @@ public Collection collectAndResolveSubtypesByTypeId(MapperConfig c AnnotatedClass baseType) { final Class rawBase = baseType.getRawType(); - Set> typesHandled = new HashSet>(); - Map byName = new LinkedHashMap(); + Set> typesHandled = new HashSet<>(); + Map byName = new LinkedHashMap<>(); NamedType rootType = new NamedType(rawBase, null); - _collectAndResolveByTypeId(baseType, rootType, config, typesHandled, byName); + _collectAndResolveByTypeId(config, baseType, rootType, typesHandled, byName); if (_registeredSubtypes != null) { for (NamedType subtype : _registeredSubtypes) { @@ -195,7 +210,7 @@ public Collection collectAndResolveSubtypesByTypeId(MapperConfig c if (rawBase.isAssignableFrom(subtype.getType())) { // yes AnnotatedClass curr = AnnotatedClassResolver.resolveWithoutSuperTypes(config, subtype.getType()); - _collectAndResolveByTypeId(curr, subtype, config, typesHandled, byName); + _collectAndResolveByTypeId(config, curr, subtype, typesHandled, byName); } } } @@ -212,12 +227,12 @@ public Collection collectAndResolveSubtypesByTypeId(MapperConfig c * Method called to find subtypes for a specific type (class), using * type (class) as the unique key (in case of conflicts). */ - protected void _collectAndResolve(AnnotatedClass annotatedType, NamedType namedType, - MapperConfig config, AnnotationIntrospector ai, + protected void _collectAndResolve(MapperConfig config, AnnotatedClass annotatedType, NamedType namedType, + AnnotationIntrospector ai, HashMap collectedSubtypes) { if (!namedType.hasName()) { - String name = ai.findTypeName(annotatedType); + String name = ai.findTypeName(config, annotatedType); if (name != null) { namedType = new NamedType(namedType.getType(), name); } @@ -236,12 +251,12 @@ protected void _collectAndResolve(AnnotatedClass annotatedType, NamedType namedT } // if it wasn't, add and check subtypes recursively collectedSubtypes.put(namedType, namedType); - Collection st = ai.findSubtypes(annotatedType); + Collection st = ai.findSubtypes(config, annotatedType); if (st != null && !st.isEmpty()) { for (NamedType subtype : st) { AnnotatedClass subtypeClass = AnnotatedClassResolver.resolveWithoutSuperTypes(config, subtype.getType()); - _collectAndResolve(subtypeClass, subtype, config, ai, collectedSubtypes); + _collectAndResolve(config, subtypeClass, subtype, ai, collectedSubtypes); } } } @@ -250,13 +265,13 @@ protected void _collectAndResolve(AnnotatedClass annotatedType, NamedType namedT * Method called to find subtypes for a specific type (class), using * type id as the unique key (in case of conflicts). */ - protected void _collectAndResolveByTypeId(AnnotatedClass annotatedType, NamedType namedType, - MapperConfig config, + protected void _collectAndResolveByTypeId(MapperConfig config, + AnnotatedClass annotatedType, NamedType namedType, Set> typesHandled, Map byName) { final AnnotationIntrospector ai = config.getAnnotationIntrospector(); if (!namedType.hasName()) { - String name = ai.findTypeName(annotatedType); + String name = ai.findTypeName(config, annotatedType); if (name != null) { namedType = new NamedType(namedType.getType(), name); } @@ -267,12 +282,12 @@ protected void _collectAndResolveByTypeId(AnnotatedClass annotatedType, NamedTyp // only check subtypes if this type hadn't yet been handled if (typesHandled.add(namedType.getType())) { - Collection st = ai.findSubtypes(annotatedType); + Collection st = ai.findSubtypes(config, annotatedType); if (st != null && !st.isEmpty()) { for (NamedType subtype : st) { AnnotatedClass subtypeClass = AnnotatedClassResolver.resolveWithoutSuperTypes(config, subtype.getType()); - _collectAndResolveByTypeId(subtypeClass, subtype, config, typesHandled, byName); + _collectAndResolveByTypeId(config, subtypeClass, subtype, typesHandled, byName); } } } @@ -285,7 +300,7 @@ protected void _collectAndResolveByTypeId(AnnotatedClass annotatedType, NamedTyp protected Collection _combineNamedAndUnnamed(Class rawBase, Set> typesHandled, Map byName) { - ArrayList result = new ArrayList(byName.values()); + ArrayList result = new ArrayList<>(byName.values()); // Ok, so... we will figure out which classes have no explicitly assigned name, // by removing Classes from Set. And for remaining classes, add an anonymous 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 98e9f90e62..f7f10fc465 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 @@ -5,7 +5,6 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.annotation.NoClass; import com.fasterxml.jackson.databind.cfg.MapperConfig; import com.fasterxml.jackson.databind.jsontype.*; @@ -46,31 +45,63 @@ public class StdTypeResolverBuilder public StdTypeResolverBuilder() { } + public StdTypeResolverBuilder(JsonTypeInfo.Value settings) { + if (settings != null) { + _idType = settings.getIdType(); + if (_idType == null) { + throw new IllegalArgumentException("idType cannot be null"); + } + _includeAs = settings.getInclusionType(); + _typeProperty = _propName(settings.getPropertyName(), _idType); + _defaultImpl = settings.getDefaultImpl(); + } + } + /** * @since 2.9 */ - protected StdTypeResolverBuilder(JsonTypeInfo.Id idType, - JsonTypeInfo.As idAs, String propName) { + public StdTypeResolverBuilder(JsonTypeInfo.Id idType, + JsonTypeInfo.As idAs, String propName) + { + if (idType == null) { + throw new IllegalArgumentException("idType cannot be null"); + } _idType = idType; _includeAs = idAs; - _typeProperty = propName; + _typeProperty = _propName(propName, _idType); + } + + protected static String _propName(String propName, JsonTypeInfo.Id idType) { + if (propName == null) { + propName = idType.getDefaultPropertyName(); + } + return propName; } public static StdTypeResolverBuilder noTypeInfoBuilder() { - return new StdTypeResolverBuilder().init(JsonTypeInfo.Id.NONE, null); + return new StdTypeResolverBuilder(JsonTypeInfo.Id.NONE, null, null); } @Override - public StdTypeResolverBuilder init(JsonTypeInfo.Id idType, TypeIdResolver idRes) + public StdTypeResolverBuilder init(JsonTypeInfo.Value settings, TypeIdResolver idRes) { - // sanity checks - if (idType == null) { - throw new IllegalArgumentException("idType cannot be null"); - } - _idType = idType; _customIdResolver = idRes; - // Let's also initialize property name as per idType default - _typeProperty = idType.getDefaultPropertyName(); + + if (settings != null) { + _idType = settings.getIdType(); + if (_idType == null) { + throw new IllegalArgumentException("idType cannot be null"); + } + _includeAs = settings.getInclusionType(); + + // Let's also initialize property name as per idType default + _typeProperty = settings.getPropertyName(); + if (_typeProperty == null) { + _typeProperty = _idType.getDefaultPropertyName(); + } + _typeIdVisible = settings.getIdVisible(); + _defaultImpl = settings.getDefaultImpl(); + } return this; } @@ -101,12 +132,6 @@ public TypeSerializer buildTypeSerializer(SerializationConfig config, throw new IllegalStateException("Do not know how to construct standard type serializer for inclusion type: "+_includeAs); } - // 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 subtypes) @@ -119,7 +144,6 @@ public TypeDeserializer buildTypeDeserializer(DeserializationConfig config, } TypeIdResolver idRes = idResolver(config, baseType, subtypes, false, true); - JavaType defaultImpl = defineDefaultImpl(config, baseType); // First, method for converting type info to type id: @@ -151,14 +175,12 @@ protected JavaType defineDefaultImpl(DeserializationConfig config, JavaType base defaultImpl = null; } } else { - // 20-Mar-2016, tatu: It is important to do specialization go through - // TypeFactory to ensure proper resolution; with 2.7 and before, direct - // call to JavaType was used, but that cannot work reliably with 2.7 // 20-Mar-2016, tatu: Can finally add a check for type compatibility BUT // if so, need to add explicit checks for marker types. Not ideal, but // seems like a reasonable compromise. - if ((_defaultImpl == Void.class) - || (_defaultImpl == NoClass.class)) { + // NOTE: `Void` actually means that for unknown type id we should get `null` + // value -- NOT that there is no default implementation. + if (_defaultImpl == Void.class) { defaultImpl = config.getTypeFactory().constructType(_defaultImpl); } else { if (baseType.hasRawClass(_defaultImpl)) { // common enough to check @@ -191,41 +213,12 @@ protected JavaType defineDefaultImpl(DeserializationConfig config, JavaType base /********************************************************** */ - @Override - public StdTypeResolverBuilder inclusion(JsonTypeInfo.As includeAs) { - if (includeAs == null) { - throw new IllegalArgumentException("includeAs cannot be null"); - } - _includeAs = includeAs; - return this; - } - - /** - * Method for constructing an instance with specified type property name - * (property name to use for type id when using "as-property" inclusion). - */ - @Override - public StdTypeResolverBuilder typeProperty(String typeIdPropName) { - // ok to have null/empty; will restore to use defaults - if (typeIdPropName == null || typeIdPropName.length() == 0) { - typeIdPropName = _idType.getDefaultPropertyName(); - } - _typeProperty = typeIdPropName; - return this; - } - @Override public StdTypeResolverBuilder defaultImpl(Class defaultImpl) { _defaultImpl = defaultImpl; return this; } - @Override - public StdTypeResolverBuilder typeIdVisibility(boolean isVisible) { - _typeIdVisible = isVisible; - return this; - } - /* /********************************************************** /* Accessors diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeDeserializerBase.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeDeserializerBase.java index 58f1a5f9a4..c88d1ae364 100644 --- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeDeserializerBase.java +++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeDeserializerBase.java @@ -224,23 +224,9 @@ protected final JsonDeserializer _findDefaultImplDeserializer(Deserializ } } - /** - * Helper method called when {@link JsonParser} indicates that it can use - * so-called native type ids. Assumption from there is that only native - * type ids are to be used. - * - * @since 2.3 - */ - @Deprecated - protected Object _deserializeWithNativeTypeId(JsonParser jp, DeserializationContext ctxt) throws IOException { - return _deserializeWithNativeTypeId(jp, ctxt, jp.getTypeId()); - } - /** * Helper method called when {@link JsonParser} indicates that it can use * so-called native type ids, and such type id has been found. - * - * @since 2.4 */ protected Object _deserializeWithNativeTypeId(JsonParser jp, DeserializationContext ctxt, Object typeId) throws IOException @@ -272,8 +258,6 @@ protected Object _deserializeWithNativeTypeId(JsonParser jp, DeserializationCont * @return If it is possible to resolve type id into a {@link JsonDeserializer} * should return that deserializer; otherwise throw an exception to indicate * the problem. - * - * @since 2.8 */ protected JavaType _handleUnknownTypeId(DeserializationContext ctxt, String typeId) throws IOException diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeNameIdResolver.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeNameIdResolver.java index d607fbf07b..fd9721f39f 100644 --- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeNameIdResolver.java +++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeNameIdResolver.java @@ -99,7 +99,7 @@ protected String idFromClass(Class clazz) // can either throw an exception, or use default name... if (_config.isAnnotationProcessingEnabled()) { BeanDescription beanDesc = _config.introspectClassAnnotations(cls); - name = _config.getAnnotationIntrospector().findTypeName(beanDesc.getClassInfo()); + name = _config.getAnnotationIntrospector().findTypeName(_config, beanDesc.getClassInfo()); } if (name == null) { // And if still not found, let's choose default? diff --git a/src/main/java/com/fasterxml/jackson/databind/module/SimpleAbstractTypeResolver.java b/src/main/java/com/fasterxml/jackson/databind/module/SimpleAbstractTypeResolver.java index 3701950f5e..19ed7de404 100644 --- a/src/main/java/com/fasterxml/jackson/databind/module/SimpleAbstractTypeResolver.java +++ b/src/main/java/com/fasterxml/jackson/databind/module/SimpleAbstractTypeResolver.java @@ -80,13 +80,6 @@ public JavaType findTypeMapping(DeserializationConfig config, JavaType type) return config.getTypeFactory().constructSpecializedType(type, dst); } - @Override - @Deprecated - public JavaType resolveAbstractType(DeserializationConfig config, JavaType type){ - // never materialize anything, so: - return null; - } - @Override public JavaType resolveAbstractType(DeserializationConfig config, BeanDescription typeDesc) { diff --git a/src/main/java/com/fasterxml/jackson/databind/module/SimpleDeserializers.java b/src/main/java/com/fasterxml/jackson/databind/module/SimpleDeserializers.java index 0a9f443b1b..604cfb6895 100644 --- a/src/main/java/com/fasterxml/jackson/databind/module/SimpleDeserializers.java +++ b/src/main/java/com/fasterxml/jackson/databind/module/SimpleDeserializers.java @@ -26,8 +26,6 @@ public class SimpleDeserializers /** * Flag to help find "generic" enum deserializer, if one has been registered. - * - * @since 2.3 */ protected boolean _hasEnumDeserializer = false; @@ -39,14 +37,11 @@ public class SimpleDeserializers public SimpleDeserializers() { } - /** - * @since 2.1 - */ public SimpleDeserializers(Map,JsonDeserializer> desers) { addDeserializers(desers); } - public void addDeserializer(Class forClass, JsonDeserializer deser) + public SimpleDeserializers addDeserializer(Class forClass, JsonDeserializer deser) { ClassKey key = new ClassKey(forClass); if (_classMappings == null) { @@ -57,13 +52,11 @@ public void addDeserializer(Class forClass, JsonDeserializer if (forClass == Enum.class) { _hasEnumDeserializer = true; } + return this; } - /** - * @since 2.1 - */ @SuppressWarnings("unchecked") - public void addDeserializers(Map,JsonDeserializer> desers) + public SimpleDeserializers addDeserializers(Map,JsonDeserializer> desers) { for (Map.Entry,JsonDeserializer> entry : desers.entrySet()) { Class cls = entry.getKey(); @@ -71,6 +64,7 @@ public void addDeserializers(Map,JsonDeserializer> desers) JsonDeserializer deser = (JsonDeserializer) entry.getValue(); addDeserializer((Class) cls, deser); } + return this; } /* diff --git a/src/main/java/com/fasterxml/jackson/databind/module/SimpleModule.java b/src/main/java/com/fasterxml/jackson/databind/module/SimpleModule.java index 5de340e5ca..989df6f403 100644 --- a/src/main/java/com/fasterxml/jackson/databind/module/SimpleModule.java +++ b/src/main/java/com/fasterxml/jackson/databind/module/SimpleModule.java @@ -1,10 +1,6 @@ package com.fasterxml.jackson.databind.module; -import java.util.Collection; -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; +import java.util.*; import com.fasterxml.jackson.core.Version; import com.fasterxml.jackson.databind.*; @@ -12,6 +8,7 @@ import com.fasterxml.jackson.databind.deser.ValueInstantiator; import com.fasterxml.jackson.databind.jsontype.NamedType; import com.fasterxml.jackson.databind.ser.BeanSerializerModifier; +import com.fasterxml.jackson.databind.util.UniqueId; /** * Vanilla {@link Module} implementation that allows registration @@ -40,17 +37,31 @@ public class SimpleModule extends com.fasterxml.jackson.databind.Module implements java.io.Serializable { - private static final long serialVersionUID = 1L; // 2.5.0 + private static final long serialVersionUID = 3L; protected final String _name; protected final Version _version; + /** + * Unique id generated to avoid instances from ever matching so all + * registrations succeed. + *

    + * NOTE! Id should be {@link java.io.Serializable} to allow serialization + * of mapper instances. + * + * @since 3.0 + */ + protected final Object _id; + protected SimpleSerializers _serializers = null; protected SimpleDeserializers _deserializers = null; protected SimpleSerializers _keySerializers = null; protected SimpleKeyDeserializers _keyDeserializers = null; + protected JsonSerializer _defaultNullKeySerializer = null; + protected JsonSerializer _defaultNullValueSerializer = null; + /** * Lazily-constructed resolver used for storing mappings from * abstract classes to more specific implementing classes @@ -65,14 +76,8 @@ public class SimpleModule */ protected SimpleValueInstantiators _valueInstantiators = null; - /** - * @since 2.2 - */ protected BeanDeserializerModifier _deserializerModifier = null; - /** - * @since 2.2 - */ protected BeanSerializerModifier _serializerModifier = null; /** @@ -86,15 +91,12 @@ public class SimpleModule */ protected LinkedHashSet _subtypes = null; - /** - * @since 2.3 - */ protected PropertyNamingStrategy _namingStrategy = null; - + /* - /********************************************************** + /********************************************************************** /* Life-cycle: creation - /********************************************************** + /********************************************************************** */ /** @@ -103,14 +105,9 @@ public class SimpleModule * use actual name and version number information. */ public SimpleModule() { - // can't chain when making reference to 'this' - // note: generate different name for direct instantiation, sub-classing - _name = (getClass() == SimpleModule.class) ? - "SimpleModule-"+System.identityHashCode(this) - : getClass().getName(); - _version = Version.unknownVersion(); + this((String) null); } - + /** * Convenience constructor that will default version to * {@link Version#unknownVersion()}. @@ -124,8 +121,7 @@ public SimpleModule(String name) { * including name from {@link Version#getArtifactId()} */ public SimpleModule(Version version) { - _name = version.getArtifactId(); - _version = version; + this(version.getArtifactId(), version); } /** @@ -138,42 +134,34 @@ public SimpleModule(Version version) { * @param version Version of the module */ public SimpleModule(String name, Version version) { + this(name, version, null); + } + + public SimpleModule(String name, Version version, Object registrationId) { + if (name == null) { + name = (getClass() == SimpleModule.class) ? + "SimpleModule-"+System.identityHashCode(this) + : getClass().getName(); + } _name = name; _version = version; + _id = (registrationId == null) ? _createId() : registrationId; } - - /** - * @since 2.1 - */ - public SimpleModule(String name, Version version, - Map,JsonDeserializer> deserializers) { - this(name, version, deserializers, null); + + // 27-Feb-2018, tatu: Need to create Registration Id that never matches any + // other id, but is serializable + protected Object _createId() { + return new UniqueId(); } - /** - * @since 2.1 + /* + /********************************************************************** + /* Simple accessors + /********************************************************************** */ - public SimpleModule(String name, Version version, - List> serializers) { - this(name, version, null, serializers); - } - /** - * @since 2.1 - */ - public SimpleModule(String name, Version version, - Map,JsonDeserializer> deserializers, - List> serializers) - { - _name = name; - _version = version; - if (deserializers != null) { - _deserializers = new SimpleDeserializers(deserializers); - } - if (serializers != null) { - _serializers = new SimpleSerializers(serializers); - } - } + @Override + public Version version() { return _version; } /** * Since instances are likely to be custom, implementation returns @@ -181,89 +169,93 @@ public SimpleModule(String name, Version version, * but class name (default impl) for sub-classes. */ @Override - public Object getTypeId() { - if (getClass() == SimpleModule.class) { - return null; - } - return super.getTypeId(); + public Object getRegistrationId() { + return _id; } - + /* - /********************************************************** + /********************************************************************** /* Simple setters to allow overriding - /********************************************************** + /********************************************************************** */ /** * Resets all currently configured serializers. */ - public void setSerializers(SimpleSerializers s) { + public SimpleModule setSerializers(SimpleSerializers s) { _serializers = s; + return this; } /** * Resets all currently configured deserializers. */ - public void setDeserializers(SimpleDeserializers d) { + public SimpleModule setDeserializers(SimpleDeserializers d) { _deserializers = d; + return this; } /** * Resets all currently configured key serializers. */ - public void setKeySerializers(SimpleSerializers ks) { + public SimpleModule setKeySerializers(SimpleSerializers ks) { _keySerializers = ks; + return this; } /** * Resets all currently configured key deserializers. */ - public void setKeyDeserializers(SimpleKeyDeserializers kd) { + public SimpleModule setKeyDeserializers(SimpleKeyDeserializers kd) { _keyDeserializers = kd; + return this; + } + + public SimpleModule setDefaultNullKeySerializer(JsonSerializer ser) { + _defaultNullKeySerializer = ser; + return this; + } + + public SimpleModule setDefaultNullValueSerializer(JsonSerializer ser) { + _defaultNullValueSerializer = ser; + return this; } /** * Resets currently configured abstract type mappings */ - public void setAbstractTypes(SimpleAbstractTypeResolver atr) { + public SimpleModule setAbstractTypes(SimpleAbstractTypeResolver atr) { _abstractTypes = atr; + return this; } /** * Resets all currently configured value instantiators */ - public void setValueInstantiators(SimpleValueInstantiators svi) { + public SimpleModule setValueInstantiators(SimpleValueInstantiators svi) { _valueInstantiators = svi; + return this; } - /** - * @since 2.2 - */ public SimpleModule setDeserializerModifier(BeanDeserializerModifier mod) { _deserializerModifier = mod; return this; } - /** - * @since 2.2 - */ public SimpleModule setSerializerModifier(BeanSerializerModifier mod) { _serializerModifier = mod; return this; } - /** - * @since 2.3 - */ protected SimpleModule setNamingStrategy(PropertyNamingStrategy naming) { _namingStrategy = naming; return this; } /* - /********************************************************** + /********************************************************************** /* Configuration methods, adding serializers - /********************************************************** + /********************************************************************** */ /** @@ -314,11 +306,11 @@ public SimpleModule addKeySerializer(Class type, JsonSerializer } /* - /********************************************************** + /********************************************************************** /* Configuration methods, adding deserializers - /********************************************************** + /********************************************************************** */ - + /** * Method for adding deserializer to handle specified type. *

    @@ -349,9 +341,9 @@ public SimpleModule addKeyDeserializer(Class type, KeyDeserializer deser) } /* - /********************************************************** + /********************************************************************** /* Configuration methods, type mapping - /********************************************************** + /********************************************************************** */ /** @@ -410,8 +402,6 @@ public SimpleModule registerSubtypes(NamedType ... subtypes) * Method for adding set of subtypes (along with type name to use) to be registered with * {@link ObjectMapper} * this is an alternative to using annotations in super type to indicate subtypes. - * - * @since 2.9 */ public SimpleModule registerSubtypes(Collection> subtypes) { @@ -426,9 +416,9 @@ public SimpleModule registerSubtypes(Collection> subtypes) } /* - /********************************************************** + /********************************************************************** /* Configuration methods, add other handlers - /********************************************************** + /********************************************************************** */ /** @@ -469,9 +459,9 @@ public SimpleModule setMixInAnnotation(Class targetType, Class mixinClass) } /* - /********************************************************** + /********************************************************************** /* Module impl - /********************************************************** + /********************************************************************** */ @Override @@ -508,36 +498,33 @@ public void setupModule(SetupContext context) context.addValueInstantiators(_valueInstantiators); } if (_deserializerModifier != null) { - context.addBeanDeserializerModifier(_deserializerModifier); + context.addDeserializerModifier(_deserializerModifier); } if (_serializerModifier != null) { - context.addBeanSerializerModifier(_serializerModifier); + context.addSerializerModifier(_serializerModifier); + } + if (_defaultNullKeySerializer != null) { + context.overrideDefaultNullKeySerializer(_defaultNullKeySerializer); + } + if (_defaultNullValueSerializer != null) { + context.overrideDefaultNullValueSerializer(_defaultNullValueSerializer); } if (_subtypes != null && _subtypes.size() > 0) { context.registerSubtypes(_subtypes.toArray(new NamedType[_subtypes.size()])); } - if (_namingStrategy != null) { - context.setNamingStrategy(_namingStrategy); - } if (_mixins != null) { for (Map.Entry,Class> entry : _mixins.entrySet()) { - context.setMixInAnnotations(entry.getKey(), entry.getValue()); + context.setMixIn(entry.getKey(), entry.getValue()); } } } - @Override - public Version version() { return _version; } - /* - /********************************************************** + /********************************************************************** /* Helper methods - /********************************************************** + /********************************************************************** */ - /** - * @since 2.9 - */ protected void _checkNotNull(Object thingy, String type) { if (thingy == null) { diff --git a/src/main/java/com/fasterxml/jackson/databind/module/SimpleSerializers.java b/src/main/java/com/fasterxml/jackson/databind/module/SimpleSerializers.java index c893cb0f76..b9fef09e55 100644 --- a/src/main/java/com/fasterxml/jackson/databind/module/SimpleSerializers.java +++ b/src/main/java/com/fasterxml/jackson/databind/module/SimpleSerializers.java @@ -2,7 +2,7 @@ import java.util.*; - +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; import com.fasterxml.jackson.databind.ser.Serializers; @@ -28,7 +28,7 @@ public class SimpleSerializers extends Serializers.Base implements java.io.Serializable { - private static final long serialVersionUID = 8531646511998456779L; + private static final long serialVersionUID = 3; /** * Class-based mappings that are used both for exact and @@ -43,8 +43,6 @@ public class SimpleSerializers /** * Flag to help find "generic" enum serializer, if one has been registered. - * - * @since 2.3 */ protected boolean _hasEnumSerializer = false; @@ -56,9 +54,6 @@ public class SimpleSerializers public SimpleSerializers() { } - /** - * @since 2.1 - */ public SimpleSerializers(List> sers) { addSerializers(sers); } @@ -72,7 +67,7 @@ public SimpleSerializers(List> sers) { * * @param ser */ - public void addSerializer(JsonSerializer ser) + public SimpleSerializers addSerializer(JsonSerializer ser) { // Interface to match? Class cls = ser.handledType(); @@ -82,22 +77,22 @@ public void addSerializer(JsonSerializer ser) +" or make serializer extend 'com.fasterxml.jackson.databind.ser.std.StdSerializer'"); } _addSerializer(cls, ser); + return this; } - public void addSerializer(Class type, JsonSerializer ser) + public SimpleSerializers addSerializer(Class type, JsonSerializer ser) { _addSerializer(type, ser); + return this; } - /** - * @since 2.1 - */ - public void addSerializers(List> sers) { + public SimpleSerializers addSerializers(List> sers) { for (JsonSerializer ser : sers) { addSerializer(ser); } + return this; } - + /* /********************************************************** /* Serializers implementation @@ -106,7 +101,7 @@ public void addSerializers(List> sers) { @Override public JsonSerializer findSerializer(SerializationConfig config, - JavaType type, BeanDescription beanDesc) + JavaType type, BeanDescription beanDesc, JsonFormat.Value formatOverrides) { Class cls = type.getRawClass(); ClassKey key = new ClassKey(cls); @@ -167,45 +162,45 @@ public JsonSerializer findSerializer(SerializationConfig config, @Override public JsonSerializer findArraySerializer(SerializationConfig config, - ArrayType type, BeanDescription beanDesc, + ArrayType type, BeanDescription beanDesc, JsonFormat.Value formatOverrides, TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) { - return findSerializer(config, type, beanDesc); + return findSerializer(config, type, beanDesc, formatOverrides); } @Override public JsonSerializer findCollectionSerializer(SerializationConfig config, - CollectionType type, BeanDescription beanDesc, + CollectionType type, BeanDescription beanDesc, JsonFormat.Value formatOverrides, TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) { - return findSerializer(config, type, beanDesc); + return findSerializer(config, type, beanDesc, formatOverrides); } @Override public JsonSerializer findCollectionLikeSerializer(SerializationConfig config, - CollectionLikeType type, BeanDescription beanDesc, + CollectionLikeType type, BeanDescription beanDesc, JsonFormat.Value formatOverrides, TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) { - return findSerializer(config, type, beanDesc); + return findSerializer(config, type, beanDesc, formatOverrides); } @Override public JsonSerializer findMapSerializer(SerializationConfig config, - MapType type, BeanDescription beanDesc, + MapType type, BeanDescription beanDesc, JsonFormat.Value formatOverrides, JsonSerializer keySerializer, TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) { - return findSerializer(config, type, beanDesc); + return findSerializer(config, type, beanDesc, formatOverrides); } @Override public JsonSerializer findMapLikeSerializer(SerializationConfig config, - MapLikeType type, BeanDescription beanDesc, + MapLikeType type, BeanDescription beanDesc, JsonFormat.Value formatOverrides, JsonSerializer keySerializer, TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) { - return findSerializer(config, type, beanDesc); + return findSerializer(config, type, beanDesc, formatOverrides); } /* - /********************************************************** + /********************************************************************** /* Internal methods - /********************************************************** + /********************************************************************** */ protected JsonSerializer _findInterfaceMapping(Class cls, ClassKey key) diff --git a/src/main/java/com/fasterxml/jackson/databind/module/package-info.java b/src/main/java/com/fasterxml/jackson/databind/module/package-info.java index 75cf295648..d35fab7c15 100644 --- a/src/main/java/com/fasterxml/jackson/databind/module/package-info.java +++ b/src/main/java/com/fasterxml/jackson/databind/module/package-info.java @@ -1,8 +1,7 @@ /** * Package that contains classes and interfaces to help implement * custom extension {@link com.fasterxml.jackson.databind.Module}s - * (which are registered using - * {@link com.fasterxml.jackson.databind.ObjectMapper#registerModule}. + * (which are registered on ObjectMapper via builders}. *

    * Note that classes in the package only support registering * handlers for non-generic types (types without type 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 e91d2913e7..1c9b4d55ec 100644 --- a/src/main/java/com/fasterxml/jackson/databind/node/ArrayNode.java +++ b/src/main/java/com/fasterxml/jackson/databind/node/ArrayNode.java @@ -1,6 +1,7 @@ package com.fasterxml.jackson.databind.node; import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.tree.ArrayTreeNode; import com.fasterxml.jackson.core.type.WritableTypeId; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.SerializerProvider; @@ -18,11 +19,10 @@ /** * Node class that represents Arrays mapped from JSON content. - *

    - * Note: class was final temporarily for Jackson 2.2. */ public class ArrayNode extends ContainerNode + implements ArrayTreeNode // since 3.0 { private final List _children; @@ -31,17 +31,11 @@ public ArrayNode(JsonNodeFactory nf) { _children = new ArrayList(); } - /** - * @since 2.8 - */ public ArrayNode(JsonNodeFactory nf, int capacity) { super(nf); _children = new ArrayList(capacity); } - /** - * @since 2.7 - */ public ArrayNode(JsonNodeFactory nf, List children) { super(nf); _children = children; @@ -161,7 +155,7 @@ public void serialize(JsonGenerator f, SerializerProvider provider) throws IOExc { final List c = _children; final int size = c.size(); - f.writeStartArray(size); + f.writeStartArray(this, size); for (int i = 0; i < size; ++i) { // we'll typically have array list // For now, assuming it's either BaseJsonNode, JsonSerializable JsonNode n = c.get(i); @@ -246,17 +240,18 @@ public List findParents(String fieldName, List foundSoFar) */ /** - * Method that will set specified field, replacing old value, - * if any. + * Method that will set specified element, replacing old value. * - * @param value to set field to; if null, will be converted + * @param value to set element to; if null, will be converted * to a {@link NullNode} first (to remove field entry, call * {@link #remove} instead) * - * @return Old value of the field, if any; null if there was no - * old value. + * @return This node after adding/replacing property value (to allow chaining) + * + * @throws IndexOutOfBoundsException If Array does not have specified element + * (that is, index is outside valid range of elements in array) */ - public JsonNode set(int index, JsonNode value) + public ArrayNode set(int index, JsonNode value) { if (value == null) { // let's not store 'raw' nulls but nodes value = nullNode(); @@ -264,9 +259,33 @@ public JsonNode set(int index, JsonNode value) if (index < 0 || index >= _children.size()) { throw new IndexOutOfBoundsException("Illegal index "+ index +", array size "+size()); } - return _children.set(index, value); + _children.set(index, value); + return this; } + /** + * Method that will set specified element, replacing old value. + * + * @param value to set element to; if null, will be converted + * to a {@link NullNode} first (to remove field entry, call + * {@link #remove} instead) + * + * @return Old value of the element, if any; null if no such element existed. + * + * @throws IndexOutOfBoundsException If Array does not have specified element + * (that is, index is outside valid range of elements in array) + */ + public JsonNode replace(int index, JsonNode value) + { + if (value == null) { // let's not store 'raw' nulls but nodes + value = nullNode(); + } + if (index < 0 || index >= _children.size()) { + throw new IndexOutOfBoundsException("Illegal index "+ index +", array size "+size()); + } + return _children.set(index, value); + } + /** * Method for adding specified node at the end of this array. * @@ -404,8 +423,6 @@ public ArrayNode addPOJO(Object value) /** * @return This array node, to allow chaining - * - * @since 2.6 */ public ArrayNode addRawValue(RawValue raw) { if (raw == null) { @@ -530,8 +547,6 @@ public ArrayNode add(BigDecimal v) { * Method for adding specified number at the end of this array. * * @return This array node, to allow chaining - * - * @since 2.9 */ public ArrayNode add(BigInteger v) { if (v == null) { diff --git a/src/main/java/com/fasterxml/jackson/databind/node/BaseJsonNode.java b/src/main/java/com/fasterxml/jackson/databind/node/BaseJsonNode.java index 576ca7c8b7..99447b5011 100644 --- a/src/main/java/com/fasterxml/jackson/databind/node/BaseJsonNode.java +++ b/src/main/java/com/fasterxml/jackson/databind/node/BaseJsonNode.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonSerializable; import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.json.JsonMapper; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; /** @@ -21,6 +22,18 @@ public abstract class BaseJsonNode { protected BaseJsonNode() { } + /* + /********************************************************** + /* Defaulting for introspection + /********************************************************** + */ + + @Override + public boolean isMissingNode() { return false; } + + @Override + public boolean isEmbeddedValue() { return false; } + /* /********************************************************** /* Basic definitions for non-container types @@ -37,7 +50,7 @@ public final JsonNode findPath(String fieldName) return value; } - // Also, force (re)definition (2.7) + // Also, force (re)definition @Override public abstract int hashCode(); /* @@ -47,15 +60,10 @@ public final JsonNode findPath(String fieldName) */ @Override - public JsonParser traverse() { - return new TreeTraversingParser(this); + public JsonParser traverse(ObjectReadContext readCtxt) { + return new TreeTraversingParser(this, readCtxt); } - @Override - public JsonParser traverse(ObjectCodec codec) { - return new TreeTraversingParser(this, codec); - } - /** * Method that can be used for efficient type detection * when using stream abstraction for traversing nodes. @@ -86,19 +94,17 @@ public JsonParser.NumberType numberType() { * Method called to serialize node instances using given generator. */ @Override - public abstract void serialize(JsonGenerator jgen, SerializerProvider provider) - throws IOException, JsonProcessingException; + public abstract void serialize(JsonGenerator jgen, SerializerProvider provider) throws IOException; /** * Type information is needed, even if JsonNode instances are "plain" JSON, * since they may be mixed with other types. */ - @Override + @Override public abstract void serializeWithType(JsonGenerator jgen, SerializerProvider provider, - TypeSerializer typeSer) - throws IOException, JsonProcessingException; + TypeSerializer typeSer) throws IOException; - /* + /* /********************************************************** /* Std method overrides /********************************************************** @@ -106,12 +112,21 @@ public abstract void serializeWithType(JsonGenerator jgen, SerializerProvider pr @Override public String toString() { - return InternalNodeMapper.nodeToString(this); + try { + return JsonMapper.shared().writeValueAsString(this); + } catch (IOException e) { // should never occur + throw new RuntimeException(e); + } } @Override public String toPrettyString() { - return InternalNodeMapper.nodeToPrettyString(this); + try { + return JsonMapper.shared() + .writerWithDefaultPrettyPrinter() + .writeValueAsString(this); + } catch (IOException e) { // should never occur + throw new RuntimeException(e); + } } } - diff --git a/src/main/java/com/fasterxml/jackson/databind/node/ContainerNode.java b/src/main/java/com/fasterxml/jackson/databind/node/ContainerNode.java index d64346da96..f2ee3573a5 100644 --- a/src/main/java/com/fasterxml/jackson/databind/node/ContainerNode.java +++ b/src/main/java/com/fasterxml/jackson/databind/node/ContainerNode.java @@ -59,6 +59,7 @@ protected ContainerNode(JsonNodeFactory nc) { @Override public final BooleanNode booleanNode(boolean v) { return _nodeFactory.booleanNode(v); } + @Override public JsonNode missingNode() { return _nodeFactory.missingNode(); } diff --git a/src/main/java/com/fasterxml/jackson/databind/node/InternalNodeMapper.java b/src/main/java/com/fasterxml/jackson/databind/node/InternalNodeMapper.java deleted file mode 100644 index d602f01631..0000000000 --- a/src/main/java/com/fasterxml/jackson/databind/node/InternalNodeMapper.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.fasterxml.jackson.databind.node; - -import java.io.IOException; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectWriter; -import com.fasterxml.jackson.databind.json.JsonMapper; - -/** - * Helper class used to implement toString() method for - * {@link BaseJsonNode}, by embedding a private instance of - * {@link JsonMapper}, only to be used for node serialization. - * - * @since 2.10 (but not to be included in 3.0) - */ -final class InternalNodeMapper { - private final static JsonMapper JSON_MAPPER = new JsonMapper(); - private final static ObjectWriter STD_WRITER = JSON_MAPPER.writer(); - private final static ObjectWriter PRETTY_WRITER = JSON_MAPPER.writer() - .withDefaultPrettyPrinter(); - - public static String nodeToString(JsonNode n) { - try { - return STD_WRITER.writeValueAsString(n); - } catch (IOException e) { // should never occur - throw new RuntimeException(e); - } - } - - public static String nodeToPrettyString(JsonNode n) { - try { - return PRETTY_WRITER.writeValueAsString(n); - } catch (IOException e) { // should never occur - throw new RuntimeException(e); - } - } -} diff --git a/src/main/java/com/fasterxml/jackson/databind/node/JsonNodeCreator.java b/src/main/java/com/fasterxml/jackson/databind/node/JsonNodeCreator.java index 1334786af4..3c4219c4db 100644 --- a/src/main/java/com/fasterxml/jackson/databind/node/JsonNodeCreator.java +++ b/src/main/java/com/fasterxml/jackson/databind/node/JsonNodeCreator.java @@ -3,14 +3,13 @@ import java.math.BigDecimal; import java.math.BigInteger; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.util.RawValue; /** * Interface that defines common "creator" functionality implemented * both by {@link JsonNodeFactory} and {@link ContainerNode} (that is, * JSON Object and Array nodes). - * - * @since 2.3 */ public interface JsonNodeCreator { @@ -19,10 +18,15 @@ public interface JsonNodeCreator public ValueNode booleanNode(boolean v); public ValueNode nullNode(); -// Not yet in 2.10, will be added in 3.0 -// public JsonNode missingNode(); + /** + * @since 3.0 + */ + public JsonNode missingNode(); - // Numeric types + // Numeric types. + // + // note! Can not return `NumericNode` when passed wrapper since `null` will + // return `NullNode` which is NOT a `NumericNode`! public ValueNode numberNode(byte v); public ValueNode numberNode(Byte value); @@ -57,8 +61,6 @@ public interface JsonNodeCreator * Note that the concept may not work with all backends, and since * no translation of any kinds is done it will not work when converting * between data formats. - * - * @since 2.6 */ public ValueNode rawValueNode(RawValue value); @@ -69,8 +71,6 @@ public interface JsonNodeCreator /** * Factory method for constructing a JSON Array node with an initial capacity - * - * @since 2.8 */ public ArrayNode arrayNode(int capacity); diff --git a/src/main/java/com/fasterxml/jackson/databind/node/JsonNodeFactory.java b/src/main/java/com/fasterxml/jackson/databind/node/JsonNodeFactory.java index a924842915..4f20d4a30e 100644 --- a/src/main/java/com/fasterxml/jackson/databind/node/JsonNodeFactory.java +++ b/src/main/java/com/fasterxml/jackson/databind/node/JsonNodeFactory.java @@ -14,10 +14,9 @@ * to behavior of node types, mostly) is needed. */ public class JsonNodeFactory - implements java.io.Serializable, // since 2.1 - JsonNodeCreator // since 2.3 + implements java.io.Serializable, + JsonNodeCreator { - // with 2.2 private static final long serialVersionUID = 1L; private final boolean _cfgBigDecimalExact; @@ -102,6 +101,9 @@ public BooleanNode booleanNode(boolean v) { return v ? BooleanNode.getTrue() : BooleanNode.getFalse(); } + @Override + public JsonNode missingNode() { return MissingNode.getInstance(); } + /** * Factory method for getting an instance of JSON null node (which * represents literal null value) @@ -109,10 +111,6 @@ public BooleanNode booleanNode(boolean v) { @Override public NullNode nullNode() { return NullNode.getInstance(); } - public JsonNode missingNode() { - return MissingNode.getInstance(); - } - /* /********************************************************** /* Factory methods for numeric values diff --git a/src/main/java/com/fasterxml/jackson/databind/node/MissingNode.java b/src/main/java/com/fasterxml/jackson/databind/node/MissingNode.java index acb6a916c0..f9718cbb03 100644 --- a/src/main/java/com/fasterxml/jackson/databind/node/MissingNode.java +++ b/src/main/java/com/fasterxml/jackson/databind/node/MissingNode.java @@ -1,6 +1,7 @@ package com.fasterxml.jackson.databind.node; import java.io.IOException; +import java.util.List; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.databind.JsonNode; @@ -20,7 +21,7 @@ * conversions. */ public final class MissingNode - extends ValueNode + extends BaseJsonNode // NOTE! Does NOT extend `ValueNode` unlike in 2.x { private final static MissingNode instance = new MissingNode(); @@ -30,11 +31,6 @@ public final class MissingNode */ protected MissingNode() { } - @Override - public boolean isMissingNode() { - return true; - } - // Immutable: no need to copy @SuppressWarnings("unchecked") @Override @@ -43,11 +39,15 @@ public boolean isMissingNode() { public static MissingNode getInstance() { return instance; } @Override - public JsonNodeType getNodeType() - { + public JsonNodeType getNodeType() { return JsonNodeType.MISSING; } + @Override + public final boolean isMissingNode() { + return true; + } + @Override public JsonToken asToken() { return JsonToken.NOT_AVAILABLE; } @Override public String asText() { return ""; } @@ -102,4 +102,45 @@ public boolean equals(Object o) public int hashCode() { return JsonNodeType.MISSING.ordinal(); } + + @Override + public JsonNode get(int index) { + return null; + } + + @Override + public JsonNode path(String fieldName) { return this; } + + @Override + public JsonNode path(int index) { return this; } + + @Override + protected JsonNode _at(JsonPointer ptr) { + return this; + } + + @Override + public JsonNode findValue(String fieldName) { + return null; + } + + @Override + public JsonNode findParent(String fieldName) { + return null; + } + + @Override + public List findValues(String fieldName, List foundSoFar) { + return foundSoFar; + } + + @Override + public List findValuesAsText(String fieldName, List foundSoFar) { + return foundSoFar; + } + + @Override + public List findParents(String fieldName, List foundSoFar) { + return foundSoFar; + } } diff --git a/src/main/java/com/fasterxml/jackson/databind/node/NodeCursor.java b/src/main/java/com/fasterxml/jackson/databind/node/NodeCursor.java index 7db58f25e1..4b99c3693b 100644 --- a/src/main/java/com/fasterxml/jackson/databind/node/NodeCursor.java +++ b/src/main/java/com/fasterxml/jackson/databind/node/NodeCursor.java @@ -10,7 +10,7 @@ * of current location within traversed JSON tree. */ abstract class NodeCursor - extends JsonStreamContext + extends TokenStreamContext { /** * Parent cursor of this cursor, if any; null for root @@ -47,13 +47,10 @@ public NodeCursor(int contextType, NodeCursor p) public final NodeCursor getParent() { return _parent; } @Override - public final String getCurrentName() { + public final String currentName() { return _currentName; } - /** - * @since 2.0 - */ public void overrideCurrentName(String name) { _currentName = name; } @@ -116,7 +113,7 @@ protected final static class RootCursor protected boolean _done = false; public RootCursor(JsonNode n, NodeCursor p) { - super(JsonStreamContext.TYPE_ROOT, p); + super(TokenStreamContext.TYPE_ROOT, p); _node = n; } @@ -156,7 +153,7 @@ protected final static class ArrayCursor protected JsonNode _currentNode; public ArrayCursor(JsonNode n, NodeCursor p) { - super(JsonStreamContext.TYPE_ARRAY, p); + super(TokenStreamContext.TYPE_ARRAY, p); _contents = n.elements(); } @@ -198,7 +195,7 @@ protected final static class ObjectCursor public ObjectCursor(JsonNode n, NodeCursor p) { - super(JsonStreamContext.TYPE_OBJECT, p); + super(TokenStreamContext.TYPE_OBJECT, p); _contents = ((ObjectNode) n).fields(); _needEntry = true; } diff --git a/src/main/java/com/fasterxml/jackson/databind/node/NullNode.java b/src/main/java/com/fasterxml/jackson/databind/node/NullNode.java index 1a222433bc..ce47dc3a24 100644 --- a/src/main/java/com/fasterxml/jackson/databind/node/NullNode.java +++ b/src/main/java/com/fasterxml/jackson/databind/node/NullNode.java @@ -48,7 +48,7 @@ public JsonNodeType getNodeType() { public final void serialize(JsonGenerator g, SerializerProvider provider) throws IOException { - provider.defaultSerializeNull(g); + provider.defaultSerializeNullValue(g); } @Override diff --git a/src/main/java/com/fasterxml/jackson/databind/node/ObjectNode.java b/src/main/java/com/fasterxml/jackson/databind/node/ObjectNode.java index 5007ffc886..cd06e72ba8 100644 --- a/src/main/java/com/fasterxml/jackson/databind/node/ObjectNode.java +++ b/src/main/java/com/fasterxml/jackson/databind/node/ObjectNode.java @@ -1,6 +1,7 @@ package com.fasterxml.jackson.databind.node; import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.tree.ObjectTreeNode; import com.fasterxml.jackson.core.type.WritableTypeId; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; @@ -13,11 +14,10 @@ /** * Node that maps to JSON Object structures in JSON content. - *

    - * Note: class was final temporarily for Jackson 2.2. */ public class ObjectNode extends ContainerNode + implements ObjectTreeNode // since 3.0 { // Note: LinkedHashMap for backwards compatibility protected final Map _children; @@ -27,9 +27,6 @@ public ObjectNode(JsonNodeFactory nc) { _children = new LinkedHashMap(); } - /** - * @since 2.4 - */ public ObjectNode(JsonNodeFactory nc, Map kids) { super(nc); _children = kids; @@ -82,7 +79,7 @@ public JsonNodeType getNodeType() { public final boolean isObject() { return true; } - + @Override public JsonToken asToken() { return JsonToken.START_OBJECT; } @Override @@ -345,7 +342,7 @@ public void serializeWithType(JsonGenerator g, SerializerProvider provider, /* /********************************************************** - /* Extended ObjectNode API, mutators, since 2.1 + /* Extended ObjectNode API, mutators /********************************************************** */ @@ -353,19 +350,14 @@ public void serializeWithType(JsonGenerator g, SerializerProvider provider, * Method that will set specified field, replacing old value, if any. * Note that this is identical to {@link #replace(String, JsonNode)}, * except for return value. - *

    - * NOTE: added to replace those uses of {@link #put(String, JsonNode)} - * where chaining with 'this' is desired. - * + * * @param value to set field to; if null, will be converted * to a {@link NullNode} first (to remove field entry, call * {@link #remove} instead) * * @return This node after adding/replacing property value (to allow chaining) - * - * @since 2.1 */ - public JsonNode set(String fieldName, JsonNode value) + public ObjectNode set(String fieldName, JsonNode value) { if (value == null) { value = nullNode(); @@ -381,10 +373,8 @@ public JsonNode set(String fieldName, JsonNode value) * @param properties Properties to add * * @return This node after adding/replacing property values (to allow chaining) - * - * @since 2.1 */ - public JsonNode setAll(Map properties) + public ObjectNode setAll(Map properties) { for (Map.Entry en : properties.entrySet()) { JsonNode n = en.getValue(); @@ -403,10 +393,8 @@ public JsonNode setAll(Map properties) * @param other Object of which properties to add to this object * * @return This node after addition (to allow chaining) - * - * @since 2.1 */ - public JsonNode setAll(ObjectNode other) + public ObjectNode setAll(ObjectNode other) { _children.putAll(other._children); return this; @@ -421,8 +409,6 @@ public JsonNode setAll(ObjectNode other) * * @return Old value of the property; null if there was no such property * with value - * - * @since 2.1 */ public JsonNode replace(String fieldName, JsonNode value) { @@ -437,10 +423,8 @@ public JsonNode replace(String fieldName, JsonNode value) * returning instance after removal. * * @return This node after removing entry (if any) - * - * @since 2.1 */ - public JsonNode without(String fieldName) + public ObjectNode without(String fieldName) { _children.remove(fieldName); return this; @@ -453,8 +437,6 @@ public JsonNode without(String fieldName) * @param fieldNames Names of fields to remove * * @return This node after removing entries - * - * @since 2.1 */ public ObjectNode without(Collection fieldNames) { @@ -467,28 +449,7 @@ public ObjectNode without(Collection fieldNames) /* Extended ObjectNode API, mutators, generic /********************************************************** */ - - /** - * Method that will set specified field, replacing old value, if any. - * - * @param value to set field to; if null, will be converted - * to a {@link NullNode} first (to remove field entry, call - * {@link #remove} instead) - * - * @return Old value of the field, if any; null if there was no - * old value. - * - * @deprecated Since 2.4 use either {@link #set(String,JsonNode)} or {@link #replace(String,JsonNode)}, - */ - @Deprecated - public JsonNode put(String fieldName, JsonNode value) - { - if (value == null) { // let's not store 'raw' nulls but nodes - value = nullNode(); - } - return _children.put(fieldName, value); - } - + /** * Method for removing field entry from this ObjectNode. * Will return value of the field, if such field existed; @@ -527,36 +488,6 @@ public ObjectNode removeAll() return this; } - /** - * Method for adding given properties to this object node, overriding - * any existing values for those properties. - * - * @param properties Properties to add - * - * @return This node after adding/replacing property values (to allow chaining) - * - * @deprecated Since 2.4 use {@link #setAll(Map)}, - */ - @Deprecated - public JsonNode putAll(Map properties) { - return setAll(properties); - } - - /** - * Method for adding all properties of the given Object, overriding - * any existing values for those properties. - * - * @param other Object of which properties to add to this object - * - * @return This node (to allow chaining) - * - * @deprecated Since 2.4 use {@link #setAll(ObjectNode)}, - */ - @Deprecated - public JsonNode putAll(ObjectNode other) { - return setAll(other); - } - /** * Method for removing all field properties out of this ObjectNode * except for ones specified in argument. @@ -632,9 +563,6 @@ public ObjectNode putPOJO(String fieldName, Object pojo) { return _put(fieldName, pojoNode(pojo)); } - /** - * @since 2.6 - */ public ObjectNode putRawValue(String fieldName, RawValue raw) { return _put(fieldName, rawValueNode(raw)); } @@ -776,8 +704,6 @@ public ObjectNode put(String fieldName, BigDecimal v) { * Method for setting value of a field to specified numeric value. * * @return This node (to allow chaining) - * - * @since 2.9 */ public ObjectNode put(String fieldName, BigInteger v) { return _put(fieldName, (v == null) ? nullNode() @@ -841,17 +767,12 @@ public boolean equals(Object o) return false; } - /** - * @since 2.3 - */ - protected boolean _childrenEqual(ObjectNode other) - { + protected boolean _childrenEqual(ObjectNode other) { return _children.equals(other._children); } @Override - public int hashCode() - { + public int hashCode() { return _children.hashCode(); } diff --git a/src/main/java/com/fasterxml/jackson/databind/node/POJONode.java b/src/main/java/com/fasterxml/jackson/databind/node/POJONode.java index c8feff0267..90f8cf3d2f 100644 --- a/src/main/java/com/fasterxml/jackson/databind/node/POJONode.java +++ b/src/main/java/com/fasterxml/jackson/databind/node/POJONode.java @@ -19,9 +19,9 @@ public class POJONode public POJONode(Object v) { _value = v; } /* - /********************************************************** + /********************************************************************** /* Base class overrides - /********************************************************** + /********************************************************************** */ @Override @@ -29,6 +29,9 @@ public JsonNodeType getNodeType() { return JsonNodeType.POJO; } + @Override + public boolean isEmbeddedValue() { return true; } + @Override public JsonToken asToken() { return JsonToken.VALUE_EMBEDDED_OBJECT; } /** @@ -46,9 +49,9 @@ public byte[] binaryValue() throws IOException } /* - /********************************************************** + /********************************************************************** /* General type coercions - /********************************************************** + /********************************************************************** */ @Override @@ -93,31 +96,31 @@ public double asDouble(double defaultValue) } return defaultValue; } - + /* - /********************************************************** + /********************************************************************** /* Public API, serialization - /********************************************************** + /********************************************************************** */ @Override public final void serialize(JsonGenerator gen, SerializerProvider ctxt) throws IOException { if (_value == null) { - ctxt.defaultSerializeNull(gen); + ctxt.defaultSerializeNullValue(gen); } else if (_value instanceof JsonSerializable) { ((JsonSerializable) _value).serialize(gen, ctxt); } else { // 25-May-2018, tatu: [databind#1991] do not call via generator but through context; // this to preserve contextual information - ctxt.defaultSerializeValue(_value, gen); + ctxt.writeValue(gen, _value); } } /* - /********************************************************** + /********************************************************************** /* Extended API - /********************************************************** + /********************************************************************** */ /** @@ -126,9 +129,9 @@ public final void serialize(JsonGenerator gen, SerializerProvider ctxt) throws I public Object getPojo() { return _value; } /* - /********************************************************** + /********************************************************************** /* Overridden standard methods - /********************************************************** + /********************************************************************** */ @Override diff --git a/src/main/java/com/fasterxml/jackson/databind/node/TextNode.java b/src/main/java/com/fasterxml/jackson/databind/node/TextNode.java index 5e92a4dd66..14d9373afd 100644 --- a/src/main/java/com/fasterxml/jackson/databind/node/TextNode.java +++ b/src/main/java/com/fasterxml/jackson/databind/node/TextNode.java @@ -3,7 +3,6 @@ import java.io.IOException; import com.fasterxml.jackson.core.*; -import com.fasterxml.jackson.core.io.CharTypes; import com.fasterxml.jackson.core.io.NumberInput; import com.fasterxml.jackson.core.util.ByteArrayBuilder; @@ -163,12 +162,4 @@ public boolean equals(Object o) @Override public int hashCode() { return _value.hashCode(); } - - @Deprecated // since 2.10 - protected static void appendQuoted(StringBuilder sb, String content) - { - sb.append('"'); - CharTypes.appendQuoted(sb, content); - sb.append('"'); - } } diff --git a/src/main/java/com/fasterxml/jackson/databind/node/TreeTraversingParser.java b/src/main/java/com/fasterxml/jackson/databind/node/TreeTraversingParser.java index 151aefaad3..e811913dee 100644 --- a/src/main/java/com/fasterxml/jackson/databind/node/TreeTraversingParser.java +++ b/src/main/java/com/fasterxml/jackson/databind/node/TreeTraversingParser.java @@ -24,7 +24,10 @@ public class TreeTraversingParser extends ParserMinimalBase /********************************************************** */ - protected ObjectCodec _objectCodec; + /** + * @since 3.0 + */ + protected final JsonNode _source; /** * Traversal context within tree @@ -64,10 +67,10 @@ public class TreeTraversingParser extends ParserMinimalBase public TreeTraversingParser(JsonNode n) { this(n, null); } - public TreeTraversingParser(JsonNode n, ObjectCodec codec) + public TreeTraversingParser(JsonNode n, ObjectReadContext readContext) { - super(0); - _objectCodec = codec; + super(readContext, 0); + _source = n; if (n.isArray()) { _nextToken = JsonToken.START_ARRAY; _nodeCursor = new NodeCursor.ArrayCursor(n, null); @@ -80,20 +83,15 @@ public TreeTraversingParser(JsonNode n, ObjectCodec codec) } @Override - public void setCodec(ObjectCodec c) { - _objectCodec = c; + public Version version() { + return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION; } @Override - public ObjectCodec getCodec() { - return _objectCodec; + public JsonNode getInputSource() { + return _source; } - @Override - public Version version() { - return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION; - } - /* /********************************************************** /* Closeable implementation @@ -187,8 +185,8 @@ public boolean isClosed() { */ @Override - public String getCurrentName() { - return (_nodeCursor == null) ? null : _nodeCursor.getCurrentName(); + public String currentName() { + return (_nodeCursor == null) ? null : _nodeCursor.currentName(); } @Override @@ -200,7 +198,7 @@ public void overrideCurrentName(String name) } @Override - public JsonStreamContext getParsingContext() { + public TokenStreamContext getParsingContext() { return _nodeCursor; } @@ -229,7 +227,7 @@ public String getText() // need to separate handling a bit... switch (_currToken) { case FIELD_NAME: - return _nodeCursor.getCurrentName(); + return _nodeCursor.currentName(); case VALUE_STRING: return currentNode().textValue(); case VALUE_NUMBER_INT: diff --git a/src/main/java/com/fasterxml/jackson/databind/node/ValueNode.java b/src/main/java/com/fasterxml/jackson/databind/node/ValueNode.java index 322fe02948..f1ad6d7150 100644 --- a/src/main/java/com/fasterxml/jackson/databind/node/ValueNode.java +++ b/src/main/java/com/fasterxml/jackson/databind/node/ValueNode.java @@ -17,13 +17,15 @@ public abstract class ValueNode extends BaseJsonNode { + protected final static JsonNode MISSING = MissingNode.getInstance(); + protected ValueNode() { } @Override protected JsonNode _at(JsonPointer ptr) { // will only allow direct matches, but no traversal through // (base class checks for direct match) - return MissingNode.getInstance(); + return MISSING; } /** @@ -66,7 +68,7 @@ public void serializeWithType(JsonGenerator g, SerializerProvider provider, public final JsonNode get(int index) { return null; } @Override - public final JsonNode path(int index) { return MissingNode.getInstance(); } + public final JsonNode path(int index) { return MISSING; } @Override public final boolean has(int index) { return false; } @@ -78,7 +80,7 @@ public void serializeWithType(JsonGenerator g, SerializerProvider provider, public final JsonNode get(String fieldName) { return null; } @Override - public final JsonNode path(String fieldName) { return MissingNode.getInstance(); } + public final JsonNode path(String fieldName) { return MISSING; } @Override public final boolean has(String fieldName) { return false; } 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 045adeaea7..a586a5d92e 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/AnyGetterWriter.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/AnyGetterWriter.java @@ -96,12 +96,10 @@ public void getAndFilter(Object bean, JsonGenerator gen, SerializerProvider prov public void resolve(SerializerProvider provider) throws JsonMappingException { // 05-Sep-2013, tatu: I _think_ this can be considered a primary property... - if (_serializer instanceof ContextualSerializer) { - JsonSerializer ser = provider.handlePrimaryContextualization(_serializer, _property); - _serializer = (JsonSerializer) ser; - if (ser instanceof MapSerializer) { - _mapSerializer = (MapSerializer) ser; - } + JsonSerializer ser = provider.handlePrimaryContextualization(_serializer, _property); + _serializer = (JsonSerializer) ser; + if (ser instanceof MapSerializer) { + _mapSerializer = (MapSerializer) ser; } } } 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 a3d422ffaf..c47e0d146c 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/BasicSerializerFactory.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/BasicSerializerFactory.java @@ -5,19 +5,28 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.nio.ByteBuffer; +import java.nio.file.Path; import java.util.*; import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; -import com.fasterxml.jackson.annotation.JsonFormat; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.cfg.SerializerFactoryConfig; import com.fasterxml.jackson.databind.ext.OptionalHandlerFactory; +import com.fasterxml.jackson.databind.ext.jdk8.DoubleStreamSerializer; +import com.fasterxml.jackson.databind.ext.jdk8.IntStreamSerializer; +import com.fasterxml.jackson.databind.ext.jdk8.LongStreamSerializer; +import com.fasterxml.jackson.databind.ext.jdk8.OptionalDoubleSerializer; +import com.fasterxml.jackson.databind.ext.jdk8.OptionalIntSerializer; +import com.fasterxml.jackson.databind.ext.jdk8.OptionalLongSerializer; +import com.fasterxml.jackson.databind.ext.jdk8.Jdk8OptionalSerializer; +import com.fasterxml.jackson.databind.ext.jdk8.Jdk8StreamSerializer; import com.fasterxml.jackson.databind.introspect.*; -import com.fasterxml.jackson.databind.jsontype.NamedType; -import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; import com.fasterxml.jackson.databind.ser.impl.*; import com.fasterxml.jackson.databind.ser.std.*; @@ -39,9 +48,9 @@ public abstract class BasicSerializerFactory implements java.io.Serializable { /* - /********************************************************** + /********************************************************************** /* Configuration, lookup tables/maps - /********************************************************** + /********************************************************************** */ /** @@ -50,17 +59,8 @@ public abstract class BasicSerializerFactory * use the class name, and keep things simple and efficient. */ protected final static HashMap> _concrete; - - /** - * Actually it may not make much sense to eagerly instantiate all - * kinds of serializers: so this Map actually contains class references, - * not instances - */ - protected final static HashMap>> _concreteLazy; static { - HashMap>> concLazy - = new HashMap>>(); HashMap> concrete = new HashMap>(); @@ -68,7 +68,7 @@ public abstract class BasicSerializerFactory /* String and string-like types (note: date types explicitly * not included -- can use either textual or numeric serialization) */ - concrete.put(String.class.getName(), new StringSerializer()); + concrete.put(String.class.getName(), StringSerializer.instance); final ToStringSerializer sls = ToStringSerializer.instance; concrete.put(StringBuffer.class.getName(), sls); concrete.put(StringBuilder.class.getName(), sls); @@ -82,37 +82,20 @@ public abstract class BasicSerializerFactory // Other numbers, more complicated concrete.put(BigInteger.class.getName(), new NumberSerializer(BigInteger.class)); - concrete.put(BigDecimal.class.getName(),new NumberSerializer(BigDecimal.class)); + concrete.put(BigDecimal.class.getName(), new NumberSerializer(BigDecimal.class)); // Other discrete non-container types: // First, Date/Time zoo: concrete.put(Calendar.class.getName(), CalendarSerializer.instance); concrete.put(java.util.Date.class.getName(), DateSerializer.instance); - // And then other standard non-structured JDK types - for (Map.Entry,Object> en : StdJdkSerializers.all()) { - Object value = en.getValue(); - if (value instanceof JsonSerializer) { - concrete.put(en.getKey().getName(), (JsonSerializer) value); - } else { - @SuppressWarnings("unchecked") - Class> cls = (Class>) value; - concLazy.put(en.getKey().getName(), cls); - } - } - - // Jackson-specific type(s) - // (Q: can this ever be sub-classed?) - concLazy.put(TokenBuffer.class.getName(), TokenBufferSerializer.class); - _concrete = concrete; - _concreteLazy = concLazy; } /* - /********************************************************** + /********************************************************************** /* Configuration - /********************************************************** + /********************************************************************** */ /** @@ -122,9 +105,9 @@ public abstract class BasicSerializerFactory protected final SerializerFactoryConfig _factoryConfig; /* - /********************************************************** + /********************************************************************** /* Life cycle - /********************************************************** + /********************************************************************** */ /** @@ -135,17 +118,6 @@ public abstract class BasicSerializerFactory protected BasicSerializerFactory(SerializerFactoryConfig config) { _factoryConfig = (config == null) ? new SerializerFactoryConfig() : config; } - - /** - * Method for getting current {@link SerializerFactoryConfig}. - *

    - * Note that since instances are immutable, you can NOT change settings - * by accessing an instance and calling methods: this will simply create - * new instance of config object. - */ - public SerializerFactoryConfig getFactoryConfig() { - return _factoryConfig; - } /** * Method used for creating a new instance of this factory, but with different @@ -157,7 +129,7 @@ public SerializerFactoryConfig getFactoryConfig() { * factory type. Check out javadocs for * {@link com.fasterxml.jackson.databind.ser.BeanSerializerFactory} for more details. */ - public abstract SerializerFactory withConfig(SerializerFactoryConfig config); + protected abstract SerializerFactory withConfig(SerializerFactoryConfig config); /** * Convenience method for creating a new factory instance with an additional @@ -186,61 +158,73 @@ public final SerializerFactory withSerializerModifier(BeanSerializerModifier mod return withConfig(_factoryConfig.withSerializerModifier(modifier)); } + @Override + public final SerializerFactory withNullValueSerializer(JsonSerializer nvs) { + return withConfig(_factoryConfig.withNullValueSerializer(nvs)); + } + + @Override + public final SerializerFactory withNullKeySerializer(JsonSerializer nks) { + return withConfig(_factoryConfig.withNullKeySerializer(nks)); + } + /* - /********************************************************** - /* SerializerFactory impl - /********************************************************** + /********************************************************************** + /* `SerializerFactory` impl + /********************************************************************** */ - // Implemented by sub-classes - @Override - public abstract JsonSerializer createSerializer(SerializerProvider prov, - JavaType type) - throws JsonMappingException; +// Implemented by sub-classes +// public abstract JsonSerializer createSerializer(SerializerProvider prov, ....) @Override @SuppressWarnings("unchecked") - public JsonSerializer createKeySerializer(SerializationConfig config, + public JsonSerializer createKeySerializer(SerializerProvider ctxt, JavaType keyType, JsonSerializer defaultImpl) + throws JsonMappingException { // We should not need any member method info; at most class annotations for Map type // ... at least, not here. - BeanDescription beanDesc = config.introspectClassAnnotations(keyType.getRawClass()); + BeanDescription beanDesc = ctxt.introspectClassAnnotations(keyType); + final SerializationConfig config = ctxt.getConfig(); JsonSerializer ser = null; // Minor optimization: to avoid constructing beanDesc, bail out if none registered if (_factoryConfig.hasKeySerializers()) { // Only thing we have here are module-provided key serializers: for (Serializers serializers : _factoryConfig.keySerializers()) { - ser = serializers.findSerializer(config, keyType, beanDesc); + ser = serializers.findSerializer(config, keyType, beanDesc, null); if (ser != null) { break; } } } if (ser == null) { - ser = defaultImpl; + ser = StdKeySerializers.getStdKeySerializer(config, keyType.getRawClass(), false); + // As per [databind#47], also need to support @JsonValue if (ser == null) { - ser = StdKeySerializers.getStdKeySerializer(config, keyType.getRawClass(), false); - // As per [databind#47], also need to support @JsonValue - if (ser == null) { - beanDesc = config.introspect(keyType); - AnnotatedMember am = beanDesc.findJsonValueAccessor(); - if (am != null) { - final Class rawType = am.getRawType(); - JsonSerializer delegate = StdKeySerializers.getStdKeySerializer(config, - rawType, true); - if (config.canOverrideAccessModifiers()) { - ClassUtil.checkAndFixAccess(am.getMember(), - config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); - } - ser = new JsonValueSerializer(am, delegate); - } else { + beanDesc = ctxt.introspect(keyType); + AnnotatedMember am = beanDesc.findJsonValueAccessor(); + if (am != null) { + final Class rawType = am.getRawType(); + JsonSerializer delegate = StdKeySerializers.getStdKeySerializer(config, + rawType, true); + if (config.canOverrideAccessModifiers()) { + ClassUtil.checkAndFixAccess(am.getMember(), + config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); + } + // need to pass both type of key Object (on which accessor called), and actual + // value type that `JsonType`-annotated accessor returns (or contains, in case of field) + ser = new JsonValueSerializer(keyType, am.getType(), false, null, delegate, am); + } else { + // And aside from JDK defaults, use `defaultImpl` if any specified + ser = defaultImpl; + if (ser == null) { ser = StdKeySerializers.getFallbackKeySerializer(config, keyType.getRawClass()); } } } } - + // [databind#120]: Allow post-processing if (_factoryConfig.hasSerializerModifiers()) { for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) { @@ -250,48 +234,48 @@ public JsonSerializer createKeySerializer(SerializationConfig config, return (JsonSerializer) ser; } - /** - * Method called to construct a type serializer for values with given declared - * base type. This is called for values other than those of bean property - * types. - */ @Override - public TypeSerializer createTypeSerializer(SerializationConfig config, - JavaType baseType) - { - BeanDescription bean = config.introspectClassAnnotations(baseType.getRawClass()); - AnnotatedClass ac = bean.getClassInfo(); - AnnotationIntrospector ai = config.getAnnotationIntrospector(); - TypeResolverBuilder b = ai.findTypeResolver(config, ac, baseType); - /* Ok: if there is no explicit type info handler, we may want to - * use a default. If so, config object knows what to use. - */ - Collection subtypes = null; - if (b == null) { - b = config.getDefaultTyper(baseType); - } else { - subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByClass(config, ac); - } - if (b == null) { - return null; - } - // 10-Jun-2015, tatu: Since not created for Bean Property, no need for post-processing - // wrt EXTERNAL_PROPERTY - return b.buildTypeSerializer(config, baseType, subtypes); + public JsonSerializer getDefaultNullKeySerializer() { + return _factoryConfig.getNullKeySerializer(); + } + + @Override + public JsonSerializer getDefaultNullValueSerializer() { + return _factoryConfig.getNullValueSerializer(); } /* - /********************************************************** + /********************************************************************** /* Additional API for other core classes - /********************************************************** + /********************************************************************** */ - protected abstract Iterable customSerializers(); + protected Iterable customSerializers() { + return _factoryConfig.serializers(); + } + + /** + * Method called to create a type information serializer for values of given + * container property + * if one is needed. If not needed (no polymorphic handling configured), should + * return null. + * + * @param containerType Declared type of the container to use as the base type for type information serializer + * + * @return Type serializer to use for property value contents, if one is needed; null if not. + */ + public TypeSerializer findPropertyContentTypeSerializer(SerializerProvider ctxt, + JavaType containerType, AnnotatedMember accessor) + throws JsonMappingException + { + return ctxt.getConfig().getTypeResolverProvider() + .findPropertyContentTypeSerializer(ctxt, accessor, containerType); + } /* - /********************************************************** + /********************************************************************** /* Overridable secondary serializer accessor methods - /********************************************************** + /********************************************************************** */ /** @@ -299,20 +283,14 @@ public TypeSerializer createTypeSerializer(SerializationConfig config, * see if we know serializer to use for given type. */ protected final JsonSerializer findSerializerByLookup(JavaType type, - SerializationConfig config, BeanDescription beanDesc, + SerializationConfig config, BeanDescription beanDesc, JsonFormat.Value format, boolean staticTyping) { - Class raw = type.getRawClass(); - String clsName = raw.getName(); - JsonSerializer ser = _concrete.get(clsName); + final Class raw = type.getRawClass(); + JsonSerializer ser = StdJdkSerializers.find(raw); if (ser == null) { - Class> serClass = _concreteLazy.get(clsName); - if (serClass != null) { - // 07-Jan-2017, tatu: Should never fail (since we control constructors), - // but if it does will throw `IllegalArgumentException` with description, - // which we could catch, re-title. - return ClassUtil.createInstance(serClass, false); - } + final String clsName = raw.getName(); + ser = _concrete.get(clsName); } return ser; } @@ -330,10 +308,8 @@ protected final JsonSerializer findSerializerByLookup(JavaType type, * based on that property * * - * - * @since 2.0 */ - protected final JsonSerializer findSerializerByAnnotations(SerializerProvider prov, + protected final JsonSerializer findSerializerByAnnotations(SerializerProvider ctxt, JavaType type, BeanDescription beanDesc) throws JsonMappingException { @@ -345,17 +321,21 @@ protected final JsonSerializer findSerializerByAnnotations(SerializerProvider // Second: @JsonValue for any type AnnotatedMember valueAccessor = beanDesc.findJsonValueAccessor(); if (valueAccessor != null) { - if (prov.canOverrideAccessModifiers()) { + if (ctxt.canOverrideAccessModifiers()) { ClassUtil.checkAndFixAccess(valueAccessor.getMember(), - prov.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); + ctxt.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); } - JsonSerializer ser = findSerializerFromAnnotation(prov, valueAccessor); - return new JsonValueSerializer(valueAccessor, ser); + JsonSerializer ser = findSerializerFromAnnotation(ctxt, valueAccessor); + JavaType valueType = valueAccessor.getType(); + // note: must get different `BeanDescription` since valueType different + TypeSerializer vts = ctxt.findTypeSerializer(valueType); + return new JsonValueSerializer(type, valueType, /* static typing */ false, + vts, ser, valueAccessor); } // No well-known annotations... return null; } - + /** * Method for checking if we can determine serializer to use based on set of * known primary types, checking for set of known base types (exact matches @@ -363,33 +343,52 @@ protected final JsonSerializer findSerializerByAnnotations(SerializerProvider * This does not include "secondary" interfaces, but * mostly concrete or abstract base classes. */ - protected final JsonSerializer findSerializerByPrimaryType(SerializerProvider prov, - JavaType type, BeanDescription beanDesc, + protected final JsonSerializer findSerializerByPrimaryType(SerializerProvider ctxt, + JavaType type, BeanDescription beanDesc, JsonFormat.Value formatOverrides, boolean staticTyping) throws JsonMappingException { - Class raw = type.getRawClass(); - - // Then check for optional/external serializers - JsonSerializer ser = findOptionalStdSerializer(prov, type, beanDesc, staticTyping); - if (ser != null) { - return ser; - } - - if (Calendar.class.isAssignableFrom(raw)) { + if (type.isTypeOrSubTypeOf(Calendar.class)) { return CalendarSerializer.instance; } - if (java.util.Date.class.isAssignableFrom(raw)) { + if (type.isTypeOrSubTypeOf(java.util.Date.class)) { return DateSerializer.instance; } + // 19-Sep-2017, tatu: Jackson 3.x adds Java 8 types. + // NOTE: while seemingly more of an add-on type, we must handle here because + // otherwise Bean-handling would be used instead... + if (type.isTypeOrSubTypeOf(Stream.class)) { + return new Jdk8StreamSerializer(type, + ctxt.getTypeFactory().findFirstTypeParameter(type, Stream.class)); + } + + if (type.isTypeOrSubTypeOf(Number.class)) { + JsonFormat.Value format = _calculateEffectiveFormat(beanDesc, Number.class, formatOverrides); + + // 21-May-2014, tatu: Couple of alternatives actually + switch (format.getShape()) { + case STRING: + return ToStringSerializer.instance; + case OBJECT: // need to bail out to let it be serialized as POJO + return null; + default: + } + return NumberSerializer.instance; + } + if (type.isTypeOrSubTypeOf(Enum.class)) { + return buildEnumSerializer(ctxt, type, beanDesc, + _calculateEffectiveFormat(beanDesc, Enum.class, formatOverrides)); + } + Class raw = type.getRawClass(); if (Map.Entry.class.isAssignableFrom(raw)) { // 18-Oct-2015, tatu: With 2.7, need to dig type info: JavaType mapEntryType = type.findSuperType(Map.Entry.class); - // 28-Apr-2015, tatu: TypeFactory does it all for us already so JavaType kt = mapEntryType.containedTypeOrUnknown(0); JavaType vt = mapEntryType.containedTypeOrUnknown(1); - return buildMapEntrySerializer(prov, type, beanDesc, staticTyping, kt, vt); + return buildMapEntrySerializer(ctxt, type, beanDesc, + _calculateEffectiveFormat(beanDesc, Map.Entry.class, formatOverrides), + staticTyping, kt, vt); } if (ByteBuffer.class.isAssignableFrom(raw)) { return new ByteBufferSerializer(); @@ -406,39 +405,30 @@ protected final JsonSerializer findSerializerByPrimaryType(SerializerProvider if (java.nio.charset.Charset.class.isAssignableFrom(raw)) { return ToStringSerializer.instance; } - if (Number.class.isAssignableFrom(raw)) { - // 21-May-2014, tatu: Couple of alternatives actually - JsonFormat.Value format = beanDesc.findExpectedFormat(null); - if (format != null) { - switch (format.getShape()) { - case STRING: - return ToStringSerializer.instance; - case OBJECT: // need to bail out to let it be serialized as POJO - case ARRAY: // or, I guess ARRAY; otherwise no point in speculating - return null; - default: - } - } - return NumberSerializer.instance; + // 19-Sep-2017, tatu: Java 8 streams, except for main `Stream` (which is "add-on" interface?) + if (LongStream.class.isAssignableFrom(raw)) { + return LongStreamSerializer.INSTANCE; + } + if (IntStream.class.isAssignableFrom(raw)) { + return IntStreamSerializer.INSTANCE; + } + if (DoubleStream.class.isAssignableFrom(raw)) { + return DoubleStreamSerializer.INSTANCE; } - if (Enum.class.isAssignableFrom(raw)) { - return buildEnumSerializer(prov.getConfig(), type, beanDesc); + // NOTE: not concrete, can not just add directly via StdJdkSerializers. Also, requires + // bit of trickery wrt class name for polymorphic... + if (Path.class.isAssignableFrom(raw)) { + return StringLikeSerializer.find(Path.class); + } + // Then check for optional/external serializers + JsonSerializer ser = OptionalHandlerFactory.instance.findSerializer(ctxt.getConfig(), + type, beanDesc); + if (ser != null) { + return ser; } return null; } - /** - * Overridable method called after checking all other types. - * - * @since 2.2 - */ - protected JsonSerializer findOptionalStdSerializer(SerializerProvider prov, - JavaType type, BeanDescription beanDesc, boolean staticTyping) - throws JsonMappingException - { - return OptionalHandlerFactory.instance.findSerializer(prov.getConfig(), type, beanDesc); - } - /** * Reflection-based serialized find method, which checks if * given class implements one of recognized "add-on" interfaces. @@ -447,29 +437,27 @@ protected JsonSerializer findOptionalStdSerializer(SerializerProvider prov, * bean classes may implement {@link Iterable}, but their main * function is usually something else. The reason for */ - protected final JsonSerializer findSerializerByAddonType(SerializationConfig config, - JavaType javaType, BeanDescription beanDesc, boolean staticTyping) throws JsonMappingException + protected final JsonSerializer findSerializerByAddonType(SerializerProvider ctxt, + JavaType javaType, BeanDescription beanDesc, JsonFormat.Value formatOverrides, + boolean staticTyping) throws JsonMappingException { - Class rawType = javaType.getRawClass(); - - if (Iterator.class.isAssignableFrom(rawType)) { - 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); + final TypeFactory tf = ctxt.getTypeFactory(); + if (javaType.isTypeOrSubTypeOf(Iterator.class)) { + return buildIteratorSerializer(ctxt, javaType, beanDesc, formatOverrides, + staticTyping, + tf.findFirstTypeParameter(javaType, Iterator.class)); } - if (Iterable.class.isAssignableFrom(rawType)) { - 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 (javaType.isTypeOrSubTypeOf(Iterable.class)) { + return buildIterableSerializer(ctxt, javaType, beanDesc, formatOverrides, + staticTyping, + tf.findFirstTypeParameter(javaType, Iterable.class)); } - if (CharSequence.class.isAssignableFrom(rawType)) { + if (javaType.isTypeOrSubTypeOf(CharSequence.class)) { return ToStringSerializer.instance; } return null; } - + /** * Helper method called to check if a class or method * has an annotation @@ -482,13 +470,13 @@ protected JsonSerializer findSerializerFromAnnotation(SerializerProvider Annotated a) throws JsonMappingException { - Object serDef = prov.getAnnotationIntrospector().findSerializer(a); + Object serDef = prov.getAnnotationIntrospector().findSerializer(prov.getConfig(), a); if (serDef == null) { return null; } - JsonSerializer ser = prov.serializerInstance(a, serDef); // One more thing however: may need to also apply a converter: - return (JsonSerializer) findConvertingSerializer(prov, a, ser); + return (JsonSerializer) findConvertingSerializer(prov, a, + prov.serializerInstance(a, serDef)); } /** @@ -506,14 +494,14 @@ protected JsonSerializer findConvertingSerializer(SerializerProvider prov, return ser; } JavaType delegateType = conv.getOutputType(prov.getTypeFactory()); - return new StdDelegatingSerializer(conv, delegateType, ser); + return new StdDelegatingSerializer(conv, delegateType, ser, null); } protected Converter findConverter(SerializerProvider prov, Annotated a) throws JsonMappingException { - Object convDef = prov.getAnnotationIntrospector().findSerializationConverter(a); + Object convDef = prov.getAnnotationIntrospector().findSerializationConverter(prov.getConfig(), a); if (convDef == null) { return null; } @@ -521,41 +509,34 @@ protected Converter findConverter(SerializerProvider prov, } /* - /********************************************************** + /********************************************************************** /* Factory methods, container types: - /********************************************************** + /********************************************************************** */ - /** - * @since 2.1 - */ - protected JsonSerializer buildContainerSerializer(SerializerProvider prov, - JavaType type, BeanDescription beanDesc, boolean staticTyping) + protected JsonSerializer buildContainerSerializer(SerializerProvider ctxt, + JavaType type, BeanDescription beanDesc, JsonFormat.Value formatOverrides, + boolean staticTyping) throws JsonMappingException { - final SerializationConfig config = prov.getConfig(); - - /* [databind#23], 15-Mar-2013, tatu: must force static handling of root value type, - * with just one important exception: if value type is "untyped", let's - * leave it as is; no clean way to make it work. - */ + // [databind#23], 15-Mar-2013, tatu: must force static handling of root value type, + // with just one important exception: if value type is "untyped", let's + // leave it as is; no clean way to make it work. if (!staticTyping && type.useStaticType()) { if (!type.isContainerType() || !type.getContentType().isJavaLangObject()) { staticTyping = true; } } - // Let's see what we can learn about element/content/value type, type serializer for it: JavaType elementType = type.getContentType(); - TypeSerializer elementTypeSerializer = createTypeSerializer(config, - elementType); - + TypeSerializer elementTypeSerializer = ctxt.findTypeSerializer(elementType); // if elements have type serializer, cannot force static typing: if (elementTypeSerializer != null) { staticTyping = false; } - JsonSerializer elementValueSerializer = _findContentSerializer(prov, + JsonSerializer elementValueSerializer = _findContentSerializer(ctxt, beanDesc.getClassInfo()); + final SerializationConfig config = ctxt.getConfig(); if (type.isMapLikeType()) { // implements java.util.Map MapLikeType mlt = (MapLikeType) type; /* 29-Sep-2012, tatu: This is actually too early to (try to) find @@ -563,23 +544,25 @@ protected JsonSerializer buildContainerSerializer(SerializerProvider prov, * issues (see [databind#75]). Instead, must be done from 'createContextual()' call. * But we do need to check class annotations. */ - JsonSerializer keySerializer = _findKeySerializer(prov, beanDesc.getClassInfo()); + JsonSerializer keySerializer = _findKeySerializer(ctxt, beanDesc.getClassInfo()); if (mlt.isTrueMapType()) { - return buildMapSerializer(prov, (MapType) mlt, beanDesc, staticTyping, + return buildMapSerializer(ctxt, (MapType) mlt, + beanDesc, formatOverrides, staticTyping, keySerializer, elementTypeSerializer, elementValueSerializer); } // With Map-like, just 2 options: (1) Custom, (2) Annotations JsonSerializer ser = null; MapLikeType mlType = (MapLikeType) type; for (Serializers serializers : customSerializers()) { // (1) Custom - ser = serializers.findMapLikeSerializer(config, - mlType, beanDesc, keySerializer, elementTypeSerializer, elementValueSerializer); + ser = serializers.findMapLikeSerializer(config, mlType, + beanDesc, formatOverrides, + keySerializer, elementTypeSerializer, elementValueSerializer); if (ser != null) { break; } } if (ser == null) { // (2) Annotations-based ones: - ser = findSerializerByAnnotations(prov, type, beanDesc); + ser = findSerializerByAnnotations(ctxt, type, beanDesc); } if (ser != null) { if (_factoryConfig.hasSerializerModifiers()) { @@ -593,21 +576,23 @@ protected JsonSerializer buildContainerSerializer(SerializerProvider prov, if (type.isCollectionLikeType()) { CollectionLikeType clt = (CollectionLikeType) type; if (clt.isTrueCollectionType()) { - return buildCollectionSerializer(prov, (CollectionType) clt, beanDesc, staticTyping, + return buildCollectionSerializer(ctxt, (CollectionType) clt, + beanDesc, formatOverrides, staticTyping, elementTypeSerializer, elementValueSerializer); } // With Map-like, just 2 options: (1) Custom, (2) Annotations JsonSerializer ser = null; CollectionLikeType clType = (CollectionLikeType) type; for (Serializers serializers : customSerializers()) { // (1) Custom - ser = serializers.findCollectionLikeSerializer(config, - clType, beanDesc, elementTypeSerializer, elementValueSerializer); + ser = serializers.findCollectionLikeSerializer(config, clType, + beanDesc, formatOverrides, + elementTypeSerializer, elementValueSerializer); if (ser != null) { break; } } if (ser == null) { // (2) Annotations-based ones: - ser = findSerializerByAnnotations(prov, type, beanDesc); + ser = findSerializerByAnnotations(ctxt, type, beanDesc); } if (ser != null) { if (_factoryConfig.hasSerializerModifiers()) { @@ -619,7 +604,8 @@ protected JsonSerializer buildContainerSerializer(SerializerProvider prov, return ser; } if (type.isArrayType()) { - return buildArraySerializer(prov, (ArrayType) type, beanDesc, staticTyping, + return buildArraySerializer(ctxt, (ArrayType) type, + beanDesc, formatOverrides, staticTyping, elementTypeSerializer, elementValueSerializer); } return null; @@ -628,11 +614,10 @@ protected JsonSerializer buildContainerSerializer(SerializerProvider prov, /** * Helper method that handles configuration details when constructing serializers for * {@link java.util.List} types that support efficient by-index access - * - * @since 2.1 */ protected JsonSerializer buildCollectionSerializer(SerializerProvider prov, - CollectionType type, BeanDescription beanDesc, boolean staticTyping, + CollectionType type, BeanDescription beanDesc, JsonFormat.Value formatOverrides, + boolean staticTyping, TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) throws JsonMappingException { @@ -643,20 +628,20 @@ protected JsonSerializer buildCollectionSerializer(SerializerProvider prov, // 2. Annotations (@JsonValue, @JsonDeserialize) // 3. Defaults for (Serializers serializers : customSerializers()) { // (1) Custom - ser = serializers.findCollectionSerializer(config, - type, beanDesc, elementTypeSerializer, elementValueSerializer); + ser = serializers.findCollectionSerializer(config, type, beanDesc, formatOverrides, + elementTypeSerializer, elementValueSerializer); if (ser != null) { break; } } + JsonFormat.Value format = _calculateEffectiveFormat(beanDesc, Collection.class, formatOverrides); if (ser == null) { ser = findSerializerByAnnotations(prov, type, beanDesc); // (2) Annotations if (ser == null) { // We may also want to use serialize Collections "as beans", if (and only if) - // this is specified with `@JsonFormat(shape=Object)` - JsonFormat.Value format = beanDesc.findExpectedFormat(null); - if ((format != null) && format.getShape() == JsonFormat.Shape.OBJECT) { + // shape specified as "POJO" + if (format.getShape() == JsonFormat.Shape.POJO) { return null; } Class raw = type.getRawClass(); @@ -672,7 +657,7 @@ protected JsonSerializer buildCollectionSerializer(SerializerProvider prov, Class elementRaw = type.getContentType().getRawClass(); if (isIndexedList(raw)) { if (elementRaw == String.class) { - // [JACKSON-829] Must NOT use if we have custom serializer + // Only optimize if std implementation, not custom if (ClassUtil.isJacksonStdImpl(elementValueSerializer)) { ser = IndexedStringListSerializer.instance; } @@ -681,7 +666,7 @@ protected JsonSerializer buildCollectionSerializer(SerializerProvider prov, elementTypeSerializer, elementValueSerializer); } } else if (elementRaw == String.class) { - // [JACKSON-829] Must NOT use if we have custom serializer + // Only optimize if std implementation, not custom if (ClassUtil.isJacksonStdImpl(elementValueSerializer)) { ser = StringCollectionSerializer.instance; } @@ -703,9 +688,9 @@ protected JsonSerializer buildCollectionSerializer(SerializerProvider prov, } /* - /********************************************************** + /********************************************************************** /* Factory methods, for Collections - /********************************************************** + /********************************************************************** */ protected boolean isIndexedList(Class cls) @@ -728,9 +713,9 @@ public JsonSerializer buildEnumSetSerializer(JavaType enumType) { } /* - /********************************************************** + /********************************************************************** /* Factory methods, for Maps - /********************************************************** + /********************************************************************** */ /** @@ -738,18 +723,18 @@ public JsonSerializer buildEnumSetSerializer(JavaType enumType) { * {@link java.util.Map} types. */ protected JsonSerializer buildMapSerializer(SerializerProvider prov, - MapType type, BeanDescription beanDesc, + MapType type, BeanDescription beanDesc, JsonFormat.Value formatOverrides, boolean staticTyping, JsonSerializer keySerializer, TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) throws JsonMappingException { + JsonFormat.Value format = _calculateEffectiveFormat(beanDesc, Map.class, formatOverrides); + // [databind#467]: This is where we could allow serialization "as POJO": But! It's // nasty to undo, and does not apply on per-property basis. So, hardly optimal - JsonFormat.Value format = beanDesc.findExpectedFormat(null); - if ((format != null) && format.getShape() == JsonFormat.Shape.OBJECT) { + if (format.getShape() == JsonFormat.Shape.POJO) { return null; } - JsonSerializer ser = null; // Order of lookups: @@ -759,7 +744,7 @@ protected JsonSerializer buildMapSerializer(SerializerProvider prov, final SerializationConfig config = prov.getConfig(); for (Serializers serializers : customSerializers()) { // (1) Custom - ser = serializers.findMapSerializer(config, type, beanDesc, + ser = serializers.findMapSerializer(config, type, beanDesc, formatOverrides, keySerializer, elementTypeSerializer, elementValueSerializer); if (ser != null) { break; } } @@ -793,10 +778,7 @@ protected JsonSerializer buildMapSerializer(SerializerProvider prov, /** * Helper method that does figures out content inclusion value to use, if any, * and construct re-configured {@link MapSerializer} appropriately. - * - * @since 2.9 */ - @SuppressWarnings("deprecation") protected MapSerializer _checkMapContentInclusion(SerializerProvider prov, BeanDescription beanDesc, MapSerializer mapSer) throws JsonMappingException @@ -809,9 +791,6 @@ protected MapSerializer _checkMapContentInclusion(SerializerProvider prov, JsonInclude.Include incl = (inclV == null) ? JsonInclude.Include.USE_DEFAULTS : inclV.getContentInclusion(); if (incl == JsonInclude.Include.USE_DEFAULTS || incl == JsonInclude.Include.ALWAYS) { - if (!prov.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES)) { - return mapSer.withContentInclusion(null, true); - } return mapSer; } @@ -853,27 +832,22 @@ protected MapSerializer _checkMapContentInclusion(SerializerProvider prov, return mapSer.withContentInclusion(valueToSuppress, suppressNulls); } - /** - * @since 2.9 - */ - protected JsonSerializer buildMapEntrySerializer(SerializerProvider prov, - JavaType type, BeanDescription beanDesc, boolean staticTyping, + protected JsonSerializer buildMapEntrySerializer(SerializerProvider ctxt, + JavaType type, BeanDescription beanDesc, JsonFormat.Value effectiveFormat, + boolean staticTyping, JavaType keyType, JavaType valueType) throws JsonMappingException { // [databind#865]: Allow serialization "as POJO" -- note: to undo, declare // serialization as `Shape.NATURAL` instead; that's JSON Object too. - JsonFormat.Value formatOverride = prov.getDefaultPropertyFormat(Map.Entry.class); - JsonFormat.Value formatFromAnnotation = beanDesc.findExpectedFormat(null); - JsonFormat.Value format = JsonFormat.Value.merge(formatFromAnnotation, formatOverride); - if (format.getShape() == JsonFormat.Shape.OBJECT) { + if (effectiveFormat.getShape() == JsonFormat.Shape.POJO) { return null; } - MapEntrySerializer ser = new MapEntrySerializer(valueType, keyType, valueType, - staticTyping, createTypeSerializer(prov.getConfig(), valueType), null); + MapEntrySerializer ser = new MapEntrySerializer(valueType, keyType, + valueType, staticTyping, ctxt.findTypeSerializer(valueType), null); final JavaType contentType = ser.getContentType(); - JsonInclude.Value inclV = _findInclusionWithContent(prov, beanDesc, + JsonInclude.Value inclV = _findInclusionWithContent(ctxt, beanDesc, contentType, Map.Entry.class); // Need to support global legacy setting, for now: @@ -905,11 +879,11 @@ protected JsonSerializer buildMapEntrySerializer(SerializerProvider prov, valueToSuppress = MapSerializer.MARKER_FOR_EMPTY; break; case CUSTOM: - valueToSuppress = prov.includeFilterInstance(null, inclV.getContentFilter()); + valueToSuppress = ctxt.includeFilterInstance(null, inclV.getContentFilter()); if (valueToSuppress == null) { // is this legal? suppressNulls = true; } else { - suppressNulls = prov.includeFilterSuppressNulls(valueToSuppress); + suppressNulls = ctxt.includeFilterSuppressNulls(valueToSuppress); } break; case NON_NULL: @@ -962,9 +936,9 @@ protected JsonInclude.Value _findInclusionWithContent(SerializerProvider prov, } /* - /********************************************************** + /********************************************************************** /* Factory methods, for Arrays - /********************************************************** + /********************************************************************** */ /** @@ -972,7 +946,7 @@ protected JsonInclude.Value _findInclusionWithContent(SerializerProvider prov, * Object[] (and subtypes, except for String). */ protected JsonSerializer buildArraySerializer(SerializerProvider prov, - ArrayType type, BeanDescription beanDesc, + ArrayType type, BeanDescription beanDesc, JsonFormat.Value formatOverrides, boolean staticTyping, TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) throws JsonMappingException @@ -985,8 +959,8 @@ protected JsonSerializer buildArraySerializer(SerializerProvider prov, JsonSerializer ser = null; for (Serializers serializers : customSerializers()) { // (1) Custom - ser = serializers.findArraySerializer(config, - type, beanDesc, elementTypeSerializer, elementValueSerializer); + ser = serializers.findArraySerializer(config, type, beanDesc, formatOverrides, + elementTypeSerializer, elementValueSerializer); if (ser != null) { break; } @@ -1018,48 +992,60 @@ protected JsonSerializer buildArraySerializer(SerializerProvider prov, } /* - /********************************************************** + /********************************************************************** /* Factory methods for Reference types - /* (demoted from BeanSF down here in 2.9) - /********************************************************** + /********************************************************************** */ - /** - * @since 2.7 - */ - public JsonSerializer findReferenceSerializer(SerializerProvider prov, ReferenceType refType, - BeanDescription beanDesc, boolean staticTyping) + public JsonSerializer findReferenceSerializer(SerializerProvider ctxt, + ReferenceType refType, BeanDescription beanDesc, JsonFormat.Value format, + boolean staticTyping) throws JsonMappingException { JavaType contentType = refType.getContentType(); TypeSerializer contentTypeSerializer = contentType.getTypeHandler(); - final SerializationConfig config = prov.getConfig(); + final SerializationConfig config = ctxt.getConfig(); if (contentTypeSerializer == null) { - contentTypeSerializer = createTypeSerializer(config, contentType); + contentTypeSerializer = ctxt.findTypeSerializer(contentType); } JsonSerializer contentSerializer = contentType.getValueHandler(); for (Serializers serializers : customSerializers()) { - JsonSerializer ser = serializers.findReferenceSerializer(config, refType, beanDesc, + JsonSerializer ser = serializers.findReferenceSerializer(config, refType, beanDesc, format, contentTypeSerializer, contentSerializer); if (ser != null) { return ser; } } if (refType.isTypeOrSubTypeOf(AtomicReference.class)) { - return buildAtomicReferenceSerializer(prov, refType, beanDesc, staticTyping, + return _buildReferenceSerializer(ctxt, AtomicReference.class, + refType, beanDesc, staticTyping, + contentTypeSerializer, contentSerializer); + } + if (refType.isTypeOrSubTypeOf(Optional.class)) { + return _buildReferenceSerializer(ctxt, Optional.class, + refType, beanDesc, staticTyping, contentTypeSerializer, contentSerializer); } + if (refType.isTypeOrSubTypeOf(OptionalInt.class)) { + return new OptionalIntSerializer(); + } + if (refType.isTypeOrSubTypeOf(OptionalLong.class)) { + return new OptionalLongSerializer(); + } + if (refType.isTypeOrSubTypeOf(OptionalDouble.class)) { + return new OptionalDoubleSerializer(); + } return null; } - protected JsonSerializer buildAtomicReferenceSerializer(SerializerProvider prov, + protected JsonSerializer _buildReferenceSerializer(SerializerProvider prov, Class baseType, ReferenceType refType, BeanDescription beanDesc, boolean staticTyping, TypeSerializer contentTypeSerializer, JsonSerializer contentSerializer) throws JsonMappingException { final JavaType contentType = refType.getReferencedType(); JsonInclude.Value inclV = _findInclusionWithContent(prov, beanDesc, - contentType, AtomicReference.class); + contentType, baseType); // Need to support global legacy setting, for now: JsonInclude.Include incl = (inclV == null) ? JsonInclude.Include.USE_DEFAULTS : inclV.getContentInclusion(); @@ -1102,50 +1088,53 @@ protected JsonSerializer buildAtomicReferenceSerializer(SerializerProvider pr break; } } - AtomicReferenceSerializer ser = new AtomicReferenceSerializer(refType, staticTyping, - contentTypeSerializer, contentSerializer); + ReferenceTypeSerializer ser; + if (baseType == Optional.class) { + ser = new Jdk8OptionalSerializer(refType, staticTyping, + contentTypeSerializer, contentSerializer); + } else { + ser = new AtomicReferenceSerializer(refType, staticTyping, + contentTypeSerializer, contentSerializer); + } return ser.withContentInclusion(valueToSuppress, suppressNulls); } /* - /********************************************************** + /********************************************************************** /* Factory methods, for non-container types - /********************************************************** + /********************************************************************** */ - /** - * @since 2.5 - */ - protected JsonSerializer buildIteratorSerializer(SerializationConfig config, - JavaType type, BeanDescription beanDesc, boolean staticTyping, + protected JsonSerializer buildIteratorSerializer(SerializerProvider ctxt, + JavaType type, BeanDescription beanDesc, JsonFormat.Value formatOverrides, + boolean staticTyping, JavaType valueType) throws JsonMappingException { - return new IteratorSerializer(valueType, staticTyping, createTypeSerializer(config, valueType)); + return new IteratorSerializer(valueType, staticTyping, + ctxt.findTypeSerializer(valueType)); } - /** - * @since 2.5 - */ - protected JsonSerializer buildIterableSerializer(SerializationConfig config, - JavaType type, BeanDescription beanDesc, boolean staticTyping, + protected JsonSerializer buildIterableSerializer(SerializerProvider ctxt, + JavaType type, BeanDescription beanDesc, JsonFormat.Value effectiveFormat, + boolean staticTyping, JavaType valueType) throws JsonMappingException { - return new IterableSerializer(valueType, staticTyping, createTypeSerializer(config, valueType)); + return new IterableSerializer(valueType, staticTyping, + ctxt.findTypeSerializer(valueType)); } - protected JsonSerializer buildEnumSerializer(SerializationConfig config, - JavaType type, BeanDescription beanDesc) + protected JsonSerializer buildEnumSerializer(SerializerProvider ctxt, + JavaType type, BeanDescription beanDesc, JsonFormat.Value effectiveFormat) throws JsonMappingException { - /* 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. - */ - JsonFormat.Value format = beanDesc.findExpectedFormat(null); - if (format != null && format.getShape() == JsonFormat.Shape.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. + if (effectiveFormat.getShape() == JsonFormat.Shape.POJO) { // one special case: suppress serialization of "getDeclaringClass()"... ((BasicBeanDescription) beanDesc).removeProperty("declaringClass"); // returning null will mean that eventually BeanSerializer gets constructed @@ -1153,8 +1142,8 @@ protected JsonSerializer buildEnumSerializer(SerializationConfig config, } @SuppressWarnings("unchecked") Class> enumClass = (Class>) type.getRawClass(); - JsonSerializer ser = EnumSerializer.construct(enumClass, config, beanDesc, format); - // [databind#120]: Allow post-processing + final SerializationConfig config = ctxt.getConfig(); + JsonSerializer ser = EnumSerializer.construct(enumClass, config, beanDesc, effectiveFormat); if (_factoryConfig.hasSerializerModifiers()) { for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) { ser = mod.modifyEnumSerializer(config, type, beanDesc, ser); @@ -1164,11 +1153,27 @@ protected JsonSerializer buildEnumSerializer(SerializationConfig config, } /* - /********************************************************** + /********************************************************************** /* Other helper methods - /********************************************************** + /********************************************************************** */ + /** + * Helper method that will combine all available pieces of format configuration + * and calculate effective format settings to use. + * + * @since 3.0 + */ + protected JsonFormat.Value _calculateEffectiveFormat(BeanDescription beanDesc, + Class baseType, JsonFormat.Value formatOverrides) + { + JsonFormat.Value fromType = beanDesc.findExpectedFormat(baseType); + if (formatOverrides == null) { + return fromType; + } + return JsonFormat.Value.merge(fromType, formatOverrides); + } + /** * Helper method called to try to find whether there is an annotation in the * class that indicates key serializer to use. @@ -1179,11 +1184,8 @@ protected JsonSerializer _findKeySerializer(SerializerProvider prov, throws JsonMappingException { AnnotationIntrospector intr = prov.getAnnotationIntrospector(); - Object serDef = intr.findKeySerializer(a); - if (serDef != null) { - return prov.serializerInstance(a, serDef); - } - return null; + Object serDef = intr.findKeySerializer(prov.getConfig(), a); + return prov.serializerInstance(a, serDef); } /** @@ -1196,11 +1198,8 @@ protected JsonSerializer _findContentSerializer(SerializerProvider prov, throws JsonMappingException { AnnotationIntrospector intr = prov.getAnnotationIntrospector(); - Object serDef = intr.findContentSerializer(a); - if (serDef != null) { - return prov.serializerInstance(a, serDef); - } - return null; + Object serDef = intr.findContentSerializer(prov.getConfig(), a); + return prov.serializerInstance(a, serDef); // ok to pass null } /** @@ -1216,41 +1215,20 @@ protected Object findFilterId(SerializationConfig config, BeanDescription beanDe * annotations for the bean class indicate that static typing * (declared types) should be used for properties. * (instead of dynamic runtime types). - * - * @since 2.1 (earlier had variant with additional 'property' parameter) */ protected boolean usesStaticTyping(SerializationConfig config, BeanDescription beanDesc, TypeSerializer typeSer) { - /* 16-Aug-2010, tatu: If there is a (value) type serializer, we cannot force - * static typing; that would make it impossible to handle expected subtypes - */ + // 16-Aug-2010, tatu: If there is a (value) type serializer, we cannot force + // static typing; that would make it impossible to handle expected subtypes if (typeSer != null) { return false; } AnnotationIntrospector intr = config.getAnnotationIntrospector(); - JsonSerialize.Typing t = intr.findSerializationTyping(beanDesc.getClassInfo()); + JsonSerialize.Typing t = intr.findSerializationTyping(config, beanDesc.getClassInfo()); if (t != null && t != JsonSerialize.Typing.DEFAULT_TYPING) { return (t == JsonSerialize.Typing.STATIC); } return config.isEnabled(MapperFeature.USE_STATIC_TYPING); } - - // Commented out in 2.9 - /* - protected Class _verifyAsClass(Object src, String methodName, Class noneClass) - { - if (src == null) { - return null; - } - if (!(src instanceof Class)) { - throw new IllegalStateException("AnnotationIntrospector."+methodName+"() returned value of type "+src.getClass().getName()+": expected type JsonSerializer or Class instead"); - } - Class cls = (Class) src; - if (cls == noneClass || ClassUtil.isBogusClass(cls)) { - return null; - } - return cls; - } - */ } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyFilter.java b/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyFilter.java deleted file mode 100644 index 1aa4acdcb0..0000000000 --- a/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyFilter.java +++ /dev/null @@ -1,94 +0,0 @@ -package com.fasterxml.jackson.databind.ser; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; -import com.fasterxml.jackson.databind.node.ObjectNode; - -/** - * Interface that defines API for filter objects use (as configured - * using {@link com.fasterxml.jackson.annotation.JsonFilter}) - * for filtering bean properties to serialize. - *

    - * Starting with version 2.3 this class is deprecated; use - * {@link PropertyFilter} instead. - * - * @deprecated Since 2.3: use {@link PropertyFilter} instead. - */ -@Deprecated -public interface BeanPropertyFilter -{ - /** - * Method called by {@link BeanSerializer} to let filter decide what to do with - * given bean property value: the usual choices are to either filter out (i.e. - * do nothing) or write using given {@link BeanPropertyWriter}, although filters - * can choose other to do something different altogether. - *

    - * Typical implementation is something like: - *

    -     * if (include(writer)) {
    -     *      writer.serializeAsField(pojo, jgen, prov);
    -     * }
    -     *
    - * - * @param pojo Object that contains property value to serialize - * @param jgen Generator use for serializing value - * @param prov Provider that can be used for accessing dynamic aspects of serialization - * processing - * @param writer Default bean property serializer to use - */ - public void serializeAsField(Object pojo, JsonGenerator jgen, SerializerProvider prov, - BeanPropertyWriter writer) - throws Exception; - - /** - * Method called by {@link BeanSerializer} to let the filter determine whether, and in what - * form the given property exist within the parent, or root, schema. Filters can omit - * adding the property to the node, or choose the form of the schema value for the property. - *

    - * Typical implementation is something like: - *

    -     * if (include(writer)) {
    -     *      writer.depositSchemaProperty(propertiesNode, provider);
    -     * }
    -     *
    - * - * @param writer Bean property writer to use to create schema value - * @param propertiesNode Node which the given property would exist within - * @param provider Provider that can be used for accessing dynamic aspects of serialization - * processing - * - * @since 2.1 - * @deprecated Since 2.3: new code should use the alternative depositSchemaProperty - * method - */ - @Deprecated - public void depositSchemaProperty(BeanPropertyWriter writer, ObjectNode propertiesNode, - SerializerProvider provider) - throws JsonMappingException; - - /** - * Method called by {@link BeanSerializer} to let the filter determine whether, and in what - * form the given property exist within the parent, or root, schema. Filters can omit - * adding the property to the node, or choose the form of the schema value for the property - *

    - * Typical implementation is something like: - *

    -     * if (include(writer)) {
    -     *      writer.depositSchemaProperty(objectVisitor, provider);
    -     * }
    -     *
    - * - * @param writer Bean property serializer to use to create schema value - * @param objectVisitor JsonObjectFormatVisitor which should be aware of - * the property's existence - * @param provider Provider that can be used for accessing dynamic aspects of serialization - * processing - * - * @since 2.1 - */ - public void depositSchemaProperty(BeanPropertyWriter writer, JsonObjectFormatVisitor objectVisitor, - SerializerProvider provider) - throws JsonMappingException; -} 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 51cb1c2cb3..8576331159 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyWriter.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyWriter.java @@ -3,18 +3,18 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; -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; + import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; import com.fasterxml.jackson.databind.introspect.*; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; -import com.fasterxml.jackson.databind.jsonschema.SchemaAware; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap; @@ -34,13 +34,11 @@ * this is to reduce likelihood of data corruption and synchronization issues. */ @JacksonStdImpl -// since 2.6. NOTE: sub-classes typically are not public class BeanPropertyWriter extends PropertyWriter // which extends // `ConcreteBeanPropertyBase` - implements java.io.Serializable // since 2.6 + implements java.io.Serializable { - // As of 2.7 - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 3L; /** * Marker object used to indicate "do not serialize if empty" @@ -66,8 +64,6 @@ public class BeanPropertyWriter extends PropertyWriter // which extends /** * Wrapper name to use for this element, if any - * - * @since 2.2 */ protected final PropertyName _wrapperName; @@ -247,24 +243,10 @@ public BeanPropertyWriter(BeanPropertyDefinition propDef, _includeInViews = includeInViews; } - @Deprecated // Since 2.9 - public BeanPropertyWriter(BeanPropertyDefinition propDef, - AnnotatedMember member, Annotations contextAnnotations, - JavaType declaredType, - JsonSerializer ser, TypeSerializer typeSer, JavaType serType, - boolean suppressNulls, Object suppressableValue) - { - this(propDef, member, contextAnnotations, declaredType, - ser, typeSer, serType, suppressNulls, suppressableValue, - null); - } - /** * Constructor that may be of use to virtual properties, when there is need * for the zero-arg ("default") constructor, and actual initialization is * done after constructor call. - * - * @since 2.5 */ protected BeanPropertyWriter() { super(PropertyMetadata.STD_REQUIRED_OR_OPTIONAL); @@ -296,16 +278,12 @@ protected BeanPropertyWriter(BeanPropertyWriter base) { this(base, base._name); } - /** - * @since 2.5 - */ protected BeanPropertyWriter(BeanPropertyWriter base, PropertyName name) { super(base); /* * 02-Dec-2014, tatu: This is a big mess, alas, what with dependency to - * MapperConfig to encode, and Afterburner having heartburn for - * SerializableString (vs SerializedString). Hope it can be - * resolved/reworked in 2.6 timeframe, if not for 2.5 + * MapperConfig to encode, and Afterburner having heart-burn for + * SerializableString (vs SerializedString). */ _name = new SerializedString(name.getSimpleName()); _wrapperName = base._wrapperName; @@ -368,18 +346,17 @@ public BeanPropertyWriter rename(NameTransformer transformer) { /** * Overridable factory method used by sub-classes - * - * @since 2.6 */ protected BeanPropertyWriter _new(PropertyName newName) { + if (getClass() != BeanPropertyWriter.class) { + throw new IllegalStateException("Method must be overridden by "+getClass()); + } return new BeanPropertyWriter(this, newName); } /** * Method called to set, reset or clear the configured type serializer for * property. - * - * @since 2.6 */ public void assignTypeSerializer(TypeSerializer typeSer) { _typeSerializer = typeSer; @@ -432,8 +409,6 @@ public void setNonTrivialBaseType(JavaType t) { * Method called to ensure that the mutator has proper access rights to * be called, as per configuration. Overridden by implementations that * have mutators that require access, fields and setters. - * - * @since 2.8.3 */ public void fixAccess(SerializationConfig config) { _member.fixAccess(config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); @@ -627,44 +602,6 @@ public JavaType getSerializationType() { return _cfgSerializationType; } - @Deprecated // since 2.9 - public Class getRawSerializationType() { - return (_cfgSerializationType == null) ? null : _cfgSerializationType - .getRawClass(); - } - - /** - * @deprecated Since 2.7, to be removed from 2.9, use {@link #getType()} instead. - */ - @Deprecated - public Class getPropertyType() { - if (_accessorMethod != null) { - return _accessorMethod.getReturnType(); - } - if (_field != null) { - return _field.getType(); - } - return null; - } - - /** - * Get the generic property type of this property writer. - * - * @return The property type, or null if not found. - * - * @deprecated Since 2.7, to be removed from 2.9, use {@link #getType()} instead. - */ - @Deprecated - public Type getGenericPropertyType() { - if (_accessorMethod != null) { - return _accessorMethod.getGenericReturnType(); - } - if (_field != null) { - return _field.getGenericType(); - } - return null; - } - public Class[] getViews() { return _includeInViews; } @@ -749,8 +686,6 @@ public void serializeAsOmittedField(Object bean, JsonGenerator gen, * Alternative to {@link #serializeAsField} that is used when a POJO is * serialized as JSON Array; the difference is that no field names are * written. - * - * @since 2.3 */ @Override public void serializeAsElement(Object bean, JsonGenerator gen, @@ -831,7 +766,8 @@ public void serializeAsPlaceholder(Object bean, JsonGenerator gen, // Also part of BeanProperty implementation @Override public void depositSchemaProperty(JsonObjectFormatVisitor v, - SerializerProvider provider) throws JsonMappingException { + SerializerProvider provider) throws JsonMappingException + { if (v != null) { if (isRequired()) { v.property(this); @@ -841,61 +777,23 @@ public void depositSchemaProperty(JsonObjectFormatVisitor v, } } - // // // Legacy support for JsonFormatVisitable - - /** - * Attempt to add the output of the given {@link BeanPropertyWriter} in the - * given {@link ObjectNode}. Otherwise, add the default schema - * {@link JsonNode} in place of the writer's output - * - * @param propertiesNode - * Node which the given property would exist within - * @param provider - * Provider that can be used for accessing dynamic aspects of - * serialization processing - */ - @Override - @Deprecated - public void depositSchemaProperty(ObjectNode propertiesNode, - SerializerProvider provider) throws JsonMappingException { - JavaType propType = getSerializationType(); - // 03-Dec-2010, tatu: SchemaAware REALLY should use JavaType, but alas - // it doesn't... - Type hint = (propType == null) ? getType() : propType.getRawClass(); - JsonNode schemaNode; - // Maybe it already has annotated/statically configured serializer? - JsonSerializer ser = getSerializer(); - if (ser == null) { // nope - ser = provider.findValueSerializer(getType(), this); - } - boolean isOptional = !isRequired(); - if (ser instanceof SchemaAware) { - schemaNode = ((SchemaAware) ser).getSchema(provider, hint, - isOptional); - } else { - schemaNode = com.fasterxml.jackson.databind.jsonschema.JsonSchema - .getDefaultSchemaNode(); - } - _depositSchemaProperty(propertiesNode, schemaNode); - } - /* - /********************************************************** + /********************************************************************** /* Helper methods - /********************************************************** + /********************************************************************** */ - protected JsonSerializer _findAndAddDynamic( - PropertySerializerMap map, Class type, - SerializerProvider provider) throws JsonMappingException { - PropertySerializerMap.SerializerAndMapResult result; + protected JsonSerializer _findAndAddDynamic(PropertySerializerMap map, + Class rawType, SerializerProvider provider) throws JsonMappingException + { + JavaType t; if (_nonTrivialBaseType != null) { - JavaType t = provider.constructSpecializedType(_nonTrivialBaseType, - type); - result = map.findAndAddPrimarySerializer(t, provider, this); + t = provider.constructSpecializedType(_nonTrivialBaseType, + rawType); } else { - result = map.findAndAddPrimarySerializer(type, provider, this); + t = provider.constructType(rawType); } + PropertySerializerMap.SerializerAndMapResult result = map.findAndAddPrimarySerializer(t, provider, this); // did we get a new map of serializers? If so, start using it if (map != result.map) { _dynamicSerializers = result.map; 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 9813e4b70e..b81f9ebd23 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializer.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; import com.fasterxml.jackson.databind.ser.impl.BeanAsArraySerializer; import com.fasterxml.jackson.databind.ser.impl.ObjectIdWriter; import com.fasterxml.jackson.databind.ser.impl.UnwrappingBeanSerializer; @@ -23,10 +24,11 @@ * done from {@link #resolve} method, and NOT from constructor; * otherwise we could end up with an infinite loop. */ +@JacksonStdImpl public class BeanSerializer extends BeanSerializerBase { - private static final long serialVersionUID = 29; // as per jackson 2.9 + private static final long serialVersionUID = 30; // as per jackson 3.0 /* /********************************************************** @@ -63,7 +65,7 @@ protected BeanSerializer(BeanSerializerBase src, ObjectIdWriter objectIdWriter, Object filterId) { super(src, objectIdWriter, filterId); } - + protected BeanSerializer(BeanSerializerBase src, Set toIgnore) { super(src, toIgnore); } @@ -112,22 +114,13 @@ protected BeanSerializerBase withIgnorals(Set toIgnore) { @Override protected BeanSerializerBase asArraySerializer() { - /* Cannot: - * - * - have Object Id (may be allowed in future) - * - have "any getter" - * - have per-property filters - */ - if ((_objectIdWriter == null) - && (_anyGetterWriter == null) - && (_propertyFilterId == null) - ) { - return new BeanAsArraySerializer(this); + if (canCreateArraySerializer()) { + return BeanAsArraySerializer.construct(this); } // already is one, so: return this; } - + /* /********************************************************** /* JsonSerializer implementation that differs between impls @@ -144,26 +137,24 @@ public final void serialize(Object bean, JsonGenerator gen, SerializerProvider p throws IOException { if (_objectIdWriter != null) { - gen.setCurrentValue(bean); // [databind#631] _serializeWithObjectId(bean, gen, provider, true); return; } - gen.writeStartObject(bean); if (_propertyFilterId != null) { - serializeFieldsFiltered(bean, gen, provider); - } else { - serializeFields(bean, gen, provider); + gen.writeStartObject(bean); + _serializeFieldsFiltered(bean, gen, provider, _propertyFilterId); + gen.writeEndObject(); + return; + } + BeanPropertyWriter[] fProps = _filteredProps; + if ((fProps != null) && (provider.getActiveView() != null)) { + gen.writeStartObject(bean); + _serializeFieldsMaybeView(bean, gen, provider, fProps); + gen.writeEndObject(); + return; } + gen.writeStartObject(bean); + _serializeFieldsNoView(bean, gen, provider, _props); gen.writeEndObject(); } - - /* - /********************************************************** - /* Standard methods - /********************************************************** - */ - - @Override public String toString() { - return "BeanSerializer for "+handledType().getName(); - } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerBuilder.java b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerBuilder.java index c89d380f44..b66938637c 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerBuilder.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerBuilder.java @@ -94,8 +94,6 @@ protected BeanSerializerBuilder(BeanSerializerBuilder src) { *

    * Note: ideally should be passed in constructor, but for backwards * compatibility, needed to add a setter instead - * - * @since 2.1 */ protected void setConfig(SerializationConfig config) { _config = config; @@ -213,6 +211,12 @@ public JsonSerializer build() _typeId.fixAccess(_config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); } } + JsonSerializer ser = UnrolledBeanSerializer.tryConstruct( + _beanDesc.getType(), this, + properties, _filteredProperties); + if (ser != null) { + return ser; + } return new BeanSerializer(_beanDesc.getType(), this, properties, _filteredProperties); } @@ -226,4 +230,3 @@ public BeanSerializer createDummy() { return BeanSerializer.createDummy(_beanDesc.getType()); } } - 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 0040256307..ce51f4af75 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java @@ -2,6 +2,7 @@ import java.util.*; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.ObjectIdGenerator; import com.fasterxml.jackson.annotation.ObjectIdGenerators; @@ -10,8 +11,6 @@ import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.cfg.SerializerFactoryConfig; import com.fasterxml.jackson.databind.introspect.*; -import com.fasterxml.jackson.databind.jsontype.NamedType; -import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; import com.fasterxml.jackson.databind.ser.impl.FilteredBeanPropertyWriter; import com.fasterxml.jackson.databind.ser.impl.ObjectIdWriter; @@ -52,9 +51,9 @@ */ public class BeanSerializerFactory extends BasicSerializerFactory - implements java.io.Serializable // since 2.1 + implements java.io.Serializable { - private static final long serialVersionUID = 1; + private static final long serialVersionUID = 3; /** * Like {@link BasicSerializerFactory}, this factory is stateless, and @@ -64,9 +63,9 @@ public class BeanSerializerFactory public final static BeanSerializerFactory instance = new BeanSerializerFactory(null); /* - /********************************************************** + /********************************************************************** /* Life-cycle: creation, configuration - /********************************************************** + /********************************************************************** */ /** @@ -95,23 +94,14 @@ public SerializerFactory withConfig(SerializerFactoryConfig config) * Instead, let's actually just throw an error if this method is called when subtype * has not properly overridden this method; this to indicate problem as soon as possible. */ - if (getClass() != BeanSerializerFactory.class) { - throw new IllegalStateException("Subtype of BeanSerializerFactory ("+getClass().getName() - +") has not properly overridden method 'withAdditionalSerializers': cannot instantiate subtype with " - +"additional serializer definitions"); - } + ClassUtil.verifyMustOverride(BeanSerializerFactory.class, this, "withConfig"); return new BeanSerializerFactory(config); } - @Override - protected Iterable customSerializers() { - return _factoryConfig.serializers(); - } - /* - /********************************************************** + /********************************************************************** /* SerializerFactory impl - /********************************************************** + /********************************************************************** */ /** @@ -126,17 +116,16 @@ protected Iterable customSerializers() { */ @Override @SuppressWarnings("unchecked") - public JsonSerializer createSerializer(SerializerProvider prov, - JavaType origType) + public JsonSerializer createSerializer(SerializerProvider ctxt, JavaType origType, + BeanDescription beanDesc, JsonFormat.Value formatOverrides) throws JsonMappingException { // Very first thing, let's check if there is explicit serializer annotation: - final SerializationConfig config = prov.getConfig(); - BeanDescription beanDesc = config.introspect(origType); - JsonSerializer ser = findSerializerFromAnnotation(prov, beanDesc.getClassInfo()); + JsonSerializer ser = findSerializerFromAnnotation(ctxt, beanDesc.getClassInfo()); if (ser != null) { return (JsonSerializer) ser; } + final SerializationConfig config = ctxt.getConfig(); boolean staticTyping; // Next: we may have annotations that further indicate actual type to use (a super type) final AnnotationIntrospector intr = config.getAnnotationIntrospector(); @@ -148,7 +137,7 @@ public JsonSerializer createSerializer(SerializerProvider prov, try { type = intr.refineSerializationType(config, beanDesc.getClassInfo(), origType); } catch (JsonMappingException e) { - return prov.reportBadTypeDefinition(beanDesc, e.getMessage()); + return ctxt.reportBadTypeDefinition(beanDesc, e.getMessage()); } } if (type == origType) { // no changes, won't force static typing @@ -156,35 +145,37 @@ public JsonSerializer createSerializer(SerializerProvider prov, } else { // changes; assume static typing; plus, need to re-introspect if class differs staticTyping = true; if (!type.hasRawClass(origType.getRawClass())) { - beanDesc = config.introspect(type); + beanDesc = ctxt.introspect(type); } } // Slight detour: do we have a Converter to consider? Converter conv = beanDesc.findSerializationConverter(); - if (conv == null) { // no, simple - return (JsonSerializer) _createSerializer2(prov, type, beanDesc, staticTyping); - } - JavaType delegateType = conv.getOutputType(prov.getTypeFactory()); - - // One more twist, as per [databind#288]; probably need to get new BeanDesc - if (!delegateType.hasRawClass(type.getRawClass())) { - beanDesc = config.introspect(delegateType); - // [#359]: explicitly check (again) for @JsonSerializer... - ser = findSerializerFromAnnotation(prov, beanDesc.getClassInfo()); - } - // [databind#731]: Should skip if nominally java.lang.Object - if (ser == null && !delegateType.isJavaLangObject()) { - ser = _createSerializer2(prov, delegateType, beanDesc, true); + if (conv != null) { // yup, need converter + JavaType delegateType = conv.getOutputType(ctxt.getTypeFactory()); + + // One more twist, as per [databind#288]; probably need to get new BeanDesc + if (!delegateType.hasRawClass(type.getRawClass())) { + beanDesc = ctxt.introspect(delegateType); + // [#359]: explicitly check (again) for @JsonSerializer... + ser = findSerializerFromAnnotation(ctxt, beanDesc.getClassInfo()); + } + // [databind#731]: Should skip if nominally java.lang.Object + if ((ser == null) && !delegateType.isJavaLangObject()) { + ser = _createSerializer2(ctxt, beanDesc, delegateType, formatOverrides, true); + } + return new StdDelegatingSerializer(conv, delegateType, ser, null); } - return new StdDelegatingSerializer(conv, delegateType, ser); + // No, regular serializer + return (JsonSerializer) _createSerializer2(ctxt, beanDesc, type, formatOverrides, staticTyping); } - protected JsonSerializer _createSerializer2(SerializerProvider prov, - JavaType type, BeanDescription beanDesc, boolean staticTyping) + protected JsonSerializer _createSerializer2(SerializerProvider ctxt, + BeanDescription beanDesc, JavaType type, JsonFormat.Value formatOverrides, + boolean staticTyping) throws JsonMappingException { JsonSerializer ser = null; - final SerializationConfig config = prov.getConfig(); + final SerializationConfig config = ctxt.getConfig(); // Container types differ from non-container types // (note: called method checks for module-provided serializers) @@ -193,18 +184,18 @@ protected JsonSerializer _createSerializer2(SerializerProvider prov, staticTyping = usesStaticTyping(config, beanDesc, null); } // 03-Aug-2012, tatu: As per [databind#40], may require POJO serializer... - ser = buildContainerSerializer(prov, type, beanDesc, staticTyping); + ser = buildContainerSerializer(ctxt, type, beanDesc, formatOverrides, staticTyping); // Will return right away, since called method does post-processing: if (ser != null) { return ser; } } else { if (type.isReferenceType()) { - ser = findReferenceSerializer(prov, (ReferenceType) type, beanDesc, staticTyping); + ser = findReferenceSerializer(ctxt, (ReferenceType) type, beanDesc, formatOverrides, staticTyping); } else { // Modules may provide serializers of POJO types: for (Serializers serializers : customSerializers()) { - ser = serializers.findSerializer(config, type, beanDesc); + ser = serializers.findSerializer(config, type, beanDesc, formatOverrides); if (ser != null) { break; } @@ -213,7 +204,7 @@ protected JsonSerializer _createSerializer2(SerializerProvider prov, // 25-Jun-2015, tatu: Then JsonSerializable, @JsonValue etc. NOTE! Prior to 2.6, // this call was BEFORE custom serializer lookup, which was wrong. if (ser == null) { - ser = findSerializerByAnnotations(prov, type, beanDesc); + ser = findSerializerByAnnotations(ctxt, type, beanDesc); } } @@ -221,161 +212,97 @@ protected JsonSerializer _createSerializer2(SerializerProvider prov, // Otherwise, we will check "primary types"; both marker types that // indicate specific handling (JsonSerializable), or main types that have // precedence over container types - ser = findSerializerByLookup(type, config, beanDesc, staticTyping); + ser = findSerializerByLookup(type, config, beanDesc, formatOverrides, staticTyping); if (ser == null) { - ser = findSerializerByPrimaryType(prov, type, beanDesc, staticTyping); + ser = findSerializerByPrimaryType(ctxt, type, beanDesc, formatOverrides, staticTyping); if (ser == null) { // And this is where this class comes in: if type is not a // known "primary JDK type", perhaps it's a bean? We can still // get a null, if we can't find a single suitable bean property. - ser = findBeanSerializer(prov, type, beanDesc); + ser = findBeanSerializer(ctxt, beanDesc, type, formatOverrides); // 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); + ser = findSerializerByAddonType(ctxt, type, beanDesc, formatOverrides, 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()); + ser = ctxt.getUnknownTypeSerializer(beanDesc.getBeanClass()); } } } } } - if (ser != null) { - // [databind#120]: Allow post-processing - if (_factoryConfig.hasSerializerModifiers()) { - for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) { - ser = mod.modifySerializer(config, beanDesc, ser); - } + // can not be null any more (always get at least "unknown" serializer) + if (_factoryConfig.hasSerializerModifiers()) { + for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) { + ser = mod.modifySerializer(config, beanDesc, ser); } } return ser; } - + /* - /********************************************************** - /* Other public methods that are not part of - /* JsonSerializerFactory API - /********************************************************** + /********************************************************************** + /* Other public methods that are not part of `SerializerFactory` API + /********************************************************************** */ /** * Method that will try to construct a {@link BeanSerializer} for * given class. Returns null if no properties are found. */ - public JsonSerializer findBeanSerializer(SerializerProvider prov, JavaType type, - BeanDescription beanDesc) + public JsonSerializer findBeanSerializer(SerializerProvider ctxt, BeanDescription beanDesc, + JavaType type, JsonFormat.Value format) throws JsonMappingException { // First things first: we know some types are not beans... if (!isPotentialBeanType(type.getRawClass())) { - // 03-Aug-2012, tatu: Except we do need to allow serializers for Enums, - // as per [databind#24] + // Except we do need to allow serializers for Enums, if shape dictates (which it does + // if we end up here) if (!type.isEnumType()) { return null; } } - return constructBeanSerializer(prov, beanDesc); - } - - /** - * Method called to create a type information serializer for values of given - * non-container property - * if one is needed. If not needed (no polymorphic handling configured), should - * return null. - * - * @param baseType Declared type to use as the base type for type information serializer - * - * @return Type serializer to use for property values, if one is needed; null if not. - */ - public TypeSerializer findPropertyTypeSerializer(JavaType baseType, - SerializationConfig config, AnnotatedMember accessor) - throws JsonMappingException - { - AnnotationIntrospector ai = config.getAnnotationIntrospector(); - TypeResolverBuilder b = ai.findPropertyTypeResolver(config, accessor, baseType); - TypeSerializer typeSer; - - // Defaulting: if no annotations on member, check value class - if (b == null) { - typeSer = createTypeSerializer(config, baseType); - } else { - Collection subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByClass( - config, accessor, baseType); - typeSer = b.buildTypeSerializer(config, baseType, subtypes); - } - return typeSer; - } - - /** - * Method called to create a type information serializer for values of given - * container property - * if one is needed. If not needed (no polymorphic handling configured), should - * return null. - * - * @param containerType Declared type of the container to use as the base type for type information serializer - * - * @return Type serializer to use for property value contents, if one is needed; null if not. - */ - public TypeSerializer findPropertyContentTypeSerializer(JavaType containerType, - SerializationConfig config, AnnotatedMember accessor) - throws JsonMappingException - { - JavaType contentType = containerType.getContentType(); - AnnotationIntrospector ai = config.getAnnotationIntrospector(); - TypeResolverBuilder b = ai.findPropertyContentTypeResolver(config, accessor, containerType); - TypeSerializer typeSer; - - // Defaulting: if no annotations on member, check value class - if (b == null) { - typeSer = createTypeSerializer(config, contentType); - } else { - Collection subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByClass(config, - accessor, contentType); - typeSer = b.buildTypeSerializer(config, contentType, subtypes); - } - return typeSer; + return constructBeanSerializer(ctxt, beanDesc); } /* - /********************************************************** + /********************************************************************** /* Overridable non-public factory methods - /********************************************************** + /********************************************************************** */ /** * Method called to construct serializer for serializing specified bean type. - * - * @since 2.1 */ @SuppressWarnings("unchecked") - protected JsonSerializer constructBeanSerializer(SerializerProvider prov, + protected JsonSerializer constructBeanSerializer(SerializerProvider ctxt, BeanDescription beanDesc) throws JsonMappingException { // 13-Oct-2010, tatu: quick sanity check: never try to create bean serializer for plain Object // 05-Jul-2012, tatu: ... but we should be able to just return "unknown type" serializer, right? if (beanDesc.getBeanClass() == Object.class) { - return prov.getUnknownTypeSerializer(Object.class); + return ctxt.getUnknownTypeSerializer(Object.class); // throw new IllegalArgumentException("Cannot create bean serializer for Object.class"); } - final SerializationConfig config = prov.getConfig(); + final SerializationConfig config = ctxt.getConfig(); BeanSerializerBuilder builder = constructBeanSerializerBuilder(beanDesc); builder.setConfig(config); // First: any detectable (auto-detect, annotations) properties to serialize? - List props = findBeanProperties(prov, beanDesc, builder); + List props = findBeanProperties(ctxt, beanDesc, builder); if (props == null) { props = new ArrayList(); } else { - props = removeOverlappingTypeIds(prov, beanDesc, builder, props); + props = removeOverlappingTypeIds(ctxt, beanDesc, builder, props); } // [databind#638]: Allow injection of "virtual" properties: - prov.getAnnotationIntrospector().findAndAddVirtualProperties(config, beanDesc.getClassInfo(), props); + ctxt.getAnnotationIntrospector().findAndAddVirtualProperties(config, beanDesc.getClassInfo(), props); - // [JACKSON-440] Need to allow modification bean properties to serialize: + // allow modification bean properties to serialize if (_factoryConfig.hasSerializerModifiers()) { for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) { props = mod.changeProperties(config, beanDesc, props); @@ -395,7 +322,7 @@ protected JsonSerializer constructBeanSerializer(SerializerProvider prov // And if Object Id is needed, some preparation for that as well: better // do before view handling, mostly for the custom id case which needs // access to a property - builder.setObjectIdWriter(constructObjectIdHandler(prov, beanDesc, props)); + builder.setObjectIdWriter(constructObjectIdHandler(ctxt, beanDesc, props)); builder.setProperties(props); builder.setFilterId(findFilterId(config, beanDesc)); @@ -406,10 +333,10 @@ protected JsonSerializer constructBeanSerializer(SerializerProvider prov // copied from BasicSerializerFactory.buildMapSerializer(): boolean staticTyping = config.isEnabled(MapperFeature.USE_STATIC_TYPING); JavaType valueType = type.getContentType(); - TypeSerializer typeSer = createTypeSerializer(config, valueType); + TypeSerializer typeSer = ctxt.findTypeSerializer(valueType); // last 2 nulls; don't know key, value serializers (yet) // 23-Feb-2015, tatu: As per [databind#705], need to support custom serializers - JsonSerializer anySer = findSerializerFromAnnotation(prov, anyGetter); + JsonSerializer anySer = findSerializerFromAnnotation(ctxt, anyGetter); if (anySer == null) { // TODO: support '@JsonIgnoreProperties' with any setter? anySer = MapSerializer.construct(/* ignored props*/ (Set) null, @@ -435,7 +362,7 @@ protected JsonSerializer constructBeanSerializer(SerializerProvider prov try { ser = (JsonSerializer) builder.build(); } catch (RuntimeException e) { - prov.reportBadTypeDefinition(beanDesc, "Failed to construct BeanSerializer for %s: (%s) %s", + ctxt.reportBadTypeDefinition(beanDesc, "Failed to construct BeanSerializer for %s: (%s) %s", beanDesc.getType(), e.getClass().getName(), e.getMessage()); } if (ser == null) { @@ -449,7 +376,7 @@ protected JsonSerializer constructBeanSerializer(SerializerProvider prov return ser; } - protected ObjectIdWriter constructObjectIdHandler(SerializerProvider prov, + protected ObjectIdWriter constructObjectIdHandler(SerializerProvider ctxt, BeanDescription beanDesc, List props) throws JsonMappingException { @@ -489,10 +416,10 @@ protected ObjectIdWriter constructObjectIdHandler(SerializerProvider prov, } // other types are simpler - JavaType type = prov.constructType(implClass); + JavaType type = ctxt.constructType(implClass); // Could require type to be passed explicitly, but we should be able to find it too: - JavaType idType = prov.getTypeFactory().findTypeParameters(type, ObjectIdGenerator.class)[0]; - gen = prov.objectIdGeneratorInstance(beanDesc.getClassInfo(), objectIdInfo); + JavaType idType = ctxt.getTypeFactory().findTypeParameters(type, ObjectIdGenerator.class)[0]; + gen = ctxt.objectIdGeneratorInstance(beanDesc.getClassInfo(), objectIdInfo); return ObjectIdWriter.construct(idType, objectIdInfo.getPropertyName(), gen, objectIdInfo.getAlwaysAsId()); } @@ -519,11 +446,11 @@ protected BeanSerializerBuilder constructBeanSerializerBuilder(BeanDescription b } /* - /********************************************************** + /********************************************************************** /* Overridable non-public introspection methods - /********************************************************** + /********************************************************************** */ - + /** * Helper method used to skip processing for types that we know * cannot be (i.e. are never consider to be) beans: @@ -541,12 +468,12 @@ protected boolean isPotentialBeanType(Class type) * Method used to collect all actual serializable properties. * Can be overridden to implement custom detection schemes. */ - protected List findBeanProperties(SerializerProvider prov, + protected List findBeanProperties(SerializerProvider ctxt, BeanDescription beanDesc, BeanSerializerBuilder builder) throws JsonMappingException { List properties = beanDesc.findProperties(); - final SerializationConfig config = prov.getConfig(); + final SerializationConfig config = ctxt.getConfig(); // ignore specified types removeIgnorableTypes(config, beanDesc, properties); @@ -580,20 +507,20 @@ protected List findBeanProperties(SerializerProvider prov, continue; } if (accessor instanceof AnnotatedMethod) { - result.add(_constructWriter(prov, property, pb, staticTyping, (AnnotatedMethod) accessor)); + result.add(_constructWriter(ctxt, property, pb, staticTyping, (AnnotatedMethod) accessor)); } else { - result.add(_constructWriter(prov, property, pb, staticTyping, (AnnotatedField) accessor)); + result.add(_constructWriter(ctxt, property, pb, staticTyping, (AnnotatedField) accessor)); } } return result; } /* - /********************************************************** + /********************************************************************** /* Overridable non-public methods for manipulating bean properties - /********************************************************** + /********************************************************************** */ - + /** * Overridable method that can filter out properties. Default implementation * checks annotations class may have. @@ -610,12 +537,7 @@ protected List filterBeanProperties(SerializationConfig conf if (ignorals != null) { Set ignored = ignorals.findIgnoredForSerialization(); if (!ignored.isEmpty()) { - Iterator it = props.iterator(); - while (it.hasNext()) { - if (ignored.contains(it.next().getName())) { - it.remove(); - } - } + props.removeIf(beanPropertyWriter -> ignored.contains(beanPropertyWriter.getName())); } } return props; @@ -673,10 +595,9 @@ protected void removeIgnorableTypes(SerializationConfig config, BeanDescription while (it.hasNext()) { BeanPropertyDefinition property = it.next(); AnnotatedMember accessor = property.getAccessor(); - /* 22-Oct-2016, tatu: Looks like this removal is an important part of - * processing, as taking it out will result in a few test failures... - * But should probably be done somewhere else, not here? - */ + // 22-Oct-2016, tatu: Looks like this removal is an important part of + // processing, as taking it out will result in a few test failures... + // But should probably be done somewhere else, not here? if (accessor == null) { it.remove(); continue; @@ -684,7 +605,6 @@ protected void removeIgnorableTypes(SerializationConfig config, BeanDescription Class type = property.getRawPrimaryType(); Boolean result = ignores.get(type); if (result == null) { - // 21-Apr-2016, tatu: For 2.8, can specify config overrides result = config.getConfigOverride(type).getIsIgnoredType(); if (result == null) { BeanDescription desc = config.introspectClassAnnotations(type); @@ -710,24 +630,16 @@ protected void removeIgnorableTypes(SerializationConfig config, BeanDescription protected void removeSetterlessGetters(SerializationConfig config, BeanDescription beanDesc, List properties) { - Iterator it = properties.iterator(); - while (it.hasNext()) { - BeanPropertyDefinition property = it.next(); - // one caveat: only remove implicit properties; - // explicitly annotated ones should remain - if (!property.couldDeserialize() && !property.isExplicitlyIncluded()) { - it.remove(); - } - } + // one caveat: only remove implicit properties; + // explicitly annotated ones should remain + properties.removeIf(property -> !property.couldDeserialize() && !property.isExplicitlyIncluded()); } /** * Helper method called to ensure that we do not have "duplicate" type ids. * Added to resolve [databind#222] - * - * @since 2.6 */ - protected List removeOverlappingTypeIds(SerializerProvider prov, + protected List removeOverlappingTypeIds(SerializerProvider ctxt, BeanDescription beanDesc, BeanSerializerBuilder builder, List props) { @@ -749,18 +661,18 @@ protected List removeOverlappingTypeIds(SerializerProvider p } return props; } - + /* - /********************************************************** + /********************************************************************** /* Internal helper methods - /********************************************************** + /********************************************************************** */ /** * Secondary helper method for constructing {@link BeanPropertyWriter} for * given member (field or method). */ - protected BeanPropertyWriter _constructWriter(SerializerProvider prov, + protected BeanPropertyWriter _constructWriter(SerializerProvider ctxt, BeanPropertyDefinition propDef, PropertyBuilder pb, boolean staticTyping, AnnotatedMember accessor) throws JsonMappingException @@ -771,24 +683,24 @@ protected BeanPropertyWriter _constructWriter(SerializerProvider prov, accessor, propDef.getMetadata()); // Does member specify a serializer? If so, let's use it. - JsonSerializer annotatedSerializer = findSerializerFromAnnotation(prov, + JsonSerializer annotatedSerializer = findSerializerFromAnnotation(ctxt, accessor); // Unlike most other code paths, serializer produced // here will NOT be resolved or contextualized, unless done here, so: - if (annotatedSerializer instanceof ResolvableSerializer) { - ((ResolvableSerializer) annotatedSerializer).resolve(prov); + if (annotatedSerializer != null) { + annotatedSerializer.resolve(ctxt); + // 05-Sep-2013, tatu: should be primary property serializer so: + annotatedSerializer = ctxt.handlePrimaryContextualization(annotatedSerializer, property); } - // 05-Sep-2013, tatu: should be primary property serializer so: - annotatedSerializer = prov.handlePrimaryContextualization(annotatedSerializer, property); // And how about polymorphic typing? First special to cover JAXB per-field settings: TypeSerializer contentTypeSer = null; // 16-Feb-2014, cgc: contentType serializers for collection-like and map-like types if (type.isContainerType() || type.isReferenceType()) { - contentTypeSer = findPropertyContentTypeSerializer(type, prov.getConfig(), accessor); + contentTypeSer = findPropertyContentTypeSerializer(ctxt, type, accessor); } // and if not JAXB collection/array with annotations, maybe regular type info? - TypeSerializer typeSer = findPropertyTypeSerializer(type, prov.getConfig(), accessor); - return pb.buildWriter(prov, propDef, type, annotatedSerializer, + TypeSerializer typeSer = ctxt.findPropertyTypeSerializer(type, accessor); + return pb.buildWriter(ctxt, propDef, type, annotatedSerializer, typeSer, contentTypeSer, accessor, staticTyping); } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerModifier.java b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerModifier.java index c0e9495994..0508eeafd2 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerModifier.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerModifier.java @@ -86,7 +86,7 @@ public BeanSerializerBuilder updateBuilder(SerializationConfig config, * {@link BeanSerializer}, modifiers may return serializers of other types; * and this is why implementations must check for type before casting. *

    - * NOTE: since 2.2, gets called for serializer of those non-POJO types that + * NOTE: this method gets called for serializer of those non-POJO types that * do not go through any of more specific modifyXxxSerializer * methods; mostly for JDK types like {@link java.util.Iterator} and such. */ @@ -97,7 +97,7 @@ public JsonSerializer modifySerializer(SerializationConfig config, /* /********************************************************** - /* Callback methods for other types (since 2.2) + /* Callback methods for other types /********************************************************** */ @@ -115,49 +115,32 @@ public JsonSerializer modifySerializer(SerializationConfig config, * * @return Serializer to use; either serializer that was passed * in, or an instance method constructed. - * - * @since 2.2 */ public JsonSerializer modifyArraySerializer(SerializationConfig config, ArrayType valueType, BeanDescription beanDesc, JsonSerializer serializer) { return serializer; } - /** - * @since 2.2 - */ public JsonSerializer modifyCollectionSerializer(SerializationConfig config, CollectionType valueType, BeanDescription beanDesc, JsonSerializer serializer) { return serializer; } - /** - * @since 2.2 - */ public JsonSerializer modifyCollectionLikeSerializer(SerializationConfig config, CollectionLikeType valueType, BeanDescription beanDesc, JsonSerializer serializer) { return serializer; } - - /** - * @since 2.2 - */ + public JsonSerializer modifyMapSerializer(SerializationConfig config, MapType valueType, BeanDescription beanDesc, JsonSerializer serializer) { return serializer; } - /** - * @since 2.2 - */ public JsonSerializer modifyMapLikeSerializer(SerializationConfig config, MapLikeType valueType, BeanDescription beanDesc, JsonSerializer serializer) { return serializer; } - /** - * @since 2.2 - */ public JsonSerializer modifyEnumSerializer(SerializationConfig config, JavaType valueType, BeanDescription beanDesc, JsonSerializer serializer) { return serializer; @@ -177,8 +160,6 @@ public JsonSerializer modifyEnumSerializer(SerializationConfig config, * * @return Serializer to use; either serializer that was passed * in, or an instance method constructed. - * - * @since 2.2 */ public JsonSerializer modifyKeySerializer(SerializationConfig config, JavaType valueType, BeanDescription beanDesc, JsonSerializer serializer) { 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 c7d4bea4ab..43f5136d88 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/ContainerSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/ContainerSerializer.java @@ -1,7 +1,10 @@ package com.fasterxml.jackson.databind.ser; +import java.io.IOException; + import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap; import com.fasterxml.jackson.databind.ser.std.StdSerializer; /** @@ -15,37 +18,69 @@ public abstract class ContainerSerializer extends StdSerializer { + /** + * Property that contains values handled by this serializer, if known; `null` + * for root value serializers (ones directly called by {@link ObjectMapper} and + * {@link ObjectWriter}). + * + * @since 3.0 + */ + protected final BeanProperty _property; + + /** + * If value type cannot be statically determined, mapping from + * runtime value types to serializers are stored in this object. + * + * @since 3.0 (in 2.x subtypes contained it) + */ + protected PropertySerializerMap _dynamicValueSerializers; + /* - /********************************************************** + /********************************************************************** /* Construction, initialization - /********************************************************** + /********************************************************************** */ - protected ContainerSerializer(Class t) { + protected ContainerSerializer(Class t) { super(t); + _property = null; + _dynamicValueSerializers = PropertySerializerMap.emptyForProperties(); } - /** - * @since 2.5 - */ - protected ContainerSerializer(JavaType fullType) { - super(fullType); - } - /** * Alternate constructor that is (alas!) needed to work * around kinks of generic type handling - * - * @param t */ + @Deprecated protected ContainerSerializer(Class t, boolean dummy) { - super(t, dummy); + super(t); + _property = null; + _dynamicValueSerializers = PropertySerializerMap.emptyForProperties(); + } + + @Deprecated // since 3.0 + protected ContainerSerializer(JavaType fullType) { + this(fullType, null); + } + + protected ContainerSerializer(JavaType fullType, BeanProperty prop) { + super(fullType); + _property = prop; + _dynamicValueSerializers = PropertySerializerMap.emptyForProperties(); } protected ContainerSerializer(ContainerSerializer src) { - super(src._handledType, false); + this(src, src._property); } - + + protected ContainerSerializer(ContainerSerializer src, BeanProperty prop) { + super(src._handledType); + _property = prop; + // 16-Apr-2018, tatu: Could retain, possibly, in some cases... but may be + // dangerous as general practice so reset + _dynamicValueSerializers = PropertySerializerMap.emptyForProperties(); + } + /** * Factory(-like) method that can be used to construct a new container * serializer that uses specified {@link TypeSerializer} for decorating @@ -62,9 +97,9 @@ public ContainerSerializer withValueTypeSerializer(TypeSerializer vts) { } /* - /********************************************************** + /********************************************************************** /* Extended API - /********************************************************** + /********************************************************************** */ /** @@ -85,14 +120,13 @@ public ContainerSerializer withValueTypeSerializer(TypeSerializer vts) { public abstract JsonSerializer getContentSerializer(); /* - /********************************************************** + /********************************************************************** /* Abstract methods for sub-classes to implement - /********************************************************** + /********************************************************************** */ -// since 2.5: should be declared abstract in future (2.9?) -// @Override -// public abstract boolean isEmpty(SerializerProvider prov, T value); + @Override + public abstract boolean isEmpty(SerializerProvider prov, T value) throws IOException; /** * Method called to determine if the given value (of type handled by @@ -103,12 +137,18 @@ public ContainerSerializer withValueTypeSerializer(TypeSerializer vts) { * containers that do not keep track of size (like linked lists may * not). *

    - * Note, too, that as of now (2.9) this method is only called by serializer + * Note, too, that this method is only called by serializer * itself; and specifically is not used for non-array/collection types * like Map or Map.Entry instances. */ public abstract boolean hasSingleElement(T value); + /* + /********************************************************************** + /* Helper methods for locating, caching element/value serializers + /********************************************************************** + */ + /** * Method that needs to be implemented to allow construction of a new * serializer object with given {@link TypeSerializer}, used when @@ -116,37 +156,33 @@ public ContainerSerializer withValueTypeSerializer(TypeSerializer vts) { */ protected abstract ContainerSerializer _withValueTypeSerializer(TypeSerializer vts); - /* - /********************************************************** - /* Helper methods for sub-types - /********************************************************** + /** + * @since 3.0 */ + protected JsonSerializer _findAndAddDynamic(SerializerProvider ctxt, Class type) + throws JsonMappingException + { + PropertySerializerMap map = _dynamicValueSerializers; + PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSecondarySerializer(type, ctxt, _property); + // did we get a new map of serializers? If so, start using it + if (map != result.map) { + _dynamicValueSerializers = result.map; + } + return result.serializer; + } /** - * Helper method used to encapsulate logic for determining whether there is - * a property annotation that overrides element type; if so, we can - * and need to statically find the serializer. - * - * @since 2.1 - * - * @deprecated Since 2.7: should not be needed; should be enough to see if - * type has 'isStatic' modifier + * @since 3.0 */ - @Deprecated - protected boolean hasContentTypeAnnotation(SerializerProvider provider, - BeanProperty property) + protected JsonSerializer _findAndAddDynamic(SerializerProvider ctxt, JavaType type) + throws JsonMappingException { - /* - if (property != null) { - AnnotationIntrospector intr = provider.getAnnotationIntrospector(); - AnnotatedMember m = property.getMember(); - if ((m != null) && (intr != null)) { - if (intr.findSerializationContentType(m, property.getType()) != null) { - return true; - } - } + PropertySerializerMap map = _dynamicValueSerializers; + PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSecondarySerializer(type, ctxt, _property); + // did we get a new map of serializers? If so, start using it + if (map != result.map) { + _dynamicValueSerializers = result.map; } - */ - return false; + return result.serializer; } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/ContextualSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/ContextualSerializer.java index b8be372fd3..0cd974f36d 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/ContextualSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/ContextualSerializer.java @@ -3,38 +3,11 @@ import com.fasterxml.jackson.databind.*; /** - * Add-on interface that {@link JsonSerializer}s can implement to get a callback - * that can be used to create contextual instances of serializer to use for - * handling properties of supported type. This can be useful - * for serializers that can be configured by annotations, or should otherwise - * have differing behavior depending on what kind of property is being serialized. - *

    - * Note that in cases where serializer needs both contextualization and - * resolution -- that is, implements both this interface and {@link ResolvableSerializer} - * -- resolution via {@link ResolvableSerializer} occurs first, and contextual - * resolution (via this interface) later on. + * @deprecated Since 3.0: method demoted to JsonSerializer */ +@Deprecated public interface ContextualSerializer { - /** - * Method called to see if a different (or differently configured) serializer - * is needed to serialize values of specified property. - * Note that instance that this method is called on is typically shared one and - * as a result method should NOT modify this instance but rather construct - * and return a new instance. This instance should only be returned as-is, in case - * it is already suitable for use. - * - * @param prov Serializer provider to use for accessing config, other serializers - * @param property Method or field that represents the property - * (and is used to access value to serialize). - * Should be available; but there may be cases where caller cannot provide it and - * null is passed instead (in which case impls usually pass 'this' serializer as is) - * - * @return Serializer to use for serializing values of specified property; - * may be this instance or a new instance. - * - * @throws JsonMappingException - */ public JsonSerializer createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException; } 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 2d521421f8..0f02e621a8 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/DefaultSerializerProvider.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/DefaultSerializerProvider.java @@ -2,43 +2,37 @@ import java.io.IOException; import java.util.*; -import java.util.concurrent.atomic.AtomicReference; import com.fasterxml.jackson.annotation.ObjectIdGenerator; import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.TokenStreamFactory; + import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.cfg.GeneratorSettings; import com.fasterxml.jackson.databind.cfg.HandlerInstantiator; import com.fasterxml.jackson.databind.introspect.Annotated; import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; -import com.fasterxml.jackson.databind.jsonschema.SchemaAware; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; -import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.ser.impl.WritableObjectId; import com.fasterxml.jackson.databind.util.ClassUtil; /** - * Standard implementation used by {@link ObjectMapper}: - * adds methods only exposed to {@link ObjectMapper}, - * as well as constructors. - *

    - * Note that class is abstract just because it does not - * define {@link #createInstance} method. + * Extension over {@link SerializerProvider} that adds methods needed by + * {@link ObjectMapper} (and {@link ObjectWriter}) but that are not to be exposed + * as general context during serialization. *

    * Also note that all custom {@link SerializerProvider} * implementations must sub-class this class: {@link ObjectMapper} * requires this type, not basic provider type. */ -public abstract class DefaultSerializerProvider +public class DefaultSerializerProvider extends SerializerProvider - implements java.io.Serializable // since 2.1; only because ObjectWriter needs it { - private static final long serialVersionUID = 1L; - /* - /********************************************************** - /* State, for non-blueprint instances - /********************************************************** + /********************************************************************** + /* Additional state + /********************************************************************** */ /** @@ -49,55 +43,22 @@ public abstract class DefaultSerializerProvider protected transient ArrayList> _objectIdGenerators; - /** - * Generator used for serialization. Needed mostly for error reporting - * purposes. - * - * @since 2.8 - */ - protected transient JsonGenerator _generator; - /* - /********************************************************** + /********************************************************************** /* Life-cycle - /********************************************************** + /********************************************************************** */ - protected DefaultSerializerProvider() { super(); } - - protected DefaultSerializerProvider(SerializerProvider src, - SerializationConfig config,SerializerFactory f) { - super(src, config, f); - } - - protected DefaultSerializerProvider(DefaultSerializerProvider src) { - super(src); - } - - /** - * Method that sub-classes need to implement: used to create a non-blueprint instances - * from the blueprint. - * This is needed to retain state during serialization. - */ - public abstract DefaultSerializerProvider createInstance(SerializationConfig config, - SerializerFactory jsf); - - /** - * Method needed to ensure that {@link ObjectMapper#copy} will work - * properly; specifically, that caches are cleared, but settings - * will otherwise remain identical; and that no sharing of state - * occurs. - * - * @since 2.5 - */ - public DefaultSerializerProvider copy() { - throw new IllegalStateException("DefaultSerializerProvider sub-class not overriding copy()"); + protected DefaultSerializerProvider(TokenStreamFactory streamFactory, + SerializationConfig config, GeneratorSettings genSettings, + SerializerFactory f, SerializerCache cache) { + super(streamFactory, config, genSettings, f, cache); } /* - /********************************************************** + /********************************************************************** /* Abstract method impls, factory methods - /********************************************************** + /********************************************************************** */ @Override @@ -175,9 +136,9 @@ public boolean includeFilterSuppressNulls(Object filter) throws JsonMappingExcep } /* - /********************************************************** + /********************************************************************** /* Object Id handling - /********************************************************** + /********************************************************************** */ @Override @@ -218,8 +179,6 @@ public WritableObjectId findObjectId(Object forPojo, ObjectIdGenerator genera * Overridable helper method used for creating {@link java.util.Map} * used for storing mappings from serializable objects to their * Object Ids. - * - * @since 2.3 */ protected Map _createObjectIdMap() { @@ -233,51 +192,15 @@ protected Map _createObjectIdMap() } /* - /********************************************************** + /********************************************************************** /* Extended API: simple accesors - /********************************************************** - */ - - /** - * Method that can be called to see if this serializer provider - * can find a serializer for an instance of given class. - *

    - * Note that no Exceptions are thrown, including unchecked ones: - * implementations are to swallow exceptions if necessary. + /********************************************************************** */ - public boolean hasSerializerFor(Class cls, AtomicReference cause) - { - // 07-Nov-2015, tatu: One special case, Object.class; will work only if - // empty beans are allowed or custom serializer registered. Easiest to - // check here. - if (cls == Object.class) { - if (!_config.isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS)) { - return true; - } - } - - try { - JsonSerializer ser = _findExplicitUntypedSerializer(cls); - return (ser != null); - } catch (JsonMappingException e) { - if (cause != null) { - cause.set(e); - } - } catch (RuntimeException e) { - if (cause == null) { // earlier behavior - throw e; - } - cause.set(e); - } - return false; - } /** * Accessor for the {@link JsonGenerator} currently in use for serializing * content. Null for blueprint instances; non-null for actual active * provider instances. - * - * @since 2.8 */ @Override public JsonGenerator getGenerator() { @@ -285,9 +208,9 @@ public JsonGenerator getGenerator() { } /* - /********************************************************** + /********************************************************************** /* Extended API called by ObjectMapper: value serialization - /********************************************************** + /********************************************************************** */ /** @@ -305,7 +228,7 @@ public void serializeValue(JsonGenerator gen, Object value) throws IOException } final Class cls = value.getClass(); // true, since we do want to cache root-level typed serializers (ditto for null property) - final JsonSerializer ser = findTypedValueSerializer(cls, true, null); + final JsonSerializer ser = findTypedValueSerializer(cls, true); PropertyName rootName = _config.getFullRootName(); if (rootName == null) { // not explicitly specified if (_config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE)) { @@ -342,7 +265,7 @@ public void serializeValue(JsonGenerator gen, Object value, JavaType rootType) t _reportIncompatibleRootType(value, rootType); } // root value, not reached via property: - JsonSerializer ser = findTypedValueSerializer(rootType, true, null); + JsonSerializer ser = findTypedValueSerializer(rootType, true); PropertyName rootName = _config.getFullRootName(); if (rootName == null) { // not explicitly specified if (_config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE)) { @@ -365,8 +288,6 @@ public void serializeValue(JsonGenerator gen, Object value, JavaType rootType) t * @param rootType Type to use for locating serializer to use, instead of actual * runtime type, if no serializer is passed * @param ser Root Serializer to use, if not null - * - * @since 2.1 */ public void serializeValue(JsonGenerator gen, Object value, JavaType rootType, JsonSerializer ser) throws IOException @@ -382,7 +303,7 @@ public void serializeValue(JsonGenerator gen, Object value, JavaType rootType, } // root value, not reached via property: if (ser == null) { - ser = findTypedValueSerializer(rootType, true, null); + ser = findTypedValueSerializer(rootType, true); } PropertyName rootName = _config.getFullRootName(); if (rootName == null) { // not explicitly specified @@ -403,8 +324,6 @@ public void serializeValue(JsonGenerator gen, Object value, JavaType rootType, /** * Alternate serialization call used for polymorphic types, when {@link TypeSerializer} * is already known, but the actual serializer may or may not be. - * - * @since 2.6 */ public void serializePolymorphic(JsonGenerator gen, Object value, JavaType rootType, JsonSerializer valueSer, TypeSerializer typeSer) @@ -426,9 +345,9 @@ public void serializePolymorphic(JsonGenerator gen, Object value, JavaType rootT */ if (valueSer == null) { if ((rootType != null) && rootType.isContainerType()) { - valueSer = findValueSerializer(rootType, null); + valueSer = handleRootContextualization(findValueSerializer(rootType)); } else { - valueSer = findValueSerializer(value.getClass(), null); + valueSer = handleRootContextualization(findValueSerializer(value.getClass())); } } @@ -485,8 +404,6 @@ private final void _serialize(JsonGenerator gen, Object value, /** * Helper method called when root value to serialize is null - * - * @since 2.3 */ protected void _serializeNull(JsonGenerator gen) throws IOException { @@ -510,40 +427,9 @@ private IOException _wrapAsIOE(JsonGenerator g, Exception e) { } /* - /******************************************************** - /* Access to caching details - /******************************************************** - */ - - /** - * Method that can be used to determine how many serializers this - * provider is caching currently - * (if it does caching: default implementation does) - * Exact count depends on what kind of serializers get cached; - * default implementation caches all serializers, including ones that - * are eagerly constructed (for optimal access speed) - *

    - * The main use case for this method is to allow conditional flushing of - * serializer cache, if certain number of entries is reached. - */ - public int cachedSerializersCount() { - return _serializerCache.size(); - } - - /** - * Method that will drop all serializers currently cached by this provider. - * This can be used to remove memory usage (in case some serializers are - * only used once or so), or to force re-construction of serializers after - * configuration changes for mapper than owns the provider. - */ - public void flushCachedSerializers() { - _serializerCache.flush(); - } - - /* - /********************************************************** + /********************************************************************** /* Extended API called by ObjectMapper: other - /********************************************************** + /********************************************************************** */ /** @@ -559,73 +445,29 @@ public void acceptJsonFormatVisitor(JavaType javaType, JsonFormatVisitorWrapper if (javaType == null) { throw new IllegalArgumentException("A class must be provided"); } - /* no need for embedded type information for JSON schema generation (all - * type information it needs is accessible via "untyped" serializer) - */ + // no need for embedded type information for JSON schema generation (all + // type information it needs is accessible via "untyped" serializer) visitor.setProvider(this); - findValueSerializer(javaType, null).acceptJsonFormatVisitor(visitor, javaType); + findRootValueSerializer(javaType).acceptJsonFormatVisitor(visitor, javaType); } - /** - * The method to be called by {@link ObjectMapper} - * to generate JSON schema for - * given type. - * - * @param type The type for which to generate schema - * - * @deprecated Should not be used any more - */ - @Deprecated // since 2.6 - public com.fasterxml.jackson.databind.jsonschema.JsonSchema generateJsonSchema(Class type) - throws JsonMappingException - { - /* no need for embedded type information for JSON schema generation (all - * type information it needs is accessible via "untyped" serializer) - */ - JsonSerializer ser = findValueSerializer(type, null); - JsonNode schemaNode = (ser instanceof SchemaAware) ? - ((SchemaAware) ser).getSchema(this, null) : com.fasterxml.jackson.databind.jsonschema.JsonSchema.getDefaultSchemaNode(); - if (!(schemaNode instanceof ObjectNode)) { - throw new IllegalArgumentException("Class " + type.getName() - +" would not be serialized as a JSON object and therefore has no schema"); - } - return new com.fasterxml.jackson.databind.jsonschema.JsonSchema((ObjectNode) schemaNode); - } - - /* - /********************************************************** + /********************************************************************** /* Helper classes - /********************************************************** + /********************************************************************** */ /** - * Concrete implementation that defines factory method(s), - * defined as final. + * Concrete implementation defined separately so it can be declared `final`. + * Alternate implements should instead just extend {@link DefaultSerializerProvider} */ - public final static class Impl extends DefaultSerializerProvider { - private static final long serialVersionUID = 1L; - - public Impl() { super(); } - public Impl(Impl src) { super(src); } - - protected Impl(SerializerProvider src, SerializationConfig config, - SerializerFactory f) { - super(src, config, f); - } - - @Override - public DefaultSerializerProvider copy() - { - if (getClass() != Impl.class) { - return super.copy(); - } - return new Impl(this); - } - - @Override - public Impl createInstance(SerializationConfig config, SerializerFactory jsf) { - return new Impl(this, config, jsf); + public final static class Impl + extends DefaultSerializerProvider + { + public Impl(TokenStreamFactory streamFactory, + SerializationConfig config, GeneratorSettings genSettings, + SerializerFactory f, SerializerCache cache) { + super(streamFactory, config, genSettings, f, cache); } } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/FilterProvider.java b/src/main/java/com/fasterxml/jackson/databind/ser/FilterProvider.java index acd13c3625..1376a246e7 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/FilterProvider.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/FilterProvider.java @@ -1,6 +1,6 @@ package com.fasterxml.jackson.databind.ser; -import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; +import com.fasterxml.jackson.core.util.Snapshottable; /** * Interface for objects that providers instances of {@link PropertyFilter} @@ -9,50 +9,20 @@ * annotation on bean class. */ public abstract class FilterProvider + implements Snapshottable { - /** - * Lookup method used to find {@link BeanPropertyFilter} that has specified id. - * Note that id is typically a {@link java.lang.String}, but is not necessarily - * limited to that; that is, while standard components use String, custom - * implementation can choose other kinds of keys. - * - * @return Filter registered with specified id, if one defined; null if - * none found. - * - * @deprecated Since 2.3 deprecated because {@link BeanPropertyFilter} is deprecated; - */ - @Deprecated - public abstract BeanPropertyFilter findFilter(Object filterId); - /** * Lookup method used to find {@link PropertyFilter} that has specified id. * Note that id is typically a {@link java.lang.String}, but is not necessarily * limited to that; that is, while standard components use String, custom * implementation can choose other kinds of keys. - *

    - * This method is the replacement for {@link #findFilter} starting with 2.3. - *

    - * Note that the default implementation is designed to support short-term - * backwards compatibility, and will call the deprecated findFilter - * method, then wrap filter if one found as {@link PropertyFilter}. - * It should be overridden by up-to-date implementations - * + * * @param filterId Id of the filter to fetch * @param valueToFilter Object being filtered (usually POJO, but may be a {@link java.util.Map}, * or in future a container), if available; not available when generating * schemas. * * @return Filter to use, if any. - * - * @since 2.3 */ - public PropertyFilter findPropertyFilter(Object filterId, Object valueToFilter) - { - @SuppressWarnings("deprecation") - BeanPropertyFilter old = findFilter(filterId); - if (old == null) { - return null; - } - return SimpleBeanPropertyFilter.from(old); - } + public abstract PropertyFilter findPropertyFilter(Object filterId, Object valueToFilter); } 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 957af4caf9..13127c1892 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/PropertyBuilder.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/PropertyBuilder.java @@ -14,7 +14,6 @@ */ public class PropertyBuilder { - // @since 2.7 private final static Object NO_DEFAULT_MARKER = Boolean.FALSE; final protected SerializationConfig _config; @@ -44,8 +43,6 @@ public class PropertyBuilder /** * Marker flag used to indicate that "real" default values are to be used * for properties, as per per-type value inclusion of type NON_DEFAULT - * - * @since 2.8 */ final protected boolean _useRealPropertyDefaults; @@ -74,9 +71,9 @@ public PropertyBuilder(SerializationConfig config, BeanDescription beanDesc) } /* - /********************************************************** + /********************************************************************** /* Public API - /********************************************************** + /********************************************************************** */ public Annotations getClassAnnotations() { @@ -231,7 +228,7 @@ protected BeanPropertyWriter buildWriter(SerializerProvider prov, ser, typeSer, serializationType, suppressNulls, valueToSuppress, views); // How about custom null serializer? - Object serDef = _annotationIntrospector.findNullSerializer(am); + Object serDef = _annotationIntrospector.findNullSerializer(_config, am); if (serDef != null) { bpw.assignNullSerializer(prov.serializerInstance(am, serDef)); } @@ -244,9 +241,9 @@ protected BeanPropertyWriter buildWriter(SerializerProvider prov, } /* - /********************************************************** + /********************************************************************** /* Helper methods; annotation access - /********************************************************** + /********************************************************************** */ /** @@ -287,7 +284,7 @@ protected JavaType findSerializationType(Annotated a, boolean useStaticTyping, J declaredType = secondary; } // If using static typing, declared type is known to be the type... - JsonSerialize.Typing typing = _annotationIntrospector.findSerializationTyping(a); + JsonSerialize.Typing typing = _annotationIntrospector.findSerializationTyping(_config, a); if ((typing != null) && (typing != JsonSerialize.Typing.DEFAULT_TYPING)) { useStaticTyping = (typing == JsonSerialize.Typing.STATIC); } @@ -300,9 +297,9 @@ protected JavaType findSerializationType(Annotated a, boolean useStaticTyping, J } /* - /********************************************************** + /********************************************************************** /* Helper methods for default value handling - /********************************************************** + /********************************************************************** */ protected Object getDefaultBean() @@ -328,48 +325,10 @@ protected Object getDefaultBean() return (def == NO_DEFAULT_MARKER) ? null : _defaultBean; } - /** - * Accessor used to find out "default value" for given property, to use for - * comparing values to serialize, to determine whether to exclude value from serialization with - * inclusion type of {@link com.fasterxml.jackson.annotation.JsonInclude.Include#NON_DEFAULT}. - * This method is called when we specifically want to know default value within context - * of a POJO, when annotation is within containing class, and not for property or - * defined as global baseline. - *

    - * Note that returning of pseudo-type - * {@link com.fasterxml.jackson.annotation.JsonInclude.Include#NON_EMPTY} requires special handling. - * - * @since 2.7 - * @deprecated Since 2.9 since this will not allow determining difference between "no default instance" - * case and default being `null`. - */ - @Deprecated // since 2.9 - protected Object getPropertyDefaultValue(String name, AnnotatedMember member, - JavaType type) - { - Object defaultBean = getDefaultBean(); - if (defaultBean == null) { - return getDefaultValue(type); - } - try { - return member.getValue(defaultBean); - } catch (Exception e) { - return _throwWrapped(e, name, defaultBean); - } - } - - /** - * @deprecated Since 2.9 - */ - @Deprecated // since 2.9 - protected Object getDefaultValue(JavaType type) { - return BeanUtil.getDefaultValue(type); - } - /* - /********************************************************** + /********************************************************************** /* Helper methods for exception handling - /********************************************************** + /********************************************************************** */ protected Object _throwWrapped(Exception e, String propName, Object defaultBean) diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/PropertyFilter.java b/src/main/java/com/fasterxml/jackson/databind/ser/PropertyFilter.java index 89f32eff97..5e81fc278c 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/PropertyFilter.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/PropertyFilter.java @@ -1,10 +1,11 @@ package com.fasterxml.jackson.databind.ser; import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.util.Snapshottable; + import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; -import com.fasterxml.jackson.databind.node.ObjectNode; /** * Interface that defines API for filter objects use (as configured @@ -19,10 +20,9 @@ * strongly recommended that custom implementations extend * {@link com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter}, * to avoid backwards compatibility issues in case interface needs to change. - * - * @since 2.3 */ public interface PropertyFilter + extends Snapshottable { /** * Method called by {@link BeanSerializer} to let the filter decide what to do with @@ -71,32 +71,7 @@ public void serializeAsField(Object pojo, JsonGenerator gen, SerializerProvider public void serializeAsElement(Object elementValue, JsonGenerator gen, SerializerProvider prov, PropertyWriter writer) throws Exception; - - /** - * Method called by {@link BeanSerializer} to let the filter determine whether, and in what - * form the given property exist within the parent, or root, schema. Filters can omit - * adding the property to the node, or choose the form of the schema value for the property. - *

    - * Typical implementation is something like: - *

    -     * if (include(writer)) {
    -     *      writer.depositSchemaProperty(propertiesNode, provider);
    -     * }
    -     *
    - * - * @param writer Bean property writer to use to create schema value - * @param propertiesNode Node which the given property would exist within - * @param provider Provider that can be used for accessing dynamic aspects of serialization - * processing - * - * @deprecated Since 2.3: new code should use the alternative depositSchemaProperty - * method - */ - @Deprecated - public void depositSchemaProperty(PropertyWriter writer, ObjectNode propertiesNode, - SerializerProvider provider) - throws JsonMappingException; - + /** * Method called by {@link BeanSerializer} to let the filter determine whether, and in what * form the given property exist within the parent, or root, schema. Filters can omit 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 9d7a7a465f..d319f7f01d 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/PropertyWriter.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/PropertyWriter.java @@ -3,22 +3,20 @@ import java.lang.annotation.Annotation; import com.fasterxml.jackson.core.JsonGenerator; + import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; import com.fasterxml.jackson.databind.introspect.ConcreteBeanPropertyBase; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; -import com.fasterxml.jackson.databind.node.ObjectNode; /** * Base class for writers used to output property values (name-value pairs) * as key/value pairs via streaming API. This is the most generic abstraction * implemented by both POJO and {@link java.util.Map} serializers, and invoked * by filtering functionality. - * - * @since 2.3 */ public abstract class PropertyWriter - extends ConcreteBeanPropertyBase // since 2.7 + extends ConcreteBeanPropertyBase implements java.io.Serializable { private static final long serialVersionUID = 1L; @@ -155,13 +153,4 @@ public abstract void serializeAsPlaceholder(Object value, JsonGenerator jgen, Se public abstract void depositSchemaProperty(JsonObjectFormatVisitor objectVisitor, SerializerProvider provider) throws JsonMappingException; - - /** - * Legacy method called for JSON Schema generation; should not be called by new code - * - * @deprecated Since 2.2 - */ - @Deprecated - public abstract void depositSchemaProperty(ObjectNode propertiesNode, SerializerProvider provider) - throws JsonMappingException; } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/ResolvableSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/ResolvableSerializer.java index c86d0c1a2c..2bc3828f83 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/ResolvableSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/ResolvableSerializer.java @@ -2,32 +2,20 @@ import com.fasterxml.jackson.databind.*; -/** +/* * Interface used to indicate serializers that want to do post-processing * after construction and being added to {@link SerializerProvider}, * but before being used. This is typically used to resolve references * to other contained types; for example, bean serializers use this * to eagerly find serializers for contained field types. - *

    - * Note that in cases where serializer needs both contextualization and - * resolution -- that is, implements both this interface and {@link ContextualSerializer} - * -- resolution via this interface occurs first, and contextual - * resolution (using {@link ContextualSerializer}) later on. */ + +/** + * @deprecated Since 3.0: method demoted to JsonSerializer + */ +@Deprecated public interface ResolvableSerializer { - /** - * Method called after {@link SerializerProvider} has registered - * the serializer, but before it has returned it to the caller. - * Called object can then resolve its dependencies to other types, - * including self-references (direct or indirect). - *

    - * Note that this method does NOT return serializer, since resolution - * is not allowed to change actual serializer to use. - * - * @param provider Provider that has constructed serializer this method - * is called on. - */ public abstract void resolve(SerializerProvider provider) throws JsonMappingException; } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/SerializerCache.java b/src/main/java/com/fasterxml/jackson/databind/ser/SerializerCache.java index 153a5334bd..88a71a7938 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/SerializerCache.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/SerializerCache.java @@ -1,45 +1,80 @@ package com.fasterxml.jackson.databind.ser; -import java.util.*; import java.util.concurrent.atomic.AtomicReference; +import com.fasterxml.jackson.core.util.Snapshottable; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.ser.impl.ReadOnlyClassToSerializerMap; +import com.fasterxml.jackson.databind.util.SimpleLookupCache; import com.fasterxml.jackson.databind.util.TypeKey; /** * Simple cache object that allows for doing 2-level lookups: first level is - * by "local" read-only lookup Map (used without locking) - * and second backup level is by a shared modifiable HashMap. - * The idea is that after a while, most serializers are found from the - * local Map (to optimize performance, reduce lock contention), - * but that during buildup we can use a shared map to reduce both - * number of distinct read-only maps constructed, and number of - * serializers constructed. + * by "local" read-only lookup Map (used without locking) and second backup + * level is by a shared modifiable HashMap. The idea is that after a while, + * most serializers are found from the local Map (to optimize performance, + * reduce lock contention), but that during buildup we can use a shared map + * to reduce both number of distinct read-only maps constructed, and number + * of serializers constructed. *

    - * Cache contains three kinds of entries, - * based on combination of class pair key. First class in key is for the - * type to serialize, and second one is type used for determining how - * to resolve value type. One (but not both) of entries can be null. + * Cache contains three kinds of entries, based on combination of class pair key. + * First class in key is for the type to serialize, and second one is type used for + * determining how to resolve value type. One (but not both) of entries can be null. */ public final class SerializerCache + implements Snapshottable, + java.io.Serializable { + private static final long serialVersionUID = 3L; + /** - * Shared, modifiable map; all access needs to be through synchronized blocks. + * By default, allow caching of up to 4000 serializer entries (for possibly up to + * 1000 types; but depending access patterns may be as few as half of that). + */ + public final static int DEFAULT_MAX_CACHED = 4000; + + /** + * Shared, modifiable map; used if local read-only copy does not contain serializer + * caller expects. *

    * NOTE: keys are of various types (see below for key types), in addition to * basic {@link JavaType} used for "untyped" serializers. */ - private final HashMap> _sharedMap - = new HashMap>(64); + private final SimpleLookupCache> _sharedMap; /** * Most recent read-only instance, created from _sharedMap, if any. */ - private final AtomicReference _readOnlyMap - = new AtomicReference(); + private final transient AtomicReference _readOnlyMap; - public SerializerCache() { } + public SerializerCache() { + this(DEFAULT_MAX_CACHED); + } + + /** + * @since 3.0 + */ + public SerializerCache(int maxCached) { + int initial = Math.min(64, maxCached>>2); + _sharedMap = new SimpleLookupCache>(initial, maxCached); + _readOnlyMap = new AtomicReference(); + } + + private SerializerCache(SimpleLookupCache> shared) { + _sharedMap = shared; + _readOnlyMap = new AtomicReference(); + } + + // Since 3.0, needed to initialize cache properly: shared map would be ok but need to + // reconstruct AtomicReference + protected Object readResolve() { + return new SerializerCache(_sharedMap); + } + + @Override + public SerializerCache snapshot() { + return new SerializerCache(_sharedMap.snapshot()); + } /** * Method that can be called to get a read-only instance populated from the @@ -59,19 +94,19 @@ private final synchronized ReadOnlyClassToSerializerMap _makeReadOnlyLookupMap() // not correctness ReadOnlyClassToSerializerMap m = _readOnlyMap.get(); if (m == null) { - m = ReadOnlyClassToSerializerMap.from(_sharedMap); + m = ReadOnlyClassToSerializerMap.from(this, _sharedMap); _readOnlyMap.set(m); } return m; } /* - /********************************************************** + /********************************************************************** /* Lookup methods for accessing shared (slow) cache - /********************************************************** + /********************************************************************** */ - public synchronized int size() { + public int size() { return _sharedMap.size(); } @@ -81,38 +116,30 @@ public synchronized int size() { */ public JsonSerializer untypedValueSerializer(Class type) { - synchronized (this) { - return _sharedMap.get(new TypeKey(type, false)); - } + return _sharedMap.get(new TypeKey(type, false)); } public JsonSerializer untypedValueSerializer(JavaType type) { - synchronized (this) { - return _sharedMap.get(new TypeKey(type, false)); - } + return _sharedMap.get(new TypeKey(type, false)); } public JsonSerializer typedValueSerializer(JavaType type) { - synchronized (this) { - return _sharedMap.get(new TypeKey(type, true)); - } + return _sharedMap.get(new TypeKey(type, true)); } public JsonSerializer typedValueSerializer(Class cls) { - synchronized (this) { - return _sharedMap.get(new TypeKey(cls, true)); - } + return _sharedMap.get(new TypeKey(cls, true)); } /* - /********************************************************** + /********************************************************************** /* Methods for adding shared serializer instances - /********************************************************** + /********************************************************************** */ - + /** * Method called if none of lookups succeeded, and caller had to construct * a serializer. If so, we will update the shared lookup map so that it @@ -120,24 +147,20 @@ public JsonSerializer typedValueSerializer(Class cls) */ public void addTypedSerializer(JavaType type, JsonSerializer ser) { - synchronized (this) { - if (_sharedMap.put(new TypeKey(type, true), ser) == null) { - // let's invalidate the read-only copy, too, to get it updated - _readOnlyMap.set(null); - } + if (_sharedMap.put(new TypeKey(type, true), ser) == null) { + // let's invalidate the read-only copy, too, to get it updated + _readOnlyMap.set(null); } } public void addTypedSerializer(Class cls, JsonSerializer ser) { - synchronized (this) { - if (_sharedMap.put(new TypeKey(cls, true), ser) == null) { - // let's invalidate the read-only copy, too, to get it updated - _readOnlyMap.set(null); - } + if (_sharedMap.put(new TypeKey(cls, true), ser) == null) { + // let's invalidate the read-only copy, too, to get it updated + _readOnlyMap.set(null); } } - + public void addAndResolveNonTypedSerializer(Class type, JsonSerializer ser, SerializerProvider provider) throws JsonMappingException @@ -151,9 +174,7 @@ public void addAndResolveNonTypedSerializer(Class type, JsonSerializer rawType, JavaType fullType, JsonSerializer ser, @@ -193,9 +210,7 @@ public void addAndResolveNonTypedSerializer(Class rawType, JavaType fullType, if ((ob1 == null) || (ob2 == null)) { _readOnlyMap.set(null); } - if (ser instanceof ResolvableSerializer) { - ((ResolvableSerializer) ser).resolve(provider); - } + ser.resolve(provider); } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/SerializerFactory.java b/src/main/java/com/fasterxml/jackson/databind/ser/SerializerFactory.java index 9e4c7d55e1..e85a0bb72a 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/SerializerFactory.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/SerializerFactory.java @@ -1,7 +1,7 @@ package com.fasterxml.jackson.databind.ser; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.jsontype.TypeSerializer; /** * Abstract class that defines API used by {@link SerializerProvider} @@ -11,60 +11,23 @@ public abstract class SerializerFactory { /* - /********************************************************** - /* Additional configuration methods - /********************************************************** + /********************************************************************** + /* Basic `SerializerFactory` API + /********************************************************************** */ /** - * Convenience method for creating a new factory instance with additional serializer - * provider; equivalent to calling - *
    -     *   withConfig(getConfig().withAdditionalSerializers(additional));
    -     *
    - */ - public abstract SerializerFactory withAdditionalSerializers(Serializers additional); - - public abstract SerializerFactory withAdditionalKeySerializers(Serializers additional); - - /** - * Convenience method for creating a new factory instance with additional bean - * serializer modifier; equivalent to calling - *
    -     *   withConfig(getConfig().withSerializerModifier(modifier));
    -     *
    - */ - public abstract SerializerFactory withSerializerModifier(BeanSerializerModifier modifier); - - /* - /********************************************************** - /* Basic SerializerFactory API: - /********************************************************** - */ - - /** - * Method called to create (or, for immutable serializers, reuse) a serializer for given type. - * - * @param prov Provider that needs to be used to resolve annotation-provided - * serializers (but NOT for others) - * - * @since 2.1 (earlier versions had method with different signature) - */ - public abstract JsonSerializer createSerializer(SerializerProvider prov, - JavaType baseType) - throws JsonMappingException; - - /** - * Method called to create a type information serializer for given base type, - * if one is needed. If not needed (no polymorphic handling configured), should - * return null. - * - * @param baseType Declared type to use as the base type for type information serializer + * Method called to create (or, for immutable serializers, reuse) a serializer for given type. * - * @return Type serializer to use for the base type, if one is needed; null if not. + * @param ctxt (not null) Context that needs to be used to resolve annotation-provided + * serializers (but NOT for others) + * @param formatOverride (nullable) Possible format overrides (from property annotations) + * to use, above and beyond what `beanDesc` defines + * + * @since 3.0 (last argument added) */ - public abstract TypeSerializer createTypeSerializer(SerializationConfig config, - JavaType baseType) + public abstract JsonSerializer createSerializer(SerializerProvider ctxt, + JavaType baseType, BeanDescription beanDesc, JsonFormat.Value formatOverride) throws JsonMappingException; /** @@ -79,7 +42,69 @@ public abstract TypeSerializer createTypeSerializer(SerializationConfig config, * @return Serializer to use, if factory knows it; null if not (in which case default * serializer is to be used) */ - public abstract JsonSerializer createKeySerializer(SerializationConfig config, + public abstract JsonSerializer createKeySerializer(SerializerProvider ctxt, JavaType type, JsonSerializer defaultImpl) throws JsonMappingException; + + /** + * Returns serializer used to (try to) output a null key, due to an entry of + * {@link java.util.Map} having null key. + * The default implementation will throw an exception if this happens; + * alternative implementation (like one that would write an Empty String) + * can be defined. + */ + public abstract JsonSerializer getDefaultNullKeySerializer(); + + public abstract JsonSerializer getDefaultNullValueSerializer(); + + /* + /********************************************************************** + /* Additional mutant factories for registering serializer overrides + /********************************************************************** + */ + + /** + * Mutant factory method for creating a new factory instance with additional serializer + * provider: provider will get inserted as the first one to be checked. + */ + public abstract SerializerFactory withAdditionalSerializers(Serializers additional); + + /** + * Mutant factory method for creating a new factory instance with additional key serializer + * provider: provider will get inserted as the first one to be checked. + */ + public abstract SerializerFactory withAdditionalKeySerializers(Serializers additional); + + /** + * Mutant factory method for creating a new factory instance with additional serializer modifier: + * modifier will get inserted as the first one to be checked. + */ + public abstract SerializerFactory withSerializerModifier(BeanSerializerModifier modifier); + + /** + * @since 3.0 + */ + public abstract SerializerFactory withNullValueSerializer(JsonSerializer nvs); + + /** + * @since 3.0 + */ + public abstract SerializerFactory withNullKeySerializer(JsonSerializer nks); + + /* + /********************************************************************** + /* Deprecated (for 2.x migration, compatibility) + /********************************************************************** + */ + + /** + * @deprecated Since 3.0 use variant that takes {@code JsonFormat.Value} argument + */ + @Deprecated // since 3.0 + public JsonSerializer createSerializer(SerializerProvider ctxt, JavaType baseType) + throws JsonMappingException + { + BeanDescription beanDesc = ctxt.introspect(baseType); + return createSerializer(ctxt, baseType, beanDesc, null); + } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/Serializers.java b/src/main/java/com/fasterxml/jackson/databind/ser/Serializers.java index 84c647ae30..2dea0bbaa2 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/Serializers.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/Serializers.java @@ -1,5 +1,6 @@ package com.fasterxml.jackson.databind.ser; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; import com.fasterxml.jackson.databind.type.*; @@ -22,42 +23,67 @@ public interface Serializers * @param type Fully resolved type of instances to serialize * @param config Serialization configuration in use * @param beanDesc Additional information about type + * @param formatOverrides (nullable) Optional format overrides (usually from property definition), + * to change definitions that {@code beanDesc} may have (and which are NOT included). Usually + * combined calling {@code Serializers.Base#calculateEffectiveFormat}. * * @return Configured serializer to use for the type; or null if implementation * does not recognize or support type */ - public JsonSerializer findSerializer(SerializationConfig config, - JavaType type, BeanDescription beanDesc); + default JsonSerializer findSerializer(SerializationConfig config, + JavaType type, BeanDescription beanDesc, JsonFormat.Value formatOverrides) + { + return null; + } /** * Method called by serialization framework first time a serializer is needed for * given {@link ReferenceType} * - * @since 2.7 + * @param formatOverrides (nullable) Optional format overrides (usually from property definition), + * to change definitions that {@code beanDesc} may have (and which are NOT included). Usually + * combined calling {@code Serializers.Base#calculateEffectiveFormat}. */ - public JsonSerializer findReferenceSerializer(SerializationConfig config, - ReferenceType type, BeanDescription beanDesc, - TypeSerializer contentTypeSerializer, JsonSerializer contentValueSerializer); + default JsonSerializer findReferenceSerializer(SerializationConfig config, + ReferenceType type, BeanDescription beanDesc, JsonFormat.Value formatOverrides, + TypeSerializer contentTypeSerializer, JsonSerializer contentValueSerializer) + { + return null; + } /** * Method called by serialization framework first time a serializer is needed for * specified array type. * Implementation should return a serializer instance if it supports * specified type; or null if it does not. + * + * @param formatOverrides (nullable) Optional format overrides (usually from property definition), + * to change definitions that {@code beanDesc} may have (and which are NOT included). Usually + * combined calling {@code Serializers.Base#calculateEffectiveFormat}. */ - public JsonSerializer findArraySerializer(SerializationConfig config, - ArrayType type, BeanDescription beanDesc, - TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer); + default JsonSerializer findArraySerializer(SerializationConfig config, ArrayType type, + BeanDescription beanDesc, JsonFormat.Value formatOverrides, + TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) + { + return null; + } /** * Method called by serialization framework first time a serializer is needed for * specified {@link java.util.Collection} type. * Implementation should return a serializer instance if it supports * specified type; or null if it does not. + * + * @param formatOverrides (nullable) Optional format overrides (usually from property definition), + * to change definitions that {@code beanDesc} may have (and which are NOT included). Usually + * combined calling {@code Serializers.Base#calculateEffectiveFormat}. */ - public JsonSerializer findCollectionSerializer(SerializationConfig config, - CollectionType type, BeanDescription beanDesc, - TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer); + default JsonSerializer findCollectionSerializer(SerializationConfig config, + CollectionType type, BeanDescription beanDesc, JsonFormat.Value formatOverrides, + TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) + { + return null; + } /** * Method called by serialization framework first time a serializer is needed for @@ -65,21 +91,35 @@ public JsonSerializer findCollectionSerializer(SerializationConfig config, * but does not implement it). * Implementation should return a serializer instance if it supports * specified type; or null if it does not. + * + * @param formatOverrides (nullable) Optional format overrides (usually from property definition), + * to change definitions that {@code beanDesc} may have (and which are NOT included). Usually + * combined calling {@code Serializers.Base#calculateEffectiveFormat}. */ - public JsonSerializer findCollectionLikeSerializer(SerializationConfig config, - CollectionLikeType type, BeanDescription beanDesc, - TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer); + default JsonSerializer findCollectionLikeSerializer(SerializationConfig config, + CollectionLikeType type, BeanDescription beanDesc, JsonFormat.Value formatOverrides, + TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) + { + return null; + } /** * Method called by serialization framework first time a serializer is needed for * specified {@link java.util.Map} type. * Implementation should return a serializer instance if it supports * specified type; or null if it does not. + * + * @param formatOverrides (nullable) Optional format overrides (usually from property definition), + * to change definitions that {@code beanDesc} may have (and which are NOT included). Usually + * combined calling {@code Serializers.Base#calculateEffectiveFormat}. */ - public JsonSerializer findMapSerializer(SerializationConfig config, - MapType type, BeanDescription beanDesc, + default JsonSerializer findMapSerializer(SerializationConfig config, + MapType type, BeanDescription beanDesc, JsonFormat.Value formatOverrides, JsonSerializer keySerializer, - TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer); + TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) + { + return null; + } /** * Method called by serialization framework first time a serializer is needed for @@ -87,11 +127,33 @@ public JsonSerializer findMapSerializer(SerializationConfig config, * but does not implement it). * Implementation should return a serializer instance if it supports * specified type; or null if it does not. + * + * @param formatOverrides (nullable) Optional format overrides (usually from property definition), + * to change definitions that {@code beanDesc} may have (and which are NOT included). Usually + * combined calling {@code Serializers.Base#calculateEffectiveFormat}. */ - public JsonSerializer findMapLikeSerializer(SerializationConfig config, - MapLikeType type, BeanDescription beanDesc, + default JsonSerializer findMapLikeSerializer(SerializationConfig config, + MapLikeType type, BeanDescription beanDesc, JsonFormat.Value formatOverrides, JsonSerializer keySerializer, - TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer); + TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) + { + return null; + } + + /** + * Method called in case that a given type or property is declared to use shape + * {@link JsonFormat.Shape#POJO} and is expected to be serialized "as POJO", that is, + * as an (JSON) Object. This is usually NOT handled by extension modules as core + * databind knows how to do this, but sometimes it may be necessary to override + * this behavior. + * + * @since 3.0 + */ + default JsonSerializer findExplicitPOJOSerializer(SerializationConfig config, + JavaType type, BeanDescription beanDesc, JsonFormat.Value formatOverrides) + { + return null; + } /** * Basic {@link Serializers} implementation that implements all methods but provides @@ -102,23 +164,21 @@ public static class Base implements Serializers { @Override public JsonSerializer findSerializer(SerializationConfig config, - JavaType type, BeanDescription beanDesc) + JavaType type, BeanDescription beanDesc, JsonFormat.Value formatOverrides) { return null; } @Override public JsonSerializer findReferenceSerializer(SerializationConfig config, - ReferenceType type, BeanDescription beanDesc, + ReferenceType type, BeanDescription beanDesc, JsonFormat.Value formatOverrides, TypeSerializer contentTypeSerializer, JsonSerializer contentValueSerializer) { - // 21-Oct-2015, tatu: For backwards compatibility, let's delegate to "bean" variant, - // for 2.7 -- remove work-around from 2.8 or later - return findSerializer(config, type, beanDesc); + return null; } @Override public JsonSerializer findArraySerializer(SerializationConfig config, - ArrayType type, BeanDescription beanDesc, + ArrayType type, BeanDescription beanDesc, JsonFormat.Value formatOverrides, TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) { return null; @@ -126,7 +186,7 @@ public JsonSerializer findArraySerializer(SerializationConfig config, @Override public JsonSerializer findCollectionSerializer(SerializationConfig config, - CollectionType type, BeanDescription beanDesc, + CollectionType type, BeanDescription beanDesc, JsonFormat.Value formatOverrides, TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) { return null; @@ -134,7 +194,7 @@ public JsonSerializer findCollectionSerializer(SerializationConfig config, @Override public JsonSerializer findCollectionLikeSerializer(SerializationConfig config, - CollectionLikeType type, BeanDescription beanDesc, + CollectionLikeType type, BeanDescription beanDesc, JsonFormat.Value formatOverrides, TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) { return null; @@ -142,7 +202,7 @@ public JsonSerializer findCollectionLikeSerializer(SerializationConfig config @Override public JsonSerializer findMapSerializer(SerializationConfig config, - MapType type, BeanDescription beanDesc, + MapType type, BeanDescription beanDesc, JsonFormat.Value formatOverrides, JsonSerializer keySerializer, TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) { @@ -151,11 +211,40 @@ public JsonSerializer findMapSerializer(SerializationConfig config, @Override public JsonSerializer findMapLikeSerializer(SerializationConfig config, - MapLikeType type, BeanDescription beanDesc, + MapLikeType type, BeanDescription beanDesc, JsonFormat.Value formatOverrides, JsonSerializer keySerializer, TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) { return null; } + + @Override + public JsonSerializer findExplicitPOJOSerializer(SerializationConfig config, + JavaType type, BeanDescription beanDesc, JsonFormat.Value formatOverrides) + { + return null; + } + + /* + /****************************************************************** + /* Helper methods + /****************************************************************** + */ + + /** + * Helper method for determining effective combination of formatting settings + * from combination of Class annotations and config overrides for type and + * possible per-property overrides (in this order of precedence from lowest + * to highest). + */ + protected JsonFormat.Value calculateEffectiveFormat(BeanDescription beanDesc, + Class baseType, JsonFormat.Value formatOverrides) + { + JsonFormat.Value fromType = beanDesc.findExpectedFormat(baseType); + if (formatOverrides == null) { + return fromType; + } + return JsonFormat.Value.merge(fromType, formatOverrides); + } } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/UnrolledBeanSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/UnrolledBeanSerializer.java new file mode 100644 index 0000000000..8d73173650 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/ser/UnrolledBeanSerializer.java @@ -0,0 +1,209 @@ +package com.fasterxml.jackson.databind.ser; + +import java.io.IOException; +import java.util.Set; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.ser.impl.BeanAsArraySerializer; +import com.fasterxml.jackson.databind.ser.impl.ObjectIdWriter; +import com.fasterxml.jackson.databind.ser.impl.UnwrappingBeanSerializer; +import com.fasterxml.jackson.databind.ser.std.BeanSerializerBase; +import com.fasterxml.jackson.databind.util.NameTransformer; + +/** + * @since 3.0 + */ +@JacksonStdImpl +public class UnrolledBeanSerializer + extends BeanSerializerBase +{ + private static final long serialVersionUID = 30; // as per jackson 3.0 + + /* 28-Oct-2017, tatu: Exact choice for max number of properties to unroll + * is difficult to pin down, but probably has to be at least 4, and + * at most 8. Partly this is due to "blocks of 4" that default bean + * serializer now uses, and partly guessing how aggressively JVM might + * inline larger methods (more unroll, bigger method). + */ + private static final int MAX_PROPS = 6; + + protected final int _propCount; + + // // // We store separate references in form more easily accessed + // // // from switch statement + + protected BeanPropertyWriter _prop1; + protected BeanPropertyWriter _prop2; + protected BeanPropertyWriter _prop3; + protected BeanPropertyWriter _prop4; + protected BeanPropertyWriter _prop5; + protected BeanPropertyWriter _prop6; + + /* + /********************************************************** + /* Life-cycle: constructors + /********************************************************** + */ + + /** + * @param builder Builder object that contains collected information + * that may be needed for serializer + * @param properties Property writers used for actual serialization + */ + public UnrolledBeanSerializer(JavaType type, BeanSerializerBuilder builder, + BeanPropertyWriter[] properties, BeanPropertyWriter[] filteredProperties) + { + super(type, builder, properties, filteredProperties); + _propCount = _props.length; + _calcUnrolled(); + } + + protected UnrolledBeanSerializer(UnrolledBeanSerializer src, Set toIgnore) { + super(src, toIgnore); + _propCount = _props.length; + _calcUnrolled(); + } + + private void _calcUnrolled() { + BeanPropertyWriter[] oProps = new BeanPropertyWriter[6]; + int offset = 6 - _propCount; + System.arraycopy(_props, 0, oProps, offset, _propCount); + _prop1 = oProps[0]; + _prop2 = oProps[1]; + _prop3 = oProps[2]; + _prop4 = oProps[3]; + _prop5 = oProps[4]; + _prop6 = oProps[5]; + } + + /** + * Factory method that will construct optimized instance if all the constraints + * are obeyed; or, if not, return `null` to indicate that instance can not be + * created. + */ + public static UnrolledBeanSerializer tryConstruct(JavaType type, BeanSerializerBuilder builder, + BeanPropertyWriter[] properties, BeanPropertyWriter[] filteredProperties) + { + if ((properties.length > MAX_PROPS) + || (builder.getFilterId() != null)) { + return null; + } + return new UnrolledBeanSerializer(type, builder, properties, filteredProperties); + } + + /* + /********************************************************** + /* Life-cycle: factory methods, fluent factories + /********************************************************** + */ + + @Override + public JsonSerializer unwrappingSerializer(NameTransformer unwrapper) { + return new UnwrappingBeanSerializer(this, unwrapper); + } + + @Override + public BeanSerializerBase withObjectIdWriter(ObjectIdWriter objectIdWriter) { + // Revert to Vanilla variant, if so: + return new BeanSerializer(this, objectIdWriter, _propertyFilterId); + } + + @Override + public BeanSerializerBase withFilterId(Object filterId) { + // Revert to Vanilla variant, if so: + return new BeanSerializer(this, _objectIdWriter, filterId); + } + + @Override + protected BeanSerializerBase withIgnorals(Set toIgnore) { + return new UnrolledBeanSerializer(this, toIgnore); + } + + @Override + protected BeanSerializerBase asArraySerializer() + { + if (canCreateArraySerializer()) { + return BeanAsArraySerializer.construct(this); + } + // Can't... so use this one + return this; + } + + @Override + public void resolve(SerializerProvider provider) throws JsonMappingException + { + super.resolve(provider); + _calcUnrolled(); + } + + /* + /********************************************************** + /* JsonSerializer implementation that differs between impls + /********************************************************** + */ + + @Override + public void serialize(Object bean, JsonGenerator gen, SerializerProvider provider) + throws IOException + { + // NOTE! We have ensured that "JSON Filter" and "Object Id" cases + // always use "vanilla" BeanSerializer, so no need to check here + + BeanPropertyWriter[] fProps = _filteredProps; + if ((fProps != null) && (provider.getActiveView() != null)) { + gen.writeStartObject(bean); + _serializeFieldsMaybeView(bean, gen, provider, fProps); + gen.writeEndObject(); + return; + } + serializeNonFiltered(bean, gen, provider); + } + + protected void serializeNonFiltered(Object bean, JsonGenerator gen, SerializerProvider provider) + throws IOException + { + gen.writeStartObject(bean); + + BeanPropertyWriter prop = null; + try { + switch (_propCount) { + default: + //case 6: + prop = _prop1; + prop.serializeAsField(bean, gen, provider); + // fall through + case 5: + prop = _prop2; + prop.serializeAsField(bean, gen, provider); + case 4: + prop = _prop3; + prop.serializeAsField(bean, gen, provider); + case 3: + prop = _prop4; + prop.serializeAsField(bean, gen, provider); + case 2: + prop = _prop5; + prop.serializeAsField(bean, gen, provider); + case 1: + prop = _prop6; + prop.serializeAsField(bean, gen, provider); + case 0: + } + prop = null; + if (_anyGetterWriter != null) { + _anyGetterWriter.getAndSerialize(bean, gen, provider); + } + } catch (Exception e) { + String name = (prop == null) ? "[anySetter]" : prop.getName(); + wrapAndThrow(provider, e, bean, name); + } catch (StackOverflowError e) { + JsonMappingException mapE = new JsonMappingException(gen, "Infinite recursion (StackOverflowError)", e); + String name = (prop == null) ? "[anySetter]" : prop.getName(); + mapE.prependPath(new JsonMappingException.Reference(bean, name)); + throw mapE; + } + gen.writeEndObject(); + } +} diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/VirtualBeanPropertyWriter.java b/src/main/java/com/fasterxml/jackson/databind/ser/VirtualBeanPropertyWriter.java index ad6cd35371..7803b8b1aa 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/VirtualBeanPropertyWriter.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/VirtualBeanPropertyWriter.java @@ -16,8 +16,6 @@ * {@link com.fasterxml.jackson.databind.annotation.JsonAppend} * to add "virtual" properties in addition to regular ones. * - * @since 2.5 - * * @see com.fasterxml.jackson.databind.ser.impl.AttributePropertyWriter */ public abstract class VirtualBeanPropertyWriter @@ -33,7 +31,7 @@ protected VirtualBeanPropertyWriter(BeanPropertyDefinition propDef, Annotations contextAnnotations, JavaType declaredType) { this(propDef, contextAnnotations, declaredType, null, null, null, - propDef.findInclusion()); + propDef.findInclusion(), null); } /** @@ -60,15 +58,6 @@ protected VirtualBeanPropertyWriter(BeanPropertyDefinition propDef, includeInViews); } - @Deprecated // since 2.8 - protected VirtualBeanPropertyWriter(BeanPropertyDefinition propDef, - Annotations contextAnnotations, JavaType declaredType, - JsonSerializer ser, TypeSerializer typeSer, JavaType serType, - JsonInclude.Value inclusion) - { - this(propDef, contextAnnotations, declaredType, ser, typeSer, serType, inclusion, null); - } - protected VirtualBeanPropertyWriter(VirtualBeanPropertyWriter base) { super(base); } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/BeanAsArraySerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/BeanAsArraySerializer.java index 64ced81def..5156044a35 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/BeanAsArraySerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/BeanAsArraySerializer.java @@ -40,13 +40,11 @@ * In cases where array-based output is not feasible, this serializer * can instead delegate to the original Object-based serializer; this * is why a reference is retained to the original serializer. - * - * @since 2.1 */ public class BeanAsArraySerializer extends BeanSerializerBase { - private static final long serialVersionUID = 1L; // since 2.6 + private static final long serialVersionUID = 3L; /** * Serializer that would produce JSON Object version; used in @@ -75,18 +73,29 @@ protected BeanAsArraySerializer(BeanSerializerBase src, super(src, oiw, filterId); _defaultSerializer = src; } - + /* /********************************************************** /* Life-cycle: factory methods, fluent factories /********************************************************** */ + /** + * @since 3.0 + */ + public static BeanSerializerBase construct(BeanSerializerBase src) + { + BeanSerializerBase ser = UnrolledBeanAsArraySerializer.tryConstruct(src); + if (ser != null) { + return ser; + } + return new BeanAsArraySerializer(src); + } + @Override public JsonSerializer unwrappingSerializer(NameTransformer transformer) { - /* If this gets called, we will just need delegate to the default - * serializer, to "undo" as-array serialization - */ + // If this gets called, we will just need delegate to the default + // serializer, to "undo" as-array serialization return _defaultSerializer.unwrappingSerializer(transformer); } @@ -129,9 +138,8 @@ public void serializeWithType(Object bean, JsonGenerator gen, SerializerProvider provider, TypeSerializer typeSer) throws IOException { - /* 10-Dec-2014, tatu: Not sure if this can be made to work reliably; - * but for sure delegating to default implementation will not work. So: - */ + // 10-Dec-2014, tatu: Not sure if this can be made to work reliably; + // but for sure delegating to default implementation will not work. So: if (_objectIdWriter != null) { _serializeWithObjectId(bean, gen, provider, typeSer); return; @@ -139,7 +147,9 @@ public void serializeWithType(Object bean, JsonGenerator gen, gen.setCurrentValue(bean); WritableTypeId typeIdDef = _typeIdDef(typeSer, bean, JsonToken.START_ARRAY); typeSer.writeTypePrefix(gen, typeIdDef); - serializeAsArray(bean, gen, provider); + final boolean filtered = (_filteredProps != null && provider.getActiveView() != null); + if (filtered) serializeFiltered(bean, gen, provider); + else serializeNonFiltered(bean, gen, provider); typeSer.writeTypeSuffix(gen, typeIdDef); } @@ -152,19 +162,20 @@ public void serializeWithType(Object bean, JsonGenerator gen, public final void serialize(Object bean, JsonGenerator gen, SerializerProvider provider) throws IOException { + final boolean filtered = (_filteredProps != null && provider.getActiveView() != null); if (provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED) && hasSingleElement(provider)) { - serializeAsArray(bean, gen, provider); + if (filtered) serializeFiltered(bean, gen, provider); + else serializeNonFiltered(bean, gen, provider); return; } - /* note: it is assumed here that limitations (type id, object id, - * any getter, filtering) have already been checked; so code here - * is trivial. - */ - gen.writeStartArray(); - // [databind#631]: Assign current value, to be accessible by custom serializers - gen.setCurrentValue(bean); - serializeAsArray(bean, gen, provider); + // note: it is assumed here that limitations (type id, object id, + // any getter, filtering) have already been checked; so code here + // is trivial. + + gen.writeStartArray(bean, _props.length); + if (filtered) serializeFiltered(bean, gen, provider); + else serializeNonFiltered(bean, gen, provider); gen.writeEndArray(); } @@ -175,30 +186,118 @@ && hasSingleElement(provider)) { */ private boolean hasSingleElement(SerializerProvider provider) { - final BeanPropertyWriter[] props; - if (_filteredProps != null && provider.getActiveView() != null) { - props = _filteredProps; - } else { - props = _props; - } - return props.length == 1; + return _props.length == 1; } - protected final void serializeAsArray(Object bean, JsonGenerator gen, SerializerProvider provider) + protected final void serializeNonFiltered(Object bean, JsonGenerator gen, + SerializerProvider provider) throws IOException { - final BeanPropertyWriter[] props; - if (_filteredProps != null && provider.getActiveView() != null) { - props = _filteredProps; - } else { - props = _props; + final BeanPropertyWriter[] props = _props; + int i = 0; + int left = props.length; + BeanPropertyWriter prop = null; + + try { + if (left > 3) { + do { + prop = props[i]; + prop.serializeAsElement(bean, gen, provider); + prop = props[i+1]; + prop.serializeAsElement(bean, gen, provider); + prop = props[i+2]; + prop.serializeAsElement(bean, gen, provider); + prop = props[i+3]; + prop.serializeAsElement(bean, gen, provider); + left -= 4; + i += 4; + } while (left > 3); + } + switch (left) { + case 3: + prop = props[i++]; + prop.serializeAsElement(bean, gen, provider); + case 2: + prop = props[i++]; + prop.serializeAsElement(bean, gen, provider); + case 1: + prop = props[i++]; + prop.serializeAsElement(bean, gen, provider); + } + // NOTE: any getters cannot be supported either + //if (_anyGetterWriter != null) { + // _anyGetterWriter.getAndSerialize(bean, gen, provider); + //} + } catch (Exception e) { + wrapAndThrow(provider, e, bean, prop.getName()); + } catch (StackOverflowError e) { + JsonMappingException mapE = JsonMappingException.from(gen, "Infinite recursion (StackOverflowError)", e); + mapE.prependPath(new JsonMappingException.Reference(bean, prop.getName())); + throw mapE; } + } + protected final void serializeFiltered(Object bean, JsonGenerator gen, SerializerProvider provider) + throws IOException + { + final BeanPropertyWriter[] props = _filteredProps; int i = 0; + int left = props.length; + BeanPropertyWriter prop = null; + try { - for (final int len = props.length; i < len; ++i) { - BeanPropertyWriter prop = props[i]; - if (prop == null) { // can have nulls in filtered list; but if so, MUST write placeholders + if (left > 3) { + do { + prop = props[i]; + if (prop == null) { // can have nulls in filtered list; but if so, MUST write placeholders + gen.writeNull(); + } else { + prop.serializeAsElement(bean, gen, provider); + } + + prop = props[i+1]; + if (prop == null) { + gen.writeNull(); + } else { + prop.serializeAsElement(bean, gen, provider); + } + + prop = props[i+2]; + if (prop == null) { + gen.writeNull(); + } else { + prop.serializeAsElement(bean, gen, provider); + } + + prop = props[i+3]; + if (prop == null) { + gen.writeNull(); + } else { + prop.serializeAsElement(bean, gen, provider); + } + + left -= 4; + i += 4; + } while (left > 3); + } + switch (left) { + case 3: + prop = props[i++]; + if (prop == null) { + gen.writeNull(); + } else { + prop.serializeAsElement(bean, gen, provider); + } + case 2: + prop = props[i++]; + if (prop == null) { + gen.writeNull(); + } else { + prop.serializeAsElement(bean, gen, provider); + } + case 1: + prop = props[i++]; + if (prop == null) { gen.writeNull(); } else { prop.serializeAsElement(bean, gen, provider); @@ -209,23 +308,11 @@ protected final void serializeAsArray(Object bean, JsonGenerator gen, Serializer // _anyGetterWriter.getAndSerialize(bean, gen, provider); //} } catch (Exception e) { - String name = (i == props.length) ? "[anySetter]" : props[i].getName(); - wrapAndThrow(provider, e, bean, name); + wrapAndThrow(provider, e, bean, prop.getName()); } catch (StackOverflowError e) { JsonMappingException mapE = JsonMappingException.from(gen, "Infinite recursion (StackOverflowError)", e); - String name = (i == props.length) ? "[anySetter]" : props[i].getName(); - mapE.prependPath(new JsonMappingException.Reference(bean, name)); + mapE.prependPath(new JsonMappingException.Reference(bean, prop.getName())); throw mapE; } } - - /* - /********************************************************** - /* Standard methods - /********************************************************** - */ - - @Override public String toString() { - return "BeanAsArraySerializer for "+handledType().getName(); - } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/FailingSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/FailingSerializer.java index 6021fa89e0..1ffd836986 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/FailingSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/FailingSerializer.java @@ -1,12 +1,10 @@ package com.fasterxml.jackson.databind.ser.impl; import java.io.IOException; -import java.lang.reflect.Type; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import com.fasterxml.jackson.databind.ser.std.StdSerializer; @@ -36,13 +34,7 @@ public void serialize(Object value, JsonGenerator g, SerializerProvider provider } @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException { - return null; - } - - @Override - public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) - { + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) { ; } } 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 44f5c877a2..c4f68ce201 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 @@ -17,7 +17,7 @@ */ @JacksonStdImpl public final class IndexedListSerializer - extends AsArraySerializerBase> + extends AsArraySerializerBase { private static final long serialVersionUID = 1L; @@ -47,13 +47,13 @@ public IndexedListSerializer withResolved(BeanProperty property, */ @Override - public boolean isEmpty(SerializerProvider prov, List value) { - return value.isEmpty(); + public boolean isEmpty(SerializerProvider prov, Object value) { + return ((List)value).isEmpty(); } @Override - public boolean hasSingleElement(List value) { - return (value.size() == 1); + public boolean hasSingleElement(Object value) { + return (((List)value).size() == 1); } @Override @@ -63,9 +63,10 @@ public ContainerSerializer _withValueTypeSerializer(TypeSerializer vts) { } @Override - public final void serialize(List value, JsonGenerator gen, SerializerProvider provider) + public final void serialize(Object value0, JsonGenerator gen, SerializerProvider provider) throws IOException { + final List value = (List) value0; final int len = value.size(); if (len == 1) { if (((_unwrapSingle == null) && @@ -75,21 +76,22 @@ public final void serialize(List value, JsonGenerator gen, SerializerProvider return; } } - gen.writeStartArray(len); + gen.writeStartArray(value, len); serializeContents(value, gen, provider); gen.writeEndArray(); } @Override - public void serializeContents(List value, JsonGenerator g, SerializerProvider provider) + public void serializeContents(Object value0, JsonGenerator g, SerializerProvider ctxt) throws IOException { + final List value = (List) value0; if (_elementSerializer != null) { - serializeContentsUsing(value, g, provider, _elementSerializer); + serializeContentsUsing(value, g, ctxt, _elementSerializer); return; } if (_valueTypeSerializer != null) { - serializeTypedContents(value, g, provider); + serializeTypedContents(value, g, ctxt); return; } final int len = value.size(); @@ -98,29 +100,27 @@ public void serializeContents(List value, JsonGenerator g, SerializerProvider } int i = 0; try { - PropertySerializerMap serializers = _dynamicSerializers; for (; i < len; ++i) { Object elem = value.get(i); if (elem == null) { - provider.defaultSerializeNull(g); + ctxt.defaultSerializeNullValue(g); } else { Class cc = elem.getClass(); - JsonSerializer serializer = serializers.serializerFor(cc); + JsonSerializer serializer = _dynamicValueSerializers.serializerFor(cc); if (serializer == null) { // To fix [JACKSON-508] if (_elementType.hasGenericTypes()) { - serializer = _findAndAddDynamic(serializers, - provider.constructSpecializedType(_elementType, cc), provider); + serializer = _findAndAddDynamic(ctxt, + ctxt.constructSpecializedType(_elementType, cc)); } else { - serializer = _findAndAddDynamic(serializers, cc, provider); + serializer = _findAndAddDynamic(ctxt, cc); } - serializers = _dynamicSerializers; } - serializer.serialize(elem, g, provider); + serializer.serialize(elem, g, ctxt); } } } catch (Exception e) { - wrapAndThrow(provider, e, value, i); + wrapAndThrow(ctxt, e, value, i); } } @@ -137,7 +137,7 @@ public void serializeContentsUsing(List value, JsonGenerator jgen, Serializer Object elem = value.get(i); try { if (elem == null) { - provider.defaultSerializeNull(jgen); + provider.defaultSerializeNullValue(jgen); } else if (typeSer == null) { ser.serialize(elem, jgen, provider); } else { @@ -150,7 +150,7 @@ public void serializeContentsUsing(List value, JsonGenerator jgen, Serializer } } - public void serializeTypedContents(List value, JsonGenerator jgen, SerializerProvider provider) + public void serializeTypedContents(List value, JsonGenerator g, SerializerProvider ctxt) throws IOException { final int len = value.size(); @@ -160,30 +160,28 @@ public void serializeTypedContents(List value, JsonGenerator jgen, Serializer int i = 0; try { final TypeSerializer typeSer = _valueTypeSerializer; - PropertySerializerMap serializers = _dynamicSerializers; + PropertySerializerMap serializers = _dynamicValueSerializers; for (; i < len; ++i) { Object elem = value.get(i); if (elem == null) { - provider.defaultSerializeNull(jgen); + ctxt.defaultSerializeNullValue(g); } else { Class cc = elem.getClass(); JsonSerializer serializer = serializers.serializerFor(cc); if (serializer == null) { - // To fix [JACKSON-508] if (_elementType.hasGenericTypes()) { - serializer = _findAndAddDynamic(serializers, - provider.constructSpecializedType(_elementType, cc), provider); + serializer = _findAndAddDynamic(ctxt, + ctxt.constructSpecializedType(_elementType, cc)); } else { - serializer = _findAndAddDynamic(serializers, cc, provider); + serializer = _findAndAddDynamic(ctxt, cc); } - serializers = _dynamicSerializers; + serializers = _dynamicValueSerializers; } - serializer.serializeWithType(elem, jgen, provider, typeSer); + serializer.serializeWithType(elem, g, ctxt, typeSer); } } } catch (Exception e) { - // [JACKSON-55] Need to add reference information - wrapAndThrow(provider, e, value, i); + wrapAndThrow(ctxt, e, value, i); } } } 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 fbe88a6b57..7e9f520b11 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 @@ -73,7 +73,7 @@ public void serialize(List value, JsonGenerator g, return; } } - g.writeStartArray(len); + g.writeStartArray(value, len); serializeContents(value, g, provider, len); g.writeEndArray(); } @@ -85,6 +85,7 @@ public void serializeWithType(List value, JsonGenerator g, SerializerPro { WritableTypeId typeIdDef = typeSer.writeTypePrefix(g, typeSer.typeId(value, JsonToken.START_ARRAY)); + g.setCurrentValue(value); serializeContents(value, g, provider, value.size()); typeSer.writeTypeSuffix(g, typeIdDef); } @@ -92,13 +93,12 @@ public void serializeWithType(List value, JsonGenerator g, SerializerPro private final void serializeContents(List value, JsonGenerator g, SerializerProvider provider, int len) throws IOException { - g.setCurrentValue(value); int i = 0; try { for (; i < len; ++i) { String str = value.get(i); if (str == null) { - provider.defaultSerializeNull(g); + provider.defaultSerializeNullValue(g); } else { g.writeString(str); } 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 cf756db2bf..ed71c526cb 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 @@ -84,7 +84,7 @@ public void serializeContents(Iterator value, JsonGenerator g, do { Object elem = value.next(); if (elem == null) { - provider.defaultSerializeNull(g); + provider.defaultSerializeNullValue(g); } else if (typeSer == null) { serializer.serialize(elem, g, provider); } else { @@ -94,32 +94,30 @@ public void serializeContents(Iterator value, JsonGenerator g, } protected void _serializeDynamicContents(Iterator value, JsonGenerator g, - SerializerProvider provider) throws IOException + SerializerProvider ctxt) throws IOException { final TypeSerializer typeSer = _valueTypeSerializer; - PropertySerializerMap serializers = _dynamicSerializers; do { Object elem = value.next(); if (elem == null) { - provider.defaultSerializeNull(g); + ctxt.defaultSerializeNullValue(g); continue; } Class cc = elem.getClass(); - JsonSerializer serializer = serializers.serializerFor(cc); + JsonSerializer serializer = _dynamicValueSerializers.serializerFor(cc); if (serializer == null) { if (_elementType.hasGenericTypes()) { - serializer = _findAndAddDynamic(serializers, - provider.constructSpecializedType(_elementType, cc), provider); + serializer = _findAndAddDynamic(ctxt, + ctxt.constructSpecializedType(_elementType, cc)); } else { - serializer = _findAndAddDynamic(serializers, cc, provider); + serializer = _findAndAddDynamic(ctxt, cc); } - serializers = _dynamicSerializers; } if (typeSer == null) { - serializer.serialize(elem, g, provider); + serializer.serialize(elem, g, ctxt); } else { - serializer.serializeWithType(elem, g, provider, typeSer); + serializer.serializeWithType(elem, g, ctxt, typeSer); } } while (value.hasNext()); } -} \ No newline at end of file +} diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/MapEntrySerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/MapEntrySerializer.java index 27fc700209..cfe6f16259 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/MapEntrySerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/MapEntrySerializer.java @@ -9,34 +9,22 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.core.type.WritableTypeId; + 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; import com.fasterxml.jackson.databind.util.ArrayBuilders; import com.fasterxml.jackson.databind.util.BeanUtil; -/** - * @since 2.5 - */ @SuppressWarnings("serial") @JacksonStdImpl public class MapEntrySerializer extends ContainerSerializer> - implements ContextualSerializer { - /** - * @since 2.9 - */ public final static Object MARKER_FOR_EMPTY = JsonInclude.Include.NON_EMPTY; - /** - * 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) @@ -46,9 +34,9 @@ public class MapEntrySerializer protected final JavaType _entryType, _keyType, _valueType; /* - /********************************************************** + /********************************************************************** /* Serializers used - /********************************************************** + /********************************************************************** */ /** @@ -66,16 +54,10 @@ public class MapEntrySerializer */ protected final TypeSerializer _valueTypeSerializer; - /** - * If value type cannot be statically determined, mapping from - * runtime value types to serializers are stored in this object. - */ - protected PropertySerializerMap _dynamicValueSerializers; - /* - /********************************************************** + /********************************************************************** /* Config settings, filtering - /********************************************************** + /********************************************************************** */ /** @@ -85,57 +67,42 @@ public class MapEntrySerializer * non-null values. * Note that inclusion value for Map instance itself is handled by caller (POJO * property that refers to the Map value). - * - * @since 2.5 */ protected final Object _suppressableValue; /** * Flag that indicates what to do with `null` values, distinct from * handling of {@link #_suppressableValue} - * - * @since 2.9 */ protected final boolean _suppressNulls; /* - /********************************************************** + /********************************************************************** /* Construction, initialization - /********************************************************** + /********************************************************************** */ public MapEntrySerializer(JavaType type, JavaType keyType, JavaType valueType, boolean staticTyping, TypeSerializer vts, BeanProperty property) { - super(type); + super(type, property); _entryType = type; _keyType = keyType; _valueType = valueType; _valueTypeIsStatic = staticTyping; _valueTypeSerializer = vts; - _property = property; - _dynamicValueSerializers = PropertySerializerMap.emptyForProperties(); _suppressableValue = null; _suppressNulls = false; } - @Deprecated // since 2.9 - protected MapEntrySerializer(MapEntrySerializer src, BeanProperty property, - TypeSerializer vts, - JsonSerializer keySer, JsonSerializer valueSer) - { - this(src, property, vts, keySer, valueSer, - src._suppressableValue, src._suppressNulls); - } - @SuppressWarnings("unchecked") protected MapEntrySerializer(MapEntrySerializer src, BeanProperty property, TypeSerializer vts, JsonSerializer keySer, JsonSerializer valueSer, Object suppressableValue, boolean suppressNulls) { - super(Map.class, false); + super(src, property); _entryType = src._entryType; _keyType = src._keyType; _valueType = src._valueType; @@ -143,9 +110,6 @@ protected MapEntrySerializer(MapEntrySerializer src, BeanProperty property, _valueTypeSerializer = src._valueTypeSerializer; _keySerializer = (JsonSerializer) keySer; _valueSerializer = (JsonSerializer) valueSer; - // [databind#2181]: may not be safe to reuse, start from empty - _dynamicValueSerializers = PropertySerializerMap.emptyForProperties(); - _property = src._property; _suppressableValue = suppressableValue; _suppressNulls = suppressNulls; } @@ -156,9 +120,6 @@ public ContainerSerializer _withValueTypeSerializer(TypeSerializer vts) { _suppressableValue, _suppressNulls); } - /** - * @since 2.9 - */ public MapEntrySerializer withResolved(BeanProperty property, JsonSerializer keySerializer, JsonSerializer valueSerializer, Object suppressableValue, boolean suppressNulls) { @@ -166,9 +127,6 @@ public MapEntrySerializer withResolved(BeanProperty property, keySerializer, valueSerializer, suppressableValue, suppressNulls); } - /** - * @since 2.9 - */ public MapEntrySerializer withContentInclusion(Object suppressableValue, boolean suppressNulls) { if ((_suppressableValue == suppressableValue) @@ -189,15 +147,11 @@ public JsonSerializer createContextual(SerializerProvider provider, 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 (_neitherNull(propertyAcc, intr)) { + keySer = provider.serializerInstance(propertyAcc, + intr.findKeySerializer(provider.getConfig(), propertyAcc)); + ser = provider.serializerInstance(propertyAcc, + intr.findContentSerializer(provider.getConfig(), propertyAcc)); } if (ser == null) { ser = _valueSerializer; @@ -209,7 +163,7 @@ public JsonSerializer createContextual(SerializerProvider provider, // 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.isJavaLangObject()) { - ser = provider.findValueSerializer(_valueType, property); + ser = provider.findSecondaryPropertySerializer(_valueType, property); } } if (keySer == null) { @@ -269,7 +223,6 @@ public JsonSerializer createContextual(SerializerProvider provider, } } } - MapEntrySerializer mser = withResolved(property, keySer, ser, valueToSuppress, suppressNulls); // but note: no (full) filtering or sorting (unlike Maps) @@ -277,9 +230,9 @@ public JsonSerializer createContextual(SerializerProvider provider, } /* - /********************************************************** + /********************************************************************** /* Accessors - /********************************************************** + /********************************************************************** */ @Override @@ -298,7 +251,7 @@ public boolean hasSingleElement(Map.Entry value) { } @Override - public boolean isEmpty(SerializerProvider prov, Entry entry) + public boolean isEmpty(SerializerProvider ctxt, Entry entry) throws IOException { Object value = entry.getValue(); if (value == null) { @@ -314,48 +267,44 @@ public boolean isEmpty(SerializerProvider prov, Entry entry) Class cc = value.getClass(); valueSer = _dynamicValueSerializers.serializerFor(cc); if (valueSer == null) { - try { - valueSer = _findAndAddDynamic(_dynamicValueSerializers, cc, prov); - } catch (JsonMappingException e) { // Ugh... cannot just throw as-is, so... - return false; - } + valueSer = _findAndAddDynamic(ctxt, cc); } } if (_suppressableValue == MARKER_FOR_EMPTY) { - return valueSer.isEmpty(prov, value); + return valueSer.isEmpty(ctxt, value); } return _suppressableValue.equals(value); } /* - /********************************************************** + /********************************************************************** /* Serialization methods - /********************************************************** + /********************************************************************** */ @Override - public void serialize(Map.Entry value, JsonGenerator gen, SerializerProvider provider) + public void serialize(Map.Entry value, JsonGenerator g, SerializerProvider ctxt) throws IOException { - gen.writeStartObject(value); - serializeDynamic(value, gen, provider); - gen.writeEndObject(); + g.writeStartObject(value); + serializeDynamic(value, g, ctxt); + g.writeEndObject(); } @Override public void serializeWithType(Map.Entry value, JsonGenerator g, - SerializerProvider provider, TypeSerializer typeSer) throws IOException + SerializerProvider ctxt, TypeSerializer typeSer) throws IOException { // [databind#631]: Assign current value, to be accessible by custom serializers g.setCurrentValue(value); WritableTypeId typeIdDef = typeSer.writeTypePrefix(g, typeSer.typeId(value, JsonToken.START_OBJECT)); - serializeDynamic(value, g, provider); + serializeDynamic(value, g, ctxt); typeSer.writeTypeSuffix(g, typeIdDef); } protected void serializeDynamic(Map.Entry value, JsonGenerator gen, - SerializerProvider provider) + SerializerProvider ctxt) throws IOException { final TypeSerializer vts = _valueTypeSerializer; @@ -363,7 +312,7 @@ protected void serializeDynamic(Map.Entry value, JsonGenerator gen, JsonSerializer keySerializer; if (keyElem == null) { - keySerializer = provider.findNullKeySerializer(_keyType, _property); + keySerializer = ctxt.findNullKeySerializer(_keyType, _property); } else { keySerializer = _keySerializer; } @@ -375,7 +324,7 @@ protected void serializeDynamic(Map.Entry value, JsonGenerator gen, if (_suppressNulls) { return; } - valueSer = provider.getDefaultNullValueSerializer(); + valueSer = ctxt.getDefaultNullValueSerializer(); } else { valueSer = _valueSerializer; if (valueSer == null) { @@ -383,17 +332,17 @@ protected void serializeDynamic(Map.Entry value, JsonGenerator gen, valueSer = _dynamicValueSerializers.serializerFor(cc); if (valueSer == null) { if (_valueType.hasGenericTypes()) { - valueSer = _findAndAddDynamic(_dynamicValueSerializers, - provider.constructSpecializedType(_valueType, cc), provider); + valueSer = _findAndAddDynamic(ctxt, + ctxt.constructSpecializedType(_valueType, cc)); } else { - valueSer = _findAndAddDynamic(_dynamicValueSerializers, cc, provider); + valueSer = _findAndAddDynamic(ctxt, cc); } } } // also may need to skip non-empty values: if (_suppressableValue != null) { if (_suppressableValue == MARKER_FOR_EMPTY) { - if (valueSer.isEmpty(provider, valueElem)) { + if (valueSer.isEmpty(ctxt, valueElem)) { return; } } if (_suppressableValue.equals(valueElem)) { @@ -401,43 +350,16 @@ protected void serializeDynamic(Map.Entry value, JsonGenerator gen, } } } - keySerializer.serialize(keyElem, gen, provider); + keySerializer.serialize(keyElem, gen, ctxt); try { if (vts == null) { - valueSer.serialize(valueElem, gen, provider); + valueSer.serialize(valueElem, gen, ctxt); } else { - valueSer.serializeWithType(valueElem, gen, provider, vts); + valueSer.serializeWithType(valueElem, gen, ctxt, vts); } } catch (Exception e) { String keyDesc = ""+keyElem; - wrapAndThrow(provider, e, value, keyDesc); + wrapAndThrow(ctxt, e, value, keyDesc); } } - - /* - /********************************************************** - /* Internal helper methods - /********************************************************** - */ - - protected final JsonSerializer _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 _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/PropertySerializerMap.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/PropertySerializerMap.java index 839fe3dd45..494784bb01 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/PropertySerializerMap.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/PropertySerializerMap.java @@ -19,19 +19,17 @@ * is important to ensure correct multi-threaded access. */ public abstract class PropertySerializerMap + implements java.io.Serializable // since 3.0 to help keep JsonSerializer serializable { + private static final long serialVersionUID = 3L; + /** * Configuration setting that determines what happens when maximum * size (currently 8) is reached: if true, will "start from beginning"; * if false, will simply stop adding new entries. - * - * @since 2.5 */ protected final boolean _resetWhenFull; - /** - * @since 2.5 - */ protected PropertySerializerMap(boolean resetWhenFull) { _resetWhenFull = resetWhenFull; } @@ -52,19 +50,7 @@ protected PropertySerializerMap(PropertySerializerMap base) { * serializer (one that is directly attached to a property). * Will both find serializer * and construct new map instance if warranted, and return both. - * - * @since 2.3 - * - * @throws JsonMappingException */ - public final SerializerAndMapResult findAndAddPrimarySerializer(Class type, - SerializerProvider provider, BeanProperty property) - throws JsonMappingException - { - JsonSerializer serializer = provider.findPrimaryPropertySerializer(type, property); - return new SerializerAndMapResult(serializer, newWith(type, serializer)); - } - public final SerializerAndMapResult findAndAddPrimarySerializer(JavaType type, SerializerProvider provider, BeanProperty property) throws JsonMappingException @@ -78,16 +64,12 @@ public final SerializerAndMapResult findAndAddPrimarySerializer(JavaType type, * serializer (one that is not directly attached to a property). * Will both find serializer * and construct new map instance if warranted, and return both. - * - * @since 2.3 - * - * @throws JsonMappingException */ public final SerializerAndMapResult findAndAddSecondarySerializer(Class type, SerializerProvider provider, BeanProperty property) throws JsonMappingException { - JsonSerializer serializer = provider.findValueSerializer(type, property); + JsonSerializer serializer = provider.findSecondaryPropertySerializer(type, property); return new SerializerAndMapResult(serializer, newWith(type, serializer)); } @@ -95,7 +77,7 @@ public final SerializerAndMapResult findAndAddSecondarySerializer(JavaType type, SerializerProvider provider, BeanProperty property) throws JsonMappingException { - JsonSerializer serializer = provider.findValueSerializer(type, property); + JsonSerializer serializer = provider.findSecondaryPropertySerializer(type, property); return new SerializerAndMapResult(serializer, newWith(type.getRawClass(), serializer)); } @@ -105,27 +87,20 @@ public final SerializerAndMapResult findAndAddSecondarySerializer(JavaType type, * have {@link com.fasterxml.jackson.databind.jsontype.TypeSerializer} wrapped * around it. Will both find the serializer * and construct new map instance if warranted, and return both. - * - * @since 2.5 - * - * @throws JsonMappingException */ public final SerializerAndMapResult findAndAddRootValueSerializer(Class type, SerializerProvider provider) throws JsonMappingException { - JsonSerializer serializer = provider.findTypedValueSerializer(type, false, null); + JsonSerializer serializer = provider.findTypedValueSerializer(type, false); return new SerializerAndMapResult(serializer, newWith(type, serializer)); } - /** - * @since 2.5 - */ public final SerializerAndMapResult findAndAddRootValueSerializer(JavaType type, SerializerProvider provider) throws JsonMappingException { - JsonSerializer serializer = provider.findTypedValueSerializer(type, false, null); + JsonSerializer serializer = provider.findTypedValueSerializer(type, false); return new SerializerAndMapResult(serializer, newWith(type.getRawClass(), serializer)); } @@ -134,8 +109,6 @@ public final SerializerAndMapResult findAndAddRootValueSerializer(JavaType type, * serializer (possible attached indirectly to a property) * Will both find serializer * and construct new map instance if warranted, and return both. - * - * @since 2.7 */ public final SerializerAndMapResult findAndAddKeySerializer(Class type, SerializerProvider provider, BeanProperty property) @@ -148,48 +121,29 @@ public final SerializerAndMapResult findAndAddKeySerializer(Class type, /** * Method that can be used to 'register' a serializer that caller has resolved * without help of this map. - * - * @since 2.5 */ public final SerializerAndMapResult addSerializer(Class type, JsonSerializer serializer) { return new SerializerAndMapResult(serializer, newWith(type, serializer)); } - /** - * @since 2.5 - */ public final SerializerAndMapResult addSerializer(JavaType type, JsonSerializer serializer) { return new SerializerAndMapResult(serializer, newWith(type.getRawClass(), serializer)); } public abstract PropertySerializerMap newWith(Class type, JsonSerializer serializer); - /** - * @deprecated Since 2.5 Use {@link #emptyForProperties} instead - */ - @Deprecated - public static PropertySerializerMap emptyMap() { - return emptyForProperties(); - } - - /** - * @since 2.5 - */ public static PropertySerializerMap emptyForProperties() { return Empty.FOR_PROPERTIES; } - /** - * @since 2.5 - */ public static PropertySerializerMap emptyForRootValues() { return Empty.FOR_ROOT_VALUES; } /* - /********************************************************** + /********************************************************************** /* Helper classes - /********************************************************** + /********************************************************************** */ /** @@ -224,9 +178,9 @@ public TypeAndSerializer(Class type, JsonSerializer serializer) { } /* - /********************************************************** + /********************************************************************** /* Implementations - /********************************************************** + /********************************************************************** */ /** @@ -234,7 +188,10 @@ public TypeAndSerializer(Class type, JsonSerializer serializer) { * map with new serializers. */ private final static class Empty extends PropertySerializerMap + implements java.io.Serializable // since 3.0 { + private static final long serialVersionUID = 3L; + // No root serializers; do not reset when full public final static Empty FOR_PROPERTIES = new Empty(false); @@ -244,7 +201,16 @@ private final static class Empty extends PropertySerializerMap protected Empty(boolean resetWhenFull) { super(resetWhenFull); } + + // @since 3.0 + public static Empty emptyFor(PropertySerializerMap src) { + return (src._resetWhenFull) ? FOR_ROOT_VALUES : FOR_PROPERTIES; + } + Object readResolve() { // for JDK serialization (since 3.0) + return emptyFor(this); + } + @Override public JsonSerializer serializerFor(Class type) { return null; // empty, nothing to find @@ -263,7 +229,10 @@ public PropertySerializerMap newWith(Class type, JsonSerializer seria * actual type. */ private final static class Single extends PropertySerializerMap + implements java.io.Serializable // since 3.0 { + private static final long serialVersionUID = 3L; + private final Class _type; private final JsonSerializer _serializer; @@ -273,6 +242,10 @@ public Single(PropertySerializerMap base, Class type, JsonSerializer _serializer = serializer; } + Object writeReplace() { // for JDK serialization (since 3.0) + return Empty.emptyFor(this); + } + @Override public JsonSerializer serializerFor(Class type) { @@ -289,7 +262,10 @@ public PropertySerializerMap newWith(Class type, JsonSerializer seria } private final static class Double extends PropertySerializerMap + implements java.io.Serializable // since 3.0 { + private static final long serialVersionUID = 3L; + private final Class _type1, _type2; private final JsonSerializer _serializer1, _serializer2; @@ -304,6 +280,10 @@ public Double(PropertySerializerMap base, _serializer2 = serializer2; } + Object writeReplace() { // for JDK serialization (since 3.0) + return Empty.emptyFor(this); + } + @Override public JsonSerializer serializerFor(Class type) { @@ -328,7 +308,10 @@ public PropertySerializerMap newWith(Class type, JsonSerializer seria } private final static class Multi extends PropertySerializerMap + implements java.io.Serializable // since 3.0 { + private static final long serialVersionUID = 3L; + /** * Let's limit number of serializers we actually cache; linear * lookup won't scale too well beyond smallish number, and if @@ -346,6 +329,10 @@ public Multi(PropertySerializerMap base, TypeAndSerializer[] entries) { _entries = entries; } + Object writeReplace() { // for JDK serialization (since 3.0) + return Empty.emptyFor(this); + } + @Override public JsonSerializer serializerFor(Class type) { diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/ReadOnlyClassToSerializerMap.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/ReadOnlyClassToSerializerMap.java index feacbce21e..ea3e12f873 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/ReadOnlyClassToSerializerMap.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/ReadOnlyClassToSerializerMap.java @@ -1,42 +1,45 @@ package com.fasterxml.jackson.databind.ser.impl; -import java.util.*; - import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.ser.SerializerCache; +import com.fasterxml.jackson.databind.util.SimpleLookupCache; import com.fasterxml.jackson.databind.util.TypeKey; /** * Optimized lookup table for accessing two types of serializers; typed * and non-typed. Only accessed from a single thread, so no synchronization * needed for accessors. - *

    - * Note that before 2.6 this class was much smaller, and referred most - * operations to separate JsonSerializerMap, but in 2.6 - * functions were combined. */ public final class ReadOnlyClassToSerializerMap { + /** + * Shared cache used for call-throughs in cases where we do not have local matches. + * + * @since 3.0 + */ + private final SerializerCache _sharedCache; + private final Bucket[] _buckets; private final int _size; private final int _mask; - public ReadOnlyClassToSerializerMap(Map> serializers) + protected ReadOnlyClassToSerializerMap(SerializerCache shared, + SimpleLookupCache> src) { - int size = findSize(serializers.size()); - _size = size; - _mask = (size-1); - Bucket[] buckets = new Bucket[size]; - for (Map.Entry> entry : serializers.entrySet()) { - TypeKey key = entry.getKey(); + _sharedCache = shared; + _size = findSize(src.size()); + _mask = (_size-1); + Bucket[] buckets = new Bucket[_size]; + src.contents((key, value) -> { int index = key.hashCode() & _mask; - buckets[index] = new Bucket(buckets[index], key, entry.getValue()); - } + buckets[index] = new Bucket(buckets[index], key, value); + }); _buckets = buckets; } - + private final static int findSize(int size) { // For small enough results (64 or less), we'll require <= 50% fill rate; otherwise 80% @@ -51,14 +54,15 @@ private final static int findSize(int size) /** * Factory method for constructing an instance. */ - public static ReadOnlyClassToSerializerMap from(HashMap> src) { - return new ReadOnlyClassToSerializerMap(src); + public static ReadOnlyClassToSerializerMap from(SerializerCache shared, + SimpleLookupCache> src) { + return new ReadOnlyClassToSerializerMap(shared, src); } /* - /********************************************************** + /********************************************************************** /* Public API - /********************************************************** + /********************************************************************** */ public int size() { return _size; } @@ -66,75 +70,71 @@ public static ReadOnlyClassToSerializerMap from(HashMap typedValueSerializer(JavaType type) { Bucket bucket = _buckets[TypeKey.typedHash(type) & _mask]; - if (bucket == null) { - return null; - } - if (bucket.matchesTyped(type)) { - return bucket.value; - } - while ((bucket = bucket.next) != null) { + if (bucket != null) { if (bucket.matchesTyped(type)) { return bucket.value; } + while ((bucket = bucket.next) != null) { + if (bucket.matchesTyped(type)) { + return bucket.value; + } + } } - return null; + return _sharedCache.typedValueSerializer(type); } - public JsonSerializer typedValueSerializer(Class type) + public JsonSerializer typedValueSerializer(Class rawType) { - Bucket bucket = _buckets[TypeKey.typedHash(type) & _mask]; - if (bucket == null) { - return null; - } - if (bucket.matchesTyped(type)) { - return bucket.value; - } - while ((bucket = bucket.next) != null) { - if (bucket.matchesTyped(type)) { + Bucket bucket = _buckets[TypeKey.typedHash(rawType) & _mask]; + if (bucket != null) { + if (bucket.matchesTyped(rawType)) { return bucket.value; } + while ((bucket = bucket.next) != null) { + if (bucket.matchesTyped(rawType)) { + return bucket.value; + } + } } - return null; + return _sharedCache.typedValueSerializer(rawType); } public JsonSerializer untypedValueSerializer(JavaType type) { Bucket bucket = _buckets[TypeKey.untypedHash(type) & _mask]; - if (bucket == null) { - return null; - } - if (bucket.matchesUntyped(type)) { - return bucket.value; - } - while ((bucket = bucket.next) != null) { + if (bucket != null) { if (bucket.matchesUntyped(type)) { return bucket.value; } + while ((bucket = bucket.next) != null) { + if (bucket.matchesUntyped(type)) { + return bucket.value; + } + } } - return null; + return _sharedCache.untypedValueSerializer(type); } - public JsonSerializer untypedValueSerializer(Class type) + public JsonSerializer untypedValueSerializer(Class rawType) { - Bucket bucket = _buckets[TypeKey.untypedHash(type) & _mask]; - if (bucket == null) { - return null; - } - if (bucket.matchesUntyped(type)) { - return bucket.value; - } - while ((bucket = bucket.next) != null) { - if (bucket.matchesUntyped(type)) { + Bucket bucket = _buckets[TypeKey.untypedHash(rawType) & _mask]; + if (bucket != null) { + if (bucket.matchesUntyped(rawType)) { return bucket.value; } + while ((bucket = bucket.next) != null) { + if (bucket.matchesUntyped(rawType)) { + return bucket.value; + } + } } - return null; + return _sharedCache.untypedValueSerializer(rawType); } /* - /********************************************************** + /********************************************************************** /* Helper classes - /********************************************************** + /********************************************************************** */ private final static class Bucket diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/SimpleBeanPropertyFilter.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/SimpleBeanPropertyFilter.java index df10cc2215..d00165664f 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/SimpleBeanPropertyFilter.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/SimpleBeanPropertyFilter.java @@ -4,7 +4,6 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; import com.fasterxml.jackson.databind.ser.*; @@ -17,9 +16,8 @@ * because it can provide default implementation for any methods that may * be added in {@link PropertyFilter} (as unfortunate as additions may be). */ -@SuppressWarnings("deprecation") public class SimpleBeanPropertyFilter - implements BeanPropertyFilter, PropertyFilter + implements PropertyFilter // sub-classes must also implement java.io.Serializable { /* @@ -30,29 +28,22 @@ public class SimpleBeanPropertyFilter protected SimpleBeanPropertyFilter() { } + @Override + public PropertyFilter snapshot() { + // 22-Feb-2018, tatu: Since instances are immutable (no API to change) + // we should be able to avoid actual copying, return instances as they are + return this; + } + /** * Convenience factory method that will return a "no-op" filter that will * simply just serialize all properties that are given, and filter out * nothing. - * - * @since 2.6 */ public static SimpleBeanPropertyFilter serializeAll() { return SerializeExceptFilter.INCLUDE_ALL; } - /** - * Factory method that was accidentally added in 2.5 with arguments; basically - * works just as an alias of {@link #filterOutAllExcept(Set)} which is not - * very useful. Instead, see {@link #serializeAll()} for intended signature. - * - * @deprecated Since 2.6; to be removed from 2.7 - */ - @Deprecated - public static SimpleBeanPropertyFilter serializeAll(Set properties) { - return new FilterExceptFilter(properties); - } - /** * Factory method to construct filter that filters out all properties except * ones includes in set @@ -77,47 +68,6 @@ public static SimpleBeanPropertyFilter serializeAllExcept(String... propertyArra return new SerializeExceptFilter(properties); } - /** - * Helper method to ease transition from {@link BeanPropertyWriter} into - * {@link PropertyWriter} - * - * @since 2.3 - */ - public static PropertyFilter from(final BeanPropertyFilter src) - { - return new PropertyFilter() { - @Override - public void serializeAsField(Object pojo, JsonGenerator jgen, - SerializerProvider prov, PropertyWriter writer) - throws Exception { - src.serializeAsField(pojo, jgen, prov, (BeanPropertyWriter) writer); - } - - @Override - public void depositSchemaProperty(PropertyWriter writer, - ObjectNode propertiesNode, SerializerProvider provider) - throws JsonMappingException { - src.depositSchemaProperty((BeanPropertyWriter) writer, propertiesNode, provider); - } - - @Override - public void depositSchemaProperty(PropertyWriter writer, - JsonObjectFormatVisitor objectVisitor, - SerializerProvider provider) throws JsonMappingException { - src.depositSchemaProperty((BeanPropertyWriter) writer, objectVisitor, provider); - } - - @Override - public void serializeAsElement(Object elementValue, - JsonGenerator jgen, SerializerProvider prov, - PropertyWriter writer) throws Exception { - // not needed; element filtering only available through new interfaces - throw new UnsupportedOperationException(); - } - - }; - } - /* /********************************************************** /* Methods for sub-classes @@ -135,8 +85,6 @@ protected boolean include(BeanPropertyWriter writer) { /** * Method called to determine whether property will be included * (if 'true' returned) or filtered out (if 'false' returned) - * - * @since 2.3 */ protected boolean include(PropertyWriter writer) { return true; @@ -146,52 +94,10 @@ protected boolean include(PropertyWriter writer) { * Method that defines what to do with container elements * (values contained in an array or {@link java.util.Collection}: * default implementation simply writes them out. - * - * @since 2.3 */ protected boolean includeElement(Object elementValue) { return true; } - - /* - /********************************************************** - /* BeanPropertyFilter (deprecated) implementation - /********************************************************** - */ - - @Deprecated - @Override - public void serializeAsField(Object bean, JsonGenerator jgen, - SerializerProvider provider, BeanPropertyWriter writer) throws Exception - { - if (include(writer)) { - writer.serializeAsField(bean, jgen, provider); - } else if (!jgen.canOmitFields()) { // since 2.3 - writer.serializeAsOmittedField(bean, jgen, provider); - } - } - - @Deprecated - @Override - public void depositSchemaProperty(BeanPropertyWriter writer, - ObjectNode propertiesNode, SerializerProvider provider) - throws JsonMappingException - { - if (include(writer)) { - writer.depositSchemaProperty(propertiesNode, provider); - } - } - - @Deprecated - @Override - public void depositSchemaProperty(BeanPropertyWriter writer, - JsonObjectFormatVisitor objectVisitor, SerializerProvider provider) - throws JsonMappingException - { - if (include(writer)) { - writer.depositSchemaProperty(objectVisitor, provider); - } - } /* /********************************************************** @@ -220,17 +126,6 @@ public void serializeAsElement(Object elementValue, JsonGenerator jgen, Serializ writer.serializeAsElement(elementValue, jgen, provider); } } - - @Deprecated - @Override - public void depositSchemaProperty(PropertyWriter writer, - ObjectNode propertiesNode, SerializerProvider provider) - throws JsonMappingException - { - if (include(writer)) { - writer.depositSchemaProperty(propertiesNode, provider); - } - } @Override public void depositSchemaProperty(PropertyWriter writer, diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/SimpleFilterProvider.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/SimpleFilterProvider.java index 52f6703fbd..08dbfec796 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/SimpleFilterProvider.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/SimpleFilterProvider.java @@ -2,21 +2,19 @@ import java.util.*; +import com.fasterxml.jackson.core.util.Snapshottable; + import com.fasterxml.jackson.databind.ser.*; /** * Simple {@link FilterProvider} implementation that just stores * direct id-to-filter mapping. - *

    - * Note that version 2.3 was a partial rewrite, now that - * {@link PropertyFilter} is set to replace BeanPropertyFilter. */ public class SimpleFilterProvider extends FilterProvider - implements java.io.Serializable // since 2.1 + implements java.io.Serializable { - // for 2.5+ - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 3L; /** * Mappings from ids to filters. @@ -43,65 +41,34 @@ public class SimpleFilterProvider /* Life-cycle: constructing, configuring /********************************************************** */ - + public SimpleFilterProvider() { - this(new HashMap()); + this(new HashMap<>()); } /** * @param mapping Mapping from id to filter; used as is if if possible */ - @SuppressWarnings("unchecked") - public SimpleFilterProvider(Map mapping) - { - /* 16-Oct-2013, tatu: Since we can now be getting both new and old - * obsolete filters (PropertyFilter vs BeanPropertyFilter), need - * to verify contents. - */ - for (Object ob : mapping.values()) { - if (!(ob instanceof PropertyFilter)) { - _filtersById = _convert(mapping); - return; - } - } - _filtersById = (Map) mapping; - } - - @SuppressWarnings("deprecation") - private final static Map _convert(Map filters) + public SimpleFilterProvider(Map mapping) { - HashMap result = new HashMap(); - for (Map.Entry entry : filters.entrySet()) { - Object f = entry.getValue(); - if (f instanceof PropertyFilter) { - result.put(entry.getKey(), (PropertyFilter) f); - } else if (f instanceof BeanPropertyFilter) { - result.put(entry.getKey(), _convert((BeanPropertyFilter) f)); - } else { - throw new IllegalArgumentException("Unrecognized filter type ("+f.getClass().getName()+")"); - } + _filtersById = mapping; + } + + protected SimpleFilterProvider(SimpleFilterProvider src) { + _defaultFilter = Snapshottable.takeSnapshot(src._defaultFilter); + _cfgFailOnUnknownId = src._cfgFailOnUnknownId; + Map f = src._filtersById; + if (f.isEmpty()) { + _filtersById = new HashMap<>(); + } else { + _filtersById = new HashMap<>(f.size()); + f.forEach((k,v) -> _filtersById.put(k, v.snapshot())); } - return result; } - @SuppressWarnings("deprecation") - private final static PropertyFilter _convert(BeanPropertyFilter f) { - return SimpleBeanPropertyFilter.from((BeanPropertyFilter) f); - } - - /** - * Method for defining filter to return for "unknown" filters; cases - * where there is no mapping from given id to an explicit filter. - * - * @param f Filter to return when no filter is found for given id - * - * @deprecated Since 2.3 should use {@link PropertyFilter} instead of {@link BeanPropertyFilter} - */ - @Deprecated - public SimpleFilterProvider setDefaultFilter(BeanPropertyFilter f) - { - _defaultFilter = SimpleBeanPropertyFilter.from(f); - return this; + @Override + public SimpleFilterProvider snapshot() { + return new SimpleFilterProvider(this); } public SimpleFilterProvider setDefaultFilter(PropertyFilter f) @@ -132,15 +99,6 @@ public boolean willFailOnUnknownId() { return _cfgFailOnUnknownId; } - /** - * @deprecated since 2.3 - */ - @Deprecated - public SimpleFilterProvider addFilter(String id, BeanPropertyFilter filter) { - _filtersById.put(id, _convert(filter)); - return this; - } - public SimpleFilterProvider addFilter(String id, PropertyFilter filter) { _filtersById.put(id, filter); return this; @@ -159,18 +117,11 @@ public PropertyFilter removeFilter(String id) { } /* - /********************************************************** + /********************************************************************** /* Public lookup API - /********************************************************** + /********************************************************************** */ - @Deprecated // since 2.3 - @Override - public BeanPropertyFilter findFilter(Object filterId) - { - throw new UnsupportedOperationException("Access to deprecated filters not supported"); - } - @Override public PropertyFilter findPropertyFilter(Object filterId, Object valueToFilter) { 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 1011babe52..9ebf87edc9 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 @@ -1,7 +1,6 @@ package com.fasterxml.jackson.databind.ser.impl; import java.io.IOException; -import java.lang.reflect.Type; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.core.JsonGenerator; @@ -12,7 +11,6 @@ import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; import com.fasterxml.jackson.databind.ser.ContainerSerializer; -import com.fasterxml.jackson.databind.ser.ContextualSerializer; import com.fasterxml.jackson.databind.ser.std.ArraySerializerBase; import com.fasterxml.jackson.databind.type.TypeFactory; @@ -23,7 +21,6 @@ @SuppressWarnings("serial") public class StringArraySerializer extends ArraySerializerBase - implements ContextualSerializer { /* Note: not clean in general, but we are betting against * anyone re-defining properties of String.class here... @@ -40,9 +37,9 @@ public class StringArraySerializer protected final JsonSerializer _elementSerializer; /* - /********************************************************** + /********************************************************************** /* Life-cycle - /********************************************************** + /********************************************************************** */ protected StringArraySerializer() { @@ -72,9 +69,9 @@ public ContainerSerializer _withValueTypeSerializer(TypeSerializer vts) { } /* - /********************************************************** + /********************************************************************** /* Post-processing - /********************************************************** + /********************************************************************** */ @Override @@ -82,10 +79,9 @@ public JsonSerializer createContextual(SerializerProvider provider, BeanProperty property) throws JsonMappingException { - /* 29-Sep-2012, tatu: Actually, we need to do much more contextual - * checking here since we finally know for sure the property, - * and it may have overrides - */ + // 29-Sep-2012, tatu: Actually, we need to do much more contextual + // checking here since we finally know for sure the property, + // and it may have overrides JsonSerializer ser = null; // First: if we have a property, may have property-annotation overrides @@ -93,10 +89,8 @@ public JsonSerializer createContextual(SerializerProvider provider, final AnnotationIntrospector ai = provider.getAnnotationIntrospector(); AnnotatedMember m = property.getMember(); if (m != null) { - Object serDef = ai.findContentSerializer(m); - if (serDef != null) { - ser = provider.serializerInstance(m, serDef); - } + ser = provider.serializerInstance(m, + ai.findContentSerializer(provider.getConfig(), m)); } } // but since formats have both property overrides and global per-type defaults, @@ -109,7 +103,7 @@ public JsonSerializer createContextual(SerializerProvider provider, // May have a content converter ser = findContextualConvertingSerializer(provider, property, ser); if (ser == null) { - ser = provider.findValueSerializer(String.class, property); + ser = provider.findSecondaryPropertySerializer(String.class, property); } // Optimization: default serializer just writes String, so we can avoid a call: if (isDefaultSerializer(ser)) { @@ -123,9 +117,9 @@ public JsonSerializer createContextual(SerializerProvider provider, } /* - /********************************************************** + /********************************************************************** /* Simple accessors - /********************************************************** + /********************************************************************** */ @Override @@ -147,11 +141,11 @@ public boolean isEmpty(SerializerProvider prov, String[] value) { public boolean hasSingleElement(String[] value) { return (value.length == 1); } - + /* - /********************************************************** + /********************************************************************** /* Actual serialization - /********************************************************** + /********************************************************************** */ @Override @@ -167,7 +161,7 @@ public final void serialize(String[] value, JsonGenerator gen, SerializerProvide return; } } - gen.writeStartArray(len); + gen.writeStartArray(value, len); serializeContents(value, gen, provider); gen.writeEndArray(); } @@ -200,18 +194,13 @@ private void serializeContentsSlow(String[] value, JsonGenerator gen, Serializer for (int i = 0, len = value.length; i < len; ++i) { String str = value[i]; if (str == null) { - provider.defaultSerializeNull(gen); + provider.defaultSerializeNullValue(gen); } else { ser.serialize(value[i], gen, provider); } } } - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) { - return createSchemaNode("array", true).set("items", createSchemaNode("string")); - } - @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { 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 b936eac2a6..a2ecccbfc6 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 @@ -77,7 +77,7 @@ public void serialize(Collection value, JsonGenerator g, return; } } - g.writeStartArray(len); + g.writeStartArray(value, len); serializeContents(value, g, provider); g.writeEndArray(); } @@ -103,7 +103,7 @@ private final void serializeContents(Collection value, JsonGenerator g, try { for (String str : value) { if (str == null) { - provider.defaultSerializeNull(g); + provider.defaultSerializeNullValue(g); } else { g.writeString(str); } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/TypeWrappedSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/TypeWrappedSerializer.java index 62ad61d6c4..5186dfd5a9 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/TypeWrappedSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/TypeWrappedSerializer.java @@ -5,7 +5,6 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; -import com.fasterxml.jackson.databind.ser.ContextualSerializer; /** * Simple serializer that will call configured type serializer, passing @@ -14,7 +13,6 @@ */ public final class TypeWrappedSerializer extends JsonSerializer - implements ContextualSerializer // since 2.9 { final protected TypeSerializer _typeSerializer; final protected JsonSerializer _serializer; @@ -45,18 +43,18 @@ public void serializeWithType(Object value, JsonGenerator g, SerializerProvider public Class handledType() { return Object.class; } /* - /********************************************************** + /********************************************************************** /* ContextualDeserializer - /********************************************************** + /********************************************************************** */ - @Override // since 2.9 + @Override public JsonSerializer createContextual(SerializerProvider provider, BeanProperty property) throws JsonMappingException { // 13-Mar-2017, tatu: Should we call `TypeSerializer.forProperty()`? JsonSerializer ser = _serializer; - if (ser instanceof ContextualSerializer) { + if (ser != null) { ser = provider.handleSecondaryContextualization(ser, property); } if (ser == _serializer) { @@ -66,9 +64,9 @@ public JsonSerializer createContextual(SerializerProvider provider, BeanPrope } /* - /********************************************************** + /********************************************************************** /* Extended API for other core classes - /********************************************************** + /********************************************************************** */ public JsonSerializer valueSerializer() { diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnknownSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnknownSerializer.java index 70b53e0634..24e02dfe25 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnknownSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnknownSerializer.java @@ -1,7 +1,6 @@ package com.fasterxml.jackson.databind.ser.impl; import java.io.IOException; -import java.lang.reflect.Type; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.type.WritableTypeId; @@ -18,11 +17,8 @@ public UnknownSerializer() { super(Object.class); } - /** - * @since 2.6 - */ public UnknownSerializer(Class cls) { - super(cls, false); + super(cls); } @Override @@ -54,11 +50,6 @@ public boolean isEmpty(SerializerProvider provider, Object value) { return true; } - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException { - return null; - } - @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnrolledBeanAsArraySerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnrolledBeanAsArraySerializer.java new file mode 100644 index 0000000000..87617ceb15 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnrolledBeanAsArraySerializer.java @@ -0,0 +1,233 @@ +package com.fasterxml.jackson.databind.ser.impl; + +import java.io.IOException; +import java.util.Set; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.core.type.WritableTypeId; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; +import com.fasterxml.jackson.databind.ser.std.BeanSerializerBase; +import com.fasterxml.jackson.databind.util.NameTransformer; + +/** + * Specialization of {@link BeanAsArraySerializer}, optimized for handling + * small number of properties where calls to property handlers can be + * "unrolled" by eliminated looping. This can help optimize execution + * significantly for some backends. + */ +public class UnrolledBeanAsArraySerializer + extends BeanSerializerBase +{ + private static final long serialVersionUID = 3L; + + /** + * Serializer that would produce JSON Object version; used in + * cases where array output cannot be used. + */ + protected final BeanSerializerBase _defaultSerializer; + + public static final int MAX_PROPS = 6; + + protected final int _propCount; + + // // // We store separate references in form more easily accessed + // // // from switch statement + + protected BeanPropertyWriter _prop1; + protected BeanPropertyWriter _prop2; + protected BeanPropertyWriter _prop3; + protected BeanPropertyWriter _prop4; + protected BeanPropertyWriter _prop5; + protected BeanPropertyWriter _prop6; + + /* + /********************************************************** + /* Life-cycle: constructors + /********************************************************** + */ + + public UnrolledBeanAsArraySerializer(BeanSerializerBase src) { + super(src, (ObjectIdWriter) null); + _defaultSerializer = src; + _propCount = _props.length; + _calcUnrolled(); + } + + protected UnrolledBeanAsArraySerializer(BeanSerializerBase src, Set toIgnore) { + super(src, toIgnore); + _defaultSerializer = src; + _propCount = _props.length; + _calcUnrolled(); + } + + private void _calcUnrolled() { + BeanPropertyWriter[] oProps = new BeanPropertyWriter[6]; + int offset = 6 - _propCount; + System.arraycopy(_props, 0, oProps, offset, _propCount); + + _prop1 = oProps[0]; + _prop2 = oProps[1]; + _prop3 = oProps[2]; + _prop4 = oProps[3]; + _prop5 = oProps[4]; + _prop6 = oProps[5]; + } + + /** + * Factory method that will construct optimized instance if all the constraints + * are obeyed; or, if not, return `null` to indicate that instance can not be + * created. + */ + public static UnrolledBeanAsArraySerializer tryConstruct(BeanSerializerBase src) + { + if ((src.propertyCount() > MAX_PROPS) + || (src.getFilterId() != null) + || src.hasViewProperties()) { + return null; + } + return new UnrolledBeanAsArraySerializer(src); + } + + /* + /********************************************************** + /* Life-cycle: factory methods, fluent factories + /********************************************************** + */ + + @Override + public JsonSerializer unwrappingSerializer(NameTransformer transformer) { + // If this gets called, we will just need delegate to the default + // serializer, to "undo" as-array serialization + return _defaultSerializer.unwrappingSerializer(transformer); + } + + @Override + public boolean isUnwrappingSerializer() { + return false; + } + + @Override + public BeanSerializerBase withObjectIdWriter(ObjectIdWriter objectIdWriter) { + // can't handle Object Ids, for now, so: + return _defaultSerializer.withObjectIdWriter(objectIdWriter); + } + + @Override + public BeanSerializerBase withFilterId(Object filterId) { + // Revert to Vanilla variant, if so: + return new BeanAsArraySerializer(_defaultSerializer, + _objectIdWriter, filterId); + } + + @Override + protected UnrolledBeanAsArraySerializer withIgnorals(Set toIgnore) { + return new UnrolledBeanAsArraySerializer(this, toIgnore); + } + + @Override + protected BeanSerializerBase asArraySerializer() { + return this; // already is one... + } + + @Override + public void resolve(SerializerProvider provider) throws JsonMappingException + { + super.resolve(provider); + _calcUnrolled(); + } + + /* + /********************************************************** + /* JsonSerializer implementation that differs between impls + /********************************************************** + */ + + // Re-defined from base class, due to differing prefixes + @Override + public void serializeWithType(Object bean, JsonGenerator gen, + SerializerProvider provider, TypeSerializer typeSer) + throws IOException + { + WritableTypeId typeIdDef = _typeIdDef(typeSer, bean, JsonToken.START_ARRAY); + typeSer.writeTypePrefix(gen, typeIdDef); + // NOTE: instances NOT constructed if view-processing available + serializeNonFiltered(bean, gen, provider); + typeSer.writeTypeSuffix(gen, typeIdDef); + } + + /** + * Main serialization method that will delegate actual output to + * configured + * {@link BeanPropertyWriter} instances. + */ + @Override + public final void serialize(Object bean, JsonGenerator gen, SerializerProvider provider) + throws IOException + { + if (provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED) + && hasSingleElement(provider)) { + serializeNonFiltered(bean, gen, provider); + return; + } + // note: it is assumed here that limitations (type id, object id, + // any getter, filtering) have already been checked; so code here + // is trivial. + + gen.writeStartArray(bean, _props.length); + // NOTE: instances NOT constructed if view-processing available + serializeNonFiltered(bean, gen, provider); + gen.writeEndArray(); + } + + /* + /********************************************************** + /* Field serialization methods + /********************************************************** + */ + + private boolean hasSingleElement(SerializerProvider provider) { + return _props.length == 1; + } + + protected final void serializeNonFiltered(Object bean, JsonGenerator gen, + SerializerProvider provider) + throws IOException + { + BeanPropertyWriter prop = null; + try { + switch (_propCount) { + default: + //case 6: + prop = _prop1; + prop.serializeAsElement(bean, gen, provider); + // fall through + case 5: + prop = _prop2; + prop.serializeAsElement(bean, gen, provider); + case 4: + prop = _prop3; + prop.serializeAsElement(bean, gen, provider); + case 3: + prop = _prop4; + prop.serializeAsElement(bean, gen, provider); + case 2: + prop = _prop5; + prop.serializeAsElement(bean, gen, provider); + case 1: + prop = _prop6; + prop.serializeAsElement(bean, gen, provider); + case 0: + } + // NOTE: any getters cannot be supported either + } catch (Exception e) { + wrapAndThrow(provider, e, bean, prop.getName()); + } catch (StackOverflowError e) { + JsonMappingException mapE = JsonMappingException.from(gen, "Infinite recursion (StackOverflowError)", e); + mapE.prependPath(new JsonMappingException.Reference(bean, prop.getName())); + throw mapE; + } + } +} diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnwrappingBeanPropertyWriter.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnwrappingBeanPropertyWriter.java index c6498b02c0..61ff226608 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnwrappingBeanPropertyWriter.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnwrappingBeanPropertyWriter.java @@ -64,8 +64,6 @@ public UnwrappingBeanPropertyWriter rename(NameTransformer transformer) /** * Overridable factory method used by sub-classes - * - * @since 2.6.0 */ protected UnwrappingBeanPropertyWriter _new(NameTransformer transformer, SerializedString newName) { @@ -158,7 +156,7 @@ public void depositSchemaProperty(final JsonObjectFormatVisitor visitor, SerializerProvider provider) throws JsonMappingException { JsonSerializer ser = provider - .findValueSerializer(this.getType(), this) + .findPrimaryPropertySerializer(getType(), this) .unwrappingSerializer(_nameTransformer); if (ser.isUnwrappingSerializer()) { @@ -170,7 +168,7 @@ public JsonObjectFormatVisitor expectObjectFormat(JavaType type) throws JsonMappingException { return visitor; } - }, this.getType()); + }, getType()); } else { super.depositSchemaProperty(visitor, provider); } @@ -208,9 +206,9 @@ protected JsonSerializer _findAndAddDynamic(PropertySerializerMap map, JsonSerializer serializer; if (_nonTrivialBaseType != null) { JavaType subtype = provider.constructSpecializedType(_nonTrivialBaseType, type); - serializer = provider.findValueSerializer(subtype, this); + serializer = provider.findPrimaryPropertySerializer(subtype, this); } else { - serializer = provider.findValueSerializer(type, this); + serializer = provider.findPrimaryPropertySerializer(type, this); } NameTransformer t = _nameTransformer; if (serializer.isUnwrappingSerializer() diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnwrappingBeanSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnwrappingBeanSerializer.java index 3b91926a4f..bb0244b1c3 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnwrappingBeanSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnwrappingBeanSerializer.java @@ -109,18 +109,25 @@ protected BeanSerializerBase asArraySerializer() { @Override public final void serialize(Object bean, JsonGenerator gen, SerializerProvider provider) throws IOException { - gen.setCurrentValue(bean); // [databind#631] if (_objectIdWriter != null) { _serializeWithObjectId(bean, gen, provider, false); return; } + // Because we do not write start-object need to call this explicitly: + // (although... is that a problem, overwriting it now?) + gen.setCurrentValue(bean); // [databind#631] if (_propertyFilterId != null) { - serializeFieldsFiltered(bean, gen, provider); - } else { - serializeFields(bean, gen, provider); + _serializeFieldsFiltered(bean, gen, provider, _propertyFilterId); + return; } + BeanPropertyWriter[] fProps = _filteredProps; + if ((fProps != null) && (provider.getActiveView() != null)) { + _serializeFieldsMaybeView(bean, gen, provider, fProps); + return; + } + _serializeFieldsNoView(bean, gen, provider, _props); } - + @Override public void serializeWithType(Object bean, JsonGenerator gen, SerializerProvider provider, TypeSerializer typeSer) throws IOException @@ -129,16 +136,22 @@ public void serializeWithType(Object bean, JsonGenerator gen, SerializerProvider provider.reportBadDefinition(handledType(), "Unwrapped property requires use of type information: cannot serialize without disabling `SerializationFeature.FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS`"); } - gen.setCurrentValue(bean); // [databind#631] if (_objectIdWriter != null) { _serializeWithObjectId(bean, gen, provider, typeSer); return; } + // Because we do not write start-object need to call this explicitly: + gen.setCurrentValue(bean); if (_propertyFilterId != null) { - serializeFieldsFiltered(bean, gen, provider); - } else { - serializeFields(bean, gen, provider); + _serializeFieldsFiltered(bean, gen, provider, _propertyFilterId); + return; + } + BeanPropertyWriter[] fProps = _filteredProps; + if ((fProps != null) && (provider.getActiveView() != null)) { + _serializeFieldsMaybeView(bean, gen, provider, fProps); + return; } + _serializeFieldsNoView(bean, gen, provider, _props); } /* 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 69af378709..808816df8a 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 @@ -10,80 +10,39 @@ import com.fasterxml.jackson.databind.ser.*; /** - * Intermediate base class for serializers used for various - * Java arrays. + * Intermediate base class for serializers used for various Java arrays. * * @param Type of arrays serializer handles */ @SuppressWarnings("serial") public abstract class ArraySerializerBase extends ContainerSerializer - implements ContextualSerializer // for 'unwrapSingleElemArray' { - protected final BeanProperty _property; - /** * Setting for specific local override for "unwrap single element arrays": * true for enable unwrapping, false for preventing it, `null` for using * global configuration. - * - * @since 2.6 */ protected final Boolean _unwrapSingle; protected ArraySerializerBase(Class cls) { super(cls); - _property = null; - _unwrapSingle = null; - } - - /** - * Use either variant that just takes type (non-contextual), or, - * copy constructor that allows passing of property. - * - * @deprecated Since 2.6 - */ - @Deprecated - protected ArraySerializerBase(Class cls, BeanProperty property) - { - super(cls); - _property = property; _unwrapSingle = null; } - protected ArraySerializerBase(ArraySerializerBase src) - { - super(src._handledType, false); - _property = src._property; + protected ArraySerializerBase(ArraySerializerBase src) { + super(src); _unwrapSingle = src._unwrapSingle; } - /** - * @since 2.6 - */ protected ArraySerializerBase(ArraySerializerBase src, BeanProperty property, Boolean unwrapSingle) { - super(src._handledType, false); - _property = property; + super(src, property); _unwrapSingle = unwrapSingle; } - /** - * @deprecated Since 2.6 - */ - @Deprecated - protected ArraySerializerBase(ArraySerializerBase src, BeanProperty property) - { - super(src._handledType, false); - _property = property; - _unwrapSingle = src._unwrapSingle; - } - - /** - * @since 2.6 - */ public abstract JsonSerializer _withResolved(BeanProperty prop, Boolean unwrapSingle); @@ -105,25 +64,6 @@ public JsonSerializer createContextual(SerializerProvider serializers, } return this; } - - // 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 void serialize(T value, JsonGenerator gen, SerializerProvider provider) throws IOException - { - if (_shouldUnwrapSingle(provider)) { - if (hasSingleElement(value)) { - serializeContents(value, gen, provider); - return; - } - } - gen.setCurrentValue(value); - gen.writeStartArray(); - // [databind#631]: Assign current value, to be accessible by custom serializers - serializeContents(value, gen, provider); - gen.writeEndArray(); - } @Override public final void serializeWithType(T value, JsonGenerator g, SerializerProvider provider, @@ -141,9 +81,6 @@ public final void serializeWithType(T value, JsonGenerator g, SerializerProvider protected abstract void serializeContents(T value, JsonGenerator jgen, SerializerProvider provider) throws IOException; - /** - * @since 2.9 - */ protected final boolean _shouldUnwrapSingle(SerializerProvider provider) { if (_unwrapSingle == null) { return provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED); 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 87647ba974..967394c5f1 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.Type; import com.fasterxml.jackson.annotation.JsonFormat; @@ -10,12 +9,8 @@ import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.introspect.AnnotatedMember; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; -import com.fasterxml.jackson.databind.jsonschema.SchemaAware; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; -import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.ser.ContainerSerializer; -import com.fasterxml.jackson.databind.ser.ContextualSerializer; -import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap; /** * Base class for serializers that will output contents as JSON @@ -25,23 +20,15 @@ @SuppressWarnings("serial") public abstract class AsArraySerializerBase extends ContainerSerializer - implements ContextualSerializer { protected final JavaType _elementType; - /** - * Collection-valued property being serialized with this instance - */ - protected final BeanProperty _property; - protected final boolean _staticTyping; /** * Setting for specific local override for "unwrap single element arrays": * true for enable unwrapping, false for preventing it, `null` for using * global configuration. - * - * @since 2.6 */ protected final Boolean _unwrapSingle; @@ -55,55 +42,26 @@ public abstract class AsArraySerializerBase */ protected final JsonSerializer _elementSerializer; - /** - * If element type cannot be statically determined, mapping from - * runtime type to serializer is handled using this object - */ - protected PropertySerializerMap _dynamicSerializers; - /* - /********************************************************** + /********************************************************************** /* Life-cycle - /********************************************************** + /********************************************************************** */ /** * Non-contextual, "blueprint" constructor typically called when the first * instance is created, without knowledge of property it was used via. - * - * @since 2.6 */ + @SuppressWarnings("unchecked") protected AsArraySerializerBase(Class cls, JavaType et, boolean staticTyping, - TypeSerializer vts, JsonSerializer elementSerializer) - { - super(cls, false); - _elementType = et; - // static if explicitly requested, or if element type is final - _staticTyping = staticTyping || (et != null && et.isFinal()); - _valueTypeSerializer = vts; - _property = null; - _elementSerializer = elementSerializer; - _dynamicSerializers = PropertySerializerMap.emptyForProperties(); - _unwrapSingle = null; - } - - /** - * @deprecated Since 2.6 Use variants that either take 'src', or do NOT pass - * BeanProperty - */ - @Deprecated - protected AsArraySerializerBase(Class cls, JavaType et, boolean staticTyping, - TypeSerializer vts, BeanProperty property, JsonSerializer elementSerializer) + TypeSerializer vts, JsonSerializer elementSerializer) { - // typing with generics is messy... have to resort to this: - super(cls, false); + super(cls); _elementType = et; // static if explicitly requested, or if element type is final _staticTyping = staticTyping || (et != null && et.isFinal()); _valueTypeSerializer = vts; - _property = property; - _elementSerializer = elementSerializer; - _dynamicSerializers = PropertySerializerMap.emptyForProperties(); + _elementSerializer = (JsonSerializer) elementSerializer; _unwrapSingle = null; } @@ -112,47 +70,22 @@ protected AsArraySerializerBase(AsArraySerializerBase src, BeanProperty property, TypeSerializer vts, JsonSerializer elementSerializer, Boolean unwrapSingle) { - super(src); + super(src, property); _elementType = src._elementType; _staticTyping = src._staticTyping; _valueTypeSerializer = vts; - _property = property; _elementSerializer = (JsonSerializer) elementSerializer; - // [databind#2181]: may not be safe to reuse, start from empty - _dynamicSerializers = PropertySerializerMap.emptyForProperties(); _unwrapSingle = unwrapSingle; } - /** - * @deprecated since 2.6: use the overloaded method that takes 'unwrapSingle' - */ - @Deprecated - protected AsArraySerializerBase(AsArraySerializerBase src, - BeanProperty property, TypeSerializer vts, JsonSerializer elementSerializer) - { - this(src, property, vts, elementSerializer, src._unwrapSingle); - } - - /** - * @deprecated since 2.6: use the overloaded method that takes 'unwrapSingle' - */ - @Deprecated - public final AsArraySerializerBase withResolved(BeanProperty property, - TypeSerializer vts, JsonSerializer elementSerializer) { - return withResolved(property, vts, elementSerializer, _unwrapSingle); - } - - /** - * @since 2.6 - */ public abstract AsArraySerializerBase withResolved(BeanProperty property, TypeSerializer vts, JsonSerializer elementSerializer, Boolean unwrapSingle); /* - /********************************************************** + /********************************************************************** /* Post-processing - /********************************************************** + /********************************************************************** */ /** @@ -162,29 +95,27 @@ public abstract AsArraySerializerBase withResolved(BeanProperty property, * known statically. */ @Override - public JsonSerializer createContextual(SerializerProvider serializers, + public JsonSerializer createContextual(SerializerProvider ctxt, BeanProperty property) throws JsonMappingException { TypeSerializer typeSer = _valueTypeSerializer; if (typeSer != null) { - typeSer = typeSer.forProperty(property); + typeSer = typeSer.forProperty(ctxt, property); } JsonSerializer ser = null; Boolean unwrapSingle = null; // First: if we have a property, may have property-annotation overrides if (property != null) { - final AnnotationIntrospector intr = serializers.getAnnotationIntrospector(); + final AnnotationIntrospector intr = ctxt.getAnnotationIntrospector(); AnnotatedMember m = property.getMember(); if (m != null) { - Object serDef = intr.findContentSerializer(m); - if (serDef != null) { - ser = serializers.serializerInstance(m, serDef); - } + ser = ctxt.serializerInstance(m, + intr.findContentSerializer(ctxt.getConfig(), m)); } } - JsonFormat.Value format = findFormatOverrides(serializers, property, handledType()); + JsonFormat.Value format = findFormatOverrides(ctxt, property, handledType()); if (format != null) { unwrapSingle = format.getFeature(JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED); } @@ -192,13 +123,13 @@ public JsonSerializer createContextual(SerializerProvider serializers, ser = _elementSerializer; } // 18-Feb-2013, tatu: May have a content converter: - ser = findContextualConvertingSerializer(serializers, property, ser); + ser = findContextualConvertingSerializer(ctxt, property, ser); if (ser == null) { // 30-Sep-2012, tatu: One more thing -- if explicit content type is annotated, // we can consider it a static case as well. if (_elementType != null) { if (_staticTyping && !_elementType.isJavaLangObject()) { - ser = serializers.findValueSerializer(_elementType, property); + ser = ctxt.findSecondaryPropertySerializer(_elementType, property); } } } @@ -212,9 +143,9 @@ public JsonSerializer createContextual(SerializerProvider serializers, } /* - /********************************************************** + /********************************************************************** /* Accessors - /********************************************************** + /********************************************************************** */ @Override @@ -228,14 +159,17 @@ public JsonSerializer getContentSerializer() { } /* - /********************************************************** + /********************************************************************** /* Serialization - /********************************************************** + /********************************************************************** */ - // NOTE: as of 2.5, sub-classes SHOULD override (in 2.4 and before, was final), + // 16-Apr-2018, tatu: Sample code, but sub-classes need to implement (for more + // efficient "is-single-unwrapped" check) + // 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 void serialize(T value, JsonGenerator gen, SerializerProvider provider) throws IOException { @@ -244,12 +178,13 @@ && hasSingleElement(value)) { serializeContents(value, gen, provider); return; } - gen.writeStartArray(); + gen.writeStartArray(value); // [databind#631]: Assign current value, to be accessible by custom serializers gen.setCurrentValue(value); serializeContents(value, gen, provider); gen.writeEndArray(); } + */ @Override public void serializeWithType(T value, JsonGenerator g, SerializerProvider provider, @@ -266,25 +201,6 @@ public void serializeWithType(T value, JsonGenerator g, SerializerProvider provi protected abstract void serializeContents(T value, JsonGenerator gen, SerializerProvider provider) throws IOException; - @SuppressWarnings("deprecation") - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) - throws JsonMappingException - { - ObjectNode o = createSchemaNode("array", true); - if (_elementSerializer != null) { - JsonNode schemaNode = null; - if (_elementSerializer instanceof SchemaAware) { - schemaNode = ((SchemaAware) _elementSerializer).getSchema(provider, null); - } - if (schemaNode == null) { - schemaNode = com.fasterxml.jackson.databind.jsonschema.JsonSchema.getDefaultSchemaNode(); - } - o.set("items", schemaNode); - } - return o; - } - @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException @@ -294,30 +210,9 @@ public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType t // 19-Oct-2016, tatu: Apparently we get null for untyped/raw `EnumSet`s... not 100% // sure what'd be the clean way but let's try this for now: if (_elementType != null) { - valueSer = visitor.getProvider().findValueSerializer(_elementType, _property); + valueSer = visitor.getProvider().findSecondaryPropertySerializer(_elementType, _property); } } visitArrayFormat(visitor, typeHint, valueSer, _elementType); } - - protected final JsonSerializer _findAndAddDynamic(PropertySerializerMap map, - Class type, SerializerProvider provider) throws JsonMappingException - { - PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSecondarySerializer(type, provider, _property); - // did we get a new map of serializers? If so, start using it - if (map != result.map) { - _dynamicSerializers = result.map; - } - return result.serializer; - } - - protected final JsonSerializer _findAndAddDynamic(PropertySerializerMap map, - JavaType type, SerializerProvider provider) throws JsonMappingException - { - PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSecondarySerializer(type, provider, _property); - if (map != result.map) { - _dynamicSerializers = result.map; - } - return result.serializer; - } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/AtomicReferenceSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/AtomicReferenceSerializer.java index d44d9af42b..53a05deab7 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/AtomicReferenceSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/AtomicReferenceSerializer.java @@ -13,9 +13,9 @@ public class AtomicReferenceSerializer private static final long serialVersionUID = 1L; /* - /********************************************************** + /********************************************************************** /* Constructors, factory methods - /********************************************************** + /********************************************************************** */ public AtomicReferenceSerializer(ReferenceType fullType, boolean staticTyping, @@ -52,9 +52,9 @@ public ReferenceTypeSerializer> withContentInclusion(Object s } /* - /********************************************************** + /********************************************************************** /* Abstract method impls - /********************************************************** + /********************************************************************** */ @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 a95d812038..aaa4fd95c8 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 @@ -1,28 +1,24 @@ package com.fasterxml.jackson.databind.ser.std; import java.io.IOException; -import java.lang.reflect.Type; import java.util.*; import com.fasterxml.jackson.annotation.*; + import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.type.WritableTypeId; + import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.introspect.AnnotatedMember; import com.fasterxml.jackson.databind.introspect.ObjectIdInfo; -import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitable; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; -import com.fasterxml.jackson.databind.jsonschema.JsonSerializableSchema; -import com.fasterxml.jackson.databind.jsonschema.SchemaAware; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; -import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.ser.*; import com.fasterxml.jackson.databind.ser.impl.MapEntrySerializer; import com.fasterxml.jackson.databind.ser.impl.ObjectIdWriter; import com.fasterxml.jackson.databind.ser.impl.PropertyBasedObjectIdGenerator; import com.fasterxml.jackson.databind.ser.impl.WritableObjectId; -import com.fasterxml.jackson.databind.util.ArrayBuilders; import com.fasterxml.jackson.databind.util.Converter; import com.fasterxml.jackson.databind.util.NameTransformer; @@ -35,22 +31,17 @@ @SuppressWarnings("serial") public abstract class BeanSerializerBase extends StdSerializer - implements ContextualSerializer, ResolvableSerializer, - JsonFormatVisitable, SchemaAware { protected final static PropertyName NAME_FOR_OBJECT_REF = new PropertyName("#object-ref"); final protected static BeanPropertyWriter[] NO_PROPS = new BeanPropertyWriter[0]; /* - /********************************************************** + /********************************************************************** /* Configuration - /********************************************************** + /********************************************************************** */ - /** - * @since 2.9 - */ final protected JavaType _beanType; /** @@ -94,9 +85,9 @@ public abstract class BeanSerializerBase final protected JsonFormat.Shape _serializationShape; /* - /********************************************************** + /********************************************************************** /* Life-cycle: constructors - /********************************************************** + /********************************************************************** */ /** @@ -124,11 +115,19 @@ protected BeanSerializerBase(JavaType type, BeanSerializerBuilder builder, _anyGetterWriter = builder.getAnyGetter(); _propertyFilterId = builder.getFilterId(); _objectIdWriter = builder.getObjectIdWriter(); - JsonFormat.Value format = builder.getBeanDescription().findExpectedFormat(null); - _serializationShape = (format == null) ? null : format.getShape(); + JsonFormat.Value format = builder.getBeanDescription().findExpectedFormat(type.getRawClass()); + _serializationShape = format.getShape(); } } + /** + * Copy-constructor that is useful for sub-classes that just want to + * copy all super-class properties without modifications. + */ + protected BeanSerializerBase(BeanSerializerBase src) { + this(src, src._props, src._filteredProps); + } + public BeanSerializerBase(BeanSerializerBase src, BeanPropertyWriter[] properties, BeanPropertyWriter[] filteredProperties) { @@ -149,10 +148,7 @@ protected BeanSerializerBase(BeanSerializerBase src, { this(src, objectIdWriter, src._propertyFilterId); } - - /** - * @since 2.3 - */ + protected BeanSerializerBase(BeanSerializerBase src, ObjectIdWriter objectIdWriter, Object filterId) { @@ -168,12 +164,6 @@ protected BeanSerializerBase(BeanSerializerBase src, _serializationShape = src._serializationShape; } - @Deprecated // since 2.8, remove soon - protected BeanSerializerBase(BeanSerializerBase src, String[] toIgnore) - { - this(src, ArrayBuilders.arrayToSet(toIgnore)); - } - protected BeanSerializerBase(BeanSerializerBase src, Set toIgnore) { super(src._handledType); @@ -210,55 +200,35 @@ protected BeanSerializerBase(BeanSerializerBase src, Set toIgnore) /** * Mutant factory used for creating a new instance with different * {@link ObjectIdWriter}. - * - * @since 2.0 */ public abstract BeanSerializerBase withObjectIdWriter(ObjectIdWriter objectIdWriter); /** * Mutant factory used for creating a new instance with additional * set of properties to ignore (from properties this instance otherwise has) - * - * @since 2.8 */ protected abstract BeanSerializerBase withIgnorals(Set toIgnore); - - /** - * Mutant factory used for creating a new instance with additional - * set of properties to ignore (from properties this instance otherwise has) - * - * @deprecated since 2.8 - */ - @Deprecated - protected BeanSerializerBase withIgnorals(String[] toIgnore) { - return withIgnorals(ArrayBuilders.arrayToSet(toIgnore)); - } /** * Mutant factory for creating a variant that output POJO as a * JSON Array. Implementations may ignore this request if output * as array is not possible (either at all, or reliably). - * - * @since 2.1 */ protected abstract BeanSerializerBase asArraySerializer(); /** * Mutant factory used for creating a new instance with different * filter id (used with JsonFilter annotation) - * - * @since 2.3 */ @Override public abstract BeanSerializerBase withFilterId(Object filterId); - + /** - * Copy-constructor that is useful for sub-classes that just want to - * copy all super-class properties without modifications. + * Lets force sub-classes to implement this, to avoid accidental missing + * of handling... */ - protected BeanSerializerBase(BeanSerializerBase src) { - this(src, src._props, src._filteredProps); - } + @Override + public abstract JsonSerializer unwrappingSerializer(NameTransformer unwrapper); /** * Copy-constructor that will also rename properties with given prefix @@ -267,7 +237,7 @@ protected BeanSerializerBase(BeanSerializerBase src) { protected BeanSerializerBase(BeanSerializerBase src, NameTransformer unwrapper) { this(src, rename(src._props, unwrapper), rename(src._filteredProps, unwrapper)); } - + private final static BeanPropertyWriter[] rename(BeanPropertyWriter[] props, NameTransformer transformer) { @@ -286,14 +256,14 @@ private final static BeanPropertyWriter[] rename(BeanPropertyWriter[] props, } /* - /********************************************************** - /* Post-constriction processing: resolvable, contextual - /********************************************************** + /********************************************************************** + /* Post-construction processing: resolvable, contextual + /********************************************************************** */ /** - * We need to implement {@link ResolvableSerializer} to be able to - * properly handle cyclic type references. + * We need to resolve dependant serializers here + * to be able to properly handle cyclic type references. */ @Override public void resolve(SerializerProvider provider) @@ -307,7 +277,7 @@ public void resolve(SerializerProvider provider) JsonSerializer nullSer = provider.findNullValueSerializer(prop); if (nullSer != null) { prop.assignNullSerializer(nullSer); - // also: remember to replace filtered property too? (see [JACKSON-364]) + // also: remember to replace filtered property too? if (i < filteredCount) { BeanPropertyWriter w2 = _filteredProps[i]; if (w2 != null) { @@ -316,7 +286,7 @@ public void resolve(SerializerProvider provider) } } } - + if (prop.hasSerializer()) { continue; } @@ -337,10 +307,9 @@ public void resolve(SerializerProvider provider) continue; } } - ser = provider.findValueSerializer(type, prop); - /* 04-Feb-2010, tatu: We may have stashed type serializer for content types - * too, earlier; if so, it's time to connect the dots here: - */ + ser = provider.findPrimaryPropertySerializer(type, prop); + // 04-Feb-2010, tatu: We may have stashed type serializer for content types + // too, earlier; if so, it's time to connect the dots here: if (type.isContainerType()) { TypeSerializer typeSer = type.getContentType().getTypeHandler(); if (typeSer != null) { @@ -379,8 +348,6 @@ public void resolve(SerializerProvider provider) * Helper method that can be used to see if specified property is annotated * to indicate use of a converter for property value (in case of container types, * it is container type itself, not key or content type). - * - * @since 2.2 */ protected JsonSerializer findConvertingSerializer(SerializerProvider provider, BeanPropertyWriter prop) @@ -390,14 +357,14 @@ protected JsonSerializer findConvertingSerializer(SerializerProvider pro if (intr != null) { AnnotatedMember m = prop.getMember(); if (m != null) { - Object convDef = intr.findSerializationConverter(m); + Object convDef = intr.findSerializationConverter(provider.getConfig(), m); if (convDef != null) { Converter conv = provider.converterInstance(prop.getMember(), convDef); JavaType delegateType = conv.getOutputType(provider.getTypeFactory()); // [databind#731]: Should skip if nominally java.lang.Object JsonSerializer ser = delegateType.isJavaLangObject() ? null - : provider.findValueSerializer(delegateType, prop); - return new StdDelegatingSerializer(conv, delegateType, ser); + : provider.findPrimaryPropertySerializer(delegateType, prop); + return new StdDelegatingSerializer(conv, delegateType, ser, prop); } } } @@ -406,18 +373,17 @@ protected JsonSerializer findConvertingSerializer(SerializerProvider pro @SuppressWarnings("incomplete-switch") @Override - public JsonSerializer createContextual(SerializerProvider provider, - BeanProperty property) + public JsonSerializer createContextual(SerializerProvider ctxt, BeanProperty property) throws JsonMappingException { - final AnnotationIntrospector intr = provider.getAnnotationIntrospector(); - final AnnotatedMember accessor = (property == null || intr == null) - ? null : property.getMember(); - final SerializationConfig config = provider.getConfig(); + final AnnotationIntrospector intr = ctxt.getAnnotationIntrospector(); + final AnnotatedMember accessor = _neitherNull(property, intr) + ? property.getMember() : null; + final SerializationConfig config = ctxt.getConfig(); // Let's start with one big transmutation: Enums that are annotated // to serialize as Objects may want to revert - JsonFormat.Value format = findFormatOverrides(provider, property, handledType()); + JsonFormat.Value format = findFormatOverrides(ctxt, property, handledType()); JsonFormat.Shape shape = null; if ((format != null) && format.hasShape()) { shape = format.getShape(); @@ -432,8 +398,8 @@ public JsonSerializer createContextual(SerializerProvider provider, // for now, just do class ones BeanDescription desc = config.introspectClassAnnotations(_beanType); JsonSerializer ser = EnumSerializer.construct(_beanType.getRawClass(), - provider.getConfig(), desc, format); - return provider.handlePrimaryContextualization(ser, property); + ctxt.getConfig(), desc, format); + return ctxt.handlePrimaryContextualization(ser, property); } // 16-Oct-2016, tatu: Ditto for `Map`, `Map.Entry` subtypes } else if (shape == JsonFormat.Shape.NATURAL) { @@ -449,7 +415,7 @@ public JsonSerializer createContextual(SerializerProvider provider, // see if "static" typing is needed, nor look for `TypeSerializer` yet... JsonSerializer ser = new MapEntrySerializer(_beanType, kt, vt, false, null, property); - return provider.handlePrimaryContextualization(ser, property); + return ctxt.handlePrimaryContextualization(ser, property); } } } @@ -465,11 +431,11 @@ public JsonSerializer createContextual(SerializerProvider provider, if (ignorals != null) { ignoredProps = ignorals.findIgnoredForSerialization(); } - ObjectIdInfo objectIdInfo = intr.findObjectIdInfo(accessor); + ObjectIdInfo objectIdInfo = intr.findObjectIdInfo(config, accessor); if (objectIdInfo == null) { // no ObjectId override, but maybe ObjectIdRef? if (oiw != null) { - objectIdInfo = intr.findObjectReferenceInfo(accessor, null); + objectIdInfo = intr.findObjectReferenceInfo(config, accessor, null); if (objectIdInfo != null) { oiw = _objectIdWriter.withAlwaysAsId(objectIdInfo.getAlwaysAsId()); } @@ -479,11 +445,11 @@ public JsonSerializer createContextual(SerializerProvider provider, // to be able to move to SerializerProvider (where it really belongs) // 2.1: allow modifications by "id ref" annotations as well: - objectIdInfo = intr.findObjectReferenceInfo(accessor, objectIdInfo); + objectIdInfo = intr.findObjectReferenceInfo(config, accessor, objectIdInfo); ObjectIdGenerator gen; Class implClass = objectIdInfo.getGeneratorType(); - JavaType type = provider.constructType(implClass); - JavaType idType = provider.getTypeFactory().findTypeParameters(type, ObjectIdGenerator.class)[0]; + JavaType type = ctxt.constructType(implClass); + JavaType idType = ctxt.getTypeFactory().findTypeParameters(type, ObjectIdGenerator.class)[0]; // Property-based generator is trickier if (implClass == ObjectIdGenerators.PropertyGenerator.class) { // most special one, needs extra work String propName = objectIdInfo.getPropertyName().getSimpleName(); @@ -491,7 +457,7 @@ public JsonSerializer createContextual(SerializerProvider provider, for (int i = 0, len = _props.length; ; ++i) { if (i == len) { - provider.reportBadDefinition(_beanType, String.format( + ctxt.reportBadDefinition(_beanType, String.format( "Invalid Object Id definition for %s: cannot find property with name '%s'", handledType().getName(), propName)); } @@ -516,7 +482,7 @@ public JsonSerializer createContextual(SerializerProvider provider, gen = new PropertyBasedObjectIdGenerator(objectIdInfo, idProp); oiw = ObjectIdWriter.construct(idType, (PropertyName) null, gen, objectIdInfo.getAlwaysAsId()); } else { // other types need to be simpler - gen = provider.objectIdGeneratorInstance(accessor, objectIdInfo); + gen = ctxt.objectIdGeneratorInstance(accessor, objectIdInfo); oiw = ObjectIdWriter.construct(idType, objectIdInfo.getPropertyName(), gen, objectIdInfo.getAlwaysAsId()); } @@ -533,7 +499,8 @@ public JsonSerializer createContextual(SerializerProvider provider, // either way, need to resolve serializer: BeanSerializerBase contextual = this; if (oiw != null) { - JsonSerializer ser = provider.findValueSerializer(oiw.idType, property); + // not really associated with the property so let's not pass it? + JsonSerializer ser = ctxt.findRootValueSerializer(oiw.idType); oiw = oiw.withSerializer(ser); if (oiw != _objectIdWriter) { contextual = contextual.withObjectIdWriter(oiw); @@ -557,9 +524,9 @@ public JsonSerializer createContextual(SerializerProvider provider, } /* - /********************************************************** - /* Accessors - /********************************************************** + /********************************************************************** + /* Public accessors + /********************************************************************** */ @Override @@ -567,10 +534,56 @@ public Iterator properties() { return Arrays.asList(_props).iterator(); } + /** + * @since 3.0 + */ + public int propertyCount() { + return _props.length; + } + + /** + * Accessor for checking if view-processing is enabled for this bean, + * that is, if it has separate set of properties with view-checking + * added. + * + * @since 3.0 + */ + public boolean hasViewProperties() { + return (_filteredProps != null); + } + /** + * @since 3.0 + */ + public Object getFilterId() { + return _propertyFilterId; + } + /* - /********************************************************** + /********************************************************************** + /* Helper methods for implementation classes + /********************************************************************** + */ + + /** + * Helper method for sub-classes to check if it should be possible to + * construct an "as-array" serializer. Returns if all of following + * hold true: + *
      + *
    • have Object Id (may be allowed in future)
    • + *
    • have "any getter"
    • + *
    + * + * @since 3.0 + */ + public boolean canCreateArraySerializer() { + return (_objectIdWriter == null) + && (_anyGetterWriter == null); + } + + /* + /********************************************************************** /* Partial JsonSerializer implementation - /********************************************************** + /********************************************************************** */ @Override @@ -590,25 +603,23 @@ public void serializeWithType(Object bean, JsonGenerator gen, throws IOException { if (_objectIdWriter != null) { - gen.setCurrentValue(bean); // [databind#631] _serializeWithObjectId(bean, gen, provider, typeSer); return; } - - gen.setCurrentValue(bean); // [databind#631] WritableTypeId typeIdDef = _typeIdDef(typeSer, bean, JsonToken.START_OBJECT); typeSer.writeTypePrefix(gen, typeIdDef); if (_propertyFilterId != null) { - serializeFieldsFiltered(bean, gen, provider); + _serializeFieldsFiltered(bean, gen, provider, _propertyFilterId); } else { - serializeFields(bean, gen, provider); + _serializeFields(bean, gen, provider); } typeSer.writeTypeSuffix(gen, typeIdDef); } - protected final void _serializeWithObjectId(Object bean, JsonGenerator gen, SerializerProvider provider, - boolean startEndObject) throws IOException + protected final void _serializeWithObjectId(Object bean, JsonGenerator gen, + SerializerProvider provider, boolean startEndObject) throws IOException { + gen.setCurrentValue(bean); final ObjectIdWriter w = _objectIdWriter; WritableObjectId objectId = provider.findObjectId(bean, w.generator); // If possible, write as id already @@ -626,9 +637,9 @@ protected final void _serializeWithObjectId(Object bean, JsonGenerator gen, Seri } objectId.writeAsField(gen, provider, w); if (_propertyFilterId != null) { - serializeFieldsFiltered(bean, gen, provider); + _serializeFieldsFiltered(bean, gen, provider, _propertyFilterId); } else { - serializeFields(bean, gen, provider); + _serializeFields(bean, gen, provider); } if (startEndObject) { gen.writeEndObject(); @@ -638,6 +649,7 @@ protected final void _serializeWithObjectId(Object bean, JsonGenerator gen, Seri protected final void _serializeWithObjectId(Object bean, JsonGenerator gen, SerializerProvider provider, TypeSerializer typeSer) throws IOException { + gen.setCurrentValue(bean); final ObjectIdWriter w = _objectIdWriter; WritableObjectId objectId = provider.findObjectId(bean, w.generator); // If possible, write as id already @@ -663,16 +675,13 @@ protected void _serializeObjectId(Object bean, JsonGenerator g, typeSer.writeTypePrefix(g, typeIdDef); objectId.writeAsField(g, provider, w); if (_propertyFilterId != null) { - serializeFieldsFiltered(bean, g, provider); + _serializeFieldsFiltered(bean, g, provider, _propertyFilterId); } else { - serializeFields(bean, g, provider); + _serializeFields(bean, g, provider); } typeSer.writeTypeSuffix(g, typeIdDef); } - /** - * @since 2.9 - */ protected final WritableTypeId _typeIdDef(TypeSerializer typeSer, Object bean, JsonToken valueShape) { if (_typeId == null) { @@ -686,83 +695,171 @@ protected final WritableTypeId _typeIdDef(TypeSerializer typeSer, return typeSer.typeId(bean, valueShape, typeId); } - @Deprecated // since 2.9 - protected final String _customTypeId(Object bean) - { - final Object typeId = _typeId.getValue(bean); - if (typeId == null) { - return ""; - } - return (typeId instanceof String) ? (String) typeId : typeId.toString(); - } - /* - /********************************************************** - /* Field serialization methods - /********************************************************** + /********************************************************************** + /* Field serialization methods, 3.0 + /********************************************************************** */ - protected void serializeFields(Object bean, JsonGenerator gen, SerializerProvider provider) + /** + * Method called called when neither JSON Filter is to be applied, nor + * view-filtering. This means that all property writers are non null + * and can be called directly. + * + * @since 3.0 + */ + protected void _serializeFieldsNoView(Object bean, JsonGenerator gen, + SerializerProvider provider, BeanPropertyWriter[] props) throws IOException { - final BeanPropertyWriter[] props; - if (_filteredProps != null && provider.getActiveView() != null) { - props = _filteredProps; - } else { - props = _props; + int i = 0; + int left = props.length; + BeanPropertyWriter prop = null; + + try { + if (left > 3) { + do { + prop = props[i]; + prop.serializeAsField(bean, gen, provider); + prop = props[i+1]; + prop.serializeAsField(bean, gen, provider); + prop = props[i+2]; + prop.serializeAsField(bean, gen, provider); + prop = props[i+3]; + prop.serializeAsField(bean, gen, provider); + left -= 4; + i += 4; + } while (left > 3); + } + switch (left) { + case 3: + prop = props[i++]; + prop.serializeAsField(bean, gen, provider); + case 2: + prop = props[i++]; + prop.serializeAsField(bean, gen, provider); + case 1: + prop = props[i++]; + prop.serializeAsField(bean, gen, provider); + } + if (_anyGetterWriter != null) { + prop = null; + _anyGetterWriter.getAndSerialize(bean, gen, provider); + } + } catch (Exception e) { + String name = (prop == null) ? "[anySetter]" : prop.getName(); + wrapAndThrow(provider, e, bean, name); + } catch (StackOverflowError e) { + JsonMappingException mapE = new JsonMappingException(gen, "Infinite recursion (StackOverflowError)", e); + String name = (prop == null) ? "[anySetter]" : prop.getName(); + mapE.prependPath(new JsonMappingException.Reference(bean, name)); + throw mapE; } + } + /** + * Method called called when no JSON Filter is to be applied, but + * View filtering is in effect and so some of properties may be + * nulls to check. + * + * @since 3.0 + */ + protected void _serializeFieldsMaybeView(Object bean, JsonGenerator gen, + SerializerProvider provider, BeanPropertyWriter[] props) + throws IOException + { int i = 0; + int left = props.length; + BeanPropertyWriter prop = null; + try { - for (final int len = props.length; i < len; ++i) { - BeanPropertyWriter prop = props[i]; - if (prop != null) { // can have nulls in filtered list + if (left > 3) { + do { + prop = props[i]; + if (prop != null) { + prop.serializeAsField(bean, gen, provider); + } + prop = props[i+1]; + if (prop != null) { + prop.serializeAsField(bean, gen, provider); + } + prop = props[i+2]; + if (prop != null) { + prop.serializeAsField(bean, gen, provider); + } + prop = props[i+3]; + if (prop != null) { + prop.serializeAsField(bean, gen, provider); + } + left -= 4; + i += 4; + } while (left > 3); + } + switch (left) { + case 3: + prop = props[i++]; + if (prop != null) { + prop.serializeAsField(bean, gen, provider); + } + case 2: + prop = props[i++]; + if (prop != null) { + prop.serializeAsField(bean, gen, provider); + } + case 1: + prop = props[i++]; + if (prop != null) { prop.serializeAsField(bean, gen, provider); } } if (_anyGetterWriter != null) { + prop = null; _anyGetterWriter.getAndSerialize(bean, gen, provider); } } catch (Exception e) { - String name = (i == props.length) ? "[anySetter]" : props[i].getName(); + String name = (prop == null) ? "[anySetter]" : prop.getName(); wrapAndThrow(provider, e, bean, name); } catch (StackOverflowError e) { - // 04-Sep-2009, tatu: Dealing with this is tricky, since we don't have many - // stack frames to spare... just one or two; can't make many calls. - - // 10-Dec-2015, tatu: and due to above, avoid "from" method, call ctor directly: - //JsonMappingException mapE = JsonMappingException.from(gen, "Infinite recursion (StackOverflowError)", e); JsonMappingException mapE = new JsonMappingException(gen, "Infinite recursion (StackOverflowError)", e); - - String name = (i == props.length) ? "[anySetter]" : props[i].getName(); + String name = (prop == null) ? "[anySetter]" : prop.getName(); mapE.prependPath(new JsonMappingException.Reference(bean, name)); throw mapE; } } + /* + /********************************************************************** + /* Field serialization methods, 2.x + /********************************************************************** + */ + + // 28-Oct-2017, tatu: Not yet optimized. Could be, if it seems + // commonly useful wrt JsonView filtering /** * Alternative serialization method that gets called when there is a * {@link PropertyFilter} that needs to be called to determine * which properties are to be serialized (and possibly how) */ - protected void serializeFieldsFiltered(Object bean, JsonGenerator gen, - SerializerProvider provider) - throws IOException, JsonGenerationException + protected void _serializeFieldsFiltered(Object bean, JsonGenerator gen, + SerializerProvider provider, Object filterId) + throws IOException { - /* note: almost verbatim copy of "serializeFields"; copied (instead of merged) - * so that old method need not add check for existence of filter. - */ final BeanPropertyWriter[] props; + final PropertyFilter filter = findPropertyFilter(provider, filterId, bean); if (_filteredProps != null && provider.getActiveView() != null) { props = _filteredProps; + // better also allow missing filter actually.. Falls down + if (filter == null) { + _serializeFieldsMaybeView(bean, gen, provider, props); + return; + } } else { props = _props; + if (filter == null) { + _serializeFieldsNoView(bean, gen, provider, props); + return; + } } - final PropertyFilter filter = findPropertyFilter(provider, _propertyFilterId, bean); - // better also allow missing filter actually.. - if (filter == null) { - serializeFields(bean, gen, provider); - return; - } + int i = 0; try { for (final int len = props.length; i < len; ++i) { @@ -787,44 +884,22 @@ protected void serializeFieldsFiltered(Object bean, JsonGenerator gen, } } - @Deprecated - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) - throws JsonMappingException + protected void _serializeFields(Object bean, JsonGenerator gen, SerializerProvider provider) + throws IOException { - ObjectNode o = createSchemaNode("object", true); - // [JACKSON-813]: Add optional JSON Schema id attribute, if found - // NOTE: not optimal, does NOT go through AnnotationIntrospector etc: - JsonSerializableSchema ann = _handledType.getAnnotation(JsonSerializableSchema.class); - if (ann != null) { - String id = ann.id(); - if (id != null && id.length() > 0) { - o.put("id", id); - } - } - - //todo: should the classname go in the title? - //o.put("title", _className); - ObjectNode propertiesNode = o.objectNode(); - final PropertyFilter filter; - if (_propertyFilterId != null) { - filter = findPropertyFilter(provider, _propertyFilterId, null); + // NOTE: only called from places where FilterId (JsonView) already checked. + if (_filteredProps != null && provider.getActiveView() != null) { + _serializeFieldsMaybeView(bean, gen, provider, _filteredProps); } else { - filter = null; - } - - for (int i = 0; i < _props.length; i++) { - BeanPropertyWriter prop = _props[i]; - if (filter == null) { - prop.depositSchemaProperty(propertiesNode, provider); - } else { - filter.depositSchemaProperty(prop, propertiesNode, provider); - } - + _serializeFieldsNoView(bean, gen, provider, _props); } - o.set("properties", propertiesNode); - return o; } + + /* + /********************************************************************** + /* Introspection (for schema generation etc) + /********************************************************************** + */ @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) @@ -863,4 +938,9 @@ public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType t } } } + + @Override + public String toString() { + return getClass().getSimpleName()+" for "+handledType().getName(); + } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/BooleanSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/BooleanSerializer.java index 135add092b..52105b2a8a 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/BooleanSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/BooleanSerializer.java @@ -1,7 +1,6 @@ package com.fasterxml.jackson.databind.ser.std; import java.io.IOException; -import java.lang.reflect.Type; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.core.JsonGenerator; @@ -9,13 +8,11 @@ import com.fasterxml.jackson.databind.BeanProperty; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; -import com.fasterxml.jackson.databind.ser.ContextualSerializer; /** * Serializer used for primitive boolean, as well as java.util.Boolean @@ -26,9 +23,7 @@ */ @JacksonStdImpl public final class BooleanSerializer -//In 2.9, removed use of intermediate type `NonTypedScalarSerializerBase` extends StdScalarSerializer - implements ContextualSerializer { private static final long serialVersionUID = 1L; @@ -70,11 +65,6 @@ public final void serializeWithType(Object value, JsonGenerator g, SerializerPro g.writeBoolean(Boolean.TRUE.equals(value)); } - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) { - return createSchemaNode("boolean", !_forPrimitive); - } - @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { visitor.expectBooleanFormat(typeHint); @@ -83,12 +73,9 @@ public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType t /** * Alternate implementation that is used when values are to be serialized * as numbers 0 (false) or 1 (true). - * - * @since 2.9 */ final static class AsNumber extends StdScalarSerializer - implements ContextualSerializer { private static final long serialVersionUID = 1L; diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/ByteArraySerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/ByteArraySerializer.java index 69c2ae602f..976a497a40 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/ByteArraySerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/ByteArraySerializer.java @@ -1,21 +1,18 @@ package com.fasterxml.jackson.databind.ser.std; import java.io.IOException; -import java.lang.reflect.Type; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.core.type.WritableTypeId; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonArrayFormatVisitor; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; -import com.fasterxml.jackson.databind.node.ObjectNode; /** * Unlike other integral number array serializers, we do not just print out byte values @@ -69,14 +66,6 @@ public void serializeWithType(byte[] value, JsonGenerator g, SerializerProvider */ } - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) - { - ObjectNode o = createSchemaNode("array", true); - ObjectNode itemSchema = createSchemaNode("byte"); //binary values written as strings? - return o.set("items", itemSchema); - } - @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/ClassSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/ClassSerializer.java deleted file mode 100644 index 6dfffcf80b..0000000000 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/ClassSerializer.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.fasterxml.jackson.databind.ser.std; - -import java.io.IOException; -import java.lang.reflect.Type; - -import com.fasterxml.jackson.core.JsonGenerator; - -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; - -/** - * Also: default bean access will not do much good with Class.class. But - * we can just serialize the class name and that should be enough. - */ -@SuppressWarnings("serial") -public class ClassSerializer - extends StdScalarSerializer> -{ - public ClassSerializer() { super(Class.class, false); } - - @Override - public void serialize(Class value, JsonGenerator g, SerializerProvider provider) throws IOException - { - g.writeString(value.getName()); - } - - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) - { - return createSchemaNode("string", true); - } - - @Override - public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) - throws JsonMappingException - { - visitStringFormat(visitor, typeHint); - } -} \ No newline at end of file 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 6dec6a7838..55974f9364 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 @@ -25,32 +25,19 @@ public class CollectionSerializer extends AsArraySerializerBase> { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 3L; /* - /********************************************************** + /********************************************************************** /* Life-cycle - /********************************************************** + /********************************************************************** */ - /** - * @since 2.6 - */ public CollectionSerializer(JavaType elemType, boolean staticTyping, TypeSerializer vts, JsonSerializer valueSerializer) { super(Collection.class, elemType, staticTyping, vts, valueSerializer); } - /** - * @deprecated since 2.6 - */ - @Deprecated // since 2.6 - public CollectionSerializer(JavaType elemType, boolean staticTyping, TypeSerializer vts, - BeanProperty property, JsonSerializer valueSerializer) { - // note: assumption is 'property' is always passed as null - this(elemType, staticTyping, vts, valueSerializer); - } - public CollectionSerializer(CollectionSerializer src, BeanProperty property, TypeSerializer vts, JsonSerializer valueSerializer, Boolean unwrapSingle) { @@ -70,9 +57,9 @@ public CollectionSerializer withResolved(BeanProperty property, } /* - /********************************************************** + /********************************************************************** /* Accessors - /********************************************************** + /********************************************************************** */ @Override @@ -86,9 +73,9 @@ public boolean hasSingleElement(Collection value) { } /* - /********************************************************** + /********************************************************************** /* Actual serialization - /********************************************************** + /********************************************************************** */ @Override @@ -103,24 +90,23 @@ public final void serialize(Collection value, JsonGenerator g, SerializerProv return; } } - g.writeStartArray(len); + g.writeStartArray(value, len); serializeContents(value, g, provider); g.writeEndArray(); } @Override - public void serializeContents(Collection value, JsonGenerator g, SerializerProvider provider) throws IOException + public void serializeContents(Collection value, JsonGenerator g, SerializerProvider ctxt) throws IOException { - g.setCurrentValue(value); if (_elementSerializer != null) { - serializeContentsUsing(value, g, provider, _elementSerializer); + serializeContentsUsing(value, g, ctxt, _elementSerializer); return; } Iterator it = value.iterator(); if (!it.hasNext()) { return; } - PropertySerializerMap serializers = _dynamicSerializers; + PropertySerializerMap serializers = _dynamicValueSerializers; final TypeSerializer typeSer = _valueTypeSerializer; int i = 0; @@ -128,29 +114,28 @@ public void serializeContents(Collection value, JsonGenerator g, SerializerPr do { Object elem = it.next(); if (elem == null) { - provider.defaultSerializeNull(g); + ctxt.defaultSerializeNullValue(g); } else { Class cc = elem.getClass(); JsonSerializer serializer = serializers.serializerFor(cc); if (serializer == null) { if (_elementType.hasGenericTypes()) { - serializer = _findAndAddDynamic(serializers, - provider.constructSpecializedType(_elementType, cc), provider); + serializer = _findAndAddDynamic(ctxt, ctxt.constructSpecializedType(_elementType, cc)); } else { - serializer = _findAndAddDynamic(serializers, cc, provider); + serializer = _findAndAddDynamic(ctxt, cc); } - serializers = _dynamicSerializers; + serializers = _dynamicValueSerializers; } if (typeSer == null) { - serializer.serialize(elem, g, provider); + serializer.serialize(elem, g, ctxt); } else { - serializer.serializeWithType(elem, g, provider, typeSer); + serializer.serializeWithType(elem, g, ctxt, typeSer); } } ++i; } while (it.hasNext()); } catch (Exception e) { - wrapAndThrow(provider, e, value, i); + wrapAndThrow(ctxt, e, value, i); } } @@ -165,7 +150,7 @@ public void serializeContentsUsing(Collection value, JsonGenerator g, Seriali Object elem = it.next(); try { if (elem == null) { - provider.defaultSerializeNull(g); + provider.defaultSerializeNullValue(g); } else { if (typeSer == null) { ser.serialize(elem, g, provider); 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 572d23eb62..2662afdc86 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 @@ -1,7 +1,6 @@ package com.fasterxml.jackson.databind.ser.std; import java.io.IOException; -import java.lang.reflect.Type; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; @@ -16,13 +15,11 @@ import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.jsonFormatVisitors.*; -import com.fasterxml.jackson.databind.ser.ContextualSerializer; import com.fasterxml.jackson.databind.util.StdDateFormat; @SuppressWarnings("serial") public abstract class DateTimeSerializerBase extends StdScalarSerializer - implements ContextualSerializer { /** * Flag that indicates that serialization must be done as the @@ -148,12 +145,6 @@ public boolean isEmpty(SerializerProvider serializers, T value) { protected abstract long _timestamp(T value); - @Override - public JsonNode getSchema(SerializerProvider serializers, Type typeHint) { - //todo: (ryan) add a format for the date in the schema? - return createSchemaNode(_asTimestamp(serializers) ? "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 def97ee4fa..a0dd770489 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 @@ -1,7 +1,6 @@ package com.fasterxml.jackson.databind.ser.std; import java.io.IOException; -import java.lang.reflect.Type; import java.util.*; import com.fasterxml.jackson.annotation.JsonFormat; @@ -13,9 +12,6 @@ import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonStringFormatVisitor; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.fasterxml.jackson.databind.ser.ContextualSerializer; import com.fasterxml.jackson.databind.util.EnumValues; /** @@ -27,30 +23,25 @@ @JacksonStdImpl public class EnumSerializer extends StdScalarSerializer> - implements ContextualSerializer { private static final long serialVersionUID = 1L; /** - * This map contains pre-resolved values (since there are ways - * to customize actual String constants to use) to use as - * serializations. + * This map contains pre-resolved values (since there are ways to customize + * actual String constants to use) to use as serializations. */ protected final EnumValues _values; /** - * Flag that is set if we statically know serialization choice - * between index and textual format (null if it needs to be dynamically - * checked). - * - * @since 2.1 + * Flag that is set if we statically know serialization choice between + * index and textual format (null if it needs to be dynamically checked). */ protected final Boolean _serializeAsIndex; /* - /********************************************************** - /* Construction, initialization - /********************************************************** + /********************************************************************** + /* Life-cycle + /********************************************************************** */ public EnumSerializer(EnumValues v, Boolean serializeAsIndex) @@ -63,17 +54,14 @@ public EnumSerializer(EnumValues v, Boolean serializeAsIndex) /** * Factory method used by {@link com.fasterxml.jackson.databind.ser.BasicSerializerFactory} * for constructing serializer instance of Enum types. - * - * @since 2.1 */ @SuppressWarnings("unchecked") public static EnumSerializer construct(Class enumClass, SerializationConfig config, BeanDescription beanDesc, JsonFormat.Value format) { - /* 08-Apr-2015, tatu: As per [databind#749], we cannot statically determine - * between name() and toString(), need to construct `EnumValues` with names, - * handle toString() case dynamically (for example) - */ + // 08-Apr-2015, tatu: As per [databind#749], we cannot statically determine + // between name() and toString(), need to construct `EnumValues` with names, + // handle toString() case dynamically (for example) EnumValues v = EnumValues.constructFromName(config, (Class>) enumClass); Boolean serializeAsIndex = _isShapeWrittenUsingIndex(enumClass, format, true, null); return new EnumSerializer(v, serializeAsIndex); @@ -85,10 +73,10 @@ public static EnumSerializer construct(Class enumClass, SerializationConfig c * choice here, however. */ @Override - public JsonSerializer createContextual(SerializerProvider serializers, + public JsonSerializer createContextual(SerializerProvider ctxt, BeanProperty property) throws JsonMappingException { - JsonFormat.Value format = findFormatOverrides(serializers, + JsonFormat.Value format = findFormatOverrides(ctxt, property, handledType()); if (format != null) { Class type = handledType(); @@ -102,61 +90,42 @@ public JsonSerializer createContextual(SerializerProvider serializers, } /* - /********************************************************** + /********************************************************************** /* Extended API for Jackson databind core - /********************************************************** + /********************************************************************** */ public EnumValues getEnumValues() { return _values; } /* - /********************************************************** + /********************************************************************** /* Actual serialization - /********************************************************** + /********************************************************************** */ @Override - public final void serialize(Enum en, JsonGenerator gen, SerializerProvider serializers) + public final void serialize(Enum en, JsonGenerator g, SerializerProvider ctxt) throws IOException { - // [JACKSON-684]: serialize as index? - if (_serializeAsIndex(serializers)) { - gen.writeNumber(en.ordinal()); + // Serialize as index? + if (_serializeAsIndex(ctxt)) { + g.writeNumber(en.ordinal()); return; } // [databind#749]: or via toString()? - if (serializers.isEnabled(SerializationFeature.WRITE_ENUMS_USING_TO_STRING)) { - gen.writeString(en.toString()); + if (ctxt.isEnabled(SerializationFeature.WRITE_ENUMS_USING_TO_STRING)) { + g.writeString(en.toString()); return; } - gen.writeString(_values.serializedValueFor(en)); + g.writeString(_values.serializedValueFor(en)); } /* - /********************************************************** + /********************************************************************** /* Schema support - /********************************************************** + /********************************************************************** */ - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) - { - if (_serializeAsIndex(provider)) { - return createSchemaNode("integer", true); - } - ObjectNode objectNode = createSchemaNode("string", true); - if (typeHint != null) { - JavaType type = provider.constructType(typeHint); - if (type.isEnumType()) { - ArrayNode enumNode = objectNode.putArray("enum"); - for (SerializableString value : _values.values()) { - enumNode.add(value.getValue()); - } - } - } - return objectNode; - } - @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException @@ -187,17 +156,17 @@ public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType t } /* - /********************************************************** + /********************************************************************** /* Helper methods - /********************************************************** + /********************************************************************** */ - protected final boolean _serializeAsIndex(SerializerProvider serializers) + protected final boolean _serializeAsIndex(SerializerProvider ctxt) { if (_serializeAsIndex != null) { return _serializeAsIndex.booleanValue(); } - return serializers.isEnabled(SerializationFeature.WRITE_ENUMS_USING_INDEX); + return ctxt.isEnabled(SerializationFeature.WRITE_ENUMS_USING_INDEX); } /** 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 6211d71281..b1c2ab5b1e 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 @@ -11,9 +11,6 @@ public class EnumSetSerializer extends AsArraySerializerBase>> { - /** - * @since 2.6 - */ public EnumSetSerializer(JavaType elemType) { super(EnumSet.class, elemType, true, null, null); } @@ -36,7 +33,7 @@ public EnumSetSerializer withResolved(BeanProperty property, Boolean unwrapSingle) { return new EnumSetSerializer(this, property, vts, elementSerializer, unwrapSingle); } - + @Override public boolean isEmpty(SerializerProvider prov, EnumSet> value) { return value.isEmpty(); @@ -60,29 +57,27 @@ public final void serialize(EnumSet> value, JsonGenerator gen, return; } } - gen.writeStartArray(len); + gen.writeStartArray(value, len); serializeContents(value, gen, provider); gen.writeEndArray(); } @Override public void serializeContents(EnumSet> value, JsonGenerator gen, - SerializerProvider provider) + SerializerProvider ctxt) throws IOException { JsonSerializer enumSer = _elementSerializer; - /* Need to dynamically find instance serializer; unfortunately - * that seems to be the only way to figure out type (no accessors - * to the enum class that set knows) - */ + // Need to dynamically find instance serializer; unfortunately that seems + // to be the only way to figure out type (no accessors to the enum class that set knows) for (Enum en : value) { if (enumSer == null) { - /* 12-Jan-2010, tatu: Since enums cannot be polymorphic, let's - * not bother with typed serializer variant here - */ - enumSer = provider.findValueSerializer(en.getDeclaringClass(), _property); + // 12-Jan-2010, tatu: Since enums cannot be polymorphic, let's + // not bother with typed serializer variant here + enumSer = _findAndAddDynamic(ctxt, en.getDeclaringClass()); + } - enumSer.serialize(en, gen, provider); + enumSer.serialize(en, gen, ctxt); } } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/FileSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/FileSerializer.java deleted file mode 100644 index 696348aba7..0000000000 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/FileSerializer.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.fasterxml.jackson.databind.ser.std; - -import java.io.File; -import java.io.IOException; -import java.lang.reflect.Type; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; - -/** - * For now, File objects get serialized by just outputting - * absolute (but not canonical) name as String value - */ -@SuppressWarnings("serial") -public class FileSerializer - extends StdScalarSerializer -{ - public FileSerializer() { super(File.class); } - - @Override - public void serialize(File value, JsonGenerator g, SerializerProvider provider) throws IOException { - g.writeString(value.getAbsolutePath()); - } - - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) { - return createSchemaNode("string", true); - } - - @Override - public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) - throws JsonMappingException - { - visitStringFormat(visitor, typeHint); - } -} \ No newline at end of file diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/InetAddressSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/InetAddressSerializer.java index 2bc0e14c10..f528177e2c 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/InetAddressSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/InetAddressSerializer.java @@ -11,7 +11,6 @@ import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; -import com.fasterxml.jackson.databind.ser.ContextualSerializer; /** * Simple serializer for {@link java.net.InetAddress}. Main complexity is @@ -25,31 +24,23 @@ @SuppressWarnings("serial") public class InetAddressSerializer extends StdScalarSerializer - implements ContextualSerializer { - /** - * @since 2.9 - */ protected final boolean _asNumeric; public InetAddressSerializer() { this(false); } - /** - * @since 2.9 - */ public InetAddressSerializer(boolean asNumeric) { super(InetAddress.class); _asNumeric = asNumeric; } @Override - public JsonSerializer createContextual(SerializerProvider serializers, + public JsonSerializer createContextual(SerializerProvider ctxt, BeanProperty property) throws JsonMappingException { - JsonFormat.Value format = findFormatOverrides(serializers, - property, handledType()); + JsonFormat.Value format = findFormatOverrides(ctxt, property, handledType()); boolean asNumeric = false; if (format != null) { JsonFormat.Shape shape = format.getShape(); 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 5cc5068452..d42bda42a8 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 @@ -36,7 +36,7 @@ public IterableSerializer withResolved(BeanProperty property, Boolean unwrapSingle) { return new IterableSerializer(this, property, vts, elementSerializer, unwrapSingle); } - + @Override public boolean isEmpty(SerializerProvider prov, Iterable value) { // Not really good way to implement this, but has to do for now: @@ -59,54 +59,53 @@ public boolean hasSingleElement(Iterable value) { } @Override - public final void serialize(Iterable value, JsonGenerator gen, - SerializerProvider provider)throws IOException + public final void serialize(Iterable value, JsonGenerator g, + SerializerProvider ctxt) throws IOException { if (((_unwrapSingle == null) && - provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) + ctxt.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) || (_unwrapSingle == Boolean.TRUE)) { if (hasSingleElement(value)) { - serializeContents(value, gen, provider); + serializeContents(value, g, ctxt); return; } } - gen.writeStartArray(); - serializeContents(value, gen, provider); - gen.writeEndArray(); + // [databind#631]: Assign current value, to be accessible by custom serializers + g.setCurrentValue(value); + g.writeStartArray(); + serializeContents(value, g, ctxt); + g.writeEndArray(); } - + @Override - public void serializeContents(Iterable value, JsonGenerator jgen, - SerializerProvider provider) throws IOException + public void serializeContents(Iterable value, JsonGenerator g, + SerializerProvider ctxt) throws IOException { Iterator it = value.iterator(); if (it.hasNext()) { final TypeSerializer typeSer = _valueTypeSerializer; - JsonSerializer prevSerializer = null; - Class prevClass = null; - do { Object elem = it.next(); if (elem == null) { - provider.defaultSerializeNull(jgen); + ctxt.defaultSerializeNullValue(g); continue; } - JsonSerializer currSerializer = _elementSerializer; - if (currSerializer == null) { - // Minor optimization to avoid most lookups: + JsonSerializer serializer = _elementSerializer; + if (serializer == null) { Class cc = elem.getClass(); - if (cc == prevClass) { - currSerializer = prevSerializer; - } else { - currSerializer = provider.findValueSerializer(cc, _property); - prevSerializer = currSerializer; - prevClass = cc; + serializer = _dynamicValueSerializers.serializerFor(cc); + if (serializer == null) { + if (_elementType.hasGenericTypes()) { + serializer = _findAndAddDynamic(ctxt, ctxt.constructSpecializedType(_elementType, cc)); + } else { + serializer = _findAndAddDynamic(ctxt, cc); + } } } if (typeSer == null) { - currSerializer.serialize(elem, jgen, provider); + serializer.serialize(elem, g, ctxt); } else { - currSerializer.serializeWithType(elem, jgen, provider, typeSer); + serializer.serializeWithType(elem, g, ctxt, typeSer); } } while (it.hasNext()); } 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 6112a7d4cf..b89d4f6654 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/JsonValueSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/JsonValueSerializer.java @@ -2,7 +2,6 @@ import java.io.IOException; import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Type; import java.util.LinkedHashSet; import java.util.Set; @@ -12,14 +11,11 @@ import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; import com.fasterxml.jackson.databind.introspect.AnnotatedMember; -import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitable; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonStringFormatVisitor; -import com.fasterxml.jackson.databind.jsonschema.SchemaAware; import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; import com.fasterxml.jackson.databind.ser.BeanSerializer; -import com.fasterxml.jackson.databind.ser.ContextualSerializer; import com.fasterxml.jackson.databind.util.ClassUtil; /** @@ -37,17 +33,19 @@ @SuppressWarnings("serial") @JacksonStdImpl public class JsonValueSerializer - extends StdSerializer - implements ContextualSerializer, JsonFormatVisitable, SchemaAware + extends StdDynamicSerializer { /** - * @since 2.9 + * Accessor (field, getter) used to access value to serialize. */ protected final AnnotatedMember _accessor; - protected final JsonSerializer _valueSerializer; + /** + * Value for annotated accessor. + */ + protected final JavaType _valueType; - protected final BeanProperty _property; + protected final boolean _staticTyping; /** * This is a flag that is set in rare (?) cases where this serializer @@ -58,9 +56,9 @@ public class JsonValueSerializer protected final boolean _forceTypeInformation; /* - /********************************************************** + /********************************************************************** /* Life-cycle - /********************************************************** + /********************************************************************** */ /** @@ -68,50 +66,43 @@ public class JsonValueSerializer * occurs if and only if the "value method" was annotated with * {@link com.fasterxml.jackson.databind.annotation.JsonSerialize#using}), otherwise * null - * - * @since 2.8 Earlier method took "raw" Method, but that does not work with access - * to information we need */ - @SuppressWarnings("unchecked") - public JsonValueSerializer(AnnotatedMember accessor, JsonSerializer ser) + public JsonValueSerializer(JavaType nominalType, + JavaType valueType, boolean staticTyping, + TypeSerializer vts, JsonSerializer ser, + AnnotatedMember accessor) { - super(accessor.getType()); + super(nominalType, null, vts, ser); + _valueType = valueType; + _staticTyping = staticTyping; _accessor = accessor; - _valueSerializer = (JsonSerializer) ser; - _property = null; _forceTypeInformation = true; // gets reconsidered when we are contextualized } - @SuppressWarnings("unchecked") - public JsonValueSerializer(JsonValueSerializer src, BeanProperty property, + protected JsonValueSerializer(JsonValueSerializer src, BeanProperty property, JsonSerializer ser, boolean forceTypeInfo) { - super(_notNullClass(src.handledType())); + super(src, property, src._valueTypeSerializer, ser); + _valueType = src._valueType; _accessor = src._accessor; - _valueSerializer = (JsonSerializer) ser; - _property = property; + _staticTyping = src._staticTyping; _forceTypeInformation = forceTypeInfo; } - @SuppressWarnings("unchecked") - private final static Class _notNullClass(Class cls) { - return (cls == null) ? Object.class : (Class) cls; - } - public JsonValueSerializer withResolved(BeanProperty property, JsonSerializer ser, boolean forceTypeInfo) { - if (_property == property && _valueSerializer == ser - && forceTypeInfo == _forceTypeInformation) { + if ((_property == property) && (_valueSerializer == ser) + && (forceTypeInfo == _forceTypeInformation)) { return this; } return new JsonValueSerializer(this, property, ser, forceTypeInfo); } /* - /********************************************************** + /********************************************************************** /* Post-processing - /********************************************************** + /********************************************************************** */ /** @@ -125,24 +116,24 @@ public JsonSerializer createContextual(SerializerProvider provider, { JsonSerializer ser = _valueSerializer; if (ser == null) { - /* Can only assign serializer statically if the declared type is final: - * if not, we don't really know the actual type until we get the instance. - */ + // Can only assign serializer statically if the declared type is final: + // if not, we don't really know the actual type until we get the instance. + // 10-Mar-2010, tatu: Except if static typing is to be used - JavaType t = _accessor.getType(); - if (provider.isEnabled(MapperFeature.USE_STATIC_TYPING) || t.isFinal()) { + if (_staticTyping || provider.isEnabled(MapperFeature.USE_STATIC_TYPING) + || _valueType.isFinal()) { // false -> no need to cache /* 10-Mar-2010, tatu: Ideally we would actually separate out type * serializer from value serializer; but, alas, there's no access * to serializer factory at this point... */ - // 05-Sep-2013, tatu: I _think_ this can be considered a primary property... - ser = provider.findPrimaryPropertySerializer(t, property); + // I _think_ this can be considered a primary property... + ser = provider.findPrimaryPropertySerializer(_valueType, 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 */ - boolean forceTypeInformation = isNaturalTypeWithStdHandling(t.getRawClass(), ser); + boolean forceTypeInformation = isNaturalTypeWithStdHandling(_valueType.getRawClass(), ser); return withResolved(property, ser, forceTypeInformation); } } else { @@ -152,86 +143,95 @@ public JsonSerializer createContextual(SerializerProvider provider, } return this; } - + /* - /********************************************************** + /********************************************************************** /* Actual serialization - /********************************************************** + /********************************************************************** */ - + @Override - public void serialize(Object bean, JsonGenerator gen, SerializerProvider prov) throws IOException + public void serialize(Object bean, JsonGenerator gen, SerializerProvider ctxt) throws IOException { + Object value; try { - Object value = _accessor.getValue(bean); - if (value == null) { - prov.defaultSerializeNull(gen); - return; - } - JsonSerializer ser = _valueSerializer; - if (ser == null) { - Class c = value.getClass(); - /* 10-Mar-2010, tatu: Ideally we would actually separate out type - * serializer from value serializer; but, alas, there's no access - * to serializer factory at this point... - */ - // let's cache it, may be needed soon again - ser = prov.findTypedValueSerializer(c, true, _property); - } - ser.serialize(value, gen, prov); + value = _accessor.getValue(bean); } catch (Exception e) { - wrapAndThrow(prov, e, bean, _accessor.getName() + "()"); + wrapAndThrow(ctxt, e, bean, _accessor.getName() + "()"); + return; // never gets here + } + if (value == null) { + ctxt.defaultSerializeNullValue(gen); + return; + } + JsonSerializer ser = _valueSerializer; + if (ser == null) { + Class cc = value.getClass(); + if (_valueType.hasGenericTypes()) { + ser = _findAndAddDynamic(ctxt, + ctxt.constructSpecializedType(_valueType, cc)); + } else { + ser = _findAndAddDynamic(ctxt, cc); + } + } + if (_valueTypeSerializer != null) { + ser.serializeWithType(value, gen, ctxt, _valueTypeSerializer); + } else { + ser.serialize(value, gen, ctxt); } } @Override - public void serializeWithType(Object bean, JsonGenerator gen, SerializerProvider provider, + public void serializeWithType(Object bean, JsonGenerator gen, SerializerProvider ctxt, TypeSerializer typeSer0) throws IOException { // Regardless of other parts, first need to find value to serialize: - Object value = null; + Object value; try { value = _accessor.getValue(bean); - // and if we got null, can also just write it directly - if (value == null) { - provider.defaultSerializeNull(gen); - return; - } - JsonSerializer ser = _valueSerializer; - if (ser == null) { // no serializer yet? Need to fetch - ser = provider.findValueSerializer(value.getClass(), _property); + } catch (Exception e) { + wrapAndThrow(ctxt, e, bean, _accessor.getName() + "()"); + return; // never gets here + } + // and if we got null, can also just write it directly + if (value == null) { + ctxt.defaultSerializeNullValue(gen); + return; + } + JsonSerializer ser = _valueSerializer; + if (ser == null) { + Class cc = value.getClass(); + if (_valueType.hasGenericTypes()) { + ser = _findAndAddDynamic(ctxt, ctxt.constructSpecializedType(_valueType, cc)); } else { - // 09-Dec-2010, tatu: To work around natural type's refusal to add type info, we do - // this (note: type is for the wrapper type, not enclosed value!) - if (_forceTypeInformation) { - // Confusing? Type id is for POJO and NOT for value returned by JsonValue accessor... - WritableTypeId typeIdDef = typeSer0.writeTypePrefix(gen, - typeSer0.typeId(bean, JsonToken.VALUE_STRING)); - ser.serialize(value, gen, provider); - typeSer0.writeTypeSuffix(gen, typeIdDef); - - return; - } + ser = _findAndAddDynamic(ctxt, cc); } - // 28-Sep-2016, tatu: As per [databind#1385], we do need to do some juggling - // to use different Object for type id (logical type) and actual serialization - // (delegat type). - TypeSerializerRerouter rr = new TypeSerializerRerouter(typeSer0, bean); - ser.serializeWithType(value, gen, provider, rr); - } catch (Exception e) { - wrapAndThrow(provider, e, bean, _accessor.getName() + "()"); } - } - - @SuppressWarnings("deprecation") - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) - throws JsonMappingException - { - if (_valueSerializer instanceof SchemaAware) { - return ((SchemaAware)_valueSerializer).getSchema(provider, null); + + // 16-Apr-2018, tatu: This is interesting piece of vestigal code but... + // I guess it is still needed, too. + + // 09-Dec-2010, tatu: To work around natural type's refusal to add type info, we do + // this (note: type is for the wrapper type, not enclosed value!) + if (_forceTypeInformation) { + // Confusing? Type id is for POJO and NOT for value returned by JsonValue accessor... + WritableTypeId typeIdDef = typeSer0.writeTypePrefix(gen, + typeSer0.typeId(bean, JsonToken.VALUE_STRING)); + ser.serialize(value, gen, ctxt); + typeSer0.writeTypeSuffix(gen, typeIdDef); + return; } - return com.fasterxml.jackson.databind.jsonschema.JsonSchema.getDefaultSchemaNode(); + + // 28-Sep-2016, tatu: As per [databind#1385], we do need to do some juggling + // to use different Object for type id (logical type) and actual serialization + // (delegate type). + + // 16-Apr-2018, tatu: What seems suspicious is that we do not use `_valueTypeSerializer` + // for anything but... it appears to work wrt existing tests, and alternative + // is not very clear. So most likely it'll fail at some point and require + // full investigation. But not today. + TypeSerializerRerouter rr = new TypeSerializerRerouter(typeSer0, bean); + ser.serializeWithType(value, gen, ctxt, rr); } @Override @@ -256,7 +256,7 @@ public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType t } JsonSerializer ser = _valueSerializer; if (ser == null) { - ser = visitor.getProvider().findTypedValueSerializer(type, false, _property); + ser = visitor.getProvider().findPrimaryPropertySerializer(type, _property); if (ser == null) { // can this ever occur? visitor.expectAnyFormat(typeHint); return; @@ -271,8 +271,6 @@ public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType t * * @return True if method handled callbacks; false if not; in latter case caller will * send default callbacks - * - * @since 2.6 */ protected boolean _acceptJsonFormatVisitorForEnum(JsonFormatVisitorWrapper visitor, JavaType typeHint, Class enumType) @@ -319,20 +317,9 @@ protected boolean isNaturalTypeWithStdHandling(Class rawType, JsonSerializer< } /* - /********************************************************** - /* Other methods - /********************************************************** - */ - - @Override - public String toString() { - return "(@JsonValue serializer for method " + _accessor.getDeclaringClass() + "#" + _accessor.getName() + ")"; - } - - /* - /********************************************************** - /* Helper class - /********************************************************** + /********************************************************************** + /* Helper class(es) + /********************************************************************** */ /** @@ -352,7 +339,8 @@ public TypeSerializerRerouter(TypeSerializer ts, Object ob) { } @Override - public TypeSerializer forProperty(BeanProperty prop) { // should never get called + public TypeSerializer forProperty(SerializerProvider ctxt, + BeanProperty prop) { // should never get called throw new UnsupportedOperationException(); } @@ -371,9 +359,7 @@ public TypeIdResolver getTypeIdResolver() { return _typeSerializer.getTypeIdResolver(); } - // // // New Write API, 2.9+ - - @Override // since 2.9 + @Override public WritableTypeId writeTypePrefix(JsonGenerator g, WritableTypeId typeId) throws IOException { // 28-Jun-2017, tatu: Important! Need to "override" value @@ -381,110 +367,11 @@ public WritableTypeId writeTypePrefix(JsonGenerator g, return _typeSerializer.writeTypePrefix(g, typeId); } - @Override // since 2.9 + @Override public WritableTypeId writeTypeSuffix(JsonGenerator g, WritableTypeId typeId) throws IOException { // NOTE: already overwrote value object so: return _typeSerializer.writeTypeSuffix(g, typeId); } - - // // // Old Write API, pre-2.9 - - @Override - @Deprecated - public void writeTypePrefixForScalar(Object value, JsonGenerator gen) throws IOException { - _typeSerializer.writeTypePrefixForScalar(_forObject, gen); - } - - @Override - @Deprecated - public void writeTypePrefixForObject(Object value, JsonGenerator gen) throws IOException { - _typeSerializer.writeTypePrefixForObject(_forObject, gen); - } - - @Override - @Deprecated - public void writeTypePrefixForArray(Object value, JsonGenerator gen) throws IOException { - _typeSerializer.writeTypePrefixForArray(_forObject, gen); - } - - @Override - @Deprecated - public void writeTypeSuffixForScalar(Object value, JsonGenerator gen) throws IOException { - _typeSerializer.writeTypeSuffixForScalar(_forObject, gen); - } - - @Override - @Deprecated - public void writeTypeSuffixForObject(Object value, JsonGenerator gen) throws IOException { - _typeSerializer.writeTypeSuffixForObject(_forObject, gen); - } - - @Override - @Deprecated - public void writeTypeSuffixForArray(Object value, JsonGenerator gen) throws IOException { - _typeSerializer.writeTypeSuffixForArray(_forObject, gen); - } - - @Override - @Deprecated - public void writeTypePrefixForScalar(Object value, JsonGenerator gen, Class type) throws IOException { - _typeSerializer.writeTypePrefixForScalar(_forObject, gen, type); - } - - @Override - @Deprecated - public void writeTypePrefixForObject(Object value, JsonGenerator gen, Class type) throws IOException { - _typeSerializer.writeTypePrefixForObject(_forObject, gen, type); - } - - @Override - @Deprecated - public void writeTypePrefixForArray(Object value, JsonGenerator gen, Class type) throws IOException { - _typeSerializer.writeTypePrefixForArray(_forObject, gen, type); - } - - /* - /********************************************************** - /* Deprecated methods (since 2.9) - /********************************************************** - */ - - @Override - @Deprecated - public void writeCustomTypePrefixForScalar(Object value, JsonGenerator gen, String typeId) - throws IOException { - _typeSerializer.writeCustomTypePrefixForScalar(_forObject, gen, typeId); - } - - @Override - @Deprecated - public void writeCustomTypePrefixForObject(Object value, JsonGenerator gen, String typeId) throws IOException { - _typeSerializer.writeCustomTypePrefixForObject(_forObject, gen, typeId); - } - - @Override - @Deprecated - public void writeCustomTypePrefixForArray(Object value, JsonGenerator gen, String typeId) throws IOException { - _typeSerializer.writeCustomTypePrefixForArray(_forObject, gen, typeId); - } - - @Override - @Deprecated - public void writeCustomTypeSuffixForScalar(Object value, JsonGenerator gen, String typeId) throws IOException { - _typeSerializer.writeCustomTypeSuffixForScalar(_forObject, gen, typeId); - } - - @Override - @Deprecated - public void writeCustomTypeSuffixForObject(Object value, JsonGenerator gen, String typeId) throws IOException { - _typeSerializer.writeCustomTypeSuffixForObject(_forObject, gen, typeId); - } - - @Override - @Deprecated - public void writeCustomTypeSuffixForArray(Object value, JsonGenerator gen, String typeId) throws IOException { - _typeSerializer.writeCustomTypeSuffixForArray(_forObject, gen, typeId); - } } } 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 c6ee24725c..ed65055744 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 @@ -9,7 +9,6 @@ import com.fasterxml.jackson.databind.introspect.AnnotatedMember; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; -import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.ser.PropertyWriter; /** @@ -42,8 +41,6 @@ public MapProperty(TypeSerializer typeSer, BeanProperty prop) /** * Initialization method that needs to be called before passing * property to filter. - * - * @since 2.9 */ public void reset(Object key, Object value, JsonSerializer keySer, JsonSerializer valueSer) @@ -54,13 +51,6 @@ public void reset(Object key, Object value, _valueSerializer = valueSer; } - @Deprecated // since 2.9 - public void reset(Object key, - JsonSerializer keySer, JsonSerializer valueSer) - { - reset(key, _value, keySer, valueSer); - } - @Override public String getName() { if (_key instanceof String) { @@ -151,13 +141,6 @@ public void depositSchemaProperty(JsonObjectFormatVisitor objectVisitor, _property.depositSchemaProperty(objectVisitor, provider); } - @Override - @Deprecated - public void depositSchemaProperty(ObjectNode propertiesNode, - SerializerProvider provider) throws JsonMappingException { - // nothing to do here - } - @Override public JavaType getType() { return _property.getType(); 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 b3a2e62daa..1e76a6b318 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 @@ -1,14 +1,15 @@ package com.fasterxml.jackson.databind.ser.std; import java.io.IOException; -import java.lang.reflect.Type; import java.util.*; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; + import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.type.WritableTypeId; + import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; import com.fasterxml.jackson.databind.introspect.AnnotatedMember; @@ -16,9 +17,7 @@ import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonMapFormatVisitor; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; import com.fasterxml.jackson.databind.ser.ContainerSerializer; -import com.fasterxml.jackson.databind.ser.ContextualSerializer; 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; import com.fasterxml.jackson.databind.util.BeanUtil; @@ -33,27 +32,18 @@ @JacksonStdImpl public class MapSerializer extends ContainerSerializer> - implements ContextualSerializer { private static final long serialVersionUID = 1L; protected final static JavaType UNSPECIFIED_TYPE = TypeFactory.unknownType(); - /** - * @since 2.9 - */ public final static Object MARKER_FOR_EMPTY = JsonInclude.Include.NON_EMPTY; /* - /********************************************************** + /********************************************************************** /* Basic information about referring property, type - /********************************************************** + /********************************************************************** */ - - /** - * Map-valued property being serialized with this instance - */ - protected final BeanProperty _property; /** * Whether static types should be used for serialization of values @@ -72,9 +62,9 @@ public class MapSerializer protected final JavaType _valueType; /* - /********************************************************** + /********************************************************************** /* Serializers used - /********************************************************** + /********************************************************************** */ /** @@ -92,16 +82,10 @@ public class MapSerializer */ protected final TypeSerializer _valueTypeSerializer; - /** - * If value type cannot be statically determined, mapping from - * runtime value types to serializers are stored in this object. - */ - protected PropertySerializerMap _dynamicValueSerializers; - /* - /********************************************************** + /********************************************************************** /* Config settings, filtering - /********************************************************** + /********************************************************************** */ /** @@ -111,8 +95,6 @@ public class MapSerializer /** * Id of the property filter to use, if any; null if none. - * - * @since 2.3 */ protected final Object _filterId; @@ -123,49 +105,40 @@ public class MapSerializer * non-null values. * Note that inclusion value for Map instance itself is handled by caller (POJO * property that refers to the Map value). - * - * @since 2.5 */ protected final Object _suppressableValue; /** * Flag that indicates what to do with `null` values, distinct from * handling of {@link #_suppressableValue} - * - * @since 2.9 */ protected final boolean _suppressNulls; /* - /********************************************************** + /********************************************************************** /* Config settings, other - /********************************************************** + /********************************************************************** */ /** * Flag set if output is forced to be sorted by keys (usually due * to annotation). - * - * @since 2.4 */ protected final boolean _sortKeys; /* - /********************************************************** + /********************************************************************** /* Life-cycle - /********************************************************** - */ - - /** - * @since 2.5 + /********************************************************************** */ + @SuppressWarnings("unchecked") protected MapSerializer(Set ignoredEntries, JavaType keyType, JavaType valueType, boolean valueTypeIsStatic, TypeSerializer vts, JsonSerializer keySerializer, JsonSerializer valueSerializer) { - super(Map.class, false); + super(Map.class); _ignoredEntries = ((ignoredEntries == null) || ignoredEntries.isEmpty()) ? null : ignoredEntries; _keyType = keyType; @@ -174,8 +147,6 @@ protected MapSerializer(Set ignoredEntries, _valueTypeSerializer = vts; _keySerializer = (JsonSerializer) keySerializer; _valueSerializer = (JsonSerializer) valueSerializer; - _dynamicValueSerializers = PropertySerializerMap.emptyForProperties(); - _property = null; _filterId = null; _sortKeys = false; _suppressableValue = null; @@ -187,7 +158,7 @@ protected MapSerializer(MapSerializer src, BeanProperty property, JsonSerializer keySerializer, JsonSerializer valueSerializer, Set ignoredEntries) { - super(Map.class, false); + super(src, property); _ignoredEntries = ((ignoredEntries == null) || ignoredEntries.isEmpty()) ? null : ignoredEntries; _keyType = src._keyType; @@ -196,22 +167,16 @@ protected MapSerializer(MapSerializer src, BeanProperty property, _valueTypeSerializer = src._valueTypeSerializer; _keySerializer = (JsonSerializer) keySerializer; _valueSerializer = (JsonSerializer) valueSerializer; - // [databind#2181]: may not be safe to reuse, start from empty - _dynamicValueSerializers = PropertySerializerMap.emptyForProperties(); - _property = property; _filterId = src._filterId; _sortKeys = src._sortKeys; _suppressableValue = src._suppressableValue; _suppressNulls = src._suppressNulls; } - /** - * @since 2.9 - */ - protected MapSerializer(MapSerializer src, TypeSerializer vts, - Object suppressableValue, boolean suppressNulls) + protected MapSerializer(MapSerializer src, + TypeSerializer vts, Object suppressableValue, boolean suppressNulls) { - super(Map.class, false); + super(src); _ignoredEntries = src._ignoredEntries; _keyType = src._keyType; _valueType = src._valueType; @@ -219,10 +184,6 @@ protected MapSerializer(MapSerializer src, TypeSerializer vts, _valueTypeSerializer = vts; _keySerializer = src._keySerializer; _valueSerializer = src._valueSerializer; - // 22-Nov-2018, tatu: probably safe (even with [databind#2181]) since it's just - // inclusion, type serializer but NOT serializer - _dynamicValueSerializers = src._dynamicValueSerializers; - _property = src._property; _filterId = src._filterId; _sortKeys = src._sortKeys; _suppressableValue = suppressableValue; @@ -231,7 +192,7 @@ protected MapSerializer(MapSerializer src, TypeSerializer vts, protected MapSerializer(MapSerializer src, Object filterId, boolean sortKeys) { - super(Map.class, false); + super(src); _ignoredEntries = src._ignoredEntries; _keyType = src._keyType; _valueType = src._valueType; @@ -239,9 +200,6 @@ protected MapSerializer(MapSerializer src, Object filterId, boolean sortKeys) _valueTypeSerializer = src._valueTypeSerializer; _keySerializer = src._keySerializer; _valueSerializer = src._valueSerializer; - // [databind#2181]: may not be safe to reuse, start from empty - _dynamicValueSerializers = PropertySerializerMap.emptyForProperties(); - _property = src._property; _filterId = filterId; _sortKeys = sortKeys; _suppressableValue = src._suppressableValue; @@ -257,9 +215,6 @@ public MapSerializer _withValueTypeSerializer(TypeSerializer vts) { return new MapSerializer(this, vts, _suppressableValue, _suppressNulls); } - /** - * @since 2.4 - */ public MapSerializer withResolved(BeanProperty property, JsonSerializer keySerializer, JsonSerializer valueSerializer, Set ignored, boolean sortKeys) @@ -284,8 +239,6 @@ public MapSerializer withFilterId(Object filterId) { /** * Mutant factory for constructing an instance with different inclusion strategy * for content (Map values). - * - * @since 2.9 */ public MapSerializer withContentInclusion(Object suppressableValue, boolean suppressNulls) { if ((suppressableValue == _suppressableValue) && (suppressNulls == _suppressNulls)) { @@ -295,9 +248,6 @@ public MapSerializer withContentInclusion(Object suppressableValue, boolean supp return new MapSerializer(this, _valueTypeSerializer, suppressableValue, suppressNulls); } - /** - * @since 2.8 - */ public static MapSerializer construct(Set ignoredEntries, JavaType mapType, boolean staticValueType, TypeSerializer vts, JsonSerializer keySerializer, JsonSerializer valueSerializer, @@ -328,66 +278,14 @@ public static MapSerializer construct(Set ignoredEntries, JavaType mapTy return ser; } - /** - * @since 2.9 - */ protected void _ensureOverride(String method) { ClassUtil.verifyMustOverride(MapSerializer.class, this, method); } - /** - * @since 2.5 - */ - @Deprecated // since 2.9 - protected void _ensureOverride() { - _ensureOverride("N/A"); - } - /* - /********************************************************** - /* Deprecated creators - /********************************************************** - */ - - /** - * @since 2.5 - * @deprecated // since 2.9 - */ - @Deprecated // since 2.9 - protected MapSerializer(MapSerializer src, TypeSerializer vts, - Object suppressableValue) - { - this(src, vts, suppressableValue, false); - } - - /** - * @deprecated since 2.9 - */ - @Deprecated // since 2.9 - public MapSerializer withContentInclusion(Object suppressableValue) { - return new MapSerializer(this, _valueTypeSerializer, suppressableValue, _suppressNulls); - } - - /** - * @since 2.3 - * - * @deprecated Since 2.8 use the other overload - */ - @Deprecated // since 2.8 - public static MapSerializer construct(String[] ignoredList, JavaType mapType, - boolean staticValueType, TypeSerializer vts, - JsonSerializer keySerializer, JsonSerializer valueSerializer, - Object filterId) - { - Set ignoredEntries = ArrayBuilders.arrayToSet(ignoredList); - return construct(ignoredEntries, mapType, staticValueType, vts, - keySerializer, valueSerializer, filterId); - } - - /* - /********************************************************** + /********************************************************************** /* Post-processing (contextualization) - /********************************************************** + /********************************************************************** */ @Override @@ -402,14 +300,10 @@ public JsonSerializer createContextual(SerializerProvider provider, // First: if we have a property, may have property-annotation overrides if (_neitherNull(propertyAcc, intr)) { - 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); - } + keySer = provider.serializerInstance(propertyAcc, + intr.findKeySerializer(provider.getConfig(), propertyAcc)); + ser = provider.serializerInstance(propertyAcc, + intr.findContentSerializer(provider.getConfig(), propertyAcc)); } if (ser == null) { ser = _valueSerializer; @@ -421,7 +315,7 @@ public JsonSerializer createContextual(SerializerProvider provider, // 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.isJavaLangObject()) { - ser = provider.findValueSerializer(_valueType, property); + ser = provider.findSecondaryPropertySerializer(_valueType, property); } } if (keySer == null) { @@ -451,7 +345,7 @@ public JsonSerializer createContextual(SerializerProvider provider, JsonFormat.Value format = findFormatOverrides(provider, property, Map.class); if (format != null) { Boolean B = format.getFeature(JsonFormat.Feature.WRITE_SORTED_MAP_ENTRIES); - if (B != null) { + if (B != null) { // do NOT override if not defined sortKeys = B.booleanValue(); } } @@ -459,9 +353,8 @@ public JsonSerializer createContextual(SerializerProvider provider, // [databind#307]: allow filtering if (property != null) { - AnnotatedMember m = property.getMember(); - if (m != null) { - Object filterId = intr.findFilterId(m); + if (propertyAcc != null) { + Object filterId = intr.findFilterId(propertyAcc); if (filterId != null) { mser = mser.withFilterId(filterId); } @@ -519,9 +412,9 @@ public JsonSerializer createContextual(SerializerProvider provider, } /* - /********************************************************** + /********************************************************************** /* Accessors - /********************************************************** + /********************************************************************** */ @Override @@ -535,7 +428,7 @@ public JsonSerializer getContentSerializer() { } @Override - public boolean isEmpty(SerializerProvider prov, Map value) + public boolean isEmpty(SerializerProvider prov, Map value) throws IOException { if (value.isEmpty()) { return true; @@ -598,9 +491,9 @@ public boolean hasSingleElement(Map value) { } /* - /********************************************************** + /********************************************************************** /* Extended API - /********************************************************** + /********************************************************************** */ /** @@ -610,17 +503,15 @@ public boolean hasSingleElement(Map value) { * (which can be overridden by custom implementations), but for some * dynamic types, it is possible that serializer is only resolved * during actual serialization. - * - * @since 2.0 */ public JsonSerializer getKeySerializer() { return _keySerializer; } - + /* - /********************************************************** + /********************************************************************** /* JsonSerializer implementation - /********************************************************** + /********************************************************************** */ @Override @@ -674,9 +565,9 @@ public void serializeWithType(Map value, JsonGenerator gen, SerializerProvi } /* - /********************************************************** + /********************************************************************** /* Secondary serialization methods - /********************************************************** + /********************************************************************** */ /** @@ -712,7 +603,7 @@ public void serializeFields(Map value, JsonGenerator gen, SerializerProvide } // And then value if (valueElem == null) { - provider.defaultSerializeNull(gen); + provider.defaultSerializeNullValue(gen); continue; } JsonSerializer serializer = _valueSerializer; @@ -810,7 +701,7 @@ public void serializeFieldsUsing(Map value, JsonGenerator gen, SerializerPr } final Object valueElem = entry.getValue(); if (valueElem == null) { - provider.defaultSerializeNull(gen); + provider.defaultSerializeNullValue(gen); } else { try { if (typeSer == null) { @@ -828,8 +719,6 @@ public void serializeFieldsUsing(Map value, JsonGenerator gen, SerializerPr /** * Helper method used when we have a JSON Filter to use for potentially * filtering out Map entries. - * - * @since 2.5 */ public void serializeFilteredFields(Map value, JsonGenerator gen, SerializerProvider provider, PropertyFilter filter, @@ -887,9 +776,6 @@ public void serializeFilteredFields(Map value, JsonGenerator gen, Serialize } } - /** - * @since 2.5 - */ public void serializeTypedFields(Map value, JsonGenerator gen, SerializerProvider provider, Object suppressableValue) // since 2.5 throws IOException @@ -946,8 +832,6 @@ public void serializeTypedFields(Map value, JsonGenerator gen, SerializerPr * "any properties" of a POJO. * * @param bean Enclosing POJO that has any-getter used to obtain "any properties" - * - * @since 2.9 */ public void serializeFilteredAnyProperties(SerializerProvider provider, JsonGenerator gen, Object bean, Map value, PropertyFilter filter, @@ -1006,19 +890,11 @@ public void serializeFilteredAnyProperties(SerializerProvider provider, JsonGene } /* - /********************************************************** + /********************************************************************** /* Schema related functionality - /********************************************************** + /********************************************************************** */ - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) - { - // even though it's possible to statically determine the "value" type of the map, - // there's no way to statically determine the keys, so the "Entries" can't be determined. - return createSchemaNode("object", true); - } - @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException @@ -1028,40 +904,18 @@ public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType t v2.keyFormat(_keySerializer, _keyType); JsonSerializer valueSer = _valueSerializer; if (valueSer == null) { - valueSer = _findAndAddDynamic(_dynamicValueSerializers, - _valueType, visitor.getProvider()); + valueSer = _findAndAddDynamic(visitor.getProvider(), _valueType); } v2.valueFormat(valueSer, _valueType); } } /* - /********************************************************** + /********************************************************************** /* Internal helper methods - /********************************************************** + /********************************************************************** */ - protected final JsonSerializer _findAndAddDynamic(PropertySerializerMap map, - Class type, SerializerProvider provider) throws JsonMappingException - { - PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSecondarySerializer(type, provider, _property); - // did we get a new map of serializers? If so, start using it - if (map != result.map) { - _dynamicValueSerializers = result.map; - } - return result.serializer; - } - - protected final JsonSerializer _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; - } - protected Map _orderEntries(Map input, JsonGenerator gen, SerializerProvider provider) throws IOException { @@ -1088,9 +942,6 @@ protected Map _orderEntries(Map input, JsonGenerator gen, return new TreeMap(input); } - /** - * @since 2.8.7 - */ protected boolean _hasNullKey(Map input) { // 19-Feb-2017, tatu: As per [databind#1513] there are many cases where `null` // keys are not allowed, and even attempt to check for presence can cause @@ -1104,23 +955,23 @@ protected boolean _hasNullKey(Map input) { return (input instanceof HashMap) && input.containsKey(null); } - protected void _writeNullKeyedEntry(JsonGenerator gen, SerializerProvider provider, + protected void _writeNullKeyedEntry(JsonGenerator g, SerializerProvider ctxt, Object value) throws IOException { - JsonSerializer keySerializer = provider.findNullKeySerializer(_keyType, _property); + JsonSerializer keySerializer = ctxt.findNullKeySerializer(_keyType, _property); JsonSerializer valueSer; if (value == null) { if (_suppressNulls) { return; } - valueSer = provider.getDefaultNullValueSerializer(); + valueSer = ctxt.getDefaultNullValueSerializer(); } else { valueSer = _valueSerializer; if (valueSer == null) { - valueSer = _findSerializer(provider, value); + valueSer = _findSerializer(ctxt, value); } if (_suppressableValue == MARKER_FOR_EMPTY) { - if (valueSer.isEmpty(provider, value)) { + if (valueSer.isEmpty(ctxt, value)) { return; } } else if ((_suppressableValue != null) @@ -1130,14 +981,14 @@ protected void _writeNullKeyedEntry(JsonGenerator gen, SerializerProvider provid } try { - keySerializer.serialize(null, gen, provider); - valueSer.serialize(value, gen, provider); + keySerializer.serialize(null, g, ctxt); + valueSer.serialize(value, g, ctxt); } catch (Exception e) { - wrapAndThrow(provider, e, value, ""); + wrapAndThrow(ctxt, e, value, ""); } } - private final JsonSerializer _findSerializer(SerializerProvider provider, + private final JsonSerializer _findSerializer(SerializerProvider ctxt, Object value) throws JsonMappingException { final Class cc = value.getClass(); @@ -1146,9 +997,9 @@ private final JsonSerializer _findSerializer(SerializerProvider provider return valueSer; } if (_valueType.hasGenericTypes()) { - return _findAndAddDynamic(_dynamicValueSerializers, - provider.constructSpecializedType(_valueType, cc), provider); + return _findAndAddDynamic(ctxt, + ctxt.constructSpecializedType(_valueType, cc)); } - return _findAndAddDynamic(_dynamicValueSerializers, cc, provider); + return _findAndAddDynamic(ctxt, cc); } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/NonTypedScalarSerializerBase.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/NonTypedScalarSerializerBase.java deleted file mode 100644 index 04b1cd5c21..0000000000 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/NonTypedScalarSerializerBase.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.fasterxml.jackson.databind.ser.std; - -import java.io.IOException; - -import com.fasterxml.jackson.core.*; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.jsontype.TypeSerializer; - -/** - * Intermediate base class for limited number of scalar types - * that should never include type information. These are "native" - * types that are default mappings for corresponding JSON scalar - * types: {@link java.lang.String}, {@link java.lang.Integer}, - * {@link java.lang.Double} and {@link java.lang.Boolean}. - */ -@SuppressWarnings("serial") -@Deprecated // since 2.9 -public abstract class NonTypedScalarSerializerBase - extends StdScalarSerializer -{ - protected NonTypedScalarSerializerBase(Class t) { - super(t); - } - - protected NonTypedScalarSerializerBase(Class t, boolean bogus) { - super(t, bogus); - } - - @Override - public final void serializeWithType(T value, JsonGenerator gen, SerializerProvider provider, - TypeSerializer typeSer) throws IOException - { - // no type info, just regular serialization - serialize(value, gen, provider); - } -} diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/NullSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/NullSerializer.java index e7e8a2f234..db340efb2a 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/NullSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/NullSerializer.java @@ -1,6 +1,5 @@ package com.fasterxml.jackson.databind.ser.std; -import java.lang.reflect.Type; import java.io.IOException; import com.fasterxml.jackson.core.*; @@ -43,12 +42,7 @@ public void serializeWithType(Object value, JsonGenerator gen, SerializerProvide { gen.writeNull(); } - - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException { - return createSchemaNode("null"); - } - + @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { visitor.expectNullFormat(typeHint); diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializer.java index 1180798362..b0426a6b9d 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializer.java @@ -1,7 +1,6 @@ package com.fasterxml.jackson.databind.ser.std; import java.io.IOException; -import java.lang.reflect.Type; import java.math.BigDecimal; import java.math.BigInteger; @@ -11,7 +10,6 @@ import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; -import com.fasterxml.jackson.databind.ser.ContextualSerializer; /** * As a fallback, we may need to use this serializer for other @@ -22,7 +20,6 @@ @SuppressWarnings("serial") public class NumberSerializer extends StdScalarSerializer - implements ContextualSerializer { /** * Static instance that is only to be used for {@link java.lang.Number}. @@ -80,11 +77,6 @@ public void serialize(Number value, JsonGenerator g, SerializerProvider provider } } - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) { - return createSchemaNode(_isInt ? "integer" : "number", true); - } - @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializers.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializers.java index 95a6aec2f7..314ed38bc1 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializers.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializers.java @@ -1,7 +1,6 @@ package com.fasterxml.jackson.databind.ser.std; import java.io.IOException; -import java.lang.reflect.Type; import java.util.Map; import com.fasterxml.jackson.annotation.JsonFormat; @@ -12,11 +11,10 @@ import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; -import com.fasterxml.jackson.databind.ser.ContextualSerializer; /** * Container class for serializers used for handling standard JDK-provided - * primitve number types and their wrapper counterparts (like {@link java.lang.Integer}). + * types. */ @SuppressWarnings("serial") public class NumberSerializers { @@ -47,17 +45,16 @@ public static void addAll(Map> allDeserializers) { */ /** - * Shared base class for actual primitive/wrapper number serializers. - * Note that this class is not meant as general-purpose base class nor - * is it part of public API: you may extend it with the caveat that not - * being part of public API its implementation and interfaces may change - * in minor releases; however deprecation markers will be used to allow - * code evolution. + * Base class for actual primitive/wrapper value serializers. *

    - * NOTE: {@code public} since 2.10: previously had {@code protected} access. + * NOTE: while you can extend this class yourself it is not designed as + * an extension point, and as such is not part of public API. This means that + * the compatibility across minor versions is only guaranteed on minor-to-minor + * basis, and class methods may be changed and/or removed via deprecation + * mechanism. Intent is, however, to allow for gradual upgrading so that methods + * to remove are marked deprecated for at least one minor version. */ public abstract static class Base extends StdScalarSerializer - implements ContextualSerializer { protected final JsonParser.NumberType _numberType; protected final String _schemaType; @@ -73,11 +70,6 @@ protected Base(Class cls, JsonParser.NumberType numberType, || (numberType == JsonParser.NumberType.BIG_INTEGER); } - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) { - return createSchemaNode(_schemaType, true); - } - @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException @@ -130,8 +122,8 @@ public void serialize(Object value, JsonGenerator gen, * This is the special serializer for regular {@link java.lang.Integer}s * (and primitive ints) *

    - * Since this is one of "natural" types, no type information is ever included - * on serialization (unlike for most scalar types, except for {@code double}. + * Since this is one of "native" types, no type information is ever included + * on serialization (unlike for most scalar types) *

    * NOTE: as of 2.6, generic signature changed to Object, to avoid generation * of bridge methods. @@ -211,7 +203,7 @@ public void serialize(Object value, JsonGenerator gen, * primitive doubles) *

    * Since this is one of "native" types, no type information is ever included - * on serialization (unlike for most scalar types other than {@code long}). + * on serialization (unlike for most scalar types as of 1.5) */ @JacksonStdImpl public static class DoubleSerializer extends Base { 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 a113ae2a91..1cb2e4b3b8 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 @@ -13,8 +13,6 @@ import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; import com.fasterxml.jackson.databind.ser.ContainerSerializer; -import com.fasterxml.jackson.databind.ser.ContextualSerializer; -import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap; /** * Generic serializer for Object arrays (Object[]). @@ -23,7 +21,6 @@ @SuppressWarnings("serial") public class ObjectArraySerializer extends ArraySerializerBase - implements ContextualSerializer { /** * Whether we are using static typing (using declared types, ignoring @@ -46,18 +43,12 @@ public class ObjectArraySerializer */ protected JsonSerializer _elementSerializer; - /** - * If element type cannot be statically determined, mapping from - * runtime type to serializer is handled using this object - */ - protected PropertySerializerMap _dynamicSerializers; - /* - /********************************************************** + /********************************************************************** /* Life-cycle - /********************************************************** + /********************************************************************** */ - + public ObjectArraySerializer(JavaType elemType, boolean staticTyping, TypeSerializer vts, JsonSerializer elementSerializer) { @@ -65,7 +56,6 @@ public ObjectArraySerializer(JavaType elemType, boolean staticTyping, _elementType = elemType; _staticTyping = staticTyping; _valueTypeSerializer = vts; - _dynamicSerializers = PropertySerializerMap.emptyForProperties(); _elementSerializer = elementSerializer; } @@ -75,9 +65,6 @@ public ObjectArraySerializer(ObjectArraySerializer src, TypeSerializer vts) _elementType = src._elementType; _valueTypeSerializer = vts; _staticTyping = src._staticTyping; - // 22-Nov-2018, tatu: probably safe (even with [databind#2181]) since it's just - // inclusion, type serializer but NOT serializer - _dynamicSerializers = src._dynamicSerializers; _elementSerializer = src._elementSerializer; } @@ -90,8 +77,6 @@ public ObjectArraySerializer(ObjectArraySerializer src, _elementType = src._elementType; _valueTypeSerializer = vts; _staticTyping = src._staticTyping; - // [databind#2181]: may not be safe to reuse, start from empty - _dynamicSerializers = PropertySerializerMap.emptyForProperties(); _elementSerializer = (JsonSerializer) elementSerializer; } @@ -116,19 +101,19 @@ public ObjectArraySerializer withResolved(BeanProperty prop, } /* - /********************************************************** + /********************************************************************** /* Post-processing - /********************************************************** + /********************************************************************** */ @Override - public JsonSerializer createContextual(SerializerProvider serializers, + public JsonSerializer createContextual(SerializerProvider ctxt, BeanProperty property) throws JsonMappingException { TypeSerializer vts = _valueTypeSerializer; - if (vts != null) { - vts = vts.forProperty(property); + if (vts != null) { // need to contextualize + vts = vts.forProperty(ctxt, property); } JsonSerializer ser = null; Boolean unwrapSingle = null; @@ -136,15 +121,13 @@ public JsonSerializer createContextual(SerializerProvider serializers, // First: if we have a property, may have property-annotation overrides if (property != null) { AnnotatedMember m = property.getMember(); - final AnnotationIntrospector intr = serializers.getAnnotationIntrospector(); + final AnnotationIntrospector intr = ctxt.getAnnotationIntrospector(); if (m != null) { - Object serDef = intr.findContentSerializer(m); - if (serDef != null) { - ser = serializers.serializerInstance(m, serDef); - } + ser = ctxt.serializerInstance(m, + intr.findContentSerializer(ctxt.getConfig(), m)); } } - JsonFormat.Value format = findFormatOverrides(serializers, property, handledType()); + JsonFormat.Value format = findFormatOverrides(ctxt, property, handledType()); if (format != null) { unwrapSingle = format.getFeature(JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED); } @@ -152,13 +135,13 @@ public JsonSerializer createContextual(SerializerProvider serializers, ser = _elementSerializer; } // [databind#124]: May have a content converter - ser = findContextualConvertingSerializer(serializers, property, ser); + ser = findContextualConvertingSerializer(ctxt, property, ser); if (ser == null) { // 30-Sep-2012, tatu: One more thing -- if explicit content type is annotated, // we can consider it a static case as well. if (_elementType != null) { if (_staticTyping && !_elementType.isJavaLangObject()) { - ser = serializers.findValueSerializer(_elementType, property); + ser = ctxt.findSecondaryPropertySerializer(_elementType, property); } } } @@ -166,9 +149,9 @@ public JsonSerializer createContextual(SerializerProvider serializers, } /* - /********************************************************** + /********************************************************************** /* Accessors - /********************************************************** + /********************************************************************** */ @Override @@ -192,71 +175,70 @@ public boolean hasSingleElement(Object[] value) { } /* - /********************************************************** + /********************************************************************** /* Actual serialization - /********************************************************** + /********************************************************************** */ @Override - public final void serialize(Object[] value, JsonGenerator gen, SerializerProvider provider) throws IOException + public final void serialize(Object[] value, JsonGenerator g, SerializerProvider ctxt) throws IOException { final int len = value.length; if (len == 1) { if (((_unwrapSingle == null) && - provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) + ctxt.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) || (_unwrapSingle == Boolean.TRUE)) { - serializeContents(value, gen, provider); + serializeContents(value, g, ctxt); return; } } - gen.writeStartArray(len); - serializeContents(value, gen, provider); - gen.writeEndArray(); + g.writeStartArray(value, len); + serializeContents(value, g, ctxt); + g.writeEndArray(); } @Override - public void serializeContents(Object[] value, JsonGenerator gen, SerializerProvider provider) throws IOException + public void serializeContents(Object[] value, JsonGenerator g, SerializerProvider ctxt) throws IOException { final int len = value.length; if (len == 0) { return; } if (_elementSerializer != null) { - serializeContentsUsing(value, gen, provider, _elementSerializer); + serializeContentsUsing(value, g, ctxt, _elementSerializer); return; } if (_valueTypeSerializer != null) { - serializeTypedContents(value, gen, provider); + serializeTypedContents(value, g, ctxt); return; } int i = 0; Object elem = null; try { - PropertySerializerMap serializers = _dynamicSerializers; for (; i < len; ++i) { elem = value[i]; if (elem == null) { - provider.defaultSerializeNull(gen); + ctxt.defaultSerializeNullValue(g); continue; } Class cc = elem.getClass(); - JsonSerializer serializer = serializers.serializerFor(cc); + JsonSerializer serializer = _dynamicValueSerializers.serializerFor(cc); if (serializer == null) { if (_elementType.hasGenericTypes()) { - serializer = _findAndAddDynamic(serializers, - provider.constructSpecializedType(_elementType, cc), provider); + serializer = _findAndAddDynamic(ctxt, + ctxt.constructSpecializedType(_elementType, cc)); } else { - serializer = _findAndAddDynamic(serializers, cc, provider); + serializer = _findAndAddDynamic(ctxt, cc); } } - serializer.serialize(elem, gen, provider); + serializer.serialize(elem, g, ctxt); } } catch (Exception e) { - wrapAndThrow(provider, e, elem, i); + wrapAndThrow(ctxt, e, elem, i); } } - public void serializeContentsUsing(Object[] value, JsonGenerator jgen, SerializerProvider provider, + public void serializeContentsUsing(Object[] value, JsonGenerator g, SerializerProvider provider, JsonSerializer ser) throws IOException { final int len = value.length; @@ -268,13 +250,13 @@ public void serializeContentsUsing(Object[] value, JsonGenerator jgen, Serialize for (; i < len; ++i) { elem = value[i]; if (elem == null) { - provider.defaultSerializeNull(jgen); + provider.defaultSerializeNullValue(g); continue; } if (typeSer == null) { - ser.serialize(elem, jgen, provider); + ser.serialize(elem, g, provider); } else { - ser.serializeWithType(elem, jgen, provider, typeSer); + ser.serializeWithType(elem, g, provider, typeSer); } } } catch (Exception e) { @@ -282,29 +264,28 @@ public void serializeContentsUsing(Object[] value, JsonGenerator jgen, Serialize } } - public void serializeTypedContents(Object[] value, JsonGenerator jgen, SerializerProvider provider) throws IOException + public void serializeTypedContents(Object[] value, JsonGenerator g, SerializerProvider ctxt) throws IOException { final int len = value.length; final TypeSerializer typeSer = _valueTypeSerializer; int i = 0; Object elem = null; try { - PropertySerializerMap serializers = _dynamicSerializers; for (; i < len; ++i) { elem = value[i]; if (elem == null) { - provider.defaultSerializeNull(jgen); + ctxt.defaultSerializeNullValue(g); continue; } Class cc = elem.getClass(); - JsonSerializer serializer = serializers.serializerFor(cc); + JsonSerializer serializer = _dynamicValueSerializers.serializerFor(cc); if (serializer == null) { - serializer = _findAndAddDynamic(serializers, cc, provider); + serializer = _findAndAddDynamic(ctxt, cc); } - serializer.serializeWithType(elem, jgen, provider, typeSer); + serializer.serializeWithType(elem, g, ctxt, typeSer); } } catch (Exception e) { - wrapAndThrow(provider, e, elem, i); + wrapAndThrow(ctxt, e, elem, i); } } @@ -315,43 +296,11 @@ public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType t JsonArrayFormatVisitor arrayVisitor = visitor.expectArrayFormat(typeHint); if (arrayVisitor != null) { JavaType contentType = _elementType; - - // [databind#1793]: Was getting `null` for `typeHint`. But why would we even use it... -/* - TypeFactory tf = visitor.getProvider().getTypeFactory(); - contentType = tf.moreSpecificType(_elementType, typeHint.getContentType()); - if (contentType == null) { - visitor.getProvider().reportBadDefinition(_elementType, - "Could not resolve type: "+_elementType); - } -*/ JsonSerializer valueSer = _elementSerializer; if (valueSer == null) { - valueSer = visitor.getProvider().findValueSerializer(contentType, _property); + valueSer = visitor.getProvider().findSecondaryPropertySerializer(contentType, _property); } arrayVisitor.itemsFormat(valueSer, contentType); } } - - protected final JsonSerializer _findAndAddDynamic(PropertySerializerMap map, - Class type, SerializerProvider provider) throws JsonMappingException - { - PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSecondarySerializer(type, provider, _property); - // did we get a new map of serializers? If so, start using it - if (map != result.map) { - _dynamicSerializers = result.map; - } - return result.serializer; - } - - protected final JsonSerializer _findAndAddDynamic(PropertySerializerMap map, - JavaType type, SerializerProvider provider) throws JsonMappingException - { - PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSecondarySerializer(type, provider, _property); - // did we get a new map of serializers? If so, start using it - if (map != result.map) { - _dynamicSerializers = result.map; - } - return result.serializer; - } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/RawSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/RawSerializer.java index bed8e7a998..cd5aa8a33c 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/RawSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/RawSerializer.java @@ -1,6 +1,5 @@ package com.fasterxml.jackson.databind.ser.std; -import java.lang.reflect.Type; import java.io.IOException; import com.fasterxml.jackson.core.*; @@ -23,7 +22,7 @@ public class RawSerializer * just take wild card and coerce type. */ public RawSerializer(Class cls) { - super(cls, false); + super(cls); } @Override @@ -41,14 +40,7 @@ public void serializeWithType(T value, JsonGenerator g, SerializerProvider provi serialize(value, g, provider); typeSer.writeTypeSuffix(g, typeIdDef); } - - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) - { - // type not really known, but since it is a JSON string: - return createSchemaNode("string", true); - } - + @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/ReferenceTypeSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/ReferenceTypeSerializer.java index 00186c6390..d101b5da5b 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/ReferenceTypeSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/ReferenceTypeSerializer.java @@ -3,14 +3,14 @@ import java.io.IOException; import com.fasterxml.jackson.annotation.JsonInclude; + import com.fasterxml.jackson.core.JsonGenerator; + import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.introspect.Annotated; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; -import com.fasterxml.jackson.databind.ser.ContextualSerializer; -import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap; import com.fasterxml.jackson.databind.type.ReferenceType; import com.fasterxml.jackson.databind.util.ArrayBuilders; import com.fasterxml.jackson.databind.util.BeanUtil; @@ -20,52 +20,28 @@ * Base implementation for values of {@link ReferenceType}. * Implements most of functionality, only leaving couple of abstract * methods for sub-classes to implement - * - * @since 2.8 */ public abstract class ReferenceTypeSerializer - extends StdSerializer - implements ContextualSerializer + extends StdDynamicSerializer { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 3L; - /** - * @since 2.9 - */ public final static Object MARKER_FOR_EMPTY = JsonInclude.Include.NON_EMPTY; - + /** * Value type */ protected final JavaType _referredType; - protected final BeanProperty _property; - - /** - * Type serializer used for values, if any. - */ - protected final TypeSerializer _valueTypeSerializer; - - /** - * Serializer for content values, if statically known. - */ - protected final JsonSerializer _valueSerializer; - /** * In case of unwrapping, need name transformer. */ protected final NameTransformer _unwrapper; - /** - * If element type cannot be statically determined, mapping from - * runtime type to serializer is handled using this object - */ - protected transient PropertySerializerMap _dynamicSerializers; - /* - /********************************************************** + /********************************************************************** /* Config settings, filtering - /********************************************************** + /********************************************************************** */ /** @@ -75,52 +51,38 @@ public abstract class ReferenceTypeSerializer * non-null values. * Note that inclusion value for Map instance itself is handled by caller (POJO * property that refers to the Map value). - * - * @since 2.9 */ protected final Object _suppressableValue; /** * Flag that indicates what to do with `null` values, distinct from * handling of {@link #_suppressableValue} - * - * @since 2.9 */ protected final boolean _suppressNulls; /* - /********************************************************** + /********************************************************************** /* Constructors, factory methods - /********************************************************** + /********************************************************************** */ public ReferenceTypeSerializer(ReferenceType fullType, boolean staticTyping, TypeSerializer vts, JsonSerializer ser) { - super(fullType); + super(fullType, null, vts, ser); _referredType = fullType.getReferencedType(); - _property = null; - _valueTypeSerializer = vts; - _valueSerializer = ser; _unwrapper = null; _suppressableValue = null; _suppressNulls = false; - _dynamicSerializers = PropertySerializerMap.emptyForProperties(); } - @SuppressWarnings("unchecked") protected ReferenceTypeSerializer(ReferenceTypeSerializer base, BeanProperty property, TypeSerializer vts, JsonSerializer valueSer, NameTransformer unwrapper, Object suppressableValue, boolean suppressNulls) { - super(base); + super(base, property, vts, valueSer); _referredType = base._referredType; - // [databind#2181]: may not be safe to reuse, start from empty - _dynamicSerializers = PropertySerializerMap.emptyForProperties(); - _property = property; - _valueTypeSerializer = vts; - _valueSerializer = (JsonSerializer) valueSer; _unwrapper = unwrapper; _suppressableValue = suppressableValue; _suppressNulls = suppressNulls; @@ -141,9 +103,9 @@ public JsonSerializer unwrappingSerializer(NameTransformer transformer) { } /* - /********************************************************** + /********************************************************************** /* Abstract methods to implement - /********************************************************** + /********************************************************************** */ /** @@ -152,8 +114,6 @@ public JsonSerializer unwrappingSerializer(NameTransformer transformer) { *

    * NOTE: caller has verified that there are changes, so implementations * need NOT check if a new instance is needed. - * - * @since 2.9 */ protected abstract ReferenceTypeSerializer withResolved(BeanProperty prop, TypeSerializer vts, JsonSerializer valueSer, @@ -165,8 +125,6 @@ protected abstract ReferenceTypeSerializer withResolved(BeanProperty prop, *

    * NOTE: caller has verified that there are changes, so implementations * need NOT check if a new instance is needed. - * - * @since 2.9 */ public abstract ReferenceTypeSerializer withContentInclusion(Object suppressableValue, boolean suppressNulls); @@ -184,31 +142,31 @@ public abstract ReferenceTypeSerializer withContentInclusion(Object suppressa protected abstract Object _getReferencedIfPresent(T value); /* - /********************************************************** + /********************************************************************** /* Contextualization (support for property annotations) - /********************************************************** + /********************************************************************** */ @Override - public JsonSerializer createContextual(SerializerProvider provider, + public JsonSerializer createContextual(SerializerProvider ctxt, BeanProperty property) throws JsonMappingException { TypeSerializer typeSer = _valueTypeSerializer; if (typeSer != null) { - typeSer = typeSer.forProperty(property); + typeSer = typeSer.forProperty(ctxt, property); } // First: do we have an annotation override from property? - JsonSerializer ser = findAnnotatedContentSerializer(provider, property); + JsonSerializer ser = findAnnotatedContentSerializer(ctxt, property); if (ser == null) { // If not, use whatever was configured by type ser = _valueSerializer; if (ser == null) { // A few conditions needed to be able to fetch serializer here: - if (_useStatic(provider, property, _referredType)) { - ser = _findSerializer(provider, _referredType, property); + if (_useStatic(ctxt, property, _referredType)) { + ser = _findSerializer(ctxt, _referredType, property); } } else { - ser = provider.handlePrimaryContextualization(ser, property); + ser = ctxt.handlePrimaryContextualization(ser, property); } } // First, resolve wrt property, resolved serializers @@ -222,7 +180,7 @@ public JsonSerializer createContextual(SerializerProvider provider, // and then see if we have property-inclusion overrides if (property != null) { - JsonInclude.Value inclV = property.findPropertyInclusion(provider.getConfig(), handledType()); + JsonInclude.Value inclV = property.findPropertyInclusion(ctxt.getConfig(), handledType()); if (inclV != null) { JsonInclude.Include incl = inclV.getContentInclusion(); @@ -248,11 +206,11 @@ public JsonSerializer createContextual(SerializerProvider provider, valueToSuppress = MARKER_FOR_EMPTY; break; case CUSTOM: - valueToSuppress = provider.includeFilterInstance(null, inclV.getContentFilter()); + valueToSuppress = ctxt.includeFilterInstance(null, inclV.getContentFilter()); if (valueToSuppress == null) { // is this legal? suppressNulls = true; } else { - suppressNulls = provider.includeFilterSuppressNulls(valueToSuppress); + suppressNulls = ctxt.includeFilterSuppressNulls(valueToSuppress); } break; case NON_NULL: @@ -275,7 +233,7 @@ public JsonSerializer createContextual(SerializerProvider provider, return refSer; } - protected boolean _useStatic(SerializerProvider provider, BeanProperty property, + protected boolean _useStatic(SerializerProvider serializers, BeanProperty property, JavaType referredType) { // First: no serializer for `Object.class`, must be dynamic @@ -291,11 +249,12 @@ protected boolean _useStatic(SerializerProvider provider, BeanProperty property, return true; } // if neither, maybe explicit annotation? - AnnotationIntrospector intr = provider.getAnnotationIntrospector(); + AnnotationIntrospector intr = serializers.getAnnotationIntrospector(); if ((intr != null) && (property != null)) { Annotated ann = property.getMember(); if (ann != null) { - JsonSerialize.Typing t = intr.findSerializationTyping(property.getMember()); + JsonSerialize.Typing t = intr.findSerializationTyping(serializers.getConfig(), + property.getMember()); if (t == JsonSerialize.Typing.STATIC) { return true; } @@ -305,17 +264,17 @@ protected boolean _useStatic(SerializerProvider provider, BeanProperty property, } } // and finally, may be forced by global static typing (unlikely...) - return provider.isEnabled(MapperFeature.USE_STATIC_TYPING); + return serializers.isEnabled(MapperFeature.USE_STATIC_TYPING); } /* - /********************************************************** + /********************************************************************** /* Accessors - /********************************************************** + /********************************************************************** */ @Override - public boolean isEmpty(SerializerProvider provider, T value) + public boolean isEmpty(SerializerProvider provider, T value) throws IOException { // First, absent value (note: null check is just sanity check here) if (!_isValuePresent(value)) { @@ -330,11 +289,7 @@ public boolean isEmpty(SerializerProvider provider, T value) } JsonSerializer ser = _valueSerializer; if (ser == null) { - try { - ser = _findCachedSerializer(provider, contents.getClass()); - } catch (JsonMappingException e) { // nasty but necessary - throw new RuntimeJsonMappingException(e); - } + ser = _findCachedSerializer(provider, contents.getClass()); } if (_suppressableValue == MARKER_FOR_EMPTY) { return ser.isEmpty(provider, contents); @@ -347,17 +302,14 @@ public boolean isUnwrappingSerializer() { return (_unwrapper != null); } - /** - * @since 2.9 - */ public JavaType getReferredType() { return _referredType; } /* - /********************************************************** + /********************************************************************** /* Serialization methods - /********************************************************** + /********************************************************************** */ @Override @@ -367,7 +319,7 @@ public void serialize(T ref, JsonGenerator g, SerializerProvider provider) Object value = _getReferencedIfPresent(ref); if (value == null) { if (_unwrapper == null) { - provider.defaultSerializeNull(g); + provider.defaultSerializeNullValue(g); } return; } @@ -390,7 +342,7 @@ public void serializeWithType(T ref, Object value = _getReferencedIfPresent(ref); if (value == null) { if (_unwrapper == null) { - provider.defaultSerializeNull(g); + provider.defaultSerializeNullValue(g); } return; } @@ -413,9 +365,9 @@ public void serializeWithType(T ref, } /* - /********************************************************** + /********************************************************************** /* Introspection support - /********************************************************** + /********************************************************************** */ @Override @@ -433,9 +385,9 @@ public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType t } /* - /********************************************************** + /********************************************************************** /* Helper methods - /********************************************************** + /********************************************************************** */ /** @@ -445,7 +397,7 @@ public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType t private final JsonSerializer _findCachedSerializer(SerializerProvider provider, Class rawType) throws JsonMappingException { - JsonSerializer ser = _dynamicSerializers.serializerFor(rawType); + JsonSerializer ser = _dynamicValueSerializers.serializerFor(rawType); if (ser == null) { // NOTE: call this instead of `map._findAndAddDynamic(...)` (which in turn calls // `findAndAddSecondarySerializer`) since we may need to apply unwrapper @@ -454,14 +406,14 @@ private final JsonSerializer _findCachedSerializer(SerializerProvider pr // [databind#1673] Must ensure we will resolve all available type information // so as not to miss generic declaration of, say, `List`... JavaType fullType = provider.constructSpecializedType(_referredType, rawType); - ser = provider.findValueSerializer(fullType, _property); + ser = provider.findSecondaryPropertySerializer(fullType, _property); } else { - ser = provider.findValueSerializer(rawType, _property); + ser = provider.findSecondaryPropertySerializer(rawType, _property); } if (_unwrapper != null) { ser = ser.unwrappingSerializer(_unwrapper); } - _dynamicSerializers = _dynamicSerializers.newWith(rawType, ser); + _dynamicValueSerializers = _dynamicValueSerializers.newWith(rawType, ser); } return ser; } @@ -474,6 +426,6 @@ private final JsonSerializer _findSerializer(SerializerProvider provider // 15-Jan-2017, tatu: ... possibly because we need to access "secondary" serializer, // not primary (primary being one for Reference type itself, not value) // return provider.findTypedValueSerializer(type, true, prop); - return provider.findValueSerializer(type, prop); + return provider.findSecondaryPropertySerializer(type, prop); } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/SqlTimeSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/SqlTimeSerializer.java index ba49c76b4f..a6f29dab68 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/SqlTimeSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/SqlTimeSerializer.java @@ -1,7 +1,6 @@ package com.fasterxml.jackson.databind.ser.std; import java.io.IOException; -import java.lang.reflect.Type; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.*; @@ -22,11 +21,6 @@ public void serialize(java.sql.Time value, JsonGenerator g, SerializerProvider p g.writeString(value.toString()); } - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) { - return createSchemaNode("string", true); - } - @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StaticListSerializerBase.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StaticListSerializerBase.java index 32594d856b..179abc0ca8 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/StaticListSerializerBase.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StaticListSerializerBase.java @@ -1,7 +1,6 @@ package com.fasterxml.jackson.databind.ser.std; import java.io.IOException; -import java.lang.reflect.Type; import java.util.*; import com.fasterxml.jackson.annotation.JsonFormat; @@ -11,7 +10,6 @@ import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonArrayFormatVisitor; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; -import com.fasterxml.jackson.databind.ser.ContextualSerializer; /** * Intermediate base class for Lists, Collections and Arrays @@ -20,34 +18,25 @@ @SuppressWarnings("serial") public abstract class StaticListSerializerBase> extends StdSerializer - implements ContextualSerializer { /** * Setting for specific local override for "unwrap single element arrays": * true for enable unwrapping, false for preventing it, `null` for using * global configuration. - * - * @since 2.6 */ protected final Boolean _unwrapSingle; protected StaticListSerializerBase(Class cls) { - super(cls, false); + super(cls); _unwrapSingle = null; } - /** - * @since 2.9 - */ protected StaticListSerializerBase(StaticListSerializerBase src, Boolean unwrapSingle) { super(src); _unwrapSingle = unwrapSingle; } - /** - * @since 2.9 - */ public abstract JsonSerializer _withResolved(BeanProperty prop, Boolean unwrapSingle); @@ -64,18 +53,16 @@ public JsonSerializer createContextual(SerializerProvider serializers, throws JsonMappingException { JsonSerializer ser = null; - Boolean unwrapSingle = null; if (property != null) { final AnnotationIntrospector intr = serializers.getAnnotationIntrospector(); AnnotatedMember m = property.getMember(); if (m != null) { - Object serDef = intr.findContentSerializer(m); - if (serDef != null) { - ser = serializers.serializerInstance(m, serDef); - } + ser = serializers.serializerInstance(m, + intr.findContentSerializer(serializers.getConfig(), m)); } } + Boolean unwrapSingle = null; JsonFormat.Value format = findFormatOverrides(serializers, property, handledType()); if (format != null) { unwrapSingle = format.getFeature(JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED); @@ -83,7 +70,7 @@ public JsonSerializer createContextual(SerializerProvider serializers, // [databind#124]: May have a content converter ser = findContextualConvertingSerializer(serializers, property, ser); if (ser == null) { - ser = serializers.findValueSerializer(String.class, property); + ser = serializers.findSecondaryPropertySerializer(String.class, property); } // Optimization: default serializer just writes String, so we can avoid a call: if (isDefaultSerializer(ser)) { @@ -103,11 +90,6 @@ public boolean isEmpty(SerializerProvider provider, T value) { return (value == null) || (value.size() == 0); } - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) { - return createSchemaNode("array", true).set("items", contentSchema()); - } - @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { JsonArrayFormatVisitor v2 = visitor.expectArrayFormat(typeHint); 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 eb05fa4b52..6a9e177c9c 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 @@ -1,17 +1,16 @@ package com.fasterxml.jackson.databind.ser.std; import java.io.IOException; -import java.lang.reflect.Type; import java.util.HashMap; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.type.WritableTypeId; + import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; -import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.ser.ContainerSerializer; import com.fasterxml.jackson.databind.type.TypeFactory; @@ -141,8 +140,7 @@ public final void serialize(boolean[] value, JsonGenerator g, SerializerProvider serializeContents(value, g, provider); return; } - g.writeStartArray(len); - g.setCurrentValue(value); + g.writeStartArray(value, len); serializeContents(value, g, provider); g.writeEndArray(); } @@ -156,14 +154,6 @@ public void serializeContents(boolean[] value, JsonGenerator g, SerializerProvid } } - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) - { - ObjectNode o = createSchemaNode("array", true); - o.set("items", createSchemaNode("boolean")); - return o; - } - @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException @@ -219,8 +209,7 @@ public final void serialize(short[] value, JsonGenerator g, SerializerProvider p serializeContents(value, g, provider); return; } - g.writeStartArray(len); - g.setCurrentValue(value); + g.writeStartArray(value, len); serializeContents(value, g, provider); g.writeEndArray(); } @@ -235,14 +224,6 @@ public void serializeContents(short[] value, JsonGenerator g, SerializerProvider } } - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) - { - //no "short" type defined by json - ObjectNode o = createSchemaNode("array", true); - return o.set("items", createSchemaNode("integer")); - } - @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException @@ -274,8 +255,7 @@ public void serialize(char[] value, JsonGenerator g, SerializerProvider provider { // [JACKSON-289] allows serializing as 'sparse' char array too: if (provider.isEnabled(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS)) { - g.writeStartArray(value.length); - g.setCurrentValue(value); + g.writeStartArray(value, value.length); _writeArrayContents(g, value); g.writeEndArray(); } else { @@ -311,15 +291,6 @@ private final void _writeArrayContents(JsonGenerator g, char[] value) } } - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) - { - ObjectNode o = createSchemaNode("array", true); - ObjectNode itemSchema = createSchemaNode("string"); - itemSchema.put("type", "string"); - return o.set("items", itemSchema); - } - @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException @@ -337,9 +308,6 @@ public static class IntArraySerializer extends ArraySerializerBase public IntArraySerializer() { super(int[].class); } - /** - * @since 2.6 - */ protected IntArraySerializer(IntArraySerializer src, BeanProperty prop, Boolean unwrapSingle) { super(src, prop, unwrapSingle); @@ -402,11 +370,6 @@ public void serializeContents(int[] value, JsonGenerator g, SerializerProvider p } } - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) { - return createSchemaNode("array", true).set("items", createSchemaNode("integer")); - } - @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { @@ -475,13 +438,6 @@ public void serializeContents(long[] value, JsonGenerator g, SerializerProvider } } - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) - { - return createSchemaNode("array", true) - .set("items", createSchemaNode("number", true)); - } - @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException @@ -539,8 +495,7 @@ public final void serialize(float[] value, JsonGenerator g, SerializerProvider p serializeContents(value, g, provider); return; } - g.writeStartArray(len); - g.setCurrentValue(value); + g.writeStartArray(value, len); serializeContents(value, g, provider); g.writeEndArray(); } @@ -554,11 +509,6 @@ public void serializeContents(float[] value, JsonGenerator g, SerializerProvider } } - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) { - return createSchemaNode("array", true).set("items", createSchemaNode("number")); - } - @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { @@ -639,11 +589,6 @@ public void serializeContents(double[] value, JsonGenerator g, SerializerProvide } } - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) { - return createSchemaNode("array", true).set("items", createSchemaNode("number")); - } - @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException 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 f16210114b..c2ff26456d 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 @@ -1,20 +1,16 @@ package com.fasterxml.jackson.databind.ser.std; +import java.io.IOException; + import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitable; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; -import com.fasterxml.jackson.databind.jsonschema.SchemaAware; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; -import com.fasterxml.jackson.databind.ser.ContextualSerializer; -import com.fasterxml.jackson.databind.ser.ResolvableSerializer; +import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap; import com.fasterxml.jackson.databind.util.ClassUtil; import com.fasterxml.jackson.databind.util.Converter; -import java.io.IOException; -import java.lang.reflect.Type; - /** * Serializer implementation where given Java type is first converted * to an intermediate "delegate type" (using a configured @@ -22,15 +18,14 @@ *

    * Note that although types may be related, they must not be same; trying * to do this will result in an exception. - * - * @since 2.1 */ @SuppressWarnings("serial") public class StdDelegatingSerializer extends StdSerializer - implements ContextualSerializer, ResolvableSerializer, - JsonFormatVisitable, SchemaAware { + // @since 3.0 + protected final BeanProperty _property; + protected final Converter _converter; /** @@ -42,11 +37,20 @@ public class StdDelegatingSerializer * Underlying serializer for type T. */ protected final JsonSerializer _delegateSerializer; + + /** + * If delegate serializer needs to be accessed dynamically (non-final + * type, static type not forced), this data structure helps with efficient + * lookups. + * + * @since 3.0 + */ + protected PropertySerializerMap _dynamicValueSerializers = PropertySerializerMap.emptyForProperties(); /* - /********************************************************** + /********************************************************************** /* Life-cycle - /********************************************************** + /********************************************************************** */ @SuppressWarnings("unchecked") @@ -56,55 +60,62 @@ public StdDelegatingSerializer(Converter converter) _converter = (Converter)converter; _delegateType = null; _delegateSerializer = null; + _property = null; } @SuppressWarnings("unchecked") public StdDelegatingSerializer(Class cls, Converter converter) { - super(cls, false); + super(cls); _converter = (Converter)converter; _delegateType = null; _delegateSerializer = null; + _property = null; } - + + /** + * @since 3.0 + */ @SuppressWarnings("unchecked") public StdDelegatingSerializer(Converter converter, - JavaType delegateType, JsonSerializer delegateSerializer) + JavaType delegateType, JsonSerializer delegateSerializer, + BeanProperty prop) { super(delegateType); _converter = converter; _delegateType = delegateType; _delegateSerializer = (JsonSerializer) delegateSerializer; + _property = prop; } - + /** * Method used for creating resolved contextual instances. Must be * overridden when sub-classing. */ protected StdDelegatingSerializer withDelegate(Converter converter, - JavaType delegateType, JsonSerializer delegateSerializer) + JavaType delegateType, JsonSerializer delegateSerializer, + BeanProperty prop) { ClassUtil.verifyMustOverride(StdDelegatingSerializer.class, this, "withDelegate"); - return new StdDelegatingSerializer(converter, delegateType, delegateSerializer); + return new StdDelegatingSerializer(converter, delegateType, delegateSerializer, prop); } - + /* - /********************************************************** + /********************************************************************** /* Contextualization - /********************************************************** + /********************************************************************** */ @Override - public void resolve(SerializerProvider provider) throws JsonMappingException + public void resolve(SerializerProvider ctxt) throws JsonMappingException { - if ((_delegateSerializer != null) - && (_delegateSerializer instanceof ResolvableSerializer)) { - ((ResolvableSerializer) _delegateSerializer).resolve(provider); + if (_delegateSerializer != null) { + _delegateSerializer.resolve(ctxt); } } @Override - public JsonSerializer createContextual(SerializerProvider provider, BeanProperty property) + public JsonSerializer createContextual(SerializerProvider ctxt, BeanProperty property) throws JsonMappingException { JsonSerializer delSer = _delegateSerializer; @@ -113,27 +124,28 @@ public JsonSerializer createContextual(SerializerProvider provider, BeanPrope 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()); + delegateType = _converter.getOutputType(ctxt.getTypeFactory()); } // 02-Apr-2015, tatu: For "dynamic case", where type is only specified as // java.lang.Object (or missing generic), [databind#731] if (!delegateType.isJavaLangObject()) { - delSer = provider.findValueSerializer(delegateType); + delSer = ctxt.findValueSerializer(delegateType); } } - if (delSer instanceof ContextualSerializer) { - delSer = provider.handleSecondaryContextualization(delSer, property); + if (delSer != null) { + delSer = ctxt.handleSecondaryContextualization(delSer, property); } - if (delSer == _delegateSerializer && delegateType == _delegateType) { + if ((delSer == _delegateSerializer) + && (delegateType == _delegateType) && (property == _property)) { return this; } - return withDelegate(_converter, delegateType, delSer); + return withDelegate(_converter, delegateType, delSer, property); } /* - /********************************************************** + /********************************************************************** /* Accessors - /********************************************************** + /********************************************************************** */ protected Converter getConverter() { @@ -144,32 +156,32 @@ public JsonSerializer createContextual(SerializerProvider provider, BeanPrope public JsonSerializer getDelegatee() { return _delegateSerializer; } - + /* - /********************************************************** + /********************************************************************** /* Serialization - /********************************************************** + /********************************************************************** */ @Override - public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException + public void serialize(Object value, JsonGenerator gen, SerializerProvider ctxt) throws IOException { Object delegateValue = convertValue(value); // should we accept nulls? if (delegateValue == null) { - provider.defaultSerializeNull(gen); + ctxt.defaultSerializeNullValue(gen); return; } // 02-Apr-2015, tatu: As per [databind#731] may need to do dynamic lookup JsonSerializer ser = _delegateSerializer; if (ser == null) { - ser = _findSerializer(delegateValue, provider); + ser = _findSerializer(delegateValue, ctxt); } - ser.serialize(delegateValue, gen, provider); + ser.serialize(delegateValue, gen, ctxt); } @Override - public void serializeWithType(Object value, JsonGenerator gen, SerializerProvider provider, + public void serializeWithType(Object value, JsonGenerator gen, SerializerProvider ctxt, TypeSerializer typeSer) throws IOException { /* 03-Oct-2012, tatu: This is actually unlikely to work ok... but for now, @@ -178,50 +190,31 @@ public void serializeWithType(Object value, JsonGenerator gen, SerializerProvide Object delegateValue = convertValue(value); JsonSerializer ser = _delegateSerializer; if (ser == null) { - ser = _findSerializer(value, provider); + ser = _findSerializer(value, ctxt); } - ser.serializeWithType(delegateValue, gen, provider, typeSer); + ser.serializeWithType(delegateValue, gen, ctxt, typeSer); } @Override - public boolean isEmpty(SerializerProvider prov, Object value) + public boolean isEmpty(SerializerProvider ctxt, Object value) throws IOException { Object delegateValue = convertValue(value); if (delegateValue == null) { return true; } - if (_delegateSerializer == null) { // best we can do for now, too costly to look up - return (value == null); + JsonSerializer ser = _delegateSerializer; + if (ser == null) { + ser = _findSerializer(value, ctxt); } - return _delegateSerializer.isEmpty(prov, delegateValue); + return ser.isEmpty(ctxt, delegateValue); } /* - /********************************************************** + /********************************************************************** /* Schema functionality - /********************************************************** + /********************************************************************** */ - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) - throws JsonMappingException - { - if (_delegateSerializer instanceof SchemaAware) { - return ((SchemaAware) _delegateSerializer).getSchema(provider, typeHint); - } - return super.getSchema(provider, typeHint); - } - - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint, - boolean isOptional) throws JsonMappingException - { - if (_delegateSerializer instanceof SchemaAware) { - return ((SchemaAware) _delegateSerializer).getSchema(provider, typeHint, isOptional); - } - return super.getSchema(provider, typeHint); - } - @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException @@ -236,9 +229,9 @@ public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType t } /* - /********************************************************** + /********************************************************************** /* Overridable methods - /********************************************************** + /********************************************************************** */ /** @@ -261,13 +254,18 @@ protected Object convertValue(Object value) { * actual type value gets converted to is not specified beyond basic * {@link java.lang.Object}, and where serializer needs to be located dynamically * based on actual value type. - * - * @since 2.6 */ - protected JsonSerializer _findSerializer(Object value, SerializerProvider serializers) + protected JsonSerializer _findSerializer(Object value, SerializerProvider ctxt) throws JsonMappingException { - // NOTE: will NOT call contextualization - return serializers.findValueSerializer(value.getClass()); + // 17-Apr-2018, tatu: Basically inline `_findAndAddDynamic(...)` + // 17-Apr-2018, tatu: difficult to know if these are primary or secondary serializers... + Class cc = value.getClass(); + PropertySerializerMap.SerializerAndMapResult result = _dynamicValueSerializers.findAndAddSecondarySerializer(cc, + ctxt, _property); + if (_dynamicValueSerializers != result.map) { + _dynamicValueSerializers = result.map; + } + return result.serializer; } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdDynamicSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdDynamicSerializer.java new file mode 100644 index 0000000000..ecc2b947d5 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdDynamicSerializer.java @@ -0,0 +1,113 @@ +package com.fasterxml.jackson.databind.ser.std; + +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap; + +/** + * Base class for standard serializers that are not (necessarily) container types + * but that similarly handle content that may vary in ways to require dynamic lookups. + * Typically these are referential or delegating types. + * + * @since 3.0 + */ +public abstract class StdDynamicSerializer + extends StdSerializer + implements java.io.Serializable +{ + private static final long serialVersionUID = 3L; + + /** + * Property for which this serializer is being used, if known at this point + * (`null` for root value serializers as well as those cached as blueprints). + */ + protected final BeanProperty _property; + + /** + * Type serializer used for values, if any: used for serializing values of + * polymorphic types. + */ + protected final TypeSerializer _valueTypeSerializer; + + /** + * Eagerly fetched serializer for actual value contained or referenced, + * if fetched. + */ + protected final JsonSerializer _valueSerializer; + + /** + * If value type cannot be statically determined, mapping from + * runtime value types to serializers are stored in this object. + * + * @since 3.0 (in 2.x subtypes contained it) + */ + protected PropertySerializerMap _dynamicValueSerializers = PropertySerializerMap.emptyForProperties(); + + /* + /********************************************************************** + /* Life-cycle + /********************************************************************** + */ + + @SuppressWarnings("unchecked") + protected StdDynamicSerializer(JavaType type, BeanProperty prop, + TypeSerializer vts, JsonSerializer valueSer) + { + super(type); + _property = prop; + _valueTypeSerializer = vts; + _valueSerializer = (JsonSerializer) valueSer; + } + + protected StdDynamicSerializer(StdDynamicSerializer src, BeanProperty prop) + { + super(src); + _property = prop; + _valueTypeSerializer = src._valueTypeSerializer; + _valueSerializer = src._valueSerializer; + } + + @SuppressWarnings("unchecked") + protected StdDynamicSerializer(StdDynamicSerializer src, + BeanProperty prop, TypeSerializer vts, JsonSerializer valueSer) + { + super(src); + _property = prop; + _valueTypeSerializer = vts; + _valueSerializer = (JsonSerializer) valueSer; + } + + /* + /********************************************************************** + /* Helper methods for locating, caching element/value serializers + /********************************************************************** + */ + + protected final JsonSerializer _findAndAddDynamic(SerializerProvider ctxt, Class type) + throws JsonMappingException + { + PropertySerializerMap map = _dynamicValueSerializers; + PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSecondarySerializer(type, + ctxt, _property); + if (map != result.map) { + _dynamicValueSerializers = result.map; + } + return result.serializer; + } + + protected final JsonSerializer _findAndAddDynamic(SerializerProvider ctxt, JavaType type) + throws JsonMappingException + { + PropertySerializerMap map = _dynamicValueSerializers; + PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSecondarySerializer(type, + ctxt, _property); + if (map != result.map) { + _dynamicValueSerializers = result.map; + } + return result.serializer; + } +} diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdJdkSerializers.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdJdkSerializers.java index b99bab3289..e84f1cd208 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdJdkSerializers.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdJdkSerializers.java @@ -1,7 +1,6 @@ package com.fasterxml.jackson.databind.ser.std; import java.io.*; -import java.lang.reflect.Type; import java.util.*; import java.util.concurrent.atomic.*; @@ -9,6 +8,7 @@ import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import com.fasterxml.jackson.databind.ser.BasicSerializerFactory; +import com.fasterxml.jackson.databind.util.TokenBuffer; /** * Class that providers access to serializers user for non-structured JDK types that @@ -19,49 +19,57 @@ public class StdJdkSerializers { /** - * Method called by {@link BasicSerializerFactory} to access - * all serializers this class provides. + * Method called by {@link BasicSerializerFactory} to find one of serializers provided here. */ - public static Collection, Object>> all() + public static final JsonSerializer find(Class raw) { - HashMap,Object> sers = new HashMap,Object>(); - - // First things that 'toString()' can handle - sers.put(java.net.URL.class, new ToStringSerializer(java.net.URL.class)); - sers.put(java.net.URI.class, new ToStringSerializer(java.net.URI.class)); - - sers.put(Currency.class, new ToStringSerializer(Currency.class)); - sers.put(UUID.class, new UUIDSerializer()); - sers.put(java.util.regex.Pattern.class, new ToStringSerializer(java.util.regex.Pattern.class)); - sers.put(Locale.class, new ToStringSerializer(Locale.class)); - - // then atomic types (note: AtomicReference defined elsewhere) - sers.put(AtomicBoolean.class, AtomicBooleanSerializer.class); - sers.put(AtomicInteger.class, AtomicIntegerSerializer.class); - sers.put(AtomicLong.class, AtomicLongSerializer.class); - - // then other types that need specialized serializers - sers.put(File.class, FileSerializer.class); - sers.put(Class.class, ClassSerializer.class); - + JsonSerializer ser = StringLikeSerializer.find(raw); + if (ser != null) { + return ser; + } + if (raw == UUID.class) { + return new UUIDSerializer(); + } + if (raw == AtomicBoolean.class) { + return new AtomicBooleanSerializer(); + } + if (raw == AtomicInteger.class) { + return new AtomicIntegerSerializer(); + } + if (raw == AtomicLong.class) { + return new AtomicLongSerializer(); + } + // Jackson-specific type(s) + // (Q: can this ever be sub-classed?) + if (raw == TokenBuffer.class) { + return new TokenBufferSerializer(); + } // And then some stranger types... not 100% they are needed but: - sers.put(Void.class, NullSerializer.instance); - sers.put(Void.TYPE, NullSerializer.instance); + if ((raw == Void.class) || (raw == Void.TYPE)) { + return NullSerializer.instance; + } + if (raw.getName().startsWith("java.sql.")) { + return _findSqlType(raw); + } + return null; + } - // 09-Jan-2015, tatu: As per [databind#1073], let's try to guard against possibility - // of some environments missing `java.sql.` types + private static JsonSerializer _findSqlType(Class raw) { try { // note: timestamps are very similar to java.util.Date, thus serialized as such - sers.put(java.sql.Timestamp.class, DateSerializer.instance); - - // leave some of less commonly used ones as lazy, no point in proactive construction - sers.put(java.sql.Date.class, SqlDateSerializer.class); - sers.put(java.sql.Time.class, SqlTimeSerializer.class); + if (raw == java.sql.Timestamp.class) { + return DateSerializer.instance; + } + if (raw == java.sql.Date.class) { + return new SqlDateSerializer(); + } + if (raw == java.sql.Time.class) { + return new SqlTimeSerializer(); + } } catch (NoClassDefFoundError e) { // nothing much we can do here; could log, but probably not useful for now. } - - return sers.entrySet(); + return null; } /* @@ -76,36 +84,26 @@ public static class AtomicBooleanSerializer public AtomicBooleanSerializer() { super(AtomicBoolean.class, false); } @Override - public void serialize(AtomicBoolean value, JsonGenerator gen, SerializerProvider provider) throws IOException, JsonGenerationException { + public void serialize(AtomicBoolean value, JsonGenerator gen, SerializerProvider provider) throws IOException { gen.writeBoolean(value.get()); } - - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) { - return createSchemaNode("boolean", true); - } - + @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { visitor.expectBooleanFormat(typeHint); } } - + public static class AtomicIntegerSerializer extends StdScalarSerializer { public AtomicIntegerSerializer() { super(AtomicInteger.class, false); } @Override - public void serialize(AtomicInteger value, JsonGenerator gen, SerializerProvider provider) throws IOException, JsonGenerationException { + public void serialize(AtomicInteger value, JsonGenerator gen, SerializerProvider provider) throws IOException { gen.writeNumber(value.get()); } - - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) { - return createSchemaNode("integer", true); - } - + @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { @@ -119,15 +117,10 @@ public static class AtomicLongSerializer public AtomicLongSerializer() { super(AtomicLong.class, false); } @Override - public void serialize(AtomicLong value, JsonGenerator gen, SerializerProvider provider) throws IOException, JsonGenerationException { + public void serialize(AtomicLong value, JsonGenerator gen, SerializerProvider provider) throws IOException { gen.writeNumber(value.get()); } - - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) { - return createSchemaNode("integer", true); - } - + @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializer.java deleted file mode 100644 index c62e444055..0000000000 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializer.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.fasterxml.jackson.databind.ser.std; - -import java.io.IOException; - -import com.fasterxml.jackson.core.*; -import com.fasterxml.jackson.databind.*; - -/** - * Specialized serializer that can be used as the generic key serializer, - * when serializing {@link java.util.Map}s to JSON Objects. - * - * @deprecated Since 2.8, use {@link StdKeySerializers.Default} instead. - */ -@SuppressWarnings("serial") -@Deprecated // since 2.8, -public class StdKeySerializer extends StdSerializer -{ - public StdKeySerializer() { super(Object.class); } - - @Override - public void serialize(Object value, JsonGenerator g, SerializerProvider provider) throws IOException { - // 19-Oct-2016, tatu: Simplified to bare essentials since this is deprecated - g.writeFieldName(value.toString()); - } -} diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializers.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializers.java index 70fe67fdd9..4f03a95747 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializers.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializers.java @@ -14,9 +14,6 @@ @SuppressWarnings("serial") public abstract class StdKeySerializers { - @SuppressWarnings("deprecation") - protected final static JsonSerializer DEFAULT_KEY_SERIALIZER = new StdKeySerializer(); - protected final static JsonSerializer DEFAULT_STRING_SERIALIZER = new StringKeySerializer(); /** @@ -81,8 +78,6 @@ public static JsonSerializer getStdKeySerializer(SerializationConfig con /** * Method called if no specified key serializer was located; will return a * "default" key serializer. - * - * @since 2.7 */ @SuppressWarnings("unchecked") public static JsonSerializer getFallbackKeySerializer(SerializationConfig config, @@ -107,14 +102,6 @@ public static JsonSerializer getFallbackKeySerializer(SerializationConfi return new Default(Default.TYPE_TO_STRING, rawKeyType); } - /** - * @deprecated since 2.7 - */ - @Deprecated - public static JsonSerializer getDefault() { - return DEFAULT_KEY_SERIALIZER; - } - /* /********************************************************** /* Standard implementations used @@ -142,7 +129,7 @@ public static class Default extends StdSerializer { protected final int _typeId; public Default(int typeId, Class type) { - super(type, false); + super(type); _typeId = typeId; } @@ -202,7 +189,7 @@ public static class Dynamic extends StdSerializer protected transient PropertySerializerMap _dynamicSerializers; public Dynamic() { - super(String.class, false); + super(String.class); _dynamicSerializers = PropertySerializerMap.emptyForProperties(); } @@ -256,7 +243,7 @@ protected JsonSerializer _findAndAddDynamic(PropertySerializerMap map, */ public static class StringKeySerializer extends StdSerializer { - public StringKeySerializer() { super(String.class, false); } + public StringKeySerializer() { super(String.class); } @Override public void serialize(Object value, JsonGenerator g, SerializerProvider provider) throws IOException { @@ -266,15 +253,13 @@ public void serialize(Object value, JsonGenerator g, SerializerProvider provider /** * Specialized instance to use for Enum keys, as per [databind#1322] - * - * @since 2.8 */ public static class EnumKeySerializer extends StdSerializer { protected final EnumValues _values; protected EnumKeySerializer(Class enumType, EnumValues values) { - super(enumType, false); + super(enumType); _values = values; } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdScalarSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdScalarSerializer.java index 916a9c980c..bc27c89755 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdScalarSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdScalarSerializer.java @@ -1,13 +1,11 @@ package com.fasterxml.jackson.databind.ser.std; import java.io.IOException; -import java.lang.reflect.Type; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.type.WritableTypeId; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; @@ -47,13 +45,6 @@ public void serializeWithType(T value, JsonGenerator g, SerializerProvider provi typeSer.writeTypeSuffix(g, typeIdDef); } - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) - throws JsonMappingException - { - return createSchemaNode("string", true); - } - @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdSerializer.java index 52ed126145..044d031b3d 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 @@ -2,7 +2,6 @@ import java.io.IOException; import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Type; import java.util.Collection; import java.util.IdentityHashMap; import java.util.Map; @@ -15,7 +14,6 @@ import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; import com.fasterxml.jackson.databind.introspect.AnnotatedMember; import com.fasterxml.jackson.databind.jsonFormatVisitors.*; -import com.fasterxml.jackson.databind.jsonschema.SchemaAware; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.ser.FilterProvider; @@ -27,19 +25,16 @@ * Base class used by all standard serializers, and can also * be used for custom serializers (in fact, this is the recommended * base class to use). - * Provides convenience methods for implementing {@link SchemaAware} */ public abstract class StdSerializer extends JsonSerializer - implements JsonFormatVisitable, SchemaAware, java.io.Serializable + implements JsonFormatVisitable, java.io.Serializable { private static final long serialVersionUID = 1L; /** * Key used for storing a lock object to prevent infinite recursion when * constructing converting serializers. - * - * @since 2.9 */ private final static Object KEY_CONTENT_CONVERTER_LOCK = new Object(); @@ -47,53 +42,48 @@ public abstract class StdSerializer * Nominal type supported, usually declared type of * property for which serializer is used. */ - protected final Class _handledType; + protected final Class _handledType; /* - /********************************************************** + /********************************************************************** /* Life-cycle - /********************************************************** + /********************************************************************** */ - protected StdSerializer(Class t) { + protected StdSerializer(Class t) { _handledType = t; } - @SuppressWarnings("unchecked") protected StdSerializer(JavaType type) { - _handledType = (Class) type.getRawClass(); + _handledType = type.getRawClass(); } /** * Alternate constructor that is (alas!) needed to work * around kinks of generic type handling */ - @SuppressWarnings("unchecked") + @Deprecated // since 3.0 protected StdSerializer(Class t, boolean dummy) { - _handledType = (Class) t; + _handledType = t; } - /** - * @since 2.6 - */ - @SuppressWarnings("unchecked") protected StdSerializer(StdSerializer src) { - _handledType = (Class) src._handledType; + _handledType = src._handledType; } /* - /********************************************************** + /********************************************************************** /* Accessors - /********************************************************** + /********************************************************************** */ @Override - public Class handledType() { return _handledType; } + public Class handledType() { return _handledType; } /* - /********************************************************** + /********************************************************************** /* Serialization - /********************************************************** + /********************************************************************** */ @Override @@ -101,9 +91,9 @@ public abstract void serialize(T value, JsonGenerator gen, SerializerProvider pr throws IOException; /* - /********************************************************** + /********************************************************************** /* Type introspection API, partial/default implementation - /********************************************************** + /********************************************************************** */ /** @@ -117,35 +107,10 @@ public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType t visitor.expectAnyFormat(typeHint); } - /** - * Default implementation simply claims type is "string"; usually - * overriden by custom serializers. - */ - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException - { - return createSchemaNode("string"); - } - - /** - * Default implementation simply claims type is "string"; usually - * overriden by custom serializers. - */ - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint, boolean isOptional) - throws JsonMappingException - { - ObjectNode schema = (ObjectNode) getSchema(provider, typeHint); - if (!isOptional) { - schema.put("required", !isOptional); - } - return schema; - } - /* - /********************************************************** + /********************************************************************** /* Helper methods for JSON Schema generation - /********************************************************** + /********************************************************************** */ protected ObjectNode createSchemaNode(String type) @@ -167,8 +132,6 @@ protected ObjectNode createSchemaNode(String type, boolean isOptional) /** * Helper method that calls necessary visit method(s) to indicate that the * underlying JSON type is JSON String. - * - * @since 2.7 */ protected void visitStringFormat(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { @@ -195,8 +158,6 @@ protected void visitStringFormat(JsonFormatVisitorWrapper visitor, JavaType type /** * Helper method that calls necessary visit method(s) to indicate that the * underlying JSON type is JSON Integer number. - * - * @since 2.7 */ protected void visitIntFormat(JsonFormatVisitorWrapper visitor, JavaType typeHint, NumberType numberType) @@ -212,8 +173,6 @@ protected void visitIntFormat(JsonFormatVisitorWrapper visitor, JavaType typeHin * Helper method that calls necessary visit method(s) to indicate that the * underlying JSON type is JSON Integer number, but that there is also a further * format restriction involved. - * - * @since 2.7 */ protected void visitIntFormat(JsonFormatVisitorWrapper visitor, JavaType typeHint, NumberType numberType, JsonValueFormat format) @@ -233,8 +192,6 @@ protected void visitIntFormat(JsonFormatVisitorWrapper visitor, JavaType typeHin /** * Helper method that calls necessary visit method(s) to indicate that the * underlying JSON type is a floating-point JSON number. - * - * @since 2.7 */ protected void visitFloatFormat(JsonFormatVisitorWrapper visitor, JavaType typeHint, NumberType numberType) @@ -246,9 +203,6 @@ protected void visitFloatFormat(JsonFormatVisitorWrapper visitor, JavaType typeH } } - /** - * @since 2.7 - */ protected void visitArrayFormat(JsonFormatVisitorWrapper visitor, JavaType typeHint, JsonSerializer itemSerializer, JavaType itemType) throws JsonMappingException @@ -259,9 +213,6 @@ protected void visitArrayFormat(JsonFormatVisitorWrapper visitor, JavaType typeH } } - /** - * @since 2.7 - */ protected void visitArrayFormat(JsonFormatVisitorWrapper visitor, JavaType typeHint, JsonFormatTypes itemType) throws JsonMappingException @@ -273,9 +224,9 @@ protected void visitArrayFormat(JsonFormatVisitorWrapper visitor, JavaType typeH } /* - /********************************************************** + /********************************************************************** /* Helper methods for exception handling - /********************************************************** + /********************************************************************** */ /** @@ -339,9 +290,9 @@ public void wrapAndThrow(SerializerProvider provider, } /* - /********************************************************** + /********************************************************************** /* Helper methods, accessing annotation-based configuration - /********************************************************** + /********************************************************************** */ /** @@ -351,11 +302,9 @@ public void wrapAndThrow(SerializerProvider provider, * * @param existingSerializer (optional) configured content * serializer if one already exists. - * - * @since 2.9 */ protected JsonSerializer findContextualConvertingSerializer(SerializerProvider provider, - BeanProperty property, JsonSerializer existingSerializer) + BeanProperty prop, JsonSerializer existingSerializer) throws JsonMappingException { // 08-Dec-2016, tatu: to fix [databind#357], need to prevent recursive calls for @@ -363,7 +312,7 @@ protected JsonSerializer findContextualConvertingSerializer(SerializerProvide @SuppressWarnings("unchecked") Map conversions = (Map) provider.getAttribute(KEY_CONTENT_CONVERTER_LOCK); if (conversions != null) { - Object lock = conversions.get(property); + Object lock = conversions.get(prop); if (lock != null) { return existingSerializer; } @@ -371,40 +320,37 @@ protected JsonSerializer findContextualConvertingSerializer(SerializerProvide conversions = new IdentityHashMap<>(); provider.setAttribute(KEY_CONTENT_CONVERTER_LOCK, conversions); } - conversions.put(property, Boolean.TRUE); - try { - JsonSerializer ser = findConvertingContentSerializer(provider, property, existingSerializer); - if (ser != null) { - return provider.handleSecondaryContextualization(ser, property); + final AnnotationIntrospector intr = provider.getAnnotationIntrospector(); + if (_neitherNull(intr, prop)) { + conversions.put(prop, Boolean.TRUE); + try { + JsonSerializer ser = _findConvertingContentSerializer(provider, intr, + prop, existingSerializer); + if (ser != null) { + return provider.handleSecondaryContextualization(ser, prop); + } + } finally { + conversions.remove(prop); } - } finally { - conversions.remove(property); } return existingSerializer; } - /** - * @deprecated Since 2.9 use {link {@link #findContextualConvertingSerializer} instead - */ - @Deprecated - protected JsonSerializer findConvertingContentSerializer(SerializerProvider provider, - BeanProperty prop, JsonSerializer existingSerializer) + private JsonSerializer _findConvertingContentSerializer(SerializerProvider provider, + AnnotationIntrospector intr, BeanProperty prop, JsonSerializer existingSerializer) throws JsonMappingException { - final AnnotationIntrospector intr = provider.getAnnotationIntrospector(); - if (_neitherNull(intr, prop)) { - AnnotatedMember m = prop.getMember(); - if (m != null) { - Object convDef = intr.findSerializationContentConverter(m); - if (convDef != null) { - Converter conv = provider.converterInstance(prop.getMember(), convDef); - JavaType delegateType = conv.getOutputType(provider.getTypeFactory()); - // [databind#731]: Should skip if nominally java.lang.Object - if ((existingSerializer == null) && !delegateType.isJavaLangObject()) { - existingSerializer = provider.findValueSerializer(delegateType); - } - return new StdDelegatingSerializer(conv, delegateType, existingSerializer); + AnnotatedMember m = prop.getMember(); + if (m != null) { + Object convDef = intr.findSerializationContentConverter(provider.getConfig(), m); + if (convDef != null) { + Converter conv = provider.converterInstance(prop.getMember(), convDef); + JavaType delegateType = conv.getOutputType(provider.getTypeFactory()); + // [databind#731]: Should skip if nominally java.lang.Object + if ((existingSerializer == null) && !delegateType.isJavaLangObject()) { + existingSerializer = provider.findValueSerializer(delegateType); } + return new StdDelegatingSerializer(conv, delegateType, existingSerializer, prop); } } return existingSerializer; @@ -413,8 +359,6 @@ protected JsonSerializer findConvertingContentSerializer(SerializerProvider p /** * Helper method used to locate filter that is needed, based on filter id * this serializer was constructed with. - * - * @since 2.3 */ protected PropertyFilter findPropertyFilter(SerializerProvider provider, Object filterId, Object valueToFilter) @@ -436,8 +380,6 @@ protected PropertyFilter findPropertyFilter(SerializerProvider provider, * defaulting. * * @param typeForDefaults Type (erased) used for finding default format settings, if any - * - * @since 2.7 */ protected JsonFormat.Value findFormatOverrides(SerializerProvider provider, BeanProperty prop, Class typeForDefaults) @@ -455,8 +397,6 @@ protected JsonFormat.Value findFormatOverrides(SerializerProvider provider, * to find whether that feature has been specifically marked as enabled or disabled. * * @param typeForDefaults Type (erased) used for finding default format settings, if any - * - * @since 2.7 */ protected Boolean findFormatFeature(SerializerProvider provider, BeanProperty prop, Class typeForDefaults, JsonFormat.Feature feat) @@ -468,9 +408,6 @@ protected Boolean findFormatFeature(SerializerProvider provider, return null; } - /** - * @since 2.8 - */ protected JsonInclude.Value findIncludeOverrides(SerializerProvider provider, BeanProperty prop, Class typeForDefaults) { @@ -483,8 +420,6 @@ protected JsonInclude.Value findIncludeOverrides(SerializerProvider provider, /** * Convenience method for finding out possibly configured content value serializer. - * - * @since 2.7.4 */ protected JsonSerializer findAnnotatedContentSerializer(SerializerProvider serializers, BeanProperty property) @@ -495,19 +430,17 @@ protected JsonSerializer findAnnotatedContentSerializer(SerializerProvider se AnnotatedMember m = property.getMember(); final AnnotationIntrospector intr = serializers.getAnnotationIntrospector(); if (m != null) { - Object serDef = intr.findContentSerializer(m); - if (serDef != null) { - return serializers.serializerInstance(m, serDef); - } + return serializers.serializerInstance(m, + intr.findContentSerializer(serializers.getConfig(), m)); } } return null; } /* - /********************************************************** + /********************************************************************** /* Helper methods, other - /********************************************************** + /********************************************************************** */ /** @@ -520,16 +453,10 @@ protected boolean isDefaultSerializer(JsonSerializer serializer) { return ClassUtil.isJacksonStdImpl(serializer); } - /** - * @since 2.9 - */ protected final static boolean _neitherNull(Object a, Object b) { return (a != null) && (b != null); } - /** - * @since 2.9 - */ protected final static boolean _nonEmpty(Collection c) { return (c != null) && !c.isEmpty(); } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StringLikeSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StringLikeSerializer.java new file mode 100644 index 0000000000..c61a931ab8 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StringLikeSerializer.java @@ -0,0 +1,134 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URL; +import java.nio.file.Path; +import java.util.*; +import java.util.regex.Pattern; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.type.WritableTypeId; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; + +/** + * "Combo" serializer used for JDK types that work almost like {@link ToStringSerializer}. + * + * @since 3.0 + */ +@JacksonStdImpl +public class StringLikeSerializer + extends StdSerializer +{ + private static final long serialVersionUID = 1L; + + protected final static int TYPE_URL = 1; + protected final static int TYPE_URI = 2; + protected final static int TYPE_FILE = 3; + protected final static int TYPE_PATH = 4; + + protected final static int TYPE_CLASS = 5; + + protected final static int TYPE_CURRENCY = 6; + protected final static int TYPE_LOCALE = 7; + protected final static int TYPE_PATTERN = 8; + + private final static Map,Integer> _types = new HashMap<>(); + static { + _types.put(URL.class, TYPE_URL); + _types.put(URI.class, TYPE_URI); + _types.put(File.class, TYPE_FILE); + _types.put(Path.class, TYPE_PATH); + + _types.put(Class.class, TYPE_CLASS); + + _types.put(Currency.class, TYPE_CURRENCY); + _types.put(Locale.class, TYPE_LOCALE); + _types.put(Pattern.class, TYPE_PATTERN); + } + + private final int _type; + + public StringLikeSerializer(Class handledType, int type) { + super(handledType); + _type = type; + } + + public static final JsonSerializer find(Class raw) + { + Integer I = _types.get(raw); + if (I == null) { + return null; + } + return new StringLikeSerializer(raw, I.intValue()); + } + + @Override + public boolean isEmpty(SerializerProvider prov, Object value) { + return value.toString().isEmpty(); + } + + @Override + public void serialize(Object value, JsonGenerator g, SerializerProvider provider) + throws IOException + { + String str; + + switch (_type) { + case TYPE_FILE: + str = ((File) value).getAbsolutePath(); + break; + case TYPE_PATH: + str = ((Path)value).toUri().toString(); + break; + case TYPE_CLASS: + str = ((Class)value).getName(); + break; + case TYPE_LOCALE: // [databind#1600] + { + Locale loc = (Locale) value; + if (loc == Locale.ROOT) { + str = ""; + } else { + str = loc.toLanguageTag(); + } + } + break; + default: + str = value.toString(); + break; + } + g.writeString(str); + } + + /** + * Default implementation will write type prefix, call regular serialization + * method (since assumption is that value itself does not need JSON + * Array or Object start/end markers), and then write type suffix. + * This should work for most cases; some sub-classes may want to + * change this behavior. + */ + @Override + public void serializeWithType(Object value, JsonGenerator g, SerializerProvider provider, + TypeSerializer typeSer) + throws IOException + { + // 15-Feb-2018, tatu: Note! In some cases `handledType` is base type, and not necessarily + // actual specific value type (f.ex. nio.Path) + WritableTypeId typeIdDef = typeSer.writeTypePrefix(g, + typeSer.typeId(value, handledType(), JsonToken.VALUE_STRING)); + serialize(value, g, provider); + typeSer.writeTypeSuffix(g, typeIdDef); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException + { + visitStringFormat(visitor, typeHint); + } +} diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StringSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StringSerializer.java index d3c621c72a..427698bf97 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/StringSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StringSerializer.java @@ -1,13 +1,11 @@ package com.fasterxml.jackson.databind.ser.std; import java.io.IOException; -import java.lang.reflect.Type; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; @@ -21,13 +19,12 @@ */ @JacksonStdImpl public final class StringSerializer -// NOTE: generic parameter changed from String to Object in 2.6, to avoid -// use of bridge methods -// In 2.9, removed use of intermediate type `NonTypedScalarSerializerBase` extends StdScalarSerializer { private static final long serialVersionUID = 1L; + public final static StringSerializer instance = new StringSerializer(); + public StringSerializer() { super(String.class, false); } @Override @@ -49,11 +46,6 @@ public final void serializeWithType(Object value, JsonGenerator gen, SerializerP gen.writeString((String) value); } - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) { - return createSchemaNode("string", true); - } - @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { visitStringFormat(visitor, typeHint); 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 eb7342281b..cff6379e97 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 @@ -1,13 +1,11 @@ package com.fasterxml.jackson.databind.ser.std; import java.io.IOException; -import java.lang.reflect.Type; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.type.WritableTypeId; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; @@ -38,13 +36,8 @@ public class ToStringSerializer */ 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); + super(handledType); } @Override @@ -81,11 +74,6 @@ public void serializeWithType(Object value, JsonGenerator g, SerializerProvider typeSer.writeTypeSuffix(g, typeIdDef); } - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException { - return createSchemaNode("string", true); - } - @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/TokenBufferSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/TokenBufferSerializer.java index a7624b078e..2ee7a7e432 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/TokenBufferSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/TokenBufferSerializer.java @@ -1,13 +1,11 @@ package com.fasterxml.jackson.databind.ser.std; import java.io.IOException; -import java.lang.reflect.Type; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.type.WritableTypeId; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; @@ -56,14 +54,6 @@ public final void serializeWithType(TokenBuffer value, JsonGenerator g, typeSer.writeTypeSuffix(g, typeIdDef); } - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) - { - // Not 100% sure what we should say here: type is basically not known. - // This seems like closest approximation - return createSchemaNode("any", true); - } - @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException 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 ef3e6aafb5..db4b94b66a 100644 --- a/src/main/java/com/fasterxml/jackson/databind/type/ArrayType.java +++ b/src/main/java/com/fasterxml/jackson/databind/type/ArrayType.java @@ -106,16 +106,6 @@ public ArrayType withStaticTyping() { /********************************************************** */ - /** - * Handling of narrowing conversions for arrays is trickier: for now, - * it is not even allowed. - */ - @Override - @Deprecated // since 2.7 - protected JavaType _narrow(Class subclass) { - return _reportUnsupported(); - } - // Should not be called, as array types in Java are not extensible; but // let's not freak out even if it is called? @Override @@ -124,10 +114,6 @@ public JavaType refine(Class contentClass, TypeBindings bindings, return null; } - private JavaType _reportUnsupported() { - throw new UnsupportedOperationException("Cannot narrow or widen array types"); - } - /* /********************************************************** /* Overridden methods 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 2bd9bbfaed..71fc0540e9 100644 --- a/src/main/java/com/fasterxml/jackson/databind/type/CollectionLikeType.java +++ b/src/main/java/com/fasterxml/jackson/databind/type/CollectionLikeType.java @@ -1,6 +1,5 @@ package com.fasterxml.jackson.databind.type; -import java.lang.reflect.TypeVariable; import java.util.Collection; import com.fasterxml.jackson.databind.JavaType; @@ -45,40 +44,15 @@ protected CollectionLikeType(TypeBase base, JavaType elemT) _elementType = elemT; } - /** - * @since 2.7 - */ public static CollectionLikeType construct(Class rawType, TypeBindings bindings, JavaType superClass, JavaType[] superInts, JavaType elemT) { return new CollectionLikeType(rawType, bindings, superClass, superInts, elemT, null, null, false); } - /** - * @deprecated Since 2.7, use {@link #upgradeFrom} for constructing instances, given - * pre-resolved {@link SimpleType}. - */ - @Deprecated // since 2.7 - public static CollectionLikeType construct(Class rawType, JavaType elemT) { - // First: may need to fabricate TypeBindings (needed for refining into - // concrete collection types, as per [databind#1102]) - TypeVariable[] vars = rawType.getTypeParameters(); - TypeBindings bindings; - if ((vars == null) || (vars.length != 1)) { - bindings = TypeBindings.emptyBindings(); - } else { - bindings = TypeBindings.create(rawType, elemT); - } - return new CollectionLikeType(rawType, bindings, - _bogusSuperClass(rawType), null, - elemT, null, null, false); - } - /** * Factory method that can be used to "upgrade" a basic type into collection-like * one; usually done via {@link TypeModifier} - * - * @since 2.7 */ public static CollectionLikeType upgradeFrom(JavaType baseType, JavaType elementType) { // 19-Oct-2015, tatu: Not sure if and how other types could be used as base; @@ -89,14 +63,6 @@ public static CollectionLikeType upgradeFrom(JavaType baseType, JavaType element throw new IllegalArgumentException("Cannot upgrade from an instance of "+baseType.getClass()); } - @Override - @Deprecated // since 2.7 - protected JavaType _narrow(Class subclass) { - return new CollectionLikeType(subclass, _bindings, - _superClass, _superInterfaces, _elementType, - _valueHandler, _typeHandler, _asStatic); - } - @Override public JavaType withContentType(JavaType contentType) { if (_elementType == contentType) { 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 7b38ab4908..558aab7f98 100644 --- a/src/main/java/com/fasterxml/jackson/databind/type/CollectionType.java +++ b/src/main/java/com/fasterxml/jackson/databind/type/CollectionType.java @@ -1,7 +1,5 @@ package com.fasterxml.jackson.databind.type; -import java.lang.reflect.TypeVariable; - import com.fasterxml.jackson.databind.JavaType; /** @@ -41,33 +39,6 @@ public static CollectionType construct(Class rawType, TypeBindings bindings, null, null, false); } - /** - * @deprecated Since 2.7, remove from 2.9 - */ - @Deprecated // since 2.7 - public static CollectionType construct(Class rawType, JavaType elemT) { - // First: may need to fabricate TypeBindings (needed for refining into - // concrete collection types, as per [databind#1102]) - TypeVariable[] vars = rawType.getTypeParameters(); - TypeBindings bindings; - if ((vars == null) || (vars.length != 1)) { - bindings = TypeBindings.emptyBindings(); - } else { - bindings = TypeBindings.create(rawType, elemT); - } - return new CollectionType(rawType, bindings, - // !!! TODO: Wrong, does have supertypes, but: - _bogusSuperClass(rawType), null, elemT, - null, null, false); - } - - @Deprecated // since 2.7 - @Override - protected JavaType _narrow(Class subclass) { - return new CollectionType(subclass, _bindings, - _superClass, _superInterfaces, _elementType, null, null, _asStatic); - } - @Override public JavaType withContentType(JavaType contentType) { if (_elementType == contentType) { 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 d62356b403..837bf9bcb1 100644 --- a/src/main/java/com/fasterxml/jackson/databind/type/MapLikeType.java +++ b/src/main/java/com/fasterxml/jackson/databind/type/MapLikeType.java @@ -1,6 +1,5 @@ package com.fasterxml.jackson.databind.type; -import java.lang.reflect.TypeVariable; import java.util.*; import com.fasterxml.jackson.databind.JavaType; @@ -54,8 +53,6 @@ protected MapLikeType(TypeBase base, JavaType keyT, JavaType valueT) { /** * Factory method that can be used to "upgrade" a basic type into * collection-like one; usually done via {@link TypeModifier} - * - * @since 2.7 */ public static MapLikeType upgradeFrom(JavaType baseType, JavaType keyT, JavaType valueT) { @@ -69,35 +66,6 @@ public static MapLikeType upgradeFrom(JavaType baseType, JavaType keyT, "Cannot upgrade from an instance of " + baseType.getClass()); } - @Deprecated - // since 2.7; remove from 2.8 - public static MapLikeType construct(Class rawType, JavaType keyT, - JavaType valueT) { - // First: may need to fabricate TypeBindings (needed for refining into - // concrete collection types, as per [databind#1102]) - TypeVariable[] vars = rawType.getTypeParameters(); - TypeBindings bindings; - if ((vars == null) || (vars.length != 2)) { - bindings = TypeBindings.emptyBindings(); - } else { - bindings = TypeBindings.create(rawType, keyT, valueT); - } - return new MapLikeType(rawType, bindings, _bogusSuperClass(rawType), - null, keyT, valueT, null, null, false); - } - - @Deprecated - // since 2.7 - @Override - protected JavaType _narrow(Class subclass) { - return new MapLikeType(subclass, _bindings, _superClass, - _superInterfaces, _keyType, _valueType, _valueHandler, - _typeHandler, _asStatic); - } - - /** - * @since 2.7 - */ public MapLikeType withKeyType(JavaType keyType) { if (keyType == _keyType) { return this; 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 efaf238df2..7d98e954d6 100644 --- a/src/main/java/com/fasterxml/jackson/databind/type/MapType.java +++ b/src/main/java/com/fasterxml/jackson/databind/type/MapType.java @@ -1,7 +1,5 @@ package com.fasterxml.jackson.databind.type; -import java.lang.reflect.TypeVariable; - import com.fasterxml.jackson.databind.JavaType; /** @@ -24,46 +22,15 @@ private MapType(Class mapType, TypeBindings bindings, keyT, valueT, valueHandler, typeHandler, asStatic); } - /** - * @since 2.7 - */ protected MapType(TypeBase base, JavaType keyT, JavaType valueT) { super(base, keyT, valueT); } - /** - * @since 2.7 - */ public static MapType construct(Class rawType, TypeBindings bindings, JavaType superClass, JavaType[] superInts, JavaType keyT, JavaType valueT) { return new MapType(rawType, bindings, superClass, superInts, keyT, valueT, null, null, false); } - - @Deprecated // since 2.7 - public static MapType construct(Class rawType, JavaType keyT, JavaType valueT) - { - // First: may need to fabricate TypeBindings (needed for refining into - // concrete collection types, as per [databind#1102]) - TypeVariable[] vars = rawType.getTypeParameters(); - TypeBindings bindings; - if ((vars == null) || (vars.length != 2)) { - bindings = TypeBindings.emptyBindings(); - } else { - bindings = TypeBindings.create(rawType, keyT, valueT); - } - // !!! TODO: Wrong, does have supertypes - return new MapType(rawType, bindings, _bogusSuperClass(rawType), null, - keyT, valueT, null, null, false); - } - - @Deprecated // since 2.7 - @Override - protected JavaType _narrow(Class subclass) { - return new MapType(subclass, _bindings, - _superClass, _superInterfaces, _keyType, _valueType, - _valueHandler, _typeHandler, _asStatic); - } @Override public MapType withTypeHandler(Object h) { diff --git a/src/main/java/com/fasterxml/jackson/databind/type/PlaceholderForType.java b/src/main/java/com/fasterxml/jackson/databind/type/PlaceholderForType.java index 7193d9898c..521dad8668 100644 --- a/src/main/java/com/fasterxml/jackson/databind/type/PlaceholderForType.java +++ b/src/main/java/com/fasterxml/jackson/databind/type/PlaceholderForType.java @@ -83,12 +83,6 @@ public JavaType refine(Class rawType, TypeBindings bindings, JavaType superCl return _unsupported(); } - @SuppressWarnings("deprecation") - @Override - protected JavaType _narrow(Class subclass) { - return _unsupported(); - } - @Override public boolean isContainerType() { return false; diff --git a/src/main/java/com/fasterxml/jackson/databind/type/ReferenceType.java b/src/main/java/com/fasterxml/jackson/databind/type/ReferenceType.java index f9ac7a381d..c3154f3be0 100644 --- a/src/main/java/com/fasterxml/jackson/databind/type/ReferenceType.java +++ b/src/main/java/com/fasterxml/jackson/databind/type/ReferenceType.java @@ -7,8 +7,6 @@ * that is, values that can be dereferenced to another value (or null), * of different type. * Referenced type is accessible using {@link #getContentType()}. - * - * @since 2.6 */ public class ReferenceType extends SimpleType { @@ -21,8 +19,6 @@ public class ReferenceType extends SimpleType * referencing type with polymorphic handling. Typically initialized when * a {@link SimpleType} is upgraded into reference type, but NOT changed * if being sub-classed. - * - * @since 2.8 */ protected final JavaType _anchorType; @@ -41,8 +37,6 @@ protected ReferenceType(Class cls, TypeBindings bindings, * Constructor used when upgrading into this type (via {@link #upgradeFrom}, * the usual way for {@link ReferenceType}s to come into existence. * Sets up what is considered the "base" reference type - * - * @since 2.7 */ protected ReferenceType(TypeBase base, JavaType refType) { @@ -73,9 +67,6 @@ public static ReferenceType upgradeFrom(JavaType baseType, JavaType refdType) { throw new IllegalArgumentException("Cannot upgrade from an instance of "+baseType.getClass()); } - /** - * @since 2.7 - */ public static ReferenceType construct(Class cls, TypeBindings bindings, JavaType superClass, JavaType[] superInts, JavaType refType) { @@ -83,13 +74,6 @@ public static ReferenceType construct(Class cls, TypeBindings bindings, refType, null, null, null, false); } - @Deprecated // since 2.7 - public static ReferenceType construct(Class cls, JavaType refType) { - return new ReferenceType(cls, TypeBindings.emptyBindings(), - // !!! TODO: missing supertypes - null, null, null, refType, null, null, false); - } - @Override public JavaType withContentType(JavaType contentType) { if (_referencedType == contentType) { @@ -170,22 +154,6 @@ protected String buildCanonicalName() return sb.toString(); } - /* - /********************************************************** - /* Narrow/widen - /********************************************************** - */ - - @Override - @Deprecated // since 2.7 - protected JavaType _narrow(Class subclass) - { - // Should we check that there is a sub-class relationship? - return new ReferenceType(subclass, _bindings, - _superClass, _superInterfaces, _referencedType, _anchorType, - _valueHandler, _typeHandler, _asStatic); - } - /* /********************************************************** /* Public API overrides diff --git a/src/main/java/com/fasterxml/jackson/databind/type/ResolvedRecursiveType.java b/src/main/java/com/fasterxml/jackson/databind/type/ResolvedRecursiveType.java index d45e1d6fa6..ba79a3a0c4 100644 --- a/src/main/java/com/fasterxml/jackson/databind/type/ResolvedRecursiveType.java +++ b/src/main/java/com/fasterxml/jackson/databind/type/ResolvedRecursiveType.java @@ -76,12 +76,6 @@ public JavaType withStaticTyping() { return this; } - @Deprecated // since 2.7 - @Override - protected JavaType _narrow(Class subclass) { - return this; - } - @Override public JavaType refine(Class rawType, TypeBindings bindings, JavaType superClass, JavaType[] superInterfaces) { 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 9e97f75e7d..e8fafe0d8a 100644 --- a/src/main/java/com/fasterxml/jackson/databind/type/SimpleType.java +++ b/src/main/java/com/fasterxml/jackson/databind/type/SimpleType.java @@ -1,7 +1,5 @@ package com.fasterxml.jackson.databind.type; -import java.util.*; - import com.fasterxml.jackson.databind.JavaType; /** @@ -41,8 +39,6 @@ protected SimpleType(Class cls, TypeBindings bindings, /** * Simple copy-constructor, usually used when upgrading/refining a simple type * into more specialized type. - * - * @since 2.7 */ protected SimpleType(TypeBase base) { super(base); @@ -84,7 +80,7 @@ public static SimpleType constructUnsafe(Class raw) { null, null, null, null, false); } - /** + /* * Method that should NOT to be used by application code: * it does NOT properly handle inspection of super-types, so neither parent * Classes nor implemented Interfaces are accessible with resulting type @@ -95,14 +91,11 @@ public static SimpleType constructUnsafe(Class raw) { * have worked acceptably: the problem comes from inability to resolve super-type * information, for which {@link TypeFactory} is needed. * - * @deprecated Since 2.7 - */ @Deprecated public static SimpleType construct(Class cls) { - /* Let's add sanity checks, just to ensure no - * Map/Collection entries are constructed - */ + // Let's add sanity checks, just to ensure no + // Map/Collection entries are constructed if (Map.class.isAssignableFrom(cls)) { throw new IllegalArgumentException("Cannot construct SimpleType for a Map (class: "+cls.getName()+")"); } @@ -117,55 +110,8 @@ public static SimpleType construct(Class cls) return new SimpleType(cls, b, _buildSuperClass(cls.getSuperclass(), b), null, null, null, false); } + */ - @Override - @Deprecated - protected JavaType _narrow(Class subclass) - { - if (_class == subclass) { - return this; - } - // Should we check that there is a sub-class relationship? - // 15-Jan-2016, tatu: Almost yes, but there are some complications with - // placeholder values (`Void`, `NoClass`), so cannot quite do yet. - // TODO: fix in 2.9 - if (!_class.isAssignableFrom(subclass)) { - /* - throw new IllegalArgumentException("Class "+subclass.getName()+" not sub-type of " - +_class.getName()); - */ - return new SimpleType(subclass, _bindings, this, _superInterfaces, - _valueHandler, _typeHandler, _asStatic); - } - // Otherwise, stitch together the hierarchy. First, super-class - Class next = subclass.getSuperclass(); - if (next == _class) { // straight up parent class? Great. - return new SimpleType(subclass, _bindings, this, - _superInterfaces, _valueHandler, _typeHandler, _asStatic); - } - if ((next != null) && _class.isAssignableFrom(next)) { - JavaType superb = _narrow(next); - return new SimpleType(subclass, _bindings, superb, - null, _valueHandler, _typeHandler, _asStatic); - } - // if not found, try a super-interface - Class[] nextI = subclass.getInterfaces(); - for (Class iface : nextI) { - if (iface == _class) { // directly implemented - return new SimpleType(subclass, _bindings, null, - new JavaType[] { this }, _valueHandler, _typeHandler, _asStatic); - } - if (_class.isAssignableFrom(iface)) { // indirect, so recurse - JavaType superb = _narrow(iface); - return new SimpleType(subclass, _bindings, null, - new JavaType[] { superb }, _valueHandler, _typeHandler, _asStatic); - } - } - // should not get here but... - throw new IllegalArgumentException("Internal error: Cannot resolve sub-type for Class "+subclass.getName()+" to " - +_class.getName()); - } - @Override public JavaType withContentType(JavaType contentType) { throw new IllegalArgumentException("Simple types have no content types; cannot call withContentType()"); @@ -267,31 +213,6 @@ public StringBuilder getGenericSignature(StringBuilder sb) return sb; } - /* - /********************************************************** - /* Internal methods - /********************************************************** - */ - - /** - * Helper method we need to recursively build skeletal representations - * of superclasses. - * - * @since 2.7 -- remove when not needed (2.8?) - */ - private static JavaType _buildSuperClass(Class superClass, TypeBindings b) - { - if (superClass == null) { - return null; - } - if (superClass == Object.class) { - return TypeFactory.unknownType(); - } - JavaType superSuper = _buildSuperClass(superClass.getSuperclass(), b); - return new SimpleType(superClass, b, - superSuper, null, null, null, false); - } - /* /********************************************************** /* Standard methods diff --git a/src/main/java/com/fasterxml/jackson/databind/type/TypeBase.java b/src/main/java/com/fasterxml/jackson/databind/type/TypeBase.java index a98d6ed0f9..cb99a9f21a 100644 --- a/src/main/java/com/fasterxml/jackson/databind/type/TypeBase.java +++ b/src/main/java/com/fasterxml/jackson/databind/type/TypeBase.java @@ -25,10 +25,7 @@ public abstract class TypeBase /** * Bindings in effect for this type instance; possibly empty. - * Needed when resolving types declared in members of this type - * (if any). - * - * @since 2.7 + * Needed when resolving types declared in members of this type (if any). */ protected final TypeBindings _bindings; @@ -52,8 +49,6 @@ protected TypeBase(Class raw, TypeBindings bindings, JavaType superClass, Jav /** * Copy-constructor used when refining/upgrading type instances. - * - * @since 2.7 */ protected TypeBase(TypeBase base) { super(base); @@ -97,12 +92,6 @@ public JavaType containedType(int index) { return _bindings.getBoundType(index); } - @Override - @Deprecated - public String containedTypeName(int index) { - return _bindings.getBoundName(index); - } - @Override public JavaType getSuperClass() { return _superClass; @@ -237,21 +226,4 @@ else if (cls == Void.TYPE) { } return sb; } - - /** - * Internal helper method used to figure out nominal super-class for - * deprecated factory methods / constructors, where we are not given - * properly resolved supertype hierarchy. - * Will basically give `JavaType` for `java.lang.Object` for classes - * other than `java.lafgn.Object`; null for others. - * - * @since 2.7 - */ - protected static JavaType _bogusSuperClass(Class cls) { - Class parent = cls.getSuperclass(); - if (parent == null) { - return null; - } - return TypeFactory.unknownType(); - } } diff --git a/src/main/java/com/fasterxml/jackson/databind/type/TypeBindings.java b/src/main/java/com/fasterxml/jackson/databind/type/TypeBindings.java index 872608a1d3..bb5c4e6f83 100644 --- a/src/main/java/com/fasterxml/jackson/databind/type/TypeBindings.java +++ b/src/main/java/com/fasterxml/jackson/databind/type/TypeBindings.java @@ -36,8 +36,6 @@ public class TypeBindings /** * Names of potentially unresolved type variables. - * - * @since 2.3 */ private final String[] _unboundVariables; 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 15b8ed7c55..c8e65578cd 100644 --- a/src/main/java/com/fasterxml/jackson/databind/type/TypeFactory.java +++ b/src/main/java/com/fasterxml/jackson/databind/type/TypeFactory.java @@ -5,10 +5,11 @@ import java.lang.reflect.*; import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.core.util.Snapshottable; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.util.ArrayBuilders; import com.fasterxml.jackson.databind.util.ClassUtil; -import com.fasterxml.jackson.databind.util.LRUMap; +import com.fasterxml.jackson.databind.util.SimpleLookupCache; /** * Class used for creating concrete {@link JavaType} instances, @@ -31,10 +32,11 @@ * */ @SuppressWarnings({"rawtypes" }) -public class TypeFactory // note: was final in 2.9, removed from 2.10 - implements java.io.Serializable +public final class TypeFactory + implements Snapshottable, + java.io.Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 3L; private final static JavaType[] NO_TYPES = new JavaType[0]; @@ -48,9 +50,9 @@ public class TypeFactory // note: was final in 2.9, removed from 2.10 protected final static TypeBindings EMPTY_BINDINGS = TypeBindings.emptyBindings(); /* - /********************************************************** + /********************************************************************** /* Constants for "well-known" classes - /********************************************************** + /********************************************************************** */ // // // Let's assume that a small set of core primitive/basic types @@ -67,45 +69,40 @@ public class TypeFactory // note: was final in 2.9, removed from 2.10 private final static Class CLS_BOOL = Boolean.TYPE; private final static Class CLS_INT = Integer.TYPE; private final static Class CLS_LONG = Long.TYPE; + private final static Class CLS_DOUBLE = Double.TYPE; /* - /********************************************************** + /********************************************************************** /* Cached pre-constructed JavaType instances - /********************************************************** + /********************************************************************** */ // note: these are primitive, hence no super types protected final static SimpleType CORE_TYPE_BOOL = new SimpleType(CLS_BOOL); protected final static SimpleType CORE_TYPE_INT = new SimpleType(CLS_INT); protected final static SimpleType CORE_TYPE_LONG = new SimpleType(CLS_LONG); + protected final static SimpleType CORE_TYPE_DOUBLE = new SimpleType(CLS_DOUBLE); // and as to String... well, for now, ignore its super types protected final static SimpleType CORE_TYPE_STRING = new SimpleType(CLS_STRING); - // @since 2.7 protected final static SimpleType CORE_TYPE_OBJECT = new SimpleType(CLS_OBJECT); /** * Cache {@link Comparable} because it is both parameteric (relatively costly to * resolve) and mostly useless (no special handling), better handle directly - * - * @since 2.7 */ protected final static SimpleType CORE_TYPE_COMPARABLE = new SimpleType(CLS_COMPARABLE); /** * Cache {@link Enum} because it is parametric AND self-referential (costly to * resolve) and useless in itself (no special handling). - * - * @since 2.7 */ protected final static SimpleType CORE_TYPE_ENUM = new SimpleType(CLS_ENUM); /** * Cache {@link Class} because it is nominally parametric, but has no really * useful information. - * - * @since 2.7 */ protected final static SimpleType CORE_TYPE_CLASS = new SimpleType(CLS_CLASS); @@ -114,12 +111,12 @@ public class TypeFactory // note: was final in 2.9, removed from 2.10 * actual generic types), we will use small cache to avoid repetitive * resolution of core types */ - protected final LRUMap _typeCache; + protected final SimpleLookupCache _typeCache; /* - /********************************************************** + /********************************************************************** /* Configuration - /********************************************************** + /********************************************************************** */ /** @@ -128,49 +125,54 @@ public class TypeFactory // note: was final in 2.9, removed from 2.10 */ protected final TypeModifier[] _modifiers; - protected final TypeParser _parser; - /** * ClassLoader used by this factory [databind#624]. */ protected final ClassLoader _classLoader; /* - /********************************************************** + /********************************************************************** /* Life-cycle - /********************************************************** + /********************************************************************** */ private TypeFactory() { this(null); } - /** - * @since 2.8 - */ - protected TypeFactory(LRUMap typeCache) { + protected TypeFactory(SimpleLookupCache typeCache) { if (typeCache == null) { - typeCache = new LRUMap(16, 200); + typeCache = new SimpleLookupCache(16, 200); } _typeCache = typeCache; - _parser = new TypeParser(this); _modifiers = null; _classLoader = null; } - protected TypeFactory(LRUMap typeCache, TypeParser p, + protected TypeFactory(SimpleLookupCache typeCache, TypeModifier[] mods, ClassLoader classLoader) { if (typeCache == null) { - typeCache = new LRUMap(16, 200); + typeCache = new SimpleLookupCache(16, 200); } _typeCache = typeCache; - // As per [databind#894] must ensure we have back-linkage from TypeFactory: - _parser = p.withFactory(this); _modifiers = mods; _classLoader = classLoader; } + /** + * Need to make a copy on snapshot() to avoid accidental leakage via cache. + * In theory only needed if there are modifiers, but since these are lightweight + * objects, let's recreate always. + */ + @Override + public TypeFactory snapshot() { + return new TypeFactory(_typeCache.snapshot(), + // this is safe since array never modified, always copy-on-mod + _modifiers, + _classLoader); + } + /** * "Mutant factory" method which will construct a new instance with specified * {@link TypeModifier} added as the first modifier to call (in case there @@ -178,7 +180,7 @@ protected TypeFactory(LRUMap typeCache, TypeParser p, */ public TypeFactory withModifier(TypeModifier mod) { - LRUMap typeCache = _typeCache; + SimpleLookupCache typeCache = _typeCache; TypeModifier[] mods; if (mod == null) { // mostly for unit tests mods = null; @@ -190,7 +192,7 @@ public TypeFactory withModifier(TypeModifier mod) } else { mods = ArrayBuilders.insertInListNoDup(_modifiers, mod); } - return new TypeFactory(typeCache, _parser, mods, _classLoader); + return new TypeFactory(typeCache, mods, _classLoader); } /** @@ -198,18 +200,16 @@ public TypeFactory withModifier(TypeModifier mod) * {@link ClassLoader} to use by {@link #findClass}. */ public TypeFactory withClassLoader(ClassLoader classLoader) { - return new TypeFactory(_typeCache, _parser, _modifiers, classLoader); + return new TypeFactory(_typeCache, _modifiers, classLoader); } /** * Mutant factory method that will construct new {@link TypeFactory} with * identical settings except for different cache; most likely one with * bigger maximum size. - * - * @since 2.8 */ - public TypeFactory withCache(LRUMap cache) { - return new TypeFactory(cache, _parser, _modifiers, _classLoader); + public TypeFactory withCache(SimpleLookupCache cache) { + return new TypeFactory(cache, _modifiers, _classLoader); } /** @@ -226,8 +226,6 @@ public TypeFactory withCache(LRUMap cache) { * if you know there is a problem with retention of type definitions; * the most likely (and currently only known) problem is retention * of {@link Class} instances via {@link JavaType} reference. - * - * @since 2.4.1 */ public void clearCache() { _typeCache.clear(); @@ -236,11 +234,11 @@ public void clearCache() { public ClassLoader getClassLoader() { return _classLoader; } - + /* - /********************************************************** + /********************************************************************** /* Static methods for non-instance-specific functionality - /********************************************************** + /********************************************************************** */ /** @@ -267,16 +265,14 @@ public static Class rawClass(Type t) { } /* - /********************************************************** + /********************************************************************** /* Low-level helper methods - /********************************************************** + /********************************************************************** */ /** * Low-level lookup method moved from {@link com.fasterxml.jackson.databind.util.ClassUtil}, * to allow for overriding of lookup functionality in environments like OSGi. - * - * @since 2.6 */ public Class findClass(String className) throws ClassNotFoundException { @@ -332,11 +328,11 @@ protected Class _findPrimitive(String className) if ("void".equals(className)) return Void.TYPE; return null; } - + /* - /********************************************************** + /********************************************************************** /* Type conversion, parameterization resolution methods - /********************************************************** + /********************************************************************** */ /** @@ -524,10 +520,6 @@ private boolean _verifyAndResolvePlaceholders(JavaType exp, JavaType act) * less-specific type of given type. Usually this is as simple as simply * finding super-type with type erasure of superClass, but * there may be need for some additional work-arounds. - * - * @param superClass - * - * @since 2.7 */ public JavaType constructGeneralizedType(JavaType baseType, Class superClass) { @@ -563,7 +555,7 @@ public JavaType constructGeneralizedType(JavaType baseType, Class superClass) */ public JavaType constructFromCanonical(String canonical) throws IllegalArgumentException { - return _parser.parse(canonical); + return TypeParser.instance.parse(this, canonical); } /** @@ -585,19 +577,20 @@ public JavaType[] findTypeParameters(JavaType type, Class expType) } /** - * @deprecated Since 2.7 resolve raw type first, then find type parameters - */ - @Deprecated // since 2.7 - public JavaType[] findTypeParameters(Class clz, Class expType, TypeBindings bindings) { - return findTypeParameters(constructType(clz, bindings), expType); - } - - /** - * @deprecated Since 2.7 resolve raw type first, then find type parameters + * Specialized alternative to {@link #findTypeParameters} + * + * @since 3.0 */ - @Deprecated // since 2.7 - public JavaType[] findTypeParameters(Class clz, Class expType) { - return findTypeParameters(constructType(clz), expType); + public JavaType findFirstTypeParameter(JavaType type, Class expType) + { + JavaType match = type.findSuperType(expType); + if (match != null) { + JavaType t = match.getBindings().getBoundType(0); + if (t != null) { + return t; + } + } + return _unknownType(); } /** @@ -607,8 +600,6 @@ public JavaType[] findTypeParameters(Class clz, Class expType) { * * @param type1 Primary type to consider * @param type2 Secondary type to consider - * - * @since 2.2 */ public JavaType moreSpecificType(JavaType type1, JavaType type2) { @@ -629,11 +620,11 @@ public JavaType moreSpecificType(JavaType type1, JavaType type2) } return type1; } - + /* - /********************************************************** + /********************************************************************** /* Public factory methods - /********************************************************** + /********************************************************************** */ public JavaType constructType(Type type) { @@ -668,48 +659,29 @@ public JavaType constructType(TypeReference typeRef) */ } + // 20-Apr-2018, tatu: Really should get rid of this... + /** - * @deprecated Since 2.7 (accidentally removed in 2.7.0; added back in 2.7.1) - */ - @Deprecated - public JavaType constructType(Type type, Class contextClass) { - JavaType contextType = (contextClass == null) ? null : constructType(contextClass); - return constructType(type, contextType); - } - - /** - * @deprecated Since 2.7 (accidentally removed in 2.7.0; added back in 2.7.1) + * Method that use by core Databind functionality, and that should NOT be called + * by application code outside databind package. + *

    + * Unchecked here not only means that no checks are made as to whether given class + * might be non-simple type (like {@link CollectionType}) but also that most of supertype + * information is not gathered. This means that unless called on primitive types or + * {@link java.lang.String}, results are probably not what you want to use. + * + * @deprecated Since 2.8, to indicate users should never call this method. */ - @Deprecated - public JavaType constructType(Type type, JavaType contextType) { - TypeBindings bindings; - if (contextType == null) { - bindings = EMPTY_BINDINGS; - } else { - bindings = contextType.getBindings(); - // 16-Nov-2016, tatu: Unfortunately as per [databind#1456] this can't - // be made to work for some cases used to work (even if accidentally); - // however, we can try a simple heuristic to increase chances of - // compatibility from 2.6 code - if (type.getClass() != Class.class) { - // Ok: so, ideally we would test super-interfaces if necessary; - // but let's assume most if not all cases are for classes. - while (bindings.isEmpty()) { - contextType = contextType.getSuperClass(); - if (contextType == null) { - break; - } - bindings = contextType.getBindings(); - } - } - } - return _fromAny(null, type, bindings); + @Deprecated // since 2.8 + public JavaType uncheckedSimpleType(Class cls) { + // 18-Oct-2015, tatu: Not sure how much problem missing super-type info is here + return _constructSimple(cls, EMPTY_BINDINGS, null, null); } /* - /********************************************************** + /********************************************************************** /* Direct factory methods - /********************************************************** + /********************************************************************** */ /** @@ -873,30 +845,11 @@ public MapLikeType constructMapLikeType(Class mapClass, JavaType keyType, Jav /** * Method for constructing a type instance with specified parameterization. - *

    - * NOTE: was briefly deprecated for 2.6. - */ +s */ public JavaType constructSimpleType(Class rawType, JavaType[] parameterTypes) { return _fromClass(null, rawType, TypeBindings.create(rawType, parameterTypes)); } - /** - * Method for constructing a type instance with specified parameterization. - * - * @since 2.6 - * - * @deprecated Since 2.7 - */ - @Deprecated - public JavaType constructSimpleType(Class rawType, Class parameterTarget, - JavaType[] parameterTypes) - { - return constructSimpleType(rawType, parameterTypes); - } - - /** - * @since 2.6 - */ public JavaType constructReferenceType(Class rawType, JavaType referredType) { return ReferenceType.construct(rawType, null, // no bindings @@ -904,23 +857,6 @@ public JavaType constructReferenceType(Class rawType, JavaType referredType) referredType); } - /** - * Method that use by core Databind functionality, and that should NOT be called - * by application code outside databind package. - *

    - * Unchecked here not only means that no checks are made as to whether given class - * might be non-simple type (like {@link CollectionType}) but also that most of supertype - * information is not gathered. This means that unless called on primitive types or - * {@link java.lang.String}, results are probably not what you want to use. - * - * @deprecated Since 2.8, to indicate users should never call this method. - */ - @Deprecated // since 2.8 - public JavaType uncheckedSimpleType(Class cls) { - // 18-Oct-2015, tatu: Not sure how much problem missing super-type info is here - return _constructSimple(cls, EMPTY_BINDINGS, null, null); - } - /** * Factory method for constructing {@link JavaType} that * represents a parameterized type. For example, to represent @@ -945,8 +881,6 @@ public JavaType uncheckedSimpleType(Class cls) { * * @param parametrized Actual full type * @param parameterClasses Type parameters to apply - * - * @since 2.5 NOTE: was briefly deprecated for 2.6 */ public JavaType constructParametricType(Class parametrized, Class... parameterClasses) { int len = parameterClasses.length; @@ -980,43 +914,39 @@ public JavaType constructParametricType(Class parametrized, Class... param * * @param rawType Actual type-erased type * @param parameterTypes Type parameters to apply - * - * @since 2.5 NOTE: was briefly deprecated for 2.6 */ public JavaType constructParametricType(Class rawType, JavaType... parameterTypes) { - return _fromClass(null, rawType, TypeBindings.create(rawType, parameterTypes)); - } - - /** - * @since 2.5 -- but will probably deprecated in 2.7 or 2.8 (not needed with 2.7) - * - * @deprecated since 2.9 Use {@link #constructParametricType(Class,JavaType...)} instead - */ - @Deprecated - public JavaType constructParametrizedType(Class parametrized, Class parametersFor, - JavaType... parameterTypes) - { - return constructParametricType(parametrized, parameterTypes); + return constructParametricType(rawType, TypeBindings.create(rawType, parameterTypes)); } /** - * @since 2.5 -- but will probably deprecated in 2.7 or 2.8 (not needed with 2.7) + * Factory method for constructing {@link JavaType} that + * represents a parameterized type. The type's parameters are + * specified as an instance of {@link TypeBindings}. This + * is useful if you already have the type's parameters such + * as those found on {@link JavaType}. For example, you could + * call + *

    +     *   return TypeFactory.constructParametricType(ArrayList.class, javaType.getBindings());
    +     * 
    + * This effectively applies the parameterized types from one + * {@link JavaType} to another class. * - * @deprecated since 2.9 Use {@link #constructParametricType(Class,Class...)} instead + * @param rawType Actual type-erased type + * @param parameterTypes Type bindings for the raw type + * @since 3.0 */ - @Deprecated - public JavaType constructParametrizedType(Class parametrized, Class parametersFor, - Class... parameterClasses) + public JavaType constructParametricType(Class rawType, TypeBindings parameterTypes) { - return constructParametricType(parametrized, parameterClasses); + return _fromClass(null, rawType, parameterTypes); } /* - /********************************************************** + /********************************************************************** /* Direct factory methods for "raw" variants, used when /* parameterization is unknown - /********************************************************** + /********************************************************************** */ /** @@ -1080,9 +1010,9 @@ public MapLikeType constructRawMapLikeType(Class mapClass) { } /* - /********************************************************** + /********************************************************************** /* Low-level factory methods - /********************************************************** + /********************************************************************** */ private JavaType _mapType(Class rawClass, TypeBindings bindings, @@ -1127,7 +1057,7 @@ private JavaType _collectionType(Class rawClass, TypeBindings bindings, return CollectionType.construct(rawClass, bindings, superClass, superInterfaces, ct); } - private JavaType _referenceType(Class rawClass, TypeBindings bindings, + protected JavaType _referenceType(Class rawClass, TypeBindings bindings, JavaType superClass, JavaType[] superInterfaces) { List typeParams = bindings.getTypeParameters(); @@ -1148,8 +1078,6 @@ private JavaType _referenceType(Class rawClass, TypeBindings bindings, * no generic parameters are passed. Default implementation may check * pre-constructed values for "well-known" types, but if none found * will simply call {@link #_newSimpleType} - * - * @since 2.7 */ protected JavaType _constructSimple(Class raw, TypeBindings bindings, JavaType superClass, JavaType[] superInterfaces) @@ -1167,8 +1095,6 @@ protected JavaType _constructSimple(Class raw, TypeBindings bindings, * Factory method that is to create a new {@link SimpleType} with no * checks whatsoever. Default implementation calls the single argument * constructor of {@link SimpleType}. - * - * @since 2.7 */ protected JavaType _newSimpleType(Class raw, TypeBindings bindings, JavaType superClass, JavaType[] superInterfaces) @@ -1177,11 +1103,6 @@ protected JavaType _newSimpleType(Class raw, TypeBindings bindings, } protected JavaType _unknownType() { - /* 15-Sep-2015, tatu: Prior to 2.7, we constructed new instance for each call. - * This may have been due to potential mutability of the instance; but that - * should not be issue any more, and creation is somewhat wasteful. So let's - * try reusing singleton/flyweight instance. - */ return CORE_TYPE_OBJECT; } @@ -1189,14 +1110,13 @@ protected JavaType _unknownType() { * Helper method called to see if requested, non-generic-parameterized * type is one of common, "well-known" types, instances of which are * pre-constructed and do not need dynamic caching. - * - * @since 2.7 */ protected JavaType _findWellKnownSimple(Class clz) { if (clz.isPrimitive()) { if (clz == CLS_BOOL) return CORE_TYPE_BOOL; if (clz == CLS_INT) return CORE_TYPE_INT; if (clz == CLS_LONG) return CORE_TYPE_LONG; + if (clz == CLS_DOUBLE) return CORE_TYPE_DOUBLE; } else { if (clz == CLS_STRING) return CORE_TYPE_STRING; if (clz == CLS_OBJECT) return CORE_TYPE_OBJECT; // since 2.7 @@ -1205,9 +1125,9 @@ protected JavaType _findWellKnownSimple(Class clz) { } /* - /********************************************************** + /********************************************************************** /* Actual type resolution, traversal - /********************************************************** + /********************************************************************** */ /** @@ -1397,14 +1317,28 @@ protected JavaType _fromWellKnownClass(ClassStack context, Class rawType, Typ return _collectionType(rawType, bindings, superClass, superInterfaces); } // and since 2.6 one referential type - if (rawType == AtomicReference.class) { + if ((rawType == AtomicReference.class) + || (rawType == Optional.class)) { + // 17-Sep-2017, tatu: Jackson 3.x brings Java 8 optional types in... return _referenceType(rawType, bindings, superClass, superInterfaces); } - // 01-Nov-2015, tatu: As of 2.7, couple of potential `CollectionLikeType`s (like - // `Iterable`, `Iterator`), and `MapLikeType`s (`Map.Entry`) are not automatically - // detected, related to difficulties in propagating type upwards (Iterable, for - // example, is a weak, tag-on type). They may be detectable in future. - return null; + // 17-Sep-2017, tatu: Jackson 3.x brings Java 8 optional types in... + JavaType refd; + if (rawType == OptionalInt.class) { + refd = CORE_TYPE_INT; + } else if (rawType == OptionalLong.class) { + refd = CORE_TYPE_LONG; + } else if (rawType == OptionalDouble.class) { + refd = CORE_TYPE_DOUBLE; + } else { + // 01-Nov-2015, tatu: As of 2.7, couple of potential `CollectionLikeType`s (like + // `Iterable`, `Iterator`), and `MapLikeType`s (`Map.Entry`) are not automatically + // detected, related to difficulties in propagating type upwards (Iterable, for + // example, is a weak, tag-on type). They may be detectable in future. + return null; + } + JavaType base = _newSimpleType(rawType, bindings, superClass, superInterfaces); + return ReferenceType.upgradeFrom(base, refd); } protected JavaType _fromWellKnownInterface(ClassStack context, Class rawType, TypeBindings bindings, @@ -1474,9 +1408,6 @@ protected JavaType _fromVariable(ClassStack context, TypeVariable var, TypeBi { // ideally should find it via bindings: final String name = var.getName(); - if (bindings == null) { - throw new IllegalArgumentException("Null `bindings` passed (type variable \""+name+"\")"); - } JavaType type = bindings.findBoundType(name); if (type != null) { return type; diff --git a/src/main/java/com/fasterxml/jackson/databind/type/TypeParser.java b/src/main/java/com/fasterxml/jackson/databind/type/TypeParser.java index 3cfcb72d9d..2860c4fada 100644 --- a/src/main/java/com/fasterxml/jackson/databind/type/TypeParser.java +++ b/src/main/java/com/fasterxml/jackson/databind/type/TypeParser.java @@ -12,25 +12,16 @@ public class TypeParser implements java.io.Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 3L; - protected final TypeFactory _factory; - - public TypeParser(TypeFactory f) { - _factory = f; - } - - /** - * @since 2.6.2 - */ - public TypeParser withFactory(TypeFactory f) { - return (f == _factory) ? this : new TypeParser(f); - } + public final static TypeParser instance = new TypeParser(); + + public TypeParser() { } - public JavaType parse(String canonical) throws IllegalArgumentException + public JavaType parse(TypeFactory tf, String canonical) throws IllegalArgumentException { MyTokenizer tokens = new MyTokenizer(canonical.trim()); - JavaType type = parseType(tokens); + JavaType type = parseType(tf, tokens); // must be end, now if (tokens.hasMoreTokens()) { throw _problem(tokens, "Unexpected tokens after complete type"); @@ -38,34 +29,34 @@ public JavaType parse(String canonical) throws IllegalArgumentException return type; } - protected JavaType parseType(MyTokenizer tokens) + protected JavaType parseType(TypeFactory tf, MyTokenizer tokens) throws IllegalArgumentException { if (!tokens.hasMoreTokens()) { throw _problem(tokens, "Unexpected end-of-string"); } - Class base = findClass(tokens.nextToken(), tokens); + Class base = findClass(tf, tokens.nextToken(), tokens); // either end (ok, non generic type), or generics if (tokens.hasMoreTokens()) { String token = tokens.nextToken(); if ("<".equals(token)) { - List parameterTypes = parseTypes(tokens); + List parameterTypes = parseTypes(tf, tokens); TypeBindings b = TypeBindings.create(base, parameterTypes); - return _factory._fromClass(null, base, b); + return tf._fromClass(null, base, b); } // can be comma that separates types, or closing '>' tokens.pushBack(token); } - return _factory._fromClass(null, base, TypeBindings.emptyBindings()); + return tf._fromClass(null, base, TypeBindings.emptyBindings()); } - protected List parseTypes(MyTokenizer tokens) + protected List parseTypes(TypeFactory tf, MyTokenizer tokens) throws IllegalArgumentException { ArrayList types = new ArrayList(); while (tokens.hasMoreTokens()) { - types.add(parseType(tokens)); + types.add(parseType(tf, tokens)); if (!tokens.hasMoreTokens()) break; String token = tokens.nextToken(); if (">".equals(token)) return types; @@ -76,10 +67,10 @@ protected List parseTypes(MyTokenizer tokens) throw _problem(tokens, "Unexpected end-of-string"); } - protected Class findClass(String className, MyTokenizer tokens) + protected Class findClass(TypeFactory tf, String className, MyTokenizer tokens) { try { - return _factory.findClass(className); + return tf.findClass(className); } catch (Exception e) { ClassUtil.throwIfRTE(e); throw _problem(tokens, "Cannot locate class '"+className+"', problem: "+e.getMessage()); diff --git a/src/main/java/com/fasterxml/jackson/databind/util/ArrayBuilders.java b/src/main/java/com/fasterxml/jackson/databind/util/ArrayBuilders.java index ae2939a7cc..9258739434 100644 --- a/src/main/java/com/fasterxml/jackson/databind/util/ArrayBuilders.java +++ b/src/main/java/com/fasterxml/jackson/databind/util/ArrayBuilders.java @@ -151,8 +151,6 @@ public DoubleBuilder() { } * Note: current implementation is not optimized for speed; if performance * ever becomes an issue, it is possible to construct much more efficient * typed instances (one for Object[] and sub-types; one per primitive type). - * - * @since 2.2 Moved from earlier Comparators class */ public static Object getArrayComparator(final Object defaultValue) { diff --git a/src/main/java/com/fasterxml/jackson/databind/util/BeanUtil.java b/src/main/java/com/fasterxml/jackson/databind/util/BeanUtil.java index 54462ceb4a..5c8f66b809 100644 --- a/src/main/java/com/fasterxml/jackson/databind/util/BeanUtil.java +++ b/src/main/java/com/fasterxml/jackson/databind/util/BeanUtil.java @@ -6,7 +6,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.introspect.AnnotatedMethod; +import com.fasterxml.jackson.databind.introspect.AnnotatedMember; /** * Helper class that contains functionality needed by both serialization @@ -20,23 +20,16 @@ public class BeanUtil /********************************************************** */ - /** - * @since 2.5 - */ - public static String okNameForGetter(AnnotatedMethod am, boolean stdNaming) { + public static String okNameForGetter(AnnotatedMember am) { String name = am.getName(); - String str = okNameForIsGetter(am, name, stdNaming); + String str = okNameForIsGetter(am, name); if (str == null) { - str = okNameForRegularGetter(am, name, stdNaming); + str = okNameForRegularGetter(am, name); } return str; } - - /** - * @since 2.5 - */ - public static String okNameForRegularGetter(AnnotatedMethod am, String name, - boolean stdNaming) + + public static String okNameForRegularGetter(AnnotatedMember am, String name) { if (name.startsWith("get")) { /* 16-Feb-2009, tatu: To handle [JACKSON-53], need to block @@ -56,54 +49,27 @@ public static String okNameForRegularGetter(AnnotatedMethod am, String name, return null; } } - return stdNaming - ? stdManglePropertyName(name, 3) - : legacyManglePropertyName(name, 3); + return stdManglePropertyName(name, 3); } return null; } - /** - * @since 2.5 - */ - public static String okNameForIsGetter(AnnotatedMethod am, String name, - boolean stdNaming) + public static String okNameForIsGetter(AnnotatedMember am, String name) { if (name.startsWith("is")) { // plus, must return a boolean Class rt = am.getRawType(); if (rt == Boolean.class || rt == Boolean.TYPE) { - return stdNaming - ? stdManglePropertyName(name, 2) - : legacyManglePropertyName(name, 2); + return stdManglePropertyName(name, 2); } } return null; } - /** - * @since 2.5 - */ - @Deprecated // since 2.9, not used any more - public static String okNameForSetter(AnnotatedMethod am, boolean stdNaming) { - String name = okNameForMutator(am, "set", stdNaming); - if ((name != null) - // 26-Nov-2009, tatu: need to suppress this internal groovy method - && (!"metaClass".equals(name) || !isGroovyMetaClassSetter(am))) { - return name; - } - return null; - } - - /** - * @since 2.5 - */ - public static String okNameForMutator(AnnotatedMethod am, String prefix, - boolean stdNaming) { + public static String okNameForMutator(AnnotatedMember am, String prefix) + { String name = am.getName(); if (name.startsWith(prefix)) { - return stdNaming - ? stdManglePropertyName(name, prefix.length()) - : legacyManglePropertyName(name, prefix.length()); + return stdManglePropertyName(name, prefix.length()); } return null; } @@ -124,8 +90,6 @@ public static String okNameForMutator(AnnotatedMethod am, String prefix, * and for structured (Maps, Collections, arrays) and reference types, criteria * {@link com.fasterxml.jackson.annotation.JsonInclude.Include#NON_DEFAULT} * is used. - * - * @since 2.7 */ public static Object getDefaultValue(JavaType type) { @@ -172,7 +136,7 @@ public static Object getDefaultValue(JavaType type) * indeed injectect by Cglib. We do this by verifying that the * result type is "net.sf.cglib.proxy.Callback[]" */ - protected static boolean isCglibGetCallbacks(AnnotatedMethod am) + protected static boolean isCglibGetCallbacks(AnnotatedMember am) { Class rt = am.getRawType(); // Ok, first: must return an array type @@ -197,21 +161,10 @@ protected static boolean isCglibGetCallbacks(AnnotatedMethod am) return false; } - /** - * Similar to {@link #isCglibGetCallbacks}, need to suppress - * a cyclic reference. - */ - protected static boolean isGroovyMetaClassSetter(AnnotatedMethod am) - { - Class argType = am.getRawParameterType(0); - String pkgName = ClassUtil.getPackageName(argType); - return (pkgName != null) && pkgName.startsWith("groovy.lang"); - } - /** * Another helper method to deal with Groovy's problematic metadata accessors */ - protected static boolean isGroovyMetaClassGetter(AnnotatedMethod am) + protected static boolean isGroovyMetaClassGetter(AnnotatedMember am) { String pkgName = ClassUtil.getPackageName(am.getRawType()); return (pkgName != null) && pkgName.startsWith("groovy.lang"); @@ -223,45 +176,9 @@ protected static boolean isGroovyMetaClassGetter(AnnotatedMethod am) /********************************************************** */ - /** - * Method called to figure out name of the property, given - * corresponding suggested name based on a method or field name. - * - * @param basename Name of accessor/mutator method, not including prefix - * ("get"/"is"/"set") - */ - protected static String legacyManglePropertyName(final String basename, final int offset) - { - final int end = basename.length(); - if (end == offset) { // empty name, nope - return null; - } - // next check: is the first character upper case? If not, return as is - char c = basename.charAt(offset); - char d = Character.toLowerCase(c); - - if (c == d) { - return basename.substring(offset); - } - // otherwise, lower case initial chars. Common case first, just one char - StringBuilder sb = new StringBuilder(end - offset); - sb.append(d); - int i = offset+1; - for (; i < end; ++i) { - c = basename.charAt(i); - d = Character.toLowerCase(c); - if (c == d) { - sb.append(basename, i, end); - break; - } - sb.append(d); - } - return sb.toString(); - } - - /** - * @since 2.5 - */ + // 24-Sep-2017, tatu: note that "std" here refers to earlier (1.x, 2.x) distinction + // between "legacy" (slightly non-conforming) and "std" (fully conforming): with 3.x + // only latter exists. protected static String stdManglePropertyName(final String basename, final int offset) { final int end = basename.length(); diff --git a/src/main/java/com/fasterxml/jackson/databind/util/ClassUtil.java b/src/main/java/com/fasterxml/jackson/databind/util/ClassUtil.java index 28e88c5fac..20917b80ac 100644 --- a/src/main/java/com/fasterxml/jackson/databind/util/ClassUtil.java +++ b/src/main/java/com/fasterxml/jackson/databind/util/ClassUtil.java @@ -8,6 +8,8 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.StreamWriteFeature; +import com.fasterxml.jackson.core.util.Named; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonMappingException; @@ -28,9 +30,6 @@ public final class ClassUtil /********************************************************** */ - /** - * @since 2.7 - */ @SuppressWarnings("unchecked") public static Iterator emptyIterator() { return (Iterator) EMPTY_ITERATOR; @@ -53,8 +52,6 @@ public static Iterator emptyIterator() { * * @param endBefore Super-type to NOT include in results, if any; when * encountered, will be ignored (and no super types are checked). - * - * @since 2.7 */ public static List findSuperTypes(JavaType type, Class endBefore, boolean addClassItself) { @@ -66,9 +63,6 @@ public static List findSuperTypes(JavaType type, Class endBefore, return result; } - /** - * @since 2.7 - */ public static List> findRawSuperTypes(Class cls, Class endBefore, boolean addClassItself) { if ((cls == null) || (cls == endBefore) || (cls == Object.class)) { return Collections.emptyList(); @@ -82,8 +76,6 @@ public static List> findRawSuperTypes(Class cls, Class endBefore, * Method for finding all super classes (but not super interfaces) of given class, * starting with the immediate super class and ending in the most distant one. * Class itself is included if addClassItself is true. - * - * @since 2.7 */ public static List> findSuperClasses(Class cls, Class endBefore, boolean addClassItself) { @@ -102,17 +94,6 @@ public static List> findSuperClasses(Class cls, Class endBefore, return result; } - @Deprecated // since 2.7 - public static List> findSuperTypes(Class cls, Class endBefore) { - return findSuperTypes(cls, endBefore, new ArrayList>(8)); - } - - @Deprecated // since 2.7 - public static List> findSuperTypes(Class cls, Class endBefore, List> result) { - _addRawSuperTypes(cls, endBefore, result, false); - return result; - } - private static void _addSuperTypes(JavaType type, Class endBefore, Collection result, boolean addClassItself) { @@ -274,8 +255,7 @@ public static boolean isCollectionMapOrArray(Class type) } public static boolean isBogusClass(Class cls) { - return (cls == Void.class || cls == Void.TYPE - || cls == com.fasterxml.jackson.databind.annotation.NoClass.class); + return (cls == Void.class || cls == Void.TYPE); } public static boolean isNonStaticInnerClass(Class cls) { @@ -283,25 +263,16 @@ public static boolean isNonStaticInnerClass(Class cls) { && (getEnclosingClass(cls) != null); } - /** - * @since 2.7 - */ public static boolean isObjectOrPrimitive(Class cls) { return (cls == CLS_OBJECT) || cls.isPrimitive(); } - /** - * @since 2.9 - */ public static boolean hasClass(Object inst, Class raw) { // 10-Nov-2016, tatu: Could use `Class.isInstance()` if we didn't care // about being exactly that type return (inst != null) && (inst.getClass() == raw); } - /** - * @since 2.9 - */ public static void verifyMustOverride(Class expType, Object instance, String method) { @@ -312,35 +283,6 @@ public static void verifyMustOverride(Class expType, Object instance, } } - /* - /********************************************************** - /* Method type detection methods - /********************************************************** - */ - - /** - * @deprecated Since 2.6 not used; may be removed before 3.x - */ - @Deprecated // since 2.6 - public static boolean hasGetterSignature(Method m) - { - // First: static methods can't be getters - if (Modifier.isStatic(m.getModifiers())) { - return false; - } - // Must take no args - Class[] pts = m.getParameterTypes(); - if (pts != null && pts.length != 0) { - return false; - } - // Can't be a void method - if (Void.TYPE == m.getReturnType()) { - return false; - } - // Otherwise looks ok: - return true; - } - /* /********************************************************** /* Exception handling; simple re-throw @@ -350,8 +292,6 @@ public static boolean hasGetterSignature(Method m) /** * Helper method that will check if argument is an {@link Error}, * and if so, (re)throw it; otherwise just return - * - * @since 2.9 */ public static Throwable throwIfError(Throwable t) { if (t instanceof Error) { @@ -363,8 +303,6 @@ public static Throwable throwIfError(Throwable t) { /** * Helper method that will check if argument is an {@link RuntimeException}, * and if so, (re)throw it; otherwise just return - * - * @since 2.9 */ public static Throwable throwIfRTE(Throwable t) { if (t instanceof RuntimeException) { @@ -376,8 +314,6 @@ public static Throwable throwIfRTE(Throwable t) { /** * Helper method that will check if argument is an {@link IOException}, * and if so, (re)throw it; otherwise just return - * - * @since 2.9 */ public static Throwable throwIfIOE(Throwable t) throws IOException { if (t instanceof IOException) { @@ -406,10 +342,7 @@ public static Throwable getRootCause(Throwable t) /** * Method that works like by calling {@link #getRootCause} and then - * either throwing it (if instanceof {@link IOException}), or - * return. - * - * @since 2.8 + * either throwing it (if instanceof {@link IOException}), or return. */ public static Throwable throwRootCauseIfIOE(Throwable t) throws IOException { return throwIfIOE(getRootCause(t)); @@ -420,7 +353,7 @@ public static Throwable throwRootCauseIfIOE(Throwable t) throws IOException { * is a checked exception; otherwise (runtime exception or error) throw as is */ public static void throwAsIAE(Throwable t) { - throwAsIAE(t, t.getMessage()); + throwAsIAE(t, exceptionMessage(t)); } /** @@ -435,15 +368,12 @@ public static void throwAsIAE(Throwable t, String msg) throw new IllegalArgumentException(msg, t); } - /** - * @since 2.9 - */ public static T throwAsMappingException(DeserializationContext ctxt, IOException e0) throws JsonMappingException { if (e0 instanceof JsonMappingException) { throw (JsonMappingException) e0; } - JsonMappingException e = JsonMappingException.from(ctxt, e0.getMessage()); + JsonMappingException e = JsonMappingException.from(ctxt, exceptionMessage(e0)); e.initCause(e0); throw e; } @@ -474,16 +404,13 @@ public static void unwrapAndThrowAsIAE(Throwable t, String msg) * error conditions tend to be hard to diagnose. However, it is often the * case that output state may be corrupt so we need to be prepared for * secondary exception without masking original one. - * - * @since 2.8 */ public static void closeOnFailAndThrowAsIOE(JsonGenerator g, Exception fail) throws IOException { - /* 04-Mar-2014, tatu: Let's try to prevent auto-closing of - * structures, which typically causes more damage. - */ - g.disable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT); + // 04-Mar-2014, tatu: Let's try to prevent auto-closing of + // structures, which typically causes more damage. + g.disable(StreamWriteFeature.AUTO_CLOSE_CONTENT); try { g.close(); } catch (Exception e) { @@ -500,15 +427,13 @@ public static void closeOnFailAndThrowAsIOE(JsonGenerator g, Exception fail) * error conditions tend to be hard to diagnose. However, it is often the * case that output state may be corrupt so we need to be prepared for * secondary exception without masking original one. - * - * @since 2.8 */ public static void closeOnFailAndThrowAsIOE(JsonGenerator g, Closeable toClose, Exception fail) throws IOException { if (g != null) { - g.disable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT); + g.disable(StreamWriteFeature.AUTO_CLOSE_CONTENT); try { g.close(); } catch (Exception e) { @@ -556,7 +481,8 @@ public static T createInstance(Class cls, boolean canFixAccess) try { return ctor.newInstance(); } catch (Exception e) { - ClassUtil.unwrapAndThrowAsIAE(e, "Failed to instantiate class "+cls.getName()+", problem: "+e.getMessage()); + ClassUtil.unwrapAndThrowAsIAE(e, "Failed to instantiate class "+cls.getName()+", problem: " + +exceptionMessage(e)); return null; } } @@ -578,7 +504,8 @@ public static Constructor findConstructor(Class cls, boolean forceAcce } catch (NoSuchMethodException e) { ; } catch (Exception e) { - ClassUtil.unwrapAndThrowAsIAE(e, "Failed to find default constructor of class "+cls.getName()+", problem: "+e.getMessage()); + ClassUtil.unwrapAndThrowAsIAE(e, "Failed to find default constructor of class "+cls.getName() + +", problem: "+exceptionMessage(e)); } return null; } @@ -713,8 +640,6 @@ public static String nameOf(Class cls) { /** * Returns either backtick-quoted `named.getName()` (if `named` not null), * or "[null]" if `named` is null. - * - * @since 2.9 */ public static String nameOf(Named named) { if (named == null) { @@ -731,8 +656,6 @@ public static String nameOf(Named named) { /** * Returns either `text` or [null]. - * - * @since 2.9 */ public static String backticked(String text) { if (text == null) { @@ -874,19 +797,6 @@ public static Class primitiveType(Class type) /********************************************************** */ - /** - * Equivalent to call: - *
    -     *   checkAndFixAccess(member, false);
    -     *
    - * - * @deprecated Since 2.7 call variant that takes boolean flag. - */ - @Deprecated - public static void checkAndFixAccess(Member member) { - checkAndFixAccess(member, false); - } - /** * Method that is called if a {@link Member} may need forced access, * to force a field, method or constructor to be accessible: this @@ -895,8 +805,6 @@ public static void checkAndFixAccess(Member member) { * @param member Accessor to call setAccessible() on. * @param force Whether to always try to make accessor accessible (true), * or only if needed as per access rights (false) - * - * @since 2.7 */ public static void checkAndFixAccess(Member member, boolean force) { @@ -918,7 +826,8 @@ public static void checkAndFixAccess(Member member, boolean force) // Google App Engine); so let's only fail if we really needed it... if (!ao.isAccessible()) { Class declClass = member.getDeclaringClass(); - throw new IllegalArgumentException("Cannot access "+member+" (from class "+declClass.getName()+"; failed to set access: "+se.getMessage()); + throw new IllegalArgumentException("Cannot access "+member+" (from class "+declClass.getName() + +"; failed to set access: "+exceptionMessage(se)); } } } diff --git a/src/main/java/com/fasterxml/jackson/databind/util/CompactStringObjectMap.java b/src/main/java/com/fasterxml/jackson/databind/util/CompactStringObjectMap.java index f27c0c72dc..f4136f44f0 100644 --- a/src/main/java/com/fasterxml/jackson/databind/util/CompactStringObjectMap.java +++ b/src/main/java/com/fasterxml/jackson/databind/util/CompactStringObjectMap.java @@ -11,11 +11,9 @@ *

    * Generics are not used to avoid bridge methods and since these maps * are not exposed as part of external API. - * - * @since 2.6 */ public final class CompactStringObjectMap - implements java.io.Serializable // since 2.6.2 + implements java.io.Serializable { private static final long serialVersionUID = 1L; @@ -36,7 +34,7 @@ private CompactStringObjectMap(int hashMask, int spillCount, Object[] hashArea) _hashArea = hashArea; } - public static CompactStringObjectMap construct(Map all) + public static CompactStringObjectMap construct(Map all) { if (all.isEmpty()) { // can this happen? return EMPTY; @@ -50,7 +48,7 @@ public static CompactStringObjectMap construct(Map all) Object[] hashArea = new Object[alloc]; int spillCount = 0; - for (Map.Entry entry : all.entrySet()) { + for (Map.Entry entry : all.entrySet()) { String key = entry.getKey(); int slot = key.hashCode() & mask; @@ -124,9 +122,6 @@ private final Object _find2(String key, int slot, Object match) return null; } - /** - * @since 2.9 - */ public Object findCaseInsensitive(String key) { for (int i = 0, end = _hashArea.length; i < end; i += 2) { Object k2 = _hashArea[i]; diff --git a/src/main/java/com/fasterxml/jackson/databind/util/ConstantValueInstantiator.java b/src/main/java/com/fasterxml/jackson/databind/util/ConstantValueInstantiator.java index 0f8abb7cb1..c5d5e25f9d 100644 --- a/src/main/java/com/fasterxml/jackson/databind/util/ConstantValueInstantiator.java +++ b/src/main/java/com/fasterxml/jackson/databind/util/ConstantValueInstantiator.java @@ -9,8 +9,6 @@ * Trivial {@link ValueInstantiator} implementation that will simply return constant * {@code Object} it is configured with. May be used as-is, or as base class to override * simplistic behavior further. - * - * @since 2.9.4 */ public class ConstantValueInstantiator extends ValueInstantiator { @@ -35,4 +33,9 @@ public Class getValueClass() { public Object createUsingDefault(DeserializationContext ctxt) throws IOException { return _value; } + + @Override + public String getValueTypeDesc() { + return _value.getClass().getName(); + } } diff --git a/src/main/java/com/fasterxml/jackson/databind/util/Converter.java b/src/main/java/com/fasterxml/jackson/databind/util/Converter.java index 102a487fed..8c865de3ec 100644 --- a/src/main/java/com/fasterxml/jackson/databind/util/Converter.java +++ b/src/main/java/com/fasterxml/jackson/databind/util/Converter.java @@ -15,7 +15,7 @@ * @param Result type from conversion * * @see com.fasterxml.jackson.databind.ser.std.StdDelegatingSerializer - * @see com.fasterxml.jackson.databind.deser.std.StdDelegatingDeserializer + * @see com.fasterxml.jackson.databind.deser.std.StdConvertingDeserializer * * @since 2.1 */ diff --git a/src/main/java/com/fasterxml/jackson/databind/util/EnumResolver.java b/src/main/java/com/fasterxml/jackson/databind/util/EnumResolver.java index f95ea55f46..0f05917533 100644 --- a/src/main/java/com/fasterxml/jackson/databind/util/EnumResolver.java +++ b/src/main/java/com/fasterxml/jackson/databind/util/EnumResolver.java @@ -54,19 +54,9 @@ public static EnumResolver constructFor(Class> enumCls, AnnotationIntros return new EnumResolver(enumCls, enumValues, map, defaultEnum); } - /** - * @deprecated Since 2.8, use {@link #constructUsingToString(Class, AnnotationIntrospector)} instead - */ - @Deprecated - public static EnumResolver constructUsingToString(Class> enumCls) { - return constructUsingToString(enumCls, null); - } - /** * Factory method for constructing resolver that maps from Enum.toString() into * Enum value - * - * @since 2.8 */ public static EnumResolver constructUsingToString(Class> enumCls, AnnotationIntrospector ai) @@ -82,9 +72,6 @@ public static EnumResolver constructUsingToString(Class> enumCls, return new EnumResolver(enumCls, enumValues, map, defaultEnum); } - /** - * @since 2.9 - */ public static EnumResolver constructUsingMethod(Class> enumCls, AnnotatedMember accessor, AnnotationIntrospector ai) diff --git a/src/main/java/com/fasterxml/jackson/databind/util/FullyNamed.java b/src/main/java/com/fasterxml/jackson/databind/util/FullyNamed.java new file mode 100644 index 0000000000..f5c22432e8 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/util/FullyNamed.java @@ -0,0 +1,20 @@ +package com.fasterxml.jackson.databind.util; + +import com.fasterxml.jackson.core.util.Named; + +import com.fasterxml.jackson.databind.PropertyName; + +/** + * Extension over {@link Named} to expose full name; most relevant + * for formats like XML that use namespacing. + * + * @since 3.0 + */ +public interface FullyNamed extends Named +{ + PropertyName getFullName(); + + default boolean hasName(PropertyName name) { + return getFullName().equals(name); + } +} diff --git a/src/main/java/com/fasterxml/jackson/databind/util/ISO8601DateFormat.java b/src/main/java/com/fasterxml/jackson/databind/util/ISO8601DateFormat.java deleted file mode 100644 index f36004fad6..0000000000 --- a/src/main/java/com/fasterxml/jackson/databind/util/ISO8601DateFormat.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.fasterxml.jackson.databind.util; - -import java.text.*; -import java.util.Date; -import java.util.GregorianCalendar; - -/** - * Provide a fast thread-safe formatter/parser DateFormat for ISO8601 dates ONLY. - * It was mainly done to be used with Jackson JSON Processor. - *

    - * Watch out for clone implementation that returns itself. - *

    - * All other methods but parse and format and clone are undefined behavior. - * - * @deprecated Use {@link com.fasterxml.jackson.databind.util.StdDateFormat} instead - */ -@Deprecated // since 2.9 -public class ISO8601DateFormat extends DateFormat -{ - private static final long serialVersionUID = 1L; - - public ISO8601DateFormat() { - this.numberFormat = new DecimalFormat();; - this.calendar = new GregorianCalendar();; - } - - @Override - public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) { - toAppendTo.append(ISO8601Utils.format(date)); - return toAppendTo; - } - - @Override - public Date parse(String source, ParsePosition pos) { - try { - return ISO8601Utils.parse(source, pos); - } - catch (ParseException e) { - return null; - } - } - - //supply our own parse(String) since pos isn't updated during parsing, - //but the exception should have the right error offset. - @Override - public Date parse(String source) throws ParseException { - return ISO8601Utils.parse(source, new ParsePosition(0)); - } - - @Override - public Object clone() { - return this; - } -} diff --git a/src/main/java/com/fasterxml/jackson/databind/util/ISO8601Utils.java b/src/main/java/com/fasterxml/jackson/databind/util/ISO8601Utils.java deleted file mode 100644 index 8078317a6f..0000000000 --- a/src/main/java/com/fasterxml/jackson/databind/util/ISO8601Utils.java +++ /dev/null @@ -1,320 +0,0 @@ -package com.fasterxml.jackson.databind.util; - -import java.text.ParseException; -import java.text.ParsePosition; -import java.util.*; - -/** - * 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 this specification - */ -@Deprecated // since 2.9 -public class ISO8601Utils -{ - protected final static int DEF_8601_LEN = "yyyy-MM-ddThh:mm:ss.SSS+00:00".length(); - - /** - * Timezone we use for 'Z' in ISO-8601 date/time values: since 2.7 - * {@link #TIMEZONE_UTC}; with earlier versions up to 2.7 was {@link #TIMEZONE_GMT}. - */ - private static final TimeZone TIMEZONE_Z = TimeZone.getTimeZone("UTC"); - - /* - /********************************************************** - /* Formatting - /********************************************************** - */ - - /** - * Format a date into 'yyyy-MM-ddThh:mm:ssZ' (default timezone, no milliseconds precision) - * - * @param date the date to format - * @return the date formatted as 'yyyy-MM-ddThh:mm:ssZ' - */ - public static String format(Date date) { - return format(date, false, TIMEZONE_Z); - } - - /** - * Format a date into 'yyyy-MM-ddThh:mm:ss[.sss]Z' (GMT timezone) - * - * @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' - */ - public static String format(Date date, boolean millis) { - return format(date, millis, TIMEZONE_Z); - } - - @Deprecated // since 2.9 - public static String format(Date date, boolean millis, TimeZone tz) { - return format(date, millis, tz, Locale.US); - } - - /** - * Format date into yyyy-MM-ddThh:mm:ss[.sss][Z|[+-]hh:mm] - * - * @param date the date to format - * @param millis true to include millis precision otherwise false - * @param tz timezone to use for the formatting (UTC will produce 'Z') - * @return the date formatted as yyyy-MM-ddThh:mm:ss[.sss][Z|[+-]hh:mm] - * - * @since 2.9 - */ - public static String format(Date date, boolean millis, TimeZone tz, Locale loc) { - Calendar calendar = new GregorianCalendar(tz, loc); - calendar.setTime(date); - - // estimate capacity of buffer as close as we can (yeah, that's pedantic ;) - StringBuilder sb = new StringBuilder(30); - sb.append(String.format( - "%04d-%02d-%02dT%02d:%02d:%02d", - calendar.get(Calendar.YEAR), - calendar.get(Calendar.MONTH) + 1, - calendar.get(Calendar.DAY_OF_MONTH), - calendar.get(Calendar.HOUR_OF_DAY), - calendar.get(Calendar.MINUTE), - calendar.get(Calendar.SECOND) - )); - if (millis) { - sb.append(String.format(".%03d", calendar.get(Calendar.MILLISECOND))); - } - - int offset = tz.getOffset(calendar.getTimeInMillis()); - if (offset != 0) { - int hours = Math.abs((offset / (60 * 1000)) / 60); - int minutes = Math.abs((offset / (60 * 1000)) % 60); - sb.append(String.format("%c%02d:%02d", - (offset < 0 ? '-' : '+'), - hours, minutes)); - } else { - sb.append('Z'); - } - return sb.toString(); - } - - /* - /********************************************************** - /* Parsing - /********************************************************** - */ - - /** - * 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 { - Exception fail = null; - try { - int offset = pos.getIndex(); - - // extract year - int year = parseInt(date, offset, offset += 4); - if (checkOffset(date, offset, '-')) { - offset += 1; - } - - // extract month - int month = parseInt(date, offset, offset += 2); - if (checkOffset(date, offset, '-')) { - offset += 1; - } - - // extract day - 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 the value has no time component (and no time zone), we are done - boolean hasT = checkOffset(date, offset, 'T'); - - if (!hasT && (date.length() <= offset)) { - Calendar calendar = new GregorianCalendar(year, month - 1, day); - - pos.setIndex(offset); - return calendar.getTime(); - } - - if (hasT) { - - // 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); - if (seconds > 59 && seconds < 63) seconds = 59; // truncate up to 3 leap seconds - // milliseconds can be optional in the format - if (checkOffset(date, offset, '.')) { - offset += 1; - int endOffset = indexOfNonDigit(date, offset + 1); // assume at least one digit - int parseEndOffset = Math.min(endOffset, offset + 3); // parse up to 3 digits - int fraction = parseInt(date, offset, parseEndOffset); - // compensate for "missing" digits - switch (parseEndOffset - offset) { // number of digits parsed - case 2: - milliseconds = fraction * 10; - break; - case 1: - milliseconds = fraction * 100; - break; - default: - milliseconds = fraction; - } - offset = endOffset; - } - } - } - } - - // extract timezone - if (date.length() <= offset) { - throw new IllegalArgumentException("No time zone indicator"); - } - - TimeZone timezone = null; - char timezoneIndicator = date.charAt(offset); - - if (timezoneIndicator == 'Z') { - timezone = TIMEZONE_Z; - offset += 1; - } else if (timezoneIndicator == '+' || timezoneIndicator == '-') { - String timezoneOffset = date.substring(offset); - offset += timezoneOffset.length(); - // 18-Jun-2015, tatu: Minor simplification, skip offset of "+0000"/"+00:00" - if ("+0000".equals(timezoneOffset) || "+00:00".equals(timezoneOffset)) { - timezone = TIMEZONE_Z; - } else { - // 18-Jun-2015, tatu: Looks like offsets only work from GMT, not UTC... - // not sure why, but that's the way it looks. Further, Javadocs for - // `java.util.TimeZone` specifically instruct use of GMT as base for - // custom timezones... odd. - String timezoneId = "GMT" + timezoneOffset; -// String timezoneId = "UTC" + timezoneOffset; - - timezone = TimeZone.getTimeZone(timezoneId); - - String act = timezone.getID(); - if (!act.equals(timezoneId)) { - /* 22-Jan-2015, tatu: Looks like canonical version has colons, but we may be given - * one without. If so, don't sweat. - * Yes, very inefficient. Hopefully not hit often. - * If it becomes a perf problem, add 'loose' comparison instead. - */ - String cleaned = act.replace(":", ""); - if (!cleaned.equals(timezoneId)) { - throw new IndexOutOfBoundsException("Mismatching time zone indicator: "+timezoneId+" given, resolves to " - +timezone.getID()); - } - } - } - } else { - throw new IndexOutOfBoundsException("Invalid time zone indicator '" + timezoneIndicator+"'"); - } - - Calendar calendar = new GregorianCalendar(timezone); - calendar.setLenient(false); - calendar.set(Calendar.YEAR, year); - calendar.set(Calendar.MONTH, month - 1); - calendar.set(Calendar.DAY_OF_MONTH, day); - calendar.set(Calendar.HOUR_OF_DAY, hour); - calendar.set(Calendar.MINUTE, minutes); - calendar.set(Calendar.SECOND, seconds); - calendar.set(Calendar.MILLISECOND, milliseconds); - - 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. - } catch (Exception e) { - fail = e; - } - String input = (date == null) ? null : ('"' + date + '"'); - String msg = fail.getMessage(); - if (msg == null || msg.isEmpty()) { - msg = "("+fail.getClass().getName()+")"; - } - ParseException ex = new ParseException("Failed to parse date " + input + ": " + msg, pos.getIndex()); - ex.initCause(fail); - throw ex; - } - - /** - * 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 - * @return true if the expected character exist at the given 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 beginIndex the start 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 - */ - private static int parseInt(String value, int beginIndex, int endIndex) throws NumberFormatException { - if (beginIndex < 0 || endIndex > value.length() || beginIndex > endIndex) { - throw new NumberFormatException(value); - } - // use same logic as in Integer.parseInt() but less generic we're not supporting negative values - int i = beginIndex; - int result = 0; - int digit; - if (i < endIndex) { - digit = Character.digit(value.charAt(i++), 10); - if (digit < 0) { - throw new NumberFormatException("Invalid number: " + value.substring(beginIndex, endIndex)); - } - result = -digit; - } - while (i < endIndex) { - digit = Character.digit(value.charAt(i++), 10); - if (digit < 0) { - throw new NumberFormatException("Invalid number: " + value.substring(beginIndex, endIndex)); - } - result *= 10; - result -= digit; - } - return -result; - } - - /** - * Returns the index of the first character in the string that is not a digit, starting at offset. - */ - private static int indexOfNonDigit(String string, int offset) { - for (int i = offset; i < string.length(); i++) { - char c = string.charAt(i); - if (c < '0' || c > '9') return i; - } - return string.length(); - } -} diff --git a/src/main/java/com/fasterxml/jackson/databind/util/JSONPObject.java b/src/main/java/com/fasterxml/jackson/databind/util/JSONPObject.java index 29b2d150de..6e699c8841 100644 --- a/src/main/java/com/fasterxml/jackson/databind/util/JSONPObject.java +++ b/src/main/java/com/fasterxml/jackson/databind/util/JSONPObject.java @@ -3,7 +3,7 @@ import java.io.IOException; import com.fasterxml.jackson.core.*; - +import com.fasterxml.jackson.core.util.JsonpCharacterEscapes; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; @@ -69,7 +69,7 @@ public void serialize(JsonGenerator gen, SerializerProvider provider) gen.writeRaw('('); if (_value == null) { - provider.defaultSerializeNull(gen); + provider.defaultSerializeNullValue(gen); } else { // NOTE: Escape line-separator characters that break JSONP only if no custom character escapes are set. // If custom escapes are in place JSONP-breaking characters will not be escaped and it is recommended to @@ -81,9 +81,9 @@ public void serialize(JsonGenerator gen, SerializerProvider provider) try { if (_serializationType != null) { - provider.findTypedValueSerializer(_serializationType, true, null).serialize(_value, gen, provider); + provider.findTypedValueSerializer(_serializationType, true).serialize(_value, gen, provider); } else { - provider.findTypedValueSerializer(_value.getClass(), true, null).serialize(_value, gen, provider); + provider.findTypedValueSerializer(_value.getClass(), true).serialize(_value, gen, provider); } } finally { if (override) { diff --git a/src/main/java/com/fasterxml/jackson/databind/util/JSONWrappedObject.java b/src/main/java/com/fasterxml/jackson/databind/util/JSONWrappedObject.java index 121b1e3688..33352f3f01 100644 --- a/src/main/java/com/fasterxml/jackson/databind/util/JSONWrappedObject.java +++ b/src/main/java/com/fasterxml/jackson/databind/util/JSONWrappedObject.java @@ -60,45 +60,45 @@ public JSONWrappedObject(String prefix, String suffix, Object value, JavaType as } /* - /************************************************************** - /* JsonSerializable(WithType) implementation - /************************************************************** + /********************************************************************** + /* JsonSerializable implementation + /********************************************************************** */ @Override - public void serializeWithType(JsonGenerator jgen, SerializerProvider provider, TypeSerializer typeSer) + public void serializeWithType(JsonGenerator g, SerializerProvider provider, TypeSerializer typeSer) throws IOException, JsonProcessingException { // No type for JSONP wrapping: value serializer will handle typing for value: - serialize(jgen, provider); + serialize(g, provider); } @Override - public void serialize(JsonGenerator jgen, SerializerProvider provider) + public void serialize(JsonGenerator g, SerializerProvider provider) throws IOException, JsonProcessingException { // First, wrapping: - if (_prefix != null) jgen.writeRaw(_prefix); + if (_prefix != null) g.writeRaw(_prefix); if (_value == null) { - provider.defaultSerializeNull(jgen); + provider.defaultSerializeNullValue(g); } else if (_serializationType != null) { - provider.findTypedValueSerializer(_serializationType, true, null).serialize(_value, jgen, provider); + provider.findTypedValueSerializer(_serializationType, true) + .serialize(_value, g, provider); } else { - Class cls = _value.getClass(); - provider.findTypedValueSerializer(cls, true, null).serialize(_value, jgen, provider); + provider.findTypedValueSerializer(_value.getClass(), true) + .serialize(_value, g, provider); } - if (_suffix != null) jgen.writeRaw(_suffix); + if (_suffix != null) g.writeRaw(_suffix); } /* - /************************************************************** + /********************************************************************** /* Accessors - /************************************************************** + /********************************************************************** */ public String getPrefix() { return _prefix; } public String getSuffix() { return _suffix; } public Object getValue() { return _value; } public JavaType getSerializationType() { return _serializationType; } - } diff --git a/src/main/java/com/fasterxml/jackson/databind/util/LinkedNode.java b/src/main/java/com/fasterxml/jackson/databind/util/LinkedNode.java index fa7cdae8bb..11ff05bb9c 100644 --- a/src/main/java/com/fasterxml/jackson/databind/util/LinkedNode.java +++ b/src/main/java/com/fasterxml/jackson/databind/util/LinkedNode.java @@ -2,11 +2,12 @@ /** * Node of a forward-only linked list. - * - * @author tatu */ public final class LinkedNode + implements java.io.Serializable { + private static final long serialVersionUID = 3L; + private final T value; private LinkedNode next; diff --git a/src/main/java/com/fasterxml/jackson/databind/util/Named.java b/src/main/java/com/fasterxml/jackson/databind/util/Named.java deleted file mode 100644 index b3f9eb0a62..0000000000 --- a/src/main/java/com/fasterxml/jackson/databind/util/Named.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.fasterxml.jackson.databind.util; - -/** - * Simple tag interface mostly to allow sorting by name. - */ -public interface Named { - public String getName(); -} diff --git a/src/main/java/com/fasterxml/jackson/databind/util/RootNameLookup.java b/src/main/java/com/fasterxml/jackson/databind/util/RootNameLookup.java index c2748d5d73..5b0c46a14a 100644 --- a/src/main/java/com/fasterxml/jackson/databind/util/RootNameLookup.java +++ b/src/main/java/com/fasterxml/jackson/databind/util/RootNameLookup.java @@ -16,10 +16,10 @@ public class RootNameLookup implements java.io.Serializable * For efficient operation, let's try to minimize number of times we * need to introspect root element name to use. */ - protected transient LRUMap _rootNames; + protected transient SimpleLookupCache _rootNames; public RootNameLookup() { - _rootNames = new LRUMap(20, 200); + _rootNames = new SimpleLookupCache(20, 200); } public PropertyName findRootName(JavaType rootType, MapperConfig config) { diff --git a/src/main/java/com/fasterxml/jackson/databind/util/LRUMap.java b/src/main/java/com/fasterxml/jackson/databind/util/SimpleLookupCache.java similarity index 58% rename from src/main/java/com/fasterxml/jackson/databind/util/LRUMap.java rename to src/main/java/com/fasterxml/jackson/databind/util/SimpleLookupCache.java index e67a76543e..b9f79eaa07 100644 --- a/src/main/java/com/fasterxml/jackson/databind/util/LRUMap.java +++ b/src/main/java/com/fasterxml/jackson/databind/util/SimpleLookupCache.java @@ -1,16 +1,21 @@ package com.fasterxml.jackson.databind.util; -import java.io.*; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.BiConsumer; + +import com.fasterxml.jackson.core.util.Snapshottable; /** - * Helper for simple bounded maps used for reusing lookup values. + * Synchronized cache with bounded size: used for reusing lookup values + * and lazily instantiated reusable items. *

    * Note that serialization behavior is such that contents are NOT serialized, * on assumption that all use cases are for caching where persistence - * does not make sense. The only thing serialized is the cache size of Map. + * does not make sense. The only thing serialized is the initial and maximum + * size of the contents. *

    - * NOTE: since version 2.4.2, this is NOT an LRU-based at all; reason + * NOTE: this is NOT an LRU-based (in 2.x it was named LRUMap); 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 efficient handling of entries. @@ -20,22 +25,52 @@ * 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 - implements java.io.Serializable +public class SimpleLookupCache + implements Snapshottable>, + java.io.Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 3L; - protected final transient int _maxEntries; + protected final int _initialEntries, _maxEntries; protected final transient ConcurrentHashMap _map; - public LRUMap(int initialEntries, int maxEntries) + /* + /********************************************************************** + /* Life-cycle + /********************************************************************** + */ + + public SimpleLookupCache(int initialEntries, int maxEntries) { + _initialEntries = initialEntries; + _maxEntries = maxEntries; // We'll use concurrency level of 4, seems reasonable _map = new ConcurrentHashMap(initialEntries, 0.8f, 4); - _maxEntries = maxEntries; } + // for JDK serialization + protected Object readResolve() { + return snapshot(); + } + + @Override + public SimpleLookupCache snapshot() { + return new SimpleLookupCache(_initialEntries, _maxEntries); + } + + public void contents(BiConsumer consumer) { + for (Map.Entry entry : _map.entrySet()) { + consumer.accept(entry.getKey(), entry.getValue()); + } + } + + /* + /********************************************************************** + /* Public API + /********************************************************************** + */ + public V put(K key, V value) { if (_map.size() >= _maxEntries) { // double-locking, yes, but safe here; trying to avoid "clear storms" @@ -48,9 +83,6 @@ public V put(K key, V value) { 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: @@ -69,30 +101,4 @@ public V putIfAbsent(K key, V value) { public void clear() { _map.clear(); } public int size() { return _map.size(); } - - /* - /********************************************************** - /* Serializable overrides - /********************************************************** - */ - - /** - * Ugly hack, to work through the requirement that _value is indeed final, - * and that JDK serialization won't call ctor(s) if Serializable is implemented. - * - * @since 2.1 - */ - protected transient int _jdkSerializeMaxEntries; - - private void readObject(ObjectInputStream in) throws IOException { - _jdkSerializeMaxEntries = in.readInt(); - } - - private void writeObject(ObjectOutputStream out) throws IOException { - out.writeInt(_jdkSerializeMaxEntries); - } - - protected Object readResolve() { - return new LRUMap(_jdkSerializeMaxEntries, _jdkSerializeMaxEntries); - } } diff --git a/src/main/java/com/fasterxml/jackson/databind/util/StdDateFormat.java b/src/main/java/com/fasterxml/jackson/databind/util/StdDateFormat.java index 02afe5fcea..12b83231f6 100644 --- a/src/main/java/com/fasterxml/jackson/databind/util/StdDateFormat.java +++ b/src/main/java/com/fasterxml/jackson/databind/util/StdDateFormat.java @@ -129,8 +129,6 @@ public class StdDateFormat *

    * Cannot be `final` because {@link #setLenient(boolean)} returns * `void`. - * - * @since 2.7 */ protected Boolean _lenient; @@ -145,10 +143,10 @@ public class StdDateFormat /** * Whether the TZ offset must be formatted with a colon between hours and minutes ({@code HH:mm} format) - * - * @since 2.9.1 + *

    + * NOTE: default changed to `true` in Jackson 3.0; was `false` earlier. */ - private boolean _tzSerializedWithColon = false; + protected boolean _tzSerializedWithColon = true; /* /********************************************************** @@ -160,12 +158,6 @@ public StdDateFormat() { _locale = DEFAULT_LOCALE; } - @Deprecated // since 2.7 - public StdDateFormat(TimeZone tz, Locale loc) { - _timezone = tz; - _locale = loc; - } - protected StdDateFormat(TimeZone tz, Locale loc, Boolean lenient) { this(tz, loc, lenient, false); } @@ -253,35 +245,6 @@ public StdDateFormat clone() { return new StdDateFormat(_timezone, _locale, _lenient, _tzSerializedWithColon); } - /** - * Method for getting a non-shared DateFormat instance - * that uses specified timezone and can handle simple ISO-8601 - * compliant date format. - * - * @since 2.4 - * - * @deprecated Since 2.9 - */ - @Deprecated // since 2.9 - public static DateFormat getISO8601Format(TimeZone tz, Locale loc) { - return _cloneFormat(DATE_FORMAT_ISO8601, DATE_FORMAT_STR_ISO8601, tz, loc, null); - } - - /** - * Method for getting a non-shared DateFormat instance - * that uses specific timezone and can handle RFC-1123 - * compliant date format. - * - * @since 2.4 - * - * @deprecated Since 2.9 - */ - @Deprecated // since 2.9 - public static DateFormat getRFC1123Format(TimeZone tz, Locale loc) { - return _cloneFormat(DATE_FORMAT_RFC1123, DATE_FORMAT_STR_RFC1123, - tz, loc, null); - } - /* /********************************************************** /* Public API, configuration @@ -478,12 +441,11 @@ protected void _format(TimeZone tz, Locale loc, Date date, // 24-Jun-2017, tatu: While `Z` would be conveniently short, older specs // mandate use of full `+0000` // formatted.append('Z'); - if( _tzSerializedWithColon ) { - buffer.append("+00:00"); - } - else { - buffer.append("+0000"); - } + if (_tzSerializedWithColon ) { + buffer.append("+00:00"); + } else { + buffer.append("+0000"); + } } } @@ -565,12 +527,12 @@ public String toPattern() { // same as SimpleDateFormat return sb.toString(); } - @Override // since 2.7[.2], as per [databind#1130] + @Override public boolean equals(Object o) { return (o == this); } - @Override // since 2.7[.2], as per [databind#1130] + @Override public int hashCode() { return System.identityHashCode(this); } 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 f31334e77d..4236e237ca 100644 --- a/src/main/java/com/fasterxml/jackson/databind/util/TokenBuffer.java +++ b/src/main/java/com/fasterxml/jackson/databind/util/TokenBuffer.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.base.ParserMinimalBase; import com.fasterxml.jackson.core.json.JsonWriteContext; +import com.fasterxml.jackson.core.sym.FieldNameMatcher; import com.fasterxml.jackson.core.util.ByteArrayBuilder; import com.fasterxml.jackson.databind.*; @@ -18,10 +19,6 @@ * ones; but not significantly less efficient for larger), highly efficient * for linear iteration and appending. Implemented as segmented/chunked * linked list of tokens; only modifications are via appends. - *

    - * Note that before version 2.0, this class was located in the "core" - * bundle, not data-binding; but since it was only used by data binding, - * was moved here to reduce size of core package */ public class TokenBuffer /* Won't use JsonGeneratorBase, to minimize overhead for validity @@ -29,67 +26,49 @@ public class TokenBuffer */ extends JsonGenerator { - protected final static int DEFAULT_GENERATOR_FEATURES = JsonGenerator.Feature.collectDefaults(); + protected final static int DEFAULT_STREAM_WRITE_FEATURES = StreamWriteFeature.collectDefaults(); /* - /********************************************************** + /********************************************************************** /* Configuration - /********************************************************** + /********************************************************************** */ - /** - * Object codec to use for stream-based object - * conversion through parser/generator interfaces. If null, - * such methods cannot be used. - */ - protected ObjectCodec _objectCodec; - /** * Parse context from "parent" parser (one from which content to buffer is read, * if specified). Used, if available, when reading content, to present full * context as if content was read from the original parser: this is useful * in error reporting and sometimes processing as well. */ - protected JsonStreamContext _parentContext; + protected TokenStreamContext _parentContext; /** * Bit flag composed of bits that indicate which - * {@link com.fasterxml.jackson.core.JsonGenerator.Feature}s + * {@link StreamWriteFeature}s * are enabled. *

    * NOTE: most features have no effect on this class */ - protected int _generatorFeatures; + protected int _streamWriteFeatures; protected boolean _closed; - /** - * @since 2.3 - */ protected boolean _hasNativeTypeIds; - /** - * @since 2.3 - */ protected boolean _hasNativeObjectIds; - /** - * @since 2.3 - */ protected boolean _mayHaveNativeIds; /** * Flag set during construction, if use of {@link BigDecimal} is to be forced * on all floating-point values. - * - * @since 2.7 */ protected boolean _forceBigDecimal; - + /* - /********************************************************** + /********************************************************************** /* Token buffering state - /********************************************************** + /********************************************************************** */ /** @@ -126,31 +105,34 @@ public class TokenBuffer protected boolean _hasNativeId = false; /* - /********************************************************** + /********************************************************************** /* Output state - /********************************************************** + /********************************************************************** */ - protected JsonWriteContext _writeContext; + protected JsonWriteContext _tokenWriteContext; + + // 05-Oct-2017, tatu: need to consider if this needs to be properly linked... + // especially for "convertValue()" use case + /** + * @since 3.0 + */ + protected ObjectWriteContext _objectWriteContext; // = ObjectWriteContext.empty(); /* - /********************************************************** - /* Life-cycle - /********************************************************** + /********************************************************************** + /* Life-cycle: constructors + /********************************************************************** */ /** - * @param codec Object codec to use for stream-based object - * conversion through parser/generator interfaces. If null, - * such methods cannot be used. * @param hasNativeIds Whether resulting {@link JsonParser} (if created) * is considered to support native type and object ids */ - public TokenBuffer(ObjectCodec codec, boolean hasNativeIds) + public TokenBuffer(boolean hasNativeIds) { - _objectCodec = codec; - _generatorFeatures = DEFAULT_GENERATOR_FEATURES; - _writeContext = JsonWriteContext.createRootContext(null); + _streamWriteFeatures = DEFAULT_STREAM_WRITE_FEATURES; + _tokenWriteContext = JsonWriteContext.createRootContext(null); // at first we have just one segment _first = _last = new Segment(); _appendAt = 0; @@ -161,21 +143,27 @@ public TokenBuffer(ObjectCodec codec, boolean hasNativeIds) } /** - * @since 2.3 + * @since 3.0 */ - public TokenBuffer(JsonParser p) { - this(p, null); + protected TokenBuffer(ObjectWriteContext writeContext, boolean hasNativeIds) + { + _objectWriteContext = writeContext; + _streamWriteFeatures = DEFAULT_STREAM_WRITE_FEATURES; + _tokenWriteContext = JsonWriteContext.createRootContext(null); + // at first we have just one segment + _first = _last = new Segment(); + _appendAt = 0; + _hasNativeTypeIds = hasNativeIds; + _hasNativeObjectIds = hasNativeIds; + + _mayHaveNativeIds = _hasNativeTypeIds | _hasNativeObjectIds; } - /** - * @since 2.7 - */ public TokenBuffer(JsonParser p, DeserializationContext ctxt) { - _objectCodec = p.getCodec(); _parentContext = p.getParsingContext(); - _generatorFeatures = DEFAULT_GENERATOR_FEATURES; - _writeContext = JsonWriteContext.createRootContext(null); + _streamWriteFeatures = DEFAULT_STREAM_WRITE_FEATURES; + _tokenWriteContext = JsonWriteContext.createRootContext(null); // at first we have just one segment _first = _last = new Segment(); _appendAt = 0; @@ -186,6 +174,52 @@ public TokenBuffer(JsonParser p, DeserializationContext ctxt) : ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS); } + /* + /********************************************************************** + /* Life-cycle: helper factory methods + /********************************************************************** + */ + + /** + * Specialized factory method used when we are converting values and do not + * have or use "real" parsers or generators. + * + * @since 3.0 + */ + public static TokenBuffer forValueConversion(SerializerProvider prov) + { + // false -> no native Object Ids available (or rather not needed) + return new TokenBuffer(prov, false); + } + + /** + * Specialized factory method used when we are buffering input being read from + * specified token stream and within specified {@link DeserializationContext}. + * + * @since 3.0 + */ + public static TokenBuffer forInputBuffering(JsonParser p, DeserializationContext ctxt) + { + return new TokenBuffer(p, ctxt); + } + + /** + * Specialized factory method used when we are generating token stream for further processing + * without tokens coming from specific input token stream. + * + * @since 3.0 + */ + public static TokenBuffer forGeneration() + { + return new TokenBuffer(false); + } + + /* + /********************************************************************** + /* Life-cycle: initialization + /********************************************************************** + */ + /** * Convenience method, equivalent to: *

    @@ -193,11 +227,9 @@ public TokenBuffer(JsonParser p, DeserializationContext ctxt)
          * b.copyCurrentStructure(p);
          * return b;
          *
    - * - * @since 2.9 */ public static TokenBuffer asCopyOfValue(JsonParser p) throws IOException { - TokenBuffer b = new TokenBuffer(p); + TokenBuffer b = new TokenBuffer(p, null); b.copyCurrentStructure(p); return b; } @@ -207,27 +239,23 @@ public static TokenBuffer asCopyOfValue(JsonParser p) throws IOException { * with contents of this buffer. Usually context is assigned at construction, * based on given parser; but it is not always available, and may not contain * intended context. - * - * @since 2.9 */ - public TokenBuffer overrideParentContext(JsonStreamContext ctxt) { + public TokenBuffer overrideParentContext(TokenStreamContext ctxt) { _parentContext = ctxt; return this; } - /** - * @since 2.7 - */ public TokenBuffer forceUseOfBigDecimal(boolean b) { _forceBigDecimal = b; return this; } - @Override - public Version version() { - return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION; - } - + /* + /********************************************************************** + /* Parser construction + /********************************************************************** + */ + /** * Method used to create a {@link JsonParser} that can read contents * stored in this buffer. Will use default _objectCodec for @@ -239,7 +267,14 @@ public Version version() { * @return Parser that can be used for reading contents stored in this buffer */ public JsonParser asParser() { - return asParser(_objectCodec); + return new Parser(null, this, + _first, _hasNativeTypeIds, _hasNativeObjectIds, _parentContext); + } + + public JsonParser asParser(ObjectReadContext readCtxt) + { + return new Parser(readCtxt, this, + _first, _hasNativeTypeIds, _hasNativeObjectIds, _parentContext); } /** @@ -249,48 +284,39 @@ public JsonParser asParser() { * p.nextToken(); * return p; * - * - * @since 2.9 */ public JsonParser asParserOnFirstToken() throws IOException { - JsonParser p = asParser(_objectCodec); + JsonParser p = asParser(); p.nextToken(); return p; } - /** - * Method used to create a {@link JsonParser} that can read contents - * stored in this buffer. - *

    - * Note: instances are not synchronized, that is, they are not thread-safe - * if there are concurrent appends to the underlying buffer. - * - * @param codec Object codec to use for stream-based object - * conversion through parser/generator interfaces. If null, - * such methods cannot be used. - * - * @return Parser that can be used for reading contents stored in this buffer - */ - public JsonParser asParser(ObjectCodec codec) - { - return new Parser(_first, codec, _hasNativeTypeIds, _hasNativeObjectIds, _parentContext); - } - /** * @param src Parser to use for accessing source information * like location, configured codec */ - public JsonParser asParser(JsonParser src) + public JsonParser asParser(ObjectReadContext readCtxt, JsonParser src) { - Parser p = new Parser(_first, src.getCodec(), _hasNativeTypeIds, _hasNativeObjectIds, _parentContext); + Parser p = new Parser(readCtxt, this, + _first, _hasNativeTypeIds, _hasNativeObjectIds, _parentContext); p.setLocation(src.getTokenLocation()); return p; } + /* + /********************************************************************** + /* Versioned (mostly since buffer is `JsonGenerator` + /********************************************************************** + */ + + @Override + public Version version() { + return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION; + } /* - /********************************************************** + /********************************************************************** /* Additional accessors - /********************************************************** + /********************************************************************** */ public JsonToken firstToken() { @@ -299,9 +325,9 @@ public JsonToken firstToken() { } /* - /********************************************************** + /********************************************************************** /* Other custom methods not needed for implementing interfaces - /********************************************************** + /********************************************************************** */ /** @@ -472,19 +498,16 @@ public void serialize(JsonGenerator gen) throws IOException /** * Helper method used by standard deserializer. - * - * @since 2.3 */ public TokenBuffer deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - if (p.getCurrentTokenId() != JsonToken.FIELD_NAME.id()) { + if (!p.hasToken(JsonToken.FIELD_NAME)) { copyCurrentStructure(p); return this; } - /* 28-Oct-2014, tatu: As per [databind#592], need to support a special case of starting from - * FIELD_NAME, which is taken to mean that we are missing START_OBJECT, but need - * to assume one did exist. - */ + // 28-Oct-2014, tatu: As per [databind#592], need to support a special case of starting from + // FIELD_NAME, which is taken to mean that we are missing START_OBJECT, but need + // to assume one did exist. JsonToken t; writeStartObject(); do { @@ -535,7 +558,7 @@ public String toString() sb.append(t.toString()); if (t == JsonToken.FIELD_NAME) { sb.append('('); - sb.append(jp.getCurrentName()); + sb.append(jp.currentName()); sb.append(')'); } } @@ -563,73 +586,66 @@ private final void _appendNativeIds(StringBuilder sb) sb.append("[typeId=").append(String.valueOf(typeId)).append(']'); } } - + + /* + /********************************************************************** + /* JsonGenerator implementation: context + /********************************************************************** + */ + + @Override + public JsonWriteContext getOutputContext() { return _tokenWriteContext; } + + @Override + public ObjectWriteContext getObjectWriteContext() { return _objectWriteContext; } + /* - /********************************************************** + /********************************************************************** /* JsonGenerator implementation: configuration - /********************************************************** + /********************************************************************** */ @Override - public JsonGenerator enable(Feature f) { - _generatorFeatures |= f.getMask(); + public JsonGenerator enable(StreamWriteFeature f) { + _streamWriteFeatures |= f.getMask(); return this; } @Override - public JsonGenerator disable(Feature f) { - _generatorFeatures &= ~f.getMask(); + public JsonGenerator disable(StreamWriteFeature f) { + _streamWriteFeatures &= ~f.getMask(); return this; } //public JsonGenerator configure(SerializationFeature f, boolean state) { } @Override - public boolean isEnabled(Feature f) { - return (_generatorFeatures & f.getMask()) != 0; + public boolean isEnabled(StreamWriteFeature f) { + return (_streamWriteFeatures & f.getMask()) != 0; } @Override - public int getFeatureMask() { - return _generatorFeatures; + public int streamWriteFeatures() { + return _streamWriteFeatures; } - @Override - @Deprecated - public JsonGenerator setFeatureMask(int mask) { - _generatorFeatures = mask; - return this; + @Override // since 3.0 + public int formatWriteFeatures() { + // 26-Oct-2018, tatu: Should not have anything format-specific... however, + // not all features default to "false" so this may not be right choice? + return 0; } - - @Override - public JsonGenerator overrideStdFeatures(int values, int mask) { - int oldState = getFeatureMask(); - _generatorFeatures = (oldState & ~mask) | (values & mask); - return this; - } - + @Override public JsonGenerator useDefaultPrettyPrinter() { // No-op: we don't indent return this; } - @Override - public JsonGenerator setCodec(ObjectCodec oc) { - _objectCodec = oc; - return this; - } - - @Override - public ObjectCodec getCodec() { return _objectCodec; } - - @Override - public final JsonWriteContext getOutputContext() { return _writeContext; } - /* - /********************************************************** + /********************************************************************** /* JsonGenerator implementation: capability introspection - /********************************************************** + /********************************************************************** */ /** @@ -641,9 +657,9 @@ public boolean canWriteBinaryNatively() { } /* - /********************************************************** + /********************************************************************** /* JsonGenerator implementation: low-level output handling - /********************************************************** + /********************************************************************** */ @Override @@ -658,17 +674,25 @@ public void close() throws IOException { public boolean isClosed() { return _closed; } /* - /********************************************************** + /********************************************************************** /* JsonGenerator implementation: write methods, structural - /********************************************************** + /********************************************************************** */ @Override public final void writeStartArray() throws IOException { - _writeContext.writeValue(); + _tokenWriteContext.writeValue(); + _append(JsonToken.START_ARRAY); + _tokenWriteContext = _tokenWriteContext.createChildArrayContext(); + } + + @Override + public final void writeStartArray(Object forValue, int len) throws IOException + { + _tokenWriteContext.writeValue(); _append(JsonToken.START_ARRAY); - _writeContext = _writeContext.createChildArrayContext(); + _tokenWriteContext = _tokenWriteContext.createChildArrayContext(forValue); } @Override @@ -676,30 +700,27 @@ public final void writeEndArray() throws IOException { _append(JsonToken.END_ARRAY); // Let's allow unbalanced tho... i.e. not run out of root level, ever - JsonWriteContext c = _writeContext.getParent(); + JsonWriteContext c = _tokenWriteContext.getParent(); if (c != null) { - _writeContext = c; + _tokenWriteContext = c; } } @Override public final void writeStartObject() throws IOException { - _writeContext.writeValue(); + _tokenWriteContext.writeValue(); _append(JsonToken.START_OBJECT); - _writeContext = _writeContext.createChildObjectContext(); + _tokenWriteContext = _tokenWriteContext.createChildObjectContext(); } - @Override // since 2.8 + @Override public void writeStartObject(Object forValue) throws IOException { - _writeContext.writeValue(); + _tokenWriteContext.writeValue(); _append(JsonToken.START_OBJECT); - JsonWriteContext ctxt = _writeContext.createChildObjectContext(); - _writeContext = ctxt; - if (forValue != null) { - ctxt.setCurrentValue(forValue); - } + JsonWriteContext ctxt = _tokenWriteContext.createChildObjectContext(forValue); + _tokenWriteContext = ctxt; } @Override @@ -707,30 +728,30 @@ public final void writeEndObject() throws IOException { _append(JsonToken.END_OBJECT); // Let's allow unbalanced tho... i.e. not run out of root level, ever - JsonWriteContext c = _writeContext.getParent(); + JsonWriteContext c = _tokenWriteContext.getParent(); if (c != null) { - _writeContext = c; + _tokenWriteContext = c; } } @Override public final void writeFieldName(String name) throws IOException { - _writeContext.writeFieldName(name); + _tokenWriteContext.writeFieldName(name); _append(JsonToken.FIELD_NAME, name); } @Override public void writeFieldName(SerializableString name) throws IOException { - _writeContext.writeFieldName(name.getValue()); + _tokenWriteContext.writeFieldName(name.getValue()); _append(JsonToken.FIELD_NAME, name); } - + /* - /********************************************************** + /********************************************************************** /* JsonGenerator implementation: write methods, textual - /********************************************************** + /********************************************************************** */ @Override @@ -814,9 +835,9 @@ public void writeRawValue(char[] text, int offset, int len) throws IOException { } /* - /********************************************************** + /********************************************************************** /* JsonGenerator implementation: write methods, primitive types - /********************************************************** + /********************************************************************** */ @Override @@ -881,9 +902,9 @@ public void writeNull() throws IOException { } /* - /*********************************************************** + /********************************************************************** /* JsonGenerator implementation: write methods for POJOs/trees - /*********************************************************** + /********************************************************************** */ @Override @@ -894,19 +915,12 @@ public void writeObject(Object value) throws IOException return; } Class raw = value.getClass(); - if (raw == byte[].class || (value instanceof RawValue)) { + if (raw == byte[].class || (value instanceof RawValue) + || (_objectWriteContext == null)) { _appendValue(JsonToken.VALUE_EMBEDDED_OBJECT, value); return; } - if (_objectCodec == null) { - /* 28-May-2014, tatu: Tricky choice here; if no codec, should we - * err out, or just embed? For now, do latter. - */ -// throw new JsonMappingException("No ObjectCodec configured for TokenBuffer, writeObject() called"); - _appendValue(JsonToken.VALUE_EMBEDDED_OBJECT, value); - } else { - _objectCodec.writeValue(this, value); - } + _objectWriteContext.writeValue(this, value); } @Override @@ -916,19 +930,17 @@ public void writeTree(TreeNode node) throws IOException writeNull(); return; } - - if (_objectCodec == null) { - // as with 'writeObject()', is codec optional? + if (_objectWriteContext == null) { _appendValue(JsonToken.VALUE_EMBEDDED_OBJECT, node); - } else { - _objectCodec.writeTree(this, node); + return; } + _objectWriteContext.writeTree(this, node); } /* - /*********************************************************** + /********************************************************************** /* JsonGenerator implementation; binary - /*********************************************************** + /********************************************************************** */ @Override @@ -957,9 +969,9 @@ public int writeBinary(Base64Variant b64variant, InputStream data, int dataLengt } /* - /*********************************************************** + /********************************************************************** /* JsonGenerator implementation: native ids - /*********************************************************** + /********************************************************************** */ @Override @@ -984,15 +996,15 @@ public void writeObjectId(Object id) { _hasNativeId = true; } - @Override // since 2.8 + @Override public void writeEmbeddedObject(Object object) throws IOException { _appendValue(JsonToken.VALUE_EMBEDDED_OBJECT, object); } /* - /********************************************************** + /********************************************************************** /* JsonGenerator implementation; pass-through copy - /********************************************************** + /********************************************************************** */ @Override @@ -1001,7 +1013,7 @@ public void copyCurrentEvent(JsonParser p) throws IOException if (_mayHaveNativeIds) { _checkNativeIds(p); } - switch (p.getCurrentToken()) { + switch (p.currentToken()) { case START_OBJECT: writeStartObject(); break; @@ -1015,7 +1027,7 @@ public void copyCurrentEvent(JsonParser p) throws IOException writeEndArray(); break; case FIELD_NAME: - writeFieldName(p.getCurrentName()); + writeFieldName(p.currentName()); break; case VALUE_STRING: if (p.hasTextCharacters()) { @@ -1077,14 +1089,14 @@ public void copyCurrentEvent(JsonParser p) throws IOException @Override public void copyCurrentStructure(JsonParser p) throws IOException { - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); // Let's handle field-name separately first if (t == JsonToken.FIELD_NAME) { if (_mayHaveNativeIds) { _checkNativeIds(p); } - writeFieldName(p.getCurrentName()); + writeFieldName(p.currentName()); t = p.nextToken(); // fall-through to copy the associated value } @@ -1123,11 +1135,11 @@ private final void _checkNativeIds(JsonParser jp) throws IOException _hasNativeId = true; } } - + /* - /********************************************************** + /********************************************************************** /* Internal methods - /********************************************************** + /********************************************************************** */ protected final void _append(JsonToken type) @@ -1159,12 +1171,10 @@ protected final void _append(JsonToken type, Object value) /** * Similar to {@link #_append(JsonToken)} but also updates context with * knowledge that a scalar value was written - * - * @since 2.6.4 */ protected final void _appendValue(JsonToken type) { - _writeContext.writeValue(); + _tokenWriteContext.writeValue(); Segment next = _hasNativeId ? _last.append(_appendAt, type, _objectId, _typeId) : _last.append(_appendAt, type); @@ -1179,12 +1189,10 @@ protected final void _appendValue(JsonToken type) /** * Similar to {@link #_append(JsonToken,Object)} but also updates context with * knowledge that a scalar value was written - * - * @since 2.6.4 */ protected final void _appendValue(JsonToken type, Object value) { - _writeContext.writeValue(); + _tokenWriteContext.writeValue(); Segment next = _hasNativeId ? _last.append(_appendAt, type, value, _objectId, _typeId) : _last.append(_appendAt, type, value); @@ -1196,60 +1204,36 @@ protected final void _appendValue(JsonToken type, Object value) } } - // 21-Oct-2016, tatu: Does not seem to be used or needed /* - protected final void _appendRaw(int rawType, Object value) - { - Segment next = _hasNativeId - ? _last.appendRaw(_appendAt, rawType, value, _objectId, _typeId) - : _last.appendRaw(_appendAt, rawType, value); - if (next == null) { - ++_appendAt; - } else { - _last = next; - _appendAt = 1; - } - } - */ - - @Override - protected void _reportUnsupportedOperation() { - throw new UnsupportedOperationException("Called operation not supported for TokenBuffer"); - } - - /* - /********************************************************** + /********************************************************************** /* Supporting classes - /********************************************************** + /********************************************************************** */ protected final static class Parser extends ParserMinimalBase { /* - /********************************************************** + /****************************************************************** /* Configuration - /********************************************************** + /****************************************************************** */ - protected ObjectCodec _codec; - /** - * @since 2.3 + * @since 3.0 */ + protected final TokenBuffer _source; + protected final boolean _hasNativeTypeIds; - /** - * @since 2.3 - */ protected final boolean _hasNativeObjectIds; protected final boolean _hasNativeIds; /* - /********************************************************** + /****************************************************************** /* Parsing state - /********************************************************** + /****************************************************************** */ /** @@ -1275,26 +1259,19 @@ protected final static class Parser protected JsonLocation _location = null; /* - /********************************************************** + /****************************************************************** /* Construction, init - /********************************************************** + /****************************************************************** */ - @Deprecated // since 2.9 - public Parser(Segment firstSeg, ObjectCodec codec, - boolean hasNativeTypeIds, boolean hasNativeObjectIds) + public Parser(ObjectReadContext readCtxt, TokenBuffer source, + Segment firstSeg, boolean hasNativeTypeIds, boolean hasNativeObjectIds, + TokenStreamContext parentContext) { - this(firstSeg, codec, hasNativeTypeIds, hasNativeObjectIds, null); - } - - public Parser(Segment firstSeg, ObjectCodec codec, - boolean hasNativeTypeIds, boolean hasNativeObjectIds, - JsonStreamContext parentContext) - { - super(0); + super(readCtxt, 0); + _source = source; _segment = firstSeg; _segmentPtr = -1; // not yet read - _codec = codec; _parsingContext = TokenBufferReadContext.createRootContext(parentContext); _hasNativeTypeIds = hasNativeTypeIds; _hasNativeObjectIds = hasNativeObjectIds; @@ -1304,22 +1281,21 @@ public Parser(Segment firstSeg, ObjectCodec codec, public void setLocation(JsonLocation l) { _location = l; } - - @Override - public ObjectCodec getCodec() { return _codec; } - - @Override - public void setCodec(ObjectCodec c) { _codec = c; } @Override public Version version() { return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION; } + @Override + public TokenBuffer getInputSource() { + return _source; + } + /* - /********************************************************** + /****************************************************************** /* Extended API beyond JsonParser - /********************************************************** + /****************************************************************** */ public JsonToken peekNextToken() throws IOException @@ -1334,11 +1310,11 @@ public JsonToken peekNextToken() throws IOException } return (seg == null) ? null : seg.type(ptr); } - + /* - /********************************************************** + /****************************************************************** /* Closeable implementation - /********************************************************** + /****************************************************************** */ @Override @@ -1349,22 +1325,26 @@ public void close() throws IOException { } /* - /********************************************************** + /****************************************************************** /* Public API, traversal - /********************************************************** + /****************************************************************** */ @Override public JsonToken nextToken() throws IOException { // If we are closed, nothing more to do - if (_closed || (_segment == null)) return null; + if (_closed || (_segment == null)) { + _currToken = null; + return null; + } // Ok, then: any more tokens? if (++_segmentPtr >= Segment.TOKENS_PER_SEGMENT) { _segmentPtr = 0; _segment = _segment.next(); if (_segment == null) { + _currToken = null; return null; } } @@ -1403,20 +1383,34 @@ public String nextFieldName() throws IOException _parsingContext.setCurrentName(name); return name; } - return (nextToken() == JsonToken.FIELD_NAME) ? getCurrentName() : null; + return (nextToken() == JsonToken.FIELD_NAME) ? currentName() : null; + } + + // NOTE: since we know there's no native matching just use simpler way: + @Override // since 3.0 + public int nextFieldName(FieldNameMatcher matcher) throws IOException { + String str = nextFieldName(); + if (str != null) { + // 15-Nov-2017, tatu: Can not assume name given is intern()ed + return matcher.matchName(str); + } + if (hasToken(JsonToken.END_OBJECT)) { + return FieldNameMatcher.MATCH_END_OBJECT; + } + return FieldNameMatcher.MATCH_ODD_TOKEN; } @Override public boolean isClosed() { return _closed; } /* - /********************************************************** + /****************************************************************** /* Public API, token accessors - /********************************************************** + /****************************************************************** */ @Override - public JsonStreamContext getParsingContext() { return _parsingContext; } + public TokenStreamContext getParsingContext() { return _parsingContext; } @Override public JsonLocation getTokenLocation() { return getCurrentLocation(); } @@ -1427,20 +1421,20 @@ public JsonLocation getCurrentLocation() { } @Override - public String getCurrentName() { + public String currentName() { // 25-Jun-2015, tatu: as per [databind#838], needs to be same as ParserBase if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) { - JsonStreamContext parent = _parsingContext.getParent(); - return parent.getCurrentName(); + TokenStreamContext parent = _parsingContext.getParent(); + return parent.currentName(); } - return _parsingContext.getCurrentName(); + return _parsingContext.currentName(); } @Override public void overrideCurrentName(String name) { // Simple, but need to look for START_OBJECT/ARRAY's "off-by-one" thing: - JsonStreamContext ctxt = _parsingContext; + TokenStreamContext ctxt = _parsingContext; if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) { ctxt = ctxt.getParent(); } @@ -1454,9 +1448,9 @@ public void overrideCurrentName(String name) } /* - /********************************************************** + /****************************************************************** /* Public API, access to token information, text - /********************************************************** + /****************************************************************** */ @Override @@ -1505,9 +1499,9 @@ public boolean hasTextCharacters() { } /* - /********************************************************** + /****************************************************************** /* Public API, access to token information, numeric - /********************************************************** + /****************************************************************** */ @Override @@ -1702,9 +1696,9 @@ protected long _convertNumberToLong(Number n) throws IOException } /* - /********************************************************** + /****************************************************************** /* Public API, access to token information, other - /********************************************************** + /****************************************************************** */ @Override @@ -1758,9 +1752,9 @@ public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IO } /* - /********************************************************** + /****************************************************************** /* Public API, native ids - /********************************************************** + /****************************************************************** */ @Override @@ -1784,9 +1778,9 @@ public Object getObjectId() { } /* - /********************************************************** + /****************************************************************** /* Internal methods - /********************************************************** + /****************************************************************** */ protected final Object _currentObject() { @@ -1938,52 +1932,6 @@ public Segment append(int index, JsonToken tokenType, Object value, return _next; } - /* - public Segment appendRaw(int index, int rawTokenType, Object value) - { - if (index < TOKENS_PER_SEGMENT) { - set(index, rawTokenType, value); - return null; - } - _next = new Segment(); - _next.set(0, rawTokenType, value); - return _next; - } - - public Segment appendRaw(int index, int rawTokenType, Object value, - Object objectId, Object typeId) - { - if (index < TOKENS_PER_SEGMENT) { - set(index, rawTokenType, value, objectId, typeId); - return null; - } - _next = new Segment(); - _next.set(0, rawTokenType, value, objectId, typeId); - return _next; - } - - private void set(int index, int rawTokenType, Object value, Object objectId, Object typeId) - { - _tokens[index] = value; - long typeCode = (long) rawTokenType; - if (index > 0) { - typeCode <<= (index << 2); - } - _tokenTypes |= typeCode; - assignNativeIds(index, objectId, typeId); - } - - private void set(int index, int rawTokenType, Object value) - { - _tokens[index] = value; - long typeCode = (long) rawTokenType; - if (index > 0) { - typeCode <<= (index << 2); - } - _tokenTypes |= typeCode; - } - */ - private void set(int index, JsonToken tokenType) { /* Assumption here is that there are no overwrites, just appends; @@ -2042,16 +1990,10 @@ private final void assignNativeIds(int index, Object objectId, Object typeId) } } - /** - * @since 2.3 - */ private Object findObjectId(int index) { return (_nativeIds == null) ? null : _nativeIds.get(_objectIdIndex(index)); } - - /** - * @since 2.3 - */ + private Object findTypeId(int index) { return (_nativeIds == null) ? null : _nativeIds.get(_typeIdIndex(index)); } diff --git a/src/main/java/com/fasterxml/jackson/databind/util/TokenBufferReadContext.java b/src/main/java/com/fasterxml/jackson/databind/util/TokenBufferReadContext.java index 66520368ed..208eb82a20 100644 --- a/src/main/java/com/fasterxml/jackson/databind/util/TokenBufferReadContext.java +++ b/src/main/java/com/fasterxml/jackson/databind/util/TokenBufferReadContext.java @@ -4,16 +4,16 @@ import com.fasterxml.jackson.core.json.JsonReadContext; /** - * Implementation of {@link JsonStreamContext} used by {@link TokenBuffer} + * Implementation of {@link TokenStreamContext} used by {@link TokenBuffer} * to link back to the original context to try to keep location information * consistent between source location and buffered content when it's re-read * from the buffer. * * @since 2.9 */ -public class TokenBufferReadContext extends JsonStreamContext +public class TokenBufferReadContext extends TokenStreamContext { - protected final JsonStreamContext _parent; + protected final TokenStreamContext _parent; protected final JsonLocation _startLocation; @@ -30,10 +30,10 @@ public class TokenBufferReadContext extends JsonStreamContext protected Object _currentValue; - protected TokenBufferReadContext(JsonStreamContext base, Object srcRef) { + protected TokenBufferReadContext(TokenStreamContext base, Object srcRef) { super(base); _parent = base.getParent(); - _currentName = base.getCurrentName(); + _currentName = base.currentName(); _currentValue = base.getCurrentValue(); if (base instanceof JsonReadContext) { JsonReadContext rc = (JsonReadContext) base; @@ -43,10 +43,10 @@ protected TokenBufferReadContext(JsonStreamContext base, Object srcRef) { } } - protected TokenBufferReadContext(JsonStreamContext base, JsonLocation startLoc) { + protected TokenBufferReadContext(TokenStreamContext base, JsonLocation startLoc) { super(base); _parent = base.getParent(); - _currentName = base.getCurrentName(); + _currentName = base.currentName(); _currentValue = base.getCurrentValue(); _startLocation = startLoc; } @@ -83,7 +83,7 @@ public void setCurrentValue(Object v) { /********************************************************** */ - public static TokenBufferReadContext createRootContext(JsonStreamContext origContext) { + public static TokenBufferReadContext createRootContext(TokenStreamContext origContext) { // First: possible to have no current context; if so, just create bogus ROOT context if (origContext == null) { return new TokenBufferReadContext(); @@ -122,12 +122,11 @@ public TokenBufferReadContext parentOrCopy() { /********************************************************** */ - @Override public String getCurrentName() { return _currentName; } + @Override public String currentName() { return _currentName; } - // @since 2.9 @Override public boolean hasCurrentName() { return _currentName != null; } - @Override public JsonStreamContext getParent() { return _parent; } + @Override public TokenStreamContext getParent() { return _parent; } public void setCurrentName(String name) throws JsonProcessingException { _currentName = name; diff --git a/src/main/java/com/fasterxml/jackson/databind/util/UniqueId.java b/src/main/java/com/fasterxml/jackson/databind/util/UniqueId.java new file mode 100644 index 0000000000..d607a9826e --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/util/UniqueId.java @@ -0,0 +1,35 @@ +package com.fasterxml.jackson.databind.util; + +/** + * Simple identity value class that may be used as Serializable key + * for entries that need to retain identity of some kind, but where + * actual appearance of id itself does not matter. + * + * @since 3.0 + */ +public class UniqueId + implements java.io.Serializable, Comparable +{ + private static final long serialVersionUID = 3L; + + protected final String _id; + + public UniqueId() { + _id = Long.toHexString(System.identityHashCode(this)); + } + + @Override + public boolean equals(Object other) { + return this == other; + } + + @Override + public int hashCode() { + return _id.hashCode(); + } + + @Override + public int compareTo(UniqueId o) { + return _id.compareTo(o._id); + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/BaseMapTest.java b/src/test/java/com/fasterxml/jackson/databind/BaseMapTest.java index b699a380fc..92b7236ee6 100644 --- a/src/test/java/com/fasterxml/jackson/databind/BaseMapTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/BaseMapTest.java @@ -3,15 +3,12 @@ import java.io.*; import java.util.*; -import static org.junit.Assert.*; - import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer; -import com.fasterxml.jackson.databind.json.JsonMapper; import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer; import com.fasterxml.jackson.databind.type.TypeFactory; @@ -21,9 +18,9 @@ public abstract class BaseMapTest private final static Object SINGLETON_OBJECT = new Object(); /* - /********************************************************** + /********************************************************************** /* Shared helper classes - /********************************************************** + /********************************************************************** */ public static class BogusSchema implements FormatSchema { @@ -84,7 +81,8 @@ protected ObjectWrapper(final Object object) { this.object = object; } public Object getObject() { return object; } - @JsonCreator + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) static ObjectWrapper jsonValue(final Object object) { return new ObjectWrapper(object); } @@ -164,9 +162,9 @@ public String toString() { } /* - /********************************************************** + /********************************************************************** /* Shared serializers - /********************************************************** + /********************************************************************** */ @SuppressWarnings("serial") @@ -194,66 +192,29 @@ public String deserialize(JsonParser p, DeserializationContext ctxt) } /* - /********************************************************** + /********************************************************************** /* Construction - /********************************************************** + /********************************************************************** */ protected BaseMapTest() { super(); } /* - /********************************************************** + /********************************************************************** /* Factory methods - /********************************************************** + /********************************************************************** */ - private static ObjectMapper SHARED_MAPPER; - - protected ObjectMapper objectMapper() { - if (SHARED_MAPPER == null) { - SHARED_MAPPER = newObjectMapper(); - } - return SHARED_MAPPER; - } - - protected ObjectWriter objectWriter() { - return objectMapper().writer(); - } - - protected ObjectReader objectReader() { - return objectMapper().reader(); - } - - protected ObjectReader objectReader(Class cls) { - return objectMapper().readerFor(cls); - } - - // @since 2.9 - protected static ObjectMapper newObjectMapper() { - return new ObjectMapper(); - } - - // @since 2.10s - protected static JsonMapper.Builder objectMapperBuilder() { - /* 27-Aug-2018, tatu: Now this is weird -- with Java 7, we seem to need - * explicit type parameters, but not with Java 8. - * This need renders builder-approach pretty much useless for Java 7, - * which is ok for 3.x (where baseline is Java 8), but potentially - * problematic for 2.10. Oh well. - */ - return JsonMapper.builder(); - } - // @since 2.7 protected TypeFactory newTypeFactory() { // this is a work-around; no null modifier added return TypeFactory.defaultInstance().withModifier(null); } - + /* - /********************************************************** + /********************************************************************** /* Additional assert methods - /********************************************************** + /********************************************************************** */ protected void assertEquals(int[] exp, int[] act) @@ -281,9 +242,9 @@ protected void assertStandardEquals(Object o) } /* - /********************************************************** + /********************************************************************** /* Helper methods, serialization - /********************************************************** + /********************************************************************** */ @SuppressWarnings("unchecked") @@ -293,39 +254,11 @@ protected Map writeAndMap(ObjectMapper m, Object value) String str = m.writeValueAsString(value); return (Map) m.readValue(str, Map.class); } - - protected String serializeAsString(ObjectMapper m, Object value) - throws IOException - { - return m.writeValueAsString(value); - } - - protected String serializeAsString(Object value) - throws IOException - { - return serializeAsString(objectMapper(), value); - } - - protected String asJSONObjectValueString(Object... args) - throws IOException - { - return asJSONObjectValueString(objectMapper(), args); - } - - protected String asJSONObjectValueString(ObjectMapper m, Object... args) - throws IOException - { - LinkedHashMap map = new LinkedHashMap(); - for (int i = 0, len = args.length; i < len; i += 2) { - map.put(args[i], args[i+1]); - } - return m.writeValueAsString(map); - } /* - /********************************************************** + /********************************************************************** /* Helper methods, deserialization - /********************************************************** + /********************************************************************** */ protected T readAndMapFromString(String input, Class cls) diff --git a/src/test/java/com/fasterxml/jackson/databind/BaseTest.java b/src/test/java/com/fasterxml/jackson/databind/BaseTest.java index e4d664b7c2..f816524ebd 100644 --- a/src/test/java/com/fasterxml/jackson/databind/BaseTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/BaseTest.java @@ -3,14 +3,15 @@ import java.io.*; import java.util.Arrays; -import junit.framework.TestCase; +import org.junit.Assert; import com.fasterxml.jackson.core.*; - -//import static org.junit.Assert.*; +import com.fasterxml.jackson.core.json.JsonFactory; +import com.fasterxml.jackson.databind.json.JsonMapper; public abstract class BaseTest - extends TestCase +// 19-Sep-2017, tatu: Remove eventually from 3.x, but needs addition of metric ton of `@Test`s + extends junit.framework.TestCase { /* /********************************************************** @@ -127,116 +128,196 @@ public boolean equals(Object o) return true; } } + + /* + /********************************************************** + /* Factory methods + /********************************************************** + */ + + private static ObjectMapper SHARED_MAPPER; + + protected ObjectMapper objectMapper() { + if (SHARED_MAPPER == null) { + SHARED_MAPPER = newJsonMapper(); + } + return SHARED_MAPPER; + } + + protected ObjectWriter objectWriter() { + return objectMapper().writer(); + } + + protected ObjectReader objectReader() { + return objectMapper().reader(); + } + + protected ObjectReader objectReader(Class cls) { + return objectMapper().readerFor(cls); + } + + protected static JsonMapper newJsonMapper() { + return new JsonMapper(); + } + + protected static JsonMapper.Builder jsonMapperBuilder() { + return JsonMapper.builder(); + } + + protected static JsonMapper.Builder jsonMapperBuilder(JsonFactory f) { + return JsonMapper.builder(f); + } + + /* + /********************************************************** + /* Pass-through to remove need for static import + /********************************************************** + */ + + public static void fail(String msg) { Assert.fail(msg); } + public static void assertNull(Object v) { Assert.assertNull(v); } + public static void assertNull(String msg, Object v) { Assert.assertNull(msg, v); } + public static void assertNotNull(Object v) { Assert.assertNotNull(v); } + public static void assertNotNull(String msg, Object v) { Assert.assertNotNull(msg, v); } + + public static void assertSame(Object ob1, Object ob2) { Assert.assertSame(ob1, ob2); } + public static void assertNotSame(Object ob1, Object ob2) { Assert.assertNotSame(ob1, ob2); } + + public static void assertTrue(boolean b) { Assert.assertTrue(b); } + public static void assertTrue(String msg, boolean b) { Assert.assertTrue(msg, b); } + public static void assertFalse(boolean b) { Assert.assertFalse(b); } + public static void assertFalse(String msg, boolean b) { Assert.assertFalse(msg, b); } + + public static void assertEquals(int exp, int act) { Assert.assertEquals(exp, act); } + public static void assertEquals(String msg, int exp, int act) { Assert.assertEquals(msg, exp, act); } + + public static void assertEquals(double exp, double act, double diff) { Assert.assertEquals(exp, act, diff); } +// protected static void assertEquals(String msg, double exp, double act) { Assert.assertEquals(msg, exp, act); } + + public static void assertEquals(String exp, String act) { Assert.assertEquals(exp, act); } + public static void assertEquals(String msg, String exp, String act) { Assert.assertEquals(msg, exp, act); } + + public static void assertEquals(Object exp, Object act) { Assert.assertEquals(exp, act); } + public static void assertEquals(String msg, Object exp, Object act) { Assert.assertEquals(msg, exp, act); } + + public static void assertArrayEquals(byte[] exp, byte[] act) { Assert.assertArrayEquals(exp, act); } + public static void assertArrayEquals(String msg, byte[] exp, byte[] act) { Assert.assertArrayEquals(msg, exp, act); } + public static void assertArrayEquals(char[] exp, char[] act) { Assert.assertArrayEquals(exp, act); } + public static void assertArrayEquals(int[] exp, int[] act) { Assert.assertArrayEquals(exp, act); } + public static void assertArrayEquals(long[] exp, long[] act) { Assert.assertArrayEquals(exp, act); } + + public static void assertArrayEquals(Object[] exp, Object[] act) { Assert.assertArrayEquals(exp, act); } + /* /********************************************************** /* High-level helpers /********************************************************** */ - protected void verifyJsonSpecSampleDoc(JsonParser jp, boolean verifyContents) + protected void verifyJsonSpecSampleDoc(JsonParser p, boolean verifyContents) throws IOException { - verifyJsonSpecSampleDoc(jp, verifyContents, true); + verifyJsonSpecSampleDoc(p, verifyContents, true); } - protected void verifyJsonSpecSampleDoc(JsonParser jp, boolean verifyContents, + protected void verifyJsonSpecSampleDoc(JsonParser p, boolean verifyContents, boolean requireNumbers) throws IOException { - if (!jp.hasCurrentToken()) { - jp.nextToken(); + if (!p.hasCurrentToken()) { + p.nextToken(); } - assertToken(JsonToken.START_OBJECT, jp.getCurrentToken()); // main object + assertToken(JsonToken.START_OBJECT, p.currentToken()); // main object - assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Image' + assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Image' if (verifyContents) { - verifyFieldName(jp, "Image"); + verifyFieldName(p, "Image"); } - assertToken(JsonToken.START_OBJECT, jp.nextToken()); // 'image' object + assertToken(JsonToken.START_OBJECT, p.nextToken()); // 'image' object - assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Width' + assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Width' if (verifyContents) { - verifyFieldName(jp, "Width"); + verifyFieldName(p, "Width"); } - verifyIntToken(jp.nextToken(), requireNumbers); + verifyIntToken(p.nextToken(), requireNumbers); if (verifyContents) { - verifyIntValue(jp, SAMPLE_SPEC_VALUE_WIDTH); + verifyIntValue(p, SAMPLE_SPEC_VALUE_WIDTH); } - assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Height' + assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Height' if (verifyContents) { - verifyFieldName(jp, "Height"); + verifyFieldName(p, "Height"); } - verifyIntToken(jp.nextToken(), requireNumbers); + verifyIntToken(p.nextToken(), requireNumbers); if (verifyContents) { - verifyIntValue(jp, SAMPLE_SPEC_VALUE_HEIGHT); + verifyIntValue(p, SAMPLE_SPEC_VALUE_HEIGHT); } - assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Title' + assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Title' if (verifyContents) { - verifyFieldName(jp, "Title"); + verifyFieldName(p, "Title"); } - assertToken(JsonToken.VALUE_STRING, jp.nextToken()); - assertEquals(SAMPLE_SPEC_VALUE_TITLE, getAndVerifyText(jp)); - assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Thumbnail' + assertToken(JsonToken.VALUE_STRING, p.nextToken()); + assertEquals(SAMPLE_SPEC_VALUE_TITLE, getAndVerifyText(p)); + assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Thumbnail' if (verifyContents) { - verifyFieldName(jp, "Thumbnail"); + verifyFieldName(p, "Thumbnail"); } - assertToken(JsonToken.START_OBJECT, jp.nextToken()); // 'thumbnail' object - assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Url' + assertToken(JsonToken.START_OBJECT, p.nextToken()); // 'thumbnail' object + assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Url' if (verifyContents) { - verifyFieldName(jp, "Url"); + verifyFieldName(p, "Url"); } - assertToken(JsonToken.VALUE_STRING, jp.nextToken()); + assertToken(JsonToken.VALUE_STRING, p.nextToken()); if (verifyContents) { - assertEquals(SAMPLE_SPEC_VALUE_TN_URL, getAndVerifyText(jp)); + assertEquals(SAMPLE_SPEC_VALUE_TN_URL, getAndVerifyText(p)); } - assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Height' + assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Height' if (verifyContents) { - verifyFieldName(jp, "Height"); + verifyFieldName(p, "Height"); } - verifyIntToken(jp.nextToken(), requireNumbers); + verifyIntToken(p.nextToken(), requireNumbers); if (verifyContents) { - verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_HEIGHT); + verifyIntValue(p, SAMPLE_SPEC_VALUE_TN_HEIGHT); } - assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Width' + assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Width' if (verifyContents) { - verifyFieldName(jp, "Width"); + verifyFieldName(p, "Width"); } // Width value is actually a String in the example - assertToken(JsonToken.VALUE_STRING, jp.nextToken()); + assertToken(JsonToken.VALUE_STRING, p.nextToken()); if (verifyContents) { - assertEquals(SAMPLE_SPEC_VALUE_TN_WIDTH, getAndVerifyText(jp)); + assertEquals(SAMPLE_SPEC_VALUE_TN_WIDTH, getAndVerifyText(p)); } - assertToken(JsonToken.END_OBJECT, jp.nextToken()); // 'thumbnail' object - assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'IDs' - assertToken(JsonToken.START_ARRAY, jp.nextToken()); // 'ids' array - verifyIntToken(jp.nextToken(), requireNumbers); // ids[0] + assertToken(JsonToken.END_OBJECT, p.nextToken()); // 'thumbnail' object + assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'IDs' + assertToken(JsonToken.START_ARRAY, p.nextToken()); // 'ids' array + verifyIntToken(p.nextToken(), requireNumbers); // ids[0] if (verifyContents) { - verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_ID1); + verifyIntValue(p, SAMPLE_SPEC_VALUE_TN_ID1); } - verifyIntToken(jp.nextToken(), requireNumbers); // ids[1] + verifyIntToken(p.nextToken(), requireNumbers); // ids[1] if (verifyContents) { - verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_ID2); + verifyIntValue(p, SAMPLE_SPEC_VALUE_TN_ID2); } - verifyIntToken(jp.nextToken(), requireNumbers); // ids[2] + verifyIntToken(p.nextToken(), requireNumbers); // ids[2] if (verifyContents) { - verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_ID3); + verifyIntValue(p, SAMPLE_SPEC_VALUE_TN_ID3); } - verifyIntToken(jp.nextToken(), requireNumbers); // ids[3] + verifyIntToken(p.nextToken(), requireNumbers); // ids[3] if (verifyContents) { - verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_ID4); + verifyIntValue(p, SAMPLE_SPEC_VALUE_TN_ID4); } - assertToken(JsonToken.END_ARRAY, jp.nextToken()); // 'ids' array + assertToken(JsonToken.END_ARRAY, p.nextToken()); // 'ids' array - assertToken(JsonToken.END_OBJECT, jp.nextToken()); // 'image' object + assertToken(JsonToken.END_OBJECT, p.nextToken()); // 'image' object - assertToken(JsonToken.END_OBJECT, jp.nextToken()); // main object + assertToken(JsonToken.END_OBJECT, p.nextToken()); // main object } private void verifyIntToken(JsonToken t, boolean requireNumbers) @@ -253,18 +334,18 @@ private void verifyIntToken(JsonToken t, boolean requireNumbers) } } - protected void verifyFieldName(JsonParser jp, String expName) + protected void verifyFieldName(JsonParser p, String expName) throws IOException { - assertEquals(expName, jp.getText()); - assertEquals(expName, jp.getCurrentName()); + assertEquals(expName, p.getText()); + assertEquals(expName, p.currentName()); } - protected void verifyIntValue(JsonParser jp, long expValue) + protected void verifyIntValue(JsonParser p, long expValue) throws IOException { // First, via textual - assertEquals(String.valueOf(expValue), jp.getText()); + assertEquals(String.valueOf(expValue), p.getText()); } /* @@ -276,24 +357,11 @@ protected void verifyIntValue(JsonParser jp, long expValue) protected JsonParser createParserUsingReader(String input) throws IOException, JsonParseException { - return createParserUsingReader(new JsonFactory(), input); - } - - protected JsonParser createParserUsingReader(JsonFactory f, String input) - throws IOException - { - return f.createParser(new StringReader(input)); + return objectMapper().createParser(new StringReader(input)); } protected JsonParser createParserUsingStream(String input, String encoding) throws IOException - { - return createParserUsingStream(new JsonFactory(), input, encoding); - } - - protected JsonParser createParserUsingStream(JsonFactory f, - String input, String encoding) - throws IOException { /* 23-Apr-2008, tatus: UTF-32 is not supported by JDK, have to * use our own codec too (which is not optimal since there's @@ -307,7 +375,7 @@ protected JsonParser createParserUsingStream(JsonFactory f, data = input.getBytes(encoding); } InputStream is = new ByteArrayInputStream(data); - return f.createParser(is); + return objectMapper().createParser(is); } /* @@ -323,9 +391,9 @@ protected void assertToken(JsonToken expToken, JsonToken actToken) } } - protected void assertToken(JsonToken expToken, JsonParser jp) + protected void assertToken(JsonToken expToken, JsonParser p) { - assertToken(expToken, jp.getCurrentToken()); + assertToken(expToken, p.currentToken()); } protected void assertType(Object ob, Class expType) @@ -344,6 +412,17 @@ protected void assertValidLocation(JsonLocation location) { assertTrue("Should have positive line number", location.getLineNr() > 0); } + protected void verifyException(Exception e, Class expType, String expMsg) + throws Exception + { + if (e.getClass() != expType) { + fail("Expected exception of type "+expType.getName()+", got "+e.getClass().getName()); + } + if (expMsg != null) { + verifyException(e, expMsg); + } + } + protected void verifyException(Throwable e, String... matches) { String msg = e.getMessage(); @@ -364,17 +443,17 @@ protected void verifyException(Throwable e, String... matches) * available methods, and ensures results are consistent, before * returning them */ - protected String getAndVerifyText(JsonParser jp) + protected String getAndVerifyText(JsonParser p) throws IOException, JsonParseException { // Ok, let's verify other accessors - int actLen = jp.getTextLength(); - char[] ch = jp.getTextCharacters(); - String str2 = new String(ch, jp.getTextOffset(), actLen); - String str = jp.getText(); + int actLen = p.getTextLength(); + char[] ch = p.getTextCharacters(); + String str2 = new String(ch, p.getTextOffset(), actLen); + String str = p.getText(); if (str.length() != actLen) { - fail("Internal problem (jp.token == "+jp.getCurrentToken()+"): jp.getText().length() ['"+str+"'] == "+str.length()+"; jp.getTextLength() == "+actLen); + fail("Internal problem (p.token == "+p.currentToken()+"): p.getText().length() ['"+str+"'] == "+str.length()+"; p.getTextLength() == "+actLen); } assertEquals("String access via getText(), getTextXxx() must be the same", str, str2); diff --git a/src/test/java/com/fasterxml/jackson/databind/TestJDKSerialization.java b/src/test/java/com/fasterxml/jackson/databind/MapperJDKSerializationTest.java similarity index 64% rename from src/test/java/com/fasterxml/jackson/databind/TestJDKSerialization.java rename to src/test/java/com/fasterxml/jackson/databind/MapperJDKSerializationTest.java index d42abe0bad..92f00aa9ce 100644 --- a/src/test/java/com/fasterxml/jackson/databind/TestJDKSerialization.java +++ b/src/test/java/com/fasterxml/jackson/databind/MapperJDKSerializationTest.java @@ -5,14 +5,17 @@ import com.fasterxml.jackson.annotation.JsonAnyGetter; import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.core.Version; import com.fasterxml.jackson.databind.DeserializationConfig; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.SerializationConfig; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.type.TypeFactory; -import com.fasterxml.jackson.databind.util.LRUMap; +import com.fasterxml.jackson.databind.util.SimpleLookupCache; /** * Tests to verify that most core Jackson components can be serialized @@ -20,7 +23,7 @@ * platforms, such as Android, where memory management is handled * much more aggressively. */ -public class TestJDKSerialization extends BaseMapTest +public class MapperJDKSerializationTest extends BaseMapTest { static class MyPojo { public int x; @@ -76,13 +79,13 @@ public Map properties() { public void testConfigs() throws IOException { - byte[] base = jdkSerialize(MAPPER.getDeserializationConfig().getBaseSettings()); + byte[] base = jdkSerialize(MAPPER.deserializationConfig().getBaseSettings()); assertNotNull(jdkDeserialize(base)); // first things first: underlying BaseSettings - - DeserializationConfig origDC = MAPPER.getDeserializationConfig(); - SerializationConfig origSC = MAPPER.getSerializationConfig(); + + DeserializationConfig origDC = MAPPER.deserializationConfig(); + SerializationConfig origSC = MAPPER.serializationConfig(); byte[] dcBytes = jdkSerialize(origDC); byte[] scBytes = jdkSerialize(origSC); @@ -110,76 +113,104 @@ public void testEnumHandlers() throws IOException ObjectMapper mapper2 = jdkDeserialize(bytes); assertNotNull(mapper2); - bytes = jdkSerialize(mapper.readerFor(EnumPOJO.class)); - ObjectReader r = jdkDeserialize(bytes); - assertNotNull(r); - - /* 14-Aug-2015, tatu: Looks like pre-loading JsonSerializer is problematic - * at this point; comment out for now. Try to fix later on. - */ - bytes = jdkSerialize(mapper.writerFor(EnumPOJO.class)); - ObjectWriter w = jdkDeserialize(bytes); - assertNotNull(w); - // plus, ensure objects are usable: - String json2 = w.writeValueAsString(new EnumPOJO()); + String json2 = mapper2.writerFor(EnumPOJO.class) + .writeValueAsString(new EnumPOJO()); assertEquals(json, json2); - EnumPOJO result2 = r.readValue(json2); + EnumPOJO result2 = mapper2.readValue(json2, EnumPOJO.class); assertNotNull(result2); } + public void testObjectMapper() throws IOException + { + final String EXP_JSON = "{\"x\":2,\"y\":3}"; + final MyPojo p = new MyPojo(2, 3); + assertEquals(EXP_JSON, MAPPER.writeValueAsString(p)); + + byte[] bytes = jdkSerialize(MAPPER); + ObjectMapper mapper2 = jdkDeserialize(bytes); + assertEquals(EXP_JSON, mapper2.writeValueAsString(p)); + MyPojo p2 = mapper2.readValue(EXP_JSON, MyPojo.class); + assertEquals(p.x, p2.x); + assertEquals(p.y, p2.y); + } + public void testObjectWriter() throws IOException { - ObjectWriter origWriter = MAPPER.writer(); + // 20-Apr-2018, tatu: ObjectReader no longer JDK serializable so + // can only check via thawed ObjectMapper + + byte[] bytes = jdkSerialize(MAPPER); + ObjectMapper mapper2 = jdkDeserialize(bytes); + ObjectWriter origWriter = mapper2.writer(); final String EXP_JSON = "{\"x\":2,\"y\":3}"; final MyPojo p = new MyPojo(2, 3); assertEquals(EXP_JSON, origWriter.writeValueAsString(p)); String json = origWriter.writeValueAsString(new AnyBean() .addEntry("a", "b")); - assertNotNull(json); - byte[] bytes = jdkSerialize(origWriter); - ObjectWriter writer2 = jdkDeserialize(bytes); - assertEquals(EXP_JSON, writer2.writeValueAsString(p)); + assertEquals("{\"a\":\"b\"}", json); } public void testObjectReader() throws IOException { - ObjectReader origReader = MAPPER.readerFor(MyPojo.class); + // 20-Apr-2018, tatu: ObjectReader no longer JDK serializable so + // can only check via thawed ObjectMapper + + byte[] bytes = jdkSerialize(MAPPER); + ObjectMapper mapper2 = jdkDeserialize(bytes); + + ObjectReader origReader = mapper2.readerFor(MyPojo.class); String JSON = "{\"x\":1,\"y\":2}"; MyPojo p1 = origReader.readValue(JSON); assertEquals(2, p1.y); - ObjectReader anyReader = MAPPER.readerFor(AnyBean.class); + ObjectReader anyReader = mapper2.readerFor(AnyBean.class); AnyBean any = anyReader.readValue(JSON); assertEquals(Integer.valueOf(2), any.properties().get("y")); - - byte[] readerBytes = jdkSerialize(origReader); - ObjectReader reader2 = jdkDeserialize(readerBytes); - MyPojo p2 = reader2.readValue(JSON); - assertEquals(2, p2.y); - - ObjectReader anyReader2 = jdkDeserialize(jdkSerialize(anyReader)); - AnyBean any2 = anyReader2.readValue(JSON); - assertEquals(Integer.valueOf(2), any2.properties().get("y")); } - public void testObjectMapper() throws IOException + public void testMapperWithModule() throws IOException { + SimpleModule module = new SimpleModule("JDKSerTestModule", Version.unknownVersion()); + { + byte[] b = jdkSerialize(module); + assertNotNull(b); + } + + JsonMapper mapper = JsonMapper.builder() + .enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS) + .enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY) + .addModule(module) + .build(); + + // just force serialization first final String EXP_JSON = "{\"x\":2,\"y\":3}"; final MyPojo p = new MyPojo(2, 3); - assertEquals(EXP_JSON, MAPPER.writeValueAsString(p)); - assertNotNull(MAPPER.getFactory()); - assertNotNull(MAPPER.getFactory().getCodec()); + assertEquals(EXP_JSON, mapper.writeValueAsString(p)); - byte[] bytes = jdkSerialize(MAPPER); + byte[] bytes = jdkSerialize(mapper); ObjectMapper mapper2 = jdkDeserialize(bytes); + + // verify settings + assertTrue(mapper.isEnabled(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)); + assertTrue(mapper.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)); + assertEquals(EXP_JSON, mapper2.writeValueAsString(p)); MyPojo p2 = mapper2.readValue(EXP_JSON, MyPojo.class); assertEquals(p.x, p2.x); assertEquals(p.y, p2.y); - // [databind#2038]: verify that codec is not lost - assertNotNull(mapper2.getFactory()); - assertNotNull(mapper2.getFactory().getCodec()); + // and then reconfigure a bit + ObjectMapper mapper3 = mapper2.rebuild() + .disable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS) + .disable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY) + .build(); + assertFalse(mapper3.isEnabled(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)); + assertFalse(mapper3.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)); + bytes = jdkSerialize(mapper3); + ObjectMapper mapper4 = jdkDeserialize(bytes); + + assertFalse(mapper4.isEnabled(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)); + assertFalse(mapper4.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)); } public void testTypeFactory() throws Exception @@ -197,11 +228,11 @@ public void testTypeFactory() throws Exception public void testLRUMap() throws Exception { - LRUMap map = new LRUMap(32, 32); + SimpleLookupCache map = new SimpleLookupCache(32, 32); map.put("a", 1); byte[] bytes = jdkSerialize(map); - LRUMap result = jdkDeserialize(bytes); + SimpleLookupCache result = jdkDeserialize(bytes); // transient implementation, will be read as empty assertEquals(0, result.size()); diff --git a/src/test/java/com/fasterxml/jackson/databind/MapperViaParserTest.java b/src/test/java/com/fasterxml/jackson/databind/MapperViaParserTest.java index a4a3272c1c..a02c31b5ab 100644 --- a/src/test/java/com/fasterxml/jackson/databind/MapperViaParserTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/MapperViaParserTest.java @@ -6,11 +6,11 @@ import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.io.CharacterEscapes; import com.fasterxml.jackson.core.io.SerializedString; +import com.fasterxml.jackson.core.json.JsonFactory; import com.fasterxml.jackson.core.json.JsonWriteFeature; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.json.JsonMapper; -public class MapperViaParserTest extends BaseMapTest +public class MapperViaParserTest extends BaseMapTest { final static int TWO_BYTE_ESCAPED = 0x111; final static int THREE_BYTE_ESCAPED = 0x1111; @@ -74,80 +74,23 @@ public SerializableString getEscapeSequence(int ch) /******************************************************** */ - public void testPojoReading() throws IOException - { - JsonFactory jf = new MappingJsonFactory(); - final String JSON = "{ \"x\" : 9 }"; - JsonParser jp = jf.createParser(new StringReader(JSON)); - - // let's try first by advancing: - assertToken(JsonToken.START_OBJECT, jp.nextToken()); - Pojo p = jp.readValueAs(Pojo.class); - assertEquals(9, p._x); - jp.close(); - - // and without - jp = jf.createParser(new StringReader(JSON)); - p = jp.readValueAs(Pojo.class); - assertEquals(9, p._x); - jp.close(); - } - - /** - * Test similar to above, but instead reads a sequence of values - */ - public void testIncrementalPojoReading() throws IOException - { - JsonFactory jf = new MappingJsonFactory(); - final String JSON = "[ 1, true, null, \"abc\" ]"; - JsonParser p = jf.createParser(new StringReader(JSON)); - - // let's advance past array start to prevent full binding - assertToken(JsonToken.START_ARRAY, p.nextToken()); - - assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); - assertEquals(Integer.valueOf(1), p.readValueAs(Integer.class)); - assertEquals(Boolean.TRUE, p.readValueAs(Boolean.class)); - /* note: null can be returned both when there is no more - * data in current scope, AND when Json null literal is - * bound! - */ - assertNull(p.readValueAs(Object.class)); - // but we can verify that it was Json null by: - assertEquals(JsonToken.VALUE_NULL, p.getLastClearedToken()); - - assertEquals("abc", p.readValueAs(String.class)); - - // this null is for actually hitting the END_ARRAY - assertNull(p.readValueAs(Object.class)); - assertEquals(JsonToken.END_ARRAY, p.getLastClearedToken()); - - // afrer which there should be nothing to advance to: - assertNull(p.nextToken()); - - p.close(); - } + private final ObjectMapper MAPPER = newJsonMapper(); @SuppressWarnings("resource") - public void testPojoReadingFailing() throws IOException + public void testPojoReadingOk() throws IOException { - // regular factory can't do it, without a call to setCodec() - JsonFactory f = new JsonFactory(); - try { - final String JSON = "{ \"x\" : 9 }"; - JsonParser p = f.createParser(new StringReader(JSON)); - Pojo pojo = p.readValueAs(Pojo.class); - fail("Expected an exception: got "+pojo); - } catch (IllegalStateException e) { - verifyException(e, "No ObjectCodec defined"); - } + final String JSON = "{ \"x\" : 9 }"; + JsonParser jp = MAPPER.createParser(new StringReader(JSON)); + jp.nextToken(); + Pojo p = jp.readValueAs(Pojo.class); + assertNotNull(p); } public void testEscapingUsingMapper() throws Exception { - ObjectMapper mapper = JsonMapper.builder() - .configure(JsonWriteFeature.ESCAPE_NON_ASCII, true) - .build(); - mapper.writeValueAsString(String.valueOf((char) 257)); + ObjectMapper mapper = new ObjectMapper(JsonFactory.builder() + .enable(JsonWriteFeature.ESCAPE_NON_ASCII).build()); + final String json = mapper.writeValueAsString(String.valueOf((char) 258)); + assertEquals(quote("\\u0102"), json); } } diff --git a/src/test/java/com/fasterxml/jackson/databind/ObjectMapperTest.java b/src/test/java/com/fasterxml/jackson/databind/ObjectMapperTest.java index 6a27095cff..1abe5092e2 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ObjectMapperTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/ObjectMapperTest.java @@ -7,10 +7,13 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonSetter; import com.fasterxml.jackson.annotation.Nulls; + import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.json.JsonWriteFeature; import com.fasterxml.jackson.core.util.MinimalPrettyPrinter; - +import com.fasterxml.jackson.databind.cfg.ConfigOverrides; +import com.fasterxml.jackson.databind.cfg.DeserializationContexts; +import com.fasterxml.jackson.databind.deser.DeserializerCache; import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector; import com.fasterxml.jackson.databind.introspect.VisibilityChecker; import com.fasterxml.jackson.databind.json.JsonMapper; @@ -46,11 +49,7 @@ public void writeArrayValueSeparator(JsonGenerator g) throws IOException } } - // for [databind#206] - @SuppressWarnings("serial") - static class NoCopyMapper extends ObjectMapper { } - - final ObjectMapper MAPPER = new ObjectMapper(); + private final JsonMapper MAPPER = new JsonMapper(); /* /********************************************************** @@ -58,20 +57,15 @@ static class NoCopyMapper extends ObjectMapper { } /********************************************************** */ - public void testFactoryFeatures() - { - assertTrue(MAPPER.isEnabled(JsonFactory.Feature.CANONICALIZE_FIELD_NAMES)); - } - - public void testGeneratorFeatures() + public void testFeatureDefaults() { - // and also for mapper - JsonMapper mapper = new JsonMapper(); - assertTrue(mapper.isEnabled(JsonGenerator.Feature.AUTO_CLOSE_TARGET)); - assertTrue(mapper.isEnabled(StreamWriteFeature.AUTO_CLOSE_TARGET)); - assertFalse(mapper.isEnabled(JsonWriteFeature.ESCAPE_NON_ASCII)); - assertTrue(mapper.isEnabled(JsonWriteFeature.WRITE_NAN_AS_STRINGS)); - mapper = JsonMapper.builder() + assertTrue(MAPPER.isEnabled(TokenStreamFactory.Feature.CANONICALIZE_FIELD_NAMES)); + assertTrue(MAPPER.isEnabled(JsonWriteFeature.QUOTE_FIELD_NAMES)); + assertTrue(MAPPER.isEnabled(StreamReadFeature.AUTO_CLOSE_SOURCE)); + assertTrue(MAPPER.isEnabled(StreamWriteFeature.AUTO_CLOSE_TARGET)); + assertFalse(MAPPER.isEnabled(JsonWriteFeature.ESCAPE_NON_ASCII)); + assertTrue(MAPPER.isEnabled(JsonWriteFeature.WRITE_NAN_AS_STRINGS)); + JsonMapper mapper = JsonMapper.builder() .disable(StreamWriteFeature.FLUSH_PASSED_TO_STREAM) .disable(JsonWriteFeature.WRITE_NAN_AS_STRINGS) .build(); @@ -79,132 +73,30 @@ public void testGeneratorFeatures() assertFalse(mapper.isEnabled(JsonWriteFeature.WRITE_NAN_AS_STRINGS)); } - public void testParserFeatures() - { - // and also for mapper - ObjectMapper mapper = new ObjectMapper(); - - assertTrue(mapper.isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE)); - assertTrue(mapper.isEnabled(StreamReadFeature.AUTO_CLOSE_SOURCE)); - assertFalse(mapper.isEnabled(JsonParser.Feature.IGNORE_UNDEFINED)); - - mapper.disable(JsonParser.Feature.AUTO_CLOSE_SOURCE, - JsonParser.Feature.STRICT_DUPLICATE_DETECTION); - assertFalse(mapper.isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE)); - assertFalse(mapper.isEnabled(StreamReadFeature.AUTO_CLOSE_SOURCE)); - } - /* /********************************************************** /* Test methods, mapper.copy() /********************************************************** */ - // [databind#28]: ObjectMapper.copy() - public void testCopy() throws Exception - { - ObjectMapper m = new ObjectMapper(); - assertTrue(m.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)); - m.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); - assertFalse(m.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)); - InjectableValues inj = new InjectableValues.Std(); - m.setInjectableValues(inj); - assertFalse(m.isEnabled(JsonParser.Feature.IGNORE_UNDEFINED)); - m.enable(JsonParser.Feature.IGNORE_UNDEFINED); - assertTrue(m.isEnabled(JsonParser.Feature.IGNORE_UNDEFINED)); - - // // First: verify that handling of features is decoupled: - - ObjectMapper m2 = m.copy(); - assertFalse(m2.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)); - m2.enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); - assertTrue(m2.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)); - assertSame(inj, m2.getInjectableValues()); - - // but should NOT change the original - assertFalse(m.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)); - - // nor vice versa: - assertFalse(m.isEnabled(DeserializationFeature.UNWRAP_ROOT_VALUE)); - assertFalse(m2.isEnabled(DeserializationFeature.UNWRAP_ROOT_VALUE)); - m.enable(DeserializationFeature.UNWRAP_ROOT_VALUE); - assertTrue(m.isEnabled(DeserializationFeature.UNWRAP_ROOT_VALUE)); - assertFalse(m2.isEnabled(DeserializationFeature.UNWRAP_ROOT_VALUE)); - - // // Also, underlying JsonFactory instances should be distinct - - assertNotSame(m.getFactory(), m2.getFactory()); - - // [databind#122]: Need to ensure mix-ins are not shared - assertEquals(0, m.getSerializationConfig().mixInCount()); - assertEquals(0, m2.getSerializationConfig().mixInCount()); - assertEquals(0, m.getDeserializationConfig().mixInCount()); - assertEquals(0, m2.getDeserializationConfig().mixInCount()); - - m.addMixIn(String.class, Integer.class); - assertEquals(1, m.getSerializationConfig().mixInCount()); - assertEquals(0, m2.getSerializationConfig().mixInCount()); - assertEquals(1, m.getDeserializationConfig().mixInCount()); - assertEquals(0, m2.getDeserializationConfig().mixInCount()); - - // [databind#913]: Ensure JsonFactory Features copied - assertTrue(m2.isEnabled(JsonParser.Feature.IGNORE_UNDEFINED)); - } - // [databind#1580] public void testCopyOfConfigOverrides() throws Exception { ObjectMapper m = new ObjectMapper(); - SerializationConfig config = m.getSerializationConfig(); - assertEquals(JsonInclude.Value.empty(), config.getDefaultPropertyInclusion()); - assertEquals(JsonSetter.Value.empty(), config.getDefaultSetterInfo()); + SerializationConfig config = m.serializationConfig(); + assertEquals(ConfigOverrides.INCLUDE_ALL, config.getDefaultPropertyInclusion()); + assertEquals(JsonSetter.Value.empty(), config.getDefaultNullHandling()); assertNull(config.getDefaultMergeable()); - VisibilityChecker defaultVis = config.getDefaultVisibilityChecker(); - assertEquals(VisibilityChecker.Std.class, defaultVis.getClass()); // change - JsonInclude.Value customIncl = JsonInclude.Value.empty().withValueInclusion(JsonInclude.Include.NON_DEFAULT); - m.setDefaultPropertyInclusion(customIncl); - JsonSetter.Value customSetter = JsonSetter.Value.forValueNulls(Nulls.SKIP); - m.setDefaultSetterInfo(customSetter); - m.setDefaultMergeable(Boolean.TRUE); - VisibilityChecker customVis = VisibilityChecker.Std.defaultInstance() + VisibilityChecker customVis = VisibilityChecker.defaultInstance() .withFieldVisibility(Visibility.ANY); - m.setVisibility(customVis); - assertSame(customVis, m.getVisibilityChecker()); - - // and verify that copy retains these settings - ObjectMapper m2 = m.copy(); - SerializationConfig config2 = m2.getSerializationConfig(); - assertSame(customIncl, config2.getDefaultPropertyInclusion()); - assertSame(customSetter, config2.getDefaultSetterInfo()); - assertEquals(Boolean.TRUE, config2.getDefaultMergeable()); - assertSame(customVis, config2.getDefaultVisibilityChecker()); - } - - public void testFailedCopy() throws Exception - { - NoCopyMapper src = new NoCopyMapper(); - try { - src.copy(); - fail("Should not pass"); - } catch (IllegalStateException e) { - verifyException(e, "does not override copy()"); - } - } - - public void testAnnotationIntrospectorCopyin() - { - ObjectMapper m = new ObjectMapper(); - m.setAnnotationIntrospector(new MyAnnotationIntrospector()); - assertEquals(MyAnnotationIntrospector.class, - m.getDeserializationConfig().getAnnotationIntrospector().getClass()); - ObjectMapper m2 = m.copy(); - - assertEquals(MyAnnotationIntrospector.class, - m2.getDeserializationConfig().getAnnotationIntrospector().getClass()); - assertEquals(MyAnnotationIntrospector.class, - m2.getSerializationConfig().getAnnotationIntrospector().getClass()); + m = jsonMapperBuilder() + .changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_DEFAULT)) + .changeDefaultVisibility(vc -> customVis) + .changeDefaultNullHandling(n -> n.withValueNulls(Nulls.SKIP)) + .defaultMergeable(Boolean.TRUE) + .build(); } /* @@ -215,11 +107,12 @@ public void testAnnotationIntrospectorCopyin() public void testProps() { - ObjectMapper m = new ObjectMapper(); // should have default factory - assertNotNull(m.getNodeFactory()); + assertNotNull(MAPPER.getNodeFactory()); JsonNodeFactory nf = new JsonNodeFactory(true); - m.setNodeFactory(nf); + JsonMapper m = JsonMapper.builder() + .nodeFactory(nf) + .build(); assertNull(m.getInjectableValues()); assertSame(nf, m.getNodeFactory()); } @@ -231,78 +124,77 @@ public void testConfigForPropertySorting() throws Exception // sort-alphabetically is disabled by default: assertFalse(m.isEnabled(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY)); - SerializationConfig sc = m.getSerializationConfig(); + SerializationConfig sc = m.serializationConfig(); assertFalse(sc.isEnabled(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY)); assertFalse(sc.shouldSortPropertiesAlphabetically()); - DeserializationConfig dc = m.getDeserializationConfig(); + DeserializationConfig dc = m.deserializationConfig(); assertFalse(dc.shouldSortPropertiesAlphabetically()); // but when enabled, should be visible: - m = objectMapperBuilder() + m = jsonMapperBuilder() .enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY) .build(); - sc = m.getSerializationConfig(); + sc = m.serializationConfig(); assertTrue(sc.isEnabled(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY)); assertTrue(sc.shouldSortPropertiesAlphabetically()); - dc = m.getDeserializationConfig(); + dc = m.deserializationConfig(); // and not just via SerializationConfig, but also via DeserializationConfig assertTrue(dc.isEnabled(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY)); assertTrue(dc.shouldSortPropertiesAlphabetically()); } - - public void testJsonFactoryLinkage() - { - // first, implicit factory, giving implicit linkage - assertSame(MAPPER, MAPPER.getFactory().getCodec()); - - // and then explicit factory, which should also be implicitly linked - JsonFactory f = new JsonFactory(); - ObjectMapper m = new ObjectMapper(f); - assertSame(f, m.getFactory()); - assertSame(m, f.getCodec()); - } - - public void testProviderConfig() throws Exception + public void testDeserializationContextCache() throws Exception { ObjectMapper m = new ObjectMapper(); final String JSON = "{ \"x\" : 3 }"; - assertEquals(0, m._deserializationContext._cache.cachedDeserializersCount()); + DeserializationContexts.DefaultImpl dc = (DeserializationContexts.DefaultImpl) m._deserializationContexts; + DeserializerCache cache = dc.cacheForTests(); + + assertEquals(0, cache.cachedDeserializersCount()); // and then should get one constructed for: Bean bean = m.readValue(JSON, Bean.class); assertNotNull(bean); // Since 2.6, serializer for int also cached: - assertEquals(2, m._deserializationContext._cache.cachedDeserializersCount()); - m._deserializationContext._cache.flushCachedDeserializers(); - assertEquals(0, m._deserializationContext._cache.cachedDeserializersCount()); + assertEquals(2, cache.cachedDeserializersCount()); + cache.flushCachedDeserializers(); + assertEquals(0, cache.cachedDeserializersCount()); // 07-Nov-2014, tatu: As per [databind#604] verify that Maps also get cached m = new ObjectMapper(); + dc = (DeserializationContexts.DefaultImpl) m._deserializationContexts; + cache = dc.cacheForTests(); + List stuff = m.readValue("[ ]", List.class); assertNotNull(stuff); // may look odd, but due to "Untyped" deserializer thing, we actually have // 4 deserializers (int, List, Map, Object) - assertEquals(4, m._deserializationContext._cache.cachedDeserializersCount()); + assertEquals(4, cache.cachedDeserializersCount()); } // For [databind#689] public void testCustomDefaultPrettyPrinter() throws Exception { - final ObjectMapper m = new ObjectMapper(); final int[] input = new int[] { 1, 2 }; + JsonMapper m = new JsonMapper(); + // without anything else, compact: assertEquals("[1,2]", m.writeValueAsString(input)); // or with default, get... defaults: - m.enable(SerializationFeature.INDENT_OUTPUT); + m = JsonMapper.builder() + .enable(SerializationFeature.INDENT_OUTPUT) + .build(); assertEquals("[ 1, 2 ]", m.writeValueAsString(input)); assertEquals("[ 1, 2 ]", m.writerWithDefaultPrettyPrinter().writeValueAsString(input)); assertEquals("[ 1, 2 ]", m.writer().withDefaultPrettyPrinter().writeValueAsString(input)); // but then with our custom thingy... - m.setDefaultPrettyPrinter(new FooPrettyPrinter()); + m = JsonMapper.builder() + .defaultPrettyPrinter(new FooPrettyPrinter()) + .enable(SerializationFeature.INDENT_OUTPUT) + .build(); assertEquals("[1 , 2]", m.writeValueAsString(input)); assertEquals("[1 , 2]", m.writerWithDefaultPrettyPrinter().writeValueAsString(input)); assertEquals("[1 , 2]", m.writer().withDefaultPrettyPrinter().writeValueAsString(input)); @@ -311,66 +203,7 @@ public void testCustomDefaultPrettyPrinter() throws Exception assertEquals("[1,2]", m.writer().without(SerializationFeature.INDENT_OUTPUT) .writeValueAsString(input)); } - - // For [databind#703], [databind#978] - public void testNonSerializabilityOfObject() - { - ObjectMapper m = new ObjectMapper(); - assertFalse(m.canSerialize(Object.class)); - // but this used to pass, incorrectly, second time around - assertFalse(m.canSerialize(Object.class)); - - // [databind#978]: Different answer if empty Beans ARE allowed - m = new ObjectMapper(); - m.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); - assertTrue(m.canSerialize(Object.class)); - assertTrue(MAPPER.writer().without(SerializationFeature.FAIL_ON_EMPTY_BEANS) - .canSerialize(Object.class)); - assertFalse(MAPPER.writer().with(SerializationFeature.FAIL_ON_EMPTY_BEANS) - .canSerialize(Object.class)); - } - - // for [databind#756] - public void testEmptyBeanSerializability() - { - // with default settings, error - assertFalse(MAPPER.writer().with(SerializationFeature.FAIL_ON_EMPTY_BEANS) - .canSerialize(EmptyBean.class)); - // but with changes - assertTrue(MAPPER.writer().without(SerializationFeature.FAIL_ON_EMPTY_BEANS) - .canSerialize(EmptyBean.class)); - } - - // for [databind#898] - public void testSerializerProviderAccess() throws Exception - { - // ensure we have "fresh" instance, just in case - ObjectMapper mapper = new ObjectMapper(); - JsonSerializer ser = mapper.getSerializerProviderInstance() - .findValueSerializer(Bean.class); - assertNotNull(ser); - assertEquals(Bean.class, ser.handledType()); - } - - // for [databind#1074] - public void testCopyOfParserFeatures() throws Exception - { - // ensure we have "fresh" instance to start with - ObjectMapper mapper = new ObjectMapper(); - assertFalse(mapper.isEnabled(JsonParser.Feature.IGNORE_UNDEFINED)); - mapper.configure(JsonParser.Feature.IGNORE_UNDEFINED, true); - assertTrue(mapper.isEnabled(JsonParser.Feature.IGNORE_UNDEFINED)); - - ObjectMapper copy = mapper.copy(); - assertTrue(copy.isEnabled(JsonParser.Feature.IGNORE_UNDEFINED)); - - // also verify there's no back-linkage - copy.configure(JsonParser.Feature.IGNORE_UNDEFINED, false); - assertFalse(copy.isEnabled(JsonParser.Feature.IGNORE_UNDEFINED)); - assertTrue(mapper.isEnabled(JsonParser.Feature.IGNORE_UNDEFINED)); - } - // since 2.8 public void testDataOutputViaMapper() throws Exception { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); @@ -390,7 +223,6 @@ public void testDataOutputViaMapper() throws Exception assertEquals(exp, bytes.toString("UTF-8")); } - // since 2.8 @SuppressWarnings("unchecked") public void testDataInputViaMapper() throws Exception { diff --git a/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java b/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java index 8dbdf4f240..5884c4bf11 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java @@ -1,6 +1,5 @@ package com.fasterxml.jackson.databind; -import java.io.StringWriter; import java.util.Collections; import java.util.List; import java.util.Map; @@ -13,7 +12,6 @@ import com.fasterxml.jackson.databind.json.JsonMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; -import com.fasterxml.jackson.databind.node.ObjectNode; public class ObjectReaderTest extends BaseMapTest { @@ -26,7 +24,7 @@ static class POJO { public void testSimpleViaParser() throws Exception { final String JSON = "[1]"; - JsonParser p = MAPPER.getFactory().createParser(JSON); + JsonParser p = MAPPER.createParser(JSON); Object ob = MAPPER.readerFor(Object.class) .readValue(p); p.close(); @@ -47,7 +45,7 @@ public void testSimpleAltSources() throws Exception assertEquals(1, ((List) ob).size()); } - public void testParserFeatures() throws Exception + public void testJsonReadFeatures() throws Exception { final String JSON = "[ /* foo */ 7 ]"; // default won't accept comments, let's change that: @@ -82,7 +80,7 @@ public void testFeatureSettings() throws Exception { ObjectReader r = MAPPER.reader(); assertFalse(r.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)); - assertFalse(r.isEnabled(JsonParser.Feature.IGNORE_UNDEFINED)); + assertFalse(r.isEnabled(StreamReadFeature.IGNORE_UNDEFINED)); r = r.withoutFeatures(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, DeserializationFeature.FAIL_ON_INVALID_SUBTYPE); @@ -101,14 +99,9 @@ public void testFeatureSettings() throws Exception public void testMiscSettings() throws Exception { ObjectReader r = MAPPER.reader(); - assertSame(MAPPER.getFactory(), r.getFactory()); + assertSame(MAPPER.tokenStreamFactory(), r.parserFactory()); - JsonFactory f = new JsonFactory(); - r = r.with(f); - assertSame(f, r.getFactory()); - assertSame(r, r.with(f)); - - assertNotNull(r.getTypeFactory()); + assertNotNull(r.typeFactory()); assertNull(r.getInjectableValues()); r = r.withAttributes(Collections.emptyMap()); @@ -129,21 +122,6 @@ public void testMiscSettings() throws Exception r = newR; } - @SuppressWarnings("deprecation") - public void testDeprecatedSettings() throws Exception - { - ObjectReader r = MAPPER.reader(); - - // and deprecated variants - ObjectReader newR = r.forType(MAPPER.constructType(String.class)); - assertSame(newR, newR.withType(String.class)); - assertSame(newR, newR.withType(MAPPER.constructType(String.class))); - - newR = newR.withRootName(PropertyName.construct("foo")); - assertNotSame(r, newR); - assertSame(newR, newR.withRootName(PropertyName.construct("foo"))); - } - public void testNoPrefetch() throws Exception { ObjectReader r = MAPPER.reader() @@ -185,7 +163,10 @@ public void testPointerLoadingAsJsonNode() throws Exception { JsonNode node = reader.readTree(source); assertTrue(node.has("name")); - assertEquals("{\"value\":1234}", node.get("name").toString()); + JsonNode entry = node.get("name"); + assertNotNull(entry); + assertTrue(entry.isObject()); + assertEquals(1234, entry.get("value").asInt()); } public void testPointerLoadingMappingIteratorOne() throws Exception { @@ -268,28 +249,6 @@ public void testTreeToValue() throws Exception List list = r.treeToValue(n, List.class); assertEquals(1, list.size()); } - - public void testCodecUnsupportedWrites() throws Exception - { - ObjectReader r = MAPPER.readerFor(String.class); - JsonGenerator g = MAPPER.getFactory().createGenerator(new StringWriter()); - ObjectNode n = MAPPER.createObjectNode(); - try { - r.writeTree(g, n); - fail("Should not pass"); - } catch (UnsupportedOperationException e) { - ; - } - try { - r.writeValue(g, "Foo"); - fail("Should not pass"); - } catch (UnsupportedOperationException e) { - ; - } - g.close(); - - g.close(); - } /* /********************************************************** diff --git a/src/test/java/com/fasterxml/jackson/databind/ObjectWriterTest.java b/src/test/java/com/fasterxml/jackson/databind/ObjectWriterTest.java index b503178903..6d718c53ee 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ObjectWriterTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/ObjectWriterTest.java @@ -91,7 +91,7 @@ public void testPrefetch() throws Exception public void testObjectWriterFeatures() throws Exception { ObjectWriter writer = MAPPER.writer() - .without(JsonWriteFeature.QUOTE_FIELD_NAMES); + .without(JsonWriteFeature.QUOTE_FIELD_NAMES); Map map = new HashMap(); map.put("a", 1); assertEquals("{a:1}", writer.writeValueAsString(map)); @@ -120,12 +120,6 @@ public void testPolymorphicWithTyping() throws Exception assertEquals(aposToQuotes("{'type':'B','b':-5}"), json); } - public void testCanSerialize() throws Exception - { - assertTrue(MAPPER.writer().canSerialize(String.class)); - assertTrue(MAPPER.writer().canSerialize(String.class, null)); - } - public void testNoPrefetch() throws Exception { ObjectWriter w = MAPPER.writer() @@ -149,7 +143,7 @@ public void testWithCloseCloseable() throws Exception input.close(); // and via explicitly passed generator - JsonGenerator g = MAPPER.getFactory().createGenerator(new StringWriter()); + JsonGenerator g = MAPPER.createGenerator(new StringWriter()); input = new CloseableValue(); assertFalse(input.closed); w.writeValue(g, input); @@ -173,13 +167,10 @@ public void testViewSettings() throws Exception public void testMiscSettings() throws Exception { ObjectWriter w = MAPPER.writer(); - assertSame(MAPPER.getFactory(), w.getFactory()); + assertSame(MAPPER.tokenStreamFactory(), w.generatorFactory()); assertFalse(w.hasPrefetchedSerializer()); - assertNotNull(w.getTypeFactory()); + assertNotNull(w.typeFactory()); - JsonFactory f = new JsonFactory(); - w = w.with(f); - assertSame(f, w.getFactory()); ObjectWriter newW = w.with(Base64Variants.MODIFIED_FOR_URL); assertNotSame(w, newW); assertSame(newW, newW.with(Base64Variants.MODIFIED_FOR_URL)); @@ -233,7 +224,7 @@ public void testFeatureSettings() throws Exception { ObjectWriter w = MAPPER.writer(); assertFalse(w.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)); - assertFalse(w.isEnabled(JsonGenerator.Feature.STRICT_DUPLICATE_DETECTION)); + assertFalse(w.isEnabled(StreamWriteFeature.STRICT_DUPLICATE_DETECTION)); ObjectWriter newW = w.with(SerializationFeature.FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS, SerializationFeature.INDENT_OUTPUT); assertNotSame(w, newW); @@ -258,15 +249,15 @@ public void testFeatureSettings() throws Exception SerializationFeature.EAGER_SERIALIZER_FETCH)); } - public void testGeneratorFeatures() throws Exception + public void testStreamWriteFeatures() throws Exception { ObjectWriter w = MAPPER.writer(); assertNotSame(w, w.with(JsonWriteFeature.ESCAPE_NON_ASCII)); assertNotSame(w, w.withFeatures(JsonWriteFeature.ESCAPE_NON_ASCII)); - assertTrue(w.isEnabled(JsonGenerator.Feature.AUTO_CLOSE_TARGET)); - assertNotSame(w, w.without(JsonGenerator.Feature.AUTO_CLOSE_TARGET)); - assertNotSame(w, w.withoutFeatures(JsonGenerator.Feature.AUTO_CLOSE_TARGET)); + assertTrue(w.isEnabled(StreamWriteFeature.AUTO_CLOSE_TARGET)); + assertNotSame(w, w.without(StreamWriteFeature.AUTO_CLOSE_TARGET)); + assertNotSame(w, w.withoutFeatures(StreamWriteFeature.AUTO_CLOSE_TARGET)); } /* diff --git a/src/test/java/com/fasterxml/jackson/databind/RoundtripTest.java b/src/test/java/com/fasterxml/jackson/databind/RoundtripTest.java deleted file mode 100644 index 704c5738a2..0000000000 --- a/src/test/java/com/fasterxml/jackson/databind/RoundtripTest.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.fasterxml.jackson.databind; - -import com.fasterxml.jackson.databind.testutil.MediaItem; - -public class RoundtripTest extends BaseMapTest -{ - private final ObjectMapper MAPPER = new ObjectMapper(); - - public void testMedaItemRoundtrip() throws Exception - { - MediaItem.Content c = new MediaItem.Content(); - c.setBitrate(9600); - c.setCopyright("none"); - c.setDuration(360000L); - c.setFormat("lzf"); - c.setHeight(640); - c.setSize(128000L); - c.setTitle("Amazing Stuff For Something Or Oth\u00CBr!"); - c.setUri("http://multi.fario.us/index.html"); - c.setWidth(1400); - - c.addPerson("Joe Sixp\u00e2ck"); - c.addPerson("Ezekiel"); - c.addPerson("Sponge-Bob Squarepant\u00DF"); - - MediaItem input = new MediaItem(c); - input.addPhoto(new MediaItem.Photo()); - input.addPhoto(new MediaItem.Photo()); - input.addPhoto(new MediaItem.Photo()); - - String json = MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(input); - - MediaItem output = MAPPER.readValue(new java.io.StringReader(json), MediaItem.class); - assertNotNull(output); - - assertNotNull(output.getImages()); - assertEquals(input.getImages().size(), output.getImages().size()); - assertNotNull(output.getContent()); - assertEquals(input.getContent().getTitle(), output.getContent().getTitle()); - assertEquals(input.getContent().getUri(), output.getContent().getUri()); - - // compare re-serialization as a simple check as well - assertEquals(json, MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(output)); - } -} diff --git a/src/test/java/com/fasterxml/jackson/databind/TestFormatSchema.java b/src/test/java/com/fasterxml/jackson/databind/TestFormatSchema.java deleted file mode 100644 index a5050b745e..0000000000 --- a/src/test/java/com/fasterxml/jackson/databind/TestFormatSchema.java +++ /dev/null @@ -1,269 +0,0 @@ -package com.fasterxml.jackson.databind; - -import java.io.*; -import java.math.BigDecimal; -import java.math.BigInteger; - -import com.fasterxml.jackson.core.*; -import com.fasterxml.jackson.core.io.IOContext; -import com.fasterxml.jackson.core.base.ParserBase; -import com.fasterxml.jackson.core.base.GeneratorBase; -import com.fasterxml.jackson.databind.ObjectMapper; - -/** - * Basic tests to ensure that {@link FormatSchema} instances are properly - * passed to {@link JsonGenerator} and {@link JsonParser} instances if - * mapper, reader or writer is configured with one. - */ -public class TestFormatSchema extends BaseMapTest -{ - /* - /********************************************************************** - /* Helper classes - /********************************************************************** - */ - - static class MySchema implements FormatSchema { - @Override - public String getSchemaType() { return "test"; } - } - - static class FactoryWithSchema extends JsonFactory - { - @Override - public String getFormatName() { return "test"; } - - @Override - public boolean canUseSchema(FormatSchema schema) { - return (schema instanceof MySchema); - } - - private static final long serialVersionUID = 1L; - @Override - protected JsonParser _createParser(Reader r, IOContext ctxt) - throws IOException, JsonParseException - { - return new ParserWithSchema(ctxt, _parserFeatures); - } - - @Override - protected JsonGenerator _createGenerator(Writer out, IOContext ctxt) throws IOException - { - return new GeneratorWithSchema(_generatorFeatures, _objectCodec); - } - } - - // Ugly, but easiest way to get schema back is to throw exception... - @SuppressWarnings("serial") - static class SchemaException extends RuntimeException - { - public final FormatSchema _schema; - - public SchemaException(FormatSchema s) { - _schema = s; - } - } - - static class ParserWithSchema extends ParserBase - { - public ParserWithSchema(IOContext ioCtxt, int features) - { - super(ioCtxt, features); - } - - @Override - public void setSchema(FormatSchema schema) { - throw new SchemaException(schema); - } - - @Override - protected void _finishString() throws IOException, JsonParseException { } - - @Override - public byte[] getBinaryValue(Base64Variant b64variant) { - return null; - } - - @Override - public byte[] getEmbeddedObject() { - return null; - } - - @Override - public String getText() throws IOException, JsonParseException { - return null; - } - - @Override - public char[] getTextCharacters() throws IOException { - return null; - } - - @Override - public int getTextLength() throws IOException, JsonParseException { - return 0; - } - - @Override - public int getTextOffset() throws IOException, JsonParseException { - return 0; - } - - @Override - public JsonToken nextToken() throws IOException, JsonParseException { - return null; - } - - @Override - public ObjectCodec getCodec() { - return null; - } - - @Override - public void setCodec(ObjectCodec c) { } - - @Override - protected void _closeInput() throws IOException { - } - - @Override - public int readBinaryValue(Base64Variant b64variant, OutputStream out) { - return 0; - } - } - - static class GeneratorWithSchema extends GeneratorBase - { - public GeneratorWithSchema(int features, ObjectCodec codec) - { - super(features, codec); - } - - @Override - public void setSchema(FormatSchema schema) { - throw new SchemaException(schema); - } - - @Override - protected void _releaseBuffers() { } - - @Override - protected void _verifyValueWrite(String typeMsg) throws IOException { } - - @Override - public void flush() throws IOException { } - - @Override - public void writeBinary(Base64Variant b64variant, byte[] data, - int offset, int len) throws IOException { } - - @Override - public void writeBoolean(boolean state) throws IOException { } - - @Override - public void writeFieldName(String name) throws IOException { } - - @Override - public void writeNull() throws IOException, JsonGenerationException { } - - @Override - public void writeNumber(short v) throws IOException { } - - @Override - public void writeNumber(int v) throws IOException { } - - @Override - public void writeNumber(long v) throws IOException { } - - @Override - public void writeNumber(BigInteger v) throws IOException { } - - @Override - public void writeNumber(double d) throws IOException { } - - @Override - public void writeNumber(float f) throws IOException { } - - @Override - public void writeNumber(BigDecimal dec) throws IOException { } - - @Override - public void writeNumber(String encodedValue) throws IOException { } - - @Override - public void writeRaw(String text) throws IOException { } - - @Override - public void writeRaw(String text, int offset, int len) { } - - @Override - public void writeRaw(char[] text, int offset, int len) { } - - @Override - public void writeRaw(char c) throws IOException { } - - @Override - public void writeRawUTF8String(byte[] text, int offset, int length) { } - - @Override - public void writeString(String text) throws IOException { } - - @Override - public void writeString(char[] text, int offset, int len) { } - - @Override - public void writeUTF8String(byte[] text, int offset, int length) { } - - @Override - public void writeStartArray() { } - - @Override - public void writeEndArray() throws IOException, JsonGenerationException { } - - @Override - public void writeStartObject() { } - - @Override - public void writeEndObject() { } - - @Override - public int writeBinary(Base64Variant b64variant, InputStream data, int dataLength) { - return -1; - } - } - - /* - /********************************************************************** - /* Unit tests - /********************************************************************** - */ - - public void testFormatForParsers() throws Exception - { - ObjectMapper mapper = new ObjectMapper(new FactoryWithSchema()); - MySchema s = new MySchema(); - StringReader r = new StringReader("{}"); - // bit ugly, but can't think of cleaner simple way to check this... - try { - mapper.reader(s).forType(Object.class).readValue(r); - fail("Excpected exception"); - } catch (SchemaException e) { - assertSame(s, e._schema); - } - } - - public void testFormatForGenerators() throws Exception - { - ObjectMapper mapper = new ObjectMapper(new FactoryWithSchema()); - MySchema s = new MySchema(); - StringWriter sw = new StringWriter(); - // bit ugly, but can't think of cleaner simple way to check this... - try { - mapper.writer(s).writeValue(sw, "Foobar"); - fail("Excpected exception"); - } catch (SchemaException e) { - assertSame(s, e._schema); - } - } - -} diff --git a/src/test/java/com/fasterxml/jackson/databind/TestHandlerInstantiation.java b/src/test/java/com/fasterxml/jackson/databind/TestHandlerInstantiation.java index c92ba3bce9..21dcc0151f 100644 --- a/src/test/java/com/fasterxml/jackson/databind/TestHandlerInstantiation.java +++ b/src/test/java/com/fasterxml/jackson/databind/TestHandlerInstantiation.java @@ -6,17 +6,19 @@ import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.annotation.JsonTypeInfo.As; import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; + import com.fasterxml.jackson.core.*; + import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.KeyDeserializer; -import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.*; import com.fasterxml.jackson.databind.cfg.HandlerInstantiator; import com.fasterxml.jackson.databind.cfg.MapperConfig; import com.fasterxml.jackson.databind.introspect.Annotated; +import com.fasterxml.jackson.databind.json.JsonMapper; import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; import com.fasterxml.jackson.databind.jsontype.impl.TypeIdResolverBase; @@ -233,16 +235,18 @@ public TypeResolverBuilder typeResolverBuilderInstance(MapperConfig config public void testDeserializer() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.setHandlerInstantiator(new MyInstantiator("abc:")); + JsonMapper mapper = JsonMapper.builder() + .handlerInstantiator(new MyInstantiator("abc:")) + .build(); MyBean result = mapper.readValue(quote("123"), MyBean.class); assertEquals("abc:123", result.value); } public void testKeyDeserializer() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.setHandlerInstantiator(new MyInstantiator("abc:")); + JsonMapper mapper = JsonMapper.builder() + .handlerInstantiator(new MyInstantiator("abc:")) + .build(); MyMap map = mapper.readValue("{\"a\":\"b\"}", MyMap.class); // easiest to test by just serializing... assertEquals("{\"KEY\":\"b\"}", mapper.writeValueAsString(map)); @@ -250,15 +254,17 @@ public void testKeyDeserializer() throws Exception public void testSerializer() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.setHandlerInstantiator(new MyInstantiator("xyz:")); + JsonMapper mapper = JsonMapper.builder() + .handlerInstantiator(new MyInstantiator("xyz:")) + .build(); assertEquals(quote("xyz:456"), mapper.writeValueAsString(new MyBean("456"))); } public void testTypeIdResolver() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.setHandlerInstantiator(new MyInstantiator("foobar")); + JsonMapper mapper = JsonMapper.builder() + .handlerInstantiator(new MyInstantiator("foobar")) + .build(); String json = mapper.writeValueAsString(new TypeIdBeanWrapper(new TypeIdBean(123))); // should now use our custom id scheme: assertEquals("{\"bean\":[\"!!!\",{\"x\":123}]}", json); diff --git a/src/test/java/com/fasterxml/jackson/databind/TestRootName.java b/src/test/java/com/fasterxml/jackson/databind/TestRootName.java index 02837dac50..b6b8e2d22d 100644 --- a/src/test/java/com/fasterxml/jackson/databind/TestRootName.java +++ b/src/test/java/com/fasterxml/jackson/databind/TestRootName.java @@ -124,9 +124,9 @@ public void testRootUsingExplicitConfig() throws Exception private ObjectMapper rootMapper() { - ObjectMapper mapper = new ObjectMapper(); - mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true); - mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true); - return mapper; + return jsonMapperBuilder() + .configure(SerializationFeature.WRAP_ROOT_VALUE, true) + .configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true) + .build(); } } diff --git a/src/test/java/com/fasterxml/jackson/databind/cfg/BogusFormatFeature.java b/src/test/java/com/fasterxml/jackson/databind/cfg/BogusFormatFeature.java deleted file mode 100644 index c059148255..0000000000 --- a/src/test/java/com/fasterxml/jackson/databind/cfg/BogusFormatFeature.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.fasterxml.jackson.databind.cfg; - -import com.fasterxml.jackson.core.FormatFeature; - -public enum BogusFormatFeature - implements FormatFeature -{ - FF_ENABLED_BY_DEFAULT(true), - FF_DISABLED_BY_DEFAULT(false); - - private boolean _default; - - private BogusFormatFeature(boolean d) { - _default = d; - } - - @Override - public boolean enabledByDefault() { - return _default; - } - - @Override - public int getMask() { - return (1 << ordinal()); - } - - @Override - public boolean enabledIn(int flags) { - return (flags & getMask()) != 0; - } -} diff --git a/src/test/java/com/fasterxml/jackson/databind/cfg/ConfigObjectsTest.java b/src/test/java/com/fasterxml/jackson/databind/cfg/ConfigObjectsTest.java deleted file mode 100644 index 0ace3b096c..0000000000 --- a/src/test/java/com/fasterxml/jackson/databind/cfg/ConfigObjectsTest.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.fasterxml.jackson.databind.cfg; - -import com.fasterxml.jackson.databind.*; - -import com.fasterxml.jackson.databind.jsontype.SubtypeResolver; -import com.fasterxml.jackson.databind.jsontype.impl.StdSubtypeResolver; - -public class ConfigObjectsTest extends BaseMapTest -{ - static class Base { } - static class Sub extends Base { } - - public void testSubtypeResolver() throws Exception - { - ObjectMapper mapper = new ObjectMapper(); - SubtypeResolver res = mapper.getSubtypeResolver(); - assertTrue(res instanceof StdSubtypeResolver); - - StdSubtypeResolver repl = new StdSubtypeResolver(); - repl.registerSubtypes(Sub.class); - mapper.setSubtypeResolver(repl); - assertSame(repl, mapper.getSubtypeResolver()); - } - - public void testMics() throws Exception - { - assertFalse(MapperFeature.AUTO_DETECT_FIELDS.enabledIn(0)); - assertTrue(MapperFeature.AUTO_DETECT_FIELDS.enabledIn(-1)); - } -} diff --git a/src/test/java/com/fasterxml/jackson/databind/cfg/ConfigOverridesTest.java b/src/test/java/com/fasterxml/jackson/databind/cfg/ConfigOverridesTest.java new file mode 100644 index 0000000000..2e3d2ad865 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/cfg/ConfigOverridesTest.java @@ -0,0 +1,20 @@ +package com.fasterxml.jackson.databind.cfg; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.databind.*; + +public class ConfigOverridesTest extends BaseMapTest +{ + public void testSnapshot() throws Exception + { + ConfigOverrides co = new ConfigOverrides(); + co.findOrCreateOverride(String.class) + .setVisibility(JsonAutoDetect.Value.construct(PropertyAccessor.SETTER, + Visibility.NONE)); + // simplest verification of snapshot(): check that string repr matches + assertEquals(co.toString(), + co.snapshot().toString()); + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/cfg/DatabindContextTest.java b/src/test/java/com/fasterxml/jackson/databind/cfg/DatabindContextTest.java deleted file mode 100644 index 9da87a0278..0000000000 --- a/src/test/java/com/fasterxml/jackson/databind/cfg/DatabindContextTest.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.fasterxml.jackson.databind.cfg; - -import com.fasterxml.jackson.databind.*; - -public class DatabindContextTest extends BaseMapTest -{ - private final ObjectMapper MAPPER = objectMapper(); - - public void testDeserializationContext() throws Exception - { - DeserializationContext ctxt = MAPPER.getDeserializationContext(); - // should be ok to try to resolve `null` - assertNull(ctxt.constructType((Class) null)); - assertNull(ctxt.constructType((java.lang.reflect.Type) null)); - } - - public void testSerializationContext() throws Exception - { - SerializerProvider ctxt = MAPPER.getSerializerProvider(); - assertNull(ctxt.constructType(null)); - } -} diff --git a/src/test/java/com/fasterxml/jackson/databind/cfg/DeserializationConfigTest.java b/src/test/java/com/fasterxml/jackson/databind/cfg/DeserializationConfigTest.java index d1ac009390..e31743e95b 100644 --- a/src/test/java/com/fasterxml/jackson/databind/cfg/DeserializationConfigTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/cfg/DeserializationConfigTest.java @@ -2,25 +2,22 @@ import java.util.Collections; -import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.StreamReadFeature; import com.fasterxml.jackson.core.json.JsonReadFeature; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.introspect.ClassIntrospector; public class DeserializationConfigTest extends BaseMapTest { - private final ObjectMapper MAPPER = new ObjectMapper(); + private final ObjectMapper MAPPER = newJsonMapper(); public void testFeatureDefaults() { ObjectMapper m = new ObjectMapper(); - DeserializationConfig cfg = m.getDeserializationConfig(); + DeserializationConfig cfg = m.deserializationConfig(); // Expected defaults: assertTrue(cfg.isEnabled(MapperFeature.USE_ANNOTATIONS)); - assertTrue(cfg.isEnabled(MapperFeature.AUTO_DETECT_SETTERS)); - assertTrue(cfg.isEnabled(MapperFeature.AUTO_DETECT_CREATORS)); - assertTrue(cfg.isEnabled(MapperFeature.USE_GETTERS_AS_SETTERS)); + assertFalse(cfg.isEnabled(MapperFeature.USE_GETTERS_AS_SETTERS)); // 3.0 assertTrue(cfg.isEnabled(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS)); assertFalse(cfg.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)); @@ -31,52 +28,44 @@ public void testFeatureDefaults() public void testBasicFeatures() throws Exception { - DeserializationConfig config = MAPPER.getDeserializationConfig(); + DeserializationConfig config = MAPPER.deserializationConfig(); assertTrue(config.hasDeserializationFeatures(DeserializationFeature.EAGER_DESERIALIZER_FETCH.getMask())); assertFalse(config.hasDeserializationFeatures(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY.getMask())); assertTrue(config.hasSomeOfFeatures(DeserializationFeature.EAGER_DESERIALIZER_FETCH.getMask() + DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY.getMask())); assertFalse(config.hasSomeOfFeatures(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY.getMask())); - // if no changes then same config object - assertSame(config, config.without()); - assertSame(config, config.with()); - assertSame(config, config.with(MAPPER.getSubtypeResolver())); - - // and then change - DeserializationConfig newConfig = config.with(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true); - assertNotSame(config, newConfig); - config = newConfig; - - // but another attempt with no real change returns same - assertSame(config, config.with(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)); - assertNotSame(config, config.with(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, false)); - assertNotSame(config, config.with(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT, DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES)); } - public void testParserFeatures() throws Exception + public void testStreamReadFeatures() throws Exception { - DeserializationConfig config = MAPPER.getDeserializationConfig(); - assertNotSame(config, config.with(JsonReadFeature.ALLOW_JAVA_COMMENTS)); - assertNotSame(config, config.withFeatures(JsonReadFeature.ALLOW_JAVA_COMMENTS, - JsonReadFeature.ALLOW_MISSING_VALUES)); - - assertNotSame(config, config.without(JsonReadFeature.ALLOW_JAVA_COMMENTS)); - assertNotSame(config, config.withoutFeatures(JsonReadFeature.ALLOW_JAVA_COMMENTS, - JsonReadFeature.ALLOW_MISSING_VALUES)); + DeserializationConfig config = MAPPER.deserializationConfig(); + + assertNotSame(config, config.with(StreamReadFeature.IGNORE_UNDEFINED)); + assertNotSame(config, config.withFeatures(StreamReadFeature.IGNORE_UNDEFINED, + StreamReadFeature.STRICT_DUPLICATE_DETECTION)); + + assertSame(config, config.without(StreamReadFeature.IGNORE_UNDEFINED)); + assertSame(config, config.withoutFeatures(StreamReadFeature.IGNORE_UNDEFINED, + StreamReadFeature.STRICT_DUPLICATE_DETECTION)); } - public void testFormatFeatures() throws Exception + public void testJsonReadFeatures() throws Exception { - DeserializationConfig config = MAPPER.getDeserializationConfig(); - assertNotSame(config, config.with(BogusFormatFeature.FF_DISABLED_BY_DEFAULT)); - assertNotSame(config, config.withFeatures(BogusFormatFeature.FF_DISABLED_BY_DEFAULT, - BogusFormatFeature.FF_ENABLED_BY_DEFAULT)); - assertNotSame(config, config.without(BogusFormatFeature.FF_ENABLED_BY_DEFAULT)); - assertNotSame(config, config.withoutFeatures(BogusFormatFeature.FF_DISABLED_BY_DEFAULT, - BogusFormatFeature.FF_ENABLED_BY_DEFAULT)); + final JsonReadFeature DISABLED_BY_DEFAULT = JsonReadFeature.ALLOW_JAVA_COMMENTS; + final JsonReadFeature DISABLED_BY_DEFAULT2 = JsonReadFeature.ALLOW_MISSING_VALUES; + DeserializationConfig config = MAPPER.deserializationConfig(); + DeserializationConfig config2 = config.with(DISABLED_BY_DEFAULT); + assertNotSame(config, config2); + DeserializationConfig config3 = config.withFeatures(DISABLED_BY_DEFAULT2, + DISABLED_BY_DEFAULT); + assertNotSame(config, config3); + + assertNotSame(config3, config3.without(DISABLED_BY_DEFAULT)); + assertNotSame(config3, config3.withoutFeatures(DISABLED_BY_DEFAULT2, + DISABLED_BY_DEFAULT)); } /* Test to verify that we don't overflow number of features; if we @@ -95,21 +84,11 @@ public void testEnumIndexes() } } - public void testOverrideIntrospectors() - { - ObjectMapper m = new ObjectMapper(); - DeserializationConfig cfg = m.getDeserializationConfig(); - // and finally, ensure we could override introspectors - cfg = cfg.with((ClassIntrospector) null); // no way to verify tho - cfg = cfg.with((AnnotationIntrospector) null); - assertNull(cfg.getAnnotationIntrospector()); - } - public void testMisc() throws Exception { - DeserializationConfig config = MAPPER.getDeserializationConfig(); - assertEquals(JsonInclude.Value.empty(), config.getDefaultPropertyInclusion()); - assertEquals(JsonInclude.Value.empty(), config.getDefaultPropertyInclusion(String.class)); + DeserializationConfig config = MAPPER.deserializationConfig(); + assertEquals(ConfigOverrides.INCLUDE_ALL, config.getDefaultPropertyInclusion()); + assertEquals(ConfigOverrides.INCLUDE_ALL, config.getDefaultPropertyInclusion(String.class)); assertSame(config, config.withRootName((PropertyName) null)); // defaults to 'none' @@ -122,6 +101,6 @@ public void testMisc() throws Exception assertNotSame(config, config.with(new ContextAttributes.Impl(Collections.singletonMap("a", "b")))); // should also be able to introspect: - assertNotNull(config.introspectDirectClassAnnotations(getClass())); +// assertNotNull(config.introspectDirectClassAnnotations(getClass())); } } diff --git a/src/test/java/com/fasterxml/jackson/databind/cfg/SerConfigTest.java b/src/test/java/com/fasterxml/jackson/databind/cfg/SerConfigTest.java index 26942fad62..28b1d64094 100644 --- a/src/test/java/com/fasterxml/jackson/databind/cfg/SerConfigTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/cfg/SerConfigTest.java @@ -2,8 +2,7 @@ import java.util.Collections; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.StreamWriteFeature; import com.fasterxml.jackson.core.json.JsonWriteFeature; import com.fasterxml.jackson.databind.*; @@ -13,59 +12,57 @@ public class SerConfigTest extends BaseMapTest public void testSerConfig() throws Exception { - SerializationConfig config = MAPPER.getSerializationConfig(); + SerializationConfig config = MAPPER.serializationConfig(); assertTrue(config.hasSerializationFeatures(SerializationFeature.FAIL_ON_EMPTY_BEANS.getMask())); assertFalse(config.hasSerializationFeatures(SerializationFeature.CLOSE_CLOSEABLE.getMask())); - assertEquals(JsonInclude.Value.empty(), config.getDefaultPropertyInclusion()); - assertEquals(JsonInclude.Value.empty(), config.getDefaultPropertyInclusion(String.class)); + assertEquals(ConfigOverrides.INCLUDE_ALL, config.getDefaultPropertyInclusion()); + assertEquals(ConfigOverrides.INCLUDE_ALL, config.getDefaultPropertyInclusion(String.class)); assertFalse(config.useRootWrapping()); - // if no changes then same config object - assertSame(config, config.without()); - assertSame(config, config.with()); - assertSame(config, config.with(MAPPER.getSubtypeResolver())); - - // and then change - SerializationConfig newConfig = config.with(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true); - assertNotSame(config, newConfig); - config = newConfig; - assertSame(config, config.with(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)); - assertNotSame(config, config.with(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, false)); - assertNotSame(config, config.with(SerializationFeature.INDENT_OUTPUT, SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)); assertSame(config, config.withRootName((PropertyName) null)); // defaults to 'none' - newConfig = config.withRootName(PropertyName.construct("foobar")); + SerializationConfig newConfig = config.withRootName(PropertyName.construct("foobar")); assertNotSame(config, newConfig); assertTrue(newConfig.useRootWrapping()); assertSame(config, config.with(config.getAttributes())); assertNotSame(config, config.with(new ContextAttributes.Impl(Collections.singletonMap("a", "b")))); - assertNotNull(config.introspectDirectClassAnnotations(getClass())); +// assertNotNull(config.introspectDirectClassAnnotations(getClass())); } - public void testGeneratorFeatures() throws Exception + public void testStreamWriteFeatures() throws Exception { - SerializationConfig config = MAPPER.getSerializationConfig(); + SerializationConfig config = MAPPER.serializationConfig(); + assertFalse(config.hasFormatFeature(JsonWriteFeature.ESCAPE_NON_ASCII)); assertNotSame(config, config.with(JsonWriteFeature.ESCAPE_NON_ASCII)); - SerializationConfig newConfig = config.withFeatures(JsonGenerator.Feature.IGNORE_UNKNOWN); + SerializationConfig newConfig = config.withFeatures(StreamWriteFeature.IGNORE_UNKNOWN); assertNotSame(config, newConfig); + assertTrue(newConfig.isEnabled(StreamWriteFeature.IGNORE_UNKNOWN)); - assertNotSame(config, config.without(JsonWriteFeature.ESCAPE_NON_ASCII)); - assertNotSame(config, config.withoutFeatures(JsonGenerator.Feature.IGNORE_UNKNOWN)); + // no change to settings, same object: + assertSame(config, config.without(JsonWriteFeature.ESCAPE_NON_ASCII)); + assertSame(config, config.withoutFeatures(StreamWriteFeature.IGNORE_UNKNOWN)); } public void testFormatFeatures() throws Exception { - SerializationConfig config = MAPPER.getSerializationConfig(); - assertNotSame(config, config.with(BogusFormatFeature.FF_DISABLED_BY_DEFAULT)); - assertNotSame(config, config.withFeatures(BogusFormatFeature.FF_DISABLED_BY_DEFAULT, - BogusFormatFeature.FF_ENABLED_BY_DEFAULT)); - assertNotSame(config, config.without(BogusFormatFeature.FF_ENABLED_BY_DEFAULT)); - assertNotSame(config, config.withoutFeatures(BogusFormatFeature.FF_DISABLED_BY_DEFAULT, - BogusFormatFeature.FF_ENABLED_BY_DEFAULT)); + final JsonWriteFeature DUSABLED_BY_DEFAULT = JsonWriteFeature.ESCAPE_NON_ASCII; + final JsonWriteFeature ENABLED_BY_DEFAULT = JsonWriteFeature.QUOTE_FIELD_NAMES; + + SerializationConfig config = MAPPER.serializationConfig(); + // feature that is NOT enabled by default + SerializationConfig config2 = config.with(DUSABLED_BY_DEFAULT); + assertNotSame(config, config2); + // and then with one that IS enabled by default: + SerializationConfig config3 = config.withFeatures(DUSABLED_BY_DEFAULT, ENABLED_BY_DEFAULT); + assertNotSame(config, config3); + + assertNotSame(config3, config3.without(ENABLED_BY_DEFAULT)); + assertNotSame(config3, config3.withoutFeatures(DUSABLED_BY_DEFAULT, + ENABLED_BY_DEFAULT)); } } diff --git a/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualDeserialization.java index 19ab6df333..df682d8243 100644 --- a/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualDeserialization.java +++ b/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualDeserialization.java @@ -12,7 +12,6 @@ import com.fasterxml.jackson.core.Version; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.deser.ContextualDeserializer; import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer; import com.fasterxml.jackson.databind.module.SimpleModule; @@ -88,7 +87,6 @@ static class ContextualMapBean static class MyContextualDeserializer extends JsonDeserializer - implements ContextualDeserializer { protected final String _fieldName; @@ -118,7 +116,6 @@ public JsonDeserializer createContextual(DeserializationContext ctxt, */ static class AnnotatedContextualDeserializer extends JsonDeserializer - implements ContextualDeserializer { protected final String _fieldName; @@ -149,7 +146,6 @@ public JsonDeserializer createContextual(DeserializationContext ctxt, static class GenericStringDeserializer extends StdScalarDeserializer - implements ContextualDeserializer { final String _value; @@ -183,10 +179,11 @@ static class GenericBean { public void testSimple() throws Exception { - ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule("test", Version.unknownVersion()); module.addDeserializer(StringValue.class, new MyContextualDeserializer()); - mapper.registerModule(module); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(module) + .build(); ContextualBean bean = mapper.readValue("{\"a\":\"1\",\"b\":\"2\"}", ContextualBean.class); assertEquals("a=1", bean.a.value); assertEquals("b=2", bean.b.value); @@ -298,10 +295,10 @@ public void testContextualType() throws Exception { private ObjectMapper _mapperWithAnnotatedContextual() { - ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule("test", Version.unknownVersion()); module.addDeserializer(StringValue.class, new AnnotatedContextualDeserializer()); - mapper.registerModule(module); - return mapper; + return jsonMapperBuilder() + .addModule(module) + .build(); } } diff --git a/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualKeyTypes.java b/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualKeyTypes.java index 6705e2cc33..74bd87fbba 100644 --- a/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualKeyTypes.java +++ b/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualKeyTypes.java @@ -10,7 +10,6 @@ import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.deser.ContextualKeyDeserializer; import com.fasterxml.jackson.databind.module.SimpleModule; -import com.fasterxml.jackson.databind.ser.ContextualSerializer; import com.fasterxml.jackson.databind.type.TypeFactory; /** @@ -27,7 +26,6 @@ public class TestContextualKeyTypes extends BaseMapTest static class ContextualKeySerializer extends JsonSerializer - implements ContextualSerializer { protected final String _prefix; @@ -90,10 +88,11 @@ static class MapBean { public void testSimpleKeySer() throws Exception { - ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule("test", Version.unknownVersion()); module.addKeySerializer(String.class, new ContextualKeySerializer("prefix")); - mapper.registerModule(module); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(module) + .build(); Map input = new HashMap(); input.put("a", Integer.valueOf(3)); String json = mapper.writerFor(TypeFactory.defaultInstance().constructMapType(HashMap.class, String.class, Object.class)) @@ -109,10 +108,11 @@ public void testSimpleKeySer() throws Exception public void testSimpleKeyDeser() throws Exception { - ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule("test", Version.unknownVersion()); module.addKeyDeserializer(String.class, new ContextualDeser("???")); - mapper.registerModule(module); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(module) + .build(); MapBean result = mapper.readValue("{\"map\":{\"a\":3}}", MapBean.class); Map map = result.map; assertNotNull(map); diff --git a/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualSerialization.java b/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualSerialization.java index b1fd92b800..6f75809334 100644 --- a/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualSerialization.java +++ b/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualSerialization.java @@ -10,8 +10,6 @@ import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.module.SimpleModule; -import com.fasterxml.jackson.databind.ser.ContextualSerializer; -import com.fasterxml.jackson.databind.ser.ResolvableSerializer; /** * Test cases to verify that it is possible to define serializers @@ -124,7 +122,6 @@ static class BeanWithClassConfig */ static class AnnotatedContextualSerializer extends JsonSerializer - implements ContextualSerializer { protected final String _prefix; @@ -160,7 +157,6 @@ public JsonSerializer createContextual(SerializerProvider prov, BeanProperty static class ContextualAndResolvable extends JsonSerializer - implements ContextualSerializer, ResolvableSerializer { protected int isContextual; protected int isResolved; @@ -202,10 +198,11 @@ public void resolve(SerializerProvider provider) { // (method, field) annotations. public void testMethodAnnotations() throws Exception { - ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule("test", Version.unknownVersion()); module.addSerializer(String.class, new AnnotatedContextualSerializer()); - mapper.registerModule(module); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(module) + .build(); assertEquals("{\"value\":\"see:foobar\"}", mapper.writeValueAsString(new ContextualBean("foobar"))); } @@ -213,29 +210,32 @@ public void testMethodAnnotations() throws Exception // for enclosing class. public void testClassAnnotations() throws Exception { - ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule("test", Version.unknownVersion()); module.addSerializer(String.class, new AnnotatedContextualSerializer()); - mapper.registerModule(module); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(module) + .build(); assertEquals("{\"value\":\"Voila->xyz\"}", mapper.writeValueAsString(new BeanWithClassConfig("xyz"))); } public void testWrappedBean() throws Exception { - ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule("test", Version.unknownVersion()); module.addSerializer(String.class, new AnnotatedContextualSerializer()); - mapper.registerModule(module); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(module) + .build(); assertEquals("{\"wrapped\":{\"value\":\"see:xyz\"}}", mapper.writeValueAsString(new ContextualBeanWrapper("xyz"))); } // Serializer should get passed property context even if contained in an array. public void testMethodAnnotationInArray() throws Exception { - ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule("test", Version.unknownVersion()); module.addSerializer(String.class, new AnnotatedContextualSerializer()); - mapper.registerModule(module); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(module) + .build(); ContextualArrayBean beans = new ContextualArrayBean("123"); assertEquals("{\"beans\":[\"array->123\"]}", mapper.writeValueAsString(beans)); } @@ -243,10 +243,11 @@ public void testMethodAnnotationInArray() throws Exception // Serializer should get passed property context even if contained in a Collection. public void testMethodAnnotationInList() throws Exception { - ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule("test", Version.unknownVersion()); module.addSerializer(String.class, new AnnotatedContextualSerializer()); - mapper.registerModule(module); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(module) + .build(); ContextualListBean beans = new ContextualListBean("abc"); assertEquals("{\"beans\":[\"list->abc\"]}", mapper.writeValueAsString(beans)); } @@ -254,10 +255,11 @@ public void testMethodAnnotationInList() throws Exception // Serializer should get passed property context even if contained in a Collection. public void testMethodAnnotationInMap() throws Exception { - ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule("test", Version.unknownVersion()); module.addSerializer(String.class, new AnnotatedContextualSerializer()); - mapper.registerModule(module); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(module) + .build(); ContextualMapBean map = new ContextualMapBean(); map.beans.put("first", "In Map"); assertEquals("{\"beans\":{\"first\":\"map->In Map\"}}", mapper.writeValueAsString(map)); diff --git a/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualWithAnnDeserializer.java b/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualWithAnnDeserializer.java index e019014b95..4682f5fd49 100644 --- a/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualWithAnnDeserializer.java +++ b/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualWithAnnDeserializer.java @@ -10,7 +10,6 @@ import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.deser.ContextualDeserializer; public class TestContextualWithAnnDeserializer extends BaseMapTest { @@ -36,7 +35,6 @@ static class AnnotatedContextualClassBean static class AnnotatedContextualDeserializer extends JsonDeserializer - implements ContextualDeserializer { protected final String _fieldName; diff --git a/src/test/java/com/fasterxml/jackson/databind/convert/ConvertingAbstractSerializer795Test.java b/src/test/java/com/fasterxml/jackson/databind/convert/ConvertingAbstractSerializer795Test.java index f25159949d..54ca255957 100644 --- a/src/test/java/com/fasterxml/jackson/databind/convert/ConvertingAbstractSerializer795Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/convert/ConvertingAbstractSerializer795Test.java @@ -32,10 +32,9 @@ public AbstractCustomType convert(String arg) { public static class AbstractCustomTypeUser { @JsonProperty @JsonDeserialize(converter = AbstractCustomTypeDeserializationConverter.class) - private final AbstractCustomType customField; + protected AbstractCustomType customField; - @JsonCreator - AbstractCustomTypeUser(@JsonProperty("customField") AbstractCustomType cf) { + public AbstractCustomTypeUser(@JsonProperty("customField") AbstractCustomType cf) { this.customField = cf; } } @@ -71,16 +70,16 @@ public static class NonAbstractCustomTypeUser { /********************************************************** */ - private static final ObjectMapper JSON_MAPPER = new ObjectMapper(); + private static final ObjectMapper JSON_MAPPER = newJsonMapper(); public void testAbstractTypeDeserialization() throws Exception { - String test="{\"customField\": \"customString\"}"; + String test = aposToQuotes("{'customField': 'customString'}"); AbstractCustomTypeUser cu = JSON_MAPPER.readValue(test, AbstractCustomTypeUser.class); assertNotNull(cu); } public void testNonAbstractDeserialization() throws Exception { - String test="{\"customField\": \"customString\"}"; + String test = aposToQuotes("{'customField': 'customString'}"); NonAbstractCustomTypeUser cu = JSON_MAPPER.readValue(test, NonAbstractCustomTypeUser.class); assertNotNull(cu); } diff --git a/src/test/java/com/fasterxml/jackson/databind/convert/TestArrayConversions.java b/src/test/java/com/fasterxml/jackson/databind/convert/TestArrayConversions.java index fbc4751bb3..1ec5a639be 100644 --- a/src/test/java/com/fasterxml/jackson/databind/convert/TestArrayConversions.java +++ b/src/test/java/com/fasterxml/jackson/databind/convert/TestArrayConversions.java @@ -11,11 +11,11 @@ public class TestArrayConversions extends com.fasterxml.jackson.databind.BaseMapTest { - final static String OVERFLOW_MSG_BYTE = "out of range of Java byte"; + final static String OVERFLOW_MSG_BYTE = "out of range of `byte`"; final static String OVERFLOW_MSG = "overflow"; - final static String OVERFLOW_MSG_INT = "out of range of int"; - final static String OVERFLOW_MSG_LONG = "out of range of long"; + final static String OVERFLOW_MSG_INT = "out of range of `int`"; + final static String OVERFLOW_MSG_LONG = "out of range of `long`"; final ObjectMapper MAPPER = new ObjectMapper(); @@ -112,8 +112,7 @@ public void testOverflows() verifyException(e, OVERFLOW_MSG_INT); } // Longs need help of BigInteger... - BigInteger biggie = BigInteger.valueOf(Long.MAX_VALUE); - biggie.add(BigInteger.ONE); + BigInteger biggie = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE); List l = new ArrayList(); l.add(biggie); try { diff --git a/src/test/java/com/fasterxml/jackson/databind/convert/TestBeanConversions.java b/src/test/java/com/fasterxml/jackson/databind/convert/TestBeanConversions.java index d0cb4eccb5..28b2a572d3 100644 --- a/src/test/java/com/fasterxml/jackson/databind/convert/TestBeanConversions.java +++ b/src/test/java/com/fasterxml/jackson/databind/convert/TestBeanConversions.java @@ -163,23 +163,26 @@ public void testIssue458() throws Exception // should work regardless of wrapping... public void testWrapping() throws Exception { - ObjectMapper wrappingMapper = new ObjectMapper(); - wrappingMapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE); - wrappingMapper.enable(SerializationFeature.WRAP_ROOT_VALUE); + ObjectMapper wrappingMapper = jsonMapperBuilder() + .enable(DeserializationFeature.UNWRAP_ROOT_VALUE) + .enable(SerializationFeature.WRAP_ROOT_VALUE) + .build(); // conversion is ok, even if it's bogus one _convertAndVerifyPoint(wrappingMapper); // also: ok to have mismatched settings, since as per [JACKSON-710], should // not actually use wrapping internally in these cases - wrappingMapper = new ObjectMapper(); - wrappingMapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE); - wrappingMapper.disable(SerializationFeature.WRAP_ROOT_VALUE); + wrappingMapper = jsonMapperBuilder() + .enable(DeserializationFeature.UNWRAP_ROOT_VALUE) + .disable(SerializationFeature.WRAP_ROOT_VALUE) + .build(); _convertAndVerifyPoint(wrappingMapper); - wrappingMapper = new ObjectMapper(); - wrappingMapper.disable(DeserializationFeature.UNWRAP_ROOT_VALUE); - wrappingMapper.enable(SerializationFeature.WRAP_ROOT_VALUE); + wrappingMapper = jsonMapperBuilder() + .disable(DeserializationFeature.UNWRAP_ROOT_VALUE) + .enable(SerializationFeature.WRAP_ROOT_VALUE) + .build(); _convertAndVerifyPoint(wrappingMapper); } @@ -213,7 +216,8 @@ private void _convertAndVerifyPoint(ObjectMapper m) } /** - * Need to test "shortcuts" introduced by [databind#11] + * Need to test "shortcuts" introduced by [databind#11] -- but + * removed with [databind#2220] */ public void testIssue11() throws Exception { @@ -248,10 +252,12 @@ public void testIssue11() throws Exception verifyException(e, "no properties discovered"); } - ObjectMapper mapper = new ObjectMapper(); - mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); + ObjectMapper mapper = jsonMapperBuilder() + .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS) + .build(); try { - assertEquals("{}", mapper.writeValueAsString(plaino)); + assertEquals("{}", mapper.writer() + .writeValueAsString(plaino)); } catch (Exception e) { throw (Exception) e.getCause(); } diff --git a/src/test/java/com/fasterxml/jackson/databind/convert/TestStringConversions.java b/src/test/java/com/fasterxml/jackson/databind/convert/TestStringConversions.java index 93a9d67b10..601916c89f 100644 --- a/src/test/java/com/fasterxml/jackson/databind/convert/TestStringConversions.java +++ b/src/test/java/com/fasterxml/jackson/databind/convert/TestStringConversions.java @@ -2,8 +2,6 @@ import java.util.*; -import static org.junit.Assert.*; - import com.fasterxml.jackson.core.Base64Variants; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; diff --git a/src/test/java/com/fasterxml/jackson/databind/convert/TestUpdateViaObjectReader.java b/src/test/java/com/fasterxml/jackson/databind/convert/TestUpdateViaObjectReader.java index 1a09c09fe0..32ca250318 100644 --- a/src/test/java/com/fasterxml/jackson/databind/convert/TestUpdateViaObjectReader.java +++ b/src/test/java/com/fasterxml/jackson/databind/convert/TestUpdateViaObjectReader.java @@ -11,8 +11,6 @@ import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import com.fasterxml.jackson.databind.module.SimpleModule; -import static org.junit.Assert.assertArrayEquals; - /** * Unit tests for verifying that "updating reader" works as * expected. @@ -65,10 +63,10 @@ static class DataADeserializer extends StdDeserializer { @Override public DataA deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - if (p.getCurrentToken() != JsonToken.START_OBJECT) { + if (p.currentToken() != JsonToken.START_OBJECT) { ctxt.reportInputMismatch(DataA.class, "Wrong current token, expected START_OBJECT, got: " - +p.getCurrentToken()); + +p.currentToken()); // never gets here } /*JsonNode node =*/ p.readValueAsTree(); @@ -220,10 +218,11 @@ public void testUpdatingWithViews() throws Exception // [databind#744] public void testIssue744() throws IOException { - ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); module.addDeserializer(DataA.class, new DataADeserializer()); - mapper.registerModule(module); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(module) + .build(); DataB db = new DataB(); db.da.i = 11; diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/AnySetterTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/AnySetterTest.java index 7e5dc85a9c..8d6abdcf00 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/AnySetterTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/AnySetterTest.java @@ -273,15 +273,17 @@ public void testBrokenWithDoubleAnnotations() throws Exception public void testIgnored() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + ObjectMapper mapper = jsonMapperBuilder() + .enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .build(); _testIgnorals(mapper); } public void testIgnoredPart2() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + ObjectMapper mapper = jsonMapperBuilder() + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .build(); _testIgnorals(mapper); } diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/NullHandlingTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/NullHandlingTest.java index 836e786aa0..36abbedd7c 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/NullHandlingTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/NullHandlingTest.java @@ -94,10 +94,11 @@ public void testNull() throws Exception } public void testAnySetterNulls() throws Exception { - ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule("test", Version.unknownVersion()); module.addDeserializer(String.class, new FunnyNullDeserializer()); - mapper.registerModule(module); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(module) + .build(); String fieldName = "fieldName"; String nullValue = "{\""+fieldName+"\":null}"; @@ -120,10 +121,11 @@ public void testAnySetterNulls() throws Exception { public void testCustomRootNulls() throws Exception { - ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule("test", Version.unknownVersion()); module.addDeserializer(String.class, new FunnyNullDeserializer()); - mapper.registerModule(module); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(module) + .build(); // should get non-default null directly: String str = mapper.readValue("null", String.class); @@ -140,10 +142,11 @@ public void testCustomRootNulls() throws Exception // [databind#407] public void testListOfNulls() throws Exception { - ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule("test", Version.unknownVersion()); module.addDeserializer(String.class, new FunnyNullDeserializer()); - mapper.registerModule(module); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(module) + .build(); List list = Arrays.asList("funny"); JavaType type = mapper.getTypeFactory().constructCollectionType(List.class, String.class); @@ -165,10 +168,11 @@ public void testListOfNulls() throws Exception // Test for [#407] public void testMapOfNulls() throws Exception { - ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule("test", Version.unknownVersion()); module.addDeserializer(String.class, new FunnyNullDeserializer()); - mapper.registerModule(module); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(module) + .build(); JavaType type = mapper.getTypeFactory().constructMapType(Map.class, String.class, String.class); // should get non-default null directly: diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/PropertyAliasTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/PropertyAliasTest.java index 3cc17ffbb0..bcec93b625 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/PropertyAliasTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/PropertyAliasTest.java @@ -42,7 +42,7 @@ protected PolyWrapperForAlias() { } public PolyWrapperForAlias(Object v) { value = v; } } - private final ObjectMapper MAPPER = newObjectMapper(); + private final ObjectMapper MAPPER = newJsonMapper(); // [databind#1029] public void testSimpleAliases() throws Exception diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/ReadOnlyDeser1805Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/ReadOnlyDeser1805Test.java index a8f68d0604..7741337eb1 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/ReadOnlyDeser1805Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/ReadOnlyDeser1805Test.java @@ -48,7 +48,7 @@ public List getRoles() { /********************************************************** */ - private final ObjectMapper MAPPER = newObjectMapper(); + private final ObjectMapper MAPPER = newJsonMapper(); public void testReadOnly1382() throws Exception { diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestArrayDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestArrayDeserialization.java index 6b506d8b6a..a59365a031 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/TestArrayDeserialization.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestArrayDeserialization.java @@ -3,8 +3,6 @@ import java.io.*; import java.util.*; -import static org.junit.Assert.*; - import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.*; @@ -69,10 +67,10 @@ public Bean2(String d) } @Override - public void serialize(JsonGenerator jgen, SerializerProvider provider) + public void serialize(JsonGenerator gen, SerializerProvider provider) throws IOException, JsonGenerationException { - jgen.writeString(_desc); + gen.writeString(_desc); } @Override public String toString() { return _desc; } @@ -84,7 +82,7 @@ public void serialize(JsonGenerator jgen, SerializerProvider provider) } @Override - public void serializeWithType(JsonGenerator jgen, + public void serializeWithType(JsonGenerator gen, SerializerProvider provider, TypeSerializer typeSer) throws IOException, JsonProcessingException { } @@ -185,22 +183,24 @@ public void testIntegerArray() throws Exception } } - // [JACKSON-620]: allow "" to mean 'null' for Arrays, List and Maps + // allow "" to mean 'null' for Arrays, List and Maps public void testFromEmptyString() throws Exception { - ObjectMapper m = new ObjectMapper(); - m.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true); + ObjectMapper m = jsonMapperBuilder() + .configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true) + .build(); assertNull(m.readValue(quote(""), Object[].class)); assertNull( m.readValue(quote(""), String[].class)); assertNull( m.readValue(quote(""), int[].class)); } - // [JACKSON-620]: allow "" to mean 'null' for Arrays, List and Maps + // allow "" to mean 'null' for Arrays, List and Maps public void testFromEmptyString2() throws Exception { - ObjectMapper m = new ObjectMapper(); - m.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true); - m.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); + ObjectMapper m = jsonMapperBuilder() + .enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, + DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY) + .build(); Product p = m.readValue("{\"thelist\":\"\"}", Product.class); assertNotNull(p); assertNull(p.thelist); @@ -250,13 +250,13 @@ public void testStringArray() throws Exception "a", "b", "abcd", "", "???", "\"quoted\"", "lf: \n", }; StringWriter sw = new StringWriter(); - JsonGenerator jg = MAPPER.getFactory().createGenerator(sw); - jg.writeStartArray(); + JsonGenerator g = MAPPER.createGenerator(sw); + g.writeStartArray(); for (String str : STRS) { - jg.writeString(str); + g.writeString(str); } - jg.writeEndArray(); - jg.close(); + g.writeEndArray(); + g.close(); String[] result = MAPPER.readValue(sw.toString(), String[].class); assertNotNull(result); @@ -331,7 +331,6 @@ public void testByteArrayAsBase64() throws Exception * get proper base64 encoding. Plus, not always using that * silly sample from Wikipedia. */ - JsonFactory jf = new JsonFactory(); StringWriter sw = new StringWriter(); int LEN = 9000; @@ -340,9 +339,9 @@ public void testByteArrayAsBase64() throws Exception TEST[i] = (byte) i; } - JsonGenerator jg = jf.createGenerator(sw); - jg.writeBinary(TEST); - jg.close(); + JsonGenerator g = MAPPER.createGenerator(sw); + g.writeBinary(TEST); + g.close(); String inputData = sw.toString(); byte[] result = MAPPER.readValue(inputData, byte[].class); @@ -356,13 +355,12 @@ public void testByteArrayAsBase64() throws Exception */ public void testByteArraysAsBase64() throws Exception { - JsonFactory jf = new JsonFactory(); StringWriter sw = new StringWriter(1000); final int entryCount = 15; - JsonGenerator jg = jf.createGenerator(sw); - jg.writeStartArray(); + JsonGenerator g = MAPPER.createGenerator(sw); + g.writeStartArray(); byte[][] entries = new byte[entryCount][]; for (int i = 0; i < entryCount; ++i) { @@ -371,10 +369,10 @@ public void testByteArraysAsBase64() throws Exception b[x] = (byte) (i + x); } entries[i] = b; - jg.writeBinary(b); + g.writeBinary(b); } - jg.writeEndArray(); - jg.close(); + g.writeEndArray(); + g.close(); String inputData = sw.toString(); @@ -575,10 +573,11 @@ public void testByteArrayTypeOverride890() throws Exception public void testCustomDeserializers() throws Exception { - ObjectMapper mapper = new ObjectMapper(); SimpleModule testModule = new SimpleModule("test", Version.unknownVersion()); testModule.addDeserializer(NonDeserializable[].class, new CustomNonDeserArrayDeserializer()); - mapper.registerModule(testModule); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(testModule) + .build(); NonDeserializable[] result = mapper.readValue("[\"a\"]", NonDeserializable[].class); assertNotNull(result); diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestBasicAnnotations.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestBasicAnnotations.java index d349ea615a..e66bf537f5 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/TestBasicAnnotations.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestBasicAnnotations.java @@ -190,13 +190,13 @@ public void testIssue442PrivateUnwrapped() throws Exception public void testAnnotationsDisabled() throws Exception { // first: verify that annotation introspection is enabled by default - assertTrue(MAPPER.getDeserializationConfig().isEnabled(MapperFeature.USE_ANNOTATIONS)); + assertTrue(MAPPER.deserializationConfig().isEnabled(MapperFeature.USE_ANNOTATIONS)); // with annotations, property is renamed AnnoBean bean = MAPPER.readValue("{ \"y\" : 0 }", AnnoBean.class); assertEquals(0, bean.value); - ObjectMapper m = objectMapperBuilder() - .configure(MapperFeature.USE_ANNOTATIONS, false) + ObjectMapper m = jsonMapperBuilder() + .disable(MapperFeature.USE_ANNOTATIONS) .build(); // without annotations, should default to default bean-based name... bean = m.readValue("{ \"x\" : 0 }", AnnoBean.class); @@ -208,8 +208,8 @@ public void testEnumsWhenDisabled() throws Exception ObjectMapper m = new ObjectMapper(); assertEquals(Alpha.B, m.readValue(quote("B"), Alpha.class)); - m = objectMapperBuilder() - .configure(MapperFeature.USE_ANNOTATIONS, false) + m = jsonMapperBuilder() + .disable(MapperFeature.USE_ANNOTATIONS) .build(); // should still use the basic name handling here assertEquals(Alpha.B, m.readValue(quote("B"), Alpha.class)); @@ -217,7 +217,7 @@ public void testEnumsWhenDisabled() throws Exception public void testNoAccessOverrides() throws Exception { - ObjectMapper m = objectMapperBuilder() + ObjectMapper m = jsonMapperBuilder() .disable(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS) .build(); SimpleBean bean = m.readValue("{\"x\":1,\"y\":2}", SimpleBean.class); 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 988ece38d2..e7cf351ba2 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/TestBeanDeserializer.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestBeanDeserializer.java @@ -47,7 +47,7 @@ public void setupModule(SetupContext context) { super.setupModule(context); if (modifier != null) { - context.addBeanDeserializerModifier(modifier); + context.addDeserializerModifier(modifier); } } } @@ -102,7 +102,6 @@ static class Issue476Type { public String name, value; } static class Issue476Deserializer extends BeanDeserializer - implements ContextualDeserializer { protected static int propCount; @@ -113,6 +112,7 @@ public Issue476Deserializer(BeanDeserializer src) { @Override public JsonDeserializer createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException { + super.createContextual(ctxt, property); propCount++; return this; } @@ -135,7 +135,7 @@ public Issue476Module() { @Override public void setupModule(SetupContext context) { - context.addBeanDeserializerModifier(new Issue476DeserializerModifier()); + context.addDeserializerModifier(new Issue476DeserializerModifier()); } } @@ -220,7 +220,7 @@ public Issue1912Module() { @Override public void setupModule(SetupContext context) { - context.addBeanDeserializerModifier(new Issue1912UseAddOrReplacePropertyDeserializerModifier()); + context.addDeserializerModifier(new Issue1912UseAddOrReplacePropertyDeserializerModifier()); } } @@ -340,8 +340,9 @@ public void testAbstractFailure() throws Exception } public void testPropertyRemoval() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new ModuleImpl(new RemovingModifier("a"))); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new ModuleImpl(new RemovingModifier("a"))) + .build(); Bean bean = mapper.readValue("{\"b\":\"2\"}", Bean.class); assertEquals("2", bean.b); // and 'a' has its default value: @@ -350,8 +351,9 @@ public void testPropertyRemoval() throws Exception public void testDeserializerReplacement() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new ModuleImpl(new ReplacingModifier(new BogusBeanDeserializer("foo", "bar")))); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new ModuleImpl(new ReplacingModifier(new BogusBeanDeserializer("foo", "bar")))) + .build(); Bean bean = mapper.readValue("{\"a\":\"xyz\"}", Bean.class); // custom deserializer always produces instance like this: assertEquals("foo", bean.a); @@ -362,8 +364,9 @@ public void testIssue476() throws Exception { final String JSON = "{\"value1\" : {\"name\" : \"fruit\", \"value\" : \"apple\"}, \"value2\" : {\"name\" : \"color\", \"value\" : \"red\"}}"; - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new Issue476Module()); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new Issue476Module()) + .build(); mapper.readValue(JSON, Issue476Bean.class); // there are 2 properties @@ -391,9 +394,10 @@ public void testPOJOFromEmptyString() throws Exception // [databind#120] public void testModifyArrayDeserializer() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new SimpleModule("test") - .setDeserializerModifier(new ArrayDeserializerModifier())); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new SimpleModule("test") + .setDeserializerModifier(new ArrayDeserializerModifier())) + .build(); Object[] result = mapper.readValue("[1,2]", Object[].class); assertEquals(1, result.length); assertEquals("foo", result[0]); @@ -401,10 +405,10 @@ public void testModifyArrayDeserializer() throws Exception public void testModifyCollectionDeserializer() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new SimpleModule("test") - .setDeserializerModifier(new CollectionDeserializerModifier()) - ); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new SimpleModule("test") + .setDeserializerModifier(new CollectionDeserializerModifier())) + .build(); List result = mapper.readValue("[1,2]", List.class); assertEquals(1, result.size()); assertEquals("foo", result.get(0)); @@ -412,10 +416,10 @@ public void testModifyCollectionDeserializer() throws Exception public void testModifyMapDeserializer() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new SimpleModule("test") - .setDeserializerModifier(new MapDeserializerModifier()) - ); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new SimpleModule("test") + .setDeserializerModifier(new MapDeserializerModifier())) + .build(); Map result = mapper.readValue("{\"a\":1,\"b\":2}", Map.class); assertEquals(1, result.size()); assertEquals("foo", result.get("a")); @@ -423,20 +427,20 @@ public void testModifyMapDeserializer() throws Exception public void testModifyEnumDeserializer() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new SimpleModule("test") - .setDeserializerModifier(new EnumDeserializerModifier()) - ); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new SimpleModule("test") + .setDeserializerModifier(new EnumDeserializerModifier())) + .build(); Object result = mapper.readValue(quote("B"), EnumABC.class); assertEquals("foo", result); } public void testModifyKeyDeserializer() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new SimpleModule("test") - .setDeserializerModifier(new KeyDeserializerModifier()) - ); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new SimpleModule("test") + .setDeserializerModifier(new KeyDeserializerModifier())) + .build(); Map result = mapper.readValue("{\"a\":1}", Map.class); assertEquals(1, result.size()); assertEquals("foo", result.entrySet().iterator().next().getKey()); @@ -448,9 +452,9 @@ public void testModifyKeyDeserializer() throws Exception */ public void testModifyStdScalarDeserializer() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new SimpleModule("test") - .setDeserializerModifier(new BeanDeserializerModifier() { + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new SimpleModule("test") + .setDeserializerModifier(new BeanDeserializerModifier() { @Override public JsonDeserializer modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer deser) { @@ -459,14 +463,16 @@ public JsonDeserializer modifyDeserializer(DeserializationConfig config, } return deser; } - })); + })) + .build(); Object result = mapper.readValue(quote("abcDEF"), String.class); assertEquals("ABCDEF", result); } public void testAddOrReplacePropertyIsUsedOnDeserialization() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new Issue1912Module()); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new Issue1912Module()) + .build(); Issue1912Bean result = mapper.readValue("{\"subBean\": {\"a\":\"foo\"}}", Issue1912Bean.class); assertEquals("foo_custom", result.subBean.a); diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestConcurrency.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestConcurrency.java index aea0e49055..edc5f713a2 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/TestConcurrency.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestConcurrency.java @@ -3,12 +3,15 @@ import java.io.IOException; import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.*; + +import com.fasterxml.jackson.databind.BaseMapTest; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; /** - * Testing for [JACKSON-237] (NPE due to race condition) + * Testing for a NPE due to race condition */ public class TestConcurrency extends BaseMapTest { @@ -18,7 +21,7 @@ public class TestConcurrency extends BaseMapTest /********************************************** */ - @JsonDeserialize(using=BeanDeserializer.class) + @JsonDeserialize(using=TestBeanDeserializer.class) static class Bean { public int value = 42; @@ -34,14 +37,13 @@ static class Bean * Dummy deserializer used for verifying that partially handled (i.e. not yet * resolved) deserializers are not allowed to be used. */ - static class BeanDeserializer + static class TestBeanDeserializer extends JsonDeserializer - implements ResolvableDeserializer { protected volatile boolean resolved = false; @Override - public Bean deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException + public Bean deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { if (!resolved) { throw new IOException("Deserializer not yet completely resolved"); @@ -52,7 +54,7 @@ public Bean deserialize(JsonParser jp, DeserializationContext ctxt) throws IOExc } @Override - public void resolve(DeserializationContext ctxt) throws JsonMappingException + public void resolve(DeserializationContext ctxt) { try { Thread.sleep(100L); @@ -69,9 +71,8 @@ public void resolve(DeserializationContext ctxt) throws JsonMappingException public void testDeserializerResolution() throws Exception { - /* Let's repeat couple of times, just to be sure; thread timing is not - * exact science; plus caching plays a role too - */ + // Let's repeat couple of times, just to be sure; thread timing is not + // exact science; plus caching plays a role too final String JSON = "{\"value\":42}"; for (int i = 0; i < 5; ++i) { diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestCustomDeserializers.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestCustomDeserializers.java index f85d995386..c8ef9bed5d 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/TestCustomDeserializers.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestCustomDeserializers.java @@ -70,14 +70,14 @@ static class CustomBeanDeserializer extends JsonDeserializer public CustomBean deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { int a = 0, b = 0; - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == JsonToken.START_OBJECT) { t = p.nextToken(); } else if (t != JsonToken.FIELD_NAME) { throw new Error(); } while(t == JsonToken.FIELD_NAME) { - final String fieldName = p.getCurrentName(); + final String fieldName = p.currentName(); t = p.nextToken(); if (t != JsonToken.VALUE_NUMBER_INT) { throw new JsonParseException(p, "expecting number got "+ t); @@ -167,7 +167,6 @@ static class Bean375Inner { } static class Bean375OuterDeserializer extends StdDeserializer - implements ContextualDeserializer { protected BeanProperty prop; @@ -191,7 +190,6 @@ public JsonDeserializer createContextual(DeserializationContext ctxt, BeanPro } static class Bean375InnerDeserializer extends StdDeserializer - implements ContextualDeserializer { protected boolean negative; @@ -269,7 +267,7 @@ public DelegatingModuleImpl() { public void setupModule(SetupContext context) { super.setupModule(context); - context.addBeanDeserializerModifier(new BeanDeserializerModifier() { + context.addDeserializerModifier(new BeanDeserializerModifier() { @Override public JsonDeserializer modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer deserializer) { @@ -358,10 +356,9 @@ public void testCustomBeanDeserializer() throws Exception // [Issue#87]: delegating deserializer public void testDelegating() throws Exception { - ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule("test", Version.unknownVersion()); module.addDeserializer(Immutable.class, - new StdDelegatingDeserializer( + new StdConvertingDeserializer( new StdConverter() { @Override public Immutable convert(JsonNode value) @@ -372,8 +369,9 @@ public Immutable convert(JsonNode value) } } )); - - mapper.registerModule(module); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(module) + .build(); Immutable imm = mapper.readValue("{\"x\":3,\"y\":7}", Immutable.class); assertEquals(3, imm.x); assertEquals(7, imm.y); @@ -382,7 +380,6 @@ public Immutable convert(JsonNode value) // [databind#623] public void testJsonNodeDelegating() throws Exception { - ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule("test", Version.unknownVersion()); module.addDeserializer(Immutable.class, new StdNodeBasedDeserializer(Immutable.class) { @@ -393,7 +390,9 @@ public Immutable convert(JsonNode root, DeserializationContext ctxt) throws IOEx return new Immutable(x, y); } }); - mapper.registerModule(module); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(module) + .build(); Immutable imm = mapper.readValue("{\"x\":-10,\"y\":3}", Immutable.class); assertEquals(-10, imm.x); assertEquals(3, imm.y); @@ -412,11 +411,12 @@ public void testIssue882() throws Exception // [#337]: convenience methods for custom deserializers to use public void testContextReadValue() throws Exception { - ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule("test", Version.unknownVersion()); module.addDeserializer(Bean375Outer.class, new Bean375OuterDeserializer()); module.addDeserializer(Bean375Inner.class, new Bean375InnerDeserializer()); - mapper.registerModule(module); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(module) + .build(); // First, without property; doubles up value: Bean375Outer outer = mapper.readValue("13", Bean375Outer.class); @@ -440,9 +440,10 @@ public void testCurrentValueAccess() throws Exception public void testCustomStringDeser() throws Exception { - ObjectMapper mapper = new ObjectMapper().registerModule( - new SimpleModule().addDeserializer(String.class, new UCStringDeserializer()) - ); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new SimpleModule() + .addDeserializer(String.class, new UCStringDeserializer())) + .build(); assertEquals("FOO", mapper.readValue(quote("foo"), String.class)); StringWrapper sw = mapper.readValue("{\"str\":\"foo\"}", StringWrapper.class); assertNotNull(sw); @@ -451,8 +452,9 @@ public void testCustomStringDeser() throws Exception public void testDelegatingDeserializer() throws Exception { - ObjectMapper mapper = new ObjectMapper().registerModule( - new DelegatingModuleImpl()); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new DelegatingModuleImpl()) + .build(); String str = mapper.readValue(quote("foo"), String.class); assertEquals("MY:foo", str); } diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestCustomFactory.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestCustomFactory.java index bda0e14346..9cc8d8a322 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/TestCustomFactory.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestCustomFactory.java @@ -33,11 +33,11 @@ public DummyDeserializer(T v, Class cls) { } @Override - public T deserialize(JsonParser jp, DeserializationContext ctxt) + public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { // need to skip, if structured... - jp.skipChildren(); + p.skipChildren(); return value; } } @@ -61,35 +61,34 @@ public CustomBean(int a, int b) { static class CustomBeanDeserializer extends JsonDeserializer { @Override - public CustomBean deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException + public CustomBean deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { int a = 0, b = 0; - JsonToken t = jp.getCurrentToken(); + JsonToken t = p.currentToken(); if (t == JsonToken.START_OBJECT) { - t = jp.nextToken(); + t = p.nextToken(); } else if (t != JsonToken.FIELD_NAME) { throw new Error(); } while(t == JsonToken.FIELD_NAME) { - final String fieldName = jp.getCurrentName(); - t = jp.nextToken(); + final String fieldName = p.currentName(); + t = p.nextToken(); if (t != JsonToken.VALUE_NUMBER_INT) { - throw new JsonParseException(jp, "expecting number got "+ t); + throw new JsonParseException(p, "expecting number got "+ t); } if (fieldName.equals("a")) { - a = jp.getIntValue(); + a = p.getIntValue(); } else if (fieldName.equals("b")) { - b = jp.getIntValue(); + b = p.getIntValue(); } else { throw new Error(); } - t = jp.nextToken(); + t = p.nextToken(); } return new CustomBean(a, b); } } - /* /********************************************************** /* Unit tests diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestGenerics.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestGenerics.java index 967a6e7efb..286dfcc7bb 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/TestGenerics.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestGenerics.java @@ -79,9 +79,9 @@ public void testGenericWrapper() throws Exception public void testGenericWrapperWithSingleElementArray() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); - + ObjectMapper mapper = jsonMapperBuilder() + .enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .build(); Wrapper result = mapper.readValue ("[{\"value\": [{ \"x\" : 13 }] }]", new TypeReference>() { }); @@ -119,11 +119,12 @@ public void testMultipleWrappers() throws Exception assertEquals(new Wrapper(7L), result3); } - //[Issue#381] + //[databind#381] public void testMultipleWrappersSingleValueArray() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); + ObjectMapper mapper = jsonMapperBuilder() + .enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .build(); // First, numeric wrapper Wrapper result = mapper.readValue @@ -160,12 +161,12 @@ public void testArrayOfGenericWrappers() throws Exception SimpleBean bean = (SimpleBean) contents; assertEquals(9, bean.x); } - - // [Issue#381] + public void testArrayOfGenericWrappersSingleValueArray() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); + ObjectMapper mapper = jsonMapperBuilder() + .enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .build(); Wrapper[] result = mapper.readValue ("[ {\"value\": [ { \"x\" : [ 9 ] } ] } ]", diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestJacksonTypes.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestJacksonTypes.java index 5ff6ca7933..27a44dd44c 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/TestJacksonTypes.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestJacksonTypes.java @@ -13,14 +13,15 @@ public class TestJacksonTypes extends com.fasterxml.jackson.databind.BaseMapTest { + private final ObjectMapper MAPPER = objectMapper(); + public void testJsonLocation() throws Exception { - ObjectMapper m = new ObjectMapper(); // note: source reference is untyped, only String guaranteed to work JsonLocation loc = new JsonLocation("whatever", -1, -1, 100, 13); // Let's use serializer here; goal is round-tripping - String ser = serializeAsString(m, loc); - JsonLocation result = m.readValue(ser, JsonLocation.class); + String ser = MAPPER.writeValueAsString(loc); + JsonLocation result = MAPPER.readValue(ser, JsonLocation.class); assertNotNull(result); assertEquals(loc.getSourceRef(), result.getSourceRef()); assertEquals(loc.getByteOffset(), result.getByteOffset()); @@ -47,9 +48,8 @@ public void testJsonLocationProps() */ public void testTokenBufferWithSample() throws Exception { - ObjectMapper m = new ObjectMapper(); // First, try standard sample doc: - TokenBuffer result = m.readValue(SAMPLE_DOC_JSON_SPEC, TokenBuffer.class); + TokenBuffer result = MAPPER.readValue(SAMPLE_DOC_JSON_SPEC, TokenBuffer.class); verifyJsonSpecSampleDoc(result.asParser(), true); result.close(); } @@ -57,13 +57,12 @@ public void testTokenBufferWithSample() throws Exception @SuppressWarnings("resource") public void testTokenBufferWithSequence() throws Exception { - ObjectMapper m = new ObjectMapper(); // and then sequence of other things - JsonParser jp = createParserUsingReader("[ 32, [ 1 ], \"abc\", { \"a\" : true } ]"); - assertToken(JsonToken.START_ARRAY, jp.nextToken()); + JsonParser p = createParserUsingReader("[ 32, [ 1 ], \"abc\", { \"a\" : true } ]"); + assertToken(JsonToken.START_ARRAY, p.nextToken()); - assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); - TokenBuffer buf = m.readValue(jp, TokenBuffer.class); + assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); + TokenBuffer buf = MAPPER.readValue(p, TokenBuffer.class); // check manually... JsonParser bufParser = buf.asParser(); @@ -72,7 +71,7 @@ public void testTokenBufferWithSequence() throws Exception assertNull(bufParser.nextToken()); // then bind to another - buf = m.readValue(jp, TokenBuffer.class); + buf = MAPPER.readValue(p, TokenBuffer.class); bufParser = buf.asParser(); assertToken(JsonToken.START_ARRAY, bufParser.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, bufParser.nextToken()); @@ -81,29 +80,28 @@ public void testTokenBufferWithSequence() throws Exception assertNull(bufParser.nextToken()); // third one, with automatic binding - buf = m.readValue(jp, TokenBuffer.class); - String str = m.readValue(buf.asParser(), String.class); + buf = MAPPER.readValue(p, TokenBuffer.class); + String str = MAPPER.readValue(buf.asParser(), String.class); assertEquals("abc", str); // and ditto for last one - buf = m.readValue(jp, TokenBuffer.class); - Map map = m.readValue(buf.asParser(), Map.class); + buf = MAPPER.readValue(p, TokenBuffer.class); + Map map = MAPPER.readValue(buf.asParser(), Map.class); assertEquals(1, map.size()); assertEquals(Boolean.TRUE, map.get("a")); - assertEquals(JsonToken.END_ARRAY, jp.nextToken()); - assertNull(jp.nextToken()); + assertEquals(JsonToken.END_ARRAY, p.nextToken()); + assertNull(p.nextToken()); } public void testJavaType() throws Exception { - ObjectMapper mapper = new ObjectMapper(); TypeFactory tf = TypeFactory.defaultInstance(); // first simple type: - String json = mapper.writeValueAsString(tf.constructType(String.class)); + String json = MAPPER.writeValueAsString(tf.constructType(String.class)); assertEquals(quote(java.lang.String.class.getName()), json); // and back - JavaType t = mapper.readValue(json, JavaType.class); + JavaType t = MAPPER.readValue(json, JavaType.class); assertNotNull(t); assertEquals(String.class, t.getRawClass()); } diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestSetterlessProperties.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestSetterlessProperties.java index 75053c464e..14ebdf68a1 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/TestSetterlessProperties.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestSetterlessProperties.java @@ -47,10 +47,12 @@ public List getList() { /********************************************************** */ - public void testSimpleSetterlessCollectionOk() - throws Exception + public void testSimpleSetterlessCollectionOk() throws Exception { - CollectionBean result = new ObjectMapper().readValue + CollectionBean result = jsonMapperBuilder() + .enable(MapperFeature.USE_GETTERS_AS_SETTERS) + .build() + .readValue ("{\"values\":[ \"abc\", \"def\" ]}", CollectionBean.class); List l = result._values; assertEquals(2, l.size()); @@ -62,15 +64,9 @@ public void testSimpleSetterlessCollectionOk() * Let's also verify that disabling the feature makes * deserialization fail for setterless bean */ - public void testSimpleSetterlessCollectionFailure() - throws Exception + public void testSimpleSetterlessCollectionFailure() throws Exception { ObjectMapper m = new ObjectMapper(); - // by default, it should be enabled - assertTrue(m.isEnabled(MapperFeature.USE_GETTERS_AS_SETTERS)); - m = objectMapperBuilder() - .configure(MapperFeature.USE_GETTERS_AS_SETTERS, false) - .build(); assertFalse(m.isEnabled(MapperFeature.USE_GETTERS_AS_SETTERS)); // and now this should fail @@ -79,17 +75,18 @@ public void testSimpleSetterlessCollectionFailure() ("{\"values\":[ \"abc\", \"def\" ]}", CollectionBean.class); fail("Expected an exception"); } catch (JsonMappingException e) { - /* Not a good exception, ideally could suggest a need for - * a setter...? - */ + // Not a good exception, ideally could suggest a need for + // a setter...? verifyException(e, "Unrecognized field"); } } - public void testSimpleSetterlessMapOk() - throws Exception + public void testSimpleSetterlessMapOk() throws Exception { - MapBean result = new ObjectMapper().readValue + MapBean result = jsonMapperBuilder() + .enable(MapperFeature.USE_GETTERS_AS_SETTERS) + .build() + .readValue ("{\"values\":{ \"a\": 15, \"b\" : -3 }}", MapBean.class); Map m = result._values; assertEquals(2, m.size()); @@ -97,11 +94,10 @@ public void testSimpleSetterlessMapOk() assertEquals(Integer.valueOf(-3), m.get("b")); } - public void testSimpleSetterlessMapFailure() - throws Exception + public void testSimpleSetterlessMapFailure() throws Exception { - ObjectMapper m = objectMapperBuilder() - .configure(MapperFeature.USE_GETTERS_AS_SETTERS, false) + ObjectMapper m = jsonMapperBuilder() + .disable(MapperFeature.USE_GETTERS_AS_SETTERS) .build(); // so this should fail now without a setter try { @@ -113,13 +109,13 @@ public void testSimpleSetterlessMapFailure() } } - /* Test precedence of "getter-as-setter" (for Lists) versus + /* Test for [JACKSON-328], precedence of "getter-as-setter" (for Lists) versus * field for same property. */ public void testSetterlessPrecedence() throws Exception { - ObjectMapper m = objectMapperBuilder() - .configure(MapperFeature.USE_GETTERS_AS_SETTERS, true) + ObjectMapper m = jsonMapperBuilder() + .enable(MapperFeature.USE_GETTERS_AS_SETTERS) .build(); Dual value = m.readValue("{\"list\":[1,2,3]}, valueType)", Dual.class); assertNotNull(value); diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestTimestampDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestTimestampDeserialization.java index 295cdad804..9279d72491 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/TestTimestampDeserialization.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestTimestampDeserialization.java @@ -28,8 +28,9 @@ public void testTimestampUtil() throws Exception public void testTimestampUtilSingleElementArray() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); + final ObjectMapper mapper = jsonMapperBuilder() + .enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .build(); long now = System.currentTimeMillis(); java.sql.Timestamp value = new java.sql.Timestamp(now); diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderAdvancedTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderAdvancedTest.java index e3f264493e..f26c2e5a74 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderAdvancedTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderAdvancedTest.java @@ -50,10 +50,10 @@ public InjectableXY build() { public void testWithInjectable() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.setInjectableValues(new InjectableValues.Std() - .addValue(String.class, "stuffValue") - ); + ObjectMapper mapper = jsonMapperBuilder() + .injectableValues(new InjectableValues.Std() + .addValue(String.class, "stuffValue")) + .build(); InjectableXY bean = mapper.readValue(aposToQuotes("{'y':3,'x':7}"), InjectableXY.class); assertEquals(8, bean._x); diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderInfiniteLoop1978Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderInfiniteLoop1979Test.java similarity index 92% rename from src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderInfiniteLoop1978Test.java rename to src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderInfiniteLoop1979Test.java index 0a6a0677e3..89bb9dd0b1 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderInfiniteLoop1978Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderInfiniteLoop1979Test.java @@ -7,7 +7,8 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -public class BuilderInfiniteLoop1978Test extends BaseMapTest +// first for [databind#1978] but follow up for [databind#1979] +public class BuilderInfiniteLoop1979Test extends BaseMapTest { static class Builder { @@ -58,8 +59,8 @@ static class SubBuilder @JsonProperty("el1") public SubBuilder withElement1(int e1) { - this.element1 = e1; - return this; + this.element1 = e1; + return this; } public SubBean build() diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderSimpleTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderSimpleTest.java index 7644dc4ecd..aeeb651b90 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderSimpleTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderSimpleTest.java @@ -304,9 +304,10 @@ public void testSimpleWithIgnores() throws Exception } // but with config overrides should pass - ObjectMapper ignorantMapper = new ObjectMapper(); - ignorantMapper.configOverride(SimpleBuilderXY.class) - .setIgnorals(JsonIgnoreProperties.Value.forIgnoreUnknown(true)); + ObjectMapper ignorantMapper = jsonMapperBuilder() + .withConfigOverride(SimpleBuilderXY.class, + override -> override.setIgnorals(JsonIgnoreProperties.Value.forIgnoreUnknown(true))) + .build(); o = ignorantMapper.readValue(json, ValueClassXY.class); assertNotNull(o); assertSame(ValueClassXY.class, o.getClass()); @@ -315,7 +316,7 @@ public void testSimpleWithIgnores() throws Exception assertEquals(value._x, 2); assertEquals(value._y, 3); } - + public void testMultiAccess() throws Exception { String json = aposToQuotes("{'c':3,'a':2,'b':-9}"); @@ -390,9 +391,10 @@ public void testWithAnySetter822() throws Exception public void testPOJOConfigResolution1557() throws Exception { - final String json = "{\"value\":1}"; - MAPPER.registerModule(new NopModule1557()); - ValueFoo value = MAPPER.readValue(json, ValueFoo.class); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new NopModule1557()) + .build(); + ValueFoo value = mapper.readValue("{\"value\":1}", ValueFoo.class); assertEquals(1, value.value); } } diff --git a/src/test/java/com/fasterxml/jackson/failing/BuilderDeserializationTest921.java b/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderWithTypeParametersTest.java similarity index 69% rename from src/test/java/com/fasterxml/jackson/failing/BuilderDeserializationTest921.java rename to src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderWithTypeParametersTest.java index 79beaee6f5..4a073da70c 100644 --- a/src/test/java/com/fasterxml/jackson/failing/BuilderDeserializationTest921.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderWithTypeParametersTest.java @@ -1,13 +1,16 @@ -package com.fasterxml.jackson.failing; +package com.fasterxml.jackson.databind.deser.builder; -import java.util.List; - -import com.fasterxml.jackson.annotation.*; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.BaseMapTest; +import com.fasterxml.jackson.databind.MapperFeature; +import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.util.LinkedHashMap; +import java.util.List; -public class BuilderDeserializationTest921 +public class BuilderWithTypeParametersTest extends BaseMapTest { public static class MyPOJO { @@ -77,7 +80,20 @@ public MyGenericPOJOWithCreator build() { } } - public void testWithBuilder() throws Exception { + public void testWithBuilderInferringBindings() throws Exception { + final ObjectMapper mapper = jsonMapperBuilder() + .enable(MapperFeature.INFER_BUILDER_TYPE_BINDINGS) + .build(); + final String json = aposToQuotes("{ 'data': [ { 'x': 'x', 'y': 'y' } ] }"); + final MyGenericPOJO deserialized = + mapper.readValue(json, new TypeReference>() {}); + assertEquals(1, deserialized.data.size()); + Object ob = deserialized.data.get(0); + assertNotNull(ob); + assertEquals(MyPOJO.class, ob.getClass()); + } + + public void testWithBuilderWithoutInferringBindings() throws Exception { final ObjectMapper mapper = new ObjectMapper(); final String json = aposToQuotes("{ 'data': [ { 'x': 'x', 'y': 'y' } ] }"); final MyGenericPOJO deserialized = @@ -85,7 +101,7 @@ public void testWithBuilder() throws Exception { assertEquals(1, deserialized.data.size()); Object ob = deserialized.data.get(0); assertNotNull(ob); - assertEquals(MyPOJO.class, ob.getClass()); + assertEquals(LinkedHashMap.class, ob.getClass()); } public void testWithCreator() throws Exception { diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderWithUnwrappedTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderWithUnwrappedTest.java index a787584a59..167c1bb187 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderWithUnwrappedTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderWithUnwrappedTest.java @@ -69,7 +69,7 @@ final static class Builder { private int age; private boolean alive; - Builder(@JsonProperty("person_id") long id) { + public Builder(@JsonProperty("person_id") long id) { this.id = id; } @@ -131,7 +131,7 @@ final static class Builder { private int age; private final boolean alive; - Builder( + public Builder( @JsonProperty("animal_id") long id, @JsonProperty("living") boolean alive ) { diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/creators/ArrayDelegatorCreatorForCollectionTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/ArrayDelegatorCreatorForCollectionTest.java index 62e4d631d2..ffb57fe2ff 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/creators/ArrayDelegatorCreatorForCollectionTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/ArrayDelegatorCreatorForCollectionTest.java @@ -19,13 +19,12 @@ public UnmodifiableSetMixin(Set s) {} public void testUnmodifiable() throws Exception { - ObjectMapper mapper = new ObjectMapper(); Class unmodSetType = Collections.unmodifiableSet(Collections.emptySet()).getClass(); - mapper.addMixIn(unmodSetType, UnmodifiableSetMixin.class); - mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); - + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY) + .addMixIn(unmodSetType, UnmodifiableSetMixin.class) + .build(); final String EXPECTED_JSON = "[\""+unmodSetType.getName()+"\",[]]"; - Set foo = mapper.readValue(EXPECTED_JSON, Set.class); assertTrue(foo.isEmpty()); } diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/creators/CreatorPropertiesTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/CreatorPropertiesTest.java index c690613fea..1f88381aac 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/creators/CreatorPropertiesTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/CreatorPropertiesTest.java @@ -96,7 +96,7 @@ public void testConstructorPropertiesInference() throws Exception assertEquals(6, result.y); // but change if configuration changed - ObjectMapper mapper = objectMapperBuilder() + ObjectMapper mapper = jsonMapperBuilder() .disable(MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES) .build(); // in which case fields are set directly: diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/creators/CreatorWithNamingStrategyTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/CreatorWithNamingStrategyTest.java index 83f5970915..348dfd06bc 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/creators/CreatorWithNamingStrategyTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/CreatorWithNamingStrategyTest.java @@ -37,17 +37,15 @@ public OneProperty(String bogus) { /********************************************************** */ - private final ObjectMapper MAPPER = newObjectMapper() - .setAnnotationIntrospector(new MyParamIntrospector()) - .setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE) - ; - // [databind#2051] public void testSnakeCaseWithOneArg() throws Exception { + ObjectMapper mapper = jsonMapperBuilder() + .propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE) + .annotationIntrospector(new MyParamIntrospector()) + .build(); final String MSG = "1st"; - OneProperty actual = MAPPER.readValue( - "{\"param_name0\":\""+MSG+"\"}", + OneProperty actual = mapper.readValue("{\"param_name0\":\""+MSG+"\"}", OneProperty.class); assertEquals("CTOR:"+MSG, actual.paramName0); } diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/creators/DelegatingCreatorAnnotations2016Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/DelegatingCreatorAnnotations2016Test.java index e892918335..e603bb1a88 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/creators/DelegatingCreatorAnnotations2016Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/DelegatingCreatorAnnotations2016Test.java @@ -37,7 +37,7 @@ public Wrapper2016ContentAs(@JsonDeserialize(contentAs = java.util.Date.class) L /********************************************************************** */ - private final ObjectMapper MAPPER = newObjectMapper(); + private final ObjectMapper MAPPER = newJsonMapper(); // [databind#2016] public void testDelegatingWithAs() throws Exception diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/creators/DelegatingCreatorAnnotations2021Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/DelegatingCreatorAnnotations2021Test.java index aa6eaa16d5..35ad729037 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/creators/DelegatingCreatorAnnotations2021Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/DelegatingCreatorAnnotations2021Test.java @@ -47,7 +47,7 @@ public Number deserialize(JsonParser p, DeserializationContext ctxt) /********************************************************************** */ - private final ObjectMapper MAPPER = newObjectMapper(); + private final ObjectMapper MAPPER = newJsonMapper(); // [databind#2021] public void testCustomDeserForDelegating() throws Exception diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/creators/DelegatingCreatorImplicitNames1001Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/DelegatingCreatorImplicitNames1001Test.java index c0043c996a..cde2afad6c 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/creators/DelegatingCreatorImplicitNames1001Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/DelegatingCreatorImplicitNames1001Test.java @@ -82,8 +82,9 @@ public void testWithoutNamedParameters() throws Exception // And then case that fails with [databind#1001] public void testWithNamedParameters() throws Exception { - ObjectMapper sut = new ObjectMapper() - .setAnnotationIntrospector(new CreatorNameIntrospector()); + ObjectMapper sut = jsonMapperBuilder() + .annotationIntrospector(new CreatorNameIntrospector()) + .build(); D d = D.make("abc:def"); diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/creators/EnumCreatorTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/EnumCreatorTest.java index 5024cea2a0..117602336a 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/creators/EnumCreatorTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/EnumCreatorTest.java @@ -263,8 +263,9 @@ public void testExceptionFromCreator() throws Exception // [databind#745] public void testDeserializerForCreatorWithEnumMaps() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new DelegatingDeserializersModule()); + final ObjectMapper mapper = jsonMapperBuilder() + .addModule(new DelegatingDeserializersModule()) + .build(); EnumMap value = mapper.readValue("{\"enumA\":\"value\"}", new TypeReference>() {}); assertEquals("value", value.get(EnumWithCreator.A)); diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/creators/ImplicitNameMatch792Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/ImplicitNameMatch792Test.java index 8eafb11b28..ac68d361cd 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/creators/ImplicitNameMatch792Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/ImplicitNameMatch792Test.java @@ -54,11 +54,14 @@ static class ReadWriteBean { private int value; - private ReadWriteBean(@JsonProperty(value="value", + // 22-Sep-2017, tatu: Note that must be either `public`; annotated with JsonCreator, + // or visibility min level for creator auto-detection needs to be raised + public ReadWriteBean(@JsonProperty(value="value", access=JsonProperty.Access.READ_WRITE) int v) { value = v; } + @JsonProperty("value") public int testValue() { return value; } // Let's also add setter to ensure conflict resolution works @@ -93,8 +96,9 @@ public String asString() { public void testBindingOfImplicitCreatorNames() throws Exception { - ObjectMapper m = new ObjectMapper(); - m.setAnnotationIntrospector(new ConstructorNameAI()); + ObjectMapper m = jsonMapperBuilder() + .annotationIntrospector(new ConstructorNameAI()) + .build(); String json = m.writeValueAsString(new Issue792Bean("a", "b")); assertEquals(aposToQuotes("{'first':'a','other':3}"), json); } diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/creators/ImplicitParamsForCreatorTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/ImplicitParamsForCreatorTest.java index 13da73a8dc..32e22023f5 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/creators/ImplicitParamsForCreatorTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/ImplicitParamsForCreatorTest.java @@ -39,8 +39,9 @@ public XY(int x, int y) { public void testNonSingleArgCreator() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.setAnnotationIntrospector(new MyParamIntrospector()); + ObjectMapper mapper = jsonMapperBuilder() + .annotationIntrospector(new MyParamIntrospector()) + .build(); XY value = mapper.readValue(aposToQuotes("{'paramName0':1,'paramName1':2}"), XY.class); assertNotNull(value); assertEquals(1, value.x); diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/creators/InnerClassCreatorTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/InnerClassCreatorTest.java index eeb733eb1e..c56523b0eb 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/creators/InnerClassCreatorTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/InnerClassCreatorTest.java @@ -51,10 +51,9 @@ static class Generic { } } - private final ObjectMapper MAPPER = new ObjectMapper(); - { - MAPPER.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); - } + private final ObjectMapper MAPPER = jsonMapperBuilder() + .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS) + .build(); // Used to trigger `ArrayIndexOutOfBoundsException` for missing creator property index public void testIssue1501() throws Exception @@ -73,6 +72,10 @@ public void testIssue1501() throws Exception public void testIssue1502() throws Exception { String ser = MAPPER.writeValueAsString(new Something1502(null)); + + // 21-Sep-2017, tatu: For some reason with 3.x this DOES pass (or maybe + // more accurately somehow with 2.x it doesn't?). + /* try { MAPPER.readValue(ser, Something1502.class); fail("Should not pass"); @@ -81,6 +84,10 @@ public void testIssue1502() throws Exception verifyException(e, "InnerSomething1502"); verifyException(e, "can only instantiate non-static inner class by using default"); } + */ + Something1502 result = MAPPER.readValue(ser, Something1502.class); + assertNotNull(result); + assertNull(result.a); } public void testIssue1503() throws Exception diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/creators/MultiArgConstructorTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/MultiArgConstructorTest.java index 72e39b78c6..b73dd6d98b 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/creators/MultiArgConstructorTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/MultiArgConstructorTest.java @@ -9,6 +9,7 @@ import com.fasterxml.jackson.databind.introspect.AnnotatedMember; import com.fasterxml.jackson.databind.introspect.AnnotatedParameter; import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector; +import com.fasterxml.jackson.databind.introspect.VisibilityChecker; public class MultiArgConstructorTest extends BaseMapTest { @@ -65,8 +66,9 @@ public String findImplicitPropertyName(AnnotatedMember param) { public void testMultiArgVisible() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - mapper.setAnnotationIntrospector(new MyParamIntrospector()); + final ObjectMapper mapper = jsonMapperBuilder() + .annotationIntrospector(new MyParamIntrospector()) + .build(); MultiArgCtorBean bean = mapper.readValue(aposToQuotes("{'b':13, 'c':2, 'a':-99}"), MultiArgCtorBean.class); assertNotNull(bean); @@ -78,8 +80,9 @@ public void testMultiArgVisible() throws Exception // But besides visibility, also allow overrides public void testMultiArgWithPartialOverride() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - mapper.setAnnotationIntrospector(new MyParamIntrospector()); + final ObjectMapper mapper = jsonMapperBuilder() + .annotationIntrospector(new MyParamIntrospector()) + .build(); MultiArgCtorBeanWithAnnotations bean = mapper.readValue(aposToQuotes("{'b2':7, 'c':222, 'a':-99}"), MultiArgCtorBeanWithAnnotations.class); assertNotNull(bean); @@ -87,16 +90,17 @@ public void testMultiArgWithPartialOverride() throws Exception assertEquals(-99, bean._a); assertEquals(222, bean.c); } - + // but let's also ensure that it is possible to prevent use of that constructor // with different visibility public void testMultiArgNotVisible() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - mapper.setAnnotationIntrospector(new MyParamIntrospector()); - mapper.setDefaultVisibility( - JsonAutoDetect.Value.noOverrides() - .withCreatorVisibility(Visibility.NONE)); + final ObjectMapper mapper = jsonMapperBuilder() + .annotationIntrospector(new MyParamIntrospector()) + .changeDefaultVisibility(vc -> VisibilityChecker.construct + (JsonAutoDetect.Value.noOverrides() + .withCreatorVisibility(Visibility.NONE))) + .build(); try { /*MultiArgCtorBean bean =*/ mapper.readValue(aposToQuotes("{'b':13, 'a':-99}"), MultiArgCtorBean.class); diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/creators/SingleArgCreatorTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/SingleArgCreatorTest.java index 90f98691d4..bcc7889d3e 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/creators/SingleArgCreatorTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/SingleArgCreatorTest.java @@ -9,12 +9,11 @@ public class SingleArgCreatorTest extends BaseMapTest { // [databind#430]: single arg BUT named; should not delegate - static class SingleNamedStringBean { final String _ss; @JsonCreator - public SingleNamedStringBean(@JsonProperty("") String ss){ + public SingleNamedStringBean(@JsonProperty("value") String ss){ this._ss = ss; } @@ -39,7 +38,7 @@ static class StringyBean { public final String value; - private StringyBean(String value) { this.value = value; } + protected StringyBean(String value) { this.value = value; } public String getValue() { return value; @@ -149,15 +148,16 @@ public static SingleArgWithImplicit from(XY v) { public void testNamedSingleArg() throws Exception { - SingleNamedStringBean bean = MAPPER.readValue(quote("foobar"), + SingleNamedStringBean bean = MAPPER.readValue(aposToQuotes("{'value':'foobar'}"), SingleNamedStringBean.class); assertEquals("foobar", bean._ss); } public void testSingleStringArgWithImplicitName() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - mapper.setAnnotationIntrospector(new MyParamIntrospector("value")); + final ObjectMapper mapper = jsonMapperBuilder() + .annotationIntrospector(new MyParamIntrospector("value")) + .build(); StringyBean bean = mapper.readValue(quote("foobar"), StringyBean.class); assertEquals("foobar", bean.getValue()); } @@ -165,8 +165,9 @@ public void testSingleStringArgWithImplicitName() throws Exception // [databind#714] public void testSingleImplicitlyNamedNotDelegating() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - mapper.setAnnotationIntrospector(new MyParamIntrospector("value")); + final ObjectMapper mapper = jsonMapperBuilder() + .annotationIntrospector(new MyParamIntrospector("value")) + .build(); StringyBeanWithProps bean = mapper.readValue("{\"value\":\"x\"}", StringyBeanWithProps.class); assertEquals("x", bean.getValue()); } @@ -198,8 +199,9 @@ public void testExplicitFactory660b() throws Exception // [databind#1383] public void testSingleImplicitDelegating() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - mapper.setAnnotationIntrospector(new MyParamIntrospector("value")); + final ObjectMapper mapper = jsonMapperBuilder() + .annotationIntrospector(new MyParamIntrospector("value")) + .build(); SingleArgWithImplicit bean = mapper.readValue(aposToQuotes("{'x':1,'y':2}"), SingleArgWithImplicit.class); XY v = bean.getFoobar(); diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreatorNullValue.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreatorNullValue.java index 9147b04ce4..8393965aa1 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreatorNullValue.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreatorNullValue.java @@ -95,8 +95,9 @@ public static JsonEntity create(@JsonProperty("type") String type, @JsonProperty */ public void testUsesDeserializersNullValue() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new TestModule()); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new TestModule()) + .build(); Container container = mapper.readValue("{}", Container.class); assertEquals(NULL_CONTAINED, container.contained); } diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreatorWithNamingStrategy556.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreatorWithNamingStrategy556.java index affafc84c6..2b14c273b6 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreatorWithNamingStrategy556.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreatorWithNamingStrategy556.java @@ -57,12 +57,10 @@ public String findImplicitPropertyName(AnnotatedMember param) { } } - private final ObjectMapper MAPPER = new ObjectMapper() - .setPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE) - ; - { - MAPPER.setAnnotationIntrospector(new MyParamIntrospector()); - } + private final ObjectMapper MAPPER = jsonMapperBuilder() + .propertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE) + .annotationIntrospector(new MyParamIntrospector()) + .build(); private final static String CTOR_JSON = aposToQuotes("{ 'MyAge' : 42, 'MyName' : 'NotMyRealName' }"); diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreators.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreators.java index 07854bc1cd..e9238c800e 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreators.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreators.java @@ -5,7 +5,6 @@ import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.exc.InvalidDefinitionException; /** * Unit tests for verifying that it is possible to annotate @@ -99,16 +98,6 @@ static FactoryBean createIt(@JsonProperty("mixed") double xyz) { } } - /** - * Simple demonstration of INVALID construtor annotation (only - * defining name for first arg) - */ - static class BrokenBean { - @JsonCreator protected BrokenBean(@JsonProperty("a") int a, - int b) { - } - } - /** * Bean that defines both creator and factory methor as * creators. Constructors have priority; but it is possible @@ -159,7 +148,6 @@ static class MultiBean { @JsonCreator public MultiBean(boolean v) { value = v; } } - // for [JACKSON-850] static class NoArgFactoryBean { public int x; public int y; @@ -170,7 +158,7 @@ static class NoArgFactoryBean { public static NoArgFactoryBean create() { return new NoArgFactoryBean(123); } } - // [Issue#208] + // [databind#208] static class FromStringBean { protected String value; @@ -436,8 +424,9 @@ public void testDeferredFactoryAndProps() throws Exception public void testFactoryCreatorWithMixin() throws Exception { - ObjectMapper m = new ObjectMapper(); - m.addMixIn(CreatorBean.class, MixIn.class); + ObjectMapper m = jsonMapperBuilder() + .addMixIn(CreatorBean.class, MixIn.class) + .build(); CreatorBean bean = m.readValue ("{ \"a\" : \"xyz\", \"x\" : 12 }", CreatorBean.class); assertEquals(11, bean.x); @@ -446,8 +435,9 @@ public void testFactoryCreatorWithMixin() throws Exception public void testFactoryCreatorWithRenamingMixin() throws Exception { - ObjectMapper m = new ObjectMapper(); - m.addMixIn(FactoryBean.class, FactoryBeanMixIn.class); + ObjectMapper m = jsonMapperBuilder() + .addMixIn(FactoryBean.class, FactoryBeanMixIn.class) + .build(); // override changes property name from "f" to "mixed" FactoryBean bean = m.readValue("{ \"mixed\" : 20.5 }", FactoryBean.class); assertEquals(20.5, bean.d); @@ -489,13 +479,4 @@ public void testMapWithFactory() throws Exception /* Test methods, invalid/broken cases /********************************************************** */ - - public void testBrokenConstructor() throws Exception - { - try { - /*BrokenBean bean =*/ MAPPER.readValue("{ \"x\" : 42 }", BrokenBean.class); - } catch (InvalidDefinitionException je) { - verifyException(je, "has no property name"); - } - } } diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreators3.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreators3.java index d07932bd21..4e66d24008 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreators3.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreators3.java @@ -131,23 +131,16 @@ public String getName(){ /********************************************************** */ - private final ObjectMapper MAPPER = newObjectMapper(); + private final ObjectMapper MAPPER = newJsonMapper(); public void testCreator541() throws Exception { - ObjectMapper mapper = objectMapperBuilder() - .disable( - MapperFeature.AUTO_DETECT_CREATORS, - MapperFeature.AUTO_DETECT_FIELDS, - MapperFeature.AUTO_DETECT_GETTERS, - MapperFeature.AUTO_DETECT_IS_GETTERS, - MapperFeature.AUTO_DETECT_SETTERS, - MapperFeature.USE_GETTERS_AS_SETTERS) + ObjectMapper mapper = jsonMapperBuilder() .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS) - .serializationInclusion(JsonInclude.Include.NON_NULL) - .build(); - + .disable(MapperFeature.USE_GETTERS_AS_SETTERS) + .changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_NULL)) + .build(); final String JSON = "{\n" + " \"foo\": {\n" + " \"0\": {\n" @@ -181,9 +174,9 @@ public void testCreator541() throws Exception // [databind#421] public void testMultiCtor421() throws Exception { - final ObjectMapper mapper = newObjectMapper(); - mapper.setAnnotationIntrospector(new MyParamIntrospector()); - + final ObjectMapper mapper = jsonMapperBuilder() + .annotationIntrospector(new MyParamIntrospector()) + .build(); MultiCtor bean = mapper.readValue(aposToQuotes("{'a':'123','b':'foo'}"), MultiCtor.class); assertNotNull(bean); assertEquals("123", bean._a); diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreatorsDelegating.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreatorsDelegating.java index 85b028f2f0..a1b8aebfa0 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreatorsDelegating.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreatorsDelegating.java @@ -30,8 +30,8 @@ static class CtorBean711 { protected String name; protected int age; - - @JsonCreator + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) public CtorBean711(@JacksonInject String n, int a) { name = n; @@ -45,14 +45,14 @@ static class FactoryBean711 protected String name1; protected String name2; protected int age; - + private FactoryBean711(int a, String n1, String n2) { age = a; name1 = n1; name2 = n2; } - - @JsonCreator + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) public static FactoryBean711 create(@JacksonInject String n1, int a, @JacksonInject String n2) { return new FactoryBean711(a, n1, n2); } @@ -65,7 +65,7 @@ static class Value592 protected Value592(Object ob, boolean bogus) { stuff = ob; } - + @JsonCreator public static Value592 from(TokenBuffer buffer) { return new Value592(buffer, false); @@ -88,7 +88,7 @@ public MapBean(Map map) { /********************************************************** */ - private final ObjectMapper MAPPER = newObjectMapper(); + private final ObjectMapper MAPPER = newJsonMapper(); public void testBooleanDelegate() throws Exception { @@ -104,10 +104,10 @@ public void testBooleanDelegate() throws Exception // As per [JACKSON-711]: should also work with delegate model (single non-annotated arg) public void testWithCtorAndDelegate() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.setInjectableValues(new InjectableValues.Std() - .addValue(String.class, "Pooka") - ); + ObjectMapper mapper = jsonMapperBuilder() + .injectableValues(new InjectableValues.Std() + .addValue(String.class, "Pooka")) + .build(); CtorBean711 bean = null; try { bean = mapper.readValue("38", CtorBean711.class); @@ -120,10 +120,10 @@ public void testWithCtorAndDelegate() throws Exception public void testWithFactoryAndDelegate() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.setInjectableValues(new InjectableValues.Std() - .addValue(String.class, "Fygar") - ); + ObjectMapper mapper = jsonMapperBuilder() + .injectableValues(new InjectableValues.Std() + .addValue(String.class, "Fygar")) + .build(); FactoryBean711 bean = null; try { bean = mapper.readValue("38", FactoryBean711.class); @@ -145,11 +145,11 @@ public void testDelegateWithTokenBuffer() throws Exception JsonParser jp = ((TokenBuffer) ob).asParser(); assertToken(JsonToken.START_OBJECT, jp.nextToken()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); - assertEquals("a", jp.getCurrentName()); + assertEquals("a", jp.currentName()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(1, jp.getIntValue()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); - assertEquals("b", jp.getCurrentName()); + assertEquals("b", jp.currentName()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(2, jp.getIntValue()); assertToken(JsonToken.END_OBJECT, jp.nextToken()); diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCustomValueInstDefaults.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCustomValueInstDefaults.java index 79edd026a7..a4386aad98 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCustomValueInstDefaults.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCustomValueInstDefaults.java @@ -377,9 +377,9 @@ public ValueInstantiator findValueInstantiator(DeserializationConfig config, // When all values are in the source, no defaults should be used. public void testAllPresent() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new BucketModule()); - + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new BucketModule()) + .build(); Bucket allPresent = mapper.readValue( "{\"a\":8,\"b\":9,\"c\":\"y\",\"d\":\"z\"}", Bucket.class); @@ -393,9 +393,9 @@ public void testAllPresent() throws Exception // When no values are in the source, all defaults should be used. public void testAllAbsent() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new BucketModule()); - + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new BucketModule()) + .build(); Bucket allAbsent = mapper.readValue( "{}", Bucket.class); @@ -410,9 +410,9 @@ public void testAllAbsent() throws Exception // be used for the missing values. public void testMixedPresentAndAbsent() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new BucketModule()); - + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new BucketModule()) + .build(); Bucket aAbsent = mapper.readValue( "{\"b\":9,\"c\":\"y\",\"d\":\"z\"}", Bucket.class); @@ -453,9 +453,9 @@ public void testMixedPresentAndAbsent() throws Exception // Ensure that 0 is not mistaken for a missing int value. public void testPresentZeroPrimitive() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new BucketModule()); - + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new BucketModule()) + .build(); Bucket aZeroRestAbsent = mapper.readValue( "{\"a\":0}", Bucket.class); @@ -469,9 +469,9 @@ public void testPresentZeroPrimitive() throws Exception // Ensure that null is not mistaken for a missing String value. public void testPresentNullReference() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new BucketModule()); - + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new BucketModule()) + .build(); Bucket cNullRestAbsent = mapper.readValue( "{\"c\":null}", Bucket.class); @@ -487,9 +487,9 @@ public void testPresentNullReference() throws Exception // has seen. Ensure that nothing breaks in that case. public void testMoreThan32CreatorParams() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new BucketModule()); - + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new BucketModule()) + .build(); BigBucket big = mapper.readValue( "{\"i03\":0,\"i11\":1,\"s05\":null,\"s08\":\"x\"}", BigBucket.class); @@ -542,8 +542,9 @@ public void testClassWith32CreatorParams() throws Exception } sb.append("\n}\n"); String json = sb.toString(); - ObjectMapper mapper = new ObjectMapper() - .registerModule(new ClassWith32Module()); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new ClassWith32Module()) + .build(); ClassWith32Props result = mapper.readValue(json, ClassWith32Props.class); // let's assume couple of first, last ones suffice assertEquals("NotNull1", result.p1); diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestPolymorphicDelegating.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestPolymorphicDelegating.java index eff86e3627..50e9bcafe8 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestPolymorphicDelegating.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestPolymorphicDelegating.java @@ -5,9 +5,7 @@ public class TestPolymorphicDelegating extends BaseMapTest { - // For [databind#580] - @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) static abstract class Issue580Base { } @@ -34,9 +32,9 @@ public Issue580Base value() { } /* - /********************************************************** - /* Unit tests - /********************************************************** + /********************************************************************** + /* Test methods + /********************************************************************** */ public void testAbstractDelegateWithCreator() throws Exception diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestValueInstantiator.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestValueInstantiator.java index a53ab8f31c..c7545e9075 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestValueInstantiator.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestValueInstantiator.java @@ -23,7 +23,9 @@ static class MyBean { String _secret; - public MyBean(String s, boolean bogus) { + // 20-Sep-2017, tatu: Must NOT be public for 3.x because we do auto-detect + // public ctors.... + protected MyBean(String s, boolean bogus) { _secret = s; } } @@ -116,9 +118,9 @@ public String getValueTypeDesc() { public boolean canCreateFromObjectWith() { return true; } @Override - public CreatorProperty[] getFromObjectArguments(DeserializationConfig config) { + public CreatorProperty[] getFromObjectArguments(DeserializationContext ctxt) { return new CreatorProperty[] { - new CreatorProperty(new PropertyName("type"), config.constructType(Class.class), null, + new CreatorProperty(new PropertyName("type"), ctxt.constructType(Class.class), null, null, null, null, 0, null, PropertyMetadata.STD_REQUIRED) }; @@ -146,9 +148,9 @@ public String getValueTypeDesc() { public boolean canCreateFromObjectWith() { return true; } @Override - public CreatorProperty[] getFromObjectArguments(DeserializationConfig config) { + public CreatorProperty[] getFromObjectArguments(DeserializationContext ctxt) { return new CreatorProperty[] { - new CreatorProperty(new PropertyName("name"), config.constructType(String.class), null, + new CreatorProperty(new PropertyName("name"), ctxt.constructType(String.class), null, null, null, null, 0, null, PropertyMetadata.STD_REQUIRED) }; @@ -341,8 +343,9 @@ public Object createUsingDelegate(DeserializationContext ctxt, Object delegate) public void testCustomBeanInstantiator() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new MyModule(MyBean.class, new MyBeanInstantiator())); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new MyModule(MyBean.class, new MyBeanInstantiator())) + .build(); MyBean bean = mapper.readValue("{}", MyBean.class); assertNotNull(bean); assertEquals("secret!", bean._secret); @@ -350,8 +353,9 @@ public void testCustomBeanInstantiator() throws Exception public void testCustomListInstantiator() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new MyModule(MyList.class, new MyListInstantiator())); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new MyModule(MyList.class, new MyListInstantiator())) + .build(); MyList result = mapper.readValue("[]", MyList.class); assertNotNull(result); assertEquals(MyList.class, result.getClass()); @@ -360,8 +364,9 @@ public void testCustomListInstantiator() throws Exception public void testCustomMapInstantiator() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new MyModule(MyMap.class, new MyMapInstantiator())); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new MyModule(MyMap.class, new MyMapInstantiator())) + .build(); MyMap result = mapper.readValue("{ \"a\":\"b\" }", MyMap.class); assertNotNull(result); assertEquals(MyMap.class, result.getClass()); @@ -376,8 +381,9 @@ public void testCustomMapInstantiator() throws Exception public void testDelegateBeanInstantiator() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new MyModule(MyBean.class, new MyDelegateBeanInstantiator())); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new MyModule(MyBean.class, new MyDelegateBeanInstantiator())) + .build(); MyBean bean = mapper.readValue("123", MyBean.class); assertNotNull(bean); assertEquals("123", bean._secret); @@ -385,8 +391,9 @@ public void testDelegateBeanInstantiator() throws Exception public void testDelegateListInstantiator() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new MyModule(MyList.class, new MyDelegateListInstantiator())); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new MyModule(MyList.class, new MyDelegateListInstantiator())) + .build(); MyList result = mapper.readValue("123", MyList.class); assertNotNull(result); assertEquals(1, result.size()); @@ -395,8 +402,9 @@ public void testDelegateListInstantiator() throws Exception public void testDelegateMapInstantiator() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new MyModule(MyMap.class, new MyDelegateMapInstantiator())); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new MyModule(MyMap.class, new MyDelegateMapInstantiator())) + .build(); MyMap result = mapper.readValue("123", MyMap.class); assertNotNull(result); assertEquals(1, result.size()); @@ -420,16 +428,16 @@ public void testCustomDelegateInstantiator() throws Exception public void testPropertyBasedBeanInstantiator() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new MyModule(CreatorBean.class, + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new MyModule(CreatorBean.class, new InstantiatorBase() { @Override public boolean canCreateFromObjectWith() { return true; } @Override - public CreatorProperty[] getFromObjectArguments(DeserializationConfig config) { + public CreatorProperty[] getFromObjectArguments(DeserializationContext ctxt) { return new CreatorProperty[] { - new CreatorProperty(new PropertyName("secret"), config.constructType(String.class), null, + new CreatorProperty(new PropertyName("secret"), ctxt.constructType(String.class), null, null, null, null, 0, null, PropertyMetadata.STD_REQUIRED) }; @@ -439,7 +447,8 @@ public CreatorProperty[] getFromObjectArguments(DeserializationConfig config) { public Object createFromObjectWith(DeserializationContext ctxt, Object[] args) { return new CreatorBean((String) args[0]); } - })); + })) + .build(); CreatorBean bean = mapper.readValue("{\"secret\":123,\"value\":37}", CreatorBean.class); assertNotNull(bean); assertEquals("123", bean._secret); @@ -447,8 +456,9 @@ public Object createFromObjectWith(DeserializationContext ctxt, Object[] args) { public void testPropertyBasedMapInstantiator() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new MyModule(MyMap.class, new CreatorMapInstantiator())); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new MyModule(MyMap.class, new CreatorMapInstantiator())) + .build(); MyMap result = mapper.readValue("{\"name\":\"bob\", \"x\":\"y\"}", MyMap.class); assertNotNull(result); assertEquals(2, result.size()); @@ -464,8 +474,8 @@ public void testPropertyBasedMapInstantiator() throws Exception public void testBeanFromString() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new MyModule(MysteryBean.class, + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new MyModule(MysteryBean.class, new InstantiatorBase() { @Override public boolean canCreateFromString() { return true; } @@ -474,7 +484,8 @@ public void testBeanFromString() throws Exception public Object createFromString(DeserializationContext ctxt, String value) { return new MysteryBean(value); } - })); + })) + .build(); MysteryBean result = mapper.readValue(quote("abc"), MysteryBean.class); assertNotNull(result); assertEquals("abc", result.value); @@ -482,8 +493,8 @@ public Object createFromString(DeserializationContext ctxt, String value) { public void testBeanFromInt() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new MyModule(MysteryBean.class, + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new MyModule(MysteryBean.class, new InstantiatorBase() { @Override public boolean canCreateFromInt() { return true; } @@ -492,7 +503,8 @@ public void testBeanFromInt() throws Exception public Object createFromInt(DeserializationContext ctxt, int value) { return new MysteryBean(value+1); } - })); + })) + .build(); MysteryBean result = mapper.readValue("37", MysteryBean.class); assertNotNull(result); assertEquals(Integer.valueOf(38), result.value); @@ -500,8 +512,8 @@ public Object createFromInt(DeserializationContext ctxt, int value) { public void testBeanFromLong() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new MyModule(MysteryBean.class, + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new MyModule(MysteryBean.class, new InstantiatorBase() { @Override public boolean canCreateFromLong() { return true; } @@ -510,7 +522,8 @@ public void testBeanFromLong() throws Exception public Object createFromLong(DeserializationContext ctxt, long value) { return new MysteryBean(value+1L); } - })); + })) + .build(); MysteryBean result = mapper.readValue("9876543210", MysteryBean.class); assertNotNull(result); assertEquals(Long.valueOf(9876543211L), result.value); @@ -518,8 +531,8 @@ public Object createFromLong(DeserializationContext ctxt, long value) { public void testBeanFromDouble() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new MyModule(MysteryBean.class, + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new MyModule(MysteryBean.class, new InstantiatorBase() { @Override public boolean canCreateFromDouble() { return true; } @@ -528,7 +541,8 @@ public void testBeanFromDouble() throws Exception public Object createFromDouble(DeserializationContext ctxt, double value) { return new MysteryBean(2.0 * value); } - })); + })) + .build(); MysteryBean result = mapper.readValue("0.25", MysteryBean.class); assertNotNull(result); assertEquals(Double.valueOf(0.5), result.value); @@ -536,8 +550,8 @@ public Object createFromDouble(DeserializationContext ctxt, double value) { public void testBeanFromBoolean() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new MyModule(MysteryBean.class, + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new MyModule(MysteryBean.class, new InstantiatorBase() { @Override public boolean canCreateFromBoolean() { return true; } @@ -546,7 +560,8 @@ public void testBeanFromBoolean() throws Exception public Object createFromBoolean(DeserializationContext ctxt, boolean value) { return new MysteryBean(Boolean.valueOf(value)); } - })); + })) + .build(); MysteryBean result = mapper.readValue("true", MysteryBean.class); assertNotNull(result); assertEquals(Boolean.TRUE, result.value); @@ -564,8 +579,9 @@ public Object createFromBoolean(DeserializationContext ctxt, boolean value) { */ public void testPolymorphicCreatorBean() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new MyModule(PolymorphicBeanBase.class, new PolymorphicBeanInstantiator())); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new MyModule(PolymorphicBeanBase.class, new PolymorphicBeanInstantiator())) + .build(); String JSON = "{\"type\":"+quote(PolymorphicBean.class.getName())+",\"name\":\"Axel\"}"; PolymorphicBeanBase result = mapper.readValue(JSON, PolymorphicBeanBase.class); assertNotNull(result); @@ -581,7 +597,6 @@ public void testEmptyBean() throws Exception assertEquals(3, bean.b); } - // @since 2.8 public void testErrorMessageForMissingCtor() throws Exception { // first fail, check message from JSON Object (no default ctor) @@ -596,7 +611,6 @@ public void testErrorMessageForMissingCtor() throws Exception } } - // @since 2.8 public void testErrorMessageForMissingStringCtor() throws Exception { // then from JSON String diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/creators/jdk8/DelegatingCreatorTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/jdk8/DelegatingCreatorTest.java new file mode 100644 index 0000000000..bab6bc1bbc --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/jdk8/DelegatingCreatorTest.java @@ -0,0 +1,92 @@ +package com.fasterxml.jackson.databind.deser.creators.jdk8; + +import com.fasterxml.jackson.annotation.*; +import com.fasterxml.jackson.core.type.*; +import com.fasterxml.jackson.databind.*; + +import org.junit.*; + +import java.util.*; + +import static org.assertj.core.api.BDDAssertions.*; + +public class DelegatingCreatorTest +{ + static class ClassWithDelegatingCreator { + private final String value; + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + ClassWithDelegatingCreator(Map props) { + this.value = props.get("value"); + } + + String getValue() { + return value; + } + } + + public static class IntWrapper + { + private final int value; + + @JsonCreator(mode=JsonCreator.Mode.PROPERTIES) + public IntWrapper(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } + + public static class GenericWrapper + { + private final T value; + + @JsonCreator(mode=JsonCreator.Mode.PROPERTIES) + public GenericWrapper(T value) { + this.value = value; + } + + public T getValue() { + return value; + } + } + + + /* + @Test + public void shouldNotOverrideJsonCreatorAnnotationWithSpecifiedMode() throws IOException { + + // given + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerModule(new ParameterNamesModule(JsonCreator.Mode.PROPERTIES)); + + // when + ClassWithDelegatingCreator actual = objectMapper.readValue("{\"value\":\"aValue\"}", + ClassWithDelegatingCreator.class); + + // then + Map props = new HashMap<>(); + props.put("value", "aValue"); + ClassWithDelegatingCreator expected = new ClassWithDelegatingCreator(props); + then(actual).isEqualToComparingFieldByField(expected); + } + */ + + @Test + public void shouldDeserializeIntWrapper() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + IntWrapper actual = mapper.readValue + ("{\"value\":13}", IntWrapper.class); + then(actual).isEqualToComparingFieldByField(new IntWrapper(13)); + } + + @Test + public void shouldDeserializeGenericWrapper() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + GenericWrapper actual = mapper.readValue + ("{\"value\":\"aValue\"}", new TypeReference>() { }); + then(actual).isEqualToComparingFieldByField(new GenericWrapper<>("aValue")); + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/creators/jdk8/EnumNamingTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/jdk8/EnumNamingTest.java new file mode 100644 index 0000000000..53edfe9cbf --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/jdk8/EnumNamingTest.java @@ -0,0 +1,38 @@ +package com.fasterxml.jackson.databind.deser.creators.jdk8; + +import org.junit.Test; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.ObjectMapper; + +import static org.junit.Assert.assertEquals; + +public class EnumNamingTest +{ + enum SurprisingEnum32 { + @JsonProperty("customValue") + ENUM_NAME; + } + + // for [module-parameter-names#32] + @Test + public void testCustomEnumName() throws Exception + { + final String EXP = "\"customValue\""; + + // First, verify default handling + + String json = new ObjectMapper() + .writeValueAsString(SurprisingEnum32.ENUM_NAME); + assertEquals(EXP, json); + + // and then with parameter names module + final ObjectMapper mapperWithNames = new ObjectMapper(); + json = mapperWithNames.writeValueAsString(SurprisingEnum32.ENUM_NAME); + assertEquals(EXP, json); + + // plus read back: + SurprisingEnum32 value = mapperWithNames.readValue(json, SurprisingEnum32.class); + assertEquals(SurprisingEnum32.ENUM_NAME, value); + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/creators/jdk8/ImmutableBean.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/jdk8/ImmutableBean.java new file mode 100644 index 0000000000..cc446da556 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/jdk8/ImmutableBean.java @@ -0,0 +1,69 @@ +package com.fasterxml.jackson.databind.deser.creators.jdk8; + +import com.fasterxml.jackson.annotation.JsonCreator; + +/** + * @author Lovro Pandzic + */ +class ImmutableBean { + + private final String name; + private final Integer value; + + // needed because names are implicit (as of Jackson 2.4), not explicit + @JsonCreator + public ImmutableBean(String name, Integer value) { + + this.name = name; + this.value = value; + } + + public String getName() { + + return name; + } + + public Integer getValue() { + + return value; + } + + @Override + public int hashCode() { + + int result = name != null ? name.hashCode() : 0; + result = 31 * result + (value != null ? value.hashCode() : 0); + return result; + } + + @Override + public boolean equals(Object o) { + + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + final ImmutableBean that = (ImmutableBean) o; + + if (name != null ? !name.equals(that.name) : that.name != null) { + return false; + } + if (value != null ? !value.equals(that.value) : that.value != null) { + return false; + } + + return true; + } + + @Override + public String toString() { + + return "ImmutableBean{" + + "name='" + name + '\'' + + ", value=" + value + + '}'; + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/creators/jdk8/ImmutableBeanWithStaticFactory.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/jdk8/ImmutableBeanWithStaticFactory.java new file mode 100644 index 0000000000..e68e967e42 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/jdk8/ImmutableBeanWithStaticFactory.java @@ -0,0 +1,62 @@ +package com.fasterxml.jackson.databind.deser.creators.jdk8; + +import com.fasterxml.jackson.annotation.JsonCreator; + +/** + * @author Lovro Pandzic + */ +class ImmutableBeanWithStaticFactory { + @JsonCreator + public static ImmutableBeanWithStaticFactory of(String name, Integer value) { + return new ImmutableBeanWithStaticFactory(name, value); + } + + private ImmutableBeanWithStaticFactory(String name, Integer value) { + this.name = name; + this.value = value; + } + + private final String name; + private final Integer value; + + public String getName() { return name;} + public Integer getValue() { return value; } + + @Override + public boolean equals(Object o) { + + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + final ImmutableBeanWithStaticFactory that = (ImmutableBeanWithStaticFactory) o; + + if (name != null ? !name.equals(that.name) : that.name != null) { + return false; + } + if (value != null ? !value.equals(that.value) : that.value != null) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + + int result = name != null ? name.hashCode() : 0; + result = 31 * result + (value != null ? value.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "ImmutableBeanWithStaticFactory{" + + "name='" + name + '\'' + + ", value=" + value + + '}'; + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/creators/jdk8/JsonCreatorTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/jdk8/JsonCreatorTest.java new file mode 100644 index 0000000000..bbab493bbe --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/jdk8/JsonCreatorTest.java @@ -0,0 +1,35 @@ +package com.fasterxml.jackson.databind.deser.creators.jdk8; + +import com.fasterxml.jackson.annotation.*; +import com.fasterxml.jackson.databind.*; +import org.junit.*; + +import static org.assertj.core.api.BDDAssertions.then; + +public class JsonCreatorTest extends BaseMapTest +{ + static class ClassWithJsonCreatorOnStaticMethod { + final String first; + final String second; + + ClassWithJsonCreatorOnStaticMethod(String first, String second) { + this.first = first; + this.second = second; + } + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + static ClassWithJsonCreatorOnStaticMethod factory(String first, String second) { + return new ClassWithJsonCreatorOnStaticMethod(first, second); + } + } + + @Test + public void testJsonCreatorOnStaticMethod() throws Exception { + ObjectMapper objectMapper = newJsonMapper(); + + String json = aposToQuotes("{'first':'1st','second':'2nd'}"); + ClassWithJsonCreatorOnStaticMethod actual = objectMapper.readValue(json, ClassWithJsonCreatorOnStaticMethod.class); + + then(actual).isEqualToComparingFieldByField(new ClassWithJsonCreatorOnStaticMethod("1st", "2nd")); + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/creators/jdk8/PersonTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/jdk8/PersonTest.java new file mode 100644 index 0000000000..b3400a51a5 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/jdk8/PersonTest.java @@ -0,0 +1,53 @@ +package com.fasterxml.jackson.databind.deser.creators.jdk8; + +import java.io.IOException; + +import com.fasterxml.jackson.databind.BaseMapTest; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class PersonTest extends BaseMapTest +{ + static class Person { + + // mandatory fields + private final String name; + private final String surname; + + // optional fields + private String nickname; + + // 29-Jan-2018, tatu: Should (apparently?! nice) work without annotation, as long as + // parameter names exist +// @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + public Person(String name, String surname) { + + this.name = name; + this.surname = surname; + } + + public String getName() { + return name; + } + + public String getSurname() { + return surname; + } + + public String getNickname() { + return nickname; + } + + public void setNickname(String nickname) { + this.nickname = nickname; + } + } + public void testPersonDeserialization() throws IOException + { + final ObjectMapper mapper = new ObjectMapper(); + Person actual = mapper.readValue("{\"name\":\"joe\",\"surname\":\"smith\",\"nickname\":\"joey\"}", Person.class); + + assertEquals("joe", actual.getName()); + assertEquals("smith", actual.getSurname()); + assertEquals("joey", actual.getNickname()); + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/dos/HugeIntegerCoerceTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/dos/HugeIntegerCoerceTest.java index 78a7f08886..aaf440b5c1 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/dos/HugeIntegerCoerceTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/dos/HugeIntegerCoerceTest.java @@ -27,7 +27,7 @@ public void testMaliciousLongForEnum() throws Exception /*ABC value =*/ MAPPER.readValue(BIG_POS_INTEGER, ABC.class); fail("Should not pass"); } catch (InputCoercionException e) { - verifyException(e, "out of range of int"); + verifyException(e, "out of range of `int`"); verifyException(e, "Integer with "+BIG_NUM_LEN+" digits"); } } diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/filter/IgnorePropertyOnDeserTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/filter/IgnorePropertyOnDeserTest.java index 5255d4bb25..cfbea1c20b 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/filter/IgnorePropertyOnDeserTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/filter/IgnorePropertyOnDeserTest.java @@ -41,7 +41,7 @@ static class Simple1595 { /**************************************************************** */ - private final ObjectMapper MAPPER = newObjectMapper(); + private final ObjectMapper MAPPER = newJsonMapper(); // [databind#1217] public void testIgnoreOnProperty1217() throws Exception @@ -67,9 +67,10 @@ public void testIgnoreOnProperty1217() throws Exception public void testIgnoreViaConfigOverride1217() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.configOverride(Point.class) - .setIgnorals(JsonIgnoreProperties.Value.forIgnoredProperties("y")); + ObjectMapper mapper = jsonMapperBuilder() + .withConfigOverride(Point.class, + o -> o.setIgnorals(JsonIgnoreProperties.Value.forIgnoredProperties("y"))) + .build(); Point p = mapper.readValue(aposToQuotes("{'x':1,'y':2}"), Point.class); // bind 'x', but ignore 'y' assertEquals(1, p.x); diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/filter/NullConversionsForContentTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/filter/NullConversionsForContentTest.java index 58885ed42c..53290fab4f 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/filter/NullConversionsForContentTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/filter/NullConversionsForContentTest.java @@ -40,7 +40,7 @@ static class NullContentUndefined { /********************************************************** */ - private final ObjectMapper MAPPER = newObjectMapper(); + private final ObjectMapper MAPPER = newJsonMapper(); // Tests to verify that we can set default settings for failure public void testFailOnNullFromDefaults() throws Exception @@ -55,8 +55,9 @@ public void testFailOnNullFromDefaults() throws Exception assertNull(result.values.get(0)); // but not when overridden globally: - ObjectMapper mapper = newObjectMapper(); - mapper.setDefaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.FAIL)); + ObjectMapper mapper = jsonMapperBuilder() + .changeDefaultNullHandling(n -> n.withContentNulls(Nulls.FAIL)) + .build(); try { mapper.readValue(JSON, listType); fail("Should not pass"); @@ -65,9 +66,10 @@ public void testFailOnNullFromDefaults() throws Exception } // or configured for type: - mapper = newObjectMapper(); - mapper.configOverride(List.class) - .setSetterInfo(JsonSetter.Value.forContentNulls(Nulls.FAIL)); + mapper = jsonMapperBuilder() + .withConfigOverride(List.class, + o -> o.setNullHandling(JsonSetter.Value.forContentNulls(Nulls.FAIL))) + .build(); try { mapper.readValue(JSON, listType); fail("Should not pass"); @@ -208,16 +210,18 @@ public void testNullsAsEmptyUsingDefaults() throws Exception TypeReference>> listType = new TypeReference>>() { }; // Let's see defaulting in action - ObjectMapper mapper = newObjectMapper(); - mapper.setDefaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.AS_EMPTY)); + ObjectMapper mapper = jsonMapperBuilder() + .changeDefaultNullHandling(n -> n.withContentNulls(Nulls.AS_EMPTY)) + .build(); NullContentUndefined> result = mapper.readValue(JSON, listType); assertEquals(1, result.values.size()); assertEquals(Integer.valueOf(0), result.values.get(0)); // or configured for type: - mapper = newObjectMapper(); - mapper.configOverride(List.class) - .setSetterInfo(JsonSetter.Value.forContentNulls(Nulls.AS_EMPTY)); + mapper = jsonMapperBuilder() + .withConfigOverride(List.class, + o -> o.setNullHandling(JsonSetter.Value.forContentNulls(Nulls.AS_EMPTY))) + .build(); result = mapper.readValue(JSON, listType); assertEquals(1, result.values.size()); assertEquals(Integer.valueOf(0), result.values.get(0)); @@ -300,15 +304,17 @@ public void testNullsSkipUsingDefaults() throws Exception TypeReference>> listType = new TypeReference>>() { }; // Let's see defaulting in action - ObjectMapper mapper = newObjectMapper(); - mapper.setDefaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.SKIP)); + ObjectMapper mapper = jsonMapperBuilder() + .changeDefaultNullHandling(n -> n.withContentNulls(Nulls.SKIP)) + .build(); NullContentUndefined> result = mapper.readValue(JSON, listType); assertEquals(0, result.values.size()); // or configured for type: - mapper = newObjectMapper(); - mapper.configOverride(List.class) - .setSetterInfo(JsonSetter.Value.forContentNulls(Nulls.SKIP)); + mapper = jsonMapperBuilder() + .withConfigOverride(List.class, + o -> o.setNullHandling(JsonSetter.Value.forContentNulls(Nulls.SKIP))) + .build(); result = mapper.readValue(JSON, listType); assertEquals(0, result.values.size()); } @@ -319,16 +325,18 @@ public void testNullsSkipWithOverrides() throws Exception final String JSON = aposToQuotes("{'values':[null]}"); TypeReference>> listType = new TypeReference>>() { }; - ObjectMapper mapper = newObjectMapper(); - // defaults call for fail; but POJO specifies "skip"; latter should win - mapper.setDefaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.FAIL)); + ObjectMapper mapper = jsonMapperBuilder() + // defaults call for fail; but POJO specifies "skip"; latter should win + .changeDefaultNullHandling(n -> n.withContentNulls(Nulls.FAIL)) + .build(); NullContentSkip> result = mapper.readValue(JSON, listType); assertEquals(0, result.values.size()); // ditto for per-type defaults - mapper = newObjectMapper(); - mapper.configOverride(List.class) - .setSetterInfo(JsonSetter.Value.forContentNulls(Nulls.FAIL)); + mapper = jsonMapperBuilder() + .withConfigOverride(List.class, + o -> o.setNullHandling(JsonSetter.Value.forContentNulls(Nulls.FAIL))) + .build(); result = mapper.readValue(JSON, listType); assertEquals(0, result.values.size()); } diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/filter/NullConversionsGenericTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/filter/NullConversionsGenericTest.java index 87df5f910c..b8d5325b86 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/filter/NullConversionsGenericTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/filter/NullConversionsGenericTest.java @@ -39,7 +39,7 @@ public NoCtorPOJO(boolean b) { } /********************************************************** */ - private final ObjectMapper MAPPER = newObjectMapper(); + private final ObjectMapper MAPPER = newJsonMapper(); public void testNullsToEmptyPojo() throws Exception { diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/filter/NullConversionsPojoTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/filter/NullConversionsPojoTest.java index b022fc657e..bc9a600625 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/filter/NullConversionsPojoTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/filter/NullConversionsPojoTest.java @@ -43,7 +43,7 @@ public void setName(String name) { /********************************************************** */ - private final ObjectMapper MAPPER = newObjectMapper(); + private final ObjectMapper MAPPER = newJsonMapper(); public void testFailOnNull() throws Exception { @@ -69,10 +69,11 @@ public void testFailOnNullWithDefaults() throws Exception String json = aposToQuotes("{'name':null}"); NullsForString def = MAPPER.readValue(json, NullsForString.class); assertNull(def.getName()); - - ObjectMapper mapper = newObjectMapper(); - mapper.configOverride(String.class) - .setSetterInfo(JsonSetter.Value.forValueNulls(Nulls.FAIL)); + + ObjectMapper mapper = jsonMapperBuilder() + .withConfigOverride(String.class, + o -> o.setNullHandling(JsonSetter.Value.forValueNulls(Nulls.FAIL))) + .build(); try { mapper.readValue(json, NullsForString.class); fail("Should not pass"); @@ -98,9 +99,10 @@ public void testNullsToEmptyScalar() throws Exception NullsForString def = MAPPER.readValue(json, NullsForString.class); assertNull(def.getName()); - ObjectMapper mapper = newObjectMapper(); - mapper.configOverride(String.class) - .setSetterInfo(JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY)); + ObjectMapper mapper = jsonMapperBuilder() + .withConfigOverride(String.class, + o -> o.setNullHandling(JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY))) + .build(); NullsForString named = mapper.readValue(json, NullsForString.class); assertEquals("", named.getName()); } diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/filter/NullConversionsSkipTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/filter/NullConversionsSkipTest.java index 9fe93a340d..1d5ccae26e 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/filter/NullConversionsSkipTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/filter/NullConversionsSkipTest.java @@ -52,7 +52,7 @@ public static class Pojo2015 { /********************************************************** */ - private final ObjectMapper MAPPER = newObjectMapper(); + private final ObjectMapper MAPPER = newJsonMapper(); public void testSkipNullField() throws Exception { @@ -103,9 +103,10 @@ public void testSkipNullWithDefaults() throws Exception StringValue result = MAPPER.readValue(json, StringValue.class); assertNull(result.value); - ObjectMapper mapper = newObjectMapper(); - mapper.configOverride(String.class) - .setSetterInfo(JsonSetter.Value.forValueNulls(Nulls.SKIP)); + ObjectMapper mapper = jsonMapperBuilder() + .withConfigOverride(String.class, + o -> o.setNullHandling(JsonSetter.Value.forValueNulls(Nulls.SKIP))) + .build(); result = mapper.readValue(json, StringValue.class); assertEquals("default", result.value); } diff --git a/src/test/java/com/fasterxml/jackson/databind/filter/ProblemHandler1767Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/filter/ProblemHandler1767Test.java similarity index 79% rename from src/test/java/com/fasterxml/jackson/databind/filter/ProblemHandler1767Test.java rename to src/test/java/com/fasterxml/jackson/databind/deser/filter/ProblemHandler1767Test.java index 05c87db4fe..c4896f4337 100644 --- a/src/test/java/com/fasterxml/jackson/databind/filter/ProblemHandler1767Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/filter/ProblemHandler1767Test.java @@ -1,12 +1,11 @@ -package com.fasterxml.jackson.databind.filter; +package com.fasterxml.jackson.databind.deser.filter; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler; public class ProblemHandler1767Test extends BaseMapTest { - static class IntHandler - extends DeserializationProblemHandler + static class IntHandler extends DeserializationProblemHandler { @Override public Object handleWeirdStringValue(DeserializationContext ctxt, @@ -35,11 +34,11 @@ public void setA(int a) { } public void testPrimitivePropertyWithHandler() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - mapper.addHandler(new IntHandler()); + final ObjectMapper mapper = jsonMapperBuilder() + .addHandler(new IntHandler()) + .build(); TestBean result = mapper.readValue(aposToQuotes("{'a': 'not-a-number'}"), TestBean.class); assertNotNull(result); assertEquals(1, result.a); } - } diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/filter/ProblemHandlerLocation1440Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/filter/ProblemHandlerLocation1440Test.java index 14464e7b42..a27a922c7b 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/filter/ProblemHandlerLocation1440Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/filter/ProblemHandlerLocation1440Test.java @@ -44,7 +44,7 @@ public boolean handleUnknownProperty(final DeserializationContext ctxt, final Js JsonDeserializer deserializer, Object beanOrClass, String propertyName) throws IOException { - final JsonStreamContext parsingContext = p.getParsingContext(); + final TokenStreamContext parsingContext = p.getParsingContext(); final List pathList = new ArrayList<>(); addParent(parsingContext, pathList); Collections.reverse(pathList); @@ -67,9 +67,9 @@ static String _join(String sep, Collection parts) { return sb.toString(); } - private void addParent(final JsonStreamContext streamContext, final List pathList) { - if (streamContext != null && streamContext.getCurrentName() != null) { - pathList.add(streamContext.getCurrentName()); + private void addParent(final TokenStreamContext streamContext, final List pathList) { + if (streamContext != null && streamContext.currentName() != null) { + pathList.add(streamContext.currentName()); addParent(streamContext.getParent(), pathList); } } @@ -124,11 +124,12 @@ public void testIncorrectContext() throws Exception +"'target': {'id': 'target_id','type': 'target_type','invalid_3': 'target_invalid_3'," +"'invalid_4': 'target_invalid_4','status': 'target_status','context': 'target_context'}}" ); - - ObjectMapper mapper = newObjectMapper(); - mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); final DeserializationProblemLogger logger = new DeserializationProblemLogger(); - mapper.addHandler(logger); + + ObjectMapper mapper = jsonMapperBuilder() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .addHandler(logger) + .build(); mapper.readValue(invalidInput, Activity.class); List probs = logger.problems(); diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/filter/ProblemHandlerTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/filter/ProblemHandlerTest.java index 1b56b58e9d..9b1b7f960d 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/filter/ProblemHandlerTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/filter/ProblemHandlerTest.java @@ -232,12 +232,13 @@ static class NoDefaultCtor { /********************************************************** */ - private final ObjectMapper MAPPER = newObjectMapper(); + private final ObjectMapper MAPPER = newJsonMapper(); public void testWeirdKeyHandling() throws Exception { - ObjectMapper mapper = newObjectMapper() - .addHandler(new WeirdKeyHandler(7)); + ObjectMapper mapper = jsonMapperBuilder() + .addHandler(new WeirdKeyHandler(7)) + .build(); IntKeyMapWrapper w = mapper.readValue("{\"stuff\":{\"foo\":\"abc\"}}", IntKeyMapWrapper.class); Map map = w.stuff; @@ -248,32 +249,34 @@ public void testWeirdKeyHandling() throws Exception public void testWeirdNumberHandling() throws Exception { - ObjectMapper mapper = newObjectMapper() + ObjectMapper mapper = jsonMapperBuilder() .addHandler(new WeirdNumberHandler(SingleValuedEnum.A)) - ; + .build(); SingleValuedEnum result = mapper.readValue("3", SingleValuedEnum.class); assertEquals(SingleValuedEnum.A, result); } public void testWeirdStringHandling() throws Exception { - ObjectMapper mapper = newObjectMapper() + ObjectMapper mapper = jsonMapperBuilder() .addHandler(new WeirdStringHandler(SingleValuedEnum.A)) - ; + .build(); SingleValuedEnum result = mapper.readValue("\"B\"", SingleValuedEnum.class); assertEquals(SingleValuedEnum.A, result); // also, write [databind#1629] try this - mapper = new ObjectMapper() - .addHandler(new WeirdStringHandler(null)); + mapper = jsonMapperBuilder() + .addHandler(new WeirdStringHandler(null)) + .build(); UUID result2 = mapper.readValue(quote("not a uuid!"), UUID.class); assertNull(result2); } public void testInvalidTypeId() throws Exception { - ObjectMapper mapper = newObjectMapper() - .addHandler(new UnknownTypeIdHandler(BaseImpl.class)); + ObjectMapper mapper = jsonMapperBuilder() + .addHandler(new UnknownTypeIdHandler(BaseImpl.class)) + .build(); BaseWrapper w = mapper.readValue("{\"value\":{\"type\":\"foo\",\"a\":4}}", BaseWrapper.class); assertNotNull(w); @@ -282,8 +285,9 @@ public void testInvalidTypeId() throws Exception public void testInvalidClassAsId() throws Exception { - ObjectMapper mapper = newObjectMapper() - .addHandler(new UnknownTypeIdHandler(Base2Impl.class)); + ObjectMapper mapper = jsonMapperBuilder() + .addHandler(new UnknownTypeIdHandler(Base2Impl.class)) + .build(); Base2Wrapper w = mapper.readValue("{\"value\":{\"clazz\":\"com.fizz\",\"a\":4}}", Base2Wrapper.class); assertNotNull(w); @@ -294,8 +298,9 @@ public void testInvalidClassAsId() throws Exception public void testMissingTypeId() throws Exception { - ObjectMapper mapper = newObjectMapper() - .addHandler(new MissingTypeIdHandler(BaseImpl.class)); + ObjectMapper mapper = jsonMapperBuilder() + .addHandler(new MissingTypeIdHandler(BaseImpl.class)) + .build(); BaseWrapper w = mapper.readValue("{\"value\":{\"a\":4}}", BaseWrapper.class); assertNotNull(w); @@ -304,8 +309,9 @@ public void testMissingTypeId() throws Exception public void testMissingClassAsId() throws Exception { - ObjectMapper mapper = newObjectMapper() - .addHandler(new MissingTypeIdHandler(Base2Impl.class)); + ObjectMapper mapper = jsonMapperBuilder() + .addHandler(new MissingTypeIdHandler(Base2Impl.class)) + .build(); Base2Wrapper w = mapper.readValue("{\"value\":{\"a\":4}}", Base2Wrapper.class); assertNotNull(w); @@ -328,8 +334,9 @@ public void testInvalidTypeIdFail() throws Exception public void testInstantiationExceptionHandling() throws Exception { - ObjectMapper mapper = newObjectMapper() - .addHandler(new InstantiationProblemHandler(BustedCtor.INST)); + ObjectMapper mapper = jsonMapperBuilder() + .addHandler(new InstantiationProblemHandler(BustedCtor.INST)) + .build(); BustedCtor w = mapper.readValue("{ }", BustedCtor.class); assertNotNull(w); @@ -337,9 +344,9 @@ public void testInstantiationExceptionHandling() throws Exception public void testMissingInstantiatorHandling() throws Exception { - ObjectMapper mapper = newObjectMapper() + ObjectMapper mapper = jsonMapperBuilder() .addHandler(new MissingInstantiationHandler(new NoDefaultCtor(13))) - ; + .build(); NoDefaultCtor w = mapper.readValue("{ \"x\" : true }", NoDefaultCtor.class); assertNotNull(w); assertEquals(13, w.value); @@ -347,9 +354,9 @@ public void testMissingInstantiatorHandling() throws Exception public void testUnexpectedTokenHandling() throws Exception { - ObjectMapper mapper = newObjectMapper() + ObjectMapper mapper = jsonMapperBuilder() .addHandler(new WeirdTokenHandler(Integer.valueOf(13))) - ; + .build(); Integer v = mapper.readValue("true", Integer.class); assertEquals(Integer.valueOf(13), v); } diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/filter/ProblemHandlerUnknownTypeId2221Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/filter/ProblemHandlerUnknownTypeId2221Test.java index 7cd1b4af0b..54886ee9dc 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/filter/ProblemHandlerUnknownTypeId2221Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/filter/ProblemHandlerUnknownTypeId2221Test.java @@ -83,25 +83,26 @@ public String toString() { ); public void testWithDeserializationProblemHandler() throws Exception { - final ObjectMapper mapper = new ObjectMapper() - .enableDefaultTyping(); - mapper.addHandler(new DeserializationProblemHandler() { - @Override - public JavaType handleUnknownTypeId(DeserializationContext ctxt, JavaType baseType, String subTypeId, TypeIdResolver idResolver, String failureMsg) throws IOException { -// System.out.println("Print out a warning here"); - return ctxt.constructType(Void.class); - } - }); + final ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping() + .addHandler(new DeserializationProblemHandler() { + @Override + public JavaType handleUnknownTypeId(DeserializationContext ctxt, JavaType baseType, String subTypeId, TypeIdResolver idResolver, String failureMsg) throws IOException { + return ctxt.constructType(Void.class); + } + }) + .build(); + GenericContent processableContent = mapper.readValue(JSON, GenericContent.class); assertNotNull(processableContent.getInnerObjects()); assertEquals(2, processableContent.getInnerObjects().size()); } public void testWithDisabledFAIL_ON_INVALID_SUBTYPE() throws Exception { - final ObjectMapper mapper = new ObjectMapper() + final ObjectMapper mapper = jsonMapperBuilder() .disable(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE) .enableDefaultTyping() - ; + .build(); GenericContent processableContent = mapper.readValue(JSON, GenericContent.class); assertNotNull(processableContent.getInnerObjects()); assertEquals(2, processableContent.getInnerObjects().size()); diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/filter/RecursiveIgnorePropertiesTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/filter/RecursiveIgnorePropertiesTest.java index e3a2c7fc71..60624cc2bf 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/filter/RecursiveIgnorePropertiesTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/filter/RecursiveIgnorePropertiesTest.java @@ -31,7 +31,7 @@ static class Persons { /********************************************************************** */ - private final ObjectMapper MAPPER = newObjectMapper(); + private final ObjectMapper MAPPER = newJsonMapper(); public void testRecursiveForDeser() throws Exception { diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/filter/TestUnknownPropertyDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/deser/filter/UnknownPropertyDeserTest.java similarity index 87% rename from src/test/java/com/fasterxml/jackson/databind/deser/filter/TestUnknownPropertyDeserialization.java rename to src/test/java/com/fasterxml/jackson/databind/deser/filter/UnknownPropertyDeserTest.java index dacf4231ec..b6b3326af7 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/filter/TestUnknownPropertyDeserialization.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/filter/UnknownPropertyDeserTest.java @@ -12,7 +12,7 @@ /** * Unit tests for checking handling of unknown properties */ -public class TestUnknownPropertyDeserialization +public class UnknownPropertyDeserTest extends BaseMapTest { final static String JSON_UNKNOWN_FIELD = "{ \"a\" : 1, \"foo\" : [ 1, 2, 3], \"b\" : -1 }"; @@ -52,7 +52,7 @@ public boolean handleUnknownProperty(DeserializationContext ctxt, throws IOException, JsonProcessingException { // very simple, just to verify that we do see correct token type - ((TestBean) bean).markUnknown(propertyName+":"+jp.getCurrentToken().toString()); + ((TestBean) bean).markUnknown(propertyName+":"+jp.currentToken().toString()); // Yup, we are good to go; must skip whatever value we'd have: jp.skipChildren(); return true; @@ -125,7 +125,7 @@ static class Bean987 { /********************************************************** */ - private final ObjectMapper MAPPER = newObjectMapper(); + private final ObjectMapper MAPPER = newJsonMapper(); /** * By default we should just get an exception if an unknown property @@ -146,9 +146,9 @@ public void testUnknownHandlingDefault() throws Exception */ public void testUnknownHandlingIgnoreWithHandler() throws Exception { - ObjectMapper mapper = newObjectMapper(); - mapper.clearProblemHandlers(); - mapper.addHandler(new MyHandler()); + ObjectMapper mapper = jsonMapperBuilder() + .addHandler(new MyHandler()) + .build(); TestBean result = mapper.readValue(new StringReader(JSON_UNKNOWN_FIELD), TestBean.class); assertNotNull(result); assertEquals(1, result._a); @@ -162,9 +162,9 @@ public void testUnknownHandlingIgnoreWithHandler() throws Exception */ public void testUnknownHandlingIgnoreWithHandlerAndObjectReader() throws Exception { - ObjectMapper mapper = newObjectMapper(); - mapper.clearProblemHandlers(); - TestBean result = mapper.readerFor(TestBean.class).withHandler(new MyHandler()).readValue(new StringReader(JSON_UNKNOWN_FIELD)); + ObjectMapper mapper = newJsonMapper(); + TestBean result = mapper.readerFor(TestBean.class).withHandler(new MyHandler()) + .readValue(new StringReader(JSON_UNKNOWN_FIELD)); assertNotNull(result); assertEquals(1, result._a); assertEquals(-1, result._b); @@ -177,8 +177,9 @@ public void testUnknownHandlingIgnoreWithHandlerAndObjectReader() throws Excepti */ public void testUnknownHandlingIgnoreWithFeature() throws Exception { - ObjectMapper mapper = newObjectMapper(); - mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + ObjectMapper mapper = jsonMapperBuilder() + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .build(); TestBean result = null; try { result = mapper.readValue(new StringReader(JSON_UNKNOWN_FIELD), TestBean.class); @@ -203,12 +204,11 @@ public void testWithClassIgnore() throws Exception assertNull(result.c()); } - /// @since 1.4 public void testClassIgnoreWithMap() throws Exception { // Let's actually use incompatible types for "a" and "d"; should not matter when ignored - IgnoreMap result = MAPPER.readValue - ("{ \"a\":[ 1],\n" + IgnoreMap result = MAPPER.readValue( + "{ \"a\":[ 1],\n" +"\"b\":2,\n" +"\"c\": \"x\",\n" +"\"d\":false }", IgnoreMap.class); @@ -271,14 +271,16 @@ public void testPropertyIgnoralForMap() throws Exception public void testIssue987() throws Exception { - ObjectMapper jsonMapper = newObjectMapper(); - jsonMapper.addHandler(new DeserializationProblemHandler() { - @Override - public boolean handleUnknownProperty(DeserializationContext ctxt, JsonParser p, JsonDeserializer deserializer, Object beanOrClass, String propertyName) throws IOException, JsonProcessingException { - p.skipChildren(); - return true; - } - }); + ObjectMapper jsonMapper = jsonMapperBuilder() + .addHandler(new DeserializationProblemHandler() { + @Override + public boolean handleUnknownProperty(DeserializationContext ctxt, JsonParser p, + JsonDeserializer deserializer, Object beanOrClass, String propertyName) throws IOException { + p.skipChildren(); + return true; + } + }) + .build(); String input = "[{\"aProperty\":\"x\",\"unknown\":{\"unknown\":{}}}]"; List deserializedList = jsonMapper.readValue(input, diff --git a/src/test/java/com/fasterxml/jackson/databind/misc/BeanPropertyMapTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/impl/BeanPropertyMapTest.java similarity index 93% rename from src/test/java/com/fasterxml/jackson/databind/misc/BeanPropertyMapTest.java rename to src/test/java/com/fasterxml/jackson/databind/deser/impl/BeanPropertyMapTest.java index 2240d5a512..5308853434 100644 --- a/src/test/java/com/fasterxml/jackson/databind/misc/BeanPropertyMapTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/impl/BeanPropertyMapTest.java @@ -1,4 +1,4 @@ -package com.fasterxml.jackson.databind.misc; +package com.fasterxml.jackson.databind.deser.impl; import java.util.*; @@ -32,7 +32,7 @@ public void testArrayOutOfBounds884() throws Exception props.add(new ObjectIdValueProperty(new MyObjectIdReader("pk"), md)); props.add(new ObjectIdValueProperty(new MyObjectIdReader("firstName"), md)); BeanPropertyMap propMap = new BeanPropertyMap(false, props, - new HashMap>()); + null, true); propMap = propMap.withProperty(new ObjectIdValueProperty(new MyObjectIdReader("@id"), md)); assertNotNull(propMap); } diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/inject/InvalidInjectionTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/inject/InvalidInjectionTest.java index a6e2543ebb..8a76371487 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/inject/InvalidInjectionTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/inject/InvalidInjectionTest.java @@ -24,7 +24,7 @@ static class BadBean2 { /********************************************************** */ - private final ObjectMapper MAPPER = newObjectMapper(); + private final ObjectMapper MAPPER = newJsonMapper(); public void testInvalidDup() throws Exception { diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/inject/TestInjectables.java b/src/test/java/com/fasterxml/jackson/databind/deser/inject/TestInjectables.java index 49acd07f06..3ba720c436 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/inject/TestInjectables.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/inject/TestInjectables.java @@ -90,16 +90,16 @@ public void setMethodValue(String methodValue) { /********************************************************** */ - private final ObjectMapper MAPPER = newObjectMapper(); + private final ObjectMapper MAPPER = newJsonMapper(); public void testSimple() throws Exception { - ObjectMapper mapper = newObjectMapper(); - mapper.setInjectableValues(new InjectableValues.Std() + ObjectMapper mapper = jsonMapperBuilder() + .injectableValues(new InjectableValues.Std() .addValue(String.class, "stuffValue") .addValue("myId", "xyz") .addValue(Long.TYPE, Long.valueOf(37)) - ); + ).build(); InjectedBean bean = mapper.readValue("{\"value\":3}", InjectedBean.class); assertEquals(3, bean.value); assertEquals("stuffValue", bean.stuff); @@ -135,12 +135,12 @@ public void testIssue471() throws Exception final Object methodInjected = "methodInjected"; final Object fieldInjected = "fieldInjected"; - ObjectMapper mapper = newObjectMapper() - .setInjectableValues(new InjectableValues.Std() + ObjectMapper mapper = jsonMapperBuilder() + .injectableValues(new InjectableValues.Std() .addValue("constructor_injected", constructorInjected) .addValue("method_injected", methodInjected) - .addValue("field_injected", fieldInjected)); - + .addValue("field_injected", fieldInjected)) + .build(); Bean471 bean = mapper.readValue(aposToQuotes( "{'x':13,'constructor_value':'constructor','method_value':'method','field_value':'field'}"), Bean471.class); diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/CollectionDeserTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/CollectionDeserTest.java index 92e461330a..bbbf6b317b 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/CollectionDeserTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/CollectionDeserTest.java @@ -139,8 +139,9 @@ public void testCustomDeserializer() throws IOException public void testImplicitArrays() throws Exception { // can't share mapper, custom configs (could create ObjectWriter tho) - ObjectMapper mapper = new ObjectMapper(); - mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); + ObjectMapper mapper = jsonMapperBuilder() + .enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY) + .build(); // first with simple scalar types (numbers), with collections List ints = mapper.readValue("4", List.class); @@ -262,8 +263,9 @@ public void testArrayIndexForExceptions() throws Exception // for [databind#828] public void testWrapExceptions() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.enable(DeserializationFeature.WRAP_EXCEPTIONS); + ObjectMapper mapper = jsonMapperBuilder() + .enable(DeserializationFeature.WRAP_EXCEPTIONS) + .build(); try { mapper.readValue("[{}]", new TypeReference>() {}); @@ -273,8 +275,9 @@ public void testWrapExceptions() throws Exception fail("The RuntimeException should have been wrapped with a JsonMappingException."); } - ObjectMapper mapperNoWrap = new ObjectMapper(); - mapperNoWrap.disable(DeserializationFeature.WRAP_EXCEPTIONS); + ObjectMapper mapperNoWrap = jsonMapperBuilder() + .disable(DeserializationFeature.WRAP_EXCEPTIONS) + .build(); try { mapperNoWrap.readValue("[{}]", new TypeReference>() {}); diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/DateDeserializationTZTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/DateDeserializationTZTest.java index d5038ee175..7ab2aa3e4c 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/DateDeserializationTZTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/DateDeserializationTZTest.java @@ -25,6 +25,9 @@ public class DateDeserializationTZTest private static final String LOCAL_TZ = "GMT+2"; private static final DateFormat FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"); + static { + FORMAT.setTimeZone(TimeZone.getTimeZone("UTC")); + } static class Annot_TimeZone { @JsonFormat(timezone="GMT+4") @@ -55,20 +58,15 @@ static class DateAsStringBeanGermany { public Date date; } - private ObjectMapper MAPPER; - - @Override - protected void setUp() throws Exception { - super.setUp(); - + private final static ObjectMapper MAPPER; + static { // Create an ObjectMapper with its timezone set to something other than the default (UTC). // This way we can verify that serialization and deserialization actually consider the time // zone set on the mapper. - ObjectMapper m = new ObjectMapper(); - m.setTimeZone(TimeZone.getTimeZone(LOCAL_TZ)); + ObjectMapper m = jsonMapperBuilder() + .defaultTimeZone(TimeZone.getTimeZone(LOCAL_TZ)) + .build(); MAPPER = m; - - FORMAT.setTimeZone(TimeZone.getTimeZone("UTC")); } /* @@ -379,8 +377,10 @@ public void testDateUtil_Annotation_PatternAndLocale() throws Exception { // Change the default locale set on the ObjectMapper to something else than the default. // This way we know if the default is correctly taken into account - ObjectMapper mapper = MAPPER.copy(); - mapper.setLocale( Locale.ITALY ); + ObjectMapper mapper = jsonMapperBuilder() + .defaultTimeZone(TimeZone.getTimeZone(LOCAL_TZ)) + .defaultLocale(Locale.ITALY) + .build(); // Build the JSON string. This is a mixed of ITALIAN and FRENCH (no ENGLISH because this // would be the default). @@ -448,9 +448,10 @@ public void testDateUtil_customDateFormat_withoutTZ() throws Exception DateFormat df = new SimpleDateFormat("yyyy-MM-dd'X'HH:mm:ss"); df.setTimeZone( TimeZone.getTimeZone("GMT+4") ); // TZ different from mapper's default - ObjectMapper mapper = new ObjectMapper(); - mapper.setTimeZone( TimeZone.getTimeZone(LOCAL_TZ) ); - mapper.setDateFormat(df); + ObjectMapper mapper = jsonMapperBuilder() + .defaultDateFormat(df) + .defaultTimeZone(TimeZone.getTimeZone(LOCAL_TZ)) + .build(); // The mapper's default TZ is used... verify(mapper, "2000-01-02X04:00:00", judate(2000, 1, 2, 4, 00, 00, 00, LOCAL_TZ)); @@ -464,8 +465,9 @@ public void testDateUtil_customDateFormat_withoutTZ() throws Exception DateFormat df = new SimpleDateFormat("yyyy-MM-dd'X'HH:mm:ss"); df.setTimeZone( TimeZone.getTimeZone("GMT+4") ); // TZ different from mapper's default - ObjectMapper mapper = new ObjectMapper(); - mapper.setDateFormat(df); + ObjectMapper mapper = jsonMapperBuilder() + .defaultDateFormat(df) + .build(); // FIXME mapper's default TZ should have been used verify(mapper, "2000-01-02X04:00:00", judate(2000, 1, 2, 4, 00, 00, 00, "GMT+4")); @@ -479,10 +481,11 @@ public void testDateUtil_customDateFormat_withoutTZ() throws Exception */ public void testDateUtil_customDateFormat_withTZ() throws Exception { - ObjectMapper mapper = new ObjectMapper(); DateFormat df = new SimpleDateFormat("yyyy-MM-dd'X'HH:mm:ssZ"); df.setTimeZone(TimeZone.getTimeZone("GMT+4")); // use a timezone different than the ObjectMapper and the system default - mapper.setDateFormat(df); + ObjectMapper mapper = jsonMapperBuilder() + .defaultDateFormat(df) + .build(); verify(mapper, "2000-01-02X03:04:05+0300", judate(2000, 1, 2, 3, 4, 5, 00, "GMT+3")); } diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/DateDeserializationTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/DateDeserializationTest.java index 5ec830eb24..6bff26e550 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/DateDeserializationTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/DateDeserializationTest.java @@ -90,7 +90,7 @@ public void setFoo(String foo) { /********************************************************** */ - private final ObjectMapper MAPPER = newObjectMapper(); + private final ObjectMapper MAPPER = newJsonMapper(); public void testDateUtil() throws Exception { @@ -494,10 +494,11 @@ public void testCalendar() throws Exception public void testCustom() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); DateFormat df = new SimpleDateFormat("yyyy-MM-dd'X'HH:mm:ss"); df.setTimeZone(TimeZone.getTimeZone("PST")); - mapper.setDateFormat(df); + ObjectMapper mapper = jsonMapperBuilder() + .defaultDateFormat(df) + .build(); String dateStr = "1972-12-28X15:45:00"; java.util.Date exp = df.parse(dateStr); @@ -623,13 +624,14 @@ public void testDateEndingWithZNonDefTZ1651() throws Exception // Standard mapper with timezone UTC: shared instance should be ok. // ... but, Travis manages to have fails, so insist on newly created - ObjectMapper mapper = newObjectMapper(); + ObjectMapper mapper = newJsonMapper(); Date dateUTC = mapper.readValue(json, Date.class); // 1970-01-01T00:00:00.000+00:00 // Mapper with timezone GMT-2 // note: must construct new one, not share - mapper = new ObjectMapper(); - mapper.setTimeZone(TimeZone.getTimeZone("GMT-2")); + mapper = jsonMapperBuilder() + .defaultTimeZone(TimeZone.getTimeZone("GMT-2")) + .build(); Date dateGMT1 = mapper.readValue(json, Date.class); // 1970-01-01T00:00:00.000-02:00 // Underlying timestamps should be the same @@ -745,9 +747,10 @@ public void testLenientCalendar() throws Exception } // similarly with Date... - ObjectMapper mapper = new ObjectMapper(); - mapper.configOverride(java.util.Date.class) - .setFormat(JsonFormat.Value.forLeniency(Boolean.FALSE)); + ObjectMapper mapper = jsonMapperBuilder() + .withConfigOverride(java.util.Date.class, + o -> o.setFormat(JsonFormat.Value.forLeniency(Boolean.FALSE))) + .build(); try { mapper.readValue(quote("2015-11-32"), java.util.Date.class); fail("Should not pass with invalid (with strict) date value"); diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/EnumAltIdTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/EnumAltIdTest.java index 2eb1eb76b5..1b1e7ecf4b 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/EnumAltIdTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/EnumAltIdTest.java @@ -41,7 +41,7 @@ protected static class StrictCaseBean { */ protected final ObjectMapper MAPPER = new ObjectMapper(); - protected final ObjectMapper MAPPER_IGNORE_CASE = objectMapperBuilder() + protected final ObjectMapper MAPPER_IGNORE_CASE = jsonMapperBuilder() .enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS) .build(); diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/EnumDeserializationTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/EnumDeserializationTest.java index 8fcd1cb6d5..6ab43eb03c 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/EnumDeserializationTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/EnumDeserializationTest.java @@ -33,7 +33,7 @@ public static class DummyDeserializer extends StdDeserializer { public DummyDeserializer() { super(Object.class); } @Override - public Object deserialize(JsonParser jp, DeserializationContext ctxt) + public Object deserialize(JsonParser p, DeserializationContext ctxt) { return AnnotatedTestEnum.OK; } @@ -43,9 +43,9 @@ public static class LcEnumDeserializer extends StdDeserializer { public LcEnumDeserializer() { super(TestEnum.class); } @Override - public TestEnum deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException + public TestEnum deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - return TestEnum.valueOf(jp.getText().toUpperCase()); + return TestEnum.valueOf(p.getText().toUpperCase()); } } @@ -173,13 +173,7 @@ public enum LanguageCodeMixin { public static class EnumModule extends SimpleModule { @Override public void setupModule(SetupContext context) { - context.setMixInAnnotations(AnEnum.class, LanguageCodeMixin.class); - } - - public static ObjectMapper setupObjectMapper(ObjectMapper mapper) { - final EnumModule module = new EnumModule(); - mapper.registerModule(module); - return mapper; + context.setMixIn(AnEnum.class, LanguageCodeMixin.class); } } @@ -196,16 +190,16 @@ public void testSimple() throws Exception // First "good" case with Strings String JSON = "\"OK\" \"RULES\" null"; // multiple main-level mappings, need explicit parser: - JsonParser jp = MAPPER.getFactory().createParser(JSON); + JsonParser p = MAPPER.createParser(JSON); - assertEquals(TestEnum.OK, MAPPER.readValue(jp, TestEnum.class)); - assertEquals(TestEnum.RULES, MAPPER.readValue(jp, TestEnum.class)); + assertEquals(TestEnum.OK, MAPPER.readValue(p, TestEnum.class)); + assertEquals(TestEnum.RULES, MAPPER.readValue(p, TestEnum.class)); // should be ok; nulls are typeless; handled by mapper, not by deserializer - assertNull(MAPPER.readValue(jp, TestEnum.class)); + assertNull(MAPPER.readValue(p, TestEnum.class)); // and no more content beyond that... - assertFalse(jp.hasCurrentToken()); + assertFalse(p.hasCurrentToken()); // Then alternative with index (0 means first entry) assertEquals(TestEnum.JACKSON, MAPPER.readValue(" 0 ", TestEnum.class)); @@ -217,7 +211,7 @@ public void testSimple() throws Exception } catch (MismatchedInputException jex) { verifyException(jex, "not one of the values accepted for Enum class"); } - jp.close(); + p.close(); } /** @@ -253,8 +247,9 @@ public void testSubclassedEnums() throws Exception public void testToStringEnums() throws Exception { // can't reuse global one due to reconfig - ObjectMapper m = new ObjectMapper(); - m.configure(DeserializationFeature.READ_ENUMS_USING_TO_STRING, true); + ObjectMapper m = jsonMapperBuilder() + .enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING) + .build(); LowerCaseEnum value = m.readValue("\"c\"", LowerCaseEnum.class); assertEquals(LowerCaseEnum.C, value); } @@ -262,7 +257,7 @@ public void testToStringEnums() throws Exception public void testNumbersToEnums() throws Exception { // by default numbers are fine: - assertFalse(MAPPER.getDeserializationConfig().isEnabled(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)); + assertFalse(MAPPER.deserializationConfig().isEnabled(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)); TestEnum value = MAPPER.readValue("1", TestEnum.class); assertSame(TestEnum.RULES, value); @@ -290,8 +285,9 @@ public void testNumbersToEnums() throws Exception public void testEnumsWithIndex() throws Exception { - ObjectMapper m = new ObjectMapper(); - m.enable(SerializationFeature.WRITE_ENUMS_USING_INDEX); + ObjectMapper m = jsonMapperBuilder() + .enable(SerializationFeature.WRITE_ENUMS_USING_INDEX) + .build(); String json = m.writeValueAsString(TestEnum.RULES); assertEquals(String.valueOf(TestEnum.RULES.ordinal()), json); TestEnum result = m.readValue(json, TestEnum.class); @@ -345,20 +341,21 @@ public void testAllowUnknownEnumValuesReadAsNullWithCreatorMethod() throws Excep public void testAllowUnknownEnumValuesForEnumSets() throws Exception { - ObjectReader reader = MAPPER.reader(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL); - EnumSet result = reader.forType(new TypeReference>() { }) + EnumSet result = MAPPER.reader(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL) + .forType(new TypeReference>() { }) .readValue("[\"NO-SUCH-VALUE\"]"); assertEquals(0, result.size()); } - + public void testAllowUnknownEnumValuesAsMapKeysReadAsNull() throws Exception { - ObjectReader reader = MAPPER.reader(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL); - ClassWithEnumMapKey result = reader.forType(ClassWithEnumMapKey.class) + ClassWithEnumMapKey result = MAPPER.reader(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL) + .forType(ClassWithEnumMapKey.class) .readValue("{\"map\":{\"NO-SUCH-VALUE\":\"val\"}}"); - assertTrue(result.map.containsKey(null)); + // 25-Jan-2018, tatu: as per [databind#1883], we upgrade it to `EnumMap`, which won't accept nulls... + assertEquals(0, result.map.size()); } - + public void testDoNotAllowUnknownEnumValuesAsMapKeysWhenReadAsNullDisabled() throws Exception { assertFalse(MAPPER.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)); @@ -373,33 +370,36 @@ public void testDoNotAllowUnknownEnumValuesAsMapKeysWhenReadAsNullDisabled() thr // [databind#141]: allow mapping of empty String into null public void testEnumsWithEmpty() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - mapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true); + final ObjectMapper mapper = jsonMapperBuilder() + .configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true) + .build(); TestEnum result = mapper.readValue("\"\"", TestEnum.class); assertNull(result); } public void testGenericEnumDeserialization() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule("foobar"); module.addDeserializer(Enum.class, new LcEnumDeserializer()); - mapper.registerModule(module); + final ObjectMapper mapper = jsonMapperBuilder() + .addModule(module) + .build(); // not sure this is totally safe but... assertEquals(TestEnum.JACKSON, mapper.readValue(quote("jackson"), TestEnum.class)); } // [databind#381] public void testUnwrappedEnum() throws Exception { - final ObjectMapper mapper = newObjectMapper(); - mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); - + final ObjectMapper mapper = jsonMapperBuilder() + .enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .build(); assertEquals(TestEnum.JACKSON, mapper.readValue("[" + quote("JACKSON") + "]", TestEnum.class)); } public void testUnwrappedEnumException() throws Exception { - final ObjectMapper mapper = newObjectMapper(); - mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); + final ObjectMapper mapper = jsonMapperBuilder() + .disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .build(); try { Object v = mapper.readValue("[" + quote("JACKSON") + "]", TestEnum.class); @@ -422,8 +422,8 @@ public void testIndexAsString() throws Exception assertSame(TestEnum.values()[1], en); // [databind#1690]: unless prevented - final ObjectMapper mapper = objectMapperBuilder() - .disable(MapperFeature.ALLOW_COERCION_OF_SCALARS) + final ObjectMapper mapper = jsonMapperBuilder() + .disable(DeserializationFeature.ALLOW_COERCION_OF_SCALARS) .build(); try { en = mapper.readValue(quote("1"), TestEnum.class); @@ -470,56 +470,63 @@ public void testDeserWithToString1161() throws Exception } public void testEnumWithDefaultAnnotation() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - mapper.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE); + final ObjectMapper mapper = jsonMapperBuilder() + .enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE) + .build(); EnumWithDefaultAnno myEnum = mapper.readValue("\"foo\"", EnumWithDefaultAnno.class); assertSame(EnumWithDefaultAnno.OTHER, myEnum); } public void testEnumWithDefaultAnnotationUsingIndexInBound1() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - mapper.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE); + final ObjectMapper mapper = jsonMapperBuilder() + .enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE) + .build(); EnumWithDefaultAnno myEnum = mapper.readValue("1", EnumWithDefaultAnno.class); assertSame(EnumWithDefaultAnno.B, myEnum); } public void testEnumWithDefaultAnnotationUsingIndexInBound2() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - mapper.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE); + final ObjectMapper mapper = jsonMapperBuilder() + .enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE) + .build(); EnumWithDefaultAnno myEnum = mapper.readValue("2", EnumWithDefaultAnno.class); assertSame(EnumWithDefaultAnno.OTHER, myEnum); } public void testEnumWithDefaultAnnotationUsingIndexSameAsLength() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - mapper.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE); + final ObjectMapper mapper = jsonMapperBuilder() + .enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE) + .build(); EnumWithDefaultAnno myEnum = mapper.readValue("3", EnumWithDefaultAnno.class); assertSame(EnumWithDefaultAnno.OTHER, myEnum); } public void testEnumWithDefaultAnnotationUsingIndexOutOfBound() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - mapper.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE); + final ObjectMapper mapper = jsonMapperBuilder() + .enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE) + .build(); EnumWithDefaultAnno myEnum = mapper.readValue("4", EnumWithDefaultAnno.class); assertSame(EnumWithDefaultAnno.OTHER, myEnum); } public void testEnumWithDefaultAnnotationWithConstructor() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - mapper.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE); + final ObjectMapper mapper = jsonMapperBuilder() + .enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE) + .build(); EnumWithDefaultAnnoAndConstructor myEnum = mapper.readValue("\"foo\"", EnumWithDefaultAnnoAndConstructor.class); assertNull("When using a constructor, the default value annotation shouldn't be used.", myEnum); } public void testExceptionFromCustomEnumKeyDeserializer() throws Exception { - ObjectMapper mapper = newObjectMapper() - .registerModule(new EnumModule()); + final ObjectMapper mapper = jsonMapperBuilder() + .addModule(new EnumModule()) + .build(); try { mapper.readValue("{\"TWO\": \"dumpling\"}", new TypeReference>() {}); diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/EnumMapDeserializationTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/EnumMapDeserializationTest.java index bf30d14e3d..935c5189ba 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/EnumMapDeserializationTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/EnumMapDeserializationTest.java @@ -157,8 +157,9 @@ public void testEnumMapAsPolymorphic() throws Exception enumMap.put(Enum1859.B, "stuff"); Pojo1859 input = new Pojo1859(enumMap); - ObjectMapper mapper = new ObjectMapper(); - mapper.enableDefaultTypingAsProperty(ObjectMapper.DefaultTyping.NON_FINAL, "@type"); + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTypingAsProperty(DefaultTyping.NON_FINAL, "@type") + .build(); // 05-Mar-2018, tatu: Original issue had this; should not make difference: /* @@ -215,9 +216,8 @@ public void testUnknownKeyAsNull() throws Exception .readerFor(new TypeReference>() { }) .with(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL) .readValue("{\"unknown\":\"value\"}"); - // 04-Jan-2017, tatu: Not sure if this is weird or not, but since `null`s are typically - // ok for "regular" JDK Maps... - assertEquals(1, value2.size()); - assertEquals("value", value2.get(null)); + // 25-Jan-2018, tatu: as per [databind#1883], we upgrade it to `EnumMap`, which won't accept nulls... + assertEquals(0, value2.size()); + assertEquals(EnumMap.class, value2.getClass()); } } diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKAtomicTypesTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKAtomicTypesTest.java index 5721556593..ffe5ce2ebd 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKAtomicTypesTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKAtomicTypesTest.java @@ -136,8 +136,9 @@ public void testAtomicReference() throws Exception // for [databind#811] public void testAbsentExclusion() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.setSerializationInclusion(JsonInclude.Include.NON_ABSENT); + ObjectMapper mapper = jsonMapperBuilder() + .changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_ABSENT)) + .build(); assertEquals(aposToQuotes("{'value':true}"), mapper.writeValueAsString(new SimpleWrapper(Boolean.TRUE))); assertEquals(aposToQuotes("{}"), @@ -146,40 +147,40 @@ public void testAbsentExclusion() throws Exception public void testSerPropInclusionAlways() throws Exception { - JsonInclude.Value incl = - JsonInclude.Value.construct(JsonInclude.Include.NON_ABSENT, JsonInclude.Include.ALWAYS); - ObjectMapper mapper = new ObjectMapper(); - mapper.setDefaultPropertyInclusion(incl); + ObjectMapper mapper = jsonMapperBuilder() + .changeDefaultPropertyInclusion( + i -> JsonInclude.Value.construct(JsonInclude.Include.NON_ABSENT, JsonInclude.Include.ALWAYS)) + .build(); assertEquals(aposToQuotes("{'value':true}"), mapper.writeValueAsString(new SimpleWrapper(Boolean.TRUE))); } public void testSerPropInclusionNonNull() throws Exception { - JsonInclude.Value incl = - JsonInclude.Value.construct(JsonInclude.Include.NON_ABSENT, JsonInclude.Include.NON_NULL); - ObjectMapper mapper = new ObjectMapper(); - mapper.setDefaultPropertyInclusion(incl); + ObjectMapper mapper = jsonMapperBuilder() + .changeDefaultPropertyInclusion( + i -> JsonInclude.Value.construct(JsonInclude.Include.NON_ABSENT, JsonInclude.Include.NON_NULL)) + .build(); assertEquals(aposToQuotes("{'value':true}"), mapper.writeValueAsString(new SimpleWrapper(Boolean.TRUE))); } public void testSerPropInclusionNonAbsent() throws Exception { - JsonInclude.Value incl = - JsonInclude.Value.construct(JsonInclude.Include.NON_ABSENT, JsonInclude.Include.NON_ABSENT); - ObjectMapper mapper = new ObjectMapper(); - mapper.setDefaultPropertyInclusion(incl); + ObjectMapper mapper = jsonMapperBuilder() + .changeDefaultPropertyInclusion( + i -> JsonInclude.Value.construct(JsonInclude.Include.NON_ABSENT, JsonInclude.Include.NON_ABSENT)) + .build(); assertEquals(aposToQuotes("{'value':true}"), mapper.writeValueAsString(new SimpleWrapper(Boolean.TRUE))); } public void testSerPropInclusionNonEmpty() throws Exception { - JsonInclude.Value incl = - JsonInclude.Value.construct(JsonInclude.Include.NON_ABSENT, JsonInclude.Include.NON_EMPTY); - ObjectMapper mapper = new ObjectMapper(); - mapper.setDefaultPropertyInclusion(incl); + ObjectMapper mapper = jsonMapperBuilder() + .changeDefaultPropertyInclusion( + i -> JsonInclude.Value.construct(JsonInclude.Include.NON_ABSENT, JsonInclude.Include.NON_EMPTY)) + .build(); assertEquals(aposToQuotes("{'value':true}"), mapper.writeValueAsString(new SimpleWrapper(Boolean.TRUE))); } @@ -207,13 +208,15 @@ public void testFilteringOfAtomicReference() throws Exception assertEquals(aposToQuotes("{'value':null}"), mapper.writeValueAsString(input)); // ditto with "no nulls" - mapper = new ObjectMapper().setSerializationInclusion(JsonInclude - .Include.NON_NULL); + mapper = jsonMapperBuilder() + .changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_NULL)) + .build(); assertEquals(aposToQuotes("{'value':null}"), mapper.writeValueAsString(input)); // but not with "non empty" - mapper = new ObjectMapper().setSerializationInclusion(JsonInclude - .Include.NON_EMPTY); + mapper = jsonMapperBuilder() + .changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_EMPTY)) + .build(); assertEquals("{}", mapper.writeValueAsString(input)); } @@ -260,11 +263,10 @@ public void testWithCustomDeserializer() throws Exception public void testEmpty1256() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.setSerializationInclusion(JsonInclude.Include.NON_ABSENT); - - String json = mapper.writeValueAsString(new Issue1256Bean()); - assertEquals("{}", json); + ObjectMapper mapper = jsonMapperBuilder() + .changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_ABSENT)) + .build(); + assertEquals("{}", mapper.writeValueAsString(new Issue1256Bean())); } // [databind#1307] diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKCollectionsDeserTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKCollectionsDeserTest.java index 0bc0aa3361..043b5efc6b 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKCollectionsDeserTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKCollectionsDeserTest.java @@ -3,7 +3,6 @@ import java.util.*; import com.fasterxml.jackson.annotation.JsonTypeInfo; - import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.*; @@ -49,9 +48,9 @@ public void testSingletonCollections() throws Exception // [databind#1868]: Verify class name serialized as is public void testUnmodifiableSet() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); - + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY) + .build(); Set theSet = Collections.unmodifiableSet(Collections.singleton("a")); String json = mapper.writeValueAsString(theSet); diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKNumberDeserTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKNumberDeserTest.java index f23f454665..4cc8858557 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKNumberDeserTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKNumberDeserTest.java @@ -131,8 +131,8 @@ public void testTextualNullAsNumber() throws Exception verifyException(e, "Cannot coerce String \"null\""); } - ObjectMapper noCoerceMapper = objectMapperBuilder() - .disable(MapperFeature.ALLOW_COERCION_OF_SCALARS) + ObjectMapper noCoerceMapper = jsonMapperBuilder() + .disable(DeserializationFeature.ALLOW_COERCION_OF_SCALARS) .build(); try { noCoerceMapper.readValue(NULL_JSON, Integer.TYPE); diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKScalarsTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKScalarsTest.java index a64e5b8249..e0aecd3cc7 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKScalarsTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKScalarsTest.java @@ -282,16 +282,19 @@ public void testIntPrimitive() throws Exception assertEquals(0, array[0]); // [databind#381] - final ObjectMapper mapper = new ObjectMapper(); - mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); + ObjectMapper mapper = jsonMapperBuilder() + .disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .build(); try { mapper.readValue("{\"v\":[3]}", IntBean.class); fail("Did not throw exception when reading a value from a single value array with the UNWRAP_SINGLE_VALUE_ARRAYS feature disabled"); } catch (MismatchedInputException exp) { //Correctly threw exception } - - mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); + + mapper = jsonMapperBuilder() + .enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .build(); result = mapper.readValue("{\"v\":[3]}", IntBean.class); assertEquals(3, result._v); @@ -343,18 +346,21 @@ public void testLongPrimitive() throws Exception assertNotNull(array); assertEquals(1, array.length); assertEquals(0, array[0]); - + // [databind#381] - final ObjectMapper mapper = new ObjectMapper(); - mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); + ObjectMapper mapper = jsonMapperBuilder() + .disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .build(); try { mapper.readValue("{\"v\":[3]}", LongBean.class); fail("Did not throw exception when reading a value from a single value array with the UNWRAP_SINGLE_VALUE_ARRAYS feature disabled"); } catch (MismatchedInputException exp) { //Correctly threw exception } - - mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); + + mapper = jsonMapperBuilder() + .enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .build(); result = mapper.readValue("{\"v\":[3]}", LongBean.class); assertEquals(3, result._v); @@ -466,8 +472,9 @@ public void testDoubleWrapper() throws Exception public void testDoubleAsArray() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); + ObjectMapper mapper = jsonMapperBuilder() + .disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .build(); final double value = 0.016; try { mapper.readValue("{\"v\":[" + value + "]}", DoubleBean.class); @@ -475,8 +482,10 @@ public void testDoubleAsArray() throws Exception } catch (JsonMappingException exp) { //Correctly threw exception } - - mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); + + mapper = jsonMapperBuilder() + .enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .build(); DoubleBean result = mapper.readValue("{\"v\":[" + value + "]}", DoubleBean.class); @@ -606,12 +615,12 @@ public void testSequenceOfInts() throws Exception sb.append(" "); sb.append(i); } - JsonParser jp = MAPPER.getFactory().createParser(sb.toString()); + JsonParser p = MAPPER.createParser(sb.toString()); for (int i = 0; i < NR_OF_INTS; ++i) { - Integer result = MAPPER.readValue(jp, Integer.class); + Integer result = MAPPER.readValue(p, Integer.class); assertEquals(Integer.valueOf(i), result); } - jp.close(); + p.close(); } /* diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKStringLikeTypesTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKStringLikeTypesTest.java index b4e5892bdc..b737555976 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKStringLikeTypesTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKStringLikeTypesTest.java @@ -228,11 +228,11 @@ public void testStackTraceElementWithCustom() throws Exception assertEquals(StackTraceBean.NUM, bean.location.getLineNumber()); // and then directly, iff registered - ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); module.addDeserializer(StackTraceElement.class, new MyStackTraceElementDeserializer()); - mapper.registerModule(module); - + ObjectMapper mapper = jsonMapperBuilder() + .addModule(module) + .build(); StackTraceElement elem = mapper.readValue("123", StackTraceElement.class); assertNotNull(elem); assertEquals(StackTraceBean.NUM, elem.getLineNumber()); @@ -281,17 +281,20 @@ public void testURL() throws Exception assertEquals(exp, MAPPER.readValue("\""+exp.toString()+"\"", URL.class)); // trivial case; null to null, embedded URL to URL - TokenBuffer buf = new TokenBuffer(null, false); + TokenBuffer buf = TokenBuffer.forGeneration(); buf.writeObject(null); assertNull(MAPPER.readValue(buf.asParser(), URL.class)); buf.close(); // then, URLitself come as is: - buf = new TokenBuffer(null, false); + buf = TokenBuffer.forGeneration(); buf.writeObject(exp); assertSame(exp, MAPPER.readValue(buf.asParser(), URL.class)); buf.close(); + } + public void testURLInvalid() throws Exception + { // and finally, invalid URL should be handled appropriately too try { URL result = MAPPER.readValue(quote("a b"), URL.class); @@ -300,11 +303,12 @@ public void testURL() throws Exception verifyException(e, "not a valid textual representation"); } } - + public void testUUID() throws Exception { - final ObjectMapper mapper = objectMapper(); - + final ObjectMapper mapper = jsonMapperBuilder() + .disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .build(); final String NULL_UUID = "00000000-0000-0000-0000-000000000000"; // first, couple of generated UUIDs: for (String value : new String[] { @@ -315,9 +319,6 @@ public void testUUID() throws Exception "82994ac2-7b23-49f2-8cc5-e24cf6ed77be", "00000007-0000-0000-0000-000000000000" }) { - - mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); - UUID uuid = UUID.fromString(value); assertEquals(uuid, mapper.readValue(quote(value), UUID.class)); @@ -362,13 +363,13 @@ public void testUUIDAux() throws Exception final UUID value = UUID.fromString("76e6d183-5f68-4afa-b94a-922c1fdb83f8"); // first, null should come as null - try (TokenBuffer buf = new TokenBuffer(null, false)) { + try (TokenBuffer buf = TokenBuffer.forGeneration()) { buf.writeObject(null); assertNull(MAPPER.readValue(buf.asParser(), UUID.class)); } // then, UUID itself come as is: - try (TokenBuffer buf = new TokenBuffer(null, false)) { + try (TokenBuffer buf = TokenBuffer.forGeneration()) { buf.writeObject(value); assertSame(value, MAPPER.readValue(buf.asParser(), UUID.class)); @@ -387,6 +388,7 @@ public void testUUIDAux() throws Exception UUID value2 = MAPPER.readValue(buf.asParser(), UUID.class); assertEquals(value, value2); + buf.close(); } } } diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/MapDeserializationTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/MapDeserializationTest.java index e40a7182a7..80610e91cc 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/MapDeserializationTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/MapDeserializationTest.java @@ -77,7 +77,9 @@ public static enum ConcreteType implements ITestType { } static class ClassStringMap extends HashMap,String> { } - + + static class ObjectWrapperMap extends HashMap { } + /* /********************************************************** /* Test methods, untyped (Object valued) maps @@ -151,14 +153,12 @@ public void testUntypedMap3() throws Exception "{ \"double\":42.0, \"string\":\"string\"," +"\"boolean\":true, \"list\":[\"list0\"]," +"\"null\":null }"; - - static class ObjectWrapperMap extends HashMap { } - + public void testSpecialMap() throws IOException { - final ObjectWrapperMap map = MAPPER.readValue(UNTYPED_MAP_JSON, ObjectWrapperMap.class); - assertNotNull(map); - _doTestUntyped(map); + final ObjectWrapperMap map = MAPPER.readValue(UNTYPED_MAP_JSON, ObjectWrapperMap.class); + assertNotNull(map); + _doTestUntyped(map); } public void testGenericMap() throws IOException @@ -181,12 +181,12 @@ private void _doTestUntyped(final Map map) assertNull(map.get("null")); assertEquals(5, map.size()); } - - // [JACKSON-620]: allow "" to mean 'null' for Maps + public void testFromEmptyString() throws Exception { - ObjectMapper m = new ObjectMapper(); - m.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true); + ObjectMapper m = jsonMapperBuilder() + .configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true) + .build(); Map result = m.readValue(quote(""), Map.class); assertNull(result); } @@ -223,9 +223,8 @@ public void testIntBooleanMap() throws Exception { // to get typing, must use type reference String JSON = "{ \"1\" : true, \"-1\" : false }"; - Map result = MAPPER.readValue - (JSON, new TypeReference>() { }); - + Map result = MAPPER.readValue + (JSON, new TypeReference>() { }); assertNotNull(result); assertEquals(HashMap.class, result.getClass()); assertEquals(2, result.size()); @@ -240,7 +239,7 @@ public void testExactStringStringMap() throws Exception { // to get typing, must use type reference String JSON = "{ \"a\" : \"b\" }"; - TreeMap result = MAPPER.readValue + Map result = MAPPER.readValue (JSON, new TypeReference>() { }); assertNotNull(result); @@ -308,8 +307,7 @@ public void testMapWithEnums() throws Exception String JSON = "{ \"KEY2\" : \"WHATEVER\" }"; // to get typing, must use type reference - Map result = MAPPER.readValue - (JSON, new TypeReference>() { }); + Map result = MAPPER.readValue(JSON, new TypeReference>() { }); assertNotNull(result); assertTrue(result instanceof Map); @@ -374,7 +372,7 @@ public void testDateMap() throws Exception public void testCalendarMap() throws Exception { // 18-Jun-2015, tatu: Should be safest to use default timezone that mapper would use - TimeZone tz = MAPPER.getSerializationConfig().getTimeZone(); + TimeZone tz = MAPPER.serializationConfig().getTimeZone(); Calendar c = Calendar.getInstance(tz); c.setTimeInMillis(123456000L); diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/MapKeyDeserializationTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/MapKeyDeserializationTest.java index 0cd9773fb0..a976cf6372 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/MapKeyDeserializationTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/MapKeyDeserializationTest.java @@ -49,7 +49,7 @@ public void testBooleanMapKeyDeserialization() throws Exception { TypeReference> type = new TypeReference>() { }; MapWrapper result = MAPPER.readValue(aposToQuotes("{'map':{'true':'foobar'}}"), type); - + assertEquals(1, result.map.size()); Assert.assertEquals(Boolean.TRUE, result.map.entrySet().iterator().next().getKey()); diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/TestDefaultForUtilCollections1868.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/TestDefaultForUtilCollections1868.java index f6dd9385f4..32c6ee9c66 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/TestDefaultForUtilCollections1868.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/TestDefaultForUtilCollections1868.java @@ -4,15 +4,15 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.databind.BaseMapTest; +import com.fasterxml.jackson.databind.DefaultTyping; import com.fasterxml.jackson.databind.ObjectMapper; // Unit tests for [databind#1868], related public class TestDefaultForUtilCollections1868 extends BaseMapTest { - private final ObjectMapper DEFAULT_MAPPER = new ObjectMapper(); - { - DEFAULT_MAPPER.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); - } + private final ObjectMapper DEFAULT_MAPPER = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY) + .build(); /* /********************************************************** diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/UntypedDeserializationTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/UntypedDeserializationTest.java index 7db72969ee..dfcd60c4d4 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/UntypedDeserializationTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/UntypedDeserializationTest.java @@ -11,8 +11,6 @@ import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping; -import com.fasterxml.jackson.databind.deser.ContextualDeserializer; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer; import com.fasterxml.jackson.databind.module.SimpleModule; @@ -54,7 +52,6 @@ public Number deserialize(JsonParser p, DeserializationContext ctxt) throws IOEx // Let's make this Contextual, to tease out cyclic resolution issues, if any static class ListDeserializer extends StdDeserializer> - implements ContextualDeserializer { public ListDeserializer() { super(List.class); } @@ -91,7 +88,7 @@ public Map deserialize(JsonParser p, DeserializationContext ctxt) { Map map = new LinkedHashMap(); while (p.nextValue() != JsonToken.END_OBJECT) { - map.put(p.getCurrentName(), "Y"+p.getText()); + map.put(p.currentName(), "Y"+p.getText()); } return map; } @@ -182,7 +179,6 @@ public void testSampleDoc() throws Exception // and that's all folks! } - @SuppressWarnings("unlikely-arg-type") public void testUntypedMap() throws Exception { // to get "untyped" default map-to-map, pass Object.class @@ -190,7 +186,7 @@ public void testUntypedMap() throws Exception // Not a guaranteed cast theoretically, but will work: @SuppressWarnings("unchecked") - Map result = (Map)MAPPER.readValue(JSON, Object.class); + Map result = (Map)MAPPER.readValue(JSON, Object.class); assertNotNull(result); assertTrue(result instanceof Map); @@ -224,8 +220,9 @@ public void testNestedUntypes() throws IOException // Allow 'upgrade' of big integers into Long, BigInteger public void testObjectSerializeWithLong() throws IOException { - final ObjectMapper mapper = new ObjectMapper(); - mapper.enableDefaultTyping(DefaultTyping.JAVA_LANG_OBJECT, As.PROPERTY); + final ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.JAVA_LANG_OBJECT, As.PROPERTY) + .build(); final long VALUE = 1337800584532L; String serialized = "{\"timestamp\":"+VALUE+"}"; @@ -245,8 +242,9 @@ public void testUntypedWithCustomScalarDesers() throws IOException SimpleModule m = new SimpleModule("test-module"); m.addDeserializer(String.class, new UCStringDeserializer()); m.addDeserializer(Number.class, new CustomNumberDeserializer(13)); - final ObjectMapper mapper = new ObjectMapper() - .registerModule(m); + final ObjectMapper mapper = jsonMapperBuilder() + .addModule(m) + .build(); Object ob = mapper.readValue("{\"a\":\"b\", \"nr\":1 }", Object.class); assertTrue(ob instanceof Map); @@ -266,9 +264,9 @@ public void testNonVanilla() throws IOException { SimpleModule m = new SimpleModule("test-module"); m.addDeserializer(String.class, new UCStringDeserializer()); - final ObjectMapper mapper = new ObjectMapper() - .registerModule(m); - + final ObjectMapper mapper = jsonMapperBuilder() + .addModule(m) + .build(); // Also: since this is now non-vanilla variant, try more alternatives List l = (List) mapper.readValue("[ true, false, 7, 0.5, \"foo\"]", Object.class); assertEquals(5, l.size()); @@ -321,9 +319,9 @@ public void testUntypedWithListDeser() throws IOException { SimpleModule m = new SimpleModule("test-module"); m.addDeserializer(List.class, new ListDeserializer()); - final ObjectMapper mapper = new ObjectMapper() - .registerModule(m); - + ObjectMapper mapper = jsonMapperBuilder() + .addModule(m) + .build(); // And then list... Object ob = mapper.readValue("[1, 2, true]", Object.class); assertTrue(ob instanceof List); @@ -338,9 +336,9 @@ public void testUntypedWithMapDeser() throws IOException { SimpleModule m = new SimpleModule("test-module"); m.addDeserializer(Map.class, new YMapDeserializer()); - final ObjectMapper mapper = new ObjectMapper() - .registerModule(m); - + ObjectMapper mapper = jsonMapperBuilder() + .addModule(m) + .build(); // And then list... Object ob = mapper.readValue("{\"a\":true}", Object.class); assertTrue(ob instanceof Map); @@ -372,8 +370,10 @@ public void testUntypedWithJsonArrays() throws Exception assertTrue(ob instanceof List); // but can change to produce Object[]: - MAPPER.configure(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY, true); - ob = MAPPER.readValue("[1]", Object.class); + ObjectMapper mapper = jsonMapperBuilder() + .enable(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY) + .build(); + ob = mapper.readValue("[1]", Object.class); assertEquals(Object[].class, ob.getClass()); } diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/merge/ArrayMergeTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/merge/ArrayMergeTest.java index 734b7f0266..d8bbbf407f 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/merge/ArrayMergeTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/merge/ArrayMergeTest.java @@ -26,7 +26,7 @@ protected MergedX() { } /******************************************************** */ - private final ObjectMapper MAPPER = objectMapperBuilder() + private final ObjectMapper MAPPER = jsonMapperBuilder() // 26-Oct-2016, tatu: Make sure we'll report merge problems by default .disable(MapperFeature.IGNORE_MERGE_FOR_UNMERGEABLE) .build(); diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/merge/CollectionMergeTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/merge/CollectionMergeTest.java index 7c8420d366..08591432de 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/merge/CollectionMergeTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/merge/CollectionMergeTest.java @@ -52,7 +52,7 @@ protected MergedX() { } /******************************************************** */ - private final ObjectMapper MAPPER = objectMapperBuilder() + private final ObjectMapper MAPPER = jsonMapperBuilder() // 26-Oct-2016, tatu: Make sure we'll report merge problems by default .disable(MapperFeature.IGNORE_MERGE_FOR_UNMERGEABLE) .build(); diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/merge/MapMerge1844Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/merge/MapMerge1844Test.java index e5fd75d374..320f21147e 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/merge/MapMerge1844Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/merge/MapMerge1844Test.java @@ -36,8 +36,9 @@ public void setMapIntegerInteger(Map mapIntegerInteger) { // for [databind#1844] public void testMap1844() throws Exception { - final ObjectMapper mapper = newObjectMapper(); - mapper.setDefaultMergeable(true); + final ObjectMapper mapper = jsonMapperBuilder() + .defaultMergeable(true) + .build(); final String f1 = aposToQuotes( "{ 'key1' : {\n" diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/merge/MapMergeTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/merge/MapMergeTest.java index 53a339350a..f2c3b7e6b9 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/merge/MapMergeTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/merge/MapMergeTest.java @@ -3,7 +3,6 @@ import java.util.*; import com.fasterxml.jackson.annotation.JsonMerge; -import com.fasterxml.jackson.annotation.JsonSetter; import com.fasterxml.jackson.annotation.Nulls; import com.fasterxml.jackson.databind.*; @@ -46,15 +45,15 @@ protected MergedIntMap() { /******************************************************** */ - private final ObjectMapper MAPPER = objectMapperBuilder() + private final ObjectMapper MAPPER = jsonMapperBuilder() // 26-Oct-2016, tatu: Make sure we'll report merge problems by default .disable(MapperFeature.IGNORE_MERGE_FOR_UNMERGEABLE) .build(); - private final ObjectMapper MAPPER_SKIP_NULLS = newObjectMapper() - .setDefaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.SKIP)); - ; - + private final ObjectMapper MAPPER_SKIP_NULLS = jsonMapperBuilder() + .changeDefaultNullHandling(n -> n.withContentNulls(Nulls.SKIP)) + .build(); + public void testShallowMapMerging() throws Exception { final String JSON = aposToQuotes("{'values':{'c':'y','d':null}}"); @@ -166,9 +165,10 @@ public void testDefaultDeepMapMerge() throws Exception public void testDisabledMergeViaGlobal() throws Exception { - ObjectMapper mapper = newObjectMapper(); // disable merging, globally; does not affect main level - mapper.setDefaultMergeable(false); + ObjectMapper mapper = jsonMapperBuilder() + .defaultMergeable(false) + .build(); HashMap input = new HashMap<>(); input.put("list", new ArrayList<>(Arrays.asList("a"))); @@ -183,10 +183,11 @@ public void testDisabledMergeViaGlobal() throws Exception public void testDisabledMergeByType() throws Exception { - ObjectMapper mapper = newObjectMapper(); // disable merging for "untyped", that is, `Object.class` - mapper.configOverride(Object.class) - .setMergeable(false); + ObjectMapper mapper = jsonMapperBuilder() + .withConfigOverride(Object.class, + o -> o.setMergeable(false)) + .build(); HashMap input = new HashMap<>(); input.put("list", new ArrayList<>(Arrays.asList("a"))); @@ -199,11 +200,11 @@ public void testDisabledMergeByType() throws Exception // and for extra points, disable by default but ENABLE for type, // which should once again allow merging - mapper = newObjectMapper(); - mapper.setDefaultMergeable(false); - mapper.configOverride(Object.class) - .setMergeable(true); - + mapper = jsonMapperBuilder() + .withConfigOverride(Object.class, + o -> o.setMergeable(true)) + .defaultMergeable(Boolean.FALSE) + .build(); input = new HashMap<>(); input.put("list", new ArrayList<>(Arrays.asList("x"))); diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/merge/MergeWithNullTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/merge/MergeWithNullTest.java index 5cd5db4c3e..fde6c70acb 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/merge/MergeWithNullTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/merge/MergeWithNullTest.java @@ -66,7 +66,7 @@ public AB(int a0, int b0) { /********************************************************** */ - private final ObjectMapper MAPPER = objectMapperBuilder() + private final ObjectMapper MAPPER = jsonMapperBuilder() // 26-Oct-2016, tatu: Make sure we'll report merge problems by default .disable(MapperFeature.IGNORE_MERGE_FOR_UNMERGEABLE) .build(); @@ -83,9 +83,10 @@ public void testBeanMergingWithNullDefault() throws Exception // First: via specific type override // important! We'll specify for value type to be merged - ObjectMapper mapper = newObjectMapper(); - mapper.configOverride(AB.class) - .setSetterInfo(JsonSetter.Value.forValueNulls(Nulls.SKIP)); + ObjectMapper mapper = jsonMapperBuilder() + .withConfigOverride(AB.class, + o -> o.setNullHandling(JsonSetter.Value.forValueNulls(Nulls.SKIP))) + .build(); config = mapper.readerForUpdating(new ConfigDefault(137, -3)) .readValue(aposToQuotes("{'loc':null}")); assertNotNull(config.loc); @@ -93,8 +94,9 @@ public void testBeanMergingWithNullDefault() throws Exception assertEquals(-3, config.loc.b); // Second: by global defaults - mapper = newObjectMapper(); - mapper.setDefaultSetterInfo(JsonSetter.Value.forValueNulls(Nulls.SKIP)); + mapper = jsonMapperBuilder() + .changeDefaultNullHandling(n -> n.withValueNulls(Nulls.SKIP)) + .build(); config = mapper.readerForUpdating(new ConfigDefault(12, 34)) .readValue(aposToQuotes("{'loc':null}")); assertNotNull(config.loc); diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/merge/NodeMergeTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/merge/NodeMergeTest.java index 09730235eb..81131043fd 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/merge/NodeMergeTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/merge/NodeMergeTest.java @@ -8,10 +8,11 @@ public class NodeMergeTest extends BaseMapTest { - private final static ObjectMapper MAPPER = objectMapperBuilder() + private final static ObjectMapper MAPPER = jsonMapperBuilder() // 26-Oct-2016, tatu: Make sure we'll report merge problems by default .disable(MapperFeature.IGNORE_MERGE_FOR_UNMERGEABLE) - .build(); + .build() + ; static class ObjectNodeWrapper { @JsonMerge diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/merge/PropertyMergeTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/merge/PropertyMergeTest.java index 83884691b3..cdf5a87e75 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/merge/PropertyMergeTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/merge/PropertyMergeTest.java @@ -93,7 +93,7 @@ static class CantMergeInts { /******************************************************** */ - private final ObjectMapper MAPPER = objectMapperBuilder() + private final ObjectMapper MAPPER = jsonMapperBuilder() // 26-Oct-2016, tatu: Make sure we'll report merge problems by default .disable(MapperFeature.IGNORE_MERGE_FOR_UNMERGEABLE) .build(); @@ -118,8 +118,10 @@ public void testBeanMergingViaType() throws Exception assertEquals(0, config.loc.b); // not passed, nor merge from original // but with type-overrides - ObjectMapper mapper = newObjectMapper(); - mapper.configOverride(AB.class).setMergeable(true); + ObjectMapper mapper = jsonMapperBuilder() + .withConfigOverride(AB.class, + o -> o.setMergeable(true)) + .build(); config = mapper.readValue(aposToQuotes("{'loc':{'a':3}}"), NonMergeConfig.class); assertEquals(3, config.loc.a); assertEquals(2, config.loc.b); // original, merged @@ -128,8 +130,9 @@ public void testBeanMergingViaType() throws Exception public void testBeanMergingViaGlobal() throws Exception { // but with type-overrides - ObjectMapper mapper = newObjectMapper() - .setDefaultMergeable(true); + ObjectMapper mapper = jsonMapperBuilder() + .defaultMergeable(true) + .build(); NonMergeConfig config = mapper.readValue(aposToQuotes("{'loc':{'a':3}}"), NonMergeConfig.class); assertEquals(3, config.loc.a); assertEquals(2, config.loc.b); // original, merged @@ -216,7 +219,7 @@ public void testReferenceMerging() throws Exception public void testInvalidPropertyMerge() throws Exception { - ObjectMapper mapper = objectMapperBuilder() + ObjectMapper mapper = jsonMapperBuilder() .disable(MapperFeature.IGNORE_MERGE_FOR_UNMERGEABLE) .build(); try { diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/merge/UpdateValueTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/merge/UpdateValueTest.java index ce38f86d7a..d0535ae736 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/merge/UpdateValueTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/merge/UpdateValueTest.java @@ -36,7 +36,7 @@ void setB(String b) { } } - private final ObjectMapper MAPPER = newObjectMapper(); + private final ObjectMapper MAPPER = newJsonMapper(); // [databind#318] (and Scala module issue #83] public void testValueUpdateWithCreator() throws Exception diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/validate/FullStreamReadTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/validate/FullStreamReadTest.java index 0ec3137e5a..e6f451513f 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/validate/FullStreamReadTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/validate/FullStreamReadTest.java @@ -10,6 +10,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; import com.fasterxml.jackson.databind.exc.MismatchedInputException; +import com.fasterxml.jackson.databind.json.JsonMapper; /** * Test for validating {@link com.fasterxml.jackson.databind.DeserializationFeature#FAIL_ON_TRAILING_TOKENS}. @@ -31,7 +32,7 @@ public class FullStreamReadTest extends BaseMapTest /********************************************************** */ - private final ObjectMapper MAPPER = newObjectMapper(); + private final ObjectMapper MAPPER = newJsonMapper(); public void testMapperAcceptTrailing() throws Exception { @@ -61,8 +62,9 @@ public void testMapperAcceptTrailing() throws Exception public void testMapperFailOnTrailing() throws Exception { // but things change if we enforce checks - ObjectMapper strict = newObjectMapper() - .enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS); + final JsonMapper strict = JsonMapper.builder() + .enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS) + .build(); assertTrue(strict.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)); // some still ok @@ -102,7 +104,6 @@ public void testMapperFailOnTrailing() throws Exception verifyException(e, "Unexpected character"); verifyException(e, "maybe a (non-standard) comment"); } - ObjectReader strictWithComments = strict.reader() .with(JsonReadFeature.ALLOW_JAVA_COMMENTS); _verifyArray(strictWithComments.readTree(JSON_OK_ARRAY_WITH_COMMENT)); @@ -112,8 +113,9 @@ public void testMapperFailOnTrailing() throws Exception public void testMapperFailOnTrailingWithNull() throws Exception { - final ObjectMapper strict = newObjectMapper() - .enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS); + final JsonMapper strict = JsonMapper.builder() + .enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS) + .build(); // some still ok JsonNode n = strict.readTree(JSON_OK_NULL); diff --git a/src/test/java/com/fasterxml/jackson/databind/exc/BasicExceptionTest.java b/src/test/java/com/fasterxml/jackson/databind/exc/BasicExceptionTest.java index 3c4d2bb357..830947a460 100644 --- a/src/test/java/com/fasterxml/jackson/databind/exc/BasicExceptionTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/exc/BasicExceptionTest.java @@ -14,12 +14,11 @@ public class BasicExceptionTest extends BaseMapTest { final ObjectMapper MAPPER = new ObjectMapper(); - final JsonFactory JSON_F = MAPPER.getFactory(); public void testBadDefinition() throws Exception { JavaType t = TypeFactory.defaultInstance().constructType(String.class); - JsonParser p = JSON_F.createParser("[]"); + JsonParser p = MAPPER.createParser("[]"); InvalidDefinitionException e = new InvalidDefinitionException(p, "Testing", t); assertEquals("Testing", e.getOriginalMessage()); @@ -30,20 +29,20 @@ public void testBadDefinition() throws Exception p.close(); // and via factory method: - BeanDescription beanDef = MAPPER.getSerializationConfig().introspectClassAnnotations(getClass()); + BeanDescription beanDef = MAPPER.serializationConfig().introspectClassAnnotations(getClass()); e = InvalidDefinitionException.from(p, "Testing", beanDef, (BeanPropertyDefinition) null); assertEquals(beanDef.getType(), e.getType()); assertNotNull(e); // and the other constructor too - JsonGenerator g = JSON_F.createGenerator(new StringWriter()); e = new InvalidDefinitionException(p, "Testing", t); assertEquals("Testing", e.getOriginalMessage()); assertEquals(String.class, e.getType().getRawClass()); // and factory + JsonGenerator g = MAPPER.createGenerator(new StringWriter()); e = InvalidDefinitionException.from(g, "Testing", beanDef, (BeanPropertyDefinition) null); assertEquals(beanDef.getType(), e.getType()); @@ -52,27 +51,10 @@ public void testBadDefinition() throws Exception g.close(); } - @SuppressWarnings("deprecation") - public void testInvalidFormat() throws Exception - { - // deprecated methods should still work: - InvalidFormatException e = new InvalidFormatException("Testing", Boolean.TRUE, - String.class); - assertSame(Boolean.TRUE, e.getValue()); - assertNull(e.getProcessor()); - assertNotNull(e); - - e = new InvalidFormatException("Testing", JsonLocation.NA, - Boolean.TRUE, String.class); - assertSame(Boolean.TRUE, e.getValue()); - assertNull(e.getProcessor()); - assertNotNull(e); - } - public void testIgnoredProperty() throws Exception { // first just construct valid instance with some variations - JsonParser p = JSON_F.createParser("{ }"); + JsonParser p = MAPPER.createParser("{ }"); IgnoredPropertyException e = IgnoredPropertyException.from(p, this, // to get class from "testProp", Collections.singletonList("x")); @@ -96,7 +78,7 @@ public void testIgnoredProperty() throws Exception public void testUnrecognizedProperty() throws Exception { - JsonParser p = JSON_F.createParser("{ }"); + JsonParser p = MAPPER.createParser("{ }"); UnrecognizedPropertyException e = UnrecognizedPropertyException.from(p, this, "testProp", Collections.singletonList("y")); assertNotNull(e); diff --git a/src/test/java/com/fasterxml/jackson/databind/exc/DeserExceptionTypeTest.java b/src/test/java/com/fasterxml/jackson/databind/exc/DeserExceptionTypeTest.java index 33a5be9c55..ef25fc7871 100644 --- a/src/test/java/com/fasterxml/jackson/databind/exc/DeserExceptionTypeTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/exc/DeserExceptionTypeTest.java @@ -27,7 +27,7 @@ static class NoCreatorsBean { public int x; // Constructor that is not detectable as Creator - public NoCreatorsBean(boolean foo, int foo2) { } + protected NoCreatorsBean(boolean foo, int foo2) { } } /* @@ -75,7 +75,7 @@ public void testExceptionWithIncomplete() throws Exception { BrokenStringReader r = new BrokenStringReader("[ 1, ", "TEST"); - JsonParser p = MAPPER.getFactory().createParser(r); + JsonParser p = MAPPER.createParser(r); try { @SuppressWarnings("unused") Object ob = MAPPER.readValue(p, Object.class); @@ -90,7 +90,7 @@ public void testExceptionWithIncomplete() public void testExceptionWithEOF() throws Exception { - JsonParser p = MAPPER.getFactory().createParser(" 3"); + JsonParser p = MAPPER.createParser(" 3"); Integer I = MAPPER.readValue(p, Integer.class); assertEquals(3, I.intValue()); @@ -103,7 +103,7 @@ public void testExceptionWithEOF() throws Exception verifyException(e, MismatchedInputException.class, "No content"); } // also: should have no current token after end-of-input - JsonToken t = p.getCurrentToken(); + JsonToken t = p.currentToken(); if (t != null) { fail("Expected current token to be null after end-of-stream, was: "+t); } @@ -120,21 +120,4 @@ public void testExceptionForNoCreators() throws Exception verifyException(e, InvalidDefinitionException.class, "no Creators"); } } - - /* - /********************************************************** - /* Helper methods - /********************************************************** - */ - - void verifyException(Exception e, Class expType, String expMsg) - throws Exception - { - if (e.getClass() != expType) { - fail("Expected exception of type "+expType.getName()+", got "+e.getClass().getName()); - } - if (expMsg != null) { - verifyException(e, expMsg); - } - } } diff --git a/src/test/java/com/fasterxml/jackson/databind/exc/ExceptionDeserializationTest.java b/src/test/java/com/fasterxml/jackson/databind/exc/ExceptionDeserializationTest.java index 536a5b4286..583ece03bc 100644 --- a/src/test/java/com/fasterxml/jackson/databind/exc/ExceptionDeserializationTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/exc/ExceptionDeserializationTest.java @@ -75,8 +75,9 @@ public void testWithCreator() throws IOException public void testWithNullMessage() throws IOException { - final ObjectMapper mapper = new ObjectMapper(); - mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + final ObjectMapper mapper = jsonMapperBuilder() + .changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_NULL)) + .build(); String json = mapper.writeValueAsString(new IOException((String) null)); IOException result = mapper.readValue(json, IOException.class); assertNotNull(result); @@ -98,8 +99,9 @@ public void testJDK7SuppressionProperty() throws IOException // [databind#381] public void testSingleValueArrayDeserialization() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); + final ObjectMapper mapper = jsonMapperBuilder() + .enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .build(); final IOException exp; try { throw new IOException("testing"); @@ -142,8 +144,9 @@ protected void _assertEquality(int ix, String prop, } public void testSingleValueArrayDeserializationException() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); + final ObjectMapper mapper = jsonMapperBuilder() + .disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .build(); final IOException exp; try { diff --git a/src/test/java/com/fasterxml/jackson/databind/exc/ExceptionSerializationTest.java b/src/test/java/com/fasterxml/jackson/databind/exc/ExceptionSerializationTest.java index fdee7db499..7d2e0136a7 100644 --- a/src/test/java/com/fasterxml/jackson/databind/exc/ExceptionSerializationTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/exc/ExceptionSerializationTest.java @@ -70,7 +70,7 @@ public void testSimple() throws Exception // to double-check [databind#1413] public void testSimpleOther() throws Exception { - JsonParser p = MAPPER.getFactory().createParser("{ }"); + JsonParser p = MAPPER.createParser("{ }"); InvalidFormatException exc = InvalidFormatException.from(p, "Test", getClass(), String.class); String json = MAPPER.writeValueAsString(exc); p.close(); @@ -96,9 +96,10 @@ public void testIgnorals() throws Exception assertNotNull(result.get("bogus2")); // and then also remova second property with config overrides - ObjectMapper mapper = new ObjectMapper(); - mapper.configOverride(ExceptionWithIgnoral.class) - .setIgnorals(JsonIgnoreProperties.Value.forIgnoredProperties("bogus2")); + ObjectMapper mapper = jsonMapperBuilder() + .withConfigOverride(ExceptionWithIgnoral.class, + o -> o.setIgnorals(JsonIgnoreProperties.Value.forIgnoredProperties("bogus2"))) + .build(); String json2 = mapper .writeValueAsString(new ExceptionWithIgnoral("foobar")); diff --git a/src/test/java/com/fasterxml/jackson/databind/exc/StackTraceElementTest.java b/src/test/java/com/fasterxml/jackson/databind/exc/StackTraceElementTest.java index 2ed27861a0..a130be441a 100644 --- a/src/test/java/com/fasterxml/jackson/databind/exc/StackTraceElementTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/exc/StackTraceElementTest.java @@ -26,8 +26,10 @@ public ErrorObject(Throwable throwable) { // for [databind#1794] where extra `declaringClass` is serialized from private field. public void testCustomStackTraceDeser() throws Exception { - ObjectMapper mapper = newObjectMapper(); - mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); + ObjectMapper mapper = jsonMapperBuilder() + .changeDefaultVisibility(vc -> + vc.withVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)) + .build(); String json = mapper .writerWithDefaultPrettyPrinter() diff --git a/src/test/java/com/fasterxml/jackson/databind/exc/TestExceptionsDuringWriting.java b/src/test/java/com/fasterxml/jackson/databind/exc/TestExceptionsDuringWriting.java index 87e7fa341c..08690b78e8 100644 --- a/src/test/java/com/fasterxml/jackson/databind/exc/TestExceptionsDuringWriting.java +++ b/src/test/java/com/fasterxml/jackson/databind/exc/TestExceptionsDuringWriting.java @@ -49,15 +49,14 @@ public void serialize(Bean value, JsonGenerator jgen, SerializerProvider provide public void testCatchAndRethrow() throws Exception { - ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule("test-exceptions", Version.unknownVersion()); module.addSerializer(Bean.class, new SerializerWithErrors()); - mapper.registerModule(module); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(module) + .build(); try { StringWriter sw = new StringWriter(); - /* And just to make things more interesting, let's create - * a nested data struct... - */ + // And just to make things more interesting, let's create a nested data struct... Bean[] b = { new Bean() }; List l = new ArrayList(); l.add(b); @@ -92,55 +91,12 @@ public void testExceptionWithSimpleMapper() verifyException(e, IOException.class, "TEST"); } } - - @SuppressWarnings("resource") - public void testExceptionWithMapperAndGenerator() - throws Exception - { - ObjectMapper mapper = new ObjectMapper(); - JsonFactory f = new MappingJsonFactory(); - BrokenStringWriter sw = new BrokenStringWriter("TEST"); - JsonGenerator jg = f.createGenerator(sw); - - try { - mapper.writeValue(jg, createLongObject()); - fail("Should have gotten an exception"); - } catch (IOException e) { - verifyException(e, IOException.class, "TEST"); - } - } - - @SuppressWarnings("resource") - public void testExceptionWithGeneratorMapping() - throws Exception - { - JsonFactory f = new MappingJsonFactory(); - JsonGenerator jg = f.createGenerator(new BrokenStringWriter("TEST")); - try { - jg.writeObject(createLongObject()); - fail("Should have gotten an exception"); - } catch (Exception e) { - verifyException(e, IOException.class, "TEST"); - } - } - /* /********************************************************** /* Helper methods /********************************************************** */ - void verifyException(Exception e, Class expType, String expMsg) - throws Exception - { - if (e.getClass() != expType) { - fail("Expected exception of type "+expType.getName()+", got "+e.getClass().getName()); - } - if (expMsg != null) { - verifyException(e, expMsg); - } - } - Object createLongObject() { List leaf = new ArrayList(); diff --git a/src/test/java/com/fasterxml/jackson/databind/ext/TestCoreXMLTypes.java b/src/test/java/com/fasterxml/jackson/databind/ext/TestCoreXMLTypes.java index 46962090ae..7dd91c08fb 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ext/TestCoreXMLTypes.java +++ b/src/test/java/com/fasterxml/jackson/databind/ext/TestCoreXMLTypes.java @@ -4,8 +4,6 @@ import javax.xml.namespace.QName; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.ext.CoreXMLDeserializers; -import com.fasterxml.jackson.databind.type.TypeFactory; /** * Core XML types (javax.xml) are considered "external" (or more precisely "optional") @@ -24,10 +22,12 @@ public class TestCoreXMLTypes /********************************************************** */ + private final ObjectMapper MAPPER = objectMapper(); + public void testQNameSer() throws Exception { QName qn = new QName("http://abc", "tag", "prefix"); - assertEquals(quote(qn.toString()), serializeAsString(qn)); + assertEquals(quote(qn.toString()), MAPPER.writeValueAsString(qn)); } public void testDurationSer() throws Exception @@ -35,7 +35,7 @@ public void testDurationSer() throws Exception DatatypeFactory dtf = DatatypeFactory.newInstance(); // arbitrary value Duration dur = dtf.newDurationDayTime(false, 15, 19, 58, 1); - assertEquals(quote(dur.toString()), serializeAsString(dur)); + assertEquals(quote(dur.toString()), MAPPER.writeValueAsString(dur)); } public void testXMLGregorianCalendarSerAndDeser() throws Exception @@ -57,10 +57,11 @@ public void testXMLGregorianCalendarSerAndDeser() throws Exception assertEquals(timestamp, calOut.toGregorianCalendar().getTimeInMillis()); // and then textual variant - mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); // this is ALMOST same as default for XMLGregorianCalendar... just need to unify Z/+0000 String exp = cal.toXMLFormat(); - String act = mapper.writeValueAsString(cal); + String act = mapper.writer() + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(cal); act = act.substring(1, act.length() - 1); // remove quotes exp = removeZ(exp); act = removeZ(act); @@ -71,8 +72,8 @@ private String removeZ(String dateStr) { if (dateStr.endsWith("Z")) { return dateStr.substring(0, dateStr.length()-1); } - if (dateStr.endsWith("+0000")) { - return dateStr.substring(0, dateStr.length()-5); + if (dateStr.endsWith("+00:00")) { + return dateStr.substring(0, dateStr.length()-6); } return dateStr; } @@ -82,16 +83,6 @@ private String removeZ(String dateStr) { /* Deserializer tests /********************************************************** */ - - // First things first: must be able to load the deserializers... - public void testDeserializerLoading() - { - CoreXMLDeserializers sers = new CoreXMLDeserializers(); - TypeFactory f = TypeFactory.defaultInstance(); - sers.findBeanDeserializer(f.constructType(Duration.class), null, null); - sers.findBeanDeserializer(f.constructType(XMLGregorianCalendar.class), null, null); - sers.findBeanDeserializer(f.constructType(QName.class), null, null); - } public void testQNameDeser() throws Exception { diff --git a/src/test/java/com/fasterxml/jackson/databind/ext/TestJava7Types.java b/src/test/java/com/fasterxml/jackson/databind/ext/TestJava7Types.java index b18b8d144d..880be4157f 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ext/TestJava7Types.java +++ b/src/test/java/com/fasterxml/jackson/databind/ext/TestJava7Types.java @@ -4,19 +4,13 @@ import java.nio.file.Paths; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping; -/** - * @since 2.7 - */ public class TestJava7Types extends BaseMapTest { public void testPathRoundtrip() throws Exception { ObjectMapper mapper = new ObjectMapper(); - Path input = Paths.get("/tmp", "foo.txt"); - String json = mapper.writeValueAsString(input); assertNotNull(json); @@ -30,8 +24,9 @@ public void testPathRoundtrip() throws Exception // [databind#1688]: public void testPolymorphicPath() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.enableDefaultTyping(DefaultTyping.NON_FINAL); + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.NON_FINAL) + .build(); Path input = Paths.get("/tmp", "foo.txt"); String json = mapper.writeValueAsString(new Object[] { input }); @@ -39,7 +34,9 @@ public void testPolymorphicPath() throws Exception Object[] obs = mapper.readValue(json, Object[].class); assertEquals(1, obs.length); Object ob = obs[0]; - assertTrue(ob instanceof Path); + if (!(ob instanceof Path)) { + fail("Should deserialize as `Path`, got: `"+ob.getClass().getName()+"`"); + } assertEquals(input.toString(), ob.toString()); } diff --git a/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/ContextualOptionalTest.java b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/ContextualOptionalTest.java new file mode 100644 index 0000000000..92ad2407f6 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/ContextualOptionalTest.java @@ -0,0 +1,50 @@ +package com.fasterxml.jackson.databind.ext.jdk8; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Optional; +import java.util.TimeZone; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.*; + +public class ContextualOptionalTest extends BaseMapTest +{ + // [datatypes-java8#17] + @JsonPropertyOrder({ "date", "date1", "date2" }) + static class ContextualOptionals + { + public Optional date; + + @JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy+MM+dd") + public Optional date1; + + @JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy*MM*dd") + public Optional date2; + } + + /* + /********************************************************** + /* Test methods + /********************************************************** + */ + + public void testContextualOptionals() throws Exception + { + SimpleDateFormat df = new SimpleDateFormat("yyyy/MM/dd"); + df.setTimeZone(TimeZone.getTimeZone("UTC")); + ObjectMapper mapper = jsonMapperBuilder() + .defaultDateFormat(df) + .build(); + ContextualOptionals input = new ContextualOptionals(); + input.date = Optional.ofNullable(new Date(0L)); + input.date1 = Optional.ofNullable(new Date(0L)); + input.date2 = Optional.ofNullable(new Date(0L)); + final String json = mapper.writeValueAsString(input); +//System.err.println("JSON:\n"+json); + assertEquals(aposToQuotes( + "{'date':'1970/01/01','date1':'1970+01+01','date2':'1970*01*01'}"), + json); + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/CreatorTest.java b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/CreatorTest.java new file mode 100644 index 0000000000..7539254f37 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/CreatorTest.java @@ -0,0 +1,48 @@ +package com.fasterxml.jackson.databind.ext.jdk8; + +import java.util.Optional; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.*; + +public class CreatorTest extends BaseMapTest +{ + static class CreatorWithOptionalStrings + { + Optional a, b; + + // note: something weird with test setup, should not need annotations + @JsonCreator + public CreatorWithOptionalStrings(@JsonProperty("a") Optional a, + @JsonProperty("b") Optional b) + { + this.a = a; + this.b = b; + } + } + + /* + /********************************************************** + /* Test methods + /********************************************************** + */ + + private final ObjectMapper MAPPER = newJsonMapper(); + + /** + * Test to ensure that creator parameters use defaulting + * (introduced in Jackson 2.6) + */ + public void testCreatorWithOptional() throws Exception + { + CreatorWithOptionalStrings bean = MAPPER.readValue( + aposToQuotes("{'a':'foo'}"), CreatorWithOptionalStrings.class); + assertNotNull(bean); + assertNotNull(bean.a); + assertNotNull(bean.b); + assertTrue(bean.a.isPresent()); + assertFalse(bean.b.isPresent()); + assertEquals("foo", bean.a.get()); + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/DoubleStreamSerializerTest.java b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/DoubleStreamSerializerTest.java new file mode 100644 index 0000000000..20f60c20c1 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/DoubleStreamSerializerTest.java @@ -0,0 +1,83 @@ +package com.fasterxml.jackson.databind.ext.jdk8; + +import static org.junit.Assert.*; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.stream.DoubleStream; + +import org.junit.Test; + +public class DoubleStreamSerializerTest extends StreamTestBase +{ + final double[] empty = {}; + + final double[] single = { 1L }; + + final double[] multipleValues = { Double.MIN_VALUE, Double.MAX_VALUE, 1.0, 0.0, 6.0, -3.0 }; + + final String exceptionMessage = "DoubleStream peek threw"; + + @Test + public void testEmptyStream() throws Exception { + + assertArrayEquals(empty, roundTrip(DoubleStream.empty()), 0.0); + } + + @Test + public void testSingleElement() throws Exception { + + assertArrayEquals(single, roundTrip(DoubleStream.of(single)), 0.0); + } + + @Test + public void testMultiElements() throws Exception { + + assertArrayEquals(multipleValues, roundTrip(DoubleStream.of(multipleValues)), 0.0); + } + + @Test + public void testDoubleStreamCloses() throws Exception { + + assertClosesOnSuccess(DoubleStream.of(multipleValues), this::roundTrip); + } + + @Test + public void testDoubleStreamClosesOnRuntimeException() throws Exception { + + assertClosesOnRuntimeException(exceptionMessage, this::roundTrip, DoubleStream.of(multipleValues) + .peek(e -> { + throw new RuntimeException(exceptionMessage); + })); + + } + + @Test + public void testDoubleStreamClosesOnSneakyIOException() throws Exception { + assertClosesOnIoException(exceptionMessage, this::roundTrip, DoubleStream.of(multipleValues) + .peek(e -> { + sneakyThrow(new IOException(exceptionMessage)); + })); + + } + + @Test + public void testDoubleStreamClosesOnWrappedIoException() { + + assertClosesOnWrappedIoException(exceptionMessage, this::roundTrip, DoubleStream.of(multipleValues) + .peek(e -> { + throw new UncheckedIOException(new IOException(exceptionMessage)); + })); + + } + + private double[] roundTrip(DoubleStream stream) { + try { + String json = objectMapper.writeValueAsString(stream); + return objectMapper.readValue(json, double[].class); + } catch (IOException e) { + sneakyThrow(e); + return null; + } + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/IntStreamSerializerTest.java b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/IntStreamSerializerTest.java new file mode 100644 index 0000000000..503c52014c --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/IntStreamSerializerTest.java @@ -0,0 +1,73 @@ +package com.fasterxml.jackson.databind.ext.jdk8; + +import static org.junit.Assert.*; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.stream.IntStream; + +import org.junit.Test; + +public class IntStreamSerializerTest extends StreamTestBase +{ + final int[] empty = {}; + + final int[] single = { 1 }; + + final int[] multipleValues = { Integer.MIN_VALUE, Integer.MAX_VALUE, 1, 0, 6, -3 }; + + final String exceptionMessage = "IntStream peek threw"; + + @Test + public void testEmptyStream() throws Exception { + assertArrayEquals(empty, roundTrip(IntStream.empty())); + } + + @Test + public void testSingleElement() throws Exception { + assertArrayEquals(single, roundTrip(IntStream.of(single))); + } + + @Test + public void testMultiElements() throws Exception { + assertArrayEquals(multipleValues, roundTrip(IntStream.of(multipleValues))); + } + + @Test + public void testIntStreamCloses() throws Exception { + assertClosesOnSuccess(IntStream.of(multipleValues), this::roundTrip); + } + + @Test + public void testIntStreamClosesOnRuntimeException() throws Exception { + assertClosesOnRuntimeException(exceptionMessage, this::roundTrip, IntStream.of(multipleValues) + .peek(e -> { + throw new RuntimeException(exceptionMessage); + })); + } + + @Test + public void testIntStreamClosesOnSneakyIOException() throws Exception { + assertClosesOnIoException(exceptionMessage, this::roundTrip, IntStream.of(multipleValues) + .peek(e -> { + sneakyThrow(new IOException(exceptionMessage)); + })); + } + + @Test + public void testIntStreamClosesOnWrappedIoException() { + assertClosesOnWrappedIoException(exceptionMessage, this::roundTrip, IntStream.of(multipleValues) + .peek(e -> { + throw new UncheckedIOException(new IOException(exceptionMessage)); + })); + } + + private int[] roundTrip(IntStream stream) { + try { + return objectMapper.readValue(objectMapper.writeValueAsBytes(stream), int[].class); + } catch (IOException e) { + sneakyThrow(e); + return null; + } + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/JDK8TypesTest.java b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/JDK8TypesTest.java new file mode 100644 index 0000000000..dae8180a58 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/JDK8TypesTest.java @@ -0,0 +1,37 @@ +package com.fasterxml.jackson.databind.ext.jdk8; + +import java.util.Optional; +import java.util.OptionalDouble; +import java.util.OptionalInt; +import java.util.OptionalLong; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.type.ReferenceType; + +public class JDK8TypesTest extends BaseMapTest +{ + private final ObjectMapper MAPPER = newJsonMapper(); + + public void testOptionalsAreReferentialTypes() throws Exception + { + JavaType t = MAPPER.constructType(Optional.class); + assertTrue(t.isReferenceType()); + ReferenceType rt = (ReferenceType) t; + assertEquals(Object.class, rt.getContentType().getRawClass()); + + t = MAPPER.constructType(OptionalInt.class); + assertTrue(t.isReferenceType()); + rt = (ReferenceType) t; + assertEquals(Integer.TYPE, rt.getContentType().getRawClass()); + + t = MAPPER.constructType(OptionalLong.class); + assertTrue(t.isReferenceType()); + rt = (ReferenceType) t; + assertEquals(Long.TYPE, rt.getContentType().getRawClass()); + + t = MAPPER.constructType(OptionalDouble.class); + assertTrue(t.isReferenceType()); + rt = (ReferenceType) t; + assertEquals(Double.TYPE, rt.getContentType().getRawClass()); + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/LongStreamSerializerTest.java b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/LongStreamSerializerTest.java new file mode 100644 index 0000000000..7ef2227441 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/LongStreamSerializerTest.java @@ -0,0 +1,82 @@ +package com.fasterxml.jackson.databind.ext.jdk8; + +import static org.junit.Assert.*; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.stream.LongStream; + +import org.junit.Test; + +public class LongStreamSerializerTest extends StreamTestBase +{ + final long[] empty = {}; + + final long[] single = { 1L }; + + final long[] multipleValues = { Long.MIN_VALUE, Long.MAX_VALUE, 1L, 0L, 6L, -3L }; + + final String exceptionMessage = "LongStream peek threw"; + + @Test + public void testEmptyStream() throws Exception { + + assertArrayEquals(empty, roundTrip(LongStream.empty())); + } + + @Test + public void testSingleElement() throws Exception { + + assertArrayEquals(single, roundTrip(LongStream.of(single))); + } + + @Test + public void testMultiElements() throws Exception { + + assertArrayEquals(multipleValues, roundTrip(LongStream.of(multipleValues))); + } + + @Test + public void testLongStreamCloses() throws Exception { + + assertClosesOnSuccess(LongStream.of(multipleValues), this::roundTrip); + } + + @Test + public void testLongStreamClosesOnRuntimeException() throws Exception { + + assertClosesOnRuntimeException(exceptionMessage, this::roundTrip, LongStream.of(multipleValues) + .peek(e -> { + throw new RuntimeException(exceptionMessage); + })); + + } + + @Test + public void testLongStreamClosesOnSneakyIOException() throws Exception { + + assertClosesOnIoException(exceptionMessage, this::roundTrip, LongStream.of(multipleValues) + .peek(e -> { + sneakyThrow(new IOException(exceptionMessage)); + })); + + } + + @Test + public void testLongStreamClosesOnWrappedIoException() { + + assertClosesOnWrappedIoException(exceptionMessage, this::roundTrip, LongStream.of(multipleValues) + .peek(e -> { + throw new UncheckedIOException(new IOException(exceptionMessage)); + })); + } + + private long[] roundTrip(LongStream stream) { + try { + return objectMapper.readValue(objectMapper.writeValueAsBytes(stream), long[].class); + } catch (IOException e) { + sneakyThrow(e); + return null; + } + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalBasicTest.java b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalBasicTest.java new file mode 100644 index 0000000000..bac013d5fd --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalBasicTest.java @@ -0,0 +1,230 @@ +package com.fasterxml.jackson.databind.ext.jdk8; + +import java.util.*; + +import com.fasterxml.jackson.annotation.*; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.JsonTypeInfo.As; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.*; + +public class OptionalBasicTest extends BaseMapTest +{ + public static final class OptionalData { + public Optional myString; + } + + @JsonAutoDetect(fieldVisibility = Visibility.ANY) + public static final class OptionalGenericData { + private Optional myData; + } + + @JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class) + public static class Unit + { + public Optional baseUnit; + + public Unit() { + } + + public Unit(final Optional u) { + baseUnit = u; + } + + public void link(final Unit u) { + baseUnit = Optional.of(u); + } + } + + // To test handling of polymorphic value types + + public static class Container { + public Optional contained; + } + + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = As.PROPERTY) + @JsonSubTypes({ + @JsonSubTypes.Type(name = "ContainedImpl", value = ContainedImpl.class), + }) + public static interface Contained { } + + public static class ContainedImpl implements Contained { } + + /* + /********************************************************************** + /* Test methods + /********************************************************************** + */ + + private final ObjectMapper MAPPER = newJsonMapper(); + + public void testOptionalTypeResolution() throws Exception { + // With 2.6, we need to recognize it as ReferenceType + JavaType t = MAPPER.constructType(Optional.class); + assertNotNull(t); + assertEquals(Optional.class, t.getRawClass()); + assertTrue(t.isReferenceType()); + } + + public void testDeserAbsent() throws Exception { + Optional value = MAPPER.readValue("null", + new TypeReference>() { + }); + assertFalse(value.isPresent()); + } + + public void testDeserSimpleString() throws Exception { + Optional value = MAPPER.readValue("\"simpleString\"", + new TypeReference>() { + }); + assertTrue(value.isPresent()); + assertEquals("simpleString", value.get()); + } + + public void testDeserInsideObject() throws Exception { + OptionalData data = MAPPER.readValue("{\"myString\":\"simpleString\"}", + OptionalData.class); + assertTrue(data.myString.isPresent()); + assertEquals("simpleString", data.myString.get()); + } + + public void testDeserComplexObject() throws Exception { + TypeReference> type = new TypeReference>() { + }; + Optional data = MAPPER.readValue( + "{\"myString\":\"simpleString\"}", type); + assertTrue(data.isPresent()); + assertTrue(data.get().myString.isPresent()); + assertEquals("simpleString", data.get().myString.get()); + } + + public void testDeserGeneric() throws Exception { + TypeReference>> type = new TypeReference>>() { + }; + Optional> data = MAPPER.readValue( + "{\"myData\":\"simpleString\"}", type); + assertTrue(data.isPresent()); + assertTrue(data.get().myData.isPresent()); + assertEquals("simpleString", data.get().myData.get()); + } + + public void testSerAbsent() throws Exception { + String value = MAPPER.writeValueAsString(Optional.empty()); + assertEquals("null", value); + } + + public void testSerSimpleString() throws Exception { + String value = MAPPER.writeValueAsString(Optional.of("simpleString")); + assertEquals("\"simpleString\"", value); + } + + public void testSerInsideObject() throws Exception { + OptionalData data = new OptionalData(); + data.myString = Optional.of("simpleString"); + String value = MAPPER.writeValueAsString(data); + assertEquals("{\"myString\":\"simpleString\"}", value); + } + + public void testSerComplexObject() throws Exception { + OptionalData data = new OptionalData(); + data.myString = Optional.of("simpleString"); + String value = MAPPER.writeValueAsString(Optional.of(data)); + assertEquals("{\"myString\":\"simpleString\"}", value); + } + + public void testSerGeneric() throws Exception { + OptionalGenericData data = new OptionalGenericData(); + data.myData = Optional.of("simpleString"); + String value = MAPPER.writeValueAsString(Optional.of(data)); + assertEquals("{\"myData\":\"simpleString\"}", value); + } + + public void testSerOptDefault() throws Exception { + OptionalData data = new OptionalData(); + data.myString = Optional.empty(); + String value = jsonMapperBuilder().changeDefaultPropertyInclusion( + incl -> incl.withValueInclusion(JsonInclude.Include.ALWAYS)) + .build() + .writeValueAsString(data); + assertEquals("{\"myString\":null}", value); + } + + public void testSerOptNull() throws Exception { + OptionalData data = new OptionalData(); + data.myString = null; + String value = jsonMapperBuilder().changeDefaultPropertyInclusion( + incl -> incl.withValueInclusion(JsonInclude.Include.NON_NULL)) + .build().writeValueAsString(data); + assertEquals("{}", value); + } + + public void testSerOptNonEmpty() throws Exception { + OptionalData data = new OptionalData(); + data.myString = null; + String value = jsonMapperBuilder().changeDefaultPropertyInclusion( + incl -> incl.withValueInclusion(JsonInclude.Include.NON_EMPTY)) + .build() + .writeValueAsString(data); + assertEquals("{}", value); + } + + public void testWithTypingEnabled() throws Exception { + final ObjectMapper mapper = jsonMapperBuilder() + // ENABLE TYPING + .enableDefaultTyping(DefaultTyping.OBJECT_AND_NON_CONCRETE) + .build(); + + final OptionalData myData = new OptionalData(); + myData.myString = Optional.ofNullable("abc"); + + final String json = mapper.writeValueAsString(myData); + final OptionalData deserializedMyData = mapper.readValue(json, + OptionalData.class); + assertEquals(myData.myString, deserializedMyData.myString); + } + + public void testObjectId() throws Exception { + final Unit input = new Unit(); + input.link(input); + String json = MAPPER.writeValueAsString(input); + Unit result = MAPPER.readValue(json, Unit.class); + assertNotNull(result); + assertNotNull(result.baseUnit); + assertTrue(result.baseUnit.isPresent()); + Unit base = result.baseUnit.get(); + assertSame(result, base); + } + + public void testOptionalCollection() throws Exception { + + TypeReference>> typeReference = new TypeReference>>() { + }; + + List> list = new ArrayList>(); + list.add(Optional.of("2014-1-22")); + list.add(Optional. empty()); + list.add(Optional.of("2014-1-23")); + + String str = MAPPER.writeValueAsString(list); + assertEquals("[\"2014-1-22\",null,\"2014-1-23\"]", str); + + List> result = MAPPER.readValue(str, typeReference); + assertEquals(list.size(), result.size()); + for (int i = 0; i < list.size(); ++i) { + assertEquals("Entry #" + i, list.get(i), result.get(i)); + } + } + + public void testPolymorphic() throws Exception + { + final Container dto = new Container(); + dto.contained = Optional.of((Contained) new ContainedImpl()); + + final String json = MAPPER.writeValueAsString(dto); + + final Container fromJson = MAPPER.readValue(json, Container.class); + assertNotNull(fromJson.contained); + assertTrue(fromJson.contained.isPresent()); + assertSame(ContainedImpl.class, fromJson.contained.get().getClass()); + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalBooleanTest.java b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalBooleanTest.java new file mode 100644 index 0000000000..b6d375ef76 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalBooleanTest.java @@ -0,0 +1,52 @@ +package com.fasterxml.jackson.databind.ext.jdk8; + +import java.util.Optional; + +import com.fasterxml.jackson.databind.*; + +public class OptionalBooleanTest extends BaseMapTest +{ + static class BooleanBean { + public Optional value; + + public BooleanBean() { } + public BooleanBean(Boolean b) { + value = Optional.ofNullable(b); + } + } + + private final ObjectMapper MAPPER = newJsonMapper(); + + // for [datatype-jdk8#23] + public void testBoolean() throws Exception + { + // First, serialization + String json = MAPPER.writeValueAsString(new BooleanBean(true)); + assertEquals(aposToQuotes("{'value':true}"), json); + json = MAPPER.writeValueAsString(new BooleanBean()); + assertEquals(aposToQuotes("{'value':null}"), json); + json = MAPPER.writeValueAsString(new BooleanBean(null)); + assertEquals(aposToQuotes("{'value':null}"), json); + + // then deser + BooleanBean b = MAPPER.readValue(aposToQuotes("{'value':null}"), BooleanBean.class); + assertNotNull(b.value); + assertFalse(b.value.isPresent()); + + b = MAPPER.readValue(aposToQuotes("{'value':false}"), BooleanBean.class); + assertNotNull(b.value); + assertTrue(b.value.isPresent()); + assertFalse(b.value.get().booleanValue()); + + b = MAPPER.readValue(aposToQuotes("{'value':true}"), BooleanBean.class); + assertNotNull(b.value); + assertTrue(b.value.isPresent()); + assertTrue(b.value.get().booleanValue()); + + // and looks like a special, somewhat non-conforming case is what a user had + // issues with + b = MAPPER.readValue(aposToQuotes("{'value':''}"), BooleanBean.class); + assertNotNull(b.value); + assertFalse(b.value.isPresent()); + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalMapsTest.java b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalMapsTest.java new file mode 100644 index 0000000000..d92f3ee7f3 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalMapsTest.java @@ -0,0 +1,45 @@ +package com.fasterxml.jackson.databind.ext.jdk8; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Optional; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.BaseMapTest; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class OptionalMapsTest extends BaseMapTest +{ + static final class OptMapBean { + public Map> values; + + public OptMapBean(String key, Optional v) { + values = new LinkedHashMap<>(); + values.put(key, v); + } + } + + /* + /********************************************************************** + /* Test methods + /********************************************************************** + */ + + public void testMapElementInclusion() throws Exception + { + ObjectMapper mapper = jsonMapperBuilder().changeDefaultPropertyInclusion( + incl -> incl.withValueInclusion(JsonInclude.Include.NON_NULL) + .withContentInclusion(JsonInclude.Include.NON_ABSENT)) + .build(); + // first: Absent entry/-ies should NOT be included + assertEquals("{\"values\":{}}", + mapper.writeValueAsString(new OptMapBean("key", Optional.empty()))); + // but non-empty should + assertEquals("{\"values\":{\"key\":\"value\"}}", + mapper.writeValueAsString(new OptMapBean("key", Optional.of("value")))); + // and actually even empty + assertEquals("{\"values\":{\"key\":\"\"}}", + mapper.writeValueAsString(new OptMapBean("key", Optional.of("")))); + } + +} diff --git a/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalNumbersTest.java b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalNumbersTest.java new file mode 100644 index 0000000000..95e8e0aa96 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalNumbersTest.java @@ -0,0 +1,192 @@ +package com.fasterxml.jackson.databind.ext.jdk8; + +import java.util.OptionalDouble; +import java.util.OptionalInt; +import java.util.OptionalLong; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.*; + +public class OptionalNumbersTest extends BaseMapTest +{ + static class OptionalIntBean { + public OptionalInt value; + + public OptionalIntBean() { value = OptionalInt.empty(); } + OptionalIntBean(int v) { + value = OptionalInt.of(v); + } + } + + static class OptionalLongBean { + public OptionalLong value; + + public OptionalLongBean() { value = OptionalLong.empty(); } + OptionalLongBean(long v) { + value = OptionalLong.of(v); + } + } + + static class OptionalDoubleBean { + public OptionalDouble value; + + public OptionalDoubleBean() { value = OptionalDouble.empty(); } + OptionalDoubleBean(double v) { + value = OptionalDouble.of(v); + } + } + + private final ObjectMapper MAPPER = newJsonMapper(); + + /* + /********************************************************** + /* Test methods, OptionalInt + /********************************************************** + */ + + public void testOptionalIntAbsent() throws Exception + { + String json = MAPPER.writeValueAsString(OptionalInt.empty()); + assertFalse(MAPPER.readValue(json, OptionalInt.class).isPresent()); + } + + public void testOptionalIntInArrayAbsent() throws Exception + { + OptionalInt[] ints = MAPPER.readValue("[null]", OptionalInt[].class); + assertEquals(1, ints.length); + assertNotNull(ints[0]); + assertFalse(ints[0].isPresent()); + } + + public void testOptionalIntPresent() throws Exception + { + assertEquals(5, MAPPER.readValue(MAPPER.writeValueAsBytes(OptionalInt.of(5)), OptionalInt.class).getAsInt()); + } + + public void testOptionalIntCoerceFromString() throws Exception + { + OptionalInt opt = MAPPER.readValue(quote("123"), OptionalInt.class); + assertEquals(123, opt.getAsInt()); + opt = MAPPER.readValue("\"\"", OptionalInt.class); + assertNotNull(opt); + assertFalse(opt.isPresent()); + + OptionalIntBean bean = MAPPER.readValue(aposToQuotes("{'value':null}"), + OptionalIntBean.class); + assertNotNull(bean.value); + assertFalse(bean.value.isPresent()); + + bean = MAPPER.readValue(aposToQuotes("{'value':'-37'}"), OptionalIntBean.class); + assertNotNull(bean.value); + assertEquals(-37L, bean.value.getAsInt()); + } + + /* + /********************************************************** + /* Test methods, OptionalLong + /********************************************************** + */ + + public void testOptionalLongAbsent() throws Exception + { + assertFalse(MAPPER.readValue(MAPPER.writeValueAsBytes(OptionalLong.empty()), OptionalLong.class).isPresent()); + } + + public void testOptionalLongInArrayAbsent() throws Exception + { + OptionalLong[] arr = MAPPER.readValue("[null]", OptionalLong[].class); + assertEquals(1, arr.length); + assertNotNull(arr[0]); + assertFalse(arr[0].isPresent()); + } + + public void testOptionalLongPresent() throws Exception + { + assertEquals(Long.MAX_VALUE, MAPPER.readValue(MAPPER.writeValueAsBytes(OptionalLong.of(Long.MAX_VALUE)), OptionalLong.class).getAsLong()); + } + + public void testOptionalLongCoerceFromString() throws Exception + { + OptionalLong opt = MAPPER.readValue(quote("123"), OptionalLong.class); + assertEquals(123L, opt.getAsLong()); + + // should coerce from empty String too (by default) + opt = MAPPER.readValue("\"\"", OptionalLong.class); + assertNotNull(opt); + assertFalse(opt.isPresent()); + + OptionalLongBean bean = MAPPER.readValue(aposToQuotes("{'value':null}"), + OptionalLongBean.class); + assertNotNull(bean.value); + assertFalse(bean.value.isPresent()); + + bean = MAPPER.readValue(aposToQuotes("{'value':'19'}"), OptionalLongBean.class); + assertNotNull(bean.value); + assertEquals(19L, bean.value.getAsLong()); + } + + public void testOptionalLongSerializeFilter() throws Exception + { + ObjectMapper mapper = jsonMapperBuilder() + .changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_NULL)) + .build(); + assertEquals(aposToQuotes("{'value':123}"), + mapper.writeValueAsString(new OptionalLongBean(123L))); + // absent is not strictly null so + assertEquals(aposToQuotes("{'value':null}"), + mapper.writeValueAsString(new OptionalLongBean())); + + // however: + mapper = jsonMapperBuilder() + .changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_ABSENT)) + .build(); + assertEquals(aposToQuotes("{'value':456}"), + mapper.writeValueAsString(new OptionalLongBean(456L))); + assertEquals(aposToQuotes("{}"), + mapper.writeValueAsString(new OptionalLongBean())); + } + + /* + /********************************************************** + /* Test methods, OptionalDouble + /********************************************************** + */ + + public void testOptionalDoubleAbsent() throws Exception + { + assertFalse(MAPPER.readValue(MAPPER.writeValueAsBytes(OptionalInt.empty()), OptionalInt.class).isPresent()); + } + + public void testOptionalDoubleInArrayAbsent() throws Exception + { + OptionalDouble[] arr = MAPPER.readValue("[null]", OptionalDouble[].class); + assertEquals(1, arr.length); + assertNotNull(arr[0]); + assertFalse(arr[0].isPresent()); + } + + public void testOptionalDoublePresent() throws Exception + { + assertEquals(Double.MIN_VALUE, MAPPER.readValue(MAPPER.writeValueAsBytes(OptionalDouble.of(Double.MIN_VALUE)), OptionalDouble.class).getAsDouble()); + } + + public void testOptionalDoubleCoerceFromString() throws Exception + { + OptionalDouble opt = MAPPER.readValue(quote("0.25"), OptionalDouble.class); + assertEquals(0.25, opt.getAsDouble()); + + // should coerce from empty String too (by default) + opt = MAPPER.readValue("\"\"", OptionalDouble.class); + assertNotNull(opt); + assertFalse(opt.isPresent()); + + OptionalDoubleBean bean = MAPPER.readValue(aposToQuotes("{'value':null}"), + OptionalDoubleBean.class); + assertNotNull(bean.value); + assertFalse(bean.value.isPresent()); + + bean = MAPPER.readValue(aposToQuotes("{'value':'0.5'}"), OptionalDoubleBean.class); + assertNotNull(bean.value); + assertEquals(0.5, bean.value.getAsDouble()); + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalTest.java b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalTest.java new file mode 100644 index 0000000000..be23916126 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalTest.java @@ -0,0 +1,255 @@ +package com.fasterxml.jackson.databind.ext.jdk8; + +import java.io.IOException; +import java.util.*; +import java.util.concurrent.atomic.AtomicReference; + +import com.fasterxml.jackson.annotation.*; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer; +import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer; + +public class OptionalTest extends BaseMapTest +{ + private static final TypeReference> OPTIONAL_STRING_TYPE = new TypeReference>() {}; + private static final TypeReference> OPTIONAL_BEAN_TYPE = new TypeReference>() {}; + + public static class TestBean + { + public int foo; + public String bar; + + @JsonCreator + public TestBean(@JsonProperty("foo") int foo, @JsonProperty("bar") String bar) + { + this.foo = foo; + this.bar = bar; + } + + @Override + public boolean equals(Object obj) + { + if (obj.getClass() != getClass()) { + return false; + } + TestBean castObj = (TestBean) obj; + return castObj.foo == foo && Objects.equals(castObj.bar, bar); + } + + @Override + public int hashCode() { + return foo ^ bar.hashCode(); + } + } + + static class OptionalStringBean { + public Optional value; + + public OptionalStringBean() { } + OptionalStringBean(String str) { + value = Optional.ofNullable(str); + } + } + + // [datatype-jdk8#4] + static class Issue4Entity { + private final Optional data; + + @JsonCreator + public Issue4Entity(@JsonProperty("data") Optional data) { + this.data = Objects.requireNonNull(data, "data"); + } + + @JsonProperty ("data") + public Optional data() { + return data; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) { + return false; + } + Issue4Entity entity = (Issue4Entity) o; + return data.equals(entity.data); + } + } + + static class CaseChangingStringWrapper { + @JsonSerialize(contentUsing=UpperCasingSerializer.class) + @JsonDeserialize(contentUsing=LowerCasingDeserializer.class) + public Optional value; + + CaseChangingStringWrapper() { } + public CaseChangingStringWrapper(String s) { value = Optional.ofNullable(s); } + } + + @SuppressWarnings("serial") + public static class UpperCasingSerializer extends StdScalarSerializer + { + public UpperCasingSerializer() { super(String.class); } + + @Override + public void serialize(String value, JsonGenerator gen, + SerializerProvider provider) throws IOException { + gen.writeString(value.toUpperCase()); + } + } + + @SuppressWarnings("serial") + public static class LowerCasingDeserializer extends StdScalarDeserializer + { + public LowerCasingDeserializer() { super(String.class); } + + @Override + public String deserialize(JsonParser p, DeserializationContext ctxt) + throws IOException, JsonProcessingException { + return p.getText().toLowerCase(); + } + } + + private ObjectMapper MAPPER = newJsonMapper(); + + /* + /********************************************************** + /* Test methods + /********************************************************** + */ + + public void testStringAbsent() throws Exception + { + assertFalse(roundtrip(Optional.empty(), OPTIONAL_STRING_TYPE).isPresent()); + } + + public void testStringPresent() throws Exception + { + assertEquals("test", roundtrip(Optional.of("test"), OPTIONAL_STRING_TYPE).get()); + } + + public void testBeanAbsent() throws Exception + { + assertFalse(roundtrip(Optional.empty(), OPTIONAL_BEAN_TYPE).isPresent()); + } + + public void testBeanPresent() throws Exception + { + final TestBean bean = new TestBean(Integer.MAX_VALUE, "woopwoopwoopwoopwoop"); + assertEquals(bean, roundtrip(Optional.of(bean), OPTIONAL_BEAN_TYPE).get()); + } + + public void testBeanWithCreator() throws Exception + { + final Issue4Entity emptyEntity = new Issue4Entity(Optional.empty()); + final String json = MAPPER.writeValueAsString(emptyEntity); + + final Issue4Entity deserialisedEntity = MAPPER.readValue(json, Issue4Entity.class); + if (!deserialisedEntity.equals(emptyEntity)) { + throw new IOException("Entities not equal"); + } + } + + public void testOptionalStringInBean() throws Exception + { + OptionalStringBean bean = MAPPER.readValue("{\"value\":\"xyz\"}", OptionalStringBean.class); + assertNotNull(bean.value); + assertEquals("xyz", bean.value.get()); + } + + // To support [datatype-jdk8#8] + public void testExcludeIfOptionalAbsent() throws Exception + { + ObjectMapper mapper = jsonMapperBuilder() + .changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_NULL)) + .build(); + assertEquals(aposToQuotes("{'value':'foo'}"), + mapper.writeValueAsString(new OptionalStringBean("foo"))); + // absent is not strictly null so + assertEquals(aposToQuotes("{'value':null}"), + mapper.writeValueAsString(new OptionalStringBean(null))); + + // however: + mapper = jsonMapperBuilder() + .changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_ABSENT)) + .build(); + assertEquals(aposToQuotes("{'value':'foo'}"), + mapper.writeValueAsString(new OptionalStringBean("foo"))); + assertEquals(aposToQuotes("{}"), + mapper.writeValueAsString(new OptionalStringBean(null))); + } + + public void testWithCustomDeserializer() throws Exception + { + CaseChangingStringWrapper w = MAPPER.readValue(aposToQuotes("{'value':'FoobaR'}"), + CaseChangingStringWrapper.class); + assertEquals("foobar", w.value.get()); + } + + // [modules-java8#36] + public void testWithCustomDeserializerIfOptionalAbsent() throws Exception + { + // 10-Aug-2017, tatu: Actually this is not true: missing value does not trigger + // specific handling + /* + assertEquals(Optional.empty(), MAPPER.readValue("{}", + CaseChangingStringWrapper.class).value); + */ + + assertEquals(Optional.empty(), MAPPER.readValue(aposToQuotes("{'value':null}"), + CaseChangingStringWrapper.class).value); + } + + public void testCustomSerializer() throws Exception + { + final String VALUE = "fooBAR"; + String json = MAPPER.writeValueAsString(new CaseChangingStringWrapper(VALUE)); + assertEquals(json, aposToQuotes("{'value':'FOOBAR'}")); + } + + public void testCustomSerializerIfOptionalAbsent() throws Exception + { + ObjectMapper mapper = jsonMapperBuilder() + .changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_NULL)) + .build(); + assertEquals(aposToQuotes("{'value':'FOO'}"), + mapper.writeValueAsString(new CaseChangingStringWrapper("foo"))); + // absent is not strictly null so + assertEquals(aposToQuotes("{'value':null}"), + mapper.writeValueAsString(new CaseChangingStringWrapper(null))); + + // however: + mapper = jsonMapperBuilder() + .changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_ABSENT)) + .build(); + assertEquals(aposToQuotes("{'value':'FOO'}"), + mapper.writeValueAsString(new CaseChangingStringWrapper("foo"))); + assertEquals(aposToQuotes("{}"), + mapper.writeValueAsString(new CaseChangingStringWrapper(null))); + } + + // [modules-java8#33]: Verify against regression... + public void testOtherRefSerializers() throws Exception + { + String json = MAPPER.writeValueAsString(new AtomicReference("foo")); + assertEquals(quote("foo"), json); + } + + /* + /********************************************************** + /* Helper methods + /********************************************************** + */ + + private Optional roundtrip(Optional obj, TypeReference> type) throws IOException + { + return MAPPER.readValue(MAPPER.writeValueAsBytes(obj), type); + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalUnwrappedTest.java b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalUnwrappedTest.java new file mode 100644 index 0000000000..14c32b3c76 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalUnwrappedTest.java @@ -0,0 +1,89 @@ +package com.fasterxml.jackson.databind.ext.jdk8; + +import java.util.Optional; +import java.util.concurrent.atomic.AtomicReference; + +import com.fasterxml.jackson.annotation.JsonUnwrapped; + +import com.fasterxml.jackson.core.json.JsonFactory; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; +import com.fasterxml.jackson.databind.ser.BeanSerializerFactory; +import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider; +import com.fasterxml.jackson.databind.ser.SerializerCache; + +public class OptionalUnwrappedTest extends BaseMapTest +{ + static class Child { + public String name = "Bob"; + } + + static class Parent { + private Child child = new Child(); + + @JsonUnwrapped + public Child getChild() { + return child; + } + } + + static class OptionalParent { + @JsonUnwrapped(prefix = "XX.") + public Optional child = Optional.of(new Child()); + } + + static class Bean { + public String id; + @JsonUnwrapped(prefix="child") + public Optional bean2; + + public Bean(String id, Optional bean2) { + this.id = id; + this.bean2 = bean2; + } + } + + static class Bean2 { + public String name; + } + + public void testUntypedWithOptionalsNotNulls() throws Exception + { + final ObjectMapper mapper = newJsonMapper(); + String jsonExp = aposToQuotes("{'XX.name':'Bob'}"); + String jsonAct = mapper.writeValueAsString(new OptionalParent()); + assertEquals(jsonExp, jsonAct); + } + + // for [datatype-jdk8#20] + public void testShouldSerializeUnwrappedOptional() throws Exception { + final ObjectMapper mapper = newJsonMapper(); + + assertEquals("{\"id\":\"foo\"}", + mapper.writeValueAsString(new Bean("foo", Optional.empty()))); + } + + // for [datatype-jdk8#26] + public void testPropogatePrefixToSchema() throws Exception { + final ObjectMapper mapper = newJsonMapper(); + + final AtomicReference propertyName = new AtomicReference<>(); + mapper.acceptJsonFormatVisitor(OptionalParent.class, new JsonFormatVisitorWrapper.Base( + new DefaultSerializerProvider.Impl(new JsonFactory(), + mapper.serializationConfig(), null, + BeanSerializerFactory.instance, new SerializerCache())) { + @Override + public JsonObjectFormatVisitor expectObjectFormat(JavaType type) { + return new JsonObjectFormatVisitor.Base(getProvider()) { + @Override + public void optionalProperty(BeanProperty prop) { + propertyName.set(prop.getName()); + } + }; + } + }); + assertEquals("XX.name", propertyName.get()); + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalWithEmptyTest.java b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalWithEmptyTest.java new file mode 100644 index 0000000000..3735bbec29 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalWithEmptyTest.java @@ -0,0 +1,38 @@ +package com.fasterxml.jackson.databind.ext.jdk8; + +import java.util.Optional; + +import com.fasterxml.jackson.core.type.TypeReference; + +import com.fasterxml.jackson.databind.*; + +public class OptionalWithEmptyTest extends BaseMapTest +{ + private final ObjectMapper MAPPER = newJsonMapper(); + + static class BooleanBean { + public Optional value; + + public BooleanBean() { } + public BooleanBean(Boolean b) { + value = Optional.ofNullable(b); + } + } + + public void testOptionalFromEmpty() throws Exception { + Optional value = MAPPER.readValue(quote(""), new TypeReference>() {}); + assertEquals(false, value.isPresent()); + } + + // for [datatype-jdk8#23] + public void testBooleanWithEmpty() throws Exception + { + // and looks like a special, somewhat non-conforming case is what a user had + // issues with + BooleanBean b = MAPPER.readValue(aposToQuotes("{'value':''}"), BooleanBean.class); + assertNotNull(b.value); + + assertEquals(false, b.value.isPresent()); + } + +} diff --git a/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalnclusionTest.java b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalnclusionTest.java new file mode 100644 index 0000000000..5123733a08 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/OptionalnclusionTest.java @@ -0,0 +1,128 @@ +package com.fasterxml.jackson.databind.ext.jdk8; + +import java.util.Optional; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.JsonInclude.Include; + +import com.fasterxml.jackson.databind.*; + +public class OptionalnclusionTest extends BaseMapTest +{ + @JsonAutoDetect(fieldVisibility=Visibility.ANY) + public static final class OptionalData { + public Optional myString = Optional.empty(); + } + + // for [datatype-jdk8#18] + static class OptionalNonEmptyStringBean { + @JsonInclude(value=Include.NON_EMPTY, content=Include.NON_EMPTY) + public Optional value; + + public OptionalNonEmptyStringBean() { } + OptionalNonEmptyStringBean(String str) { + value = Optional.ofNullable(str); + } + } + + public static final class OptionalGenericData { + public Optional myData; + public static OptionalGenericData construct(T data) { + OptionalGenericData ret = new OptionalGenericData(); + ret.myData = Optional.of(data); + return ret; + } + } + + /* + /********************************************************** + /* Test methods + /********************************************************** + */ + + private final ObjectMapper MAPPER = newJsonMapper(); + + public void testSerOptNonEmpty() throws Exception + { + OptionalData data = new OptionalData(); + data.myString = null; + String value = jsonMapperBuilder() + .changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_EMPTY)) + .build() + .writeValueAsString(data); + assertEquals("{}", value); + } + + public void testSerOptNonDefault() throws Exception + { + OptionalData data = new OptionalData(); + data.myString = null; + String value = jsonMapperBuilder() + .changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_DEFAULT)) + .build() + .writeValueAsString(data); + assertEquals("{}", value); + } + + public void testSerOptNonAbsent() throws Exception + { + OptionalData data = new OptionalData(); + data.myString = null; + String value = jsonMapperBuilder() + .changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_ABSENT)) + .build() + .writeValueAsString(data); + assertEquals("{}", value); + } + + public void testExcludeEmptyStringViaOptional() throws Exception + { + String json = MAPPER.writeValueAsString(new OptionalNonEmptyStringBean("x")); + assertEquals("{\"value\":\"x\"}", json); + json = MAPPER.writeValueAsString(new OptionalNonEmptyStringBean(null)); + assertEquals("{}", json); + json = MAPPER.writeValueAsString(new OptionalNonEmptyStringBean("")); + assertEquals("{}", json); + } + + public void testSerPropInclusionAlways() throws Exception + { + ObjectMapper mapper = jsonMapperBuilder() + .changeDefaultPropertyInclusion(incl -> + JsonInclude.Value.construct(JsonInclude.Include.NON_ABSENT, JsonInclude.Include.ALWAYS)) + .build(); + assertEquals("{\"myData\":true}", + mapper.writeValueAsString(OptionalGenericData.construct(Boolean.TRUE))); + } + + public void testSerPropInclusionNonNull() throws Exception + { + ObjectMapper mapper = jsonMapperBuilder().changeDefaultPropertyInclusion( + i -> JsonInclude.Value.construct(JsonInclude.Include.NON_ABSENT, JsonInclude.Include.NON_NULL)) + .build(); + assertEquals("{\"myData\":true}", + mapper.writeValueAsString(OptionalGenericData.construct(Boolean.TRUE))); + } + + public void testSerPropInclusionNonAbsent() throws Exception + { + ObjectMapper mapper = jsonMapperBuilder() + .changeDefaultPropertyInclusion( + i -> JsonInclude.Value.construct(JsonInclude.Include.NON_ABSENT, JsonInclude.Include.NON_ABSENT)) + .build(); + assertEquals("{\"myData\":true}", + mapper.writeValueAsString(OptionalGenericData.construct(Boolean.TRUE))); + } + + public void testSerPropInclusionNonEmpty() throws Exception + { + ObjectMapper mapper = jsonMapperBuilder() + .changeDefaultPropertyInclusion( + i -> JsonInclude.Value.construct(JsonInclude.Include.NON_ABSENT, JsonInclude.Include.NON_EMPTY)) + .build(); + assertEquals("{\"myData\":true}", + mapper.writeValueAsString(OptionalGenericData.construct(Boolean.TRUE))); + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/PolymoprhicOptionalTest.java b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/PolymoprhicOptionalTest.java new file mode 100644 index 0000000000..b659252c8f --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/PolymoprhicOptionalTest.java @@ -0,0 +1,40 @@ +package com.fasterxml.jackson.databind.ext.jdk8; + +import java.util.Optional; + +import com.fasterxml.jackson.annotation.*; +import com.fasterxml.jackson.annotation.JsonTypeInfo.As; + +import com.fasterxml.jackson.databind.*; + +public class PolymoprhicOptionalTest extends BaseMapTest +{ + // For [datatype-jdk8#14] + public static class Container { + public Optional contained; + } + + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = As.PROPERTY) + @JsonSubTypes({ + @JsonSubTypes.Type(name = "ContainedImpl", value = ContainedImpl.class), + }) + public static interface Contained { } + + public static class ContainedImpl implements Contained { } + + private final ObjectMapper MAPPER = newJsonMapper(); + + // [datatype-jdk8#14] + public void testPolymorphic14() throws Exception + { + final Container dto = new Container(); + dto.contained = Optional.of(new ContainedImpl()); + + final String json = MAPPER.writeValueAsString(dto); + + final Container fromJson = MAPPER.readValue(json, Container.class); + assertNotNull(fromJson.contained); + assertTrue(fromJson.contained.isPresent()); + assertSame(ContainedImpl.class, fromJson.contained.get().getClass()); + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/SchemaVisitorTest.java b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/SchemaVisitorTest.java new file mode 100644 index 0000000000..021ee1a0bc --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/SchemaVisitorTest.java @@ -0,0 +1,72 @@ +package com.fasterxml.jackson.databind.ext.jdk8; + +import java.util.*; +import java.util.concurrent.atomic.AtomicReference; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonParser.NumberType; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsonFormatVisitors.*; + +// trivial tests visitor used (mostly) for JSON Schema generation +public class SchemaVisitorTest extends BaseMapTest +{ + private final ObjectMapper MAPPER = newJsonMapper(); + + // for [datatype-jdk8#25] + public void testOptionalInteger() throws Exception + { + final AtomicReference result = new AtomicReference<>(); + MAPPER.acceptJsonFormatVisitor(OptionalInt.class, + new JsonFormatVisitorWrapper.Base() { + @Override + public JsonIntegerFormatVisitor expectIntegerFormat(JavaType type) { + return new JsonIntegerFormatVisitor.Base() { + @Override + public void numberType(NumberType t) { + result.set(t); + } + }; + } + }); + assertEquals(JsonParser.NumberType.INT, result.get()); + } + + // for [datatype-jdk8#25] + public void testOptionalLong() throws Exception + { + final AtomicReference result = new AtomicReference<>(); + MAPPER.acceptJsonFormatVisitor(OptionalLong.class, + new JsonFormatVisitorWrapper.Base() { + @Override + public JsonIntegerFormatVisitor expectIntegerFormat(JavaType type) { + return new JsonIntegerFormatVisitor.Base() { + @Override + public void numberType(NumberType t) { + result.set(t); + } + }; + } + }); + assertEquals(JsonParser.NumberType.LONG, result.get()); + } + + // for [datatype-jdk8#25] + public void testOptionalDouble() throws Exception + { + final AtomicReference result = new AtomicReference<>(); + MAPPER.acceptJsonFormatVisitor(OptionalDouble.class, + new JsonFormatVisitorWrapper.Base() { + @Override + public JsonNumberFormatVisitor expectNumberFormat(JavaType type) { + return new JsonNumberFormatVisitor.Base() { + @Override + public void numberType(NumberType t) { + result.set(t); + } + }; + } + }); + assertEquals(JsonParser.NumberType.DOUBLE, result.get()); + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/StreamSerializerTest.java b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/StreamSerializerTest.java new file mode 100644 index 0000000000..19d19975eb --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/StreamSerializerTest.java @@ -0,0 +1,202 @@ +package com.fasterxml.jackson.databind.ext.jdk8; + +import static org.junit.Assert.*; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.*; +import java.util.stream.Stream; + +import org.junit.Test; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.type.TypeReference; + +public class StreamSerializerTest extends StreamTestBase +{ + static class TestBean + { + public int foo; + public String bar; + + @JsonCreator + public TestBean(@JsonProperty("foo") int foo, @JsonProperty("bar") String bar) + { + this.foo = foo; + this.bar = bar; + } + + @Override + public boolean equals(Object obj) + { + if (obj.getClass() != getClass()) { + return false; + } + TestBean castObj = (TestBean) obj; + return castObj.foo == foo && Objects.equals(castObj.bar, bar); + } + + @Override + public int hashCode() { + return foo ^ bar.hashCode(); + } + } + TestBean[] empty = {}; + + TestBean testBean1 = new TestBean(1, "one"); + + TestBean testBean2 = new TestBean(2, "two"); + + TestBean[] single = { testBean1 }; + + TestBean[] multipleValues = { testBean1, testBean2 }; + + + @Test + public void testEmptyStream() throws Exception { + assertArrayEquals(empty, this.roundTrip(Stream.empty(), TestBean[].class)); + } + + @Test + public void testNestedStreamEmptyElement() throws Exception { + final List>> expected = Arrays.asList(new NestedStream<>(new ArrayList<>())); + final Collection>> actual = roundTrip(expected.stream(), new TypeReference>>>() {}); + assertEquals(expected,actual); + } + + @Test + public void testSingleElement() throws Exception { + assertArrayEquals(single, roundTrip(Stream.of(single), TestBean[].class)); + } + + @Test + public void testNestedStreamSingleElement() throws Exception { + final List>> nestedStream = Arrays.asList(new NestedStream<>(Arrays.asList("foo"))); + final Collection>> roundTrip = roundTrip(nestedStream.stream(), new TypeReference>>>() {}); + assertEquals(roundTrip,nestedStream); + } + + @Test + public void testMultiElements() throws Exception { + assertArrayEquals(multipleValues, roundTrip(Stream.of(multipleValues), TestBean[].class)); + } + + @Test + public void testNestedStreamMultiElements() throws Exception { + final List>> expected = Arrays.asList(new NestedStream<>(Arrays.asList("foo")),new NestedStream<>(Arrays.asList("bar"))); + final Collection>> actual = roundTrip(expected.stream(), new TypeReference>>>() {}); + assertEquals(expected,actual); + } + + @Test + public void testStreamCloses() throws Exception { + assertClosesOnSuccess(Stream.of(multipleValues), stream -> roundTrip(stream, TestBean[].class)); + } + + @Test + public void testStreamClosesOnRuntimeException() throws Exception { + String exceptionMessage = "Stream peek threw"; + assertClosesOnRuntimeException(exceptionMessage, stream -> roundTrip(stream, TestBean[].class), + Stream.of(multipleValues) + .peek(e -> { + throw new RuntimeException(exceptionMessage); + })); + } + + @Test + public void testStreamClosesOnSneakyIOException() throws Exception { + String exceptionMessage = "Stream peek threw"; + assertClosesOnIoException(exceptionMessage, stream -> roundTrip(stream, TestBean[].class), + Stream.of(multipleValues) + .peek(e -> { + sneakyThrow(new IOException(exceptionMessage)); + })); + } + + @Test + public void testStreamClosesOnWrappedIoException() throws Exception { + final String exceptionMessage = "Stream peek threw"; + + assertClosesOnWrappedIoException(exceptionMessage, stream -> roundTrip(stream, TestBean[].class), + Stream.of(multipleValues) + .peek(e -> { + throw new UncheckedIOException(new IOException(exceptionMessage)); + })); + } + + private R[] roundTrip(Stream stream, Class clazz) { + try { + String json = objectMapper.writeValueAsString(stream); + return objectMapper.readValue(json, clazz); + } catch (IOException e) { + sneakyThrow(e); + return null; + } + } + + private R roundTrip(Stream stream, TypeReference tr) { + try { + return objectMapper.readValue(objectMapper.writeValueAsString(stream), tr); + } catch (IOException e) { + sneakyThrow(e); + return null; + } + } + + /** + * Test class to verify nested streams are handled, even though, in general, this is likely a risky thing to do. + * + * @param the type of the container contents + * @param the container type. + */ + static class NestedStream> { + C values; + + NestedStream(){}; + + public NestedStream(C values) { + super(); + this.values = values; + } + + public Stream getValues(){ + return values.stream(); + } + + protected void setValues(C values) { + this.values = values; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((values == null) ? 0 : values.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + @SuppressWarnings("rawtypes") + NestedStream other = (NestedStream) obj; + if (values == null) { + if (other.values != null) + return false; + } else if (!values.equals(other.values)) + return false; + return true; + } + + @Override + public String toString() { + return "NestedStream [values=" + this.values + "]"; + } + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/StreamTestBase.java b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/StreamTestBase.java new file mode 100644 index 0000000000..565aca465f --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/StreamTestBase.java @@ -0,0 +1,110 @@ +package com.fasterxml.jackson.databind.ext.jdk8; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; +import java.util.stream.BaseStream; + +import org.hamcrest.CustomMatcher; +import org.hamcrest.Description; +import org.hamcrest.core.AllOf; +import org.hamcrest.core.Is; +import org.junit.Rule; +import org.junit.rules.ExpectedException; + +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import static org.junit.Assert.assertTrue; + +public abstract class StreamTestBase + // 19-Sep-2017, tatu: For some reason doing this will break `ExpectedException` rule. + // Typical auto-magic that I hate -- but this code came as contribution. + //extends com.fasterxml.jackson.databind.BaseMapTest +{ + @Rule + public final ExpectedException expectedException = ExpectedException.none(); + + protected final ObjectMapper objectMapper = new ObjectMapper(); + + /** + * Throws the supplied checked exception without enforcing checking. + * + * @param t the throwable to sneaky throw. + */ + static final void sneakyThrow(final Throwable t) { + castAndThrow(t); + } + + /** + * Uses erasure to throw checked exceptions as unchecked. + *

    Called by {@link #sneakyThrow(Throwable)}

    + */ + @SuppressWarnings("unchecked") + static void castAndThrow(final Throwable t) throws T { + throw (T) t; + } + + > void assertClosesOnSuccess(S baseStream, Consumer roundTrip) { + AtomicBoolean closed = new AtomicBoolean(); + roundTrip.accept(baseStream.onClose(() -> closed.set(true))); + assertTrue(closed.get()); + } + + > void assertClosesOnRuntimeException(String exceptionMessage, + Consumer roundTrip, S baseStream) { + AtomicBoolean closed = new AtomicBoolean(); + initExpectedException(RuntimeException.class, exceptionMessage,closed); + roundTrip.accept(baseStream.onClose(() -> closed.set(true))); + } + + > void assertClosesOnIoException(String exceptionMessage, Consumer roundTrip, + S baseStream) { + AtomicBoolean closed = new AtomicBoolean(); + initExpectedExceptionIoException(exceptionMessage,closed); + roundTrip.accept(baseStream.onClose(() -> closed.set(true))); + } + + > void assertClosesOnWrappedIoException(String exceptionMessage, + Consumer roundTrip, S baseStream) { + AtomicBoolean closed = new AtomicBoolean(); + final String actualMessage = "Unexpected IOException (of type java.io.IOException): " + exceptionMessage; + initExpectedExceptionIoException(actualMessage,closed); + roundTrip.accept(baseStream.onClose(() -> closed.set(true))); + } + + void initExpectedExceptionIoException(final String exceptionMessage, AtomicBoolean closed) { + this.expectedException.expect(new IsClosedMatcher(closed)); + this.expectedException.expect(Is.isA(IOException.class)); + this.expectedException.expectMessage(exceptionMessage); + } + + void initExpectedException(Class cause, final String exceptionMessage, AtomicBoolean closed) { + this.expectedException.expect(AllOf.allOf(Is.isA(JsonMappingException.class), new IsClosedMatcher(closed))); + this.expectedException.expect(Is.isA(JsonMappingException.class)); + this.expectedException.expectCause(Is.isA(cause)); + this.expectedException.expectMessage(exceptionMessage); + } + + /** + * Matcher that matches when the StreamTestBase.closed() value is set to true. + */ + static class IsClosedMatcher extends CustomMatcher { + final AtomicBoolean closed; + + public IsClosedMatcher(AtomicBoolean closed) { + super("Check flag closed"); + this.closed = closed; + } + + @Override + public void describeMismatch(Object item, Description description) { + description.appendText("The onClose method was not called"); + } + + @Override + public boolean matches(Object item) { + return closed.get(); + } + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/TestOptionalWithPolymorphic.java b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/TestOptionalWithPolymorphic.java new file mode 100644 index 0000000000..da086045e5 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/ext/jdk8/TestOptionalWithPolymorphic.java @@ -0,0 +1,128 @@ +package com.fasterxml.jackson.databind.ext.jdk8; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Optional; + +import com.fasterxml.jackson.annotation.*; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; + +public class TestOptionalWithPolymorphic extends BaseMapTest +{ + static class ContainerA { + @JsonProperty private Optional name = Optional.empty(); + @JsonProperty private Optional strategy = Optional.empty(); + } + + static class ContainerB { + @JsonProperty private Optional name = Optional.empty(); + @JsonProperty private Strategy strategy = null; + } + + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") + @JsonSubTypes({ @JsonSubTypes.Type(name = "Foo", value = Foo.class), + @JsonSubTypes.Type(name = "Bar", value = Bar.class), + @JsonSubTypes.Type(name = "Baz", value = Baz.class) }) + interface Strategy { } + + static class Foo implements Strategy { + @JsonProperty private final int foo; + + @JsonCreator + Foo(@JsonProperty("foo") int foo) { + this.foo = foo; + } + } + + static class Bar implements Strategy { + @JsonProperty private final boolean bar; + + @JsonCreator + Bar(@JsonProperty("bar") boolean bar) { + this.bar = bar; + } + } + + static class Baz implements Strategy { + @JsonProperty private final String baz; + + @JsonCreator + Baz(@JsonProperty("baz") String baz) { + this.baz = baz; + } + } + + static class AbstractOptional { + @JsonDeserialize(contentAs=Integer.class) + public Optional value; + } + + /* + /********************************************************** + /* Test methods + /********************************************************** + */ + + final ObjectMapper MAPPER = newJsonMapper(); + + public void testOptionalMapsFoo() throws Exception { + + Map foo = new LinkedHashMap<>(); + Map loop = new LinkedHashMap<>(); + loop.put("type", "Foo"); + loop.put("foo", 42); + + foo.put("name", "foo strategy"); + foo.put("strategy", loop); + + _test(MAPPER, foo); + } + + public void testOptionalMapsBar() throws Exception { + + Map bar = new LinkedHashMap<>(); + Map loop = new LinkedHashMap<>(); + loop.put("type", "Bar"); + loop.put("bar", true); + + bar.put("name", "bar strategy"); + bar.put("strategy", loop); + + _test(MAPPER, bar); + } + + public void testOptionalMapsBaz() throws Exception { + Map baz = new LinkedHashMap<>(); + Map loop = new LinkedHashMap<>(); + loop.put("type", "Baz"); + loop.put("baz", "hello world!"); + + baz.put("name", "bar strategy"); + baz.put("strategy", loop); + + _test(MAPPER, baz); + } + + public void testOptionalWithTypeAnnotation13() throws Exception + { + AbstractOptional result = MAPPER.readValue("{\"value\" : 5}", + AbstractOptional.class); + assertNotNull(result); + assertNotNull(result.value); + Object ob = result.value.get(); + assertEquals(Integer.class, ob.getClass()); + assertEquals(Integer.valueOf(5), ob); + } + + private void _test(ObjectMapper m, Map map) throws Exception { + String json = m.writeValueAsString(map); + + ContainerA objA = m.readValue(json, ContainerA.class); + assertNotNull(objA); + + ContainerB objB = m.readValue(json, ContainerB.class); + assertNotNull(objB); + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/format/BooleanFormatTest.java b/src/test/java/com/fasterxml/jackson/databind/format/BooleanFormatTest.java index 5794cf2ca3..d9dfc550c6 100644 --- a/src/test/java/com/fasterxml/jackson/databind/format/BooleanFormatTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/format/BooleanFormatTest.java @@ -50,15 +50,16 @@ public AltBoolean() { } /********************************************************** */ - private final static ObjectMapper MAPPER = newObjectMapper(); + private final static ObjectMapper MAPPER = newJsonMapper(); public void testShapeViaDefaults() throws Exception { assertEquals(aposToQuotes("{'b':true}"), MAPPER.writeValueAsString(new BooleanWrapper(true))); - ObjectMapper m = newObjectMapper(); - m.configOverride(Boolean.class) - .setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.NUMBER)); + ObjectMapper m = jsonMapperBuilder() + .withConfigOverride(Boolean.class, + o -> o.setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.NUMBER))) + .build(); assertEquals(aposToQuotes("{'b':1}"), m.writeValueAsString(new BooleanWrapper(true))); } diff --git a/src/test/java/com/fasterxml/jackson/databind/format/CollectionFormatShapeTest.java b/src/test/java/com/fasterxml/jackson/databind/format/CollectionFormatShapeTest.java index 6eea8fefa8..dd2e9f4186 100644 --- a/src/test/java/com/fasterxml/jackson/databind/format/CollectionFormatShapeTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/format/CollectionFormatShapeTest.java @@ -12,7 +12,7 @@ public class CollectionFormatShapeTest extends BaseMapTest { // [databind#40]: Allow serialization 'as POJO' (resulting in JSON Object) @JsonPropertyOrder({ "size", "value" }) - @JsonFormat(shape=Shape.OBJECT) + @JsonFormat(shape=Shape.POJO) @JsonIgnoreProperties({ "empty" }) // from 'isEmpty()' static class CollectionAsPOJO extends ArrayList @@ -40,7 +40,7 @@ public void setSize(int i) { } /********************************************************** */ - private final static ObjectMapper MAPPER = newObjectMapper(); + private final static ObjectMapper MAPPER = newJsonMapper(); public void testListAsObjectRoundtrip() throws Exception { diff --git a/src/test/java/com/fasterxml/jackson/databind/format/DateFormatTest.java b/src/test/java/com/fasterxml/jackson/databind/format/DateFormatTest.java index f94bd02478..e952a3e877 100644 --- a/src/test/java/com/fasterxml/jackson/databind/format/DateFormatTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/format/DateFormatTest.java @@ -18,9 +18,10 @@ public DateWrapper() { } public void testTypeDefaults() throws Exception { - ObjectMapper mapper = newObjectMapper(); - mapper.configOverride(Date.class) - .setFormat(JsonFormat.Value.forPattern("yyyy.dd.MM")); + ObjectMapper mapper = jsonMapperBuilder() + .withConfigOverride(Date.class, + o -> o.setFormat(JsonFormat.Value.forPattern("yyyy.dd.MM"))) + .build(); // First serialize, should result in this (in UTC): String json = mapper.writeValueAsString(new DateWrapper(0L)); assertEquals(aposToQuotes("{'value':'1970.01.01'}"), json); diff --git a/src/test/java/com/fasterxml/jackson/databind/format/EnumFormatShapeTest.java b/src/test/java/com/fasterxml/jackson/databind/format/EnumFormatShapeTest.java index 4e11a8ed04..839c224246 100644 --- a/src/test/java/com/fasterxml/jackson/databind/format/EnumFormatShapeTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/format/EnumFormatShapeTest.java @@ -12,7 +12,7 @@ public class EnumFormatShapeTest extends BaseMapTest { - @JsonFormat(shape=JsonFormat.Shape.OBJECT) + @JsonFormat(shape=JsonFormat.Shape.POJO) static enum PoNUM { A("a1"), B("b2"); @@ -76,7 +76,7 @@ static class ColorWrapper { /********************************************************** */ - private final ObjectMapper MAPPER = newObjectMapper(); + private final ObjectMapper MAPPER = newJsonMapper(); // Tests for JsonFormat.shape diff --git a/src/test/java/com/fasterxml/jackson/databind/format/MapEntryFormatTest.java b/src/test/java/com/fasterxml/jackson/databind/format/MapEntryFormatTest.java index ce875a517e..11604ca686 100644 --- a/src/test/java/com/fasterxml/jackson/databind/format/MapEntryFormatTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/format/MapEntryFormatTest.java @@ -22,7 +22,7 @@ public BeanWithMapEntry(String key, String value) { } } - @JsonFormat(shape=JsonFormat.Shape.OBJECT) + @JsonFormat(shape=JsonFormat.Shape.POJO) static class MapEntryAsObject implements Map.Entry { protected String key, value; @@ -103,7 +103,7 @@ public EmptyEntryWrapper(String key, String value) { /********************************************************** */ - private final ObjectMapper MAPPER = newObjectMapper(); + private final ObjectMapper MAPPER = newJsonMapper(); public void testInclusion() throws Exception { @@ -169,9 +169,10 @@ public void testAsObjectRoundtrip() throws Exception // [databind#1895] public void testDefaultShapeOverride() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.configOverride(Map.Entry.class) - .setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.OBJECT)); + ObjectMapper mapper = jsonMapperBuilder() + .withConfigOverride(Map.Entry.class, + o -> o.setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.POJO))) + .build(); Map.Entry input = new BeanWithMapEntry("foo", "bar").entry; assertEquals(aposToQuotes("{'key':'foo','value':'bar'}"), mapper.writeValueAsString(input)); diff --git a/src/test/java/com/fasterxml/jackson/databind/format/MapFormatShapeTest.java b/src/test/java/com/fasterxml/jackson/databind/format/MapFormatShapeTest.java index a475d405aa..beecf9f943 100644 --- a/src/test/java/com/fasterxml/jackson/databind/format/MapFormatShapeTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/format/MapFormatShapeTest.java @@ -20,7 +20,7 @@ static class Map476Base extends LinkedHashMap { public int extra = 13; } - @JsonFormat(shape=JsonFormat.Shape.OBJECT) + @JsonFormat(shape=JsonFormat.Shape.POJO) static class Map476AsPOJO extends Map476Base { } @JsonPropertyOrder({ "a", "b", "c" }) @@ -29,7 +29,7 @@ static class Bean476Container { public Map476AsPOJO a; public Map476Base b; - @JsonFormat(shape=JsonFormat.Shape.OBJECT) + @JsonFormat(shape=JsonFormat.Shape.POJO) public Map476Base c; public Bean476Container(int forA, int forB, int forC) { @@ -60,7 +60,7 @@ public Bean476Override(int value) { } // from [databind#1540] - @JsonFormat(shape = JsonFormat.Shape.OBJECT) + @JsonFormat(shape = JsonFormat.Shape.POJO) @JsonPropertyOrder({ "property", "map" }) static class Map1540Implementation implements Map { public int property; @@ -153,24 +153,6 @@ public void testSerializeAsPOJOViaClass() throws Exception result); } - // Can't yet use per-property overrides at all, see [databind#1419] - - /* - public void testSerializeAsPOJOViaProperty() throws Exception - { - String result = MAPPER.writeValueAsString(new Bean476Container(1,0,3)); - assertEquals(aposToQuotes("{'a':{'extra':13,'empty':false},'c':{'empty':false,'value':3}}"), - result); - } - - public void testSerializeNaturalViaOverride() throws Exception - { - String result = MAPPER.writeValueAsString(new Bean476Override(123)); - assertEquals(aposToQuotes("{'stuff':{'value':123}}"), - result); - } - */ - /* /********************************************************** /* Test methods, deserialization/roundtrip diff --git a/src/test/java/com/fasterxml/jackson/databind/interop/DeprecatedTypeHandling1102Test.java b/src/test/java/com/fasterxml/jackson/databind/interop/DeprecatedTypeHandling1102Test.java deleted file mode 100644 index 1a8fefb798..0000000000 --- a/src/test/java/com/fasterxml/jackson/databind/interop/DeprecatedTypeHandling1102Test.java +++ /dev/null @@ -1,109 +0,0 @@ -package com.fasterxml.jackson.databind.interop; - -import java.util.*; - -import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.type.CollectionType; -import com.fasterxml.jackson.databind.type.MapType; -import com.fasterxml.jackson.databind.type.SimpleType; -import com.fasterxml.jackson.databind.type.TypeFactory; - -/** - * Set of tests to ensure that changes between 2.6 and 2.7 can - * be handled somewhat gracefully. - */ -public class DeprecatedTypeHandling1102Test extends BaseMapTest -{ - static class Point { - public int x; - - int _y; - - public void setY(int y0) { _y = y0; } - public int getY() { return _y; } - } - - static class Point3D extends Point { - public int z; - } - - final ObjectMapper MAPPER = objectMapper(); - - @SuppressWarnings("deprecation") - public void testSimplePOJOType() throws Exception - { - JavaType elem = SimpleType.construct(Point.class); - - Point p = MAPPER.readValue(aposToQuotes("{'x':1,'y':2}"), elem); - assertNotNull(p); - assertEquals(1, p.x); - assertEquals(2, p.getY()); - } - - @SuppressWarnings("deprecation") - public void testPOJOSubType() throws Exception - { - JavaType elem = SimpleType.construct(Point3D.class); - - Point3D p = MAPPER.readValue(aposToQuotes("{'x':1,'z':3,'y':2}"), elem); - assertNotNull(p); - assertEquals(1, p.x); - assertEquals(2, p.getY()); - assertEquals(3, p.z); - } - - @SuppressWarnings("deprecation") - public void testExplicitCollectionType() throws Exception - { - JavaType elem = SimpleType.construct(Point.class); - JavaType t = CollectionType.construct(List.class, elem); - - final String json = aposToQuotes("[ {'x':1,'y':2}, {'x':3,'y':6 }]"); - - List l = MAPPER.readValue(json, t); - assertNotNull(l); - assertEquals(2, l.size()); - Object ob = l.get(0); - assertEquals(Point.class, ob.getClass()); - Point p = (Point) ob; - assertEquals(1, p.x); - assertEquals(2, p.getY()); - } - - @SuppressWarnings("deprecation") - public void testExplicitMapType() throws Exception - { - JavaType key = SimpleType.construct(String.class); - JavaType elem = SimpleType.construct(Point.class); - JavaType t = MapType.construct(Map.class, key, elem); - - final String json = aposToQuotes("{'x':{'x':3,'y':5}}"); - - Map m = MAPPER.readValue(json, t); - assertNotNull(m); - assertEquals(1, m.size()); - Object ob = m.values().iterator().next(); - assertEquals(Point.class, ob.getClass()); - Point p = (Point) ob; - assertEquals(3, p.x); - assertEquals(5, p.getY()); - } - - @SuppressWarnings("deprecation") - public void testDeprecatedTypeResolution() throws Exception - { - TypeFactory tf = MAPPER.getTypeFactory(); - - // first, with real (if irrelevant) context - JavaType t = tf.constructType(Point.class, getClass()); - assertEquals(Point.class, t.getRawClass()); - - // and then missing context - JavaType t2 = tf.constructType(Point.class, (Class) null); - assertEquals(Point.class, t2.getRawClass()); - - JavaType ctxt = tf.constructType(getClass()); - JavaType t3 = tf.constructType(Point.class, ctxt); - assertEquals(Point.class, t3.getRawClass()); - } -} diff --git a/src/test/java/com/fasterxml/jackson/databind/interop/IllegalTypesCheckTest.java b/src/test/java/com/fasterxml/jackson/databind/interop/IllegalTypesCheckTest.java index 5ccb0fb783..abc069a307 100644 --- a/src/test/java/com/fasterxml/jackson/databind/interop/IllegalTypesCheckTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/interop/IllegalTypesCheckTest.java @@ -57,8 +57,9 @@ public void testXalanTypes1599() throws Exception +" ]\n" +"}" ); - ObjectMapper mapper = new ObjectMapper(); - mapper.enableDefaultTyping(); + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping() + .build(); try { mapper.readValue(JSON, Bean1599.class); fail("Should not pass"); @@ -101,8 +102,9 @@ public void testSpringTypes1737() throws Exception // // // Tests for [databind#1872] public void testJDKTypes1872() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY) + .build(); String json = aposToQuotes(String.format("{'@class':'%s','authorities':['java.util.ArrayList',[]]}", Authentication1872.class.getName())); diff --git a/src/test/java/com/fasterxml/jackson/databind/interop/TestFormatDetection.java b/src/test/java/com/fasterxml/jackson/databind/interop/TestFormatDetection.java deleted file mode 100644 index 3d1da2ff38..0000000000 --- a/src/test/java/com/fasterxml/jackson/databind/interop/TestFormatDetection.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.fasterxml.jackson.databind.interop; - -import com.fasterxml.jackson.core.JsonProcessingException; - -import com.fasterxml.jackson.databind.*; - -public class TestFormatDetection extends BaseMapTest -{ - private final ObjectReader READER = objectReader(); - - static class POJO { - public int x, y; - - public POJO() { } - public POJO(int x, int y) { - this.x = x; - this.y = y; - } - } - - /* - /********************************************************** - /* Test methods - /********************************************************** - */ - - public void testSimpleWithJSON() throws Exception - { - ObjectReader detecting = READER.forType(POJO.class); - detecting = detecting.withFormatDetection(detecting); - POJO pojo = detecting.readValue(utf8Bytes("{\"x\":1}")); - assertNotNull(pojo); - assertEquals(1, pojo.x); - } - - public void testSequenceWithJSON() throws Exception - { - ObjectReader detecting = READER.forType(POJO.class); - detecting = detecting.withFormatDetection(detecting); - MappingIterator it = detecting. - readValues(utf8Bytes(aposToQuotes("{'x':1}\n{'x':2,'y':5}"))); - - assertTrue(it.hasNextValue()); - POJO pojo = it.nextValue(); - assertEquals(1, pojo.x); - - assertTrue(it.hasNextValue()); - pojo = it.nextValue(); - assertEquals(2, pojo.x); - assertEquals(5, pojo.y); - - assertFalse(it.hasNextValue()); - it.close(); - - // And again with nodes - ObjectReader r2 = READER.forType(JsonNode.class); - r2 = r2.withFormatDetection(r2); - MappingIterator nodes = r2. - readValues(utf8Bytes(aposToQuotes("{'x':1}\n{'x':2,'y':5}"))); - - assertTrue(nodes.hasNextValue()); - JsonNode n = nodes.nextValue(); - assertEquals(1, n.size()); - - assertTrue(nodes.hasNextValue()); - n = nodes.nextValue(); - assertEquals(2, n.size()); - assertEquals(2, n.path("x").asInt()); - assertEquals(5, n.path("y").asInt()); - - assertFalse(nodes.hasNextValue()); - nodes.close(); - } - - public void testInvalid() throws Exception - { - ObjectReader detecting = READER.forType(POJO.class); - detecting = detecting.withFormatDetection(detecting); - try { - detecting.readValue(utf8Bytes("1")); - fail("Should have failed"); - } catch (JsonProcessingException e) { - verifyException(e, "Cannot detect format from input"); - } - } -} diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/AutoDetect1947Test.java b/src/test/java/com/fasterxml/jackson/databind/introspect/AutoDetect1947Test.java deleted file mode 100644 index 165bcc64c9..0000000000 --- a/src/test/java/com/fasterxml/jackson/databind/introspect/AutoDetect1947Test.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.fasterxml.jackson.databind.introspect; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import com.fasterxml.jackson.databind.*; - -// Test(s) for [databind#1947], regression for 2.9 -public class AutoDetect1947Test extends BaseMapTest -{ - static class Entity1947 { - public int shouldBeDetected; - public String shouldNotBeDetected; - - @JsonProperty - public int getShouldBeDetected() { - return shouldBeDetected; - } - - public void setShouldBeDetected(int shouldBeDetected) { - this.shouldBeDetected = shouldBeDetected; - } - - public String getShouldNotBeDetected() { - return shouldNotBeDetected; - } - - public void setShouldNotBeDetected(String shouldNotBeDetected) { - this.shouldNotBeDetected = shouldNotBeDetected; - } - } - public void testDisablingAll() throws Exception - { - ObjectMapper mapper = objectMapperBuilder() - .disable(MapperFeature.AUTO_DETECT_SETTERS) - .disable(MapperFeature.AUTO_DETECT_FIELDS) - .disable(MapperFeature.AUTO_DETECT_GETTERS) - .disable(MapperFeature.AUTO_DETECT_CREATORS) - .disable(MapperFeature.AUTO_DETECT_IS_GETTERS) - .build(); - String json = mapper.writeValueAsString(new Entity1947()); - JsonNode n = mapper.readTree(json); - assertEquals(1, n.size()); - assertTrue(n.has("shouldBeDetected")); - assertFalse(n.has("shouldNotBeDetected")); - } -} diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/BeanDescriptionTest.java b/src/test/java/com/fasterxml/jackson/databind/introspect/BeanDescriptionTest.java index 3d75f65435..aa6c66b09c 100644 --- a/src/test/java/com/fasterxml/jackson/databind/introspect/BeanDescriptionTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/introspect/BeanDescriptionTest.java @@ -16,7 +16,7 @@ static class DocumentedBean { public void testClassDesc() throws Exception { - BeanDescription beanDesc = MAPPER.getDeserializationConfig().introspect(MAPPER.constructType(DocumentedBean.class)); + BeanDescription beanDesc = MAPPER.deserializationConfig().introspect(MAPPER.constructType(DocumentedBean.class)); assertEquals(CLASS_DESC, beanDesc.findClassDescription()); } } diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/BeanNamingTest.java b/src/test/java/com/fasterxml/jackson/databind/introspect/BeanNamingTest.java index 15f7eb7470..e840d41f0c 100644 --- a/src/test/java/com/fasterxml/jackson/databind/introspect/BeanNamingTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/introspect/BeanNamingTest.java @@ -16,19 +16,12 @@ public int getA() { return 3; } } - - public void testSimple() throws Exception - { - ObjectMapper mapper = new ObjectMapper(); - assertFalse(mapper.isEnabled(MapperFeature.USE_STD_BEAN_NAMING)); - assertEquals(aposToQuotes("{'url':'http://foo'}"), - mapper.writeValueAsString(new URLBean())); - assertEquals(aposToQuotes("{'a':3}"), - mapper.writeValueAsString(new ABean())); - mapper = objectMapperBuilder() - .enable(MapperFeature.USE_STD_BEAN_NAMING) - .build(); + // 24-Sep-2017, tatu: Used to test for `MapperFeature.USE_STD_BEAN_NAMING`, but with 3.x + // that is always enabled. + public void testMultipleLeadingCapitalLetters() throws Exception + { + ObjectMapper mapper = objectMapper(); assertEquals(aposToQuotes("{'URL':'http://foo'}"), mapper.writeValueAsString(new URLBean())); assertEquals(aposToQuotes("{'a':3}"), diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/CustomAnnotationIntrospector1756Test.java b/src/test/java/com/fasterxml/jackson/databind/introspect/CustomAnnotationIntrospector1756Test.java index 62d6207e31..764cd87c4d 100644 --- a/src/test/java/com/fasterxml/jackson/databind/introspect/CustomAnnotationIntrospector1756Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/introspect/CustomAnnotationIntrospector1756Test.java @@ -7,6 +7,7 @@ import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.cfg.MapperConfig; import com.fasterxml.jackson.databind.module.SimpleModule; @SuppressWarnings("serial") @@ -87,12 +88,14 @@ public String findImplicitPropertyName(final AnnotatedMember member) { return null; } - @SuppressWarnings("deprecation") @Override - public boolean hasCreatorAnnotation(Annotated a) { - final AnnotatedConstructor ctor = (AnnotatedConstructor) a; - return (ctor.getParameterCount() > 0) - && (ctor.getParameter(0).getAnnotation(Field1756.class) != null); + public JsonCreator.Mode findCreatorAnnotation(MapperConfig config, Annotated a) { + final AnnotatedConstructor ctor = (AnnotatedConstructor) a; + if ((ctor.getParameterCount() > 0) + && (ctor.getParameter(0).getAnnotation(Field1756.class) != null)) { + return JsonCreator.Mode.PROPERTIES; + } + return null; } } @@ -108,9 +111,9 @@ public void testIssue1756() throws Exception { Issue1756Module m = new Issue1756Module(); m.addAbstractTypeMapping(Foobar.class, FoobarImpl.class); - final ObjectMapper mapper = new ObjectMapper() - .registerModule(m); - + ObjectMapper mapper = jsonMapperBuilder() + .addModule(m) + .build(); final Foobar foobar = mapper.readValue(aposToQuotes("{'bar':'bar', 'foo':'foo'}"), Foobar.class); assertNotNull(foobar); diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/IgnoredCreatorProperty1572Test.java b/src/test/java/com/fasterxml/jackson/databind/introspect/IgnoredCreatorProperty1572Test.java index a6eeca81e7..3d736b92be 100644 --- a/src/test/java/com/fasterxml/jackson/databind/introspect/IgnoredCreatorProperty1572Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/introspect/IgnoredCreatorProperty1572Test.java @@ -58,8 +58,9 @@ public String findImplicitPropertyName(AnnotatedMember member) { // [databind#1572] public void testIgnoredCtorParam() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - mapper.setAnnotationIntrospector(new ImplicitNames()); + final ObjectMapper mapper = jsonMapperBuilder() + .annotationIntrospector(new ImplicitNames()) + .build(); String JSON = aposToQuotes("{'innerTest': {\n" +"'str':'str',\n" +"'otherStr': 'otherStr'\n" diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/IgnoredFieldPresentInCreatorProperty2001Test.java b/src/test/java/com/fasterxml/jackson/databind/introspect/IgnoredFieldPresentInCreatorProperty2001Test.java index a551c0ae0d..2cdeec2c64 100644 --- a/src/test/java/com/fasterxml/jackson/databind/introspect/IgnoredFieldPresentInCreatorProperty2001Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/introspect/IgnoredFieldPresentInCreatorProperty2001Test.java @@ -2,26 +2,27 @@ import java.beans.ConstructorProperties; -import com.fasterxml.jackson.annotation.*; - +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.BaseMapTest; -public class IgnoredFieldPresentInCreatorProperty2001Test extends BaseMapTest -{ - static public class Foo { +// Tests for [databind#2001] +public class IgnoredFieldPresentInCreatorProperty2001Test extends BaseMapTest { + static public class Foo { + @JsonIgnore public String query; - // 01-May-2018, tatu: Important! Without this there is no problem - @ConstructorProperties("rawQuery") @JsonCreator + @ConstructorProperties("rawQuery") public Foo(@JsonProperty("query") String rawQuery) { query = rawQuery; } } public void testIgnoredFieldPresentInPropertyCreator() throws Exception { - Foo deserialized = newObjectMapper().readValue("{\"query\": \"bar\"}", Foo.class); + Foo deserialized = newJsonMapper().readValue("{\"query\": \"bar\"}", Foo.class); assertEquals("bar", deserialized.query); } } diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/IntrospectorPairTest.java b/src/test/java/com/fasterxml/jackson/databind/introspect/IntrospectorPairTest.java index a862713a74..86e8725040 100644 --- a/src/test/java/com/fasterxml/jackson/databind/introspect/IntrospectorPairTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/introspect/IntrospectorPairTest.java @@ -5,7 +5,6 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; - import com.fasterxml.jackson.core.Version; import com.fasterxml.jackson.databind.*; @@ -13,7 +12,6 @@ import com.fasterxml.jackson.databind.deser.std.StringDeserializer; import com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer; import com.fasterxml.jackson.databind.jsontype.NamedType; -import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; import com.fasterxml.jackson.databind.ser.std.StringSerializer; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; @@ -28,7 +26,7 @@ public Version version() { } @Override - public JsonInclude.Value findPropertyInclusion(Annotated a) { + public JsonInclude.Value findPropertyInclusion(MapperConfig config, Annotated a) { return JsonInclude.Value.empty() .withContentInclusion(JsonInclude.Include.ALWAYS) .withValueInclusion(JsonInclude.Include.NON_ABSENT); @@ -42,7 +40,7 @@ public Version version() { } @Override - public JsonInclude.Value findPropertyInclusion(Annotated a) { + public JsonInclude.Value findPropertyInclusion(MapperConfig config, Annotated a) { return JsonInclude.Value.empty() .withContentInclusion(JsonInclude.Include.NON_EMPTY) .withValueInclusion(JsonInclude.Include.USE_DEFAULTS); @@ -64,12 +62,12 @@ public Version version() { } @Override - public Object findDeserializer(Annotated am) { + public Object findDeserializer(MapperConfig config, Annotated am) { return _deserializer; } @Override - public Object findSerializer(Annotated am) { + public Object findSerializer(MapperConfig config, Annotated am) { return _serializer; } } @@ -96,7 +94,7 @@ public Version version() { } @Override - public JsonInclude.Value findPropertyInclusion(Annotated a) { + public JsonInclude.Value findPropertyInclusion(MapperConfig config, Annotated a) { return JsonInclude.Value.empty() .withContentInclusion(JsonInclude.Include.NON_EMPTY) .withValueInclusion(JsonInclude.Include.USE_DEFAULTS); @@ -150,10 +148,10 @@ public String findClassDescription(AnnotatedClass ac) { */ @Override - public VisibilityChecker findAutoDetectVisibility(AnnotatedClass ac, - VisibilityChecker checker) + public VisibilityChecker findAutoDetectVisibility(MapperConfig config, + AnnotatedClass ac, VisibilityChecker checker) { - VisibilityChecker vc = (VisibilityChecker) values.get("findAutoDetectVisibility"); + VisibilityChecker vc = (VisibilityChecker) values.get("findAutoDetectVisibility"); // not really good but: return (vc == null) ? checker : vc; } @@ -164,36 +162,15 @@ public VisibilityChecker findAutoDetectVisibility(AnnotatedClass ac, /****************************************************** */ - @Override - public TypeResolverBuilder findTypeResolver(MapperConfig config, - AnnotatedClass ac, JavaType baseType) - { - return (TypeResolverBuilder) values.get("findTypeResolver"); - } - - @Override - public TypeResolverBuilder findPropertyTypeResolver(MapperConfig config, - AnnotatedMember am, JavaType baseType) - { - return (TypeResolverBuilder) values.get("findPropertyTypeResolver"); - } - - @Override - public TypeResolverBuilder findPropertyContentTypeResolver(MapperConfig config, - AnnotatedMember am, JavaType baseType) - { - return (TypeResolverBuilder) values.get("findPropertyContentTypeResolver"); - } - @SuppressWarnings("unchecked") @Override - public List findSubtypes(Annotated a) + public List findSubtypes(MapperConfig config, Annotated a) { return (List) values.get("findSubtypes"); } @Override - public String findTypeName(AnnotatedClass ac) { + public String findTypeName(MapperConfig config, AnnotatedClass ac) { return (String) values.get("findTypeName"); } @@ -363,19 +340,19 @@ public void testFindSerializer() throws Exception AnnotationIntrospector nop2 = new IntrospectorWithHandlers(null, JsonSerializer.None.class); assertSame(serString, - new AnnotationIntrospectorPair(intr1, intr2).findSerializer(null)); + new AnnotationIntrospectorPair(intr1, intr2).findSerializer(null, null)); assertSame(serToString, - new AnnotationIntrospectorPair(intr2, intr1).findSerializer(null)); + new AnnotationIntrospectorPair(intr2, intr1).findSerializer(null, null)); // also: no-op instance should not block real one, regardless assertSame(serString, - new AnnotationIntrospectorPair(nop, intr1).findSerializer(null)); + new AnnotationIntrospectorPair(nop, intr1).findSerializer(null, null)); assertSame(serString, - new AnnotationIntrospectorPair(nop2, intr1).findSerializer(null)); + new AnnotationIntrospectorPair(nop2, intr1).findSerializer(null, null)); // nor should no-op result in non-null result - assertNull(new AnnotationIntrospectorPair(nop, nop2).findSerializer(null)); - assertNull(new AnnotationIntrospectorPair(nop2, nop).findSerializer(null)); + assertNull(new AnnotationIntrospectorPair(nop, nop2).findSerializer(null, null)); + assertNull(new AnnotationIntrospectorPair(nop2, nop).findSerializer(null, null)); } public void testFindDeserializer() throws Exception @@ -389,18 +366,18 @@ public void testFindDeserializer() throws Exception AnnotationIntrospector nop2 = new IntrospectorWithHandlers(JsonDeserializer.None.class, null); assertSame(deserString, - new AnnotationIntrospectorPair(intr1, intr2).findDeserializer(null)); + new AnnotationIntrospectorPair(intr1, intr2).findDeserializer(null, null)); assertSame(deserObject, - new AnnotationIntrospectorPair(intr2, intr1).findDeserializer(null)); + new AnnotationIntrospectorPair(intr2, intr1).findDeserializer(null, null)); // also: no-op instance should not block real one, regardless assertSame(deserString, - new AnnotationIntrospectorPair(nop, intr1).findDeserializer(null)); + new AnnotationIntrospectorPair(nop, intr1).findDeserializer(null, null)); assertSame(deserString, - new AnnotationIntrospectorPair(nop2, intr1).findDeserializer(null)); + new AnnotationIntrospectorPair(nop2, intr1).findDeserializer(null, null)); // nor should no-op result in non-null result - assertNull(new AnnotationIntrospectorPair(nop, nop2).findDeserializer(null)); - assertNull(new AnnotationIntrospectorPair(nop2, nop).findDeserializer(null)); + assertNull(new AnnotationIntrospectorPair(nop, nop2).findDeserializer(null, null)); + assertNull(new AnnotationIntrospectorPair(nop2, nop).findDeserializer(null, null)); } /* @@ -411,15 +388,16 @@ public void testFindDeserializer() throws Exception public void testFindAutoDetectVisibility() throws Exception { - VisibilityChecker vc = VisibilityChecker.Std.defaultInstance(); + VisibilityChecker vc = VisibilityChecker.defaultInstance(); IntrospectorWithMap intr1 = new IntrospectorWithMap() .add("findAutoDetectVisibility", vc); + SerializationConfig config = null; assertNull(new AnnotationIntrospectorPair(NO_ANNOTATIONS, NO_ANNOTATIONS) - .findAutoDetectVisibility(null, null)); + .findAutoDetectVisibility(config, null, null)); assertSame(vc, new AnnotationIntrospectorPair(intr1, NO_ANNOTATIONS) - .findAutoDetectVisibility(null, null)); + .findAutoDetectVisibility(config, null, null)); assertSame(vc, new AnnotationIntrospectorPair(NO_ANNOTATIONS, intr1) - .findAutoDetectVisibility(null, null)); + .findAutoDetectVisibility(config, null, null)); } /* @@ -450,11 +428,11 @@ public void testFindTypeName() { .add("findTypeName", "type1"); IntrospectorWithMap intr2 = new IntrospectorWithMap() .add("findTypeName", "type2"); - assertNull(new AnnotationIntrospectorPair(NO_ANNOTATIONS, NO_ANNOTATIONS).findTypeName(null)); + assertNull(new AnnotationIntrospectorPair(NO_ANNOTATIONS, NO_ANNOTATIONS).findTypeName(null, null)); assertEquals("type1", - new AnnotationIntrospectorPair(intr1, intr2).findTypeName(null)); + new AnnotationIntrospectorPair(intr1, intr2).findTypeName(null, null)); assertEquals("type2", - new AnnotationIntrospectorPair(intr2, intr1).findTypeName(null)); + new AnnotationIntrospectorPair(intr2, intr1).findTypeName(null, null)); } /* @@ -496,8 +474,8 @@ public void testHasAnySetter() { public void testInclusionMerging() throws Exception { // argument is ignored by test introspectors, may be null - JsonInclude.Value v12 = introPair12.findPropertyInclusion(null); - JsonInclude.Value v21 = introPair21.findPropertyInclusion(null); + JsonInclude.Value v12 = introPair12.findPropertyInclusion(null, null); + JsonInclude.Value v21 = introPair21.findPropertyInclusion(null, null); assertEquals(JsonInclude.Include.ALWAYS, v12.getContentInclusion()); assertEquals(JsonInclude.Include.NON_ABSENT, v12.getValueInclusion()); diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollectorTest.java b/src/test/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollectorTest.java index ef4969154a..3457399e4a 100644 --- a/src/test/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollectorTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollectorTest.java @@ -8,6 +8,7 @@ import java.util.*; import com.fasterxml.jackson.annotation.*; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; @@ -359,8 +360,9 @@ public void testSimpleIgnoreAndRename() public void testGlobalVisibilityForGetters() { - ObjectMapper m = objectMapperBuilder() - .configure(MapperFeature.AUTO_DETECT_GETTERS, false) + ObjectMapper m = jsonMapperBuilder() + .changeDefaultVisibility(vc -> + vc.withVisibility(PropertyAccessor.GETTER, Visibility.NONE)) .build(); POJOPropertiesCollector coll = collector(m, SimpleGetterVisibility.class, true); // should be 1, expect that we disabled getter auto-detection, so @@ -415,22 +417,22 @@ public void testSimpleWithType() public void testInnerClassWithAnnotationsInCreator() throws Exception { - BasicBeanDescription beanDesc; + BeanDescription beanDesc; // first with serialization - beanDesc = MAPPER.getSerializationConfig().introspect(MAPPER.constructType(Issue701Bean.class)); + beanDesc = MAPPER.serializationConfig().introspect(MAPPER.constructType(Issue701Bean.class)); assertNotNull(beanDesc); // then with deserialization - beanDesc = MAPPER.getDeserializationConfig().introspect(MAPPER.constructType(Issue701Bean.class)); + beanDesc = MAPPER.deserializationConfig().introspect(MAPPER.constructType(Issue701Bean.class)); assertNotNull(beanDesc); } public void testUseAnnotationsFalse() throws Exception { // note: need a separate mapper, need to reconfigure - ObjectMapper mapper = objectMapperBuilder() - .configure(MapperFeature.USE_ANNOTATIONS, false) + ObjectMapper mapper = jsonMapperBuilder() + .disable(MapperFeature.USE_ANNOTATIONS) .build(); - BasicBeanDescription beanDesc = mapper.getSerializationConfig().introspect(mapper.constructType(Jackson703.class)); + BeanDescription beanDesc = mapper.serializationConfig().introspect(mapper.constructType(Jackson703.class)); assertNotNull(beanDesc); Jackson703 bean = new Jackson703(); @@ -440,7 +442,7 @@ public void testUseAnnotationsFalse() throws Exception public void testJackson744() throws Exception { - BeanDescription beanDesc = MAPPER.getDeserializationConfig().introspect + BeanDescription beanDesc = MAPPER.deserializationConfig().introspect (MAPPER.constructType(Issue744Bean.class)); assertNotNull(beanDesc); AnnotatedMember setter = beanDesc.findAnySetterAccessor(); @@ -453,19 +455,19 @@ public void testJackson744() throws Exception public void testPropertyDesc() throws Exception { // start via deser - BeanDescription beanDesc = MAPPER.getDeserializationConfig().introspect(MAPPER.constructType(PropDescBean.class)); + BeanDescription beanDesc = MAPPER.deserializationConfig().introspect(MAPPER.constructType(PropDescBean.class)); _verifyProperty(beanDesc, true, false, "13"); // and then via ser: - beanDesc = MAPPER.getSerializationConfig().introspect(MAPPER.constructType(PropDescBean.class)); + beanDesc = MAPPER.serializationConfig().introspect(MAPPER.constructType(PropDescBean.class)); _verifyProperty(beanDesc, true, false, "13"); } // [databind#438]: Support @JsonProperty.index public void testPropertyIndex() throws Exception { - BeanDescription beanDesc = MAPPER.getDeserializationConfig().introspect(MAPPER.constructType(PropDescBean.class)); + BeanDescription beanDesc = MAPPER.deserializationConfig().introspect(MAPPER.constructType(PropDescBean.class)); _verifyProperty(beanDesc, false, true, "13"); - beanDesc = MAPPER.getSerializationConfig().introspect(MAPPER.constructType(PropDescBean.class)); + beanDesc = MAPPER.serializationConfig().introspect(MAPPER.constructType(PropDescBean.class)); _verifyProperty(beanDesc, false, true, "13"); } @@ -540,10 +542,10 @@ protected POJOPropertiesCollector collector(ObjectMapper m0, BasicClassIntrospector bci = new BasicClassIntrospector(); // no real difference between serialization, deserialization, at least here if (forSerialization) { - return bci.collectProperties(m0.getSerializationConfig(), + return bci.collectProperties(m0.serializationConfig(), m0.constructType(cls), null, true, "set"); } - return bci.collectProperties(m0.getDeserializationConfig(), + return bci.collectProperties(m0.deserializationConfig(), m0.constructType(cls), null, false, "set"); } } diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/TestAnnotationBundles.java b/src/test/java/com/fasterxml/jackson/databind/introspect/TestAnnotationBundles.java index e9a0714ce2..c7d37b2ea2 100644 --- a/src/test/java/com/fasterxml/jackson/databind/introspect/TestAnnotationBundles.java +++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TestAnnotationBundles.java @@ -79,7 +79,7 @@ static class RecursiveHolder2 { static class RecursiveHolder3 { public int x; - @JsonCreator + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) @HolderA public RecursiveHolder3(int x) { this.x = x; } } @@ -119,8 +119,10 @@ public PropertyName findNameForSerialization(Annotated a) public void testKeepAnnotationBundle() throws Exception { - MAPPER.setAnnotationIntrospector(new BundleAnnotationIntrospector()); - assertEquals("{\"important\":42}", MAPPER.writeValueAsString(new InformingHolder())); + ObjectMapper mapper = jsonMapperBuilder() + .annotationIntrospector(new BundleAnnotationIntrospector()) + .build(); + assertEquals("{\"important\":42}", mapper.writeValueAsString(new InformingHolder())); } public void testRecursiveBundlesField() throws Exception { diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/TestAutoDetect.java b/src/test/java/com/fasterxml/jackson/databind/introspect/TestAutoDetect.java index 9ab933d334..8de7c70f91 100644 --- a/src/test/java/com/fasterxml/jackson/databind/introspect/TestAutoDetect.java +++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TestAutoDetect.java @@ -4,19 +4,32 @@ import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.introspect.VisibilityChecker; +import com.fasterxml.jackson.databind.exc.InvalidDefinitionException; public class TestAutoDetect extends BaseMapTest { - static class PrivateBean { + // 21-Sep-2017, tatu: With 2.x, private delegating ctor was acceptable; with 3.x + // must be non-private OR annotated + static class ProtectedBean { String a; - private PrivateBean() { } + protected ProtectedBean(String a) { this.a = a; } + } + + // Private scalar constructor ok, but only if annotated (or level changed) + static class PrivateBeanAnnotated { + String a; - private PrivateBean(String a) { this.a = a; } + @JsonCreator + private PrivateBeanAnnotated(String a) { this.a = a; } } + static class PrivateBeanNonAnnotated { + String a; + private PrivateBeanNonAnnotated(String a) { this.a = a; } + } + // test for [databind#1347], config overrides for visibility @JsonPropertyOrder(alphabetic=true) static class Feature1347SerBean { @@ -43,26 +56,48 @@ public void setValue(int x) { private final ObjectMapper MAPPER = new ObjectMapper(); - public void testPrivateCtor() throws Exception + public void testProtectedDelegatingCtor() throws Exception { // first, default settings, with which construction works ok ObjectMapper m = new ObjectMapper(); - PrivateBean bean = m.readValue("\"abc\"", PrivateBean.class); + ProtectedBean bean = m.readValue(quote("abc"), ProtectedBean.class); assertEquals("abc", bean.a); // then by increasing visibility requirement: - m = new ObjectMapper(); - VisibilityChecker vc = m.getVisibilityChecker(); - vc = vc.withCreatorVisibility(JsonAutoDetect.Visibility.PUBLIC_ONLY); - m.setVisibility(vc); + m = jsonMapperBuilder() + .changeDefaultVisibility(vc -> vc.withScalarConstructorVisibility(JsonAutoDetect.Visibility.PUBLIC_ONLY)) + .build(); try { - m.readValue("\"abc\"", PrivateBean.class); + m.readValue("\"abc\"", ProtectedBean.class); fail("Expected exception for missing constructor"); } catch (JsonProcessingException e) { - verifyException(e, "no String-argument constructor/factory"); + verifyException(e, InvalidDefinitionException.class, "no String-argument constructor/factory"); } } + public void testPrivateDelegatingCtor() throws Exception + { + // first, default settings, with which construction works ok + ObjectMapper m = new ObjectMapper(); + PrivateBeanAnnotated bean = m.readValue(quote("abc"), PrivateBeanAnnotated.class); + assertEquals("abc", bean.a); + + // but not so much without + try { + m.readValue("\"abc\"", PrivateBeanNonAnnotated.class); + fail("Expected exception for missing constructor"); + } catch (JsonProcessingException e) { + verifyException(e, InvalidDefinitionException.class, "no String-argument constructor/factory"); + } + + // except if we lower requirement + m = jsonMapperBuilder() + .changeDefaultVisibility(vc -> vc.withScalarConstructorVisibility(JsonAutoDetect.Visibility.ANY)) + .build(); + bean = m.readValue(quote("xyz"), PrivateBeanAnnotated.class); + assertEquals("xyz", bean.a); + } + // [databind#1347] public void testVisibilityConfigOverridesForSer() throws Exception { @@ -71,10 +106,11 @@ public void testVisibilityConfigOverridesForSer() throws Exception assertEquals(aposToQuotes("{'field':2,'value':3}"), MAPPER.writeValueAsString(input)); - ObjectMapper mapper = new ObjectMapper(); - mapper.configOverride(Feature1347SerBean.class) - .setVisibility(JsonAutoDetect.Value.construct(PropertyAccessor.GETTER, - Visibility.NONE)); + ObjectMapper mapper = jsonMapperBuilder() + .withConfigOverride(Feature1347SerBean.class, + o -> o.setVisibility(JsonAutoDetect.Value.construct(PropertyAccessor.GETTER, + Visibility.NONE))) + .build(); assertEquals(aposToQuotes("{'field':2}"), mapper.writeValueAsString(input)); } @@ -94,10 +130,11 @@ public void testVisibilityConfigOverridesForDeser() throws Exception } // but when instructed to ignore setter, should work - ObjectMapper mapper = new ObjectMapper(); - mapper.configOverride(Feature1347DeserBean.class) - .setVisibility(JsonAutoDetect.Value.construct(PropertyAccessor.SETTER, - Visibility.NONE)); + ObjectMapper mapper = jsonMapperBuilder() + .withConfigOverride(Feature1347DeserBean.class, + o -> o.setVisibility(JsonAutoDetect.Value.construct(PropertyAccessor.SETTER, + Visibility.NONE))) + .build(); Feature1347DeserBean result = mapper.readValue(JSON, Feature1347DeserBean.class); assertEquals(3, result.value); } 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 390f5f300a..c19fc5a8dc 100644 --- a/src/test/java/com/fasterxml/jackson/databind/introspect/TestBuilderMethods.java +++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TestBuilderMethods.java @@ -49,7 +49,7 @@ protected POJOPropertiesCollector collector(Class cls, String prefix) { BasicClassIntrospector bci = new BasicClassIntrospector(); // no real difference between serialization, deserialization, at least here - return bci.collectProperties(mapper.getSerializationConfig(), + return bci.collectProperties(mapper.serializationConfig(), mapper.constructType(cls), null, false, prefix); } diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/TestInferredMutators.java b/src/test/java/com/fasterxml/jackson/databind/introspect/TestInferredMutators.java index 23fafcc77b..0ec70f8a00 100644 --- a/src/test/java/com/fasterxml/jackson/databind/introspect/TestInferredMutators.java +++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TestInferredMutators.java @@ -27,13 +27,9 @@ public static class FixedPoint { /********************************************************** */ - // for #190 public void testFinalFieldIgnoral() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - // default value is 'enabled', for backwards compatibility - assertTrue(mapper.isEnabled(MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS)); - mapper = objectMapperBuilder() + ObjectMapper mapper = jsonMapperBuilder() .disable(MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS) .build(); try { @@ -55,7 +51,7 @@ public void testDeserializationInference() throws Exception assertEquals(2, p.x); // but without it, should fail: - mapper = objectMapperBuilder() + mapper = jsonMapperBuilder() .disable(MapperFeature.INFER_PROPERTY_MUTATORS) .build(); try { diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/TestJacksonAnnotationIntrospector.java b/src/test/java/com/fasterxml/jackson/databind/introspect/TestJacksonAnnotationIntrospector.java index ef9adde132..e765839daf 100644 --- a/src/test/java/com/fasterxml/jackson/databind/introspect/TestJacksonAnnotationIntrospector.java +++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TestJacksonAnnotationIntrospector.java @@ -7,17 +7,15 @@ import javax.xml.namespace.QName; import com.fasterxml.jackson.annotation.*; + import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; + import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.*; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; -import com.fasterxml.jackson.databind.introspect.AnnotatedClass; import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector; -import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; -import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder; -import com.fasterxml.jackson.databind.type.TypeFactory; @SuppressWarnings("serial") public class TestJacksonAnnotationIntrospector @@ -116,16 +114,6 @@ public QName deserialize(JsonParser jp, DeserializationContext ctxt) } } - public static class DummyBuilder extends StdTypeResolverBuilder - // - { - } - - @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS) - @JsonTypeResolver(DummyBuilder.class) - static class TypeResolverBean { } - - // @since 1.7 @JsonIgnoreType static class IgnoredType { } @@ -157,8 +145,9 @@ public String[] findEnumValues(Class enumType, Enum[] enumValues, String[ */ public void testSerializeDeserializeWithJaxbAnnotations() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.enable(SerializationFeature.INDENT_OUTPUT); + ObjectMapper mapper = jsonMapperBuilder() + .enable(SerializationFeature.INDENT_OUTPUT) + .build(); JacksonExample ex = new JacksonExample(); QName qname = new QName("urn:hi", "hello"); ex.setQname(qname); @@ -181,22 +170,11 @@ public void testSerializeDeserializeWithJaxbAnnotations() throws Exception assertEquals(ex.enumProperty, readEx.enumProperty); } - public void testJsonTypeResolver() throws Exception - { - ObjectMapper mapper = new ObjectMapper(); - JacksonAnnotationIntrospector ai = new JacksonAnnotationIntrospector(); - AnnotatedClass ac = AnnotatedClassResolver.resolveWithoutSuperTypes(mapper.getSerializationConfig(), - TypeResolverBean.class); - JavaType baseType = TypeFactory.defaultInstance().constructType(TypeResolverBean.class); - TypeResolverBuilder rb = ai.findTypeResolver(mapper.getDeserializationConfig(), ac, baseType); - assertNotNull(rb); - assertSame(DummyBuilder.class, rb.getClass()); - } - public void testEnumHandling() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.setAnnotationIntrospector(new LcEnumIntrospector()); + ObjectMapper mapper = jsonMapperBuilder() + .annotationIntrospector(new LcEnumIntrospector()) + .build(); assertEquals("\"value1\"", mapper.writeValueAsString(EnumExample.VALUE1)); EnumExample result = mapper.readValue(quote("value1"), EnumExample.class); assertEquals(EnumExample.VALUE1, result); diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/TestNamingStrategyCustom.java b/src/test/java/com/fasterxml/jackson/databind/introspect/TestNamingStrategyCustom.java index 725b2ecbfb..12546d7a53 100644 --- a/src/test/java/com/fasterxml/jackson/databind/introspect/TestNamingStrategyCustom.java +++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TestNamingStrategyCustom.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.databind.BaseMapTest; +import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.PropertyNamingStrategy; import com.fasterxml.jackson.databind.annotation.*; @@ -173,15 +174,17 @@ static class BeanWithPrefixNames public void testSimpleGetters() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.setPropertyNamingStrategy(new PrefixStrategy()); + ObjectMapper mapper = jsonMapperBuilder() + .propertyNamingStrategy(new PrefixStrategy()) + .build(); assertEquals("{\"Get-key\":123}", mapper.writeValueAsString(new GetterBean())); } public void testSimpleSetters() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.setPropertyNamingStrategy(new PrefixStrategy()); + ObjectMapper mapper = jsonMapperBuilder() + .propertyNamingStrategy(new PrefixStrategy()) + .build(); SetterBean bean = mapper.readValue("{\"Set-key\":13}", SetterBean.class); assertEquals(13, bean.value); } @@ -189,8 +192,9 @@ public void testSimpleSetters() throws Exception public void testSimpleFields() throws Exception { // First serialize - ObjectMapper mapper = new ObjectMapper(); - mapper.setPropertyNamingStrategy(new PrefixStrategy()); + ObjectMapper mapper = jsonMapperBuilder() + .propertyNamingStrategy(new PrefixStrategy()) + .build(); String json = mapper.writeValueAsString(new FieldBean(999)); assertEquals("{\"Field-key\":999}", json); @@ -202,8 +206,9 @@ public void testSimpleFields() throws Exception public void testCStyleNaming() throws Exception { // First serialize - ObjectMapper mapper = new ObjectMapper(); - mapper.setPropertyNamingStrategy(new CStyleStrategy()); + ObjectMapper mapper = jsonMapperBuilder() + .propertyNamingStrategy(new CStyleStrategy()) + .build(); String json = mapper.writeValueAsString(new PersonBean("Joe", "Sixpack", 42)); assertEquals("{\"first_name\":\"Joe\",\"last_name\":\"Sixpack\",\"age\":42}", json); @@ -216,8 +221,10 @@ public void testCStyleNaming() throws Exception public void testWithGetterAsSetter() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.setPropertyNamingStrategy(new CStyleStrategy()); + ObjectMapper mapper = jsonMapperBuilder() + .enable(MapperFeature.USE_GETTERS_AS_SETTERS) + .propertyNamingStrategy(new CStyleStrategy()) + .build(); SetterlessWithValue input = new SetterlessWithValue().add(3); String json = mapper.writeValueAsString(input); assertEquals("{\"value_list\":[{\"int_value\":3}]}", json); @@ -230,8 +237,9 @@ public void testWithGetterAsSetter() throws Exception public void testLowerCase() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.setPropertyNamingStrategy(new LcStrategy()); + ObjectMapper mapper = jsonMapperBuilder() + .propertyNamingStrategy(new LcStrategy()) + .build(); // mapper.disable(DeserializationConfig.DeserializationFeature.USE_GETTERS_AS_SETTERS); RenamedCollectionBean result = mapper.readValue("{\"thevalues\":[\"a\"]}", RenamedCollectionBean.class); @@ -243,8 +251,9 @@ public void testLowerCase() throws Exception // @JsonNaming / [databind#45] public void testPerClassAnnotation() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - mapper.setPropertyNamingStrategy(new LcStrategy()); + final ObjectMapper mapper = jsonMapperBuilder() + .propertyNamingStrategy(new LcStrategy()) + .build(); BeanWithPrefixNames input = new BeanWithPrefixNames(); String json = mapper.writeValueAsString(input); assertEquals("{\"Get-a\":3}", json); diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/TestNamingStrategyStd.java b/src/test/java/com/fasterxml/jackson/databind/introspect/TestNamingStrategyStd.java index e59e1d7482..ddbe651a6f 100644 --- a/src/test/java/com/fasterxml/jackson/databind/introspect/TestNamingStrategyStd.java +++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TestNamingStrategyStd.java @@ -170,16 +170,10 @@ protected FirstNameBean() { } {"uId", "u_id" }, }); - private ObjectMapper _lcWithUndescoreMapper; - - @Override - public void setUp() throws Exception - { - super.setUp(); - _lcWithUndescoreMapper = new ObjectMapper(); - _lcWithUndescoreMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); - } - + private static ObjectMapper _lcWithUndescoreMapper = jsonMapperBuilder() + .propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE) + .build(); + /* /********************************************************** /* Test methods for SNAKE_CASE @@ -280,8 +274,9 @@ public void testPascalCaseStandAlone() // [databind#428] public void testIssue428PascalWithOverrides() throws Exception { - String json = new ObjectMapper() - .setPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE) + String json = jsonMapperBuilder() + .propertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE) + .build() .writeValueAsString(new Bean428()); if (!json.contains(quote("fooBar"))) { fail("Should use name 'fooBar', does not: "+json); @@ -327,8 +322,9 @@ public void testKebabCaseStrategyStandAlone() public void testSimpleKebabCase() throws Exception { final FirstNameBean input = new FirstNameBean("Bob"); - ObjectMapper m = new ObjectMapper() - .setPropertyNamingStrategy(PropertyNamingStrategy.KEBAB_CASE); + ObjectMapper m = jsonMapperBuilder() + .propertyNamingStrategy(PropertyNamingStrategy.KEBAB_CASE) + .build(); assertEquals(aposToQuotes("{'first-name':'Bob'}"), m.writeValueAsString(input)); @@ -337,6 +333,40 @@ public void testSimpleKebabCase() throws Exception assertEquals("Billy", result.firstName); } + /* + /********************************************************** + /* Test methods for LOWER_CASE_WITH_DOTS + /********************************************************** + */ + + public void testLowerCaseWithDotsStrategyStandAlone() + { + assertEquals("some.value", + PropertyNamingStrategy.LOWER_CASE_WITH_DOTS.nameForField(null, null, "someValue")); + assertEquals("some.value", + PropertyNamingStrategy.LOWER_CASE_WITH_DOTS.nameForField(null, null, "SomeValue")); + assertEquals("url", + PropertyNamingStrategy.LOWER_CASE_WITH_DOTS.nameForField(null, null, "URL")); + assertEquals("url.stuff", + PropertyNamingStrategy.LOWER_CASE_WITH_DOTS.nameForField(null, null, "URLStuff")); + assertEquals("some.url.stuff", + PropertyNamingStrategy.LOWER_CASE_WITH_DOTS.nameForField(null, null, "SomeURLStuff")); + } + + public void testSimpleLowerCaseWithDots() throws Exception + { + final FirstNameBean input = new FirstNameBean("Bob"); + ObjectMapper m = jsonMapperBuilder() + .propertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE_WITH_DOTS) + .build(); + + assertEquals(aposToQuotes("{'first.name':'Bob'}"), m.writeValueAsString(input)); + + FirstNameBean result = m.readValue(aposToQuotes("{'first.name':'Billy'}"), + FirstNameBean.class); + assertEquals("Billy", result.firstName); + } + /* /********************************************************** /* Test methods, other @@ -348,8 +378,9 @@ public void testSimpleKebabCase() throws Exception */ public void testNamingWithObjectNode() throws Exception { - ObjectMapper m = new ObjectMapper() - .setPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE); + ObjectMapper m = jsonMapperBuilder() + .propertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE) + .build(); ClassWithObjectNodeField result = m.readValue( "{ \"id\": \"1\", \"json\": { \"foo\": \"bar\", \"baz\": \"bing\" } }", @@ -363,32 +394,32 @@ public void testNamingWithObjectNode() throws Exception public void testExplicitRename() throws Exception { - ObjectMapper m = objectMapperBuilder() - .propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE) - .enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY) - .build(); - // by default, renaming will not take place on explicitly named fields - assertEquals(aposToQuotes("{'firstName':'Peter','lastName':'Venkman','user_age':'35'}"), - m.writeValueAsString(new ExplicitBean())); - - m = objectMapperBuilder() - .propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE) - .enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY) - .enable(MapperFeature.ALLOW_EXPLICIT_PROPERTY_RENAMING) - .build(); - // w/ feature enabled, ALL property names should get re-written - assertEquals(aposToQuotes("{'first_name':'Peter','last_name':'Venkman','user_age':'35'}"), - m.writeValueAsString(new ExplicitBean())); - - // test deserialization as well - ExplicitBean bean = - m.readValue(aposToQuotes("{'first_name':'Egon','last_name':'Spengler','user_age':'32'}"), - ExplicitBean.class); - - assertNotNull(bean); - assertEquals("Egon", bean.userFirstName); - assertEquals("Spengler", bean.userLastName); - assertEquals("32", bean.userAge); + ObjectMapper m = jsonMapperBuilder() + .enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY) + .propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE) + .build(); + // by default, renaming will not take place on explicitly named fields + assertEquals(aposToQuotes("{'firstName':'Peter','lastName':'Venkman','user_age':'35'}"), + m.writeValueAsString(new ExplicitBean())); + + m = jsonMapperBuilder() + .propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE) + .enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY) + .enable(MapperFeature.ALLOW_EXPLICIT_PROPERTY_RENAMING) + .build(); + // w/ feature enabled, ALL property names should get re-written + assertEquals(aposToQuotes("{'first_name':'Peter','last_name':'Venkman','user_age':'35'}"), + m.writeValueAsString(new ExplicitBean())); + + // test deserialization as well + ExplicitBean bean = + m.readValue(aposToQuotes("{'first_name':'Egon','last_name':'Spengler','user_age':'32'}"), + ExplicitBean.class); + + assertNotNull(bean); + assertEquals("Egon", bean.userFirstName); + assertEquals("Spengler", bean.userLastName); + assertEquals("32", bean.userAge); } // Also verify that "no naming strategy" should be ok 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 19a158b95a..dc819222fe 100644 --- a/src/test/java/com/fasterxml/jackson/databind/introspect/TestPropertyConflicts.java +++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TestPropertyConflicts.java @@ -116,7 +116,7 @@ public void testRegularAndIsGetter() throws Exception public void testInferredNameConflictsWithGetters() throws Exception { - ObjectMapper mapper = objectMapperBuilder() + ObjectMapper mapper = jsonMapperBuilder() .annotationIntrospector(new InferingIntrospector()) .build(); String json = mapper.writeValueAsString(new Infernal()); @@ -125,22 +125,17 @@ public void testInferredNameConflictsWithGetters() throws Exception public void testInferredNameConflictsWithSetters() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.setAnnotationIntrospector(new InferingIntrospector()); + ObjectMapper mapper = jsonMapperBuilder() + .annotationIntrospector(new InferingIntrospector()) + .build(); Infernal inf = mapper.readValue(aposToQuotes("{'stuff':'Bob'}"), Infernal.class); assertNotNull(inf); } public void testIssue541() throws Exception { - ObjectMapper mapper = objectMapperBuilder() - .disable( - MapperFeature.AUTO_DETECT_CREATORS, - MapperFeature.AUTO_DETECT_FIELDS, - MapperFeature.AUTO_DETECT_GETTERS, - MapperFeature.AUTO_DETECT_IS_GETTERS, - MapperFeature.AUTO_DETECT_SETTERS, - MapperFeature.USE_GETTERS_AS_SETTERS - ).build(); + ObjectMapper mapper = jsonMapperBuilder() + .disable(MapperFeature.USE_GETTERS_AS_SETTERS) + .build(); Bean541 data = mapper.readValue("{\"str\":\"the string\"}", Bean541.class); if (data == null) { throw new IllegalStateException("data is null"); diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/TestScalaLikeImplicitProperties.java b/src/test/java/com/fasterxml/jackson/databind/introspect/TestScalaLikeImplicitProperties.java index 397db2fa49..875a9ad97c 100644 --- a/src/test/java/com/fasterxml/jackson/databind/introspect/TestScalaLikeImplicitProperties.java +++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TestScalaLikeImplicitProperties.java @@ -184,8 +184,9 @@ public void testGetterSetterProperty() throws Exception private ObjectMapper manglingMapper() { - ObjectMapper m = new ObjectMapper(); - m.setAnnotationIntrospector(new NameMangler()); + ObjectMapper m = jsonMapperBuilder() + .annotationIntrospector(new NameMangler()) + .build(); return m; } } diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/TransientTest.java b/src/test/java/com/fasterxml/jackson/databind/introspect/TransientTest.java index 3b6fc2a972..c00bcea286 100644 --- a/src/test/java/com/fasterxml/jackson/databind/introspect/TransientTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TransientTest.java @@ -63,7 +63,7 @@ public void testTransientFieldHandling() throws Exception MAPPER.writeValueAsString(new SimplePrunableTransient())); // but may change that - ObjectMapper m = objectMapperBuilder() + ObjectMapper m = jsonMapperBuilder() .enable(MapperFeature.PROPAGATE_TRANSIENT_MARKER) .build(); assertEquals(aposToQuotes("{'x':42}"), diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/VisibilityForSerializationTest.java b/src/test/java/com/fasterxml/jackson/databind/introspect/VisibilityForSerializationTest.java index 5736aca653..37f0f3e62c 100644 --- a/src/test/java/com/fasterxml/jackson/databind/introspect/VisibilityForSerializationTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/introspect/VisibilityForSerializationTest.java @@ -91,8 +91,9 @@ public void testGlobalAutoDetection() throws IOException // Then auto-detection disabled. But note: we MUST create a new // mapper, since old version of serializer may be cached by now - m = objectMapperBuilder() - .configure(MapperFeature.AUTO_DETECT_GETTERS, false) + m = jsonMapperBuilder() + .changeDefaultVisibility(vc -> + vc.withVisibility(PropertyAccessor.GETTER, Visibility.NONE)) .build(); result = writeAndMap(m, new GetterClass()); assertEquals(1, result.size()); @@ -108,9 +109,11 @@ public void testPerClassAutoDetection() throws IOException assertTrue(result.containsKey("x")); // And then class-level auto-detection enabling, should override defaults - m = objectMapperBuilder() - .configure(MapperFeature.AUTO_DETECT_GETTERS, true) + m = jsonMapperBuilder() + .changeDefaultVisibility(vc -> + vc.withVisibility(PropertyAccessor.GETTER, Visibility.PUBLIC_ONLY)) .build(); + result = writeAndMap(m, new EnabledGetterClass()); assertEquals(2, result.size()); assertTrue(result.containsKey("x")); @@ -119,46 +122,30 @@ public void testPerClassAutoDetection() throws IOException public void testPerClassAutoDetectionForIsGetter() throws IOException { - ObjectMapper m = objectMapperBuilder() + ObjectMapper m = jsonMapperBuilder() + .changeDefaultVisibility(vc -> // class level should override - .configure(MapperFeature.AUTO_DETECT_GETTERS, true) - .configure(MapperFeature.AUTO_DETECT_IS_GETTERS, false) + vc.withVisibility(PropertyAccessor.GETTER, Visibility.PUBLIC_ONLY) + .withVisibility(PropertyAccessor.IS_GETTER, Visibility.NONE)) .build(); + Map result = writeAndMap(m, new EnabledIsGetterClass()); assertEquals(0, result.size()); assertFalse(result.containsKey("ok")); } - // Simple test verifying that chainable methods work ok... - public void testConfigChainability() - { - ObjectMapper m = new ObjectMapper(); - assertTrue(m.isEnabled(MapperFeature.AUTO_DETECT_SETTERS)); - assertTrue(m.isEnabled(MapperFeature.AUTO_DETECT_GETTERS)); - m = objectMapperBuilder() - .configure(MapperFeature.AUTO_DETECT_SETTERS, false) - .configure(MapperFeature.AUTO_DETECT_GETTERS, false) - .build(); - assertFalse(m.isEnabled(MapperFeature.AUTO_DETECT_SETTERS)); - assertFalse(m.isEnabled(MapperFeature.AUTO_DETECT_GETTERS)); - } - public void testVisibilityFeatures() throws Exception { - ObjectMapper om = objectMapperBuilder() + ObjectMapper om = jsonMapperBuilder() + .disable(MapperFeature.USE_GETTERS_AS_SETTERS, MapperFeature.INFER_PROPERTY_MUTATORS) + .enable(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS, MapperFeature.USE_ANNOTATIONS) + .changeDefaultVisibility(vc -> + vc.withVisibility(PropertyAccessor.ALL, Visibility.NONE)) + .build(); // Only use explicitly specified values to be serialized/deserialized (i.e., JSONProperty). - .configure(MapperFeature.AUTO_DETECT_FIELDS, false) - .configure(MapperFeature.AUTO_DETECT_GETTERS, false) - .configure(MapperFeature.AUTO_DETECT_SETTERS, false) - .configure(MapperFeature.AUTO_DETECT_IS_GETTERS, false) - .configure(MapperFeature.USE_GETTERS_AS_SETTERS, false) - .configure(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS, true) - .configure(MapperFeature.INFER_PROPERTY_MUTATORS, false) - .configure(MapperFeature.USE_ANNOTATIONS, true) - .build(); - + JavaType javaType = om.getTypeFactory().constructType(TCls.class); - BeanDescription desc = (BeanDescription) om.getSerializationConfig().introspect(javaType); + BeanDescription desc = (BeanDescription) om.serializationConfig().introspect(javaType); List props = desc.findProperties(); if (props.size() != 1) { fail("Should find 1 property, not "+props.size()+"; properties = "+props); diff --git a/src/test/java/com/fasterxml/jackson/databind/jsonschema/NewSchemaTest.java b/src/test/java/com/fasterxml/jackson/databind/jsonschema/NewSchemaTest.java index e62a025ca2..aedd42251f 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsonschema/NewSchemaTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsonschema/NewSchemaTest.java @@ -12,7 +12,6 @@ import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.jsonFormatVisitors.*; import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; /** * Basic tests to exercise low-level support added for JSON Schema module and @@ -144,11 +143,7 @@ private void _visit(BeanProperty prop) throws JsonMappingException if (prov == null) { throw new Error("SerializerProvider missing"); } - ser = prov.findValueSerializer(prop.getType(), prop); - } - // and this just for bit of extra coverage... - if (ser instanceof StdSerializer) { - assertNotNull(((StdSerializer) ser).getSchema(prov, prop.getType())); + ser = prov.findPrimaryPropertySerializer(prop.getType(), prop); } JsonFormatVisitorWrapper visitor = new JsonFormatVisitorWrapper.Base(getProvider()); ser.acceptJsonFormatVisitor(visitor, prop.getType()); @@ -310,7 +305,7 @@ public void optionalProperty(BeanProperty prop) throws JsonMappingException { } final SerializerProvider prov = getProvider(); if (ser == null) { - ser = prov.findValueSerializer(prop.getType(), prop); + ser = prov.findPrimaryPropertySerializer(prop.getType(), prop); } ser.acceptJsonFormatVisitor(new JsonFormatVisitorWrapper.Base() { @Override diff --git a/src/test/java/com/fasterxml/jackson/databind/jsonschema/TestGenerateJsonSchema.java b/src/test/java/com/fasterxml/jackson/databind/jsonschema/TestGenerateJsonSchema.java deleted file mode 100644 index 7edfd4dae2..0000000000 --- a/src/test/java/com/fasterxml/jackson/databind/jsonschema/TestGenerateJsonSchema.java +++ /dev/null @@ -1,250 +0,0 @@ -package com.fasterxml.jackson.databind.jsonschema; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.Collection; -import java.util.Map; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.fasterxml.jackson.databind.ser.FilterProvider; -import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; -import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; - -/** - * @author Ryan Heaton - */ -@SuppressWarnings("deprecation") -public class TestGenerateJsonSchema - extends com.fasterxml.jackson.databind.BaseMapTest -{ - /* - /********************************************************** - /* Helper classes - /********************************************************** - */ - - public static class SimpleBean - { - private int property1; - private String property2; - private String[] property3; - private Collection property4; - @JsonProperty(required=true) - private String property5; - - public int getProperty1() - { - return property1; - } - - public void setProperty1(int property1) - { - this.property1 = property1; - } - - public String getProperty2() - { - return property2; - } - - public void setProperty2(String property2) - { - this.property2 = property2; - } - - public String[] getProperty3() - { - return property3; - } - - public void setProperty3(String[] property3) - { - this.property3 = property3; - } - - public Collection getProperty4() - { - return property4; - } - - public void setProperty4(Collection property4) - { - this.property4 = property4; - } - - public String getProperty5() - { - return property5; - } - - public void setProperty5(String property5) - { - this.property5 = property5; - } - } - - public class TrivialBean { - public String name; - } - - @JsonSerializableSchema(id="myType") - public class BeanWithId { - public String value; - } - - static class UnwrappingRoot - { - public int age; - - @JsonUnwrapped(prefix="name.") - public Name name; - } - - static class Name { - public String first, last; - } - - @JsonPropertyOrder({ "dec", "bigInt" }) - static class Numbers { - public BigDecimal dec; - public BigInteger bigInt; - } - - /* - /********************************************************** - /* Unit tests - /********************************************************** - */ - - private final ObjectMapper MAPPER = new ObjectMapper(); - - /** - * tests generating json-schema stuff. - */ - public void testOldSchemaGeneration() throws Exception - { - JsonSchema jsonSchema = MAPPER.generateJsonSchema(SimpleBean.class); - - assertNotNull(jsonSchema); - - // test basic equality, and that equals() handles null, other obs - assertTrue(jsonSchema.equals(jsonSchema)); - assertFalse(jsonSchema.equals(null)); - assertFalse(jsonSchema.equals("foo")); - - // other basic things - assertNotNull(jsonSchema.toString()); - assertNotNull(JsonSchema.getDefaultSchemaNode()); - - ObjectNode root = jsonSchema.getSchemaNode(); - assertEquals("object", root.get("type").asText()); - assertEquals(false, root.path("required").booleanValue()); - JsonNode propertiesSchema = root.get("properties"); - assertNotNull(propertiesSchema); - JsonNode property1Schema = propertiesSchema.get("property1"); - assertNotNull(property1Schema); - assertEquals("integer", property1Schema.get("type").asText()); - assertEquals(false, property1Schema.path("required").booleanValue()); - JsonNode property2Schema = propertiesSchema.get("property2"); - assertNotNull(property2Schema); - assertEquals("string", property2Schema.get("type").asText()); - assertEquals(false, property2Schema.path("required").booleanValue()); - JsonNode property3Schema = propertiesSchema.get("property3"); - assertNotNull(property3Schema); - assertEquals("array", property3Schema.get("type").asText()); - assertEquals(false, property3Schema.path("required").booleanValue()); - assertEquals("string", property3Schema.get("items").get("type").asText()); - JsonNode property4Schema = propertiesSchema.get("property4"); - assertNotNull(property4Schema); - assertEquals("array", property4Schema.get("type").asText()); - assertEquals(false, property4Schema.path("required").booleanValue()); - assertEquals("number", property4Schema.get("items").get("type").asText()); - } - - @JsonFilter("filteredBean") - protected static class FilteredBean { - - @JsonProperty - private String secret = "secret"; - - @JsonProperty - private String obvious = "obvious"; - - public String getSecret() { return secret; } - public void setSecret(String s) { secret = s; } - - public String getObvious() { return obvious; } - public void setObvious(String s) {obvious = s; } - } - - final static FilterProvider secretFilterProvider = new SimpleFilterProvider() - .addFilter("filteredBean", SimpleBeanPropertyFilter.filterOutAllExcept(new String[]{"obvious"})); - - public void testGeneratingJsonSchemaWithFilters() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.setFilters(secretFilterProvider); - JsonSchema schema = mapper.generateJsonSchema(FilteredBean.class); - JsonNode node = schema.getSchemaNode().get("properties"); - assertTrue(node.has("obvious")); - assertFalse(node.has("secret")); - } - - /** - * Additional unit test for verifying that schema object itself - * can be properly serialized - */ - public void testSchemaSerialization() throws Exception - { - JsonSchema jsonSchema = MAPPER.generateJsonSchema(SimpleBean.class); - Map result = writeAndMap(MAPPER, jsonSchema); - assertNotNull(result); - // no need to check out full structure, just basics... - assertEquals("object", result.get("type")); - // only add 'required' if it is true... - assertNull(result.get("required")); - assertNotNull(result.get("properties")); - } - - public void testThatObjectsHaveNoItems() throws Exception - { - JsonSchema jsonSchema = MAPPER.generateJsonSchema(TrivialBean.class); - String json = jsonSchema.toString().replaceAll("\"", "'"); - // can we count on ordering being stable? I think this is true with current ObjectNode impl - // as perh [JACKSON-563]; 'required' is only included if true - assertEquals("{'type':'object','properties':{'name':{'type':'string'}}}", - json); - } - - public void testSchemaId() throws Exception - { - JsonSchema jsonSchema = MAPPER.generateJsonSchema(BeanWithId.class); - String json = jsonSchema.toString().replaceAll("\"", "'"); - assertEquals("{'type':'object','id':'myType','properties':{'value':{'type':'string'}}}", - json); - } - - // [databind#271] - public void testUnwrapping() throws Exception - { - JsonSchema jsonSchema = MAPPER.generateJsonSchema(UnwrappingRoot.class); - String json = jsonSchema.toString().replaceAll("\"", "'"); - String EXP = "{'type':'object'," - +"'properties':{'age':{'type':'integer'}," - +"'name.first':{'type':'string'},'name.last':{'type':'string'}}}"; - assertEquals(EXP, json); - } - - // - public void testNumberTypes() throws Exception - { - JsonSchema jsonSchema = MAPPER.generateJsonSchema(Numbers.class); - String json = quotesToApos(jsonSchema.toString()); - String EXP = "{'type':'object'," - +"'properties':{'dec':{'type':'number'}," - +"'bigInt':{'type':'integer'}}}"; - assertEquals(EXP, json); - } -} diff --git a/src/test/java/com/fasterxml/jackson/databind/jsonschema/TestReadJsonSchema.java b/src/test/java/com/fasterxml/jackson/databind/jsonschema/TestReadJsonSchema.java deleted file mode 100644 index 164533bc8a..0000000000 --- a/src/test/java/com/fasterxml/jackson/databind/jsonschema/TestReadJsonSchema.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.fasterxml.jackson.databind.jsonschema; - -import java.util.*; - -import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.jsonschema.JsonSchema; - -/** - * Trivial test to ensure JsonSchema can be also deserialized - */ -@SuppressWarnings("deprecation") -public class TestReadJsonSchema - extends com.fasterxml.jackson.databind.BaseMapTest -{ - enum SchemaEnum { YES, NO; } - - static class Schemable { - public String name; - public char[] nameBuffer; - - // We'll include tons of stuff, just to force generation of schema - public boolean[] states; - public byte[] binaryData; - public short[] shorts; - public int[] ints; - public long[] longs; - - public float[] floats; - public double[] doubles; - - public Object[] objects; - public JsonSerializable someSerializable; - - public Iterable iterableOhYeahBaby; - - public List extra; - public ArrayList extra2; - public Iterator extra3; - - public Map sizes; - public EnumMap> whatever; - - SchemaEnum testEnum; - public EnumSet testEnums; - } - - /** - * Verifies that a simple schema that is serialized can be - * deserialized back to equal schema instance - */ - public void testDeserializeSimple() throws Exception - { - ObjectMapper mapper = new ObjectMapper(); - JsonSchema schema = mapper.generateJsonSchema(Schemable.class); - assertNotNull(schema); - - String schemaStr = mapper.writeValueAsString(schema); - assertNotNull(schemaStr); - JsonSchema result = mapper.readValue(schemaStr, JsonSchema.class); - assertEquals("Trying to read from '"+schemaStr+"'", schema, result); - } -} diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/AbstractTypeMapping1186Test.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/AbstractTypeMapping1186Test.java index a5a453736c..1fa1bca239 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/AbstractTypeMapping1186Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/AbstractTypeMapping1186Test.java @@ -34,7 +34,9 @@ public static class MyObject { public void testDeserializeMyContainer() throws Exception { SimpleModule module = new SimpleModule().addAbstractTypeMapping(IContainer.class, MyContainer.class); - final ObjectMapper mapper = new ObjectMapper().registerModule(module); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(module) + .build(); String json = "{\"ts\": [ { \"msg\": \"hello\"} ] }"; final Object o = mapper.readValue(json, mapper.getTypeFactory().constructParametricType(IContainer.class, MyObject.class)); diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/Generic1128Test.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/Generic1128Test.java index 6f3fe37e30..0a19238baa 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/Generic1128Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/Generic1128Test.java @@ -48,9 +48,9 @@ static class DevMContainer extends ContainerBase{ } public void testIssue1128() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); - + ObjectMapper mapper = jsonMapperBuilder() + .changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_EMPTY)) + .build(); final DevMContainer devMContainer1 = new DevMContainer(); final DevM entity = new DevM(); final Dev parent = new Dev(); diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/NoTypeInfoTest.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/NoTypeInfoTest.java index 65054d59a4..f5e8fe8531 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/NoTypeInfoTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/NoTypeInfoTest.java @@ -24,8 +24,9 @@ final static class NoType implements NoTypeInterface { public void testWithIdNone() throws Exception { - final ObjectMapper mapper = newObjectMapper(); - mapper.enableDefaultTyping(); + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping() + .build(); // serialize without type info String json = mapper.writeValueAsString(new NoType()); assertEquals("{\"a\":3}", json); diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/PolymorphicList1451SerTest.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/PolymorphicList1451SerTest.java index 6617c0b336..c5ff6d4f21 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/PolymorphicList1451SerTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/PolymorphicList1451SerTest.java @@ -19,10 +19,10 @@ public static class B extends A { private final String CLASS_NAME = getClass().getSimpleName(); public void testCollectionWithTypeInfo() throws Exception { - ObjectMapper mapper = new ObjectMapper() + ObjectMapper mapper = jsonMapperBuilder() .disable(SerializationFeature.EAGER_SERIALIZER_FETCH) // .disable(DeserializationFeature.EAGER_DESERIALIZER_FETCH) - ; + .build(); List input = new ArrayList(); A a = new A(); diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/PolymorphicViaRefTypeTest.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/PolymorphicViaRefTypeTest.java index 5610d8a023..67cbb2c51c 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/PolymorphicViaRefTypeTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/PolymorphicViaRefTypeTest.java @@ -6,7 +6,6 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping; public class PolymorphicViaRefTypeTest extends BaseMapTest { @@ -61,8 +60,9 @@ public void testPolymorphicAtomicRefProperty() throws Exception public void testAtomicRefViaDefaultTyping() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.enableDefaultTyping(DefaultTyping.NON_FINAL); + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.NON_FINAL) + .build(); AtomicStringWrapper data = new AtomicStringWrapper("foo"); String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(data); AtomicStringWrapper result = mapper.readValue(json, AtomicStringWrapper.class); diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/SubTypeResolution1964Test.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/SubTypeResolution1964Test.java index 616e316e07..0f22c9690e 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/SubTypeResolution1964Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/SubTypeResolution1964Test.java @@ -72,7 +72,7 @@ public MetaAttribute() { } /********************************************************************** */ - final ObjectMapper MAPPER = newObjectMapper(); + final ObjectMapper MAPPER = newJsonMapper(); // [databind#1964] public void testTypeCompatibility1964() throws Exception @@ -85,7 +85,7 @@ public void testTypeCompatibility1964() throws Exception Collection values = new HashSet<>(); values.add("ARTIFACTS_RESOLVE"); repoPrivilegesMap.put(key, values); - + AccessModel accessModel = new AccessModel(); accessModel.setRepositoryPrivileges(repoPrivilegesMap); diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestAbstractContainers.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestAbstractContainers.java index 49925efc13..f5196e7640 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestAbstractContainers.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestAbstractContainers.java @@ -18,7 +18,7 @@ public class TestAbstractContainers extends BaseMapTest @JsonSubTypes.Type(value = MapWrapper.class, name = "wrapper"), }) static class MapWrapper { - public IDataValueMap map = new DataValueMap(); // This does NOT work + public IDataValueMap map = new DataValueMap(); // This does NOT work } @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="_type_") diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestAbstractTypeNames.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestAbstractTypeNames.java index b98ce41000..7c3cc30461 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestAbstractTypeNames.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestAbstractTypeNames.java @@ -8,7 +8,6 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping; /** * Unit tests for checking how combination of interfaces, implementation @@ -97,8 +96,9 @@ final static class BeanWithAnon { public void testEmptyCollection() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.configure(SerializationFeature.INDENT_OUTPUT, true); + ObjectMapper mapper = jsonMapperBuilder() + .configure(SerializationFeature.INDENT_OUTPUT, true) + .build(); Listfriends = new ArrayList(); friends.add(new DefaultUser("Joe Hildebrandt", null)); friends.add(new DefaultEmployee("Richard Nasr",null,"MDA")); @@ -109,10 +109,11 @@ public void testEmptyCollection() throws Exception /* 24-Feb-2011, tatu: For now let's simply require registration of * concrete subtypes; can't think of a way to avoid that for now */ - mapper = new ObjectMapper(); - mapper.registerSubtypes(DefaultEmployee.class); - mapper.registerSubtypes(DefaultUser.class); - + mapper = jsonMapperBuilder() + .registerSubtypes(DefaultEmployee.class, + DefaultUser.class) + .build(); + User result = mapper.readValue(json, User.class); assertNotNull(result); assertEquals(DefaultEmployee.class, result.getClass()); @@ -126,8 +127,9 @@ public void testEmptyCollection() throws Exception // [JACKSON-584]: change anonymous non-static inner type into static type: public void testInnerClassWithType() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.enableDefaultTyping(DefaultTyping.NON_FINAL); + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.NON_FINAL) + .build(); String json = mapper.writeValueAsString(new BeanWithAnon()); BeanWithAnon result = mapper.readValue(json, BeanWithAnon.class); assertEquals(BeanWithAnon.class, result.getClass()); diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestBaseTypeAsDefault.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestBaseTypeAsDefault.java index f5789c04e4..e319725ee2 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestBaseTypeAsDefault.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestBaseTypeAsDefault.java @@ -37,11 +37,11 @@ static class ChildOfChild extends ChildOfAbstract { /********************************************************** */ - protected ObjectMapper MAPPER_WITH_BASE = objectMapperBuilder() + protected ObjectMapper MAPPER_WITH_BASE = jsonMapperBuilder() .enable(MapperFeature.USE_BASE_TYPE_AS_DEFAULT_IMPL) .build(); - protected ObjectMapper MAPPER_WITHOUT_BASE = objectMapperBuilder() + protected ObjectMapper MAPPER_WITHOUT_BASE = jsonMapperBuilder() .disable(MapperFeature.USE_BASE_TYPE_AS_DEFAULT_IMPL) .build(); 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 0f3cc04b76..fc75449f54 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestGenericListSerialization.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestGenericListSerialization.java @@ -54,7 +54,8 @@ public void testSubTypesFor356() throws Exception embedded.add(new Child1()); embedded.add(new Child2()); input.setResult(embedded); - ObjectMapper mapper = objectMapperBuilder() + + ObjectMapper mapper = jsonMapperBuilder() .configure(MapperFeature.USE_STATIC_TYPING, true) .build(); diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPolymorphicDeserialization676.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPolymorphicDeserialization676.java index d6b1bcc557..c577511f71 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPolymorphicDeserialization676.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPolymorphicDeserialization676.java @@ -85,8 +85,9 @@ public void testDeSerFail() throws IOException { } public void testDeSerCorrect() throws IOException { - ObjectMapper mapper = new ObjectMapper(); - mapper.enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS); + ObjectMapper mapper = jsonMapperBuilder() + .enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS) + .build(); Map map = new HashMap(); map.put("1", 1); // commenting out the following statement will fail the test diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPolymorphicWithDefaultImpl.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPolymorphicWithDefaultImpl.java index 7d260c7851..6ffc53bfdd 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPolymorphicWithDefaultImpl.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPolymorphicWithDefaultImpl.java @@ -5,7 +5,6 @@ import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.annotation.NoClass; import com.fasterxml.jackson.databind.exc.InvalidTypeIdException; /** @@ -43,14 +42,6 @@ else if (obj instanceof String) { } } - /** - * Note: NoClass here has special meaning, of mapping invalid - * types into null instances. - */ - @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", - defaultImpl = NoClass.class) - public static class DefaultWithNoClass { } - /** * Also another variant to verify that from 2.5 on, can use non-deprecated * value for the same. @@ -178,17 +169,10 @@ public void testDeserializationWithArrayOfSize2() throws Exception } // [databind#148] - public void testDefaultAsNoClass() throws Exception - { - Object ob = MAPPER.readerFor(DefaultWithNoClass.class).readValue("{ }"); - assertNull(ob); - ob = MAPPER.readerFor(DefaultWithNoClass.class).readValue("{ \"bogus\":3 }"); - assertNull(ob); - } - - // same, with 2.5 and Void.class public void testDefaultAsVoid() throws Exception { + // 07-Mar-2018, tatu: Specifically, use of `Void` should infer that unknown type + // values should become `null`s Object ob = MAPPER.readerFor(DefaultWithVoidAsDefault.class).readValue("{ }"); assertNull(ob); ob = MAPPER.readerFor(DefaultWithVoidAsDefault.class).readValue("{ \"bogus\":3 }"); @@ -198,8 +182,9 @@ public void testDefaultAsVoid() throws Exception // [databind#148] public void testBadTypeAsNull() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.disable(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE); + ObjectMapper mapper = jsonMapperBuilder() + .disable(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE) + .build(); Object ob = mapper.readValue("{}", MysteryPolymorphic.class); assertNull(ob); ob = mapper.readValue("{ \"whatever\":13}", MysteryPolymorphic.class); @@ -249,8 +234,9 @@ public void testUnknownTypeIDRecovery() throws Exception public void testUnknownClassAsSubtype() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false); + ObjectMapper mapper = jsonMapperBuilder() + .configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false) + .build(); BaseWrapper w = mapper.readValue(aposToQuotes ("{'value':{'clazz':'com.foobar.Nothing'}}'"), BaseWrapper.class); diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestScalars.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestScalars.java index 89481bf7df..268b34abe0 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestScalars.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestScalars.java @@ -44,13 +44,13 @@ public ScalarList add(Object v) { } /* - /********************************************************** - /* Unit tests - /********************************************************** + /********************************************************************** + /* Test methods + /********************************************************************** */ - final ObjectMapper MAPPER = newObjectMapper(); - + final ObjectMapper MAPPER = newJsonMapper(); + /** * Ensure that per-property dynamic types work, both for "native" types * and others diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestSubtypes.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestSubtypes.java index 5517a2cc4e..2d1362c658 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestSubtypes.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestSubtypes.java @@ -1,22 +1,18 @@ package com.fasterxml.jackson.databind.jsontype; - -import com.fasterxml.jackson.core.Version; - import java.util.*; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.annotation.JsonTypeInfo.As; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; + +import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.exc.InvalidTypeIdException; import com.fasterxml.jackson.databind.jsontype.NamedType; import com.fasterxml.jackson.databind.module.SimpleModule; -public class TestSubtypes extends com.fasterxml.jackson.databind.BaseMapTest +public class TestSubtypes extends BaseMapTest { @JsonTypeInfo(use=JsonTypeInfo.Id.NAME) static abstract class SuperType { @@ -54,22 +50,6 @@ static class PropertyBean public PropertyBean(SuperType v) { value = v; } } - @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=As.PROPERTY, - property="#type", - defaultImpl=DefaultImpl.class) - static abstract class SuperTypeWithDefault { } - - static class DefaultImpl extends SuperTypeWithDefault { - public int a; - } - - @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=As.PROPERTY, property="#type") - static abstract class SuperTypeWithoutDefault { } - - static class DefaultImpl505 extends SuperTypeWithoutDefault { - public int a; - } - @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=As.PROPERTY, property="type") @JsonSubTypes({ @JsonSubTypes.Type(ImplX.class), @JsonSubTypes.Type(ImplY.class) }) @@ -163,18 +143,19 @@ static class Factory1311ImplA implements Factory1311 { } static class Factory1311ImplB implements Factory1311 { } /* - /********************************************************** + /********************************************************************** /* Unit tests - /********************************************************** + /********************************************************************** */ private final ObjectMapper MAPPER = new ObjectMapper(); public void testPropertyWithSubtypes() throws Exception { - ObjectMapper mapper = new ObjectMapper(); // must register subtypes - mapper.registerSubtypes(SubB.class, SubC.class, SubD.class); + ObjectMapper mapper = jsonMapperBuilder() + .registerSubtypes(SubB.class, SubC.class, SubD.class) + .build(); String json = mapper.writeValueAsString(new PropertyBean(new SubC())); PropertyBean result = mapper.readValue(json, PropertyBean.class); assertSame(SubC.class, result.value.getClass()); @@ -183,23 +164,25 @@ public void testPropertyWithSubtypes() throws Exception // also works via modules public void testSubtypesViaModule() throws Exception { - ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); module.registerSubtypes(SubB.class, SubC.class, SubD.class); - mapper.registerModule(module); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(module) + .build(); String json = mapper.writeValueAsString(new PropertyBean(new SubC())); PropertyBean result = mapper.readValue(json, PropertyBean.class); assertSame(SubC.class, result.value.getClass()); // and as per [databind#1653]: - mapper = new ObjectMapper(); module = new SimpleModule(); List> l = new ArrayList<>(); l.add(SubB.class); l.add(SubC.class); l.add(SubD.class); module.registerSubtypes(l); - mapper.registerModule(module); + mapper = jsonMapperBuilder() + .addModule(module) + .build(); json = mapper.writeValueAsString(new PropertyBean(new SubC())); result = mapper.readValue(json, PropertyBean.class); assertSame(SubC.class, result.value.getClass()); @@ -212,8 +195,9 @@ public void testSerialization() throws Exception assertEquals("{\"@type\":\"TypeB\",\"b\":1}", MAPPER.writeValueAsString(bean)); // but we can override type name here too - ObjectMapper mapper = new ObjectMapper(); - mapper.registerSubtypes(new NamedType(SubB.class, "typeB")); + ObjectMapper mapper = jsonMapperBuilder() + .registerSubtypes(new NamedType(SubB.class, "typeB")) + .build(); assertEquals("{\"@type\":\"typeB\",\"b\":1}", mapper.writeValueAsString(bean)); // and default name ought to be simple class name; with context @@ -222,9 +206,9 @@ public void testSerialization() throws Exception public void testDeserializationNonNamed() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerSubtypes(SubC.class); - + ObjectMapper mapper = jsonMapperBuilder() + .registerSubtypes(SubC.class) + .build(); // default name should be unqualified class name SuperType bean = mapper.readValue("{\"@type\":\"TestSubtypes$SubC\", \"c\":1}", SuperType.class); assertSame(SubC.class, bean.getClass()); @@ -233,9 +217,10 @@ public void testDeserializationNonNamed() throws Exception public void testDeserializatioNamed() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerSubtypes(SubB.class); - mapper.registerSubtypes(new NamedType(SubD.class, "TypeD")); + ObjectMapper mapper = jsonMapperBuilder() + .registerSubtypes(SubB.class) + .registerSubtypes(new NamedType(SubD.class, "TypeD")) + .build(); SuperType bean = mapper.readValue("{\"@type\":\"TypeB\", \"b\":13}", SuperType.class); assertSame(SubB.class, bean.getClass()); @@ -247,78 +232,30 @@ public void testDeserializatioNamed() throws Exception assertEquals(-4, ((SubD) bean).d); } - // Trying to reproduce [JACKSON-366] public void testEmptyBean() throws Exception { // First, with annotations - ObjectMapper mapper = new ObjectMapper(); - mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, true); + ObjectMapper mapper = jsonMapperBuilder() + .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, true) + .build(); String json = mapper.writeValueAsString(new EmptyBean()); assertEquals("{\"@type\":\"TestSubtypes$EmptyBean\"}", json); - mapper = new ObjectMapper(); - mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + mapper = jsonMapperBuilder() + .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false) + .build(); json = mapper.writeValueAsString(new EmptyBean()); assertEquals("{\"@type\":\"TestSubtypes$EmptyBean\"}", json); // and then with defaults - mapper = new ObjectMapper(); - mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); - mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + mapper = jsonMapperBuilder() + .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false) + .enableDefaultTyping(DefaultTyping.NON_FINAL) + .build(); json = mapper.writeValueAsString(new EmptyNonFinal()); assertEquals("[\"com.fasterxml.jackson.databind.jsontype.TestSubtypes$EmptyNonFinal\",{}]", json); } - public void testDefaultImpl() throws Exception - { - // first, test with no type information - SuperTypeWithDefault bean = MAPPER.readValue("{\"a\":13}", SuperTypeWithDefault.class); - assertEquals(DefaultImpl.class, bean.getClass()); - assertEquals(13, ((DefaultImpl) bean).a); - - // and then with unmapped info - bean = MAPPER.readValue("{\"a\":14,\"#type\":\"foobar\"}", SuperTypeWithDefault.class); - assertEquals(DefaultImpl.class, bean.getClass()); - assertEquals(14, ((DefaultImpl) bean).a); - - bean = MAPPER.readValue("{\"#type\":\"foobar\",\"a\":15}", SuperTypeWithDefault.class); - assertEquals(DefaultImpl.class, bean.getClass()); - assertEquals(15, ((DefaultImpl) bean).a); - - bean = MAPPER.readValue("{\"#type\":\"foobar\"}", SuperTypeWithDefault.class); - assertEquals(DefaultImpl.class, bean.getClass()); - assertEquals(0, ((DefaultImpl) bean).a); - } - - // [JACKSON-505]: ok to also default to mapping there might be for base type - public void testDefaultImplViaModule() throws Exception - { - final String JSON = "{\"a\":123}"; - - // first: without registration etc, epic fail: - try { - MAPPER.readValue(JSON, SuperTypeWithoutDefault.class); - fail("Expected an exception"); - } catch (InvalidTypeIdException e) { - verifyException(e, "missing type id property '#type'"); - } - - // but then succeed when we register default impl - ObjectMapper mapper = new ObjectMapper(); - SimpleModule module = new SimpleModule("test", Version.unknownVersion()); - module.addAbstractTypeMapping(SuperTypeWithoutDefault.class, DefaultImpl505.class); - mapper.registerModule(module); - SuperTypeWithoutDefault bean = mapper.readValue(JSON, SuperTypeWithoutDefault.class); - assertNotNull(bean); - assertEquals(DefaultImpl505.class, bean.getClass()); - assertEquals(123, ((DefaultImpl505) bean).a); - - bean = mapper.readValue("{\"#type\":\"foobar\"}", SuperTypeWithoutDefault.class); - assertEquals(DefaultImpl505.class, bean.getClass()); - assertEquals(0, ((DefaultImpl505) bean).a); - - } - public void testErrorMessage() throws Exception { ObjectMapper mapper = new ObjectMapper(); try { diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestSubtypesWithDefaultImpl.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestSubtypesWithDefaultImpl.java new file mode 100644 index 0000000000..108e56100f --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestSubtypesWithDefaultImpl.java @@ -0,0 +1,86 @@ +package com.fasterxml.jackson.databind.jsontype; + +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeInfo.As; +import com.fasterxml.jackson.core.Version; +import com.fasterxml.jackson.databind.BaseMapTest; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.exc.InvalidTypeIdException; +import com.fasterxml.jackson.databind.module.SimpleModule; + +public class TestSubtypesWithDefaultImpl extends BaseMapTest +{ + @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=As.PROPERTY, + property="#type", + defaultImpl=DefaultImpl.class) + static abstract class SuperTypeWithDefault { } + + static class DefaultImpl extends SuperTypeWithDefault { + public int a; + } + + @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=As.PROPERTY, property="#type") + static abstract class SuperTypeWithoutDefault { } + + static class DefaultImpl505 extends SuperTypeWithoutDefault { + public int a; + } + + /* + /********************************************************************** + /* Unit tests + /********************************************************************** + */ + + private final ObjectMapper MAPPER = new ObjectMapper(); + + public void testDefaultImpl() throws Exception + { + // first, test with no type information + SuperTypeWithDefault bean = MAPPER.readValue("{\"a\":13}", SuperTypeWithDefault.class); + assertEquals(DefaultImpl.class, bean.getClass()); + assertEquals(13, ((DefaultImpl) bean).a); + + // and then with unmapped info + bean = MAPPER.readValue("{\"a\":14,\"#type\":\"foobar\"}", SuperTypeWithDefault.class); + assertEquals(DefaultImpl.class, bean.getClass()); + assertEquals(14, ((DefaultImpl) bean).a); + + bean = MAPPER.readValue("{\"#type\":\"foobar\",\"a\":15}", SuperTypeWithDefault.class); + assertEquals(DefaultImpl.class, bean.getClass()); + assertEquals(15, ((DefaultImpl) bean).a); + + bean = MAPPER.readValue("{\"#type\":\"foobar\"}", SuperTypeWithDefault.class); + assertEquals(DefaultImpl.class, bean.getClass()); + assertEquals(0, ((DefaultImpl) bean).a); + } + + // [JACKSON-505]: ok to also default to mapping there might be for base type + public void testDefaultImplViaModule() throws Exception + { + final String JSON = "{\"a\":123}"; + + // first: without registration etc, epic fail: + try { + MAPPER.readValue(JSON, SuperTypeWithoutDefault.class); + fail("Expected an exception"); + } catch (InvalidTypeIdException e) { + verifyException(e, "missing type id property '#type'"); + } + + // but then succeed when we register default impl + SimpleModule module = new SimpleModule("test", Version.unknownVersion()); + module.addAbstractTypeMapping(SuperTypeWithoutDefault.class, DefaultImpl505.class); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(module) + .build(); + SuperTypeWithoutDefault bean = mapper.readValue(JSON, SuperTypeWithoutDefault.class); + assertNotNull(bean); + assertEquals(DefaultImpl505.class, bean.getClass()); + assertEquals(123, ((DefaultImpl505) bean).a); + + bean = mapper.readValue("{\"#type\":\"foobar\"}", SuperTypeWithoutDefault.class); + assertEquals(DefaultImpl505.class, bean.getClass()); + assertEquals(0, ((DefaultImpl505) bean).a); + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypeNames.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypeNames.java index 735b22d3c7..94cefac8e6 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypeNames.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypeNames.java @@ -45,7 +45,7 @@ public void testBaseTypeId1616() throws Exception { ObjectMapper mapper = new ObjectMapper(); Collection subtypes = new StdSubtypeResolver().collectAndResolveSubtypesByTypeId( - mapper.getDeserializationConfig(), + mapper.deserializationConfig(), // note: `null` is fine here as `AnnotatedMember`: null, mapper.constructType(Base1616.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 dfd562ede5..5ebe69d07a 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedArrayDeserialization.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedArrayDeserialization.java @@ -3,8 +3,6 @@ import java.util.ArrayList; import java.util.LinkedList; -import static org.junit.Assert.assertArrayEquals; - import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.databind.*; @@ -102,11 +100,12 @@ public void testLongListAsWrapper() throws Exception public void testLongArray() throws Exception { - ObjectMapper m = new ObjectMapper(); // use class name, WRAPPER_OBJECT - m.addMixIn(long[].class, WrapperMixIn.class); + ObjectMapper mapper = jsonMapperBuilder() + .addMixIn(long[].class, WrapperMixIn.class) + .build(); String JSON = "{\""+long[].class.getName()+"\":[5, 6, 7]}"; - long[] value = m.readValue(JSON, long[].class); + long[] value = mapper.readValue(JSON, long[].class); assertNotNull(value); assertEquals(3, value.length); assertArrayEquals(new long[] { 5L, 6L, 7L} , 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 102c2017d9..61bc9b3d26 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedArraySerialization.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedArraySerialization.java @@ -126,8 +126,9 @@ public void testStringListAsObjectWrapper() throws Exception public void testIntArray() throws Exception { - ObjectMapper m = new ObjectMapper(); - m.addMixIn(int[].class, WrapperMixIn.class); + ObjectMapper m = jsonMapperBuilder() + .addMixIn(int[].class, WrapperMixIn.class) + .build(); int[] input = new int[] { 1, 2, 3 }; String clsName = int[].class.getName(); assertEquals("{\""+clsName+"\":[1,2,3]}", m.writeValueAsString(input)); @@ -148,8 +149,8 @@ public void testGenericArray() throws Exception assertEquals(EXP, MAPPER.writeValueAsString(input)); // then with static typing enabled: - ObjectMapper m = objectMapperBuilder() - .configure(MapperFeature.USE_STATIC_TYPING, true) + ObjectMapper m = jsonMapperBuilder() + .enable(MapperFeature.USE_STATIC_TYPING) .build(); assertEquals(EXP, m.writeValueAsString(input)); } 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 c1ea52c2e7..8767ef8046 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedDeserialization.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedDeserialization.java @@ -1,5 +1,6 @@ package com.fasterxml.jackson.databind.jsontype; +import java.io.IOException; import java.util.*; import com.fasterxml.jackson.annotation.*; @@ -106,15 +107,17 @@ static class Issue506NumberBean /* Unit tests /********************************************************** */ - + + private final ObjectMapper MAPPER = newJsonMapper(); + /** * First things first, let's ensure we can serialize using * class name, written as main-level property name */ public void testSimpleClassAsProperty() throws Exception { - ObjectMapper m = new ObjectMapper(); - Animal a = m.readValue(asJSONObjectValueString("@classy", Cat.class.getName(), + Animal a = MAPPER.readValue(asJSONObjectValueString(MAPPER, + "@classy", Cat.class.getName(), "furColor", "tabby", "name", "Garfield"), Animal.class); assertNotNull(a); assertEquals(Cat.class, a.getClass()); @@ -126,8 +129,9 @@ public void testSimpleClassAsProperty() throws Exception // Test inclusion using wrapper style public void testTypeAsWrapper() throws Exception { - ObjectMapper m = new ObjectMapper(); - m.addMixIn(Animal.class, TypeWithWrapper.class); + ObjectMapper m = jsonMapperBuilder() + .addMixIn(Animal.class, TypeWithWrapper.class) + .build(); String JSON = "{\".TestTypedDeserialization$Dog\" : " +asJSONObjectValueString(m, "name", "Scooby", "boneCount", "6")+" }"; Animal a = m.readValue(JSON, Animal.class); @@ -141,8 +145,9 @@ public void testTypeAsWrapper() throws Exception // Test inclusion using 2-element array public void testTypeAsArray() throws Exception { - ObjectMapper m = new ObjectMapper(); - m.addMixIn(Animal.class, TypeWithArray.class); + ObjectMapper m = jsonMapperBuilder() + .addMixIn(Animal.class, TypeWithArray.class) + .build(); // hmmh. Not good idea to rely on exact output, order may change. But... String JSON = "[\""+Dog.class.getName()+"\", " +asJSONObjectValueString(m, "name", "Martti", "boneCount", "11")+" ]"; @@ -156,23 +161,24 @@ public void testTypeAsArray() throws Exception // Use basic Animal as contents of a regular List public void testListAsArray() throws Exception { - ObjectMapper m = new ObjectMapper(); // This time using PROPERTY style (default) again String JSON = "[\n" - +asJSONObjectValueString(m, "@classy", Cat.class.getName(), "name", "Hello", "furColor", "white") - +",\n" - // let's shuffle doggy's fields a bit for testing - +asJSONObjectValueString(m, - "boneCount", Integer.valueOf(1), - "@classy", Dog.class.getName(), - "name", "Bob" - ) - +",\n" - +asJSONObjectValueString(m, "@classy", Fish.class.getName()) - +", null\n]"; + +asJSONObjectValueString(MAPPER, + "@classy", Cat.class.getName(), "name", "Hello", "furColor", "white") + +",\n" + // let's shuffle doggy's fields a bit for testing + +asJSONObjectValueString(MAPPER, + "boneCount", Integer.valueOf(1), + "@classy", Dog.class.getName(), + "name", "Bob" + ) + +",\n" + +asJSONObjectValueString(MAPPER, + "@classy", Fish.class.getName()) + +", null\n]"; JavaType expType = TypeFactory.defaultInstance().constructCollectionType(ArrayList.class, Animal.class); - List animals = m.readValue(JSON, expType); + List animals = MAPPER.readValue(JSON, expType); assertNotNull(animals); assertEquals(4, animals.size()); Cat c = (Cat) animals.get(0); @@ -188,11 +194,11 @@ public void testListAsArray() throws Exception public void testCagedAnimal() throws Exception { - ObjectMapper m = new ObjectMapper(); - String jsonCat = asJSONObjectValueString(m, "@classy", Cat.class.getName(), "name", "Nilson", "furColor", "black"); + String jsonCat = asJSONObjectValueString(MAPPER, + "@classy", Cat.class.getName(), "name", "Nilson", "furColor", "black"); String JSON = "{\"animal\":"+jsonCat+"}"; - AnimalContainer cont = m.readValue(JSON, AnimalContainer.class); + AnimalContainer cont = MAPPER.readValue(JSON, AnimalContainer.class); assertNotNull(cont); Animal a = cont.animal; assertNotNull(a); @@ -220,10 +226,9 @@ public void testIssue506WithDate() throws Exception Issue506DateBean input = new Issue506DateBean(); input.date = new Date(1234L); - ObjectMapper mapper = new ObjectMapper(); - String json = mapper.writeValueAsString(input); + String json = MAPPER.writeValueAsString(input); - Issue506DateBean output = mapper.readValue(json, Issue506DateBean.class); + Issue506DateBean output = MAPPER.readValue(json, Issue506DateBean.class); assertEquals(input.date, output.date); } @@ -233,12 +238,20 @@ public void testIssue506WithNumber() throws Exception Issue506NumberBean input = new Issue506NumberBean(); input.number = Long.valueOf(4567L); - ObjectMapper mapper = new ObjectMapper(); - String json = mapper.writeValueAsString(input); + String json = MAPPER.writeValueAsString(input); - Issue506NumberBean output = mapper.readValue(json, Issue506NumberBean.class); + Issue506NumberBean output = MAPPER.readValue(json, Issue506NumberBean.class); assertEquals(input.number, output.number); } + + private String asJSONObjectValueString(ObjectMapper mapper, Object... args) throws IOException + { + LinkedHashMap map = new LinkedHashMap(); + for (int i = 0, len = args.length; i < len; i += 2) { + map.put(args[i], args[i+1]); + } + return mapper.writeValueAsString(map); + } } 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 7b04c45555..3060e6423b 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedSerialization.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedSerialization.java @@ -101,8 +101,9 @@ public void testSimpleClassAsProperty() throws Exception */ public void testTypeAsWrapper() throws Exception { - ObjectMapper m = new ObjectMapper(); - m.addMixIn(Animal.class, TypeWithWrapper.class); + ObjectMapper m = jsonMapperBuilder() + .addMixIn(Animal.class, TypeWithWrapper.class) + .build(); Map result = writeAndMap(m, new Cat("Venla", "black")); // should get a wrapper; keyed by minimal class name ("Cat" here) assertEquals(1, result.size()); @@ -119,8 +120,9 @@ public void testTypeAsWrapper() throws Exception */ public void testTypeAsArray() throws Exception { - ObjectMapper m = new ObjectMapper(); - m.addMixIn(Animal.class, TypeWithArray.class); + ObjectMapper m = jsonMapperBuilder() + .addMixIn(Animal.class, TypeWithArray.class) + .build(); // hmmh. Not good idea to rely on exact output, order may change. But... Map result = writeAndMap(m, new AnimalWrapper(new Dog("Amadeus", 7))); // First level, wrapper @@ -149,9 +151,10 @@ public void testTypeAsArray() throws Exception public void testInArray() throws Exception { // ensure we'll use mapper with default configs - ObjectMapper m = new ObjectMapper(); - // ... so this should NOT be needed... - m.disableDefaultTyping(); + ObjectMapper m = jsonMapperBuilder() + // ... so this should NOT be needed... + .disableDefaultTyping() + .build(); Animal[] animals = new Animal[] { new Cat("Miuku", "white"), new Dog("Murre", 9) }; Map map = new HashMap(); @@ -181,14 +184,12 @@ public void testInArray() throws Exception */ public void testEmptyBean() throws Exception { - ObjectMapper m = new ObjectMapper(); - m.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + ObjectMapper m = jsonMapperBuilder() + .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false) + .build(); assertEquals("{\"@type\":\"empty\"}", m.writeValueAsString(new Empty())); } - /** - * Unit test for [JACKSON-543] - */ public void testTypedMaps() throws Exception { ObjectMapper mapper = new ObjectMapper(); 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 bb4cd9b507..2552706c9d 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestWithGenerics.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestWithGenerics.java @@ -10,7 +10,6 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.ser.BeanSerializerFactory; -import com.fasterxml.jackson.databind.ser.ResolvableSerializer; public class TestWithGenerics extends BaseMapTest { @@ -67,10 +66,7 @@ static class SomeObject { public String someValue = UUID.randomUUID().toString(); } - // Beans for [JACKSON-430] - static class CustomJsonSerializer extends JsonSerializer - implements ResolvableSerializer { private final JsonSerializer beanSerializer; @@ -84,7 +80,7 @@ public void serialize( Object value, JsonGenerator jgen, SerializerProvider prov } @Override - public Class handledType() { return beanSerializer.handledType(); } + public Class handledType() { return beanSerializer.handledType(); } @Override public void serializeWithType( Object value, JsonGenerator jgen, SerializerProvider provider, TypeSerializer typeSer ) @@ -96,9 +92,7 @@ public void serializeWithType( Object value, JsonGenerator jgen, SerializerProvi @Override public void resolve(SerializerProvider provider) throws JsonMappingException { - if (beanSerializer instanceof ResolvableSerializer) { - ((ResolvableSerializer) beanSerializer).resolve(provider); - } + beanSerializer.resolve(provider); } } @@ -165,10 +159,11 @@ public void testWrapperWithExplicitType() throws Exception public void testJackson387() throws Exception { - ObjectMapper om = new ObjectMapper(); - om.enableDefaultTyping( ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT, JsonTypeInfo.As.PROPERTY ); - om.setSerializationInclusion(JsonInclude.Include.NON_NULL ); - om.enable( SerializationFeature.INDENT_OUTPUT); + ObjectMapper om = jsonMapperBuilder() + .enable( SerializationFeature.INDENT_OUTPUT) + .enableDefaultTyping( DefaultTyping.JAVA_LANG_OBJECT, JsonTypeInfo.As.PROPERTY) + .changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_NULL)) + .build(); MyClass mc = new MyClass(); @@ -200,9 +195,10 @@ public void testJackson387() throws Exception public void testJackson430() throws Exception { - ObjectMapper om = new ObjectMapper(); // om.getSerializationConfig().setSerializationInclusion( Inclusion.NON_NULL ); - om.setSerializerFactory( new CustomJsonSerializerFactory() ); + ObjectMapper om = jsonMapperBuilder() + .serializerFactory(new CustomJsonSerializerFactory()) + .build(); MyClass mc = new MyClass(); mc.params.add(new MyParam(1)); diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TypeDeserializerTest.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TypeDeserializerTest.java index 2b1fa8ea0a..ce47e1b3f8 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TypeDeserializerTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TypeDeserializerTest.java @@ -1,7 +1,7 @@ package com.fasterxml.jackson.databind.jsontype; import com.fasterxml.jackson.core.*; - +import com.fasterxml.jackson.core.json.JsonFactory; import com.fasterxml.jackson.databind.*; public class TypeDeserializerTest extends BaseMapTest @@ -10,28 +10,28 @@ public void testUtilMethods() throws Exception { final JsonFactory f = new JsonFactory(); - JsonParser p = f.createParser("true"); + JsonParser p = f.createParser(ObjectReadContext.empty(), "true"); assertNull(TypeDeserializer.deserializeIfNatural(p, null, Object.class)); p.nextToken(); assertEquals(Boolean.TRUE, TypeDeserializer.deserializeIfNatural(p, null, Object.class)); p.close(); - p = f.createParser("false "); + p = f.createParser(ObjectReadContext.empty(), "false "); p.nextToken(); assertEquals(Boolean.FALSE, TypeDeserializer.deserializeIfNatural(p, null, Object.class)); p.close(); - p = f.createParser("1"); + p = f.createParser(ObjectReadContext.empty(), "1"); p.nextToken(); assertEquals(Integer.valueOf(1), TypeDeserializer.deserializeIfNatural(p, null, Object.class)); p.close(); - p = f.createParser("0.5 "); + p = f.createParser(ObjectReadContext.empty(), "0.5 "); p.nextToken(); assertEquals(Double.valueOf(0.5), TypeDeserializer.deserializeIfNatural(p, null, Object.class)); p.close(); - p = f.createParser("\"foo\" [ ] "); + p = f.createParser(ObjectReadContext.empty(), "\"foo\" [ ] "); p.nextToken(); assertEquals("foo", TypeDeserializer.deserializeIfNatural(p, null, Object.class)); diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TypeRefinementForMapTest.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TypeRefinementForMapTest.java index 711c77c10f..6c614f36b5 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TypeRefinementForMapTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TypeRefinementForMapTest.java @@ -9,7 +9,9 @@ import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +@SuppressWarnings("serial") public class TypeRefinementForMapTest extends BaseMapTest { interface HasUniqueId { @@ -36,7 +38,6 @@ static class Data // public MyHashMap items; } - @SuppressWarnings("serial") static class MyHashMap> extends LinkedHashMap { @@ -84,7 +85,8 @@ public Object deserializeKey(String s, DeserializationContext deserializationCon } } - static class CompoundKeySerializer extends JsonSerializer { + static class CompoundKeySerializer extends StdSerializer { + public CompoundKeySerializer() { super(CompoundKey.class); } @Override public void serialize(CompoundKey compoundKey, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { jsonGenerator.writeFieldName(compoundKey.getPart0() + '|' + compoundKey.getPart1()); @@ -120,8 +122,9 @@ public void testMapKeyRefinement1384() throws Exception { final String TEST_INSTANCE_SERIALIZED = "{\"mapProperty\":[\"java.util.HashMap\",{\"Compound|Key\":\"Value\"}]}"; - ObjectMapper mapper = new ObjectMapper().enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); - + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.NON_FINAL) + .build(); TestClass testInstance = mapper.readValue(TEST_INSTANCE_SERIALIZED, TestClass.class); assertEquals(1, testInstance.mapProperty.size()); Object key = testInstance.mapProperty.keySet().iterator().next(); diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TypeResolverTest.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TypeResolverTest.java index 0635129616..8655824ba3 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TypeResolverTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TypeResolverTest.java @@ -37,13 +37,14 @@ static class MyMap extends HashMap { } public static void testSubtypeResolution() throws Exception { - ObjectMapper mapper = new ObjectMapper(); SimpleAbstractTypeResolver resolver = new SimpleAbstractTypeResolver(); resolver.addMapping(Map.class, MyMap.class); SimpleModule basicModule = new SimpleModule(); basicModule.setAbstractTypes(resolver); - mapper.registerModule(basicModule); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(basicModule) + .build(); String value = "{\"z\": {\"zz\": {\"a\": 42}}}"; A a = mapper.readValue(value, A.class); diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/UnknownSubClassTest.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/UnknownSubClassTest.java index ba670983d6..1757d36e62 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/UnknownSubClassTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/UnknownSubClassTest.java @@ -17,8 +17,9 @@ static class BaseWrapper { public void testUnknownClassAsSubtype() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false); + ObjectMapper mapper = jsonMapperBuilder() + .configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false) + .build(); BaseWrapper w = mapper.readValue(aposToQuotes ("{'value':{'clazz':'com.foobar.Nothing'}}'"), BaseWrapper.class); diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForArrays.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForArrays.java index 52e51668cc..7930be487c 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForArrays.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForArrays.java @@ -7,17 +7,10 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping; import com.fasterxml.jackson.databind.node.ObjectNode; public class TestDefaultForArrays extends BaseMapTest { - /* - /********************************************************** - /* Helper types - /********************************************************** - */ - static class ArrayBean { public Object[] values; @@ -45,11 +38,12 @@ protected PrimitiveArrayBean() { } */ public void testArrayTypingSimple() throws Exception { - ObjectMapper m = new ObjectMapper(); - m.enableDefaultTyping(DefaultTyping.NON_CONCRETE_AND_ARRAYS); + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.NON_CONCRETE_AND_ARRAYS) + .build(); ArrayBean bean = new ArrayBean(new String[0]); - String json = m.writeValueAsString(bean); - ArrayBean result = m.readValue(json, ArrayBean.class); + String json = mapper.writeValueAsString(bean); + ArrayBean result = mapper.readValue(json, ArrayBean.class); assertNotNull(result.values); assertEquals(String[].class, result.values.getClass()); } @@ -57,45 +51,51 @@ public void testArrayTypingSimple() throws Exception // And let's try it with deeper array as well public void testArrayTypingNested() throws Exception { - ObjectMapper m = new ObjectMapper(); - m.enableDefaultTyping(DefaultTyping.NON_CONCRETE_AND_ARRAYS); + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.NON_CONCRETE_AND_ARRAYS) + .build(); ArrayBean bean = new ArrayBean(new String[0][0]); - String json = m.writeValueAsString(bean); - ArrayBean result = m.readValue(json, ArrayBean.class); + String json = mapper.writeValueAsString(bean); + ArrayBean result = mapper.readValue(json, ArrayBean.class); assertNotNull(result.values); assertEquals(String[][].class, result.values.getClass()); } public void testNodeInArray() throws Exception { - JsonNode node = new ObjectMapper().readTree("{\"a\":3}"); - - ObjectMapper m = new ObjectMapper(); - m.enableDefaultTyping(DefaultTyping.JAVA_LANG_OBJECT); + JsonNode node = objectMapper().readTree("{\"a\":3}"); + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.JAVA_LANG_OBJECT) + .build(); Object[] obs = new Object[] { node }; - String json = m.writeValueAsString(obs); - Object[] result = m.readValue(json, Object[].class); + String json = mapper.writeValueAsString(obs); + Object[] result = mapper.readValue(json, Object[].class); assertEquals(1, result.length); Object ob = result[0]; assertTrue(ob instanceof JsonNode); } @SuppressWarnings("deprecation") - public void testNodeInEmptyArray() throws Exception { + public void testNodeInEmptyArray() throws Exception + { Map> outerMap = new HashMap>(); outerMap.put("inner", new ArrayList()); - ObjectMapper m = new ObjectMapper().disable(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS); - JsonNode tree = m.convertValue(outerMap, JsonNode.class); + ObjectMapper vanillaMapper = jsonMapperBuilder() + .disable(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS) + .build(); + JsonNode tree = vanillaMapper.convertValue(outerMap, JsonNode.class); - String json = m.writeValueAsString(tree); + String json = vanillaMapper.writeValueAsString(tree); assertEquals("{}", json); - JsonNode node = new ObjectMapper().readTree("{\"a\":[]}"); - - m.enableDefaultTyping(DefaultTyping.JAVA_LANG_OBJECT); + JsonNode node = vanillaMapper.readTree("{\"a\":[]}"); + + ObjectMapper mapper = vanillaMapper.rebuild() + .enableDefaultTyping(DefaultTyping.JAVA_LANG_OBJECT) + .build(); Object[] obs = new Object[] { node }; - json = m.writeValueAsString(obs); - Object[] result = m.readValue(json, Object[].class); + json = mapper.writeValueAsString(obs); + Object[] result = mapper.readValue(json, Object[].class); assertEquals(1, result.length); Object elem = result[0]; @@ -105,8 +105,9 @@ public void testNodeInEmptyArray() throws Exception { public void testArraysOfArrays() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY) + .build(); Object value = new Object[][] { new Object[] {} }; String json = mapper.writeValueAsString(value); @@ -119,17 +120,18 @@ public void testArraysOfArrays() throws Exception public void testArrayTypingForPrimitiveArrays() throws Exception { - ObjectMapper m = new ObjectMapper(); - m.enableDefaultTyping(DefaultTyping.NON_CONCRETE_AND_ARRAYS); - _testArrayTypingForPrimitiveArrays(m, new int[] { 1, 2, 3 }); - _testArrayTypingForPrimitiveArrays(m, new long[] { 1, 2, 3 }); - _testArrayTypingForPrimitiveArrays(m, new short[] { 1, 2, 3 }); - _testArrayTypingForPrimitiveArrays(m, new double[] { 0.5, 5.5, -1.0 }); - _testArrayTypingForPrimitiveArrays(m, new float[] { 0.5f, 5.5f, -1.0f }); - _testArrayTypingForPrimitiveArrays(m, new boolean[] { true, false }); - _testArrayTypingForPrimitiveArrays(m, new byte[] { 1, 2, 3 }); - - _testArrayTypingForPrimitiveArrays(m, new char[] { 'a', 'b' }); + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.NON_CONCRETE_AND_ARRAYS) + .build(); + _testArrayTypingForPrimitiveArrays(mapper, new int[] { 1, 2, 3 }); + _testArrayTypingForPrimitiveArrays(mapper, new long[] { 1, 2, 3 }); + _testArrayTypingForPrimitiveArrays(mapper, new short[] { 1, 2, 3 }); + _testArrayTypingForPrimitiveArrays(mapper, new double[] { 0.5, 5.5, -1.0 }); + _testArrayTypingForPrimitiveArrays(mapper, new float[] { 0.5f, 5.5f, -1.0f }); + _testArrayTypingForPrimitiveArrays(mapper, new boolean[] { true, false }); + _testArrayTypingForPrimitiveArrays(mapper, new byte[] { 1, 2, 3 }); + + _testArrayTypingForPrimitiveArrays(mapper, new char[] { 'a', 'b' }); } private void _testArrayTypingForPrimitiveArrays(ObjectMapper mapper, Object v) throws Exception { diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForEnums.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForEnums.java index b716c24cd6..33a235092c 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForEnums.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForEnums.java @@ -25,11 +25,15 @@ protected static class TimeUnitBean { } /* - /********************************************************** + /********************************************************************** /* Test methods - /********************************************************** + /********************************************************************** */ + private final ObjectMapper DEFTYPING_MAPPER = jsonMapperBuilder() + .enableDefaultTyping() + .build(); + public void testSimpleEnumBean() throws Exception { TimeUnitBean bean = new TimeUnitBean(); @@ -42,36 +46,29 @@ public void testSimpleEnumBean() throws Exception assertEquals(TimeUnit.SECONDS, result.timeUnit); // then with type info - m = new ObjectMapper(); - m.enableDefaultTyping(); - json = m.writeValueAsString(bean); - result = m.readValue(json, TimeUnitBean.class); + json = DEFTYPING_MAPPER.writeValueAsString(bean); + result = DEFTYPING_MAPPER.readValue(json, TimeUnitBean.class); assertEquals(TimeUnit.SECONDS, result.timeUnit); } - + public void testSimpleEnumsInObjectArray() throws Exception { - ObjectMapper m = new ObjectMapper(); - m.enableDefaultTyping(); - // Typing is needed for enums - String json = m.writeValueAsString(new Object[] { TestEnum.A }); + String json = DEFTYPING_MAPPER.writeValueAsString(new Object[] { TestEnum.A }); assertEquals("[[\"com.fasterxml.jackson.databind.jsontype.deftyping.TestDefaultForEnums$TestEnum\",\"A\"]]", json); // and let's verify we get it back ok as well: - Object[] value = m.readValue(json, Object[].class); + Object[] value = DEFTYPING_MAPPER.readValue(json, Object[].class); assertEquals(1, value.length); assertSame(TestEnum.A, value[0]); } public void testSimpleEnumsAsField() throws Exception { - ObjectMapper m = new ObjectMapper(); - m.enableDefaultTyping(); - String json = m.writeValueAsString(new EnumHolder(TestEnum.B)); + String json = DEFTYPING_MAPPER.writeValueAsString(new EnumHolder(TestEnum.B)); assertEquals("{\"value\":[\"com.fasterxml.jackson.databind.jsontype.deftyping.TestDefaultForEnums$TestEnum\",\"B\"]}", json); - EnumHolder holder = m.readValue(json, EnumHolder.class); + EnumHolder holder = DEFTYPING_MAPPER.readValue(json, EnumHolder.class); assertSame(TestEnum.B, holder.value); } } diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForLists.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForLists.java index b061be6e32..2ef2cd4f02 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForLists.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForLists.java @@ -5,7 +5,6 @@ import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping; public class TestDefaultForLists extends BaseMapTest @@ -69,8 +68,9 @@ public SetBean(String str) { public void testListOfLongs() throws Exception { - ObjectMapper m = new ObjectMapper(); - m.enableDefaultTyping(); + ObjectMapper m = jsonMapperBuilder() + .enableDefaultTyping() + .build(); ListOfLongs input = new ListOfLongs(1L, 2L, 3L); String json = m.writeValueAsString(input); assertEquals("{\"longs\":[\"java.util.ArrayList\",[1,2,3]]}", json); @@ -91,8 +91,9 @@ public void testListOfLongs() throws Exception */ public void testListOfNumbers() throws Exception { - ObjectMapper m = new ObjectMapper(); - m.enableDefaultTyping(); + ObjectMapper m = jsonMapperBuilder() + .enableDefaultTyping() + .build(); ListOfNumbers input = new ListOfNumbers(Long.valueOf(1L), Integer.valueOf(2), Double.valueOf(3.0)); String json = m.writeValueAsString(input); assertEquals("{\"nums\":[\"java.util.ArrayList\",[[\"java.lang.Long\",1],2,3.0]]}", json); @@ -107,8 +108,9 @@ public void testListOfNumbers() throws Exception public void testDateTypes() throws Exception { - ObjectMapper m = new ObjectMapper(); - m.enableDefaultTyping(); + ObjectMapper m = jsonMapperBuilder() + .enableDefaultTyping() + .build(); ObjectListBean input = new ObjectListBean(); List inputList = new ArrayList(); inputList.add(TimeZone.getTimeZone("EST")); @@ -125,8 +127,9 @@ public void testDateTypes() throws Exception public void testJackson628() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.enableDefaultTyping(DefaultTyping.NON_FINAL); + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.NON_FINAL) + .build(); ArrayList data = new ArrayList(); String json = mapper.writeValueAsString(data); List output = mapper.readValue(json, List.class); @@ -135,9 +138,10 @@ public void testJackson628() throws Exception public void testJackson667() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, - JsonTypeInfo.As.PROPERTY); + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.NON_FINAL, + JsonTypeInfo.As.PROPERTY) + .build(); String json = mapper.writeValueAsString(new SetBean("abc")); SetBean bean = mapper.readValue(json, SetBean.class); assertNotNull(bean); diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForMaps.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForMaps.java index c1fca0a7d7..2e291d2d9f 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForMaps.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForMaps.java @@ -7,6 +7,7 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.jsontype.NamedType; import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; +import com.fasterxml.jackson.databind.jsontype.impl.DefaultTypeResolverBuilder; import com.fasterxml.jackson.databind.jsontype.impl.TypeNameIdResolver; import com.fasterxml.jackson.databind.type.TypeFactory; @@ -70,12 +71,13 @@ public void addChildItem(String key, ItemMap childItem) { public void testJackson428() throws Exception { - ObjectMapper serMapper = new ObjectMapper(); - - TypeResolverBuilder serializerTyper = new ObjectMapper.DefaultTypeResolverBuilder(ObjectMapper.DefaultTyping.NON_FINAL); - serializerTyper = serializerTyper.init(JsonTypeInfo.Id.NAME, createTypeNameIdResolver(true)); - serializerTyper = serializerTyper.inclusion(JsonTypeInfo.As.PROPERTY); - serMapper.setDefaultTyping(serializerTyper); + TypeResolverBuilder serializerTyper = new DefaultTypeResolverBuilder(DefaultTyping.NON_FINAL, + JsonTypeInfo.As.PROPERTY); +// serializerTyper = serializerTyper.init(JsonTypeInfo.Id.NAME, createTypeNameIdResolver(true)); +// serializerTyper = serializerTyper.inclusion(JsonTypeInfo.As.PROPERTY); + ObjectMapper serMapper = jsonMapperBuilder() + .setDefaultTyping(serializerTyper) + .build(); // Let's start by constructing something to serialize first MapHolder holder = new MapHolder(); @@ -86,12 +88,13 @@ public void testJackson428() throws Exception String json = serMapper.writeValueAsString(holder); // Then deserialize: need separate mapper to initialize type id resolver appropriately - ObjectMapper deserMapper = new ObjectMapper(); - TypeResolverBuilder deserializerTyper = new ObjectMapper.DefaultTypeResolverBuilder(ObjectMapper.DefaultTyping.NON_FINAL); - deserializerTyper = deserializerTyper.init(JsonTypeInfo.Id.NAME, createTypeNameIdResolver(false)); - deserializerTyper = deserializerTyper.inclusion(JsonTypeInfo.As.PROPERTY); - deserMapper.setDefaultTyping(deserializerTyper); - + TypeResolverBuilder deserializerTyper = new DefaultTypeResolverBuilder(DefaultTyping.NON_FINAL, + JsonTypeInfo.As.PROPERTY); +// deserializerTyper = deserializerTyper.init(JsonTypeInfo.Id.NAME, createTypeNameIdResolver(false)); +// deserializerTyper = deserializerTyper.inclusion(JsonTypeInfo.As.PROPERTY); + ObjectMapper deserMapper = jsonMapperBuilder() + .setDefaultTyping(deserializerTyper) + .build(); MapHolder result = deserMapper.readValue(json, MapHolder.class); assertNotNull(result); Map map = result.map; @@ -114,14 +117,15 @@ protected TypeNameIdResolver createTypeNameIdResolver(boolean forSerialization) subtypes.add(new NamedType(ArrayList.class, "AList")); subtypes.add(new NamedType(HashMap.class, "HMap")); ObjectMapper mapper = new ObjectMapper(); - return TypeNameIdResolver.construct(mapper.getDeserializationConfig(), + return TypeNameIdResolver.construct(mapper.deserializationConfig(), TypeFactory.defaultInstance().constructType(Object.class), subtypes, forSerialization, !forSerialization); } public void testList() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE, JsonTypeInfo.As.PROPERTY); + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.OBJECT_AND_NON_CONCRETE, JsonTypeInfo.As.PROPERTY) + .build(); ItemList child = new ItemList(); child.value = "I am child"; @@ -136,8 +140,9 @@ public void testList() throws Exception public void testMap() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE, JsonTypeInfo.As.PROPERTY); + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.OBJECT_AND_NON_CONCRETE, JsonTypeInfo.As.PROPERTY) + .build(); ItemMap child = new ItemMap(); child.value = "I am child"; diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForObject.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForObject.java index 5f0a23a571..7d477b5df7 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForObject.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForObject.java @@ -89,15 +89,16 @@ static public class DomainBeanWrapper { */ public void testBeanAsObject() throws Exception { - ObjectMapper m = new ObjectMapper(); - m.enableDefaultTyping(); + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping() + .build(); // note: need to wrap, to get declared as Object - String str = m.writeValueAsString(new Object[] { new StringBean("abc") }); + String str = mapper.writeValueAsString(new Object[] { new StringBean("abc") }); _verifySerializationAsMap(str); // Ok: serialization seems to work as expected. Now deserialize: - Object ob = m.readValue(str, Object[].class); + Object ob = mapper.readValue(str, Object[].class); assertNotNull(ob); Object[] result = (Object[]) ob; assertNotNull(result[0]); @@ -108,14 +109,14 @@ public void testBeanAsObject() throws Exception // 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"); + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTypingAsProperty(DefaultTyping.NON_FINAL, ".hype") + .build(); // note: need to wrap, to get declared as Object - String json = m.writeValueAsString(new StringBean("abc")); + String json = mapper.writeValueAsString(new StringBean("abc")); // Ok: serialization seems to work as expected. Now deserialize: - Object result = m.readValue(json, Object.class); + Object result = mapper.readValue(json, Object.class); assertNotNull(result); assertEquals(StringBean.class, result.getClass()); assertEquals("abc", ((StringBean) result).name); @@ -140,8 +141,9 @@ public void testAbstractBean() throws Exception } // and then that we will succeed with default type info - m = new ObjectMapper(); - m.enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE); + m = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.OBJECT_AND_NON_CONCRETE) + .build(); serial = m.writeValueAsString(input); AbstractBean[] beans = m.readValue(serial, AbstractBean[].class); assertEquals(1, beans.length); @@ -155,22 +157,25 @@ public void testAbstractBean() throws Exception */ public void testNonFinalBean() throws Exception { - ObjectMapper m = new ObjectMapper(); // first: use "object or abstract" typing: should produce no type info: - m.enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE); + ObjectMapper m = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.OBJECT_AND_NON_CONCRETE) + .build(); StringBean bean = new StringBean("x"); assertEquals("{\"name\":\"x\"}", m.writeValueAsString(bean)); // then non-final, and voila: - m = new ObjectMapper(); - m.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); + m = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.NON_FINAL) + .build(); assertEquals("[\""+StringBean.class.getName()+"\",{\"name\":\"x\"}]", m.writeValueAsString(bean)); } public void testNullValue() throws Exception { - ObjectMapper m = new ObjectMapper(); - m.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); + ObjectMapper m = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.NON_FINAL) + .build(); BeanHolder h = new BeanHolder(); String json = m.writeValueAsString(h); assertNotNull(json); @@ -181,17 +186,19 @@ public void testNullValue() throws Exception public void testEnumAsObject() throws Exception { + final ObjectMapper vanillaMapper = objectMapper(); + // wrapping to be declared as object Object[] input = new Object[] { Choice.YES }; Object[] input2 = new Object[] { ComplexChoice.MAYBE}; // first, without type info: - assertEquals("[\"YES\"]", serializeAsString(input)); - assertEquals("[\"MAYBE\"]", serializeAsString(input2)); + assertEquals("[\"YES\"]", vanillaMapper.writeValueAsString(input)); + assertEquals("[\"MAYBE\"]", vanillaMapper.writeValueAsString(input2)); // and then with it - ObjectMapper m = new ObjectMapper(); - m.enableDefaultTyping(); - + ObjectMapper m = jsonMapperBuilder() + .enableDefaultTyping() + .build(); String json = m.writeValueAsString(input); assertEquals("[[\""+Choice.class.getName()+"\",\"YES\"]]", json); @@ -213,8 +220,9 @@ public void testEnumSet() throws Exception { EnumSet set = EnumSet.of(Choice.NO); Object[] input = new Object[] { set }; - ObjectMapper m = new ObjectMapper(); - m.enableDefaultTyping(); + ObjectMapper m = jsonMapperBuilder() + .enableDefaultTyping() + .build(); String json = m.writeValueAsString(input); Object[] output = m.readValue(json, Object[].class); assertEquals(1, output.length); @@ -232,8 +240,9 @@ public void testEnumMap() throws Exception EnumMap map = new EnumMap(Choice.class); map.put(Choice.NO, "maybe"); Object[] input = new Object[] { map }; - ObjectMapper m = new ObjectMapper(); - m.enableDefaultTyping(); + ObjectMapper m = jsonMapperBuilder() + .enableDefaultTyping() + .build(); String json = m.writeValueAsString(input); Object[] output = m.readValue(json, Object[].class); assertEquals(1, output.length); @@ -247,8 +256,9 @@ public void testEnumMap() throws Exception public void testJackson311() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.NON_FINAL) + .build(); String json = mapper.writeValueAsString(new PolymorphicType("hello", 2)); PolymorphicType value = mapper.readValue(json, PolymorphicType.class); assertEquals("hello", value.foo); @@ -258,11 +268,11 @@ public void testJackson311() throws Exception // Also, let's ensure TokenBuffer gets properly handled public void testTokenBuffer() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); - + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.NON_FINAL) + .build(); // Ok, first test JSON Object containing buffer: - TokenBuffer buf = new TokenBuffer(mapper, false); + TokenBuffer buf = TokenBuffer.forGeneration(); buf.writeStartObject(); buf.writeNumberField("num", 42); buf.writeEndObject(); @@ -280,7 +290,7 @@ public void testTokenBuffer() throws Exception buf.close(); // then as an array: - buf = new TokenBuffer(mapper, false); + buf = TokenBuffer.forGeneration(); buf.writeStartArray(); buf.writeBoolean(true); buf.writeEndArray(); @@ -297,7 +307,7 @@ public void testTokenBuffer() throws Exception buf.close(); // and finally as scalar - buf = new TokenBuffer(mapper, false); + buf = TokenBuffer.forGeneration(); buf.writeNumber(321); json = mapper.writeValueAsString(new ObjectHolder(buf)); holder = mapper.readValue(json, ObjectHolder.class); @@ -311,13 +321,11 @@ public void testTokenBuffer() throws Exception buf.close(); } - /** - * Test for [JACKSON-352] - */ public void testIssue352() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.enableDefaultTyping (ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE, JsonTypeInfo.As.PROPERTY); + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping (DefaultTyping.OBJECT_AND_NON_CONCRETE, JsonTypeInfo.As.PROPERTY) + .build(); DiscussBean d1 = new DiscussBean(); d1.subject = "mouse"; d1.weight=88; @@ -334,18 +342,20 @@ public void testIssue352() throws Exception // Test to ensure we can also use "As.PROPERTY" inclusion and custom property name public void testFeature432() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.enableDefaultTypingAsProperty(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE, "*CLASS*"); + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTypingAsProperty(DefaultTyping.OBJECT_AND_NON_CONCRETE, "*CLASS*") + .build(); String json = mapper.writeValueAsString(new BeanHolder(new StringBean("punny"))); assertEquals("{\"bean\":{\"*CLASS*\":\"com.fasterxml.jackson.databind.jsontype.deftyping.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); + /*ObjectMapper mapper =*/ jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.JAVA_LANG_OBJECT, + JsonTypeInfo.As.EXTERNAL_PROPERTY) + .build(); fail("Should not have passed"); } catch (IllegalArgumentException e) { verifyException(e, "Cannot use includeAs of EXTERNAL_PROPERTY"); diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForScalars.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForScalars.java index 7bb7b5c431..cc8b7afc15 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForScalars.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForScalars.java @@ -2,8 +2,6 @@ import java.util.*; -import static org.junit.Assert.*; - import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder; @@ -26,11 +24,15 @@ static class Data { } /* - /********************************************************** + /********************************************************************** /* Test methods - /********************************************************** + /********************************************************************** */ - + + private final ObjectMapper DEFAULT_TYPING_MAPPER = jsonMapperBuilder() + .enableDefaultTyping() + .build(); + /** * Unit test to verify that limited number of core types do NOT include * type information, even if declared as Object. This is only done for types @@ -39,32 +41,26 @@ static class Data { */ public void testNumericScalars() throws Exception { - ObjectMapper m = new ObjectMapper(); - m.enableDefaultTyping(); - // no typing for Integer, Double, yes for others - assertEquals("[123]", m.writeValueAsString(new Object[] { Integer.valueOf(123) })); - assertEquals("[[\"java.lang.Long\",37]]", m.writeValueAsString(new Object[] { Long.valueOf(37) })); - assertEquals("[0.25]", m.writeValueAsString(new Object[] { Double.valueOf(0.25) })); - assertEquals("[[\"java.lang.Float\",0.5]]", m.writeValueAsString(new Object[] { Float.valueOf(0.5f) })); + assertEquals("[123]", DEFAULT_TYPING_MAPPER.writeValueAsString(new Object[] { Integer.valueOf(123) })); + assertEquals("[[\"java.lang.Long\",37]]", DEFAULT_TYPING_MAPPER.writeValueAsString(new Object[] { Long.valueOf(37) })); + assertEquals("[0.25]", DEFAULT_TYPING_MAPPER.writeValueAsString(new Object[] { Double.valueOf(0.25) })); + assertEquals("[[\"java.lang.Float\",0.5]]", DEFAULT_TYPING_MAPPER.writeValueAsString(new Object[] { Float.valueOf(0.5f) })); } public void testDateScalars() throws Exception { - ObjectMapper m = new ObjectMapper(); - m.enableDefaultTyping(); - long ts = 12345678L; assertEquals("[[\"java.util.Date\","+ts+"]]", - m.writeValueAsString(new Object[] { new Date(ts) })); + DEFAULT_TYPING_MAPPER.writeValueAsString(new Object[] { new Date(ts) })); // Calendar is trickier... hmmh. Need to ensure round-tripping Calendar c = Calendar.getInstance(); c.setTimeInMillis(ts); - String json = m.writeValueAsString(new Object[] { c }); + String json = DEFAULT_TYPING_MAPPER.writeValueAsString(new Object[] { c }); assertEquals("[[\""+c.getClass().getName()+"\","+ts+"]]", json); // and let's make sure it also comes back same way: - Object[] result = m.readValue(json, Object[].class); + Object[] result = DEFAULT_TYPING_MAPPER.readValue(json, Object[].class); assertEquals(1, result.length); assertTrue(result[0] instanceof Calendar); assertEquals(ts, ((Calendar) result[0]).getTimeInMillis()); @@ -72,12 +68,9 @@ public void testDateScalars() throws Exception public void testMiscScalars() throws Exception { - ObjectMapper m = new ObjectMapper(); - m.enableDefaultTyping(); - // no typing for Strings, booleans - assertEquals("[\"abc\"]", m.writeValueAsString(new Object[] { "abc" })); - assertEquals("[true,null,false]", m.writeValueAsString(new Boolean[] { true, null, false })); + assertEquals("[\"abc\"]", DEFAULT_TYPING_MAPPER.writeValueAsString(new Object[] { "abc" })); + assertEquals("[true,null,false]", DEFAULT_TYPING_MAPPER.writeValueAsString(new Boolean[] { true, null, false })); } /** @@ -86,26 +79,26 @@ public void testMiscScalars() throws Exception */ public void testScalarArrays() throws Exception { - ObjectMapper m = new ObjectMapper(); - m.enableDefaultTyping(ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT); + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.JAVA_LANG_OBJECT) + .build(); Object[] input = new Object[] { "abc", new Date(1234567), null, Integer.valueOf(456) }; - String json = m.writeValueAsString(input); + String json = mapper.writeValueAsString(input); assertEquals("[\"abc\",[\"java.util.Date\",1234567],null,456]", json); // and should deserialize back as well: - Object[] output = m.readValue(json, Object[].class); + Object[] output = mapper.readValue(json, Object[].class); assertArrayEquals(input, output); } + // Loosely scalar public void test417() throws Exception { - ObjectMapper m = new ObjectMapper(); - m.enableDefaultTyping(); Jackson417Bean input = new Jackson417Bean(); - String json = m.writeValueAsString(input); - Jackson417Bean result = m.readValue(json, Jackson417Bean.class); + String json = DEFAULT_TYPING_MAPPER.writeValueAsString(input); + Jackson417Bean result = DEFAULT_TYPING_MAPPER.readValue(json, Jackson417Bean.class); assertEquals(input.foo, result.foo); assertEquals(input.bar, result.bar); } @@ -120,13 +113,12 @@ public void testDefaultTypingWithLong() throws Exception mapData.put("longAsField", data); // Configure Jackson to preserve types - ObjectMapper mapper = new ObjectMapper(); - StdTypeResolverBuilder resolver = new StdTypeResolverBuilder(); - resolver.init(JsonTypeInfo.Id.CLASS, null); - resolver.inclusion(JsonTypeInfo.As.PROPERTY); - resolver.typeProperty("__t"); - mapper.setDefaultTyping(resolver); - mapper.enable(SerializationFeature.INDENT_OUTPUT); + StdTypeResolverBuilder resolver = new StdTypeResolverBuilder(JsonTypeInfo.Id.CLASS, + JsonTypeInfo.As.PROPERTY, "__t"); + ObjectMapper mapper = jsonMapperBuilder() + .enable(SerializationFeature.INDENT_OUTPUT) + .setDefaultTyping(resolver) + .build(); // Serialize String json = mapper.writeValueAsString(mapData); diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForTreeNodes.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForTreeNodes.java index 39a2cee663..d36f935b1f 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForTreeNodes.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForTreeNodes.java @@ -18,11 +18,10 @@ public Foo() { } /********************************************************** */ - private final ObjectMapper DEFAULT_MAPPER = new ObjectMapper(); - { - DEFAULT_MAPPER.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); - } - + private final ObjectMapper DEFAULT_MAPPER = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY) + .build(); + public void testValueAsStringWithDefaultTyping() throws Exception { Foo foo = new Foo("baz"); diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultWithCreators.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultWithCreators.java index b75b47801f..932e60d04e 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultWithCreators.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultWithCreators.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.databind.BaseMapTest; +import com.fasterxml.jackson.databind.DefaultTyping; import com.fasterxml.jackson.databind.ObjectMapper; public class TestDefaultWithCreators @@ -65,8 +66,9 @@ public byte[] getBytes() { public void testWithCreators() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.NON_FINAL) + .build(); UrlJob input = new UrlJob(123L, "http://foo", 3); String json = mapper.writeValueAsString(input); assertNotNull(json); @@ -83,8 +85,9 @@ public void testWithCreators() throws Exception public void testWithCreatorAndJsonValue() throws Exception { final byte[] BYTES = new byte[] { 1, 2, 3, 4, 5 }; - ObjectMapper mapper = new ObjectMapper(); - mapper.enableDefaultTyping(); + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping() + .build(); String json = mapper.writeValueAsString(new Bean1385Wrapper( new Bean1385(BYTES) )); diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/ExternalTypeId198Test.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/ExternalTypeId198Test.java index c34793f199..1bd61c73af 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/ExternalTypeId198Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/ExternalTypeId198Test.java @@ -26,27 +26,32 @@ static class Character { public static abstract class Attack { public String side; - @JsonCreator - public Attack(String side) { + protected Attack(String side) { this.side = side; } } public static class Kick extends Attack { - @JsonCreator + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) public Kick(String side) { super(side); } } public static class Punch extends Attack { - @JsonCreator + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) public Punch(String side) { super(side); } } - final ObjectMapper MAPPER = new ObjectMapper(); + /* + /********************************************************** + /* Test methods + /********************************************************** + */ + + final ObjectMapper MAPPER = newJsonMapper(); public void testFails() throws Exception { String json = "{ \"name\": \"foo\", \"attack\":\"right\" } }"; diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/ExternalTypeIdTest.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/ExternalTypeIdTest.java index 7537129929..5d34666073 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/ExternalTypeIdTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/ExternalTypeIdTest.java @@ -147,7 +147,7 @@ static class BaseContainer { protected BaseContainer() { throw new IllegalStateException("wrong constructor called"); } @JsonCreator - public BaseContainer(@JsonProperty("baseContainerProperty") String bcp, @JsonProperty("base") Base b) { + BaseContainer(@JsonProperty("baseContainerProperty") String bcp, @JsonProperty("base") Base b) { baseContainerProperty = bcp; base = b; } @@ -291,8 +291,9 @@ private void setValue(Object value) { public void testSimpleSerialization() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerSubtypes(ValueBean.class); + ObjectMapper mapper = jsonMapperBuilder() + .registerSubtypes(ValueBean.class) + .build(); // This may look odd, but one implementation nastiness is the fact // that we cannot properly serialize type id before the object, // because call is made after property name (for object) has already @@ -314,8 +315,9 @@ public void testImproperExternalIdSerialization() throws Exception // for [databind#942] public void testExternalTypeIdWithNull() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerSubtypes(ValueBean.class); + ObjectMapper mapper = jsonMapperBuilder() + .registerSubtypes(ValueBean.class) + .build(); ExternalBean b; b = mapper.readValue(aposToQuotes("{'bean':null,'extType':'vbean'}"), ExternalBean.class); @@ -333,8 +335,9 @@ public void testExternalTypeIdWithNull() throws Exception public void testSimpleDeserialization() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerSubtypes(ValueBean.class); + ObjectMapper mapper = jsonMapperBuilder() + .registerSubtypes(ValueBean.class) + .build(); ExternalBean result = mapper.readValue("{\"bean\":{\"value\":11},\"extType\":\"vbean\"}", ExternalBean.class); assertNotNull(result); assertNotNull(result.bean); @@ -353,8 +356,9 @@ public void testSimpleDeserialization() throws Exception // externally typed things, mixed with other stuff... public void testMultipleTypeIdsDeserialization() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerSubtypes(ValueBean.class); + ObjectMapper mapper = jsonMapperBuilder() + .registerSubtypes(ValueBean.class) + .build(); String json = mapper.writeValueAsString(new ExternalBean3(3)); ExternalBean3 result = mapper.readValue(json, ExternalBean3.class); assertNotNull(result); @@ -370,8 +374,9 @@ public void testMultipleTypeIdsDeserialization() throws Exception // Also, it should be ok to use @JsonCreator as well... public void testExternalTypeWithCreator() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerSubtypes(ValueBean.class); + ObjectMapper mapper = jsonMapperBuilder() + .registerSubtypes(ValueBean.class) + .build(); String json = mapper.writeValueAsString(new ExternalBeanWithCreator(7)); ExternalBeanWithCreator result = mapper.readValue(json, ExternalBeanWithCreator.class); assertNotNull(result); @@ -399,7 +404,7 @@ public void testIssue798() throws Exception Base base = new Derived1("derived1 prop val", "base prop val"); BaseContainer baseContainer = new BaseContainer("bc prop val", base); String generatedJson = MAPPER.writeValueAsString(baseContainer); - BaseContainer baseContainer2 = MAPPER.readValue(generatedJson,BaseContainer.class); + BaseContainer baseContainer2 = MAPPER.readValue(generatedJson, BaseContainer.class); assertEquals("bc prop val", baseContainer.getBaseContainerProperty()); Base b = baseContainer2.getBase(); diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/ExternalTypeIdTest1288.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/ExternalTypeIdTest1288.java index d756d40457..2793f057ce 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/ExternalTypeIdTest1288.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/ExternalTypeIdTest1288.java @@ -16,9 +16,7 @@ public class ExternalTypeIdTest1288 extends BaseMapTest { public static class ClassesWithoutBuilder { - public static class CreditCardDetails implements PaymentDetails { - protected String cardHolderFirstName; protected String cardHolderLastName; protected String number; @@ -33,51 +31,49 @@ public static class CreditCardDetails implements PaymentDetails { protected String description; - public void setCardHolderFirstName (String cardHolderFirstName) { + public void setCardHolderFirstName(String cardHolderFirstName) { this.cardHolderFirstName = cardHolderFirstName; } - public void setCardHolderLastName (String cardHolderLastName) { + public void setCardHolderLastName(String cardHolderLastName) { this.cardHolderLastName = cardHolderLastName; } - public void setNumber (String number) { + public void setNumber(String number) { this.number = number; } - public void setExpiryDate (String expiryDate) { + public void setExpiryDate(String expiryDate) { this.expiryDate = expiryDate; } - public void setCsc (int csc) { + public void setCsc(int csc) { this.csc = csc; } - public void setAddress (String address) { + public void setAddress(String address) { this.address = address; } - public void setZipCode (String zipCode) { + public void setZipCode(String zipCode) { this.zipCode = zipCode; } - public void setCity (String city) { + public void setCity(String city) { this.city = city; } - public void setProvince (String province) { + public void setProvince(String province) { this.province = province; } - public void setCountryCode (String countryCode) { + public void setCountryCode(String countryCode) { this.countryCode = countryCode; } - public void setDescription (String description) { + public void setDescription(String description) { this.description = description; } - - } public static class EncryptedCreditCardDetails implements PaymentDetails { @@ -86,11 +82,11 @@ public static class EncryptedCreditCardDetails implements PaymentDetails { protected String name; - public void setPaymentInstrumentID (UUID paymentInstrumentID) { + public void setPaymentInstrumentID(UUID paymentInstrumentID) { this.paymentInstrumentID = paymentInstrumentID; } - public void setName (String name) { + public void setName(String name) { this.name = name; } @@ -102,22 +98,22 @@ public enum FormOfPayment { private final Class clazz; - FormOfPayment (final Class clazz) { + FormOfPayment(final Class clazz) { this.clazz = clazz; } @SuppressWarnings ("unchecked") - public Class getDetailsClass () { + public Class getDetailsClass() { return (Class) this.clazz; } - public static FormOfPayment fromDetailsClass (Class detailsClass) { + public static FormOfPayment fromDetailsClass(Class detailsClass) { for (FormOfPayment fop : FormOfPayment.values ()) { if (fop.clazz == detailsClass) { return fop; } } - throw new IllegalArgumentException ("not found"); + throw new IllegalArgumentException("not found"); } } @@ -133,45 +129,45 @@ public static class PaymentMean { PaymentDetails paymentDetails; - public void setFormOfPayment (FormOfPayment formOfPayment) { + public void setFormOfPayment(FormOfPayment formOfPayment) { this.formOfPayment = formOfPayment; } - @JsonTypeInfo (use = JsonTypeInfo.Id.CUSTOM, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "form_of_payment", visible = true) - @JsonTypeIdResolver (PaymentDetailsTypeIdResolver.class) - public void setPaymentDetails (PaymentDetails paymentDetails) { + @JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "form_of_payment", visible = true) + @JsonTypeIdResolver(PaymentDetailsTypeIdResolver.class) + public void setPaymentDetails(PaymentDetails paymentDetails) { this.paymentDetails = paymentDetails; } } public static class PaymentDetailsTypeIdResolver extends TypeIdResolverBase { - @SuppressWarnings ("unchecked") + @SuppressWarnings("unchecked") @Override - public String idFromValue (Object value) { + public String idFromValue(Object value) { if (! (value instanceof PaymentDetails)) { return null; } - return FormOfPayment.fromDetailsClass ((Class) value.getClass ()).name (); + return FormOfPayment.fromDetailsClass((Class) value.getClass ()).name (); } @Override - public String idFromValueAndType (Object value, Class suggestedType) { + public String idFromValueAndType(Object value, Class suggestedType) { return this.idFromValue (value); } @Override - public JavaType typeFromId (DatabindContext context, String id) { + public JavaType typeFromId(DatabindContext context, String id) { return context.getTypeFactory().constructType(FormOfPayment.valueOf(id).getDetailsClass ()); } @Override - public String getDescForKnownTypeIds () { + public String getDescForKnownTypeIds() { return "PaymentDetails"; } @Override - public Id getMechanism () { + public Id getMechanism() { return JsonTypeInfo.Id.CUSTOM; } } @@ -194,14 +190,15 @@ public static class CompanyCreditCardDetailsBuilder implements Builder { private String province; private String countryCode; - public CompanyCreditCardDetailsBuilder address (final String a) { + public CompanyCreditCardDetailsBuilder address(final String a) { address = a; return this; } @Override public CreditCardDetails build() { - return new CreditCardDetails (this.cardHolderFirstName, this.cardHolderLastName, this.number, this.expiryDate, this.csc, this.address, this.zipCode, this.city, + return new CreditCardDetails (this.cardHolderFirstName, this.cardHolderLastName, this.number, + this.expiryDate, this.csc, this.address, this.zipCode, this.city, this.province, this.countryCode, "COMPANY CREDIT CARD"); } @@ -210,42 +207,42 @@ public CompanyCreditCardDetailsBuilder cardHolderFirstName(final String cardHold return this; } - public CompanyCreditCardDetailsBuilder cardHolderLastName (final String cardHolderLastName) { + public CompanyCreditCardDetailsBuilder cardHolderLastName(final String cardHolderLastName) { this.cardHolderLastName = cardHolderLastName; return this; } - public CompanyCreditCardDetailsBuilder city (final String city) { + public CompanyCreditCardDetailsBuilder city(final String city) { this.city = city; return this; } - public CompanyCreditCardDetailsBuilder countryCode (final String countryCode) { + public CompanyCreditCardDetailsBuilder countryCode(final String countryCode) { this.countryCode = countryCode; return this; } - public CompanyCreditCardDetailsBuilder csc (final int csc) { + public CompanyCreditCardDetailsBuilder csc(final int csc) { this.csc = csc; return this; } - public CompanyCreditCardDetailsBuilder expiryDate (final String expiryDate) { + public CompanyCreditCardDetailsBuilder expiryDate(final String expiryDate) { this.expiryDate = expiryDate; return this; } - public CompanyCreditCardDetailsBuilder number (final String number) { + public CompanyCreditCardDetailsBuilder number(final String number) { this.number = number; return this; } - public CompanyCreditCardDetailsBuilder province (final String province) { + public CompanyCreditCardDetailsBuilder province(final String province) { this.province = province; return this; } - public CompanyCreditCardDetailsBuilder zipCode (final String zipCode) { + public CompanyCreditCardDetailsBuilder zipCode(final String zipCode) { this.zipCode = zipCode; return this; } @@ -265,69 +262,69 @@ public static class IndividualCreditCardDetailsBuilder implements Builder { private String countryCode; private String description; - public IndividualCreditCardDetailsBuilder address (final String address) { + public IndividualCreditCardDetailsBuilder address(final String address) { this.address = address; return this; } @Override public CreditCardDetails build () { - return new CreditCardDetails (this.cardHolderFirstName, this.cardHolderLastName, this.number, this.expiryDate, this.csc, this.address, this.zipCode, this.city, + return new CreditCardDetails (this.cardHolderFirstName, this.cardHolderLastName, this.number, + this.expiryDate, this.csc, this.address, this.zipCode, this.city, this.province, this.countryCode, this.description); } - public IndividualCreditCardDetailsBuilder cardHolderFirstName (final String cardHolderFirstName) { + public IndividualCreditCardDetailsBuilder cardHolderFirstName(final String cardHolderFirstName) { this.cardHolderFirstName = cardHolderFirstName; return this; } - public IndividualCreditCardDetailsBuilder cardHolderLastName (final String cardHolderLastName) { + public IndividualCreditCardDetailsBuilder cardHolderLastName(final String cardHolderLastName) { this.cardHolderLastName = cardHolderLastName; return this; } - public IndividualCreditCardDetailsBuilder city (final String city) { + public IndividualCreditCardDetailsBuilder city(final String city) { this.city = city; return this; } - public IndividualCreditCardDetailsBuilder countryCode (final String countryCode) { + public IndividualCreditCardDetailsBuilder countryCode(final String countryCode) { this.countryCode = countryCode; return this; } - public IndividualCreditCardDetailsBuilder csc (final int csc) { + public IndividualCreditCardDetailsBuilder csc(final int csc) { this.csc = csc; return this; } - public IndividualCreditCardDetailsBuilder description (final String description) { + public IndividualCreditCardDetailsBuilder description(final String description) { this.description = description; return this; } - public IndividualCreditCardDetailsBuilder expiryDate (final String expiryDate) { + public IndividualCreditCardDetailsBuilder expiryDate(final String expiryDate) { this.expiryDate = expiryDate; return this; } - public IndividualCreditCardDetailsBuilder number (final String number) { + public IndividualCreditCardDetailsBuilder number(final String number) { this.number = number; return this; } - public IndividualCreditCardDetailsBuilder province (final String p) { + public IndividualCreditCardDetailsBuilder province(final String p) { province = p; return this; } - public IndividualCreditCardDetailsBuilder zipCode (final String z) { + public IndividualCreditCardDetailsBuilder zipCode(final String z) { zipCode = z; return this; } - } - + protected final String cardHolderFirstName; protected final String cardHolderLastName; protected final String number; @@ -342,8 +339,9 @@ public IndividualCreditCardDetailsBuilder zipCode (final String z) { protected final String description; - public CreditCardDetails (final String cardHolderFirstName, final String cardHolderLastName, final String number, final String expiryDate, final int csc, - final String address, final String zipCode, final String city, final String province, final String countryCode, final String description) { + public CreditCardDetails(final String cardHolderFirstName, final String cardHolderLastName, + final String number, final String expiryDate, final int csc, final String address, + final String zipCode, final String city, final String province, final String countryCode, final String description) { super (); this.cardHolderFirstName = cardHolderFirstName; this.cardHolderLastName = cardHolderLastName; @@ -358,8 +356,8 @@ public CreditCardDetails (final String cardHolderFirstName, final String cardHol this.description = description; } } - - @JsonDeserialize (builder = EncryptedCreditCardDetails.InstrumentedCreditCardBuilder.class) + + @JsonDeserialize(builder = EncryptedCreditCardDetails.InstrumentedCreditCardBuilder.class) public static class EncryptedCreditCardDetails implements PaymentDetails { @JsonPOJOBuilder (withPrefix = "") public static class InstrumentedCreditCardBuilder implements Builder { @@ -367,16 +365,16 @@ public static class InstrumentedCreditCardBuilder implements Builder { private String name; @Override - public EncryptedCreditCardDetails build () { - return new EncryptedCreditCardDetails (this.paymentInstrumentID, this.name); + public EncryptedCreditCardDetails build() { + return new EncryptedCreditCardDetails(this.paymentInstrumentID, this.name); } - public InstrumentedCreditCardBuilder name (final String name) { + public InstrumentedCreditCardBuilder name(final String name) { this.name = name; return this; } - public InstrumentedCreditCardBuilder paymentInstrumentID (final UUID paymentInstrumentID) { + public InstrumentedCreditCardBuilder paymentInstrumentID(final UUID paymentInstrumentID) { this.paymentInstrumentID = paymentInstrumentID; return this; } @@ -385,7 +383,7 @@ public InstrumentedCreditCardBuilder paymentInstrumentID (final UUID paymentInst protected final UUID paymentInstrumentID; protected final String name; - private EncryptedCreditCardDetails (final UUID paymentInstrumentID, final String name) { + private EncryptedCreditCardDetails(final UUID paymentInstrumentID, final String name) { super (); this.paymentInstrumentID = paymentInstrumentID; this.name = name; @@ -394,7 +392,7 @@ private EncryptedCreditCardDetails (final UUID paymentInstrumentID, final String public enum FormOfPayment { INDIVIDUAL_CREDIT_CARD (CreditCardDetails.IndividualCreditCardDetailsBuilder.class), COMPANY_CREDIT_CARD ( - CreditCardDetails.CompanyCreditCardDetailsBuilder.class), INSTRUMENTED_CREDIT_CARD (EncryptedCreditCardDetails.InstrumentedCreditCardBuilder.class); + CreditCardDetails.CompanyCreditCardDetailsBuilder.class), INSTRUMENTED_CREDIT_CARD(EncryptedCreditCardDetails.InstrumentedCreditCardBuilder.class); private final Class builderClass; @@ -403,17 +401,17 @@ public enum FormOfPayment { } @SuppressWarnings ("unchecked") - public Class getDetailsClass () { - return (Class) this.builderClass.getEnclosingClass (); + public Class getDetailsClass() { + return (Class) this.builderClass.getEnclosingClass(); } - public static FormOfPayment fromDetailsClass (Class detailsClass) { + public static FormOfPayment fromDetailsClass(Class detailsClass) { for (FormOfPayment fop : FormOfPayment.values ()) { if (fop.builderClass.getEnclosingClass () == detailsClass) { return fop; } } - throw new IllegalArgumentException ("not found"); + throw new IllegalArgumentException("not found"); } } @@ -429,23 +427,23 @@ public static class PaymentMean { @JsonPOJOBuilder (withPrefix = "") @JsonPropertyOrder ({ "form_of_payment", "payment_details" }) public static class Builder { - private FormOfPayment formOfPayment; + private FormOfPayment formOfPayment; private PaymentDetails paymentDetails; - public PaymentMean build () { + public PaymentMean build() { return new PaymentMean (this.formOfPayment, this.paymentDetails); } // if you annotate with @JsonIgnore, it works, but the value // disappears in the constructor - public Builder formOfPayment (final FormOfPayment val) { + public Builder formOfPayment(final FormOfPayment val) { this.formOfPayment = val; return this; } @JsonTypeInfo (use = JsonTypeInfo.Id.CUSTOM, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "form_of_payment", visible = true) @JsonTypeIdResolver (PaymentDetailsTypeIdResolver.class) - public Builder paymentDetails (final PaymentDetails val) { + public Builder paymentDetails(final PaymentDetails val) { this.paymentDetails = val; return this; } @@ -455,10 +453,10 @@ public static Builder create() { return new Builder(); } - protected final FormOfPayment formOfPayment; + protected final FormOfPayment formOfPayment; protected final PaymentDetails paymentDetails; - private PaymentMean (final FormOfPayment formOfPayment, final PaymentDetails paymentDetails) { + private PaymentMean(final FormOfPayment formOfPayment, final PaymentDetails paymentDetails) { super (); this.formOfPayment = formOfPayment; this.paymentDetails = paymentDetails; @@ -468,30 +466,30 @@ private PaymentMean (final FormOfPayment formOfPayment, final PaymentDetails pay public static class PaymentDetailsTypeIdResolver extends TypeIdResolverBase { @SuppressWarnings ("unchecked") @Override - public String idFromValue (Object value) { + public String idFromValue(Object value) { if (! (value instanceof PaymentDetails)) { return null; } - return FormOfPayment.fromDetailsClass ((Class) value.getClass ()).name (); + return FormOfPayment.fromDetailsClass((Class) value.getClass ()).name (); } @Override - public String idFromValueAndType (Object value, Class suggestedType) { - return this.idFromValue (value); + public String idFromValueAndType(Object value, Class suggestedType) { + return this.idFromValue(value); } @Override - public JavaType typeFromId (DatabindContext context, String id) { + public JavaType typeFromId(DatabindContext context, String id) { return context.getTypeFactory().constructType(FormOfPayment.valueOf (id).getDetailsClass ()); } @Override - public String getDescForKnownTypeIds () { + public String getDescForKnownTypeIds() { return "PaymentDetails"; } @Override - public Id getMechanism () { + public Id getMechanism() { return JsonTypeInfo.Id.CUSTOM; } } @@ -503,8 +501,9 @@ public void testVisibleExternalTypeId1288() throws Exception final String asJson1 = "{\"form_of_payment\":\"INDIVIDUAL_CREDIT_CARD\", \"payment_details\":{\"card_holder_first_name\":\"John\", \"card_holder_last_name\":\"Doe\", \"number\":\"XXXXXXXXXXXXXXXX\", \"expiry_date\":\"MM/YY\"," + "\"csc\":666,\"address\":\"10 boulevard de Sebastopol\",\"zip_code\":\"75001\",\"city\":\"Paris\",\"province\":\"Ile-de-France\",\"country_code\":\"FR\",\"description\":\"John Doe personal credit card\"}}"; final String asJson2 = "{\"form_of_payment\":\"INSTRUMENTED_CREDIT_CARD\",\"payment_details\":{\"payment_instrument_id\":\"00000000-0000-0000-0000-000000000000\", \"name\":\"Mr John Doe encrypted credit card\"}}"; - final ObjectMapper objectMapper = new ObjectMapper ().setPropertyNamingStrategy (PropertyNamingStrategy.SNAKE_CASE); - + final ObjectMapper objectMapper = jsonMapperBuilder() + .propertyNamingStrategy (PropertyNamingStrategy.SNAKE_CASE) + .build(); ClassesWithoutBuilder.PaymentMean ob1 = objectMapper.readValue (asJson1, ClassesWithoutBuilder.PaymentMean.class); assertNotNull(ob1); ClassesWithBuilder.PaymentMean ob2 = objectMapper.readValue (asJson2, ClassesWithBuilder.PaymentMean.class); diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/TestSubtypesExternalPropertyMissingProperty.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/TestSubtypesExternalPropertyMissingProperty.java index ac414b3ac1..8fb53e11fd 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/TestSubtypesExternalPropertyMissingProperty.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/TestSubtypesExternalPropertyMissingProperty.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.json.JsonMapper; import org.junit.Rule; import org.junit.Test; @@ -95,7 +96,7 @@ public Orange(String name, String c) { } } - private final ObjectMapper MAPPER = new ObjectMapper(); +// private final ObjectMapper MAPPER = new ObjectMapper(); /* /********************************************************** @@ -123,18 +124,27 @@ public Orange(String name, String c) { /********************************************************** */ + protected static JsonMapper.Builder jsonMapperBuilder() { + return JsonMapper.builder(); + } + /** * Deserialization tests for external type id property present */ @Test public void testDeserializationPresent() throws Exception { - MAPPER.disable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY); - checkOrangeBox(); - checkAppleBox(); - - MAPPER.enable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY); - checkOrangeBox(); - checkAppleBox(); + ObjectMapper mapper = jsonMapperBuilder() + .disable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY) + .build(); + checkOrangeBox(mapper); + checkAppleBox(mapper); + + mapper = jsonMapperBuilder() + .enable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY) + .build(); + + checkOrangeBox(mapper); + checkAppleBox(mapper); } /** @@ -142,13 +152,17 @@ public void testDeserializationPresent() throws Exception { */ @Test public void testDeserializationNull() throws Exception { - MAPPER.disable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY); - checkOrangeBoxNull(orangeBoxNullJson); - checkAppleBoxNull(appleBoxNullJson); - - MAPPER.enable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY); - checkOrangeBoxNull(orangeBoxNullJson); - checkAppleBoxNull(appleBoxNullJson); + ObjectMapper mapper = jsonMapperBuilder() + .disable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY) + .build(); + checkOrangeBoxNull(mapper, orangeBoxNullJson); + checkAppleBoxNull(mapper, appleBoxNullJson); + + mapper = jsonMapperBuilder() + .enable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY) + .build(); + checkOrangeBoxNull(mapper, orangeBoxNullJson); + checkAppleBoxNull(mapper, appleBoxNullJson); } /** @@ -156,13 +170,17 @@ public void testDeserializationNull() throws Exception { */ @Test public void testDeserializationEmpty() throws Exception { - MAPPER.disable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY); - checkOrangeBoxEmpty(orangeBoxEmptyJson); - checkAppleBoxEmpty(appleBoxEmptyJson); - - MAPPER.enable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY); - checkOrangeBoxEmpty(orangeBoxEmptyJson); - checkAppleBoxEmpty(appleBoxEmptyJson); + ObjectMapper mapper = jsonMapperBuilder() + .disable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY) + .build(); + checkOrangeBoxEmpty(mapper, orangeBoxEmptyJson); + checkAppleBoxEmpty(mapper, appleBoxEmptyJson); + + mapper = jsonMapperBuilder() + .enable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY) + .build(); + checkOrangeBoxEmpty(mapper, orangeBoxEmptyJson); + checkAppleBoxEmpty(mapper, appleBoxEmptyJson); } /** @@ -170,13 +188,17 @@ public void testDeserializationEmpty() throws Exception { */ @Test public void testDeserializationMissing() throws Exception { - MAPPER.disable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY); - checkOrangeBoxNull(orangeBoxMissingJson); - checkAppleBoxNull(appleBoxMissingJson); - - MAPPER.enable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY); - checkBoxJsonMappingException(orangeBoxMissingJson); - checkBoxJsonMappingException(appleBoxMissingJson); + ObjectMapper mapper = jsonMapperBuilder() + .disable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY) + .build(); + checkOrangeBoxNull(mapper, orangeBoxMissingJson); + checkAppleBoxNull(mapper, appleBoxMissingJson); + + mapper = jsonMapperBuilder() + .enable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY) + .build(); + checkBoxJsonMappingException(mapper, orangeBoxMissingJson); + checkBoxJsonMappingException(mapper, appleBoxMissingJson); } /** @@ -184,17 +206,21 @@ public void testDeserializationMissing() throws Exception { */ @Test public void testDeserializationMissingRequired() throws Exception { - MAPPER.disable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY); - checkReqBoxJsonMappingException(orangeBoxMissingJson); - checkReqBoxJsonMappingException(appleBoxMissingJson); - - MAPPER.enable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY); - checkReqBoxJsonMappingException(orangeBoxMissingJson); - checkReqBoxJsonMappingException(appleBoxMissingJson); + ObjectMapper mapper = jsonMapperBuilder() + .disable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY) + .build(); + checkReqBoxJsonMappingException(mapper, orangeBoxMissingJson); + checkReqBoxJsonMappingException(mapper, appleBoxMissingJson); + + mapper = jsonMapperBuilder() + .enable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY) + .build(); + checkReqBoxJsonMappingException(mapper, orangeBoxMissingJson); + checkReqBoxJsonMappingException(mapper, appleBoxMissingJson); } - private void checkOrangeBox() throws Exception { - Box deserOrangeBox = MAPPER.readValue(orangeBoxJson, Box.class); + private void checkOrangeBox(ObjectMapper mapper) throws Exception { + Box deserOrangeBox = mapper.readValue(orangeBoxJson, Box.class); assertEquals(orangeBox.type, deserOrangeBox.type); Fruit deserOrange = deserOrangeBox.fruit; @@ -203,8 +229,8 @@ private void checkOrangeBox() throws Exception { assertEquals(orange.color, ((Orange) deserOrange).color); } - private void checkAppleBox() throws Exception { - Box deserAppleBox = MAPPER.readValue(appleBoxJson, Box.class); + private void checkAppleBox(ObjectMapper mapper) throws Exception { + Box deserAppleBox = mapper.readValue(appleBoxJson, Box.class); assertEquals(appleBox.type, deserAppleBox.type); Fruit deserApple = deserAppleBox.fruit; @@ -213,8 +239,8 @@ private void checkAppleBox() throws Exception { assertEquals(apple.seedCount, ((Apple) deserApple).seedCount); } - private void checkOrangeBoxEmpty(String json) throws Exception { - Box deserOrangeBox = MAPPER.readValue(json, Box.class); + private void checkOrangeBoxEmpty(ObjectMapper mapper, String json) throws Exception { + Box deserOrangeBox = mapper.readValue(json, Box.class); assertEquals(orangeBox.type, deserOrangeBox.type); Fruit deserOrange = deserOrangeBox.fruit; @@ -223,8 +249,8 @@ private void checkOrangeBoxEmpty(String json) throws Exception { assertNull(((Orange) deserOrange).color); } - private void checkAppleBoxEmpty(String json) throws Exception { - Box deserAppleBox = MAPPER.readValue(json, Box.class); + private void checkAppleBoxEmpty(ObjectMapper mapper, String json) throws Exception { + Box deserAppleBox = mapper.readValue(json, Box.class); assertEquals(appleBox.type, deserAppleBox.type); Fruit deserApple = deserAppleBox.fruit; @@ -233,27 +259,27 @@ private void checkAppleBoxEmpty(String json) throws Exception { assertEquals(0, ((Apple) deserApple).seedCount); } - private void checkOrangeBoxNull(String json) throws Exception { - Box deserOrangeBox = MAPPER.readValue(json, Box.class); + private void checkOrangeBoxNull(ObjectMapper mapper, String json) throws Exception { + Box deserOrangeBox = mapper.readValue(json, Box.class); assertEquals(orangeBox.type, deserOrangeBox.type); assertNull(deserOrangeBox.fruit); } - private void checkAppleBoxNull(String json) throws Exception { - Box deserAppleBox = MAPPER.readValue(json, Box.class); + private void checkAppleBoxNull(ObjectMapper mapper, String json) throws Exception { + Box deserAppleBox = mapper.readValue(json, Box.class); assertEquals(appleBox.type, deserAppleBox.type); assertNull(deserAppleBox.fruit); } - private void checkBoxJsonMappingException(String json) throws Exception { + private void checkBoxJsonMappingException(ObjectMapper mapper, String json) throws Exception { thrown.expect(JsonMappingException.class); thrown.expectMessage("Missing property 'fruit' for external type id 'type'"); - MAPPER.readValue(json, Box.class); + mapper.readValue(json, Box.class); } - private void checkReqBoxJsonMappingException(String json) throws Exception { + private void checkReqBoxJsonMappingException(ObjectMapper mapper, String json) throws Exception { thrown.expect(JsonMappingException.class); thrown.expectMessage("Missing property 'fruit' for external type id 'type'"); - MAPPER.readValue(json, ReqBox.class); + mapper.readValue(json, ReqBox.class); } } diff --git a/src/test/java/com/fasterxml/jackson/databind/misc/AccessFixTest.java b/src/test/java/com/fasterxml/jackson/databind/misc/AccessFixTest.java index 34ff725186..94e37dff14 100644 --- a/src/test/java/com/fasterxml/jackson/databind/misc/AccessFixTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/misc/AccessFixTest.java @@ -24,7 +24,7 @@ public void checkPermission(Permission perm) throws SecurityException { public void testCauseOfThrowableIgnoral() throws Exception { final SecurityManager origSecMan = System.getSecurityManager(); - ObjectMapper mapper = objectMapperBuilder() + ObjectMapper mapper = jsonMapperBuilder() .disable(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS) .build(); try { diff --git a/src/test/java/com/fasterxml/jackson/databind/misc/CaseInsensitive1854Test.java b/src/test/java/com/fasterxml/jackson/databind/misc/CaseInsensitive1854Test.java index 44fdd7f301..7fe97ac970 100644 --- a/src/test/java/com/fasterxml/jackson/databind/misc/CaseInsensitive1854Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/misc/CaseInsensitive1854Test.java @@ -55,7 +55,7 @@ public String getId() { public void testIssue1854() throws Exception { - ObjectMapper mapper = objectMapperBuilder() + ObjectMapper mapper = jsonMapperBuilder() .enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES) .build(); final String DOC = aposToQuotes("{'ID': 1, 'Items': [ { 'ChildID': 10 } ]}"); diff --git a/src/test/java/com/fasterxml/jackson/databind/misc/CaseInsensitiveDeserTest.java b/src/test/java/com/fasterxml/jackson/databind/misc/CaseInsensitiveDeserTest.java index 4e0224954c..0fcdf1e61c 100644 --- a/src/test/java/com/fasterxml/jackson/databind/misc/CaseInsensitiveDeserTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/misc/CaseInsensitiveDeserTest.java @@ -50,8 +50,8 @@ public InsensitiveCreator(@JsonProperty("value") int v0) { /******************************************************** */ - private final ObjectMapper MAPPER = new ObjectMapper(); - private final ObjectMapper INSENSITIVE_MAPPER = objectMapperBuilder() + private final ObjectMapper MAPPER = objectMapper(); + private final ObjectMapper INSENSITIVE_MAPPER = jsonMapperBuilder() .enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES) .build(); @@ -111,10 +111,11 @@ public void testCreatorWithInsensitive() throws Exception // And allow config overrides too public void testCaseInsensitiveWithClassFormat() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.configOverride(Role.class) - .setFormat(JsonFormat.Value.empty() - .withFeature(JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)); + ObjectMapper mapper = jsonMapperBuilder() + .withConfigOverride(Role.class, + o -> o.setFormat(JsonFormat.Value.empty() + .withFeature(JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES))) + .build(); Role role = mapper.readValue (aposToQuotes("{'id':'12','name':'Foo'}"), Role.class); diff --git a/src/test/java/com/fasterxml/jackson/databind/misc/RaceCondition738Test.java b/src/test/java/com/fasterxml/jackson/databind/misc/RaceCondition738Test.java index bb5bb348ca..92254db3ea 100644 --- a/src/test/java/com/fasterxml/jackson/databind/misc/RaceCondition738Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/misc/RaceCondition738Test.java @@ -63,7 +63,7 @@ public void testRepeatedly() throws Exception { } void runOnce(int round, int max) throws Exception { - final ObjectMapper mapper = newObjectMapper(); + final ObjectMapper mapper = newJsonMapper(); Callable writeJson = new Callable() { @Override public String call() throws Exception { diff --git a/src/test/java/com/fasterxml/jackson/databind/misc/TestBlocking.java b/src/test/java/com/fasterxml/jackson/databind/misc/TestBlocking.java index 8f9592f7b3..bfb4a1f4f6 100644 --- a/src/test/java/com/fasterxml/jackson/databind/misc/TestBlocking.java +++ b/src/test/java/com/fasterxml/jackson/databind/misc/TestBlocking.java @@ -7,11 +7,10 @@ import com.fasterxml.jackson.databind.*; /** - * Unit test mostly written to cover issue [JACKSON-81]; unintended blocking + * Unit test mostly written to cover issue with unintended blocking * after data binding. */ -public class TestBlocking - extends BaseMapTest +public class TestBlocking extends BaseMapTest { /** * This is an indirect test that should trigger problems if (and only if) diff --git a/src/test/java/com/fasterxml/jackson/databind/misc/ThreadSafety1759Test.java b/src/test/java/com/fasterxml/jackson/databind/misc/ThreadSafety1759Test.java index 236629f4fd..43ceaaa8f3 100644 --- a/src/test/java/com/fasterxml/jackson/databind/misc/ThreadSafety1759Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/misc/ThreadSafety1759Test.java @@ -19,7 +19,7 @@ public class ThreadSafety1759Test extends BaseMapTest public void testCalendarForDeser() throws Exception { - final ObjectMapper mapper = newObjectMapper(); + final ObjectMapper mapper = newJsonMapper(); final int numThreads = 4; final int COUNT = 3000; diff --git a/src/test/java/com/fasterxml/jackson/databind/mixins/MapperMixinsCopy1998Test.java b/src/test/java/com/fasterxml/jackson/databind/mixins/MapperMixinsCopy1998Test.java index 2a78dcd5e6..cc027396a6 100644 --- a/src/test/java/com/fasterxml/jackson/databind/mixins/MapperMixinsCopy1998Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/mixins/MapperMixinsCopy1998Test.java @@ -1,10 +1,9 @@ package com.fasterxml.jackson.databind.mixins; -import java.io.IOException; - import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.cfg.MapperBuilder; public class MapperMixinsCopy1998Test extends BaseMapTest { @@ -88,42 +87,56 @@ public MyChildB(String name) { } } - @SuppressWarnings("deprecation") - public void testB_KO() throws Exception + // [databind#1998]: leakage of state via ObjectMapper.copy() (2.x) and similar (3.x) + public void testSharedBuilder() throws Exception { - final ObjectMapper DEFAULT = defaultMapper(); + final MapperBuilder B = defaultMapper(); MyModelRoot myModelInstance = new MyModelRoot(); myModelInstance.setChild(new MyChildB("testB")); - ObjectMapper myObjectMapper = DEFAULT.copy(); + ObjectMapper mapper = B.build(); - String postResult = getString(myModelInstance, myObjectMapper); + String postResult = mapper.writeValueAsString(myModelInstance); assertEquals(FULLMODEL, postResult); -// System.out.println("postResult: "+postResult); - myObjectMapper = DEFAULT.copy(); -// myObjectMapper = defaultMapper(); - myObjectMapper.addMixIn(MyModelRoot.class, MixinConfig.MyModelRoot.class) + mapper = B + .addMixIn(MyModelRoot.class, MixinConfig.MyModelRoot.class) .addMixIn(MyModelChildBase.class, MixinConfig.MyModelChildBase.class) .disable(MapperFeature.DEFAULT_VIEW_INCLUSION) - .setConfig(myObjectMapper.getSerializationConfig().withView(MyModelView.class)); - - String result = getString(myModelInstance, myObjectMapper); + .build(); + String result = mapper + .writerWithView(MyModelView.class) + .writeValueAsString(myModelInstance); assertEquals(EXPECTED, result); - } - private String getString(MyModelRoot myModelInstance, ObjectMapper myObjectMapper) throws IOException { - return myObjectMapper.writerFor(MyModelRoot.class).writeValueAsString(myModelInstance); + // [databind#1998]: leakage of state via ObjectMapper.copy() (2.x) and similar (3.x) + public void testSharingViaRebuild() throws Exception + { + final MapperBuilder B = defaultMapper(); + MyModelRoot myModelInstance = new MyModelRoot(); + myModelInstance.setChild(new MyChildB("testB")); + + ObjectMapper mapper = B.build(); + + String postResult = mapper.writeValueAsString(myModelInstance); + assertEquals(FULLMODEL, postResult); + + mapper = mapper.rebuild() + .addMixIn(MyModelRoot.class, MixinConfig.MyModelRoot.class) + .addMixIn(MyModelChildBase.class, MixinConfig.MyModelChildBase.class) + .disable(MapperFeature.DEFAULT_VIEW_INCLUSION) + .build(); + String result = mapper + .writerWithView(MyModelView.class) + .writeValueAsString(myModelInstance); + assertEquals(EXPECTED, result); } - private ObjectMapper defaultMapper() + private MapperBuilder defaultMapper() { - return objectMapperBuilder() - .serializationInclusion(JsonInclude.Include.NON_EMPTY) - .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) - .configure(MapperFeature.ALLOW_COERCION_OF_SCALARS, false) - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true) - .build(); + return jsonMapperBuilder().changeDefaultPropertyInclusion(incl -> + incl.withValueInclusion(JsonInclude.Include.NON_EMPTY)) + ; } } diff --git a/src/test/java/com/fasterxml/jackson/databind/mixins/MixinsWithBundlesTest.java b/src/test/java/com/fasterxml/jackson/databind/mixins/MixinsWithBundlesTest.java index 79dc4947e7..6d574b7d3c 100644 --- a/src/test/java/com/fasterxml/jackson/databind/mixins/MixinsWithBundlesTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/mixins/MixinsWithBundlesTest.java @@ -36,7 +36,9 @@ public String getStuff() { } public void testMixinWithBundles() throws Exception { - ObjectMapper mapper = new ObjectMapper().addMixIn(Foo.class, FooMixin.class); + ObjectMapper mapper = jsonMapperBuilder() + .addMixIn(Foo.class, FooMixin.class) + .build(); String result = mapper.writeValueAsString(new Foo("result")); assertEquals("{\"bar\":\"result\"}", result); } 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 50e1d1c60c..a3a7d3096e 100644 --- a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForClass.java +++ b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForClass.java @@ -59,8 +59,9 @@ public void testClassMixInsTopLevel() throws IOException // Then with leaf-level mix-in; without (method) auto-detect, // should use field - m = new ObjectMapper(); - m.addMixIn(LeafClass.class, MixIn.class); + m = jsonMapperBuilder() + .addMixIn(LeafClass.class, MixIn.class) + .build(); result = m.readValue("{\"a\":\"value\"}", LeafClass.class); assertEquals("value", result.a); } @@ -69,8 +70,9 @@ public void testClassMixInsTopLevel() throws IOException // when deserializing leaf (but will if deserializing base class) public void testClassMixInsMidLevel() throws IOException { - ObjectMapper m = new ObjectMapper(); - m.addMixIn(BaseClass.class, MixIn.class); + ObjectMapper m = jsonMapperBuilder() + .addMixIn(BaseClass.class, MixIn.class) + .build(); { BaseClass result = m.readValue("{\"a\":\"value\"}", BaseClass.class); assertEquals("value", result.a); @@ -88,8 +90,9 @@ public void testClassMixInsMidLevel() throws IOException */ public void testClassMixInsForObjectClass() throws IOException { - ObjectMapper m = new ObjectMapper(); - m.addMixIn(Object.class, MixIn.class); + ObjectMapper m = jsonMapperBuilder() + .addMixIn(Object.class, MixIn.class) + .build(); // will be seen for BaseClass { BaseClass result = m.readValue("{\"a\":\"\"}", BaseClass.class); @@ -106,8 +109,9 @@ public void testClassMixInsForObjectClass() throws IOException // [databind#1990]: can apply mix-in to `Object#hashCode()` to force serialization public void testHashCodeViaObject() throws Exception { - ObjectMapper mapper = new ObjectMapper() - .addMixIn(Object.class, HashCodeMixIn.class); + ObjectMapper mapper = jsonMapperBuilder() + .addMixIn(Object.class, HashCodeMixIn.class) + .build(); // First, with something that overrides hashCode() assertEquals( "{\"hashCode\":13}", 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 22c2510505..f37be41919 100644 --- a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForCreators.java +++ b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForCreators.java @@ -111,44 +111,47 @@ static TestMixinDeserForCreators.Pair2020 with(@JsonProperty("value0") Object va public void testForConstructor() throws IOException { - ObjectMapper m = new ObjectMapper(); - m.addMixIn(BaseClassWithPrivateCtor.class, MixInForPrivate.class); - BaseClassWithPrivateCtor result = m.readValue("\"?\"", BaseClassWithPrivateCtor.class); + ObjectMapper mapper = jsonMapperBuilder() + .addMixIn(BaseClassWithPrivateCtor.class, MixInForPrivate.class) + .build(); + BaseClassWithPrivateCtor result = mapper.readValue("\"?\"", BaseClassWithPrivateCtor.class); assertEquals("?...", result._a); } public void testForFactoryAndCtor() throws IOException { - ObjectMapper m = new ObjectMapper(); BaseClass result; // First: test default behavior: should use constructor - result = m.readValue("\"string\"", BaseClass.class); + result = new ObjectMapper().readValue("\"string\"", BaseClass.class); assertEquals("string...", result._a); // Then with simple mix-in: should change to use the factory method - m = new ObjectMapper(); - m.addMixIn(BaseClass.class, MixIn.class); - result = m.readValue("\"string\"", BaseClass.class); + ObjectMapper mapper = jsonMapperBuilder() + .addMixIn(BaseClass.class, MixIn.class) + .build(); + result = mapper.readValue("\"string\"", BaseClass.class); assertEquals("stringX", result._a); } public void testFactoryDelegateMixIn() throws IOException { - ObjectMapper m = new ObjectMapper(); - m.addMixIn(StringWrapper.class, StringWrapperMixIn.class); - StringWrapper result = m.readValue("\"a\"", StringWrapper.class); + ObjectMapper mapper = jsonMapperBuilder() + .addMixIn(StringWrapper.class, StringWrapperMixIn.class) + .build(); + StringWrapper result = mapper.readValue("\"a\"", StringWrapper.class); assertEquals("a", result._value); } // [databind#2020] public void testFactoryPropertyMixin() throws Exception { - ObjectMapper objectMapper = new ObjectMapper(); - objectMapper.addMixIn(Pair2020.class, MyPairMixIn8.class); + ObjectMapper mapper = jsonMapperBuilder() + .addMixIn(Pair2020.class, MyPairMixIn8.class) + .build(); String doc = aposToQuotes( "{'value0' : 456, 'value1' : 789}"); - Pair2020 pair2 = objectMapper.readValue(doc, Pair2020.class); + Pair2020 pair2 = mapper.readValue(doc, Pair2020.class); assertEquals(456, pair2.x); assertEquals(789, pair2.y); } 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 0014feae65..93b2100458 100644 --- a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForMethods.java +++ b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForMethods.java @@ -44,9 +44,10 @@ interface MixIn */ public void testWithAnySetter() throws IOException { - ObjectMapper m = new ObjectMapper(); - m.addMixIn(BaseClass.class, MixIn.class); - BaseClass result = m.readValue("{ \"a\" : 3, \"b\" : true }", BaseClass.class); + ObjectMapper mapper = jsonMapperBuilder() + .addMixIn(BaseClass.class, MixIn.class) + .build(); + BaseClass result = mapper.readValue("{ \"a\" : 3, \"b\" : true }", BaseClass.class); assertNotNull(result); assertEquals(2, result.values.size()); assertEquals(Integer.valueOf(3), result.values.get("a")); 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 3fad43c3d3..ee2fe92512 100644 --- a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinInheritance.java +++ b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinInheritance.java @@ -50,8 +50,9 @@ static abstract class BeanoMixinSub2 extends BeanoMixinSuper2 { public void testMixinFieldInheritance() throws IOException { - ObjectMapper mapper = new ObjectMapper(); - mapper.addMixIn(Beano.class, BeanoMixinSub.class); + ObjectMapper mapper = jsonMapperBuilder() + .addMixIn(Beano.class, BeanoMixinSub.class) + .build(); Map result; result = writeAndMap(mapper, new Beano()); assertEquals(2, result.size()); @@ -63,8 +64,9 @@ public void testMixinFieldInheritance() throws IOException public void testMixinMethodInheritance() throws IOException { - ObjectMapper mapper = new ObjectMapper(); - mapper.addMixIn(Beano2.class, BeanoMixinSub2.class); + ObjectMapper mapper = jsonMapperBuilder() + .addMixIn(Beano2.class, BeanoMixinSub2.class) + .build(); Map result; result = writeAndMap(mapper, new Beano2()); assertEquals(2, result.size()); diff --git a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinMerging.java b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinMerging.java index f16fac1389..07631ab646 100644 --- a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinMerging.java +++ b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinMerging.java @@ -1,7 +1,8 @@ package com.fasterxml.jackson.databind.mixins; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; import com.fasterxml.jackson.annotation.JsonProperty; - +import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.module.SimpleModule; @@ -38,13 +39,14 @@ static class PersonMixin extends ContactMixin implements Person {} public void testDisappearingMixins515() throws Exception { SimpleModule module = new SimpleModule("Test"); - module.setMixInAnnotation(Person.class, PersonMixin.class); - ObjectMapper mapper = objectMapperBuilder() - .disable(MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS) - .disable(MapperFeature.AUTO_DETECT_FIELDS) - .disable(MapperFeature.AUTO_DETECT_GETTERS) - .disable(MapperFeature.AUTO_DETECT_IS_GETTERS) + module.setMixInAnnotation(Person.class, PersonMixin.class); + ObjectMapper mapper = jsonMapperBuilder() .disable(MapperFeature.INFER_PROPERTY_MUTATORS) + .disable(MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS) + .changeDefaultVisibility(vc -> vc + .withVisibility(PropertyAccessor.FIELD, Visibility.NONE) + .withVisibility(PropertyAccessor.GETTER, Visibility.NONE) + .withVisibility(PropertyAccessor.IS_GETTER, Visibility.NONE)) .addModule(module) .build(); assertEquals("{\"city\":\"Seattle\"}", mapper.writeValueAsString(new PersonImpl())); 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 9fd211fc5d..ce72455a46 100644 --- a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerForClass.java +++ b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerForClass.java @@ -78,16 +78,18 @@ public void testClassMixInsTopLevel() throws IOException assertEquals("abc", result.get("a")); // then with top-level override - mapper = new ObjectMapper(); - mapper.addMixIn(LeafClass.class, MixIn.class); + mapper = jsonMapperBuilder() + .addMixIn(LeafClass.class, MixIn.class) + .build(); result = writeAndMap(mapper, new LeafClass("abc")); assertEquals(2, result.size()); assertEquals("abc", result.get("a")); assertEquals("c", result.get("c")); // mid-level override; should not have any effect - mapper = new ObjectMapper(); - mapper.addMixIn(BaseClass.class, MixIn.class); + mapper = jsonMapperBuilder() + .addMixIn(BaseClass.class, MixIn.class) + .build(); result = writeAndMap(mapper, new LeafClass("abc")); assertEquals(1, result.size()); assertEquals("abc", result.get("a")); @@ -107,20 +109,11 @@ public void testClassMixInsMidLevel() throws IOException assertEquals("c2", result.get("c")); // then with working mid-level override, which effectively suppresses 'a' - mapper = new ObjectMapper(); - mapper.addMixIn(BaseClass.class, MixInAutoDetect.class); + mapper = jsonMapperBuilder() + .addMixIn(BaseClass.class, MixInAutoDetect.class) + .build(); result = writeAndMap(mapper, bean); assertEquals(1, result.size()); assertEquals("c2", result.get("c")); - - // and related to [databind#245], apply mix-ins to a copy of ObjectMapper - ObjectMapper mapper2 = new ObjectMapper(); - result = writeAndMap(mapper2, bean); - assertEquals(2, result.size()); - ObjectMapper mapper3 = mapper2.copy(); - 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 2f7bf12b40..2a4faba3be 100644 --- a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerForFields.java +++ b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerForFields.java @@ -68,8 +68,9 @@ public void testFieldMixInsTopLevel() throws IOException assertEquals("1", result.get("a")); // and then with simple mix-in - mapper = new ObjectMapper(); - mapper.addMixIn(BaseClass.class, MixIn.class); + mapper = jsonMapperBuilder() + .addMixIn(BaseClass.class, MixIn.class) + .build(); result = writeAndMap(mapper, bean); assertEquals(2, result.size()); assertEquals("1", result.get("a")); @@ -78,15 +79,16 @@ public void testFieldMixInsTopLevel() throws IOException public void testMultipleFieldMixIns() throws IOException { - ObjectMapper mapper = new ObjectMapper(); // ordering here shouldn't matter really... HashMap,Class> mixins = new HashMap,Class>(); mixins.put(SubClass.class, MixIn.class); mixins.put(BaseClass.class, MixIn2.class); - mapper.setMixIns(mixins); - Map result; - result = writeAndMap(mapper, new SubClass("1", "2")); + ObjectMapper mapper = jsonMapperBuilder() + .addMixIns(mixins) + .build(); + + Map result = writeAndMap(mapper, new SubClass("1", "2")); assertEquals(1, result.size()); // 'a' should be suppressed; 'b' mapped to 'banana' assertEquals("2", result.get("banana")); 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 a57cffe382..b3585a9feb 100644 --- a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerForMethods.java +++ b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerForMethods.java @@ -5,8 +5,7 @@ import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.introspect.ClassIntrospector; -import com.fasterxml.jackson.databind.introspect.ClassIntrospector.MixInResolver; +import com.fasterxml.jackson.databind.introspect.MixInResolver; public class TestMixinSerForMethods extends BaseMapTest @@ -109,8 +108,9 @@ public void testLeafMixin() throws IOException assertEquals("b2", result.get("b")); // then with leaf-level mix-in - mapper = new ObjectMapper(); - mapper.addMixIn(BaseClass.class, MixIn.class); + mapper = jsonMapperBuilder() + .addMixIn(BaseClass.class, MixIn.class) + .build(); result = writeAndMap(mapper, bean); assertEquals(2, result.size()); assertEquals("b2", result.get("b2")); @@ -124,11 +124,12 @@ public void testLeafMixin() throws IOException */ public void testIntermediateMixin() throws IOException { - ObjectMapper mapper = new ObjectMapper(); Map result; LeafClass bean = new LeafClass("XXX", "b2"); - mapper.addMixIn(BaseClass.class, MixIn.class); + ObjectMapper mapper = jsonMapperBuilder() + .addMixIn(BaseClass.class, MixIn.class) + .build(); result = writeAndMap(mapper, bean); assertEquals(1, result.size()); assertEquals("XXX", result.get("a")); @@ -140,8 +141,9 @@ public void testIntermediateMixin() throws IOException */ public void testIntermediateMixin2() throws IOException { - ObjectMapper mapper = new ObjectMapper(); - mapper.addMixIn(EmptyBean.class, MixInForSimple.class); + ObjectMapper mapper = jsonMapperBuilder() + .addMixIn(EmptyBean.class, MixInForSimple.class) + .build(); Map result = writeAndMap(mapper, new SimpleBean()); assertEquals(1, result.size()); assertEquals(Integer.valueOf(42), result.get("x")); @@ -150,21 +152,23 @@ public void testIntermediateMixin2() throws IOException // [databind#688] public void testCustomResolver() throws IOException { - ObjectMapper mapper = new ObjectMapper(); - mapper.setMixInResolver(new ClassIntrospector.MixInResolver() { - @Override - public Class findMixInClassFor(Class target) { - if (target == EmptyBean.class) { - return MixInForSimple.class; - } - return null; - } - - @Override - public MixInResolver copy() { - return this; - } - }); + ObjectMapper mapper = jsonMapperBuilder() + .mixInOverrides(new MixInResolver() { + @Override + public Class findMixInClassFor(Class target) { + if (target == EmptyBean.class) { + return MixInForSimple.class; + } + return null; + } + + @Override + public MixInResolver snapshot() { + return this; + } + }) + .build(); + Map result = writeAndMap(mapper, new SimpleBean()); assertEquals(1, result.size()); assertEquals(Integer.valueOf(42), result.get("x")); 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 1f3e37db9a..28d9fc4fae 100644 --- a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerWithViews.java +++ b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerWithViews.java @@ -174,34 +174,30 @@ public void testIssue560() throws Exception A a = new A("myname", 29, "mysurname"); // Property SerializationConfig.SerializationFeature.DEFAULT_VIEW_INCLUSION set to false - ObjectMapper mapper = objectMapperBuilder() - .configure(MapperFeature.DEFAULT_VIEW_INCLUSION, Boolean.FALSE) - .addMixIn(A.class, AMixInAnnotation.class) - .build(); + ObjectMapper mapper = jsonMapperBuilder() + .disable(MapperFeature.DEFAULT_VIEW_INCLUSION) + .addMixIn(A.class, AMixInAnnotation.class) + .build(); String json = mapper.writerWithView(AView.class).writeValueAsString(a); - assertTrue(json.indexOf("\"name\"") > 0); } - + /* /********************************************************** /* Helper methods /********************************************************** */ - + private ObjectMapper createObjectMapper() { - ObjectMapper objectMapper = objectMapperBuilder() - .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false ) - .serializationInclusion(JsonInclude.Include.NON_NULL ) - .configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false ) - .build(); - Map, Class> sourceMixins = new HashMap, Class>( ); sourceMixins.put( SimpleTestData.class, TestDataJAXBMixin.class ); sourceMixins.put( ComplexTestData.class, TestComplexDataJAXBMixin.class ); - - objectMapper.setMixIns(sourceMixins); - return objectMapper; + return jsonMapperBuilder() + .changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_NULL)) + .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS) + .disable(MapperFeature.DEFAULT_VIEW_INCLUSION) + .addMixIns(sourceMixins) + .build(); } } diff --git a/src/test/java/com/fasterxml/jackson/databind/module/SimpleModuleArgCheckTest.java b/src/test/java/com/fasterxml/jackson/databind/module/SimpleModuleArgCheckTest.java index 8de27cd85b..b3713a17d7 100644 --- a/src/test/java/com/fasterxml/jackson/databind/module/SimpleModuleArgCheckTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/module/SimpleModuleArgCheckTest.java @@ -1,9 +1,5 @@ package com.fasterxml.jackson.databind.module; -import java.util.Collections; -import java.util.List; -import java.util.Map; - import com.fasterxml.jackson.core.Version; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.jsontype.NamedType; @@ -18,9 +14,7 @@ public class SimpleModuleArgCheckTest extends BaseMapTest public void testInvalidForDeserializers() throws Exception { - SimpleModule mod = new SimpleModule("test", Version.unknownVersion(), - (Map,JsonDeserializer>) null); - + SimpleModule mod = new SimpleModule("test", Version.unknownVersion()); try { mod.addDeserializer(String.class, null); fail("Should not pass"); @@ -36,39 +30,6 @@ public void testInvalidForDeserializers() throws Exception } } - /* - /********************************************************** - /* Unit tests for invalid serializers - /********************************************************** - */ - - public void testInvalidForSerializers() throws Exception - { - SimpleModule mod = new SimpleModule("test", Version.unknownVersion(), - (List>) null); - - try { - mod.addSerializer(String.class, null); - fail("Should not pass"); - } catch (IllegalArgumentException e) { - verifyException(e, "Cannot pass `null` as serializer"); - } - - try { - mod.addSerializer((JsonSerializer) null); - fail("Should not pass"); - } catch (IllegalArgumentException e) { - verifyException(e, "Cannot pass `null` as serializer"); - } - - try { - mod.addKeySerializer(String.class, null); - fail("Should not pass"); - } catch (IllegalArgumentException e) { - verifyException(e, "Cannot pass `null` as key serializer"); - } - } - /* /********************************************************** /* Unit tests for invalid misc other @@ -77,12 +38,7 @@ public void testInvalidForSerializers() throws Exception public void testInvalidAbstractTypeMapping() throws Exception { - // just for funsies let's use more esoteric constructor - Map,JsonDeserializer> desers = Collections.emptyMap(); - List> sers = Collections.emptyList(); - SimpleModule mod = new SimpleModule("test", Version.unknownVersion(), - desers, sers); - + SimpleModule mod = new SimpleModule("test", Version.unknownVersion()); try { mod.addAbstractTypeMapping(null, String.class); fail("Should not pass"); @@ -99,8 +55,7 @@ public void testInvalidAbstractTypeMapping() throws Exception public void testInvalidSubtypeMappings() throws Exception { - SimpleModule mod = new SimpleModule("test", Version.unknownVersion(), - null, null); + SimpleModule mod = new SimpleModule("test", Version.unknownVersion()); try { mod.registerSubtypes(String.class, null); fail("Should not pass"); diff --git a/src/test/java/com/fasterxml/jackson/databind/module/SimpleModuleTest.java b/src/test/java/com/fasterxml/jackson/databind/module/SimpleModuleTest.java index 40a189e69d..8595a8483b 100644 --- a/src/test/java/com/fasterxml/jackson/databind/module/SimpleModuleTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/module/SimpleModuleTest.java @@ -1,13 +1,12 @@ package com.fasterxml.jackson.databind.module; import java.io.IOException; -import java.lang.reflect.Type; import java.util.*; - import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.cfg.MapperBuilder; import com.fasterxml.jackson.databind.module.SimpleDeserializers; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.module.SimpleSerializers; @@ -40,26 +39,21 @@ static class CustomBeanSerializer extends StdSerializer public CustomBeanSerializer() { super(CustomBean.class); } @Override - public void serialize(CustomBean value, JsonGenerator jgen, SerializerProvider provider) + public void serialize(CustomBean value, JsonGenerator g, SerializerProvider provider) throws IOException, JsonProcessingException { // We will write it as a String, with '|' as delimiter - jgen.writeString(value.str + "|" + value.num); - } - - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException { - return null; + g.writeString(value.str + "|" + value.num); } } static class CustomBeanDeserializer extends JsonDeserializer { @Override - public CustomBean deserialize(JsonParser jp, DeserializationContext ctxt) - throws IOException, JsonProcessingException + public CustomBean deserialize(JsonParser p, DeserializationContext ctxt) + throws IOException { - String text = jp.getText(); + String text = p.getText(); int ix = text.indexOf('|'); if (ix < 0) { throw new IOException("Failed to parse String value of \""+text+"\""); @@ -75,25 +69,20 @@ static class SimpleEnumSerializer extends StdSerializer public SimpleEnumSerializer() { super(SimpleEnum.class); } @Override - public void serialize(SimpleEnum value, JsonGenerator jgen, SerializerProvider provider) - throws IOException, JsonProcessingException + public void serialize(SimpleEnum value, JsonGenerator g, SerializerProvider provider) + throws IOException { - jgen.writeString(value.name().toLowerCase()); - } - - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException { - return null; + g.writeString(value.name().toLowerCase()); } } static class SimpleEnumDeserializer extends JsonDeserializer { @Override - public SimpleEnum deserialize(JsonParser jp, DeserializationContext ctxt) - throws IOException, JsonProcessingException + public SimpleEnum deserialize(JsonParser p, DeserializationContext ctxt) + throws IOException { - return SimpleEnum.valueOf(jp.getText().toUpperCase()); + return SimpleEnum.valueOf(p.getText().toUpperCase()); } } @@ -116,8 +105,8 @@ static class BaseSerializer extends StdScalarSerializer public BaseSerializer() { super(Base.class); } @Override - public void serialize(Base value, JsonGenerator jgen, SerializerProvider provider) throws IOException { - jgen.writeString("Base:"+value.getText()); + public void serialize(Base value, JsonGenerator g, SerializerProvider provider) throws IOException { + g.writeString("Base:"+value.getText()); } } @@ -157,25 +146,6 @@ public AnotherSimpleModule(String name, Version version) { } } - protected static class ContextVerifierModule extends com.fasterxml.jackson.databind.Module - { - @Override - public String getModuleName() { return "x"; } - - @Override - public Version version() { return Version.unknownVersion(); } - - @Override - public void setupModule(SetupContext context) - { - ObjectCodec c = context.getOwner(); - assertNotNull(c); - assertTrue(c instanceof ObjectMapper); - ObjectMapper m = context.getOwner(); - assertNotNull(m); - } - } - static class TestModule626 extends SimpleModule { final Class mixin, target; public TestModule626(Class t, Class m) { @@ -186,7 +156,7 @@ public TestModule626(Class t, Class m) { @Override public void setupModule(SetupContext context) { - context.setMixInAnnotations(target, mixin); + context.setMixIn(target, mixin); } } @@ -216,8 +186,13 @@ public void testWithoutModule() mapper.readValue("{\"str\":\"ab\",\"num\":2}", CustomBean.class); fail("Should have caused an exception"); } catch (IOException e) { + // 20-Sep-2017, tatu: Jackson 2.x had different exception; 3.x finds implicits too + verifyException(e, "Unrecognized field \"str\""); + + /* verifyException(e, "Cannot construct"); verifyException(e, "no creators"); + */ } } @@ -229,31 +204,34 @@ public void testWithoutModule() public void testSimpleBeanSerializer() throws Exception { - ObjectMapper mapper = new ObjectMapper(); SimpleModule mod = new SimpleModule("test", Version.unknownVersion()); mod.addSerializer(new CustomBeanSerializer()); - mapper.registerModule(mod); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(mod) + .build(); assertEquals(quote("abcde|5"), mapper.writeValueAsString(new CustomBean("abcde", 5))); } public void testSimpleEnumSerializer() throws Exception { - ObjectMapper mapper = new ObjectMapper(); SimpleModule mod = new SimpleModule("test", Version.unknownVersion()); mod.addSerializer(new SimpleEnumSerializer()); // for fun, call "multi-module" registration - mapper.registerModules(mod); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(mod) + .build(); assertEquals(quote("b"), mapper.writeValueAsString(SimpleEnum.B)); } public void testSimpleInterfaceSerializer() throws Exception { - ObjectMapper mapper = new ObjectMapper(); SimpleModule mod = new SimpleModule("test", Version.unknownVersion()); mod.addSerializer(new BaseSerializer()); // and another variant here too List mods = Arrays.asList(mod); - mapper.registerModules(mods); + ObjectMapper mapper = jsonMapperBuilder() + .addModules(mods) + .build(); assertEquals(quote("Base:1"), mapper.writeValueAsString(new Impl1())); assertEquals(quote("Base:2"), mapper.writeValueAsString(new Impl2())); } @@ -266,10 +244,11 @@ public void testSimpleInterfaceSerializer() throws Exception public void testSimpleBeanDeserializer() throws Exception { - ObjectMapper mapper = new ObjectMapper(); SimpleModule mod = new SimpleModule("test", Version.unknownVersion()); mod.addDeserializer(CustomBean.class, new CustomBeanDeserializer()); - mapper.registerModule(mod); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(mod) + .build(); CustomBean bean = mapper.readValue(quote("xyz|3"), CustomBean.class); assertEquals("xyz", bean.str); assertEquals(3, bean.num); @@ -277,10 +256,11 @@ public void testSimpleBeanDeserializer() throws Exception public void testSimpleEnumDeserializer() throws Exception { - ObjectMapper mapper = new ObjectMapper(); SimpleModule mod = new SimpleModule("test", Version.unknownVersion()); mod.addDeserializer(SimpleEnum.class, new SimpleEnumDeserializer()); - mapper.registerModule(mod); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(mod) + .build(); SimpleEnum result = mapper.readValue(quote("a"), SimpleEnum.class); assertSame(SimpleEnum.A, result); } @@ -297,17 +277,19 @@ public void testMultipleModules() throws Exception mod2.setDeserializers(new SimpleDeserializers(desers)); mod2.addSerializer(CustomBean.class, new CustomBeanSerializer()); - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(mod1); - mapper.registerModule(mod2); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(mod1) + .addModule(mod2) + .build(); assertEquals(quote("b"), mapper.writeValueAsString(SimpleEnum.B)); SimpleEnum result = mapper.readValue(quote("a"), SimpleEnum.class); assertSame(SimpleEnum.A, result); // also let's try it with different order of registration, just in case - mapper = new ObjectMapper(); - mapper.registerModule(mod2); - mapper.registerModule(mod1); + mapper = jsonMapperBuilder() + .addModule(mod2) + .addModule(mod1) + .build(); assertEquals(quote("b"), mapper.writeValueAsString(SimpleEnum.B)); result = mapper.readValue(quote("a"), SimpleEnum.class); assertSame(SimpleEnum.A, result); @@ -318,14 +300,16 @@ public void testGetRegisteredModules() MySimpleModule mod1 = new MySimpleModule("test1", Version.unknownVersion()); AnotherSimpleModule mod2 = new AnotherSimpleModule("test2", Version.unknownVersion()); - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(mod1); - mapper.registerModule(mod2); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(mod1) + .addModule(mod2) + .build(); - Set registeredModuleIds = mapper.getRegisteredModuleIds(); - assertEquals(2, registeredModuleIds.size()); - assertTrue(registeredModuleIds.contains(mod1.getTypeId())); - assertTrue(registeredModuleIds.contains(mod2.getTypeId())); + List mods = new ArrayList<>(mapper.getRegisteredModules()); + assertEquals(2, mods.size()); + // Should retain ordering even if not mandated + assertEquals("test1", mods.get(0).getModuleName()); + assertEquals("test2", mods.get(1).getModuleName()); } /* @@ -338,8 +322,9 @@ public void testMixIns() throws Exception { SimpleModule module = new SimpleModule("test", Version.unknownVersion()); module.setMixInAnnotation(MixableBean.class, MixInForOrder.class); - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(module); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(module) + .build(); Map props = this.writeAndMap(mapper, new MixableBean()); assertEquals(3, props.size()); assertEquals(Integer.valueOf(3), props.get("c")); @@ -349,24 +334,32 @@ public void testMixIns() throws Exception public void testAccessToMapper() throws Exception { - ContextVerifierModule module = new ContextVerifierModule(); - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(module); - } - - // [databind#626] - public void testMixIns626() throws Exception - { - ObjectMapper mapper = new ObjectMapper(); - // no real annotations, but nominally add ones from 'String' to 'Object', just for testing - mapper.registerModule(new TestModule626(Object.class, String.class)); - Class found = mapper.findMixInClassFor(Object.class); - assertEquals(String.class, found); + com.fasterxml.jackson.databind.Module module = new com.fasterxml.jackson.databind.Module() + { + @Override + public String getModuleName() { return "x"; } + + @Override + public Version version() { return Version.unknownVersion(); } + + @Override + public void setupModule(SetupContext context) + { + Object c = context.getOwner(); + if (!(c instanceof MapperBuilder)) { + throw new RuntimeException("Owner should be a `MapperBuilder` but is not; is: "+c); + } + } + }; + ObjectMapper mapper = jsonMapperBuilder() + .addModule(module) + .build(); + assertNotNull(mapper); } public void testAutoDiscovery() throws Exception { - List mods = ObjectMapper.findModules(); + List mods = MapperBuilder.findModules(); assertEquals(0, mods.size()); } } diff --git a/src/test/java/com/fasterxml/jackson/databind/module/TestAbstractTypes.java b/src/test/java/com/fasterxml/jackson/databind/module/TestAbstractTypes.java index cc55d31563..8c15160c1d 100644 --- a/src/test/java/com/fasterxml/jackson/databind/module/TestAbstractTypes.java +++ b/src/test/java/com/fasterxml/jackson/databind/module/TestAbstractTypes.java @@ -86,23 +86,25 @@ public String getValue() { public void testCollectionDefaulting() throws Exception { - ObjectMapper mapper = new ObjectMapper(); SimpleModule mod = new SimpleModule("test", Version.unknownVersion()); // let's ensure we get hierarchic mapping mod.addAbstractTypeMapping(Collection.class, List.class); mod.addAbstractTypeMapping(List.class, LinkedList.class); - mapper.registerModule(mod); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(mod) + .build(); Collection result = mapper.readValue("[]", Collection.class); assertEquals(LinkedList.class, result.getClass()); } public void testMapDefaultingBasic() throws Exception { - ObjectMapper mapper = new ObjectMapper(); SimpleModule mod = new SimpleModule("test", Version.unknownVersion()); // default is HashMap, so: mod.addAbstractTypeMapping(Map.class, TreeMap.class); - mapper.registerModule(mod); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(mod) + .build(); Map result = mapper.readValue("{}", Map.class); assertEquals(TreeMap.class, result.getClass()); } @@ -110,14 +112,15 @@ public void testMapDefaultingBasic() throws Exception // [databind#700] public void testDefaultingRecursive() throws Exception { - ObjectMapper mapper = new ObjectMapper(); SimpleModule mod = new SimpleModule("test", Version.unknownVersion()); // defaults: LinkedHashMap, ArrayList mod.addAbstractTypeMapping(Map.class, TreeMap.class); mod.addAbstractTypeMapping(List.class, LinkedList.class); - mapper.registerModule(mod); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(mod) + .build(); Object result; result = mapper.readValue("[ {} ]", Object.class); @@ -138,11 +141,12 @@ public void testDefaultingRecursive() throws Exception public void testInterfaceDefaulting() throws Exception { - ObjectMapper mapper = new ObjectMapper(); SimpleModule mod = new SimpleModule("test", Version.unknownVersion()); // let's ensure we get hierarchic mapping mod.addAbstractTypeMapping(CharSequence.class, MyString.class); - mapper.registerModule(mod); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(mod) + .build(); Object result = mapper.readValue(quote("abc"), CharSequence.class); assertEquals(MyString.class, result.getClass()); assertEquals("abc", ((MyString) result).value); @@ -150,8 +154,9 @@ public void testInterfaceDefaulting() throws Exception // and ditto for POJOs mod = new SimpleModule(); mod.addAbstractTypeMapping(Abstract.class, AbstractImpl.class); - mapper = new ObjectMapper() - .registerModule(mod); + mapper = jsonMapperBuilder() + .addModule(mod) + .build(); Abstract a = mapper.readValue("{}", Abstract.class); assertNotNull(a); } @@ -159,13 +164,15 @@ public void testInterfaceDefaulting() throws Exception // [databind#2019]: mappings from multiple modules public void testAbstractMappingsFromTwoModules() throws Exception { - ObjectMapper mapper = newObjectMapper(); SimpleModule module1 = new SimpleModule("module1"); module1.addAbstractTypeMapping(Datatype1.class, SimpleDatatype1.class); SimpleModule module2 = new SimpleModule("module2"); module2.addAbstractTypeMapping(Datatype2.class, SimpleDatatype2.class); - mapper.registerModules(module1, module2); + + ObjectMapper mapper = jsonMapperBuilder() + .addModules(module1, module2) + .build(); final String JSON_EXAMPLE = "{\"value\": \"aaa\"}"; Datatype1 value1 = mapper.readValue(JSON_EXAMPLE, Datatype1.class); diff --git a/src/test/java/com/fasterxml/jackson/databind/module/TestCustomEnumKeyDeserializer.java b/src/test/java/com/fasterxml/jackson/databind/module/TestCustomEnumKeyDeserializer.java index bc5a53c56e..d27f09c03a 100644 --- a/src/test/java/com/fasterxml/jackson/databind/module/TestCustomEnumKeyDeserializer.java +++ b/src/test/java/com/fasterxml/jackson/databind/module/TestCustomEnumKeyDeserializer.java @@ -142,7 +142,7 @@ public TestEnumModule() { @Override public void setupModule(SetupContext context) { - context.setMixInAnnotations(TestEnum.class, TestEnumMixin.class); + context.setMixIn(TestEnum.class, TestEnumMixin.class); SimpleSerializers keySerializers = new SimpleSerializers(); keySerializers.addSerializer(new TestEnumKeySerializer()); context.addKeySerializers(keySerializers); @@ -171,9 +171,9 @@ public void testWithEnumKeys() throws Exception { ObjectMapper plainObjectMapper = new ObjectMapper(); JsonNode tree = plainObjectMapper.readTree(aposToQuotes("{'red' : [ 'a', 'b']}")); - ObjectMapper fancyObjectMapper = new ObjectMapper().registerModule(new TestEnumModule()); - - // this line is might throw with Jackson 2.6.2. + ObjectMapper fancyObjectMapper = jsonMapperBuilder() + .addModule(new TestEnumModule()) + .build(); Map> map = fancyObjectMapper.convertValue(tree, new TypeReference>>() { } ); assertNotNull(map); @@ -184,8 +184,9 @@ public void testWithEnumKeys() throws Exception { // public void testWithTree749() throws Exception public void withTree749() throws Exception { - ObjectMapper mapper = new ObjectMapper().registerModule(new TestEnumModule()); - + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new TestEnumModule()) + .build(); Map inputMap = new LinkedHashMap(); Map> replacements = new LinkedHashMap>(); Map reps = new LinkedHashMap(); @@ -213,8 +214,9 @@ public SuperTypeEnum deserialize(JsonParser p, DeserializationContext deserializ return SuperTypeEnum.valueOf(p.getText()); } }); - ObjectMapper mapper = new ObjectMapper() - .registerModule(simpleModule); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(simpleModule) + .build(); SuperType superType = mapper.readValue("{\"someMap\": {\"FOO\": \"bar\"}}", SuperType.class); @@ -260,8 +262,9 @@ public Object deserializeKey(String key, DeserializationContext ctxt) }; } }); - ObjectMapper mapper = new ObjectMapper() - .registerModule(module); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(module) + .build(); // First, enum value as is KeyEnum key = mapper.readValue(quote(KeyEnum.replacements.name().toUpperCase()), diff --git a/src/test/java/com/fasterxml/jackson/databind/module/TestDuplicateRegistration.java b/src/test/java/com/fasterxml/jackson/databind/module/TestDuplicateRegistration.java index 454156d7aa..79cbc34188 100644 --- a/src/test/java/com/fasterxml/jackson/databind/module/TestDuplicateRegistration.java +++ b/src/test/java/com/fasterxml/jackson/databind/module/TestDuplicateRegistration.java @@ -1,15 +1,25 @@ package com.fasterxml.jackson.databind.module; +import java.util.concurrent.atomic.AtomicInteger; + import com.fasterxml.jackson.core.Version; import com.fasterxml.jackson.databind.*; public class TestDuplicateRegistration extends BaseMapTest { static class MyModule extends com.fasterxml.jackson.databind.Module { - public int regCount; - - public MyModule() { + private final AtomicInteger counter; + private final Object id; + + public MyModule(AtomicInteger c, Object id) { super(); + counter = c; + this.id = id; + } + + @Override + public Object getRegistrationId() { + return id; } @Override @@ -24,36 +34,28 @@ public Version version() { @Override public void setupModule(SetupContext context) { - ++regCount; + counter.addAndGet(1); } } - @SuppressWarnings("deprecation") public void testDuplicateRegistration() throws Exception { // by default, duplicate registration should be prevented - ObjectMapper mapper = new ObjectMapper(); - assertTrue(mapper.isEnabled(MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS)); - MyModule module = new MyModule(); - mapper.registerModule(module); - mapper.registerModule(module); - mapper.registerModule(module); - assertEquals(1, module.regCount); - - // but may be allowed by changing setting - mapper.disable(MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS); - mapper.registerModule(module); - assertEquals(2, module.regCount); - - final MyModule module2 = new MyModule(); - // and ditto for a new instance - @SuppressWarnings("unused") - ObjectMapper mapper2 = objectMapperBuilder() - .disable(MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS) - .addModule(module2) - .addModule(module2) - .addModule(module2) + AtomicInteger counter = new AtomicInteger(); + /*ObjectMapper mapper =*/ jsonMapperBuilder() + .addModule(new MyModule(counter, "id")) + .addModule(new MyModule(counter, "id")) + .addModule(new MyModule(counter, "id")) + .build(); + assertEquals(1, counter.get()); + + // but may be allowed by using non-identical id + AtomicInteger counter2 = new AtomicInteger(); + /*ObjectMapper mapper2 =*/ jsonMapperBuilder() + .addModule(new MyModule(counter2, "id1")) + .addModule(new MyModule(counter2, "id2")) + .addModule(new MyModule(counter2, "id3")) .build(); - assertEquals(3, module2.regCount); + assertEquals(3, counter2.get()); } } diff --git a/src/test/java/com/fasterxml/jackson/databind/module/TestKeyDeserializers.java b/src/test/java/com/fasterxml/jackson/databind/module/TestKeyDeserializers.java index a9bd0d89f0..bea38db85c 100644 --- a/src/test/java/com/fasterxml/jackson/databind/module/TestKeyDeserializers.java +++ b/src/test/java/com/fasterxml/jackson/databind/module/TestKeyDeserializers.java @@ -31,10 +31,11 @@ static class Foo { public void testKeyDeserializers() throws Exception { - ObjectMapper mapper = new ObjectMapper(); SimpleModule mod = new SimpleModule("test", Version.unknownVersion()); mod.addKeyDeserializer(Foo.class, new FooKeyDeserializer()); - mapper.registerModule(mod); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(mod) + .build(); Map map = mapper.readValue("{\"a\":3}", new TypeReference>() {} ); assertNotNull(map); 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 cdd89ea076..b8a69414cc 100644 --- a/src/test/java/com/fasterxml/jackson/databind/module/TestTypeModifierNameResolution.java +++ b/src/test/java/com/fasterxml/jackson/databind/module/TestTypeModifierNameResolution.java @@ -11,49 +11,53 @@ public class TestTypeModifierNameResolution extends BaseMapTest { - interface MyType { - String getData(); - void setData(String data); - } - - static class MyTypeImpl implements MyType { - private String data; - - @Override - public String getData() { - return data; - } - - @Override - public void setData(String data) { - this.data = data; - } - } - - static class CustomTypeModifier extends TypeModifier { - @Override - public JavaType modifyType(JavaType type, Type jdkType, TypeBindings context, TypeFactory typeFactory) { - if (type.getRawClass().equals(MyTypeImpl.class)) { - return typeFactory.constructType(MyType.class); - } - return type; - } - } - - @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.WRAPPER_OBJECT) - public interface Mixin { } - - // Expect that the TypeModifier kicks in when the type id is written. - public void testTypeModiferNameResolution() throws Exception - { - ObjectMapper mapper = new ObjectMapper(); - mapper.setTypeFactory(mapper.getTypeFactory().withModifier(new CustomTypeModifier())); - mapper.addMixIn(MyType.class, Mixin.class); - - MyType obj = new MyTypeImpl(); - obj.setData("something"); - - String s = mapper.writer().writeValueAsString(obj); - assertTrue(s.startsWith("{\"TestTypeModifierNameResolution$MyType\":")); - } -} \ No newline at end of file + interface MyType { + String getData(); + void setData(String data); + } + + static class MyTypeImpl implements MyType { + private String data; + + @Override + public String getData() { + return data; + } + + @Override + public void setData(String data) { + this.data = data; + } + } + + private static class CustomTypeModifier extends TypeModifier { + @Override + public JavaType modifyType(JavaType type, Type jdkType, TypeBindings context, TypeFactory typeFactory) { + if (type.hasRawClass(MyTypeImpl.class)) { + return typeFactory.constructType(MyType.class); + } + return type; + } + } + + @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.WRAPPER_OBJECT) + public interface Mixin { } + + // Expect that the TypeModifier kicks in when the type id is written. + public void testTypeModiferNameResolution() throws Exception + { + final ObjectMapper mapper = jsonMapperBuilder() + .typeFactory(TypeFactory.defaultInstance().withModifier(new CustomTypeModifier())) + .addMixIn(MyType.class, Mixin.class) + .build(); + + MyType obj = new MyTypeImpl(); + obj.setData("something"); + + String s = mapper.writer().writeValueAsString(obj); + final String EXP = "{\"TestTypeModifierNameResolution$MyType\":"; + if (!s.startsWith(EXP)) { + fail("Should start with ["+EXP+"], does not ["+s+"]"); + } + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/module/TestTypeModifiers.java b/src/test/java/com/fasterxml/jackson/databind/module/TestTypeModifiers.java index 27ac41e97f..c260191f61 100644 --- a/src/test/java/com/fasterxml/jackson/databind/module/TestTypeModifiers.java +++ b/src/test/java/com/fasterxml/jackson/databind/module/TestTypeModifiers.java @@ -3,6 +3,7 @@ import java.io.IOException; import java.lang.reflect.Type; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JsonSerialize; @@ -11,18 +12,13 @@ import com.fasterxml.jackson.databind.module.SimpleDeserializers; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.Serializers; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; import com.fasterxml.jackson.databind.type.*; @SuppressWarnings("serial") public class TestTypeModifiers extends BaseMapTest { - /* - /********************************************************** - /* Helper classes - /********************************************************** - */ - - static class ModifierModule extends SimpleModule + private static class ModifierModule extends SimpleModule { public ModifierModule() { super("test", Version.unknownVersion()); @@ -34,7 +30,7 @@ public void setupModule(SetupContext context) context.addSerializers(new Serializers.Base() { @Override public JsonSerializer findMapLikeSerializer(SerializationConfig config, - MapLikeType type, BeanDescription beanDesc, + MapLikeType type, BeanDescription beanDesc, JsonFormat.Value format, JsonSerializer keySerializer, TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) { @@ -46,7 +42,7 @@ public JsonSerializer findMapLikeSerializer(SerializationConfig config, @Override public JsonSerializer findCollectionLikeSerializer(SerializationConfig config, - CollectionLikeType type, BeanDescription beanDesc, + CollectionLikeType type, BeanDescription beanDesc, JsonFormat.Value format, TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) { if (CollectionMarker.class.isAssignableFrom(type.getRawClass())) { @@ -81,8 +77,9 @@ public JsonDeserializer findMapLikeDeserializer(MapLikeType type, Deserializa } } - static class XxxSerializer extends JsonSerializer + static class XxxSerializer extends StdSerializer { + public XxxSerializer() { super(Object.class); } @Override public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException { jgen.writeString("xxx:"+value); @@ -127,12 +124,13 @@ public MyCollectionLikeType(int v) { public Integer getValue() { return value; } } - static class MyMapSerializer extends JsonSerializer> + static class MyMapSerializer extends StdSerializer> { protected final JsonSerializer _keySerializer; protected final JsonSerializer _valueSerializer; - + public MyMapSerializer(JsonSerializer keySer, JsonSerializer valueSer) { + super(MapMarker.class); _keySerializer = keySer; _valueSerializer = valueSer; } @@ -156,39 +154,40 @@ public void serialize(MapMarker value, JsonGenerator jgen, SerializerProvid static class MyMapDeserializer extends JsonDeserializer> { @Override - public MapMarker deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { - if (jp.getCurrentToken() != JsonToken.START_OBJECT) throw new IOException("Wrong token: "+jp.getCurrentToken()); - if (jp.nextToken() != JsonToken.FIELD_NAME) throw new IOException("Wrong token: "+jp.getCurrentToken()); - String key = jp.getCurrentName(); - if (jp.nextToken() != JsonToken.VALUE_NUMBER_INT) throw new IOException("Wrong token: "+jp.getCurrentToken()); - int value = jp.getIntValue(); - if (jp.nextToken() != JsonToken.END_OBJECT) throw new IOException("Wrong token: "+jp.getCurrentToken()); + public MapMarker deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + if (p.currentToken() != JsonToken.START_OBJECT) throw new IOException("Wrong token: "+p.currentToken()); + if (p.nextToken() != JsonToken.FIELD_NAME) throw new IOException("Wrong token: "+p.currentToken()); + String key = p.currentName(); + if (p.nextToken() != JsonToken.VALUE_NUMBER_INT) throw new IOException("Wrong token: "+p.currentToken()); + int value = p.getIntValue(); + if (p.nextToken() != JsonToken.END_OBJECT) throw new IOException("Wrong token: "+p.currentToken()); return new MyMapLikeType(key, value); } } - static class MyCollectionSerializer extends JsonSerializer + static class MyCollectionSerializer extends StdSerializer { + public MyCollectionSerializer() { super(MyCollectionLikeType.class); } @Override - public void serialize(MyCollectionLikeType value, JsonGenerator jgen, SerializerProvider provider) throws IOException { - jgen.writeStartArray(); - jgen.writeNumber(value.value); - jgen.writeEndArray(); + public void serialize(MyCollectionLikeType value, JsonGenerator g, SerializerProvider provider) throws IOException { + g.writeStartArray(); + g.writeNumber(value.value); + g.writeEndArray(); } } static class MyCollectionDeserializer extends JsonDeserializer { @Override - public MyCollectionLikeType deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { - if (jp.getCurrentToken() != JsonToken.START_ARRAY) throw new IOException("Wrong token: "+jp.getCurrentToken()); - if (jp.nextToken() != JsonToken.VALUE_NUMBER_INT) throw new IOException("Wrong token: "+jp.getCurrentToken()); - int value = jp.getIntValue(); - if (jp.nextToken() != JsonToken.END_ARRAY) throw new IOException("Wrong token: "+jp.getCurrentToken()); + public MyCollectionLikeType deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + if (p.currentToken() != JsonToken.START_ARRAY) throw new IOException("Wrong token: "+p.currentToken()); + if (p.nextToken() != JsonToken.VALUE_NUMBER_INT) throw new IOException("Wrong token: "+p.currentToken()); + int value = p.getIntValue(); + if (p.nextToken() != JsonToken.END_ARRAY) throw new IOException("Wrong token: "+p.currentToken()); return new MyCollectionLikeType(value); } } - static class MyTypeModifier extends TypeModifier + private static class MyTypeModifier extends TypeModifier { @Override public JavaType modifyType(JavaType type, Type jdkType, TypeBindings bindings, TypeFactory typeFactory) @@ -212,15 +211,21 @@ public JavaType modifyType(JavaType type, Type jdkType, TypeBindings bindings, T /********************************************************** */ + private final ObjectMapper MY_TYPE_MAPPER = jsonMapperBuilder() + .typeFactory(TypeFactory.defaultInstance().withModifier(new MyTypeModifier())) + .build(); + + private final ObjectMapper MAPPER_WITH_MODIFIER = jsonMapperBuilder() + .typeFactory(TypeFactory.defaultInstance().withModifier(new MyTypeModifier())) + .addModule(new ModifierModule()) + .build(); + /** * Basic test for ensuring that we can get "xxx-like" types recognized. */ public void testMapLikeTypeConstruction() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.setTypeFactory(mapper.getTypeFactory().withModifier(new MyTypeModifier())); - - JavaType type = mapper.constructType(MyMapLikeType.class); + JavaType type = MY_TYPE_MAPPER.constructType(MyMapLikeType.class); assertTrue(type.isMapLikeType()); // also, must have resolved type info JavaType param = ((MapLikeType) type).getKeyType(); @@ -233,10 +238,7 @@ public void testMapLikeTypeConstruction() throws Exception public void testCollectionLikeTypeConstruction() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.setTypeFactory(mapper.getTypeFactory().withModifier(new MyTypeModifier())); - - JavaType type = mapper.constructType(MyCollectionLikeType.class); + JavaType type = MY_TYPE_MAPPER.constructType(MyCollectionLikeType.class); assertTrue(type.isCollectionLikeType()); JavaType param = ((CollectionLikeType) type).getContentType(); assertNotNull(param); @@ -245,40 +247,25 @@ public void testCollectionLikeTypeConstruction() throws Exception public void testCollectionLikeSerialization() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.setTypeFactory(mapper.getTypeFactory().withModifier(new MyTypeModifier())); - mapper.registerModule(new ModifierModule()); - assertEquals("[19]", mapper.writeValueAsString(new MyCollectionLikeType(19))); + assertEquals("[19]", MAPPER_WITH_MODIFIER.writeValueAsString(new MyCollectionLikeType(19))); } public void testMapLikeSerialization() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.setTypeFactory(mapper.getTypeFactory().withModifier(new MyTypeModifier())); - mapper.registerModule(new ModifierModule()); // Due to custom serializer, should get: - assertEquals("{\"x\":\"xxx:3\"}", mapper.writeValueAsString(new MyMapLikeType("x", 3))); + assertEquals("{\"x\":\"xxx:3\"}", MAPPER_WITH_MODIFIER.writeValueAsString(new MyMapLikeType("x", 3))); } - public void testCollectionLikeDeserialization() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.setTypeFactory(mapper.getTypeFactory().withModifier(new MyTypeModifier())); - mapper.registerModule(new ModifierModule()); - // !!! TBI - MyMapLikeType result = mapper.readValue("{\"a\":13}", MyMapLikeType.class); + MyMapLikeType result = MAPPER_WITH_MODIFIER.readValue("{\"a\":13}", MyMapLikeType.class); assertEquals("a", result.getKey()); assertEquals(Integer.valueOf(13), result.getValue()); } public void testMapLikeDeserialization() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.setTypeFactory(mapper.getTypeFactory().withModifier(new MyTypeModifier())); - mapper.registerModule(new ModifierModule()); - // !!! TBI - MyCollectionLikeType result = mapper.readValue("[-37]", MyCollectionLikeType.class); + MyCollectionLikeType result = MAPPER_WITH_MODIFIER.readValue("[-37]", MyCollectionLikeType.class); assertEquals(Integer.valueOf(-37), result.getValue()); } } diff --git a/src/test/java/com/fasterxml/jackson/databind/node/ArrayNodeTest.java b/src/test/java/com/fasterxml/jackson/databind/node/ArrayNodeTest.java index 718c04b02b..4a61684469 100644 --- a/src/test/java/com/fasterxml/jackson/databind/node/ArrayNodeTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/node/ArrayNodeTest.java @@ -239,8 +239,7 @@ public void testParser() throws Exception ArrayNode n = new ArrayNode(JsonNodeFactory.instance); n.add(123); TreeTraversingParser p = new TreeTraversingParser(n, null); - p.setCodec(null); - assertNull(p.getCodec()); + assertNull(p.getObjectReadContext()); assertNotNull(p.getParsingContext()); assertNotNull(p.getTokenLocation()); assertNotNull(p.getCurrentLocation()); @@ -251,7 +250,7 @@ public void testParser() throws Exception assertToken(JsonToken.START_ARRAY, p.nextToken()); p.skipChildren(); - assertToken(JsonToken.END_ARRAY, p.getCurrentToken()); + assertToken(JsonToken.END_ARRAY, p.currentToken()); p.close(); p = new TreeTraversingParser(n, null); diff --git a/src/test/java/com/fasterxml/jackson/databind/node/EmptyContentAsTreeTest.java b/src/test/java/com/fasterxml/jackson/databind/node/EmptyContentAsTreeTest.java index 020ced6182..22f6acf803 100644 --- a/src/test/java/com/fasterxml/jackson/databind/node/EmptyContentAsTreeTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/node/EmptyContentAsTreeTest.java @@ -25,32 +25,32 @@ public class EmptyContentAsTreeTest extends BaseMapTest public void testNullFromEOFWithParserAndMapper() throws Exception { - try (JsonParser p = MAPPER.getFactory().createParser(EMPTY0)) { + try (JsonParser p = MAPPER.createParser(EMPTY0)) { _assertNullTree(MAPPER.readTree(p)); } - try (JsonParser p = MAPPER.getFactory().createParser(EMPTY1)) { + try (JsonParser p = MAPPER.createParser(EMPTY1)) { _assertNullTree(MAPPER.readTree(p)); } - try (JsonParser p = MAPPER.getFactory().createParser(new StringReader(EMPTY0))) { + try (JsonParser p = MAPPER.createParser(new StringReader(EMPTY0))) { _assertNullTree(MAPPER.readTree(p)); } - try (JsonParser p = MAPPER.getFactory().createParser(new StringReader(EMPTY1))) { + try (JsonParser p = MAPPER.createParser(new StringReader(EMPTY1))) { _assertNullTree(MAPPER.readTree(p)); } - try (JsonParser p = MAPPER.getFactory().createParser(EMPTY0_BYTES)) { + try (JsonParser p = MAPPER.createParser(EMPTY0_BYTES)) { _assertNullTree(MAPPER.readTree(p)); } - try (JsonParser p = MAPPER.getFactory().createParser(EMPTY1_BYTES)) { + try (JsonParser p = MAPPER.createParser(EMPTY1_BYTES)) { _assertNullTree(MAPPER.readTree(p)); } - try (JsonParser p = MAPPER.getFactory().createParser(EMPTY1_BYTES, 0, EMPTY1_BYTES.length)) { + try (JsonParser p = MAPPER.createParser(EMPTY1_BYTES, 0, EMPTY1_BYTES.length)) { _assertNullTree(MAPPER.readTree(p)); } - try (JsonParser p = MAPPER.getFactory().createParser(new ByteArrayInputStream(EMPTY0_BYTES))) { + try (JsonParser p = MAPPER.createParser(new ByteArrayInputStream(EMPTY0_BYTES))) { _assertNullTree(MAPPER.readTree(p)); } - try (JsonParser p = MAPPER.getFactory().createParser(new ByteArrayInputStream(EMPTY1_BYTES))) { + try (JsonParser p = MAPPER.createParser(new ByteArrayInputStream(EMPTY1_BYTES))) { _assertNullTree(MAPPER.readTree(p)); } } @@ -58,33 +58,33 @@ public void testNullFromEOFWithParserAndMapper() throws Exception // [databind#1406] public void testNullFromEOFWithParserAndReader() throws Exception { - try (JsonParser p = MAPPER.getFactory().createParser(EMPTY0)) { + try (JsonParser p = MAPPER.createParser(EMPTY0)) { _assertNullTree(MAPPER.reader().readTree(p)); } - try (JsonParser p = MAPPER.getFactory().createParser(EMPTY1)) { + try (JsonParser p = MAPPER.createParser(EMPTY1)) { _assertNullTree(MAPPER.reader().readTree(p)); } - try (JsonParser p = MAPPER.getFactory().createParser(new StringReader(EMPTY0))) { + try (JsonParser p = MAPPER.createParser(new StringReader(EMPTY0))) { _assertNullTree(MAPPER.reader().readTree(p)); } - try (JsonParser p = MAPPER.getFactory().createParser(new StringReader(EMPTY1))) { + try (JsonParser p = MAPPER.createParser(new StringReader(EMPTY1))) { _assertNullTree(MAPPER.reader().readTree(p)); } - try (JsonParser p = MAPPER.getFactory().createParser(EMPTY0_BYTES)) { + try (JsonParser p = MAPPER.createParser(EMPTY0_BYTES)) { _assertNullTree(MAPPER.reader().readTree(p)); } - try (JsonParser p = MAPPER.getFactory().createParser(EMPTY1_BYTES)) { + try (JsonParser p = MAPPER.createParser(EMPTY1_BYTES)) { _assertNullTree(MAPPER.reader().readTree(p)); } - try (JsonParser p = MAPPER.getFactory().createParser(EMPTY1_BYTES, 0, EMPTY1_BYTES.length)) { + try (JsonParser p = MAPPER.createParser(EMPTY1_BYTES, 0, EMPTY1_BYTES.length)) { _assertNullTree(MAPPER.reader().readTree(p)); } - try (JsonParser p = MAPPER.getFactory().createParser(new ByteArrayInputStream(EMPTY0_BYTES))) { + try (JsonParser p = MAPPER.createParser(new ByteArrayInputStream(EMPTY0_BYTES))) { _assertNullTree(MAPPER.reader().readTree(p)); } - try (JsonParser p = MAPPER.getFactory().createParser(new ByteArrayInputStream(EMPTY1_BYTES))) { + try (JsonParser p = MAPPER.createParser(new ByteArrayInputStream(EMPTY1_BYTES))) { _assertNullTree(MAPPER.reader().readTree(p)); } } diff --git a/src/test/java/com/fasterxml/jackson/databind/node/NotANumberConversionTest.java b/src/test/java/com/fasterxml/jackson/databind/node/NotANumberConversionTest.java index 3fcb71dbae..f0a8efa874 100644 --- a/src/test/java/com/fasterxml/jackson/databind/node/NotANumberConversionTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/node/NotANumberConversionTest.java @@ -6,10 +6,9 @@ public class NotANumberConversionTest extends BaseMapTest { - private final ObjectMapper m = new ObjectMapper(); - { - m.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS); - } + private final ObjectMapper m = jsonMapperBuilder() + .enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS) + .build(); public void testBigDecimalWithNaN() throws Exception { diff --git a/src/test/java/com/fasterxml/jackson/databind/node/NumberNodesTest.java b/src/test/java/com/fasterxml/jackson/databind/node/NumberNodesTest.java index f12cb94846..a168cd3889 100644 --- a/src/test/java/com/fasterxml/jackson/databind/node/NumberNodesTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/node/NumberNodesTest.java @@ -3,9 +3,10 @@ import java.math.BigDecimal; import java.math.BigInteger; -import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.core.StreamWriteFeature; +import com.fasterxml.jackson.core.json.JsonFactory; import com.fasterxml.jackson.databind.*; /** @@ -363,9 +364,11 @@ public void testBigIntegerNode() throws Exception public void testBigDecimalAsPlain() throws Exception { - ObjectMapper mapper = new ObjectMapper() + ObjectMapper mapper = jsonMapperBuilder(JsonFactory.builder() + .enable(StreamWriteFeature.WRITE_BIGDECIMAL_AS_PLAIN) + .build()) .enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS) - .enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN); + .build(); final String INPUT = "{\"x\":1e2}"; final JsonNode node = mapper.readTree(INPUT); String result = mapper.writeValueAsString(node); diff --git a/src/test/java/com/fasterxml/jackson/databind/node/POJONodeTest.java b/src/test/java/com/fasterxml/jackson/databind/node/POJONodeTest.java index 638cb4302d..efff03e635 100644 --- a/src/test/java/com/fasterxml/jackson/databind/node/POJONodeTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/node/POJONodeTest.java @@ -31,7 +31,7 @@ public void serialize(Data value, JsonGenerator gen, SerializerProvider provider } } - final ObjectMapper MAPPER = newObjectMapper(); + final ObjectMapper MAPPER = newJsonMapper(); public void testPOJONodeCustomSer() throws Exception { 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 e22082d58c..5c647dd13e 100644 --- a/src/test/java/com/fasterxml/jackson/databind/node/TestConversions.java +++ b/src/test/java/com/fasterxml/jackson/databind/node/TestConversions.java @@ -4,12 +4,11 @@ import java.math.BigDecimal; import java.util.*; -import static org.junit.Assert.*; - import org.junit.Assert; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.json.JsonFactory; import com.fasterxml.jackson.core.type.WritableTypeId; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; @@ -52,17 +51,17 @@ static class Issue467Tree { static class Issue467Serializer extends JsonSerializer { @Override - public void serialize(Issue467Bean value, JsonGenerator jgen, + public void serialize(Issue467Bean value, JsonGenerator g, SerializerProvider provider) throws IOException { - jgen.writeObject(new Issue467TmpBean(value.i)); + g.writeObject(new Issue467TmpBean(value.i)); } } static class Issue467TreeSerializer extends JsonSerializer { @Override - public void serialize(Issue467Tree value, JsonGenerator jgen, + public void serialize(Issue467Tree value, JsonGenerator g, SerializerProvider provider) throws IOException { - jgen.writeTree(BooleanNode.TRUE); + g.writeTree(BooleanNode.TRUE); } } @@ -132,8 +131,9 @@ public Leaf deserialize(JsonParser jp, DeserializationContext ctxt) public void testTreeToValue() throws Exception { String JSON = "{\"leaf\":{\"value\":13}}"; - ObjectMapper mapper = new ObjectMapper(); - mapper.addMixIn(Leaf.class, LeafMixIn.class); + ObjectMapper mapper = jsonMapperBuilder() + .addMixIn(Leaf.class, LeafMixIn.class) + .build(); JsonNode root = mapper.readTree(JSON); // Ok, try converting to bean using two mechanisms Root r1 = mapper.treeToValue(root, Root.class); @@ -217,7 +217,7 @@ public void testIssue709() throws Exception public void testEmbeddedByteArray() throws Exception { - TokenBuffer buf = new TokenBuffer(MAPPER, false); + TokenBuffer buf = TokenBuffer.forGeneration(); buf.writeObject(new byte[3]); JsonNode node = MAPPER.readTree(buf.asParser()); buf.close(); @@ -230,8 +230,9 @@ public void testEmbeddedByteArray() throws Exception // [databind#232] public void testBigDecimalAsPlainStringTreeConversion() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN); + ObjectMapper mapper = new ObjectMapper(JsonFactory.builder() + .enable(StreamWriteFeature.WRITE_BIGDECIMAL_AS_PLAIN) + .build()); Map map = new HashMap(); String PI_STR = "3.00000000"; map.put("pi", new BigDecimal(PI_STR)); @@ -248,11 +249,11 @@ static class CustomSerializedPojo implements JsonSerializable public void setFoo(final String foo) { node.put("foo", foo); } - + @Override - public void serialize(final JsonGenerator jgen, final SerializerProvider provider) throws IOException + public void serialize(final JsonGenerator g, final SerializerProvider provider) throws IOException { - jgen.writeTree(node); + g.writeTree(node); } @Override diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TestJsonNode.java b/src/test/java/com/fasterxml/jackson/databind/node/TestJsonNode.java index 1741d4b7a5..ecf788cd59 100644 --- a/src/test/java/com/fasterxml/jackson/databind/node/TestJsonNode.java +++ b/src/test/java/com/fasterxml/jackson/databind/node/TestJsonNode.java @@ -176,13 +176,12 @@ public int compare(JsonNode o1, JsonNode o2) { // [databind#793] public void testArrayWithDefaultTyping() throws Exception { - ObjectMapper mapper = new ObjectMapper() - .enableDefaultTyping(); - + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping() + .build(); JsonNode array = mapper.readTree("[ 1, 2 ]"); assertTrue(array.isArray()); assertEquals(2, array.size()); - JsonNode obj = mapper.readTree("{ \"a\" : 2 }"); assertTrue(obj.isObject()); assertEquals(1, obj.size()); diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TestTreeTraversingParser.java b/src/test/java/com/fasterxml/jackson/databind/node/TestTreeTraversingParser.java index a4170d4ccd..4a1bae765e 100644 --- a/src/test/java/com/fasterxml/jackson/databind/node/TestTreeTraversingParser.java +++ b/src/test/java/com/fasterxml/jackson/databind/node/TestTreeTraversingParser.java @@ -4,8 +4,6 @@ import java.math.BigInteger; import java.util.*; -import static org.junit.Assert.*; - import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.core.*; @@ -38,7 +36,7 @@ public static class Inner { /********************************************************** */ - private final ObjectMapper MAPPER = newObjectMapper(); + private final ObjectMapper MAPPER = newJsonMapper(); public void testSimple() throws Exception { @@ -46,60 +44,60 @@ public void testSimple() throws Exception final String JSON = "{ \"a\" : 123, \"list\" : [ 12.25, null, true, { }, [ ] ] }"; JsonNode tree = MAPPER.readTree(JSON); - JsonParser p = tree.traverse(); + JsonParser p = tree.traverse(ObjectReadContext.empty()); - assertNull(p.getCurrentToken()); - assertNull(p.getCurrentName()); + assertNull(p.currentToken()); + assertNull(p.currentName()); assertToken(JsonToken.START_OBJECT, p.nextToken()); - assertNull(p.getCurrentName()); + assertNull(p.currentName()); assertEquals("Expected START_OBJECT", JsonToken.START_OBJECT.asString(), p.getText()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); - assertEquals("a", p.getCurrentName()); + assertEquals("a", p.currentName()); assertEquals("a", p.getText()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); - assertEquals("a", p.getCurrentName()); + assertEquals("a", p.currentName()); assertEquals(123, p.getIntValue()); assertEquals("123", p.getText()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); - assertEquals("list", p.getCurrentName()); + assertEquals("list", p.currentName()); assertEquals("list", p.getText()); assertToken(JsonToken.START_ARRAY, p.nextToken()); - assertEquals("list", p.getCurrentName()); + assertEquals("list", p.currentName()); assertEquals(JsonToken.START_ARRAY.asString(), p.getText()); assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); - assertNull(p.getCurrentName()); + assertNull(p.currentName()); assertEquals(12.25, p.getDoubleValue(), 0); assertEquals("12.25", p.getText()); assertToken(JsonToken.VALUE_NULL, p.nextToken()); - assertNull(p.getCurrentName()); + assertNull(p.currentName()); assertEquals(JsonToken.VALUE_NULL.asString(), p.getText()); assertToken(JsonToken.VALUE_TRUE, p.nextToken()); - assertNull(p.getCurrentName()); + assertNull(p.currentName()); assertTrue(p.getBooleanValue()); assertEquals(JsonToken.VALUE_TRUE.asString(), p.getText()); assertToken(JsonToken.START_OBJECT, p.nextToken()); - assertNull(p.getCurrentName()); + assertNull(p.currentName()); assertToken(JsonToken.END_OBJECT, p.nextToken()); - assertNull(p.getCurrentName()); + assertNull(p.currentName()); assertToken(JsonToken.START_ARRAY, p.nextToken()); - assertNull(p.getCurrentName()); + assertNull(p.currentName()); assertToken(JsonToken.END_ARRAY, p.nextToken()); - assertNull(p.getCurrentName()); + assertNull(p.currentName()); assertToken(JsonToken.END_ARRAY, p.nextToken()); assertToken(JsonToken.END_OBJECT, p.nextToken()); - assertNull(p.getCurrentName()); + assertNull(p.currentName()); assertNull(p.nextToken()); @@ -110,19 +108,19 @@ public void testSimple() throws Exception public void testArray() throws Exception { // For convenience, parse tree from JSON first - JsonParser p = MAPPER.readTree("[]").traverse(); + JsonParser p = MAPPER.readTree("[]").traverse(ObjectReadContext.empty()); assertToken(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.END_ARRAY, p.nextToken()); p.close(); - p = MAPPER.readTree("[[]]").traverse(); + p = MAPPER.readTree("[[]]").traverse(ObjectReadContext.empty()); assertToken(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.END_ARRAY, p.nextToken()); assertToken(JsonToken.END_ARRAY, p.nextToken()); p.close(); - p = MAPPER.readTree("[[ 12.1 ]]").traverse(); + p = MAPPER.readTree("[[ 12.1 ]]").traverse(ObjectReadContext.empty()); assertToken(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); @@ -138,7 +136,7 @@ public void testNested() throws Exception "{\"coordinates\":[[[-3,\n1],[179.859681,51.175092]]]}" ; JsonNode tree = MAPPER.readTree(JSON); - JsonParser p = tree.traverse(); + JsonParser p = tree.traverse(ObjectReadContext.empty()); assertToken(JsonToken.START_OBJECT, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); @@ -169,7 +167,7 @@ public void testNested() throws Exception public void testSpecDoc() throws Exception { JsonNode tree = MAPPER.readTree(SAMPLE_DOC_JSON_SPEC); - JsonParser p = tree.traverse(); + JsonParser p = tree.traverse(ObjectReadContext.empty()); verifyJsonSpecSampleDoc(p, true); p.close(); } @@ -178,9 +176,9 @@ public void testBinaryPojo() throws Exception { byte[] inputBinary = new byte[] { 1, 2, 100 }; POJONode n = new POJONode(inputBinary); - JsonParser p = n.traverse(); + JsonParser p = n.traverse(ObjectReadContext.empty()); - assertNull(p.getCurrentToken()); + assertNull(p.currentToken()); assertToken(JsonToken.VALUE_EMBEDDED_OBJECT, p.nextToken()); byte[] data = p.getBinaryValue(); assertNotNull(data); @@ -194,9 +192,9 @@ public void testBinaryNode() throws Exception { byte[] inputBinary = new byte[] { 0, -5 }; BinaryNode n = new BinaryNode(inputBinary); - JsonParser p = n.traverse(); + JsonParser p = n.traverse(ObjectReadContext.empty()); - assertNull(p.getCurrentToken()); + assertNull(p.currentToken()); // exposed as POJO... not as VALUE_STRING assertToken(JsonToken.VALUE_EMBEDDED_OBJECT, p.nextToken()); byte[] data = p.getBinaryValue(); @@ -213,8 +211,8 @@ public void testBinaryNode() throws Exception public void testTextAsBinary() throws Exception { TextNode n = new TextNode(" APs=\n"); - JsonParser p = n.traverse(); - assertNull(p.getCurrentToken()); + JsonParser p = n.traverse(ObjectReadContext.empty()); + assertNull(p.currentToken()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); byte[] data = p.getBinaryValue(); assertNotNull(data); @@ -226,7 +224,7 @@ public void testTextAsBinary() throws Exception // Also: let's verify we get an exception for garbage... n = new TextNode("?!??"); - p = n.traverse(); + p = n.traverse(ObjectReadContext.empty()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); try { p.getBinaryValue(); @@ -263,7 +261,7 @@ public void testSkipChildrenWrt370() throws Exception ObjectNode n = MAPPER.createObjectNode(); n.putObject("inner").put("value", "test"); n.putObject("unknown").putNull("inner"); - Jackson370Bean obj = MAPPER.readValue(n.traverse(), Jackson370Bean.class); + Jackson370Bean obj = MAPPER.readValue(n.traverse(ObjectReadContext.empty()), Jackson370Bean.class); assertNotNull(obj.inner); assertEquals("test", obj.inner.value); } @@ -273,7 +271,7 @@ public void testSkipChildrenWrt370() throws Exception public void testNumberOverflowInt() throws IOException { final long tooBig = 1L + Integer.MAX_VALUE; - try (final JsonParser p = MAPPER.readTree("[ "+tooBig+" ]").traverse()) { + try (final JsonParser p = MAPPER.readTree("[ "+tooBig+" ]").traverse(ObjectReadContext.empty())) { assertToken(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(NumberType.LONG, p.getNumberType()); @@ -281,10 +279,10 @@ public void testNumberOverflowInt() throws IOException p.getIntValue(); fail("Expected failure for `int` overflow"); } catch (InputCoercionException e) { - verifyException(e, "Numeric value ("+tooBig+") out of range of int"); + verifyException(e, "Numeric value ("+tooBig+") out of range of `int`"); } } - try (final JsonParser p = MAPPER.readTree("{ \"value\" : "+tooBig+" }").traverse()) { + try (final JsonParser p = MAPPER.readTree("{ \"value\" : "+tooBig+" }").traverse(ObjectReadContext.empty())) { assertToken(JsonToken.START_OBJECT, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); @@ -293,12 +291,12 @@ public void testNumberOverflowInt() throws IOException p.getIntValue(); fail("Expected failure for `int` overflow"); } catch (InputCoercionException e) { - verifyException(e, "Numeric value ("+tooBig+") out of range of int"); + verifyException(e, "Numeric value ("+tooBig+") out of range of `int`"); } } // But also from floating-point final String tooBig2 = "1.0e10"; - try (final JsonParser p = MAPPER.readTree("[ "+tooBig2+" ]").traverse()) { + try (final JsonParser p = MAPPER.readTree("[ "+tooBig2+" ]").traverse(ObjectReadContext.empty())) { assertToken(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); assertEquals(NumberType.DOUBLE, p.getNumberType()); @@ -306,7 +304,7 @@ public void testNumberOverflowInt() throws IOException p.getIntValue(); fail("Expected failure for `int` overflow"); } catch (InputCoercionException e) { - verifyException(e, "Numeric value ("+tooBig2+") out of range of int"); + verifyException(e, "Numeric value ("+tooBig2+") out of range of `int`"); } } } @@ -314,7 +312,7 @@ public void testNumberOverflowInt() throws IOException public void testNumberOverflowLong() throws IOException { final BigInteger tooBig = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE); - try (final JsonParser p = MAPPER.readTree("[ "+tooBig+" ]").traverse()) { + try (final JsonParser p = MAPPER.readTree("[ "+tooBig+" ]").traverse(ObjectReadContext.empty())) { assertToken(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(NumberType.BIG_INTEGER, p.getNumberType()); @@ -322,10 +320,10 @@ public void testNumberOverflowLong() throws IOException p.getLongValue(); fail("Expected failure for `long` overflow"); } catch (InputCoercionException e) { - verifyException(e, "Numeric value ("+tooBig+") out of range of long"); + verifyException(e, "Numeric value ("+tooBig+") out of range of `long`"); } } - try (final JsonParser p = MAPPER.readTree("{ \"value\" : "+tooBig+" }").traverse()) { + try (final JsonParser p = MAPPER.readTree("{ \"value\" : "+tooBig+" }").traverse(ObjectReadContext.empty())) { assertToken(JsonToken.START_OBJECT, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); @@ -334,12 +332,12 @@ public void testNumberOverflowLong() throws IOException p.getLongValue(); fail("Expected failure for `long` overflow"); } catch (InputCoercionException e) { - verifyException(e, "Numeric value ("+tooBig+") out of range of long"); + verifyException(e, "Numeric value ("+tooBig+") out of range of `long`"); } } // But also from floating-point final String tooBig2 = "1.0e30"; - try (final JsonParser p = MAPPER.readTree("[ "+tooBig2+" ]").traverse()) { + try (final JsonParser p = MAPPER.readTree("[ "+tooBig2+" ]").traverse(ObjectReadContext.empty())) { assertToken(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); assertEquals(NumberType.DOUBLE, p.getNumberType()); @@ -347,7 +345,7 @@ public void testNumberOverflowLong() throws IOException p.getLongValue(); fail("Expected failure for `long` overflow"); } catch (InputCoercionException e) { - verifyException(e, "Numeric value ("+tooBig2+") out of range of long"); + verifyException(e, "Numeric value ("+tooBig2+") out of range of `long`"); } } } diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TestTreeWithType.java b/src/test/java/com/fasterxml/jackson/databind/node/TestTreeWithType.java index 83956272e4..8dc74bfff4 100644 --- a/src/test/java/com/fasterxml/jackson/databind/node/TestTreeWithType.java +++ b/src/test/java/com/fasterxml/jackson/databind/node/TestTreeWithType.java @@ -35,7 +35,7 @@ public class SavedCookieDeserializer extends JsonDeserializer { @Override public SavedCookie deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException { - ObjectCodec oc = jsonParser.getCodec(); + ObjectReadContext oc = jsonParser.getObjectReadContext(); JsonNode node = oc.readTree(jsonParser); return new SavedCookie(node.path("name").textValue(), node.path("value").textValue()); @@ -68,9 +68,9 @@ public void testValueAsStringWithoutDefaultTyping() throws Exception { } public void testValueAsStringWithDefaultTyping() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); - + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY) + .build(); Foo foo = new Foo("baz"); String json = mapper.writeValueAsString(foo); @@ -82,9 +82,10 @@ public void testReadTreeWithDefaultTyping() throws Exception { final String CLASS = Foo.class.getName(); - final ObjectMapper mapper = new ObjectMapper(); - mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, - JsonTypeInfo.As.PROPERTY); + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.NON_FINAL, + JsonTypeInfo.As.PROPERTY) + .build(); String json = "{\"@class\":\""+CLASS+"\",\"bar\":\"baz\"}"; JsonNode jsonNode = mapper.readTree(json); assertEquals(jsonNode.get("bar").textValue(), "baz"); @@ -97,10 +98,11 @@ public void testValueToTreeWithoutDefaultTyping() throws Exception { assertEquals(jsonNode.get("bar").textValue(), foo.bar); } - public void testValueToTreeWithDefaultTyping() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); - + public void testValueToTreeWithDefaultTyping() throws Exception + { + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY) + .build(); Foo foo = new Foo("baz"); JsonNode jsonNode = mapper.valueToTree(foo); assertEquals(jsonNode.get("bar").textValue(), foo.bar); @@ -108,19 +110,17 @@ public void testValueToTreeWithDefaultTyping() throws Exception { public void testIssue353() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - - mapper.enableDefaultTypingAsProperty(ObjectMapper.DefaultTyping.NON_FINAL, "@class"); - - SimpleModule testModule = new SimpleModule("MyModule", new Version(1, 0, 0, null, "TEST", "TEST")); - testModule.addDeserializer(SavedCookie.class, new SavedCookieDeserializer()); - mapper.registerModule(testModule); - - SavedCookie savedCookie = new SavedCookie("key", "v"); - String json = mapper.writeValueAsString(savedCookie); - SavedCookie out = mapper.readerFor(SavedCookie.class).readValue(json); - - assertEquals("key", out.name); - assertEquals("v", out.value); + SimpleModule testModule = new SimpleModule("MyModule", new Version(1, 0, 0, null, "TEST", "TEST")); + testModule.addDeserializer(SavedCookie.class, new SavedCookieDeserializer()); + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTypingAsProperty(DefaultTyping.NON_FINAL, "@class") + .addModule(testModule) + .build(); + SavedCookie savedCookie = new SavedCookie("key", "v"); + String json = mapper.writeValueAsString(savedCookie); + SavedCookie out = mapper.readerFor(SavedCookie.class).readValue(json); + + assertEquals("key", out.name); + assertEquals("v", out.value); } } diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TreeFromIncompleteJsonTest.java b/src/test/java/com/fasterxml/jackson/databind/node/TreeFromIncompleteJsonTest.java index bb2a6dbd92..5856a8f6fe 100644 --- a/src/test/java/com/fasterxml/jackson/databind/node/TreeFromIncompleteJsonTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/node/TreeFromIncompleteJsonTest.java @@ -13,7 +13,7 @@ public class TreeFromIncompleteJsonTest extends BaseMapTest public void testErrorHandling() throws IOException { String json = "{\"A\":{\"B\":\n"; - JsonParser parser = MAPPER.getFactory().createParser(json); + JsonParser parser = MAPPER.createParser(json); try { parser.readValueAsTree(); } catch (JsonEOFException e) { diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TreeReadViaMapperTest.java b/src/test/java/com/fasterxml/jackson/databind/node/TreeReadViaMapperTest.java index c27f9dbcd2..0fb8381976 100644 --- a/src/test/java/com/fasterxml/jackson/databind/node/TreeReadViaMapperTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/node/TreeReadViaMapperTest.java @@ -114,8 +114,8 @@ public void testEOF() throws Exception +"}, \"type\": 3, \"url\": \"http://www.google.com\" } ],\n" +"\"name\": \"xyz\", \"type\": 1, \"url\" : null }\n " ; - JsonFactory jf = new JsonFactory(); - JsonParser p = jf.createParser(new StringReader(JSON)); + final ObjectMapper mapper = objectMapper(); + JsonParser p = MAPPER.createParser(new StringReader(JSON)); JsonNode result = MAPPER.readTree(p); assertTrue(result.isObject()); @@ -128,9 +128,8 @@ public void testEOF() throws Exception public void testNullViaParser() throws Exception { final String JSON = " null "; - JsonFactory jf = new JsonFactory(); - try (JsonParser p = jf.createParser(new StringReader(JSON))) { + try (JsonParser p = MAPPER.createParser(new StringReader(JSON))) { final JsonNode result = MAPPER.readTree(p); assertTrue(result.isNull()); } @@ -138,10 +137,9 @@ public void testNullViaParser() throws Exception public void testMultiple() throws Exception { - String JSON = "12 \"string\" [ 1, 2, 3 ]"; - JsonFactory jf = new JsonFactory(); - JsonParser p = jf.createParser(new StringReader(JSON)); final ObjectMapper mapper = objectMapper(); + String JSON = "12 \"string\" [ 1, 2, 3 ]"; + JsonParser p = mapper.createParser(new StringReader(JSON)); JsonNode result = mapper.readTree(p); assertTrue(result.isIntegralNumber()); diff --git a/src/test/java/com/fasterxml/jackson/databind/objectid/ObjectId825BTest.java b/src/test/java/com/fasterxml/jackson/databind/objectid/ObjectId825BTest.java index 8c36f278a6..1bd7482b76 100644 --- a/src/test/java/com/fasterxml/jackson/databind/objectid/ObjectId825BTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/objectid/ObjectId825BTest.java @@ -137,9 +137,10 @@ static class V extends AbstractData { public void testFull825() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE); - mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT); + final ObjectMapper mapper = jsonMapperBuilder() + .enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT) + .enableDefaultTyping(DefaultTyping.OBJECT_AND_NON_CONCRETE) + .build(); String INPUT = aposToQuotes( "{\n"+ diff --git a/src/test/java/com/fasterxml/jackson/databind/objectid/ObjectId825Test.java b/src/test/java/com/fasterxml/jackson/databind/objectid/ObjectId825Test.java index 86f4eb94e6..8ac1fcb49d 100644 --- a/src/test/java/com/fasterxml/jackson/databind/objectid/ObjectId825Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/objectid/ObjectId825Test.java @@ -24,11 +24,10 @@ static class TestC extends TestAbst { static class TestD extends AbstractEntity { } - private final ObjectMapper DEF_TYPING_MAPPER = new ObjectMapper(); - { - DEF_TYPING_MAPPER.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); - } - + private final ObjectMapper DEF_TYPING_MAPPER = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.NON_FINAL) + .build(); + public void testDeserialize() throws Exception { TestA a = new TestA(); a.oidString = "oidA"; diff --git a/src/test/java/com/fasterxml/jackson/databind/objectid/ObjectWithCreator1261Test.java b/src/test/java/com/fasterxml/jackson/databind/objectid/ObjectWithCreator1261Test.java index be6c5fdc2f..01f6b92fcd 100644 --- a/src/test/java/com/fasterxml/jackson/databind/objectid/ObjectWithCreator1261Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/objectid/ObjectWithCreator1261Test.java @@ -65,10 +65,10 @@ public Child(String n) { public void testObjectIds1261() throws Exception { - ObjectMapper mapper = objectMapperBuilder() + ObjectMapper mapper = jsonMapperBuilder() .enable(SerializationFeature.INDENT_OUTPUT) - .enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY) - .build(); + .enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY) + .build(); Answer initialAnswer = createInitialAnswer(); String initialAnswerString = mapper.writeValueAsString(initialAnswer); diff --git a/src/test/java/com/fasterxml/jackson/databind/objectid/TestAbstractWithObjectId.java b/src/test/java/com/fasterxml/jackson/databind/objectid/TestAbstractWithObjectId.java index b2267fb597..2e6cd292df 100644 --- a/src/test/java/com/fasterxml/jackson/databind/objectid/TestAbstractWithObjectId.java +++ b/src/test/java/com/fasterxml/jackson/databind/objectid/TestAbstractWithObjectId.java @@ -52,14 +52,15 @@ public void testIssue877() throws Exception myList.add(two); // make an object mapper that will add class info in so deserialisation works - ObjectMapper om = new ObjectMapper(); - om.enableDefaultTypingAsProperty(ObjectMapper.DefaultTyping.NON_FINAL, "@class"); + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTypingAsProperty(DefaultTyping.NON_FINAL, "@class") + .build(); // write and print the JSON - String json = om.writerWithDefaultPrettyPrinter().writeValueAsString(myList); + String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(myList); ListWrapper result; - result = om.readValue(json, new TypeReference>() { }); + result = mapper.readValue(json, new TypeReference>() { }); assertNotNull(result); // see what we get back diff --git a/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectId.java b/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectId.java index a51776cdf6..8fb6c19736 100644 --- a/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectId.java +++ b/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectId.java @@ -16,35 +16,33 @@ static class Wrapper { @JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="@id") static class ColumnMetadata { - private final String name; - private final String type; - private final String comment; - - @JsonCreator - public ColumnMetadata( - @JsonProperty("name") String name, - @JsonProperty("type") String type, - @JsonProperty("comment") String comment - ) { - this.name = name; - this.type = type; - this.comment = comment; - } - - @JsonProperty("name") - public String getName() { - return name; - } - - @JsonProperty("type") - public String getType() { - return type; - } - - @JsonProperty("comment") - public String getComment() { - return comment; - } + private final String name; + private final String type; + private final String comment; + + @JsonCreator + public ColumnMetadata(@JsonProperty("name") String name, + @JsonProperty("type") String type, + @JsonProperty("comment") String comment) { + this.name = name; + this.type = type; + this.comment = comment; + } + + @JsonProperty("name") + public String getName() { + return name; + } + + @JsonProperty("type") + public String getType() { + return type; + } + + @JsonProperty("comment") + public String getComment() { + return comment; + } } /* Problem in which always-as-id reference may prevent initial diff --git a/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdDeserialization.java index 2db9bdc610..dede44b60c 100644 --- a/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdDeserialization.java +++ b/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdDeserialization.java @@ -431,7 +431,7 @@ public void testCustomPoolResolver() throws Exception pool.put(3, new WithCustomResolution(3, 3)); pool.put(4, new WithCustomResolution(4, 4)); pool.put(5, new WithCustomResolution(5, 5)); - ContextAttributes attrs = MAPPER.getDeserializationConfig().getAttributes().withSharedAttribute(POOL_KEY, pool); + ContextAttributes attrs = MAPPER.deserializationConfig().getAttributes().withSharedAttribute(POOL_KEY, pool); String content = "{\"data\":[1,2,3,4,5]}"; CustomResolutionWrapper wrapper = MAPPER.readerFor(CustomResolutionWrapper.class).with(attrs).readValue(content); assertFalse(wrapper.data.isEmpty()); diff --git a/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdWithEquals.java b/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdWithEquals.java index ea02709fbc..4b31ba0d70 100644 --- a/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdWithEquals.java +++ b/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdWithEquals.java @@ -80,10 +80,9 @@ public int hashCode() { public void testSimpleEquals() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - // Verify default state too - assertFalse(mapper.isEnabled(SerializationFeature.USE_EQUALITY_FOR_OBJECT_ID)); - mapper.enable(SerializationFeature.USE_EQUALITY_FOR_OBJECT_ID); + ObjectMapper mapper = jsonMapperBuilder() + .enable(SerializationFeature.USE_EQUALITY_FOR_OBJECT_ID) + .build(); Foo foo = new Foo(1); @@ -123,8 +122,9 @@ public void testEqualObjectIdsExternal() throws Exception // Element[] input = new Element[] { element, element2 }; List input = Arrays.asList(element, element2); - ObjectMapper mapper = new ObjectMapper(); - mapper.enable(SerializationFeature.USE_EQUALITY_FOR_OBJECT_ID); + ObjectMapper mapper = jsonMapperBuilder() + .enable(SerializationFeature.USE_EQUALITY_FOR_OBJECT_ID) + .build(); // String json = mapper.writeValueAsString(input); String json = mapper.writerFor(new TypeReference>() { }) diff --git a/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdWithPolymorphic.java b/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdWithPolymorphic.java index 486abf3368..b5e3f8f250 100644 --- a/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdWithPolymorphic.java +++ b/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdWithPolymorphic.java @@ -7,7 +7,6 @@ import com.fasterxml.jackson.annotation.ObjectIdGenerators; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping; public class TestObjectIdWithPolymorphic extends BaseMapTest { @@ -131,11 +130,12 @@ public void testPolymorphicRoundtrip() throws Exception public void testIssue811() throws Exception { - ObjectMapper om = new ObjectMapper(); - om.enable(SerializationFeature.WRITE_ENUMS_USING_INDEX); - om.enable(SerializationFeature.INDENT_OUTPUT); - om.enableDefaultTypingAsProperty(DefaultTyping.NON_FINAL, "@class"); - + ObjectMapper om = jsonMapperBuilder() + .enableDefaultTypingAsProperty(DefaultTyping.NON_FINAL, "@class") + .enable(SerializationFeature.WRITE_ENUMS_USING_INDEX, + SerializationFeature.INDENT_OUTPUT) + .build(); + Process p = new Process(); Scope s = new Scope(p, null); FaultHandler fh = new FaultHandler(p); diff --git a/src/test/java/com/fasterxml/jackson/databind/seq/PolyMapWriter827Test.java b/src/test/java/com/fasterxml/jackson/databind/seq/PolyMapWriter827Test.java index 574a08c427..6504bbdef7 100644 --- a/src/test/java/com/fasterxml/jackson/databind/seq/PolyMapWriter827Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/seq/PolyMapWriter827Test.java @@ -7,12 +7,14 @@ import org.junit.Assert; import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; + import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; // for [databind#827] +@SuppressWarnings("serial") public class PolyMapWriter827Test extends BaseMapTest { static class CustomKey { @@ -23,21 +25,21 @@ static class CustomKey { public String toString() { return "BAD-KEY"; } } - public class CustomKeySerializer extends JsonSerializer { + public class CustomKeySerializer extends StdSerializer { + public CustomKeySerializer() { super(CustomKey.class); } @Override - public void serialize(CustomKey key, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException { - jsonGenerator.writeFieldName(key.a + "," + key.b); + public void serialize(CustomKey key, JsonGenerator g, SerializerProvider serializerProvider) throws IOException { + g.writeFieldName(key.a + "," + key.b); } } public void testPolyCustomKeySerializer() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); - - mapper.registerModule(new SimpleModule("keySerializerModule") - .addKeySerializer(CustomKey.class, new CustomKeySerializer())); - + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.NON_FINAL) + .addModule(new SimpleModule("keySerializerModule") + .addKeySerializer(CustomKey.class, new CustomKeySerializer())) + .build(); Map map = new HashMap(); CustomKey key = new CustomKey(); key.a = "foo"; diff --git a/src/test/java/com/fasterxml/jackson/databind/seq/ReadValuesTest.java b/src/test/java/com/fasterxml/jackson/databind/seq/ReadValuesTest.java index 9f3634ed67..2042a08f1b 100644 --- a/src/test/java/com/fasterxml/jackson/databind/seq/ReadValuesTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/seq/ReadValuesTest.java @@ -163,26 +163,10 @@ public void testRootMaps() throws Exception /********************************************************** */ - public void testRootBeansWithParser() throws Exception - { - final String JSON = "{\"a\":3}{\"a\":27} "; - JsonParser jp = MAPPER.getFactory().createParser(JSON); - - Iterator it = jp.readValuesAs(Bean.class); - - assertTrue(it.hasNext()); - Bean b = it.next(); - assertEquals(3, b.a); - assertTrue(it.hasNext()); - b = it.next(); - assertEquals(27, b.a); - assertFalse(it.hasNext()); - } - public void testRootArraysWithParser() throws Exception { final String JSON = "[1][3]"; - JsonParser jp = MAPPER.getFactory().createParser(JSON); + JsonParser jp = MAPPER.createParser(JSON); // NOTE: We must point JsonParser to the first element; if we tried to // use "managed" accessor, it would try to advance past START_ARRAY. @@ -202,7 +186,7 @@ public void testRootArraysWithParser() throws Exception public void testHasNextWithEndArray() throws Exception { final String JSON = "[1,3]"; - JsonParser jp = MAPPER.getFactory().createParser(JSON); + JsonParser jp = MAPPER.createParser(JSON); // NOTE: We must point JsonParser to the first element; if we tried to // use "managed" accessor, it would try to advance past START_ARRAY. @@ -243,7 +227,7 @@ public void testHasNextWithEndArrayManagedParser() throws Exception { public void testNonRootBeans() throws Exception { final String JSON = "{\"leaf\":[{\"a\":3},{\"a\":27}]}"; - JsonParser jp = MAPPER.getFactory().createParser(JSON); + JsonParser jp = MAPPER.createParser(JSON); assertToken(JsonToken.START_OBJECT, jp.nextToken()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); assertToken(JsonToken.START_ARRAY, jp.nextToken()); @@ -267,7 +251,7 @@ public void testNonRootBeans() throws Exception public void testNonRootMapsWithParser() throws Exception { final String JSON = "[{\"a\":3},{\"a\":27}]"; - JsonParser jp = MAPPER.getFactory().createParser(JSON); + JsonParser jp = MAPPER.createParser(JSON); assertToken(JsonToken.START_ARRAY, jp.nextToken()); // can either advance to first START_OBJECT, or clear current token; @@ -311,7 +295,7 @@ public void testNonRootMapsWithObjectReader() throws Exception public void testNonRootArraysUsingParser() throws Exception { final String JSON = "[[1],[3]]"; - JsonParser p = MAPPER.getFactory().createParser(JSON); + JsonParser p = MAPPER.createParser(JSON); assertToken(JsonToken.START_ARRAY, p.nextToken()); // Important: as of 2.1, START_ARRAY can only be skipped if the diff --git a/src/test/java/com/fasterxml/jackson/databind/seq/SequenceWriterTest.java b/src/test/java/com/fasterxml/jackson/databind/seq/SequenceWriterTest.java index c103a34ae3..62d1e21f10 100644 --- a/src/test/java/com/fasterxml/jackson/databind/seq/SequenceWriterTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/seq/SequenceWriterTest.java @@ -9,7 +9,6 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeName; -import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.io.SerializedString; import com.fasterxml.jackson.databind.*; @@ -108,14 +107,12 @@ public void testSimpleNonArray() throws Exception strw.toString()); strw = new StringWriter(); - JsonGenerator gen = WRITER.getFactory().createGenerator(strw); w = WRITER .withRootValueSeparator(new SerializedString("/")) - .writeValues(gen); + .writeValues(strw); w.write(new Bean(1)) .write(new Bean(2)); w.close(); - gen.close(); assertEquals(aposToQuotes("{'a':1}/{'a':2}"), strw.toString()); } @@ -132,14 +129,12 @@ public void testSimpleArray() throws Exception strw.toString()); strw = new StringWriter(); - JsonGenerator gen = WRITER.getFactory().createGenerator(strw); - w = WRITER.writeValuesAsArray(gen); + w = WRITER.writeValuesAsArray(strw); Collection bean = Collections.singleton(new Bean(3)); w.write(new Bean(1)) .write(null) .writeAll((Iterable) bean); w.close(); - gen.close(); assertEquals(aposToQuotes("[{'a':1},null,{'a':3}]"), strw.toString()); } diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/AnyGetterTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/AnyGetterTest.java index 23746d492a..c55f0e7802 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/AnyGetterTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/AnyGetterTest.java @@ -82,7 +82,7 @@ public Map getParameters(){ static class Issue705Serializer extends StdSerializer { public Issue705Serializer() { - super(Map.class, false); + super(Map.class); } @Override @@ -153,15 +153,15 @@ public void testAnyOnly() throws Exception ObjectMapper m; // First, with normal fail settings: - m = new ObjectMapper(); - m.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, true); - String json = serializeAsString(m, new AnyOnlyBean()); - assertEquals("{\"a\":3}", json); + m = jsonMapperBuilder() + .enable(SerializationFeature.FAIL_ON_EMPTY_BEANS) + .build(); + assertEquals("{\"a\":3}", m.writeValueAsString(new AnyOnlyBean())); // then without fail - m = new ObjectMapper(); - m.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); - json = serializeAsString(m, new AnyOnlyBean()); + String json = m.writer() + .without(SerializationFeature.FAIL_ON_EMPTY_BEANS) + .writeValueAsString(new AnyOnlyBean()); assertEquals("{\"a\":3}", json); } diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/BeanSerializerModifier1612Test.java b/src/test/java/com/fasterxml/jackson/databind/ser/BeanSerializerModifier1612Test.java index 2be2594f8e..aeeab4f2e1 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/BeanSerializerModifier1612Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/BeanSerializerModifier1612Test.java @@ -53,10 +53,11 @@ public void testIssue1612() throws Exception { SimpleModule mod = new SimpleModule(); mod.setSerializerModifier(new Modifier1612()); - ObjectMapper objectMapper = new ObjectMapper() - .registerModule(mod); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(mod) + .build(); try { - objectMapper.writeValueAsString(new Bean1612(0, 1, 2d)); + mapper.writeValueAsString(new Bean1612(0, 1, 2d)); fail("Should not pass"); } catch (InvalidDefinitionException e) { verifyException(e, "Failed to construct BeanSerializer"); diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/BeanSerializerModifierTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/BeanSerializerModifierTest.java index 6f0e571d27..51979a1143 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/BeanSerializerModifierTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/BeanSerializerModifierTest.java @@ -40,7 +40,7 @@ public void setupModule(SetupContext context) { super.setupModule(context); if (modifier != null) { - context.addBeanSerializerModifier(modifier); + context.addSerializerModifier(modifier); } } } @@ -129,16 +129,19 @@ public JsonSerializer build() { } } - static class BogusBeanSerializer extends JsonSerializer + static class BogusBeanSerializer extends StdSerializer { private final int _value; - public BogusBeanSerializer(int v) { _value = v; } - + public BogusBeanSerializer(int v) { + super(Object.class); + _value = v; + } + @Override - public void serialize(Object value, JsonGenerator jgen, + public void serialize(Object value, JsonGenerator g, SerializerProvider provider) throws IOException { - jgen.writeNumber(_value); + g.writeNumber(_value); } } @@ -193,8 +196,8 @@ static class ArraySerializerModifier extends BeanSerializerModifier { public JsonSerializer modifyArraySerializer(SerializationConfig config, ArrayType valueType, BeanDescription beanDesc, JsonSerializer serializer) { return new StdSerializer(Object.class) { - @Override public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException { - jgen.writeNumber(123); + @Override public void serialize(Object value, JsonGenerator g, SerializerProvider provider) throws IOException { + g.writeNumber(123); } }; } @@ -205,8 +208,8 @@ static class CollectionSerializerModifier extends BeanSerializerModifier { public JsonSerializer modifyCollectionSerializer(SerializationConfig config, CollectionType valueType, BeanDescription beanDesc, JsonSerializer serializer) { return new StdSerializer(Object.class) { - @Override public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException { - jgen.writeNumber(123); + @Override public void serialize(Object value, JsonGenerator g, SerializerProvider provider) throws IOException { + g.writeNumber(123); } }; } @@ -217,8 +220,8 @@ static class MapSerializerModifier extends BeanSerializerModifier { public JsonSerializer modifyMapSerializer(SerializationConfig config, MapType valueType, BeanDescription beanDesc, JsonSerializer serializer) { return new StdSerializer(Object.class) { - @Override public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException { - jgen.writeNumber(123); + @Override public void serialize(Object value, JsonGenerator g, SerializerProvider provider) throws IOException { + g.writeNumber(123); } }; } @@ -229,8 +232,8 @@ static class EnumSerializerModifier extends BeanSerializerModifier { public JsonSerializer modifyEnumSerializer(SerializationConfig config, JavaType valueType, BeanDescription beanDesc, JsonSerializer serializer) { return new StdSerializer(Object.class) { - @Override public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException { - jgen.writeNumber(123); + @Override public void serialize(Object value, JsonGenerator g, SerializerProvider provider) throws IOException { + g.writeNumber(123); } }; } @@ -241,8 +244,8 @@ static class KeySerializerModifier extends BeanSerializerModifier { public JsonSerializer modifyKeySerializer(SerializationConfig config, JavaType valueType, BeanDescription beanDesc, JsonSerializer serializer) { return new StdSerializer(Object.class) { - @Override public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException { - jgen.writeFieldName("foo"); + @Override public void serialize(Object value, JsonGenerator g, SerializerProvider provider) throws IOException { + g.writeFieldName("foo"); } }; } @@ -256,62 +259,67 @@ public JsonSerializer modifyKeySerializer(SerializationConfig config, public void testPropertyRemoval() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new SerializerModifierModule(new RemovingModifier("a"))); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new SerializerModifierModule(new RemovingModifier("a"))) + .build(); Bean bean = new Bean(); assertEquals("{\"b\":\"b\"}", mapper.writeValueAsString(bean)); } public void testPropertyReorder() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new SerializerModifierModule(new ReorderingModifier())); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new SerializerModifierModule(new ReorderingModifier())) + .build(); Bean bean = new Bean(); assertEquals("{\"a\":\"a\",\"b\":\"b\"}", mapper.writeValueAsString(bean)); } public void testBuilderReplacement() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new SerializerModifierModule(new BuilderModifier(new BogusBeanSerializer(17)))); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new SerializerModifierModule(new BuilderModifier(new BogusBeanSerializer(17)))) + .build(); Bean bean = new Bean(); assertEquals("17", mapper.writeValueAsString(bean)); } public void testSerializerReplacement() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new SerializerModifierModule(new ReplacingModifier(new BogusBeanSerializer(123)))); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new SerializerModifierModule(new ReplacingModifier(new BogusBeanSerializer(123)))) + .build(); Bean bean = new Bean(); assertEquals("123", mapper.writeValueAsString(bean)); } - // for [JACKSON-670] public void testEmptyBean() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new SimpleModule("test", Version.unknownVersion()) { + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new SimpleModule("test", Version.unknownVersion()) { @Override public void setupModule(SetupContext context) { super.setupModule(context); - context.addBeanSerializerModifier(new EmptyBeanModifier()); + context.addSerializerModifier(new EmptyBeanModifier()); } - }); + }) + .build(); String json = mapper.writeValueAsString(new EmptyBean()); assertEquals("{\"bogus\":\"foo\"}", json); } public void testEmptyBean539() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new SimpleModule("test", Version.unknownVersion()) { + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new SimpleModule("test", Version.unknownVersion()) { @Override public void setupModule(SetupContext context) { super.setupModule(context); - context.addBeanSerializerModifier(new EmptyBeanModifier539()); + context.addSerializerModifier(new EmptyBeanModifier539()); } - }); + }) + .build(); String json = mapper.writeValueAsString(new EmptyBean()); assertEquals("42", json); } @@ -320,41 +328,46 @@ public void setupModule(SetupContext context) public void testModifyArraySerializer() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new SimpleModule("test") - .setSerializerModifier(new ArraySerializerModifier())); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new SimpleModule("test") + .setSerializerModifier(new ArraySerializerModifier())) + .build(); assertEquals("123", mapper.writeValueAsString(new Integer[] { 1, 2 })); } public void testModifyCollectionSerializer() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new SimpleModule("test") - .setSerializerModifier(new CollectionSerializerModifier())); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new SimpleModule("test") + .setSerializerModifier(new CollectionSerializerModifier())) + .build(); assertEquals("123", mapper.writeValueAsString(new ArrayList())); } public void testModifyMapSerializer() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new SimpleModule("test") - .setSerializerModifier(new MapSerializerModifier())); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new SimpleModule("test") + .setSerializerModifier(new MapSerializerModifier())) + .build(); assertEquals("123", mapper.writeValueAsString(new HashMap())); } public void testModifyEnumSerializer() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new SimpleModule("test") - .setSerializerModifier(new EnumSerializerModifier())); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new SimpleModule("test") + .setSerializerModifier(new EnumSerializerModifier())) + .build(); assertEquals("123", mapper.writeValueAsString(ABC.C)); } public void testModifyKeySerializer() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new SimpleModule("test") - .setSerializerModifier(new KeySerializerModifier())); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new SimpleModule("test") + .setSerializerModifier(new KeySerializerModifier())) + .build(); Map map = new HashMap(); map.put("x", 3); assertEquals("{\"foo\":3}", mapper.writeValueAsString(map)); diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/JsonValueTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/JsonValueTest.java index dc8c78fdad..7aa0c566a4 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/JsonValueTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/JsonValueTest.java @@ -215,8 +215,8 @@ public void testSimpleFieldJsonValue() throws Exception public void testJsonValueWithUseSerializer() throws Exception { - String result = serializeAsString(MAPPER, new ToStringValueClass(Integer.valueOf(123))); - assertEquals("\"123\"", result); + assertEquals("\"123\"", + MAPPER.writeValueAsString(new ToStringValueClass(Integer.valueOf(123)))); } /** @@ -224,8 +224,8 @@ public void testJsonValueWithUseSerializer() throws Exception */ public void testMixedJsonValue() throws Exception { - String result = serializeAsString(MAPPER, new ToStringValueClass2("xyz")); - assertEquals("\"xyz\"", result); + assertEquals("\"xyz\"", + MAPPER.writeValueAsString(new ToStringValueClass2("xyz"))); } public void testDisabling() throws Exception @@ -240,7 +240,7 @@ public void testValueWithStaticType() throws Exception assertEquals("{\"a\":\"a\",\"b\":\"b\"}", MAPPER.writeValueAsString(new ValueWrapper())); // then static - ObjectMapper staticMapper = objectMapperBuilder() + ObjectMapper staticMapper = jsonMapperBuilder() .configure(MapperFeature.USE_STATIC_TYPING, true) .build(); assertEquals("{\"a\":\"a\"}", staticMapper.writeValueAsString(new ValueWrapper())); @@ -289,10 +289,10 @@ public void testJsonValueWithCustomOverride() throws Exception assertEquals(quote("value"), MAPPER.writeValueAsString(INPUT)); // but custom serializer should override it - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new SimpleModule() - .addSerializer(Bean838.class, new Bean838Serializer()) - ); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(new SimpleModule() + .addSerializer(Bean838.class, new Bean838Serializer())) + .build(); assertEquals("42", mapper.writeValueAsString(INPUT)); } } diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/SerializationFeaturesTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/SerializationFeaturesTest.java index edaefcfd21..d4ffb1222d 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/SerializationFeaturesTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/SerializationFeaturesTest.java @@ -39,25 +39,28 @@ private static class StringListBean { /********************************************************** */ - // Test for [JACKSON-282] + private final ObjectMapper MAPPER = new ObjectMapper(); + @SuppressWarnings("resource") public void testCloseCloseable() throws IOException { - ObjectMapper m = new ObjectMapper(); // default should be disabled: CloseableBean bean = new CloseableBean(); - m.writeValueAsString(bean); + MAPPER.writeValueAsString(bean); assertFalse(bean.wasClosed); // but can enable it: - m.configure(SerializationFeature.CLOSE_CLOSEABLE, true); bean = new CloseableBean(); - m.writeValueAsString(bean); + MAPPER.writer() + .with(SerializationFeature.CLOSE_CLOSEABLE) + .writeValueAsString(bean); assertTrue(bean.wasClosed); // also: let's ensure that ObjectWriter won't interfere with it bean = new CloseableBean(); - m.writerFor(CloseableBean.class).writeValueAsString(bean); + MAPPER.writerFor(CloseableBean.class) + .with(SerializationFeature.CLOSE_CLOSEABLE) + .writeValueAsString(bean); assertTrue(bean.wasClosed); } @@ -70,38 +73,36 @@ public void testCharArrays() throws IOException assertEquals(quote("abc"), m.writeValueAsString(chars)); // new feature: serialize as JSON array: - m.configure(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS, true); - assertEquals("[\"a\",\"b\",\"c\"]", m.writeValueAsString(chars)); + assertEquals("[\"a\",\"b\",\"c\"]", + m.writer() + .with(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS) + .writeValueAsString(chars)); } - // Test for [JACKSON-401] public void testFlushingAutomatic() throws IOException { ObjectMapper mapper = new ObjectMapper(); - assertTrue(mapper.getSerializationConfig().isEnabled(SerializationFeature.FLUSH_AFTER_WRITE_VALUE)); + assertTrue(mapper.serializationConfig().isEnabled(SerializationFeature.FLUSH_AFTER_WRITE_VALUE)); // default is to flush after writeValue() StringWriter sw = new StringWriter(); - JsonGenerator g = mapper.getFactory().createGenerator(sw); - mapper.writeValue(g, Integer.valueOf(13)); + mapper.writeValue(sw, Integer.valueOf(13)); assertEquals("13", sw.toString()); - g.close(); // ditto with ObjectWriter sw = new StringWriter(); - g = mapper.getFactory().createGenerator(sw); ObjectWriter ow = mapper.writer(); - ow.writeValue(g, Integer.valueOf(99)); + ow.writeValue(sw, Integer.valueOf(99)); assertEquals("99", sw.toString()); - g.close(); } public void testFlushingNotAutomatic() throws IOException { // but should not occur if configured otherwise - ObjectMapper mapper = new ObjectMapper(); - mapper.configure(SerializationFeature.FLUSH_AFTER_WRITE_VALUE, false); + ObjectMapper mapper = jsonMapperBuilder() + .configure(SerializationFeature.FLUSH_AFTER_WRITE_VALUE, false) + .build(); StringWriter sw = new StringWriter(); - JsonGenerator g = mapper.getFactory().createGenerator(sw); + JsonGenerator g = mapper.createGenerator(sw); mapper.writeValue(g, Integer.valueOf(13)); // no flushing now: @@ -112,7 +113,7 @@ public void testFlushingNotAutomatic() throws IOException g.close(); // Also, same should happen with ObjectWriter sw = new StringWriter(); - g = mapper.getFactory().createGenerator(sw); + g = mapper.createGenerator(sw); ObjectWriter ow = mapper.writer(); ow.writeValue(g, Integer.valueOf(99)); assertEquals("", sw.toString()); diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestAnnotations.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestAnnotations.java index d23101b508..c24a346fcc 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/TestAnnotations.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestAnnotations.java @@ -9,20 +9,16 @@ import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; /** * This unit test suite tests use of Annotations for * bean serialization. */ +@SuppressWarnings("serial") public class TestAnnotations extends BaseMapTest { - /* - /********************************************************** - /* Helper classes - /********************************************************** - */ - /// Class for testing {@link JsonProperty} annotations with getters final static class SizeClassGetter { @@ -135,8 +131,9 @@ static class GettersWithoutSetters2 /********************************************************** */ - public final static class BogusSerializer extends JsonSerializer + public final static class BogusSerializer extends StdSerializer { + public BogusSerializer() { super(Object.class); } @Override public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationException @@ -145,8 +142,9 @@ public void serialize(Object value, JsonGenerator jgen, SerializerProvider provi } } - private final static class StringSerializer extends JsonSerializer + private final static class StringSerializer extends StdSerializer { + public StringSerializer() { super(Object.class); } @Override public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationException @@ -229,8 +227,9 @@ public void testActiveMethodSerializer() throws Exception public void testInactiveMethodSerializer() throws Exception { String json = MAPPER.writeValueAsString(new InactiveClassMethodSerializer(8)); - // Here we will get wrapped as an object, since we have - // full object, just override a single property + /* Here we will get wrapped as an object, since we have + * full object, just override a single property + */ assertEquals("{\"x\":8}", json); } @@ -244,7 +243,7 @@ public void testGettersWithoutSetters() throws Exception assertEquals("{\"a\":3,\"b\":4,\"c\":5,\"d\":6}", m.writeValueAsString(bean)); // but 3 if we require mutator: - m = objectMapperBuilder() + m = jsonMapperBuilder() .enable(MapperFeature.REQUIRE_SETTERS_FOR_GETTERS) .build(); assertEquals("{\"a\":3,\"c\":5,\"d\":6}", m.writeValueAsString(bean)); @@ -253,7 +252,7 @@ public void testGettersWithoutSetters() throws Exception public void testGettersWithoutSettersOverride() throws Exception { GettersWithoutSetters2 bean = new GettersWithoutSetters2(); - ObjectMapper m = objectMapperBuilder() + ObjectMapper m = jsonMapperBuilder() .enable(MapperFeature.REQUIRE_SETTERS_FOR_GETTERS) .build(); assertEquals("{\"a\":123}", m.writeValueAsString(bean)); diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestArraySerialization.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestArraySerialization.java index 1aa8bd5866..a4509addc1 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/TestArraySerialization.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestArraySerialization.java @@ -18,20 +18,20 @@ public void testLongStringArray() throws Exception } String str = sb.toString(); byte[] data = MAPPER.writeValueAsBytes(new String[] { "abc", str, null, str }); - JsonParser jp = MAPPER.getFactory().createParser(data); - assertToken(JsonToken.START_ARRAY, jp.nextToken()); - assertToken(JsonToken.VALUE_STRING, jp.nextToken()); - assertEquals("abc", jp.getText()); - assertToken(JsonToken.VALUE_STRING, jp.nextToken()); - String actual = jp.getText(); + JsonParser p = MAPPER.createParser(data); + assertToken(JsonToken.START_ARRAY, p.nextToken()); + assertToken(JsonToken.VALUE_STRING, p.nextToken()); + assertEquals("abc", p.getText()); + assertToken(JsonToken.VALUE_STRING, p.nextToken()); + String actual = p.getText(); assertEquals(str.length(), actual.length()); assertEquals(str, actual); - assertToken(JsonToken.VALUE_NULL, jp.nextToken()); - assertToken(JsonToken.VALUE_STRING, jp.nextToken()); - assertEquals(str, jp.getText()); - assertToken(JsonToken.END_ARRAY, jp.nextToken()); - assertNull(jp.nextToken()); - jp.close(); + assertToken(JsonToken.VALUE_NULL, p.nextToken()); + assertToken(JsonToken.VALUE_STRING, p.nextToken()); + assertEquals(str, p.getText()); + assertToken(JsonToken.END_ARRAY, p.nextToken()); + assertNull(p.nextToken()); + p.close(); } public void testIntArray() throws Exception @@ -51,17 +51,16 @@ public void testBigIntArray() throws Exception // Let's try couple of times, to ensure that state is handled // correctly by ObjectMapper (wrt buffer recycling used // with 'writeAsBytes()') - JsonFactory f = MAPPER.getFactory(); for (int round = 0; round < 3; ++round) { byte[] data = MAPPER.writeValueAsBytes(ints); - JsonParser jp = f.createParser(data); - assertToken(JsonToken.START_ARRAY, jp.nextToken()); + JsonParser p = MAPPER.createParser(data); + assertToken(JsonToken.START_ARRAY, p.nextToken()); for (int i = 0; i < SIZE; ++i) { - assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); - assertEquals(i, jp.getIntValue()); + assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); + assertEquals(i, p.getIntValue()); } - assertToken(JsonToken.END_ARRAY, jp.nextToken()); - jp.close(); + assertToken(JsonToken.END_ARRAY, p.nextToken()); + p.close(); } } diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestAutoDetect.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestAutoDetect.java index c468f26836..669d3ed284 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/TestAutoDetect.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestAutoDetect.java @@ -5,7 +5,6 @@ import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.introspect.VisibilityChecker; /** * Unit tests for checking extended auto-detect configuration, @@ -71,10 +70,10 @@ public void testProtectedViaAnnotations() throws Exception public void testPrivateUsingGlobals() throws Exception { - ObjectMapper m = new ObjectMapper(); - VisibilityChecker vc = m.getVisibilityChecker(); - vc = vc.withFieldVisibility(JsonAutoDetect.Visibility.ANY); - m.setVisibility(vc); + ObjectMapper m = jsonMapperBuilder() + .changeDefaultVisibility(vc -> + vc.withFieldVisibility(JsonAutoDetect.Visibility.ANY)) + .build(); Map result = writeAndMap(m, new FieldBean()); assertEquals(3, result.size()); @@ -82,10 +81,11 @@ public void testPrivateUsingGlobals() throws Exception assertEquals("protected", result.get("p2")); assertEquals("private", result.get("p3")); - m = new ObjectMapper(); - vc = m.getVisibilityChecker(); - vc = vc.withGetterVisibility(JsonAutoDetect.Visibility.ANY); - m.setVisibility(vc); + m = jsonMapperBuilder() + .changeDefaultVisibility(vc -> + vc.withGetterVisibility(JsonAutoDetect.Visibility.ANY) + ) + .build(); result = writeAndMap(m, new MethodBean()); assertEquals(3, result.size()); assertEquals("a", result.get("a")); @@ -96,12 +96,11 @@ public void testPrivateUsingGlobals() throws Exception // [JACKSON-621] public void testBasicSetup() throws Exception { - ObjectMapper m = new ObjectMapper(); - VisibilityChecker vc = m.getVisibilityChecker(); - vc = vc.with(JsonAutoDetect.Visibility.ANY); - m.setVisibility(vc); - - Map result = writeAndMap(m, new FieldBean()); + ObjectMapper mapper = jsonMapperBuilder() + .changeDefaultVisibility(vc -> + vc.with(JsonAutoDetect.Visibility.ANY)) + .build(); + Map result = writeAndMap(mapper, new FieldBean()); assertEquals(3, result.size()); assertEquals("public", result.get("p1")); assertEquals("protected", result.get("p2")); @@ -111,10 +110,12 @@ public void testBasicSetup() throws Exception // [JACKSON-595] public void testMapperShortcutMethods() throws Exception { - ObjectMapper m = new ObjectMapper(); - m.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); + ObjectMapper mapper = jsonMapperBuilder() + .changeDefaultVisibility(vc -> vc + .withVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)) + .build(); - Map result = writeAndMap(m, new FieldBean()); + Map result = writeAndMap(mapper, new FieldBean()); assertEquals(3, result.size()); assertEquals("public", result.get("p1")); assertEquals("protected", result.get("p2")); 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 1feeba634b..d36f0841a6 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/TestCustomSerializers.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestCustomSerializers.java @@ -17,6 +17,7 @@ import com.fasterxml.jackson.databind.ser.std.CollectionSerializer; import com.fasterxml.jackson.databind.ser.std.StdDelegatingSerializer; import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; import com.fasterxml.jackson.databind.util.StdConverter; /** @@ -25,8 +26,9 @@ @SuppressWarnings("serial") public class TestCustomSerializers extends BaseMapTest { - static class ElementSerializer extends JsonSerializer + static class ElementSerializer extends StdSerializer { + public ElementSerializer() { super(Element.class); } @Override public void serialize(Element value, JsonGenerator gen, SerializerProvider provider) throws IOException, JsonProcessingException { gen.writeString("element"); @@ -156,18 +158,18 @@ public StringListWrapper(String... values) { public void testCustomization() throws Exception { - ObjectMapper objectMapper = new ObjectMapper(); - objectMapper.addMixIn(Element.class, ElementMixin.class); + ObjectMapper mapper = jsonMapperBuilder() + .addMixIn(Element.class, ElementMixin.class) + .build(); Element element = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument().createElement("el"); StringWriter sw = new StringWriter(); - objectMapper.writeValue(sw, element); + mapper.writeValue(sw, element); assertEquals(sw.toString(), "\"element\""); } @SuppressWarnings({ "unchecked", "rawtypes" }) public void testCustomLists() throws Exception { - ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule("test", Version.unknownVersion()); JsonSerializer ser = new CollectionSerializer(null, false, null, null); final JsonSerializer collectionSerializer = (JsonSerializer) ser; @@ -183,15 +185,19 @@ public void serialize(Collection value, JsonGenerator gen, SerializerProvider pr gen.writeNull(); } } + + @Override + public Class handledType() { return Collection.class; } }); - mapper.registerModule(module); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(module) + .build(); assertEquals("null", mapper.writeValueAsString(new ArrayList())); } // [databind#87]: delegating serializer public void testDelegating() throws Exception { - ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule("test", Version.unknownVersion()); module.addSerializer(new StdDelegatingSerializer(Immutable.class, new StdConverter>() { @@ -204,7 +210,9 @@ public Map convert(Immutable value) return map; } })); - mapper.registerModule(module); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(module) + .build(); assertEquals("{\"x\":3,\"y\":7}", mapper.writeValueAsString(new Immutable())); } @@ -238,8 +246,9 @@ public void testWithCustomElements() throws Exception SimpleModule module = new SimpleModule("test", Version.unknownVersion()); module.addSerializer(String.class, new UCStringSerializer()); - ObjectMapper mapper = new ObjectMapper() - .registerModule(module); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(module) + .build(); assertEquals(quote("FOOBAR"), mapper.writeValueAsString("foobar")); assertEquals(aposToQuotes("['FOO',null]"), 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 c27c5ad623..2b9020e1af 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/TestEmptyClass.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestEmptyClass.java @@ -54,8 +54,8 @@ public boolean isEmpty(SerializerProvider provider, NonZero value) { /********************************************************** */ - protected final ObjectMapper mapper = new ObjectMapper(); - + protected final ObjectMapper MAPPER = objectMapper(); + /** * Test to check that [JACKSON-201] works if there is a recognized * annotation (which indicates type is serializable) @@ -64,17 +64,19 @@ public void testEmptyWithAnnotations() throws Exception { // First: without annotations, should complain try { - serializeAsString(mapper, new Empty()); + MAPPER.writeValueAsString(new Empty()); + fail("Should fail"); } catch (JsonMappingException e) { verifyException(e, "No serializer found for class"); } // But not if there is a recognized annotation - assertEquals("{}", serializeAsString(mapper, new EmptyWithAnno())); + assertEquals("{}", MAPPER.writeValueAsString(new EmptyWithAnno())); // Including class annotation through mix-ins - ObjectMapper m2 = new ObjectMapper(); - m2.addMixIn(Empty.class, EmptyWithAnno.class); + ObjectMapper m2 = jsonMapperBuilder() + .addMixIn(Empty.class, EmptyWithAnno.class) + .build(); assertEquals("{}", m2.writeValueAsString(new Empty())); } @@ -85,17 +87,18 @@ public void testEmptyWithAnnotations() throws Exception public void testEmptyWithFeature() throws Exception { // should be enabled by default - assertTrue(mapper.getSerializationConfig().isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS)); - mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); - assertEquals("{}", serializeAsString(mapper, new Empty())); + assertTrue(MAPPER.isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS)); + assertEquals("{}", + MAPPER.writer() + .without(SerializationFeature.FAIL_ON_EMPTY_BEANS) + .writeValueAsString(new Empty())); } - // [JACKSON-695], JsonSerializer.isEmpty() public void testCustomNoEmpty() throws Exception { // first non-empty: - assertEquals("{\"value\":123}", mapper.writeValueAsString(new NonZeroWrapper(123))); + assertEquals("{\"value\":123}", MAPPER.writeValueAsString(new NonZeroWrapper(123))); // then empty: - assertEquals("{}", mapper.writeValueAsString(new NonZeroWrapper(0))); + assertEquals("{}", MAPPER.writeValueAsString(new NonZeroWrapper(0))); } } 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 d2120b9347..63b3e43130 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/TestEnumSerialization.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestEnumSerialization.java @@ -20,12 +20,6 @@ public class TestEnumSerialization extends BaseMapTest { - /* - /********************************************************** - /* Helper enums - /********************************************************** - */ - /** * Test enumeration for verifying Enum serialization functionality. */ @@ -214,7 +208,6 @@ public void testEnumUsingToString() throws Exception assertEquals("\"c2\"", sw.toString()); } - // Test [JACKSON-214] public void testSubclassedEnums() throws Exception { assertEquals("\"B\"", MAPPER.writeValueAsString(EnumWithSubClass.B)); @@ -227,9 +220,10 @@ public void testEnumsWithJsonValue() throws Exception { public void testEnumsWithJsonValueUsingMixin() throws Exception { // can't share, as new mix-ins are added - ObjectMapper m = new ObjectMapper(); - m.addMixIn(TestEnum.class, ToStringMixin.class); - assertEquals("\"b\"", m.writeValueAsString(TestEnum.B)); + ObjectMapper mapper = jsonMapperBuilder() + .addMixIn(TestEnum.class, ToStringMixin.class) + .build(); + assertEquals("\"b\"", mapper.writeValueAsString(TestEnum.B)); } // [databind#601] @@ -250,11 +244,11 @@ public void testSerializableEnum() throws Exception assertEquals("\"foo\"", MAPPER.writeValueAsString(SerializableEnum.A)); } - // [JACKSON-212] public void testToStringEnum() throws Exception { - ObjectMapper m = new ObjectMapper(); - m.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true); + ObjectMapper m = jsonMapperBuilder() + .configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true) + .build(); assertEquals("\"b\"", m.writeValueAsString(LowerCaseEnum.B)); // [databind#749] but should also be able to dynamically disable @@ -265,8 +259,9 @@ public void testToStringEnum() throws Exception public void testToStringEnumWithEnumMap() throws Exception { - ObjectMapper m = new ObjectMapper(); - m.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING); + ObjectMapper m = jsonMapperBuilder() + .enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING) + .build(); EnumMap enums = new EnumMap(LowerCaseEnum.class); enums.put(LowerCaseEnum.C, "value"); assertEquals("{\"c\":\"value\"}", m.writeValueAsString(enums)); @@ -302,11 +297,12 @@ public void testAsIndex() throws Exception assertEquals(quote("B"), m.writeValueAsString(TestEnum.B)); // but we can change (dynamically, too!) it to be number-based - m.enable(SerializationFeature.WRITE_ENUMS_USING_INDEX); + m = jsonMapperBuilder() + .enable(SerializationFeature.WRITE_ENUMS_USING_INDEX) + .build(); assertEquals("1", m.writeValueAsString(TestEnum.B)); } - // [JACKSON-757] public void testAnnotationsOnEnumCtor() throws Exception { assertEquals(quote("V1"), MAPPER.writeValueAsString(OK.V1)); @@ -314,15 +310,15 @@ public void testAnnotationsOnEnumCtor() throws Exception assertEquals(quote("V2"), MAPPER.writeValueAsString(NOT_OK2.V2)); } - // [Issue#227] public void testGenericEnumSerializer() throws Exception { // By default, serialize using name - ObjectMapper m = new ObjectMapper(); SimpleModule module = new SimpleModule("foobar"); module.addSerializer(Enum.class, new LowerCasingEnumSerializer()); - m.registerModule(module); - assertEquals(quote("b"), m.writeValueAsString(TestEnum.B)); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(module) + .build(); + assertEquals(quote("b"), mapper.writeValueAsString(TestEnum.B)); } // [databind#594] diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestIterable.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestIterable.java index fdd7cd7774..38dfbf948f 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/TestIterable.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestIterable.java @@ -100,16 +100,16 @@ public void serialize(A a, JsonGenerator jsonGenerator, SerializerProvider provi } /* - /********************************************************** + /********************************************************************** /* Test methods - /********************************************************** + /********************************************************************** */ private final ObjectMapper MAPPER = new ObjectMapper(); - private final ObjectMapper STATIC_MAPPER = objectMapperBuilder() - .enable(MapperFeature.USE_STATIC_TYPING) - .build(); + private final ObjectMapper STATIC_MAPPER = jsonMapperBuilder() + .enable(MapperFeature.USE_STATIC_TYPING) + .build(); public void testIterator() throws IOException { @@ -136,6 +136,18 @@ public void testWithIterable() throws IOException STATIC_MAPPER.writeValueAsString(new BeanWithIterable())); assertEquals("[1,2,3]", STATIC_MAPPER.writeValueAsString(new IntIterable())); + + assertEquals("{\"values\":[\"value\"]}", + MAPPER.writeValueAsString(new BeanWithIterable())); + assertEquals("[1,2,3]", + MAPPER.writeValueAsString(new IntIterable())); + + // 17-Apr-2018, tatu: Turns out we may need "fresh" mapper for some failures? + ObjectMapper freshMapper = new ObjectMapper(); + assertEquals("{\"values\":[\"value\"]}", + freshMapper.writeValueAsString(new BeanWithIterable())); + assertEquals("[1,2,3]", + freshMapper.writeValueAsString(new IntIterable())); } public void testWithIterator() throws IOException diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestJacksonTypes.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestJacksonTypes.java index 3e9d1a3964..780df4ee14 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/TestJacksonTypes.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestJacksonTypes.java @@ -13,12 +13,13 @@ public class TestJacksonTypes extends BaseMapTest { + private final ObjectMapper MAPPER = objectMapper(); + public void testLocation() throws IOException { File f = new File("/tmp/test.json"); JsonLocation loc = new JsonLocation(f, -1, 100, 13); - ObjectMapper mapper = new ObjectMapper(); - Map result = writeAndMap(mapper, loc); + Map result = writeAndMap(MAPPER, loc); assertEquals(5, result.size()); assertEquals(f.getAbsolutePath(), result.get("sourceRef")); assertEquals(Integer.valueOf(-1), result.get("charOffset")); @@ -35,14 +36,14 @@ public void testLocation() throws IOException public void testTokenBuffer() throws Exception { // First, copy events from known good source (StringReader) - JsonParser jp = createParserUsingReader(SAMPLE_DOC_JSON_SPEC); - TokenBuffer tb = new TokenBuffer(null, false); - while (jp.nextToken() != null) { - tb.copyCurrentEvent(jp); + JsonParser p = createParserUsingReader(SAMPLE_DOC_JSON_SPEC); + TokenBuffer tb = TokenBuffer.forGeneration(); + while (p.nextToken() != null) { + tb.copyCurrentEvent(p); } - jp.close(); + p.close(); // Then serialize as String - String str = serializeAsString(tb); + String str = MAPPER.writeValueAsString(tb); tb.close(); // and verify it looks ok verifyJsonSpecSampleDoc(createParserUsingReader(str), true); diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonSerialize.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonSerialize.java index 248fa38997..212ed1003a 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonSerialize.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonSerialize.java @@ -8,20 +8,16 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; /** * This unit test suite tests use of @JsonClass Annotation * with bean serialization. */ +@SuppressWarnings("serial") public class TestJsonSerialize extends BaseMapTest { - /* - /********************************************************** - /* Annotated helper classes - /********************************************************** - */ - interface ValueInterface { public int getX(); } @@ -80,14 +76,10 @@ public Long getValue() { } } - @SuppressWarnings("serial") static class ValueMap extends HashMap { } - @SuppressWarnings("serial") static class ValueList extends ArrayList { } - @SuppressWarnings("serial") static class ValueLinkedList extends LinkedList { } - - // Classes for [JACKSON-294] + static class Foo294 { @JsonProperty private String id; @@ -114,8 +106,9 @@ public Bar294(String id) { public String getName() { return name; } } - static class Bar294Serializer extends JsonSerializer + static class Bar294Serializer extends StdSerializer { + public Bar294Serializer() { super(Bar294.class); } @Override public void serialize(Bar294 bar, JsonGenerator jgen, SerializerProvider provider) throws IOException @@ -131,6 +124,10 @@ public void serialize(Bar294 bar, JsonGenerator jgen, */ final ObjectMapper MAPPER = objectMapper(); + + private final ObjectMapper STATIC_MAPPER = jsonMapperBuilder() + .enable(MapperFeature.USE_STATIC_TYPING) + .build(); @SuppressWarnings("unchecked") public void testSimpleValueDefinition() throws Exception @@ -147,7 +144,7 @@ public void testSimpleValueDefinition() throws Exception public void testBrokenAnnotation() throws Exception { try { - serializeAsString(MAPPER, new BrokenClass()); + MAPPER.writeValueAsString(new BrokenClass()); fail("Should not succeed"); } catch (Exception e) { verifyException(e, "types not related"); @@ -188,41 +185,29 @@ public void testMixedTypingForClass() throws Exception public void testStaticTypingWithMap() throws Exception { - ObjectMapper m = objectMapperBuilder() - .configure(MapperFeature.USE_STATIC_TYPING, true) - .build(); ValueMap map = new ValueMap(); map.put("a", new ValueClass()); - assertEquals("{\"a\":{\"x\":3}}", serializeAsString(m, map)); + assertEquals("{\"a\":{\"x\":3}}", STATIC_MAPPER.writeValueAsString(map)); } public void testStaticTypingWithArrayList() throws Exception { - ObjectMapper m = objectMapperBuilder() - .configure(MapperFeature.USE_STATIC_TYPING, true) - .build(); ValueList list = new ValueList(); list.add(new ValueClass()); - assertEquals("[{\"x\":3}]", m.writeValueAsString(list)); + assertEquals("[{\"x\":3}]", STATIC_MAPPER.writeValueAsString(list)); } public void testStaticTypingWithLinkedList() throws Exception { - ObjectMapper m = objectMapperBuilder() - .configure(MapperFeature.USE_STATIC_TYPING, true) - .build(); ValueLinkedList list = new ValueLinkedList(); list.add(new ValueClass()); - assertEquals("[{\"x\":3}]", serializeAsString(m, list)); + assertEquals("[{\"x\":3}]", STATIC_MAPPER.writeValueAsString(list)); } public void testStaticTypingWithArray() throws Exception { - ObjectMapper m = objectMapperBuilder() - .configure(MapperFeature.USE_STATIC_TYPING, true) - .build(); ValueInterface[] array = new ValueInterface[] { new ValueClass() }; - assertEquals("[{\"x\":3}]", serializeAsString(m, array)); + assertEquals("[{\"x\":3}]", STATIC_MAPPER.writeValueAsString(array)); } public void testIssue294() throws Exception @@ -241,13 +226,15 @@ static class Response { public void testWithIsGetter() throws Exception { - ObjectMapper m = new ObjectMapper(); - m.setVisibility(PropertyAccessor.GETTER, Visibility.NONE) - .setVisibility(PropertyAccessor.FIELD, Visibility.ANY) - .setVisibility(PropertyAccessor.CREATOR, Visibility.NONE) - .setVisibility(PropertyAccessor.IS_GETTER, Visibility.NONE) - .setVisibility(PropertyAccessor.SETTER, Visibility.NONE); - final String JSON = m.writeValueAsString(new Response()); - assertEquals(aposToQuotes("{'a':'x','something':true}"), JSON); + ObjectMapper mapper = jsonMapperBuilder() + .changeDefaultVisibility(vc -> vc + .withVisibility(PropertyAccessor.GETTER, Visibility.NONE) + .withVisibility(PropertyAccessor.FIELD, Visibility.ANY) + .withVisibility(PropertyAccessor.CREATOR, Visibility.NONE) + .withVisibility(PropertyAccessor.IS_GETTER, Visibility.NONE) + .withVisibility(PropertyAccessor.SETTER, Visibility.NONE)) + .build(); + assertEquals(aposToQuotes("{'a':'x','something':true}"), + mapper.writeValueAsString(new Response())); } } diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonSerialize2.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonSerialize2.java index 314c30ef16..c13692ecae 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonSerialize2.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonSerialize2.java @@ -179,7 +179,9 @@ public void testSerializedAsMapWithPropertyAnnotations2() throws IOException public void testEmptyInclusionContainers() throws IOException { ObjectMapper defMapper = MAPPER; - ObjectMapper inclMapper = new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_EMPTY); + ObjectMapper inclMapper = jsonMapperBuilder() + .changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_EMPTY)) + .build(); ListWrapper list = new ListWrapper(); assertEquals("{\"list\":[]}", defMapper.writeValueAsString(list)); @@ -199,7 +201,6 @@ public void testEmptyInclusionContainers() throws IOException public void testNullSerializer() throws Exception { - String json = MAPPER.writeValueAsString(new NullBean()); - assertEquals("{\"value\":null}", json); + assertEquals("{\"value\":null}", MAPPER.writeValueAsString(new NullBean())); } } diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestKeySerializers.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestKeySerializers.java index dd97392231..4208690813 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/TestKeySerializers.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestKeySerializers.java @@ -9,32 +9,41 @@ import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; +import com.fasterxml.jackson.databind.jsontype.impl.DefaultTypeResolverBuilder; import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +@SuppressWarnings("serial") public class TestKeySerializers extends BaseMapTest { - public static class KarlSerializer extends JsonSerializer + public static class KarlSerializer extends StdSerializer { + public KarlSerializer() { super(String.class); } @Override public void serialize(String value, JsonGenerator gen, SerializerProvider provider) throws IOException { gen.writeFieldName("Karl"); } } - public static class NullKeySerializer extends JsonSerializer + public static class NullKeySerializer extends StdSerializer { private String _null; - public NullKeySerializer(String s) { _null = s; } + public NullKeySerializer(String s) { + super(String.class); + _null = s; + } @Override public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException { gen.writeFieldName(_null); } } - public static class NullValueSerializer extends JsonSerializer + public static class NullValueSerializer extends StdSerializer { private String _null; - public NullValueSerializer(String s) { _null = s; } + public NullValueSerializer(String s) { super(String.class); + _null = s; + } @Override public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException { gen.writeString(_null); @@ -71,7 +80,8 @@ public String toLC() { } } - static class ABCKeySerializer extends JsonSerializer { + static class ABCKeySerializer extends StdSerializer { + public ABCKeySerializer() { super(ABC.class); } @Override public void serialize(ABC value, JsonGenerator gen, SerializerProvider provider) throws IOException { @@ -159,20 +169,23 @@ public void testBoth() throws IOException public void testCustomForEnum() throws IOException { // cannot use shared mapper as we are registering a module - final ObjectMapper mapper = new ObjectMapper(); SimpleModule mod = new SimpleModule("test"); mod.addKeySerializer(ABC.class, new ABCKeySerializer()); - mapper.registerModule(mod); - + ObjectMapper mapper = jsonMapperBuilder() + .addModule(mod) + .build(); String json = mapper.writeValueAsString(new ABCMapWrapper()); assertEquals("{\"stuff\":{\"xxxB\":\"bar\"}}", json); } public void testCustomNullSerializers() throws IOException { - final ObjectMapper mapper = new ObjectMapper(); - mapper.getSerializerProvider().setNullKeySerializer(new NullKeySerializer("NULL-KEY")); - mapper.getSerializerProvider().setNullValueSerializer(new NullValueSerializer("NULL")); + final ObjectMapper mapper = jsonMapperBuilder() + .addModule(new SimpleModule() + .setDefaultNullKeySerializer(new NullKeySerializer("NULL-KEY")) + .setDefaultNullValueSerializer(new NullValueSerializer("NULL")) + ) + .build(); Map input = new HashMap<>(); input.put(null, 3); String json = mapper.writeValueAsString(input); @@ -188,12 +201,12 @@ public void testCustomEnumInnerMapKey() throws Exception { innerMap.put("one", "1"); map.put(ABC.A, innerMap); outerMap.put(Outer.inner, map); - final ObjectMapper mapper = new ObjectMapper(); SimpleModule mod = new SimpleModule("test"); mod.setMixInAnnotation(ABC.class, ABCMixin.class); mod.addKeySerializer(ABC.class, new ABCKeySerializer()); - mapper.registerModule(mod); - + ObjectMapper mapper = jsonMapperBuilder() + .addModule(mod) + .build(); JsonNode tree = mapper.convertValue(outerMap, JsonNode.class); JsonNode innerNode = tree.get("inner"); @@ -203,17 +216,15 @@ public void testCustomEnumInnerMapKey() throws Exception { // [databind#838] public void testUnWrappedMapWithDefaultType() throws Exception{ - final ObjectMapper mapper = new ObjectMapper(); SimpleModule mod = new SimpleModule("test"); mod.addKeySerializer(ABC.class, new ABCKeySerializer()); - mapper.registerModule(mod); - - TypeResolverBuilder typer = new ObjectMapper.DefaultTypeResolverBuilder(ObjectMapper.DefaultTyping.NON_FINAL); - typer = typer.init(JsonTypeInfo.Id.NAME, null); - typer = typer.inclusion(JsonTypeInfo.As.PROPERTY); - //typer = typer.typeProperty(TYPE_FIELD); - typer = typer.typeIdVisibility(true); - mapper.setDefaultTyping(typer); + TypeResolverBuilder typer = new DefaultTypeResolverBuilder(DefaultTyping.NON_FINAL, + JsonTypeInfo.As.PROPERTY, JsonTypeInfo.Id.NAME, null) + .typeIdVisibility(true); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(mod) + .setDefaultTyping(typer) + .build(); Map stuff = new HashMap(); stuff.put(ABC.B, "bar"); @@ -223,18 +234,16 @@ public void testUnWrappedMapWithDefaultType() throws Exception{ } // [databind#838] - @SuppressWarnings("deprecation") public void testUnWrappedMapWithKeySerializer() throws Exception{ SimpleModule mod = new SimpleModule("test"); mod.addKeySerializer(ABC.class, new ABCKeySerializer()); - final ObjectMapper mapper = new ObjectMapper() - .registerModule(mod) - .enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT) - .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) - .disable(SerializationFeature.WRITE_NULL_MAP_VALUES) - .setSerializationInclusion(JsonInclude.Include.NON_EMPTY) + final ObjectMapper mapper = jsonMapperBuilder() + .changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_EMPTY)) + .enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .addModule(mod) + .build() ; - Map> stuff = new HashMap>(); stuff.put(ABC.B, new BAR("bar")); String json = mapper.writerFor(new TypeReference>>() {}) 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 26cabd6acf..2af6099c69 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/TestMapSerialization.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestMapSerialization.java @@ -9,7 +9,6 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping; import com.fasterxml.jackson.databind.annotation.JsonSerialize; @SuppressWarnings("serial") @@ -186,11 +185,13 @@ public void testMapEntry() throws IOException assertEquals(aposToQuotes("[{'answer':42}]"), json); // and maybe with bit of extra typing? - ObjectMapper mapper = new ObjectMapper().enableDefaultTyping(DefaultTyping.NON_FINAL); + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.NON_FINAL) + .build(); json = mapper.writeValueAsString(input); assertEquals(aposToQuotes("['"+StringIntMapEntry.class.getName()+"',{'answer':42}]"), json); - } + } public void testMapEntryWrapper() throws IOException { @@ -216,9 +217,10 @@ public void testNullJsonMapping691() throws Exception public void testNullJsonInTypedMap691() throws Exception { Map map = new HashMap(); map.put("NULL", null); - - ObjectMapper mapper = new ObjectMapper(); - mapper.addMixIn(Object.class, Mixin691.class); + + ObjectMapper mapper = jsonMapperBuilder() + .addMixIn(Object.class, Mixin691.class) + .build(); String json = mapper.writeValueAsString(map); assertEquals("{\"@class\":\"java.util.HashMap\",\"NULL\":null}", json); } diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestRootType.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestRootType.java index 984207a670..832e7c1e40 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/TestRootType.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestRootType.java @@ -70,11 +70,10 @@ static class TestCommandChild extends TestCommandParent { } /********************************************************** */ - final ObjectMapper WRAP_ROOT_MAPPER = new ObjectMapper(); - { - WRAP_ROOT_MAPPER.configure(SerializationFeature.WRAP_ROOT_VALUE, true); - } - + final ObjectMapper WRAP_ROOT_MAPPER = jsonMapperBuilder() + .enable(SerializationFeature.WRAP_ROOT_VALUE) + .build(); + @SuppressWarnings("unchecked") public void testSuperClass() throws Exception { @@ -114,9 +113,9 @@ public void testSuperInterface() throws Exception public void testInArray() throws Exception { - ObjectMapper mapper = objectMapperBuilder() // must force static typing, otherwise won't matter a lot - .configure(MapperFeature.USE_STATIC_TYPING, true) + ObjectMapper mapper = jsonMapperBuilder() + .enable(MapperFeature.USE_STATIC_TYPING) .build(); SubType[] ob = new SubType[] { new SubType() }; String json = mapper.writerFor(BaseInterface[].class).writeValueAsString(ob); @@ -165,8 +164,7 @@ public void testJackson398() throws Exception assertEquals(EXP, json); StringWriter out = new StringWriter(); - JsonFactory f = new JsonFactory(); - mapper.writerFor(collectionType).writeValue(f.createGenerator(out), typedList); + mapper.writerFor(collectionType).writeValue(mapper.createGenerator(out), typedList); assertEquals(EXP, out.toString()); } diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestConfig.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestSerConfig.java similarity index 69% rename from src/test/java/com/fasterxml/jackson/databind/ser/TestConfig.java rename to src/test/java/com/fasterxml/jackson/databind/ser/TestSerConfig.java index b605b74ff5..ec72a59118 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/TestConfig.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestSerConfig.java @@ -5,15 +5,15 @@ import java.util.*; import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.core.JsonGenerator; + import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.introspect.ClassIntrospector; +import com.fasterxml.jackson.databind.cfg.SerializationContexts; /** * Unit tests for checking handling of SerializationConfig. */ -public class TestConfig +public class TestSerConfig extends BaseMapTest { /* @@ -68,42 +68,18 @@ public void testEnumIndexes() public void testDefaults() { - SerializationConfig cfg = MAPPER.getSerializationConfig(); + SerializationConfig cfg = MAPPER.serializationConfig(); // First, defaults: assertTrue(cfg.isEnabled(MapperFeature.USE_ANNOTATIONS)); - assertTrue(cfg.isEnabled(MapperFeature.AUTO_DETECT_GETTERS)); assertTrue(cfg.isEnabled(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS)); assertTrue(cfg.isEnabled(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)); assertFalse(cfg.isEnabled(SerializationFeature.INDENT_OUTPUT)); assertFalse(cfg.isEnabled(MapperFeature.USE_STATIC_TYPING)); - - // since 1.3: - assertTrue(cfg.isEnabled(MapperFeature.AUTO_DETECT_IS_GETTERS)); - // since 1.4 - assertTrue(cfg.isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS)); - // since 1.5 assertTrue(cfg.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION)); - - } - - public void testOverrideIntrospectors() - { - SerializationConfig cfg = MAPPER.getSerializationConfig(); - // and finally, ensure we could override introspectors - cfg = cfg.with((ClassIntrospector) null); // no way to verify tho - cfg = cfg.with((AnnotationIntrospector) null); - assertNull(cfg.getAnnotationIntrospector()); - } - - public void testMisc() - { - ObjectMapper m = new ObjectMapper(); - m.setDateFormat(null); // just to execute the code path - assertNotNull(m.getSerializationConfig().toString()); // ditto } public void testIndentation() throws Exception @@ -124,20 +100,27 @@ public void testAnnotationsDisabled() throws Exception Map result = writeAndMap(MAPPER, new AnnoBean()); assertEquals(2, result.size()); - ObjectMapper m2 = objectMapperBuilder() + ObjectMapper m2 = jsonMapperBuilder() .configure(MapperFeature.USE_ANNOTATIONS, false) .build(); result = writeAndMap(m2, new AnnoBean()); assertEquals(1, result.size()); } + @SuppressWarnings("serial") + static class TestObjectMapper + extends ObjectMapper + { + public SerializationContexts getSerializationContexts() { return _serializationContexts; } + } + /** - * Test for verifying working of [JACKSON-191] + * Test for verifying some aspects of serializer caching */ public void testProviderConfig() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - DefaultSerializerProvider prov = (DefaultSerializerProvider) mapper.getSerializerProvider(); + TestObjectMapper mapper = new TestObjectMapper(); + SerializationContexts prov = mapper.getSerializationContexts(); assertEquals(0, prov.cachedSerializersCount()); // and then should get one constructed for: Map result = this.writeAndMap(mapper, new AnnoBean()); @@ -145,21 +128,17 @@ public void testProviderConfig() throws Exception assertEquals(Integer.valueOf(1), result.get("x")); assertEquals(Integer.valueOf(2), result.get("y")); - /* Note: it is 2 because we'll also get serializer for basic 'int', not - * just AnnoBean - */ - /* 12-Jan-2010, tatus: Actually, probably more, if and when we typing - * aspects are considered (depending on what is cached) - */ + // Note: it is 2 because we'll also get serializer for basic 'int', not just AnnoBean + // 12-Jan-2010, tatus: Actually, probably more, if and when we typing + // aspects are considered (depending on what is cached) int count = prov.cachedSerializersCount(); - if (count < 2) { + if (count < 2 || count > 10) { fail("Should have at least 2 cached serializers, got "+count); } prov.flushCachedSerializers(); assertEquals(0, prov.cachedSerializersCount()); } - // Test for [Issue#12] public void testIndentWithPassedGenerator() throws Exception { Indentable input = new Indentable(); @@ -169,47 +148,45 @@ public void testIndentWithPassedGenerator() throws Exception final ObjectWriter indentWriter = MAPPER.writer().with(SerializationFeature.INDENT_OUTPUT); assertEquals(INDENTED, indentWriter.writeValueAsString(input)); - // [Issue#12] StringWriter sw = new StringWriter(); - JsonGenerator jgen = MAPPER.getFactory().createGenerator(sw); - indentWriter.writeValue(jgen, input); - jgen.close(); + indentWriter.writeValue(sw, input); assertEquals(INDENTED, sw.toString()); // and also with ObjectMapper itself sw = new StringWriter(); - ObjectMapper m2 = new ObjectMapper(); - m2.enable(SerializationFeature.INDENT_OUTPUT); - jgen = m2.getFactory().createGenerator(sw); - m2.writeValue(jgen, input); - jgen.close(); + ObjectMapper m2 = jsonMapperBuilder() + .enable(SerializationFeature.INDENT_OUTPUT) + .build(); + m2.writeValue(sw, input); assertEquals(INDENTED, sw.toString()); } public void testNoAccessOverrides() throws Exception { - ObjectMapper m = objectMapperBuilder() - .disable(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS) - .build(); + ObjectMapper m = jsonMapperBuilder() + .disable(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS) + .build(); assertEquals("{\"x\":1}", m.writeValueAsString(new SimpleBean())); } public void testDateFormatConfig() throws Exception { - ObjectMapper mapper = new ObjectMapper(); TimeZone tz1 = TimeZone.getTimeZone("America/Los_Angeles"); - TimeZone tz2 = TimeZone.getTimeZone("Central Standard Time"); + TimeZone tz2 = TimeZone.getTimeZone("CST"); // sanity checks assertEquals(tz1, tz1); assertEquals(tz2, tz2); if (tz1.equals(tz2)) { - fail(); + fail("Should not be equal"); } - mapper.setTimeZone(tz1); - assertEquals(tz1, mapper.getSerializationConfig().getTimeZone()); - assertEquals(tz1, mapper.getDeserializationConfig().getTimeZone()); + ObjectMapper mapper = jsonMapperBuilder() + .defaultTimeZone(tz1) + .build(); + + assertEquals(tz1, mapper.serializationConfig().getTimeZone()); + assertEquals(tz1, mapper.deserializationConfig().getTimeZone()); // also better stick via reader/writer as well assertEquals(tz1, mapper.writer().getConfig().getTimeZone()); @@ -217,11 +194,15 @@ public void testDateFormatConfig() throws Exception SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); f.setTimeZone(tz2); - mapper.setDateFormat(f); + mapper = jsonMapperBuilder() + .defaultTimeZone(tz1) + .defaultDateFormat(f) + .build(); + // should not change the timezone tho - assertEquals(tz1, mapper.getSerializationConfig().getTimeZone()); - assertEquals(tz1, mapper.getDeserializationConfig().getTimeZone()); + assertEquals(tz1, mapper.serializationConfig().getTimeZone()); + assertEquals(tz1, mapper.deserializationConfig().getTimeZone()); assertEquals(tz1, mapper.writer().getConfig().getTimeZone()); assertEquals(tz1, mapper.reader().getConfig().getTimeZone()); } 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 6cbcd2bb61..52e1d3667a 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/TestSerializationOrder.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestSerializationOrder.java @@ -94,9 +94,8 @@ public BeanForGH311(@JsonProperty("b") int b, @JsonProperty("a") int a) { //b an /********************************************* */ - final ObjectMapper MAPPER = new ObjectMapper(); - - // Test for [JACKSON-170] + private final ObjectMapper MAPPER = objectMapper(); + public void testImplicitOrderByCreator() throws Exception { assertEquals("{\"c\":1,\"a\":2,\"b\":0}", MAPPER.writeValueAsString(new BeanWithCreator(1, 2))); @@ -115,11 +114,11 @@ public void testAlphabeticOrder() throws Exception public void testOrderWithMixins() throws Exception { - ObjectMapper m = objectMapperBuilder() + ObjectMapper mapper = jsonMapperBuilder() .addMixIn(BeanWithOrder.class, OrderMixIn.class) .build(); assertEquals("{\"b\":2,\"a\":1,\"c\":3,\"d\":4}", - serializeAsString(m, new BeanWithOrder(1, 2, 3, 4))); + mapper.writeValueAsString(new BeanWithOrder(1, 2, 3, 4))); } public void testOrderWrt268() throws Exception @@ -130,21 +129,19 @@ public void testOrderWrt268() throws Exception public void testOrderWithFeature() throws Exception { - ObjectMapper m = objectMapperBuilder() - .configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true) + ObjectMapper mapper = jsonMapperBuilder() + .enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY) .build(); assertEquals("{\"a\":1,\"b\":2,\"c\":3,\"d\":4}", - m.writeValueAsString(new BeanFor459())); + mapper.writeValueAsString(new BeanFor459())); } - // [Issue#311] - public void testAlphaAndCreatorOrdering() throws Exception { - ObjectMapper m = objectMapperBuilder() - .configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true) + ObjectMapper mapper = jsonMapperBuilder() + .enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY) .build(); - String json = m.writeValueAsString(new BeanForGH311(2, 1)); + String json = mapper.writeValueAsString(new BeanForGH311(2, 1)); assertEquals("{\"a\":1,\"b\":2}", json); } } diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestSerializerProvider.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestSerializerProvider.java deleted file mode 100644 index f690c27293..0000000000 --- a/src/test/java/com/fasterxml/jackson/databind/ser/TestSerializerProvider.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.fasterxml.jackson.databind.ser; - -import java.util.concurrent.atomic.AtomicReference; - -import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.ser.BeanSerializerFactory; - -public class TestSerializerProvider - extends com.fasterxml.jackson.databind.BaseMapTest -{ - static class MyBean { - public int getX() { return 3; } - } - - static class NoPropsBean { - } - - public void testFindExplicit() throws JsonMappingException - { - ObjectMapper mapper = new ObjectMapper(); - SerializationConfig config = mapper.getSerializationConfig(); - SerializerFactory f = new BeanSerializerFactory(null); - DefaultSerializerProvider prov = new DefaultSerializerProvider.Impl().createInstance(config, f); - - // Should have working default key and null key serializers - assertNotNull(prov.findKeySerializer(mapper.constructType(String.class), null)); - assertNotNull(prov.getDefaultNullKeySerializer()); - assertNotNull(prov.getDefaultNullValueSerializer()); - // as well as 'unknown type' one (throws exception) - assertNotNull(prov.getUnknownTypeSerializer(getClass())); - - assertTrue(prov.createInstance(config, f).hasSerializerFor(String.class, null)); - // call twice to verify it'll be cached (second code path) - assertTrue(prov.createInstance(config, f).hasSerializerFor(String.class, null)); - - assertTrue(prov.createInstance(config, f).hasSerializerFor(MyBean.class, null)); - assertTrue(prov.createInstance(config, f).hasSerializerFor(MyBean.class, null)); - - // And then some negative testing - AtomicReference cause = new AtomicReference(); - assertFalse(prov.createInstance(config, f).hasSerializerFor(NoPropsBean.class, cause)); - Throwable t = cause.get(); - // no actual exception: just fails since there are no properties - assertNull(t); - } -} diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestSimpleTypes.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestSimpleTypes.java index 7d39b0f162..d9cba40f90 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/TestSimpleTypes.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestSimpleTypes.java @@ -4,8 +4,6 @@ import com.fasterxml.jackson.databind.BaseMapTest; import com.fasterxml.jackson.databind.ObjectMapper; -import static org.junit.Assert.*; - /** * Unit tests for verifying serialization of simple basic non-structured * types; primitives (and/or their wrappers), Strings. @@ -17,14 +15,14 @@ public class TestSimpleTypes public void testBoolean() throws Exception { - assertEquals("true", serializeAsString(MAPPER, Boolean.TRUE)); - assertEquals("false", serializeAsString(MAPPER, Boolean.FALSE)); + assertEquals("true", MAPPER.writeValueAsString(Boolean.TRUE)); + assertEquals("false", MAPPER.writeValueAsString(Boolean.FALSE)); } public void testBooleanArray() throws Exception { - assertEquals("[true,false]", serializeAsString(MAPPER, new boolean[] { true, false} )); - assertEquals("[true,false]", serializeAsString(MAPPER, new Boolean[] { Boolean.TRUE, Boolean.FALSE} )); + assertEquals("[true,false]", MAPPER.writeValueAsString(new boolean[] { true, false} )); + assertEquals("[true,false]", MAPPER.writeValueAsString(new Boolean[] { Boolean.TRUE, Boolean.FALSE} )); } public void testByteArray() throws Exception @@ -35,8 +33,8 @@ public void testByteArray() throws Exception data2[i] = data[i]; // auto-boxing } // For this we need to deserialize, to get base64 codec - String str1 = serializeAsString(MAPPER, data); - String str2 = serializeAsString(MAPPER, data2); + String str1 = MAPPER.writeValueAsString(data); + String str2 = MAPPER.writeValueAsString(data2); assertArrayEquals(data, MAPPER.readValue(str1, byte[].class)); assertArrayEquals(data2, MAPPER.readValue(str2, Byte[].class)); } @@ -63,14 +61,14 @@ public void testBase64Variants() throws Exception public void testShortArray() throws Exception { - assertEquals("[0,1]", serializeAsString(MAPPER, new short[] { 0, 1 })); - assertEquals("[2,3]", serializeAsString(MAPPER, new Short[] { 2, 3 })); + assertEquals("[0,1]", MAPPER.writeValueAsString(new short[] { 0, 1 })); + assertEquals("[2,3]", MAPPER.writeValueAsString(new Short[] { 2, 3 })); } public void testIntArray() throws Exception { - assertEquals("[0,-3]", serializeAsString(MAPPER, new int[] { 0, -3 })); - assertEquals("[13,9]", serializeAsString(MAPPER, new Integer[] { 13, 9 })); + assertEquals("[0,-3]", MAPPER.writeValueAsString(new int[] { 0, -3 })); + assertEquals("[13,9]", MAPPER.writeValueAsString(new Integer[] { 13, 9 })); } /* Note: dealing with floating-point values is tricky; not sure if @@ -89,7 +87,7 @@ public void testFloat() throws Exception if (Float.isNaN(f) || Float.isInfinite(f)) { expected = "\""+expected+"\""; } - assertEquals(expected,serializeAsString(MAPPER, Float.valueOf(f))); + assertEquals(expected, MAPPER.writeValueAsString(Float.valueOf(f))); } } diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestTreeSerialization.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestTreeSerialization.java index b2828755b4..9f812c0f30 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/TestTreeSerialization.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestTreeSerialization.java @@ -33,7 +33,7 @@ public void testSimpleViaObjectMapper() ObjectNode n2 = n.putObject("ob"); n2.putArray("arr"); StringWriter sw = new StringWriter(); - JsonGenerator jg = mapper.getFactory().createGenerator(sw); + JsonGenerator jg = mapper.createGenerator(sw); mapper.writeTree(jg, n); Map result = (Map) mapper.readValue(sw.toString(), Map.class); @@ -61,12 +61,10 @@ public void testPOJOString() ObjectNode n = mapper.getNodeFactory().objectNode(); n.set("pojo", mapper.getNodeFactory().pojoNode("abc")); StringWriter sw = new StringWriter(); - JsonGenerator jg = mapper.getFactory().createGenerator(sw); - mapper.writeTree(jg, n); + mapper.writeValue(sw, n); Map result = (Map) mapper.readValue(sw.toString(), Map.class); assertEquals(1, result.size()); assertEquals("abc", result.get("pojo")); - jg.close(); } @SuppressWarnings("unchecked") @@ -77,8 +75,7 @@ public void testPOJOIntArray() ObjectNode n = mapper.getNodeFactory().objectNode(); n.set("pojo", mapper.getNodeFactory().pojoNode(new int[] { 1, 2, 3 })); StringWriter sw = new StringWriter(); - JsonGenerator jg = mapper.getFactory().createGenerator(sw); - mapper.writeTree(jg, n); + mapper.writeValue(sw, n); Map result = (Map) mapper.readValue(sw.toString(), Map.class); @@ -89,20 +86,17 @@ public void testPOJOIntArray() for (int i = 0; i < 3; ++i) { assertEquals(Integer.valueOf(i+1), list.get(i)); } - jg.close(); } @SuppressWarnings("unchecked") - public void testPOJOBean() - throws IOException + public void testPOJOBean() throws IOException { ObjectMapper mapper = new ObjectMapper(); // also need tree mapper to construct tree to serialize ObjectNode n = mapper.getNodeFactory().objectNode(); n.set("pojo", mapper.getNodeFactory().pojoNode(new Bean())); StringWriter sw = new StringWriter(); - JsonGenerator jg = mapper.getFactory().createGenerator(sw); - mapper.writeTree(jg, n); + mapper.writeValue(sw, n); Map result = (Map) mapper.readValue(sw.toString(), Map.class); @@ -111,6 +105,5 @@ public void testPOJOBean() assertEquals(2, bean.size()); assertEquals("y", bean.get("x")); assertEquals(Integer.valueOf(13), bean.get("y")); - jg.close(); } } diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/filter/IgnorePropsForSerTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/filter/IgnorePropsForSerTest.java index 73c34a48cd..3b95715c50 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/filter/IgnorePropsForSerTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/filter/IgnorePropsForSerTest.java @@ -143,9 +143,10 @@ public void testIgnoreViaPropsAndClass() throws Exception public void testIgnoreViaConfigOverride() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.configOverride(Point.class) - .setIgnorals(JsonIgnoreProperties.Value.forIgnoredProperties("x")); + ObjectMapper mapper = jsonMapperBuilder() + .withConfigOverride(Point.class, + o -> o.setIgnorals(JsonIgnoreProperties.Value.forIgnoredProperties("x"))) + .build(); assertEquals("{\"y\":3}", mapper.writeValueAsString(new Point(2, 3))); } diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/filter/JsonInclude1327Test.java b/src/test/java/com/fasterxml/jackson/databind/ser/filter/JsonInclude1327Test.java index bac6df4630..1bf192454d 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/filter/JsonInclude1327Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/filter/JsonInclude1327Test.java @@ -5,12 +5,10 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; /** * Unit tests for checking that alternative settings for - * {@link JsonSerialize#include} annotation property work - * as expected. + * inclusion annotation properties work as expected. */ public class JsonInclude1327Test extends BaseMapTest @@ -33,9 +31,9 @@ static class Issue1327BeanAlways { // for [databind#1327] public void testClassDefaultsForEmpty() throws Exception { - ObjectMapper om = new ObjectMapper(); - om.setSerializationInclusion(JsonInclude.Include.NON_NULL); - + ObjectMapper om = jsonMapperBuilder() + .changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_NULL)) + .build(); final String jsonString = om.writeValueAsString(new Issue1327BeanEmpty()); if (jsonString.contains("myList")) { @@ -44,11 +42,10 @@ public void testClassDefaultsForEmpty() throws Exception { } public void testClassDefaultsForAlways() throws Exception { - ObjectMapper om = new ObjectMapper(); - om.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); - + ObjectMapper om = jsonMapperBuilder() + .changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_EMPTY)) + .build(); final String jsonString = om.writeValueAsString(new Issue1327BeanAlways()); - if (!jsonString.contains("myList")) { fail("Should contain `myList` with Include.ALWAYS: "+jsonString); } diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/filter/JsonIncludeOverrideTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/filter/JsonIncludeOverrideTest.java index 1a8dbc58ba..7385651ff1 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/filter/JsonIncludeOverrideTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/filter/JsonIncludeOverrideTest.java @@ -13,7 +13,7 @@ /** * Unit tests for checking that overridden settings for - * {@link com.fasterxml.jackson.databind.annotation.JsonSerialize#include} annotation property work + * JsonInclude annotation property work * as expected. */ public class JsonIncludeOverrideTest @@ -62,15 +62,17 @@ public void testPropConfigOverridesForInclude() throws IOException mapper.writeValueAsString(empty)); // and then change inclusion criteria for either - mapper = new ObjectMapper(); - mapper.configOverride(Map.class) - .setInclude(JsonInclude.Value.construct(JsonInclude.Include.NON_EMPTY, null)); + mapper = jsonMapperBuilder() + .withConfigOverride(Map.class, + o -> o.setInclude(JsonInclude.Value.construct(JsonInclude.Include.NON_EMPTY, null))) + .build(); assertEquals(aposToQuotes("{'list':[]}"), mapper.writeValueAsString(empty)); - mapper = new ObjectMapper(); - mapper.configOverride(List.class) - .setInclude(JsonInclude.Value.construct(JsonInclude.Include.NON_EMPTY, null)); + mapper = jsonMapperBuilder() + .withConfigOverride(List.class, + o -> o.setInclude(JsonInclude.Value.construct(JsonInclude.Include.NON_EMPTY, null))) + .build(); assertEquals(aposToQuotes("{'map':{}}"), mapper.writeValueAsString(empty)); } @@ -84,17 +86,19 @@ public void testOverrideForIncludeAsPropertyNonNull() throws Exception mapper.writeValueAsString(nullValues)); // and then change inclusion as property criteria for either - mapper = new ObjectMapper(); - mapper.configOverride(String.class) - .setIncludeAsProperty(JsonInclude.Value - .construct(JsonInclude.Include.NON_NULL, null)); + mapper = jsonMapperBuilder() + .withConfigOverride(String.class, + o -> o.setIncludeAsProperty(JsonInclude.Value + .construct(JsonInclude.Include.NON_NULL, null))) + .build(); assertEquals("{\"num\":null}", mapper.writeValueAsString(nullValues)); - mapper = new ObjectMapper(); - mapper.configOverride(Integer.class) - .setIncludeAsProperty(JsonInclude.Value - .construct(JsonInclude.Include.NON_NULL, null)); + mapper = jsonMapperBuilder() + .withConfigOverride(Integer.class, + o -> o.setIncludeAsProperty(JsonInclude.Value + .construct(JsonInclude.Include.NON_NULL, null))) + .build(); assertEquals("{\"plain\":null}", mapper.writeValueAsString(nullValues)); } @@ -108,17 +112,19 @@ public void testOverrideForIncludeAsPropertyAlways() throws Exception mapper.writeValueAsString(nullValues)); // and then change inclusion as property criteria for either - mapper = new ObjectMapper(); - mapper.configOverride(String.class) - .setIncludeAsProperty(JsonInclude.Value - .construct(JsonInclude.Include.ALWAYS, null)); + mapper = jsonMapperBuilder() + .withConfigOverride(String.class, + o -> o.setIncludeAsProperty(JsonInclude.Value + .construct(JsonInclude.Include.ALWAYS, null))) + .build(); assertEquals(aposToQuotes("{'annotated':null,'plain':null}"), mapper.writeValueAsString(nullValues)); - mapper = new ObjectMapper(); - mapper.configOverride(Integer.class) - .setIncludeAsProperty(JsonInclude.Value - .construct(JsonInclude.Include.ALWAYS, null)); + mapper = jsonMapperBuilder() + .withConfigOverride(Integer.class, + o -> o.setIncludeAsProperty(JsonInclude.Value + .construct(JsonInclude.Include.ALWAYS, null))) + .build(); assertEquals(aposToQuotes("{'num':null,'annotated':null}"), mapper.writeValueAsString(nullValues)); } @@ -127,31 +133,34 @@ public void testOverridesForIncludeAndIncludeAsPropertyNonNull() throws Exceptio { // First, with ALWAYS override on containing bean, all included JsonIncludeOverrideTest.MixedTypeNonNullBean nullValues = new JsonIncludeOverrideTest.MixedTypeNonNullBean(); - ObjectMapper mapper = new ObjectMapper(); - mapper.configOverride(JsonIncludeOverrideTest.MixedTypeNonNullBean.class) - .setInclude(JsonInclude.Value - .construct(JsonInclude.Include.ALWAYS, null)); + ObjectMapper mapper = jsonMapperBuilder() + .withConfigOverride(JsonIncludeOverrideTest.MixedTypeNonNullBean.class, + o -> o.setInclude(JsonInclude.Value + .construct(JsonInclude.Include.ALWAYS, null))) + .build(); assertEquals(aposToQuotes("{'num':null,'annotated':null,'plain':null}"), mapper.writeValueAsString(nullValues)); // and then change inclusion as property criteria for either - mapper = new ObjectMapper(); - mapper.configOverride(JsonIncludeOverrideTest.MixedTypeNonNullBean.class) - .setInclude(JsonInclude.Value - .construct(JsonInclude.Include.ALWAYS, null)); - mapper.configOverride(String.class) - .setIncludeAsProperty(JsonInclude.Value - .construct(JsonInclude.Include.NON_NULL, null)); + mapper = jsonMapperBuilder() + .withConfigOverride(JsonIncludeOverrideTest.MixedTypeNonNullBean.class, + o -> o.setInclude(JsonInclude.Value + .construct(JsonInclude.Include.ALWAYS, null))) + .withConfigOverride(String.class, + o -> o.setIncludeAsProperty(JsonInclude.Value + .construct(JsonInclude.Include.NON_NULL, null))) + .build(); assertEquals(aposToQuotes("{'num':null,'annotated':null}"), mapper.writeValueAsString(nullValues)); - mapper = new ObjectMapper(); - mapper.configOverride(JsonIncludeOverrideTest.MixedTypeNonNullBean.class) - .setInclude(JsonInclude.Value - .construct(JsonInclude.Include.ALWAYS, null)); - mapper.configOverride(Integer.class) - .setIncludeAsProperty(JsonInclude.Value - .construct(JsonInclude.Include.NON_NULL, null)); + mapper = jsonMapperBuilder() + .withConfigOverride(JsonIncludeOverrideTest.MixedTypeNonNullBean.class, + o -> o.setInclude(JsonInclude.Value + .construct(JsonInclude.Include.ALWAYS, null))) + .withConfigOverride(Integer.class, + o -> o.setIncludeAsProperty(JsonInclude.Value + .construct(JsonInclude.Include.NON_NULL, null))) + .build(); assertEquals(aposToQuotes("{'annotated':null,'plain':null}"), mapper.writeValueAsString(nullValues)); } @@ -160,31 +169,34 @@ public void testOverridesForIncludeAndIncludeAsPropertyAlways() throws Exception { // First, with NON_NULL override on containing bean, empty JsonIncludeOverrideTest.MixedTypeAlwaysBean nullValues = new JsonIncludeOverrideTest.MixedTypeAlwaysBean(); - ObjectMapper mapper = new ObjectMapper(); - mapper.configOverride(JsonIncludeOverrideTest.MixedTypeAlwaysBean.class) - .setInclude(JsonInclude.Value - .construct(JsonInclude.Include.NON_NULL, null)); + ObjectMapper mapper = jsonMapperBuilder() + .withConfigOverride(JsonIncludeOverrideTest.MixedTypeAlwaysBean.class, + o -> o.setInclude(JsonInclude.Value + .construct(JsonInclude.Include.NON_NULL, null))) + .build(); assertEquals("{}", mapper.writeValueAsString(nullValues)); // and then change inclusion as property criteria for either - mapper = new ObjectMapper(); - mapper.configOverride(JsonIncludeOverrideTest.MixedTypeAlwaysBean.class) - .setInclude(JsonInclude.Value - .construct(JsonInclude.Include.NON_NULL, null)); - mapper.configOverride(String.class) - .setIncludeAsProperty(JsonInclude.Value - .construct(JsonInclude.Include.ALWAYS, null)); + mapper = jsonMapperBuilder() + .withConfigOverride(JsonIncludeOverrideTest.MixedTypeAlwaysBean.class, + o -> o.setInclude(JsonInclude.Value + .construct(JsonInclude.Include.NON_NULL, null))) + .withConfigOverride(String.class, + o -> o.setIncludeAsProperty(JsonInclude.Value + .construct(JsonInclude.Include.ALWAYS, null))) + .build(); assertEquals("{\"plain\":null}", mapper.writeValueAsString(nullValues)); - mapper = new ObjectMapper(); - mapper.configOverride(JsonIncludeOverrideTest.MixedTypeAlwaysBean.class) - .setInclude(JsonInclude.Value - .construct(JsonInclude.Include.NON_NULL, null)); - mapper.configOverride(Integer.class) - .setIncludeAsProperty(JsonInclude.Value - .construct(JsonInclude.Include.ALWAYS, null)); + mapper = jsonMapperBuilder() + .withConfigOverride(JsonIncludeOverrideTest.MixedTypeAlwaysBean.class, + o -> o.setInclude(JsonInclude.Value + .construct(JsonInclude.Include.NON_NULL, null))) + .withConfigOverride(Integer.class, + o -> o.setIncludeAsProperty(JsonInclude.Value + .construct(JsonInclude.Include.ALWAYS, null))) + .build(); assertEquals("{\"num\":null}", mapper.writeValueAsString(nullValues)); } diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/filter/JsonIncludeTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/filter/JsonIncludeTest.java index 5d18dfff41..3b3c573039 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/filter/JsonIncludeTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/filter/JsonIncludeTest.java @@ -8,11 +8,10 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; /** * Unit tests for checking that alternative settings for - * {@link JsonSerialize#include} annotation property work + * JsonInclude annotation property work * as expected. */ public class JsonIncludeTest @@ -281,7 +280,9 @@ public void testDefaultForIntegers() throws IOException public void testEmptyInclusionScalars() throws IOException { ObjectMapper defMapper = MAPPER; - ObjectMapper inclMapper = new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_EMPTY); + ObjectMapper inclMapper = jsonMapperBuilder() + .changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_EMPTY)) + .build(); // First, Strings StringWrapper str = new StringWrapper(""); @@ -309,8 +310,9 @@ public void testEmptyInclusionScalars() throws IOException // [databind#1351], [databind#1417] public void testIssue1351() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.setSerializationInclusion(JsonInclude.Include.NON_DEFAULT); + ObjectMapper mapper = jsonMapperBuilder() + .changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_DEFAULT)) + .build(); assertEquals(aposToQuotes("{}"), mapper.writeValueAsString(new Issue1351Bean(null, (double) 0))); // [databind#1417] diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/filter/NullSerializationTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/filter/NullSerializationTest.java index d1cf0836b5..1da3f9ca30 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/filter/NullSerializationTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/filter/NullSerializationTest.java @@ -3,17 +3,25 @@ import java.io.*; import com.fasterxml.jackson.core.*; + import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.cfg.GeneratorSettings; +import com.fasterxml.jackson.databind.cfg.SerializationContexts; +import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider; +import com.fasterxml.jackson.databind.ser.SerializerCache; import com.fasterxml.jackson.databind.ser.SerializerFactory; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; public class NullSerializationTest extends BaseMapTest { - static class NullSerializer extends JsonSerializer + @SuppressWarnings("serial") + static class NullSerializer extends StdSerializer { + public NullSerializer() { super(Object.class); } @Override public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException @@ -31,22 +39,35 @@ static class Bean2 { } @SuppressWarnings("serial") - static class MyNullProvider extends DefaultSerializerProvider + static class MyNullSerializerContexts extends SerializationContexts { - public MyNullProvider() { super(); } - public MyNullProvider(MyNullProvider base, SerializationConfig config, SerializerFactory jsf) { - super(base, config, jsf); + public MyNullSerializerContexts() { super(); } + public MyNullSerializerContexts(TokenStreamFactory tsf, SerializerFactory serializerFactory, + SerializerCache cache) { + super(tsf, serializerFactory, cache); } - // not really a proper impl, but has to do @Override - public DefaultSerializerProvider copy() { - return this; + public SerializationContexts forMapper(Object mapper, + TokenStreamFactory tsf, SerializerFactory serializerFactory, + SerializerCache cache) { + return new MyNullSerializerContexts(tsf, serializerFactory, cache); } - + @Override - public DefaultSerializerProvider createInstance(SerializationConfig config, SerializerFactory jsf) { - return new MyNullProvider(this, config, jsf); + public DefaultSerializerProvider createContext(SerializationConfig config, + GeneratorSettings genSettings) { + return new MyNullSerializerProvider(_streamFactory, _cache, + config, genSettings, _serializerFactory); + } + } + + static class MyNullSerializerProvider extends DefaultSerializerProvider + { + public MyNullSerializerProvider(TokenStreamFactory streamFactory, + SerializerCache cache, SerializationConfig config, + GeneratorSettings genSettings, SerializerFactory f) { + super(streamFactory, config, genSettings, f, cache); } @Override @@ -72,9 +93,9 @@ static class NullValuedType { } */ /* - /********************************************************** + /********************************************************************** /* Test methods - /********************************************************** + /********************************************************************** */ private final ObjectMapper MAPPER = objectMapper(); @@ -86,22 +107,22 @@ public void testSimple() throws Exception public void testOverriddenDefaultNulls() throws Exception { - DefaultSerializerProvider sp = new DefaultSerializerProvider.Impl(); - sp.setNullValueSerializer(new NullSerializer()); - ObjectMapper m = new ObjectMapper(); - m.setSerializerProvider(sp); + ObjectMapper m = jsonMapperBuilder() + .addModule(new SimpleModule() + .setDefaultNullValueSerializer(new NullSerializer())) + .build(); assertEquals("\"foobar\"", m.writeValueAsString(null)); } public void testCustomNulls() throws Exception { - ObjectMapper m = new ObjectMapper(); - m.setSerializerProvider(new MyNullProvider()); + ObjectMapper m = jsonMapperBuilder() + .serializationContexts(new MyNullSerializerContexts()) + .build(); assertEquals("{\"name\":\"foobar\"}", m.writeValueAsString(new Bean1())); assertEquals("{\"type\":null}", m.writeValueAsString(new Bean2())); } - // #281 public void testCustomNullForTrees() throws Exception { ObjectNode root = MAPPER.createObjectNode(); @@ -111,10 +132,11 @@ public void testCustomNullForTrees() throws Exception assertEquals("{\"a\":null}", MAPPER.writeValueAsString(root)); // but then we can customize it: - DefaultSerializerProvider prov = new MyNullProvider(); - prov.setNullValueSerializer(new NullSerializer()); - ObjectMapper m = new ObjectMapper(); - m.setSerializerProvider(prov); + ObjectMapper m = jsonMapperBuilder() + .serializationContexts(new MyNullSerializerContexts()) + .addModule(new SimpleModule() + .setDefaultNullValueSerializer(new NullSerializer())) + .build(); assertEquals("{\"a\":\"foobar\"}", m.writeValueAsString(root)); } diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/filter/TestIgnoredTypes.java b/src/test/java/com/fasterxml/jackson/databind/ser/filter/TestIgnoredTypes.java index 3279c0215c..3bfc3ad223 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/filter/TestIgnoredTypes.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/filter/TestIgnoredTypes.java @@ -82,8 +82,9 @@ public void testIgnoredType() throws Exception public void testSingleWithMixins() throws Exception { SimpleModule module = new SimpleModule(); module.setMixInAnnotation(Person.class, PersonMixin.class); - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(module); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(module) + .build(); PersonWrapper input = new PersonWrapper(); String json = mapper.writeValueAsString(input); assertEquals("{\"value\":1}", json); @@ -92,8 +93,9 @@ public void testSingleWithMixins() throws Exception { public void testListWithMixins() throws Exception { SimpleModule module = new SimpleModule(); module.setMixInAnnotation(Person.class, PersonMixin.class); - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(module); + ObjectMapper mapper = jsonMapperBuilder() + .addModule(module) + .build(); List persons = new ArrayList(); persons.add(new Person("Bob")); String json = mapper.writeValueAsString(persons); @@ -102,8 +104,10 @@ public void testListWithMixins() throws Exception { public void testIgnoreUsingConfigOverride() throws Exception { - final ObjectMapper mapper = objectMapper(); - mapper.configOverride(Wrapped.class).setIsIgnoredType(true); + final ObjectMapper mapper = jsonMapperBuilder() + .withConfigOverride(Wrapped.class, + o -> o.setIsIgnoredType(true)) + .build(); // serialize , first String json = mapper.writeValueAsString(new Wrapper()); diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/filter/TestJsonFilter.java b/src/test/java/com/fasterxml/jackson/databind/ser/filter/TestJsonFilter.java index 84b057c125..15476af1a2 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/filter/TestJsonFilter.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/filter/TestJsonFilter.java @@ -4,7 +4,7 @@ import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonStreamContext; +import com.fasterxml.jackson.core.TokenStreamContext; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.ser.FilterProvider; import com.fasterxml.jackson.databind.ser.PropertyWriter; @@ -42,12 +42,12 @@ static class C { static class CheckSiblingContextFilter extends SimpleBeanPropertyFilter { @Override public void serializeAsField(Object bean, JsonGenerator jgen, SerializerProvider prov, PropertyWriter writer) throws Exception { - JsonStreamContext sc = jgen.getOutputContext(); + TokenStreamContext sc = jgen.getOutputContext(); if (writer.getName() != null && writer.getName().equals("c")) { //This assertion is failing as sc.getParent() incorrectly returns 'a'. If you comment out the member 'a' // in the CheckSiblingContextBean, you'll see that the sc.getParent() correctly returns 'b' - assertEquals("b", sc.getParent().getCurrentName()); + assertEquals("b", sc.getParent().currentName()); } writer.serializeAsField(bean, jgen, prov); } @@ -57,13 +57,14 @@ public void testCheckSiblingContextFilter() { FilterProvider prov = new SimpleFilterProvider().addFilter("checkSiblingContextFilter", new CheckSiblingContextFilter()); - ObjectMapper mapper = new ObjectMapper(); - mapper.setFilterProvider(prov); - mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + ObjectMapper mapper = jsonMapperBuilder() + .filterProvider(prov) + .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS) + .build(); mapper.valueToTree(new CheckSiblingContextBean()); } - // [Issue#89] + // [databind#89] static class Pod { protected String username; @@ -91,7 +92,7 @@ public void setUserPassword(String value) { } } - // [Issue#306]: JsonFilter for properties, too! + // [databind#306]: JsonFilter for properties, too! @JsonPropertyOrder(alphabetic=true) static class FilteredProps @@ -118,9 +119,9 @@ public void testSimpleInclusionFilter() throws Exception SimpleBeanPropertyFilter.filterOutAllExcept("a")); assertEquals("{\"a\":\"a\"}", MAPPER.writer(prov).writeValueAsString(new Bean())); - // [JACKSON-504]: also verify it works via mapper - ObjectMapper mapper = new ObjectMapper(); - mapper.setFilterProvider(prov); + ObjectMapper mapper = jsonMapperBuilder() + .filterProvider(prov) + .build(); assertEquals("{\"a\":\"a\"}", mapper.writeValueAsString(new Bean())); } @@ -151,20 +152,20 @@ public void testMissingFilter() throws Exception // but when changing behavior, should work difference SimpleFilterProvider fp = new SimpleFilterProvider().setFailOnUnknownId(false); - ObjectMapper mapper = new ObjectMapper(); - mapper.setFilterProvider(fp); + ObjectMapper mapper = jsonMapperBuilder() + .filterProvider(fp) + .build(); String json = mapper.writeValueAsString(new Bean()); assertEquals("{\"a\":\"a\",\"b\":\"b\"}", json); } - - // defaulting, as per [JACKSON-449] + public void testDefaultFilter() throws Exception { FilterProvider prov = new SimpleFilterProvider().setDefaultFilter(SimpleBeanPropertyFilter.filterOutAllExcept("b")); assertEquals("{\"b\":\"b\"}", MAPPER.writer(prov).writeValueAsString(new Bean())); } - // [Issue#89] combining @JsonIgnore, @JsonProperty + // [databind#89] combining @JsonIgnore, @JsonProperty public void testIssue89() throws Exception { ObjectMapper mapper = new ObjectMapper(); @@ -181,7 +182,7 @@ public void testIssue89() throws Exception assertEquals("foo!", pod2.userPassword); } - // Wrt [Issue#306] + // Wrt [databind#306] public void testFilterOnProperty() throws Exception { FilterProvider prov = new SimpleFilterProvider() diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/filter/TestMapFiltering.java b/src/test/java/com/fasterxml/jackson/databind/ser/filter/TestMapFiltering.java index ab2a9a93fb..5d9c800149 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/filter/TestMapFiltering.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/filter/TestMapFiltering.java @@ -8,9 +8,9 @@ import com.fasterxml.jackson.annotation.JsonFilter; import com.fasterxml.jackson.annotation.JsonInclude; 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.*; import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; @@ -56,6 +56,11 @@ public MapBeanNoOffset() { static class TestMapFilter implements PropertyFilter { + @Override + public PropertyFilter snapshot() { + return this; + } + @Override public void serializeAsField(Object bean, JsonGenerator g, SerializerProvider provider, PropertyWriter writer) @@ -89,12 +94,6 @@ public void serializeAsElement(Object elementValue, JsonGenerator jgen, // not needed for testing } - @Override - @Deprecated - public void depositSchemaProperty(PropertyWriter writer, - ObjectNode propertiesNode, SerializerProvider provider) - throws JsonMappingException { } - @Override public void depositSchemaProperty(PropertyWriter writer, JsonObjectFormatVisitor objectVisitor, @@ -236,21 +235,6 @@ public void testMapAbsentValue() throws IOException assertEquals(aposToQuotes("{'a':'foo'}"), json); } - @SuppressWarnings("deprecation") - public void testMapNullSerialization() throws IOException - { - ObjectMapper m = new ObjectMapper(); - Map map = new HashMap(); - map.put("a", null); - // by default, should output null-valued entries: - assertEquals("{\"a\":null}", m.writeValueAsString(map)); - // but not if explicitly asked not to (note: config value is dynamic here) - - m = new ObjectMapper(); - m.disable(SerializationFeature.WRITE_NULL_MAP_VALUES); - assertEquals("{}", m.writeValueAsString(map)); - } - // [databind#527] public void testMapWithOnlyEmptyValues() throws IOException { @@ -271,9 +255,10 @@ public void testMapWithOnlyEmptyValues() throws IOException public void testMapViaGlobalNonEmpty() throws Exception { // basic Map subclass: - ObjectMapper mapper = new ObjectMapper(); - mapper.setDefaultPropertyInclusion(JsonInclude.Value.empty() - .withContentInclusion(JsonInclude.Include.NON_EMPTY)); + ObjectMapper mapper = jsonMapperBuilder() + .changeDefaultPropertyInclusion(incl -> incl + .withContentInclusion(JsonInclude.Include.NON_EMPTY)) + .build(); assertEquals(aposToQuotes("{'a':'b'}"), mapper.writeValueAsString( new StringMap497() .add("x", "") @@ -284,10 +269,11 @@ public void testMapViaGlobalNonEmpty() throws Exception public void testMapViaTypeOverride() throws Exception { // basic Map subclass: - ObjectMapper mapper = new ObjectMapper(); - mapper.configOverride(Map.class) - .setInclude(JsonInclude.Value.empty() - .withContentInclusion(JsonInclude.Include.NON_EMPTY)); + ObjectMapper mapper = jsonMapperBuilder() + .withConfigOverride(Map.class, + o -> o.setInclude(JsonInclude.Value.empty() + .withContentInclusion(JsonInclude.Include.NON_EMPTY))) + .build(); assertEquals(aposToQuotes("{'a':'b'}"), mapper.writeValueAsString( new StringMap497() .add("foo", "") diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/jdk/AtomicTypeSerializationTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/AtomicTypeSerializationTest.java index 19f2890189..fbf7d92c3d 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/jdk/AtomicTypeSerializationTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/AtomicTypeSerializationTest.java @@ -106,8 +106,10 @@ public void testContextualAtomicReference() throws Exception { SimpleDateFormat df = new SimpleDateFormat("yyyy/MM/dd"); df.setTimeZone(TimeZone.getTimeZone("UTC")); - final ObjectMapper mapper = objectMapper(); - mapper.setDateFormat(df); + + ObjectMapper mapper = jsonMapperBuilder() + .defaultDateFormat(df) + .build(); ContextualOptionals input = new ContextualOptionals(); input.date = new AtomicReference<>(new Date(0L)); input.date1 = new AtomicReference<>(new Date(0L)); diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/jdk/CollectionSerializationTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/CollectionSerializationTest.java index 1d09d746a8..b89f2e881a 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/jdk/CollectionSerializationTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/CollectionSerializationTest.java @@ -6,8 +6,8 @@ import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping; import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; public class CollectionSerializationTest extends BaseMapTest @@ -47,8 +47,11 @@ public PseudoList(String... values) { } } - static class ListSerializer extends JsonSerializer> + @SuppressWarnings("serial") + static class ListSerializer extends StdSerializer> { + public ListSerializer() { super(List.class); } + @Override public void serialize(List value, JsonGenerator gen, SerializerProvider provider) throws IOException @@ -58,7 +61,6 @@ public void serialize(List value, JsonGenerator gen, SerializerProvider } } - // for [JACKSON-254], suppression of empty collections static class EmptyListBean { public List empty = new ArrayList(); } @@ -123,14 +125,14 @@ public void testCollections() throws IOException String json = MAPPER.writeValueAsString(value); // and then need to verify: - JsonParser jp = new JsonFactory().createParser(json); - assertToken(JsonToken.START_ARRAY, jp.nextToken()); + JsonParser p = MAPPER.createParser(json); + assertToken(JsonToken.START_ARRAY, p.nextToken()); for (int i = 0; i < entryLen; ++i) { - assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); - assertEquals(i, jp.getIntValue()); + assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); + assertEquals(i, p.getIntValue()); } - assertToken(JsonToken.END_ARRAY, jp.nextToken()); - jp.close(); + assertToken(JsonToken.END_ARRAY, p.nextToken()); + p.close(); } } @@ -144,37 +146,37 @@ public void testBigCollection() throws IOException } // Let's test using 3 main variants... for (int mode = 0; mode < 3; ++mode) { - JsonParser jp = null; + JsonParser p = null; switch (mode) { case 0: { byte[] data = MAPPER.writeValueAsBytes(value); - jp = new JsonFactory().createParser(data); + p = MAPPER.createParser(data); } break; case 1: { StringWriter sw = new StringWriter(value.size()); MAPPER.writeValue(sw, value); - jp = createParserUsingReader(sw.toString()); + p = createParserUsingReader(sw.toString()); } break; case 2: { String str = MAPPER.writeValueAsString(value); - jp = createParserUsingReader(str); + p = createParserUsingReader(str); } break; } // and verify - assertToken(JsonToken.START_ARRAY, jp.nextToken()); + assertToken(JsonToken.START_ARRAY, p.nextToken()); for (int i = 0; i <= COUNT; ++i) { - assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); - assertEquals(i, jp.getIntValue()); + assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken()); + assertEquals(i, p.getIntValue()); } - assertToken(JsonToken.END_ARRAY, jp.nextToken()); - jp.close(); + assertToken(JsonToken.END_ARRAY, p.nextToken()); + p.close(); } } @@ -259,8 +261,9 @@ public void testEmptyListOrArray() throws IOException assertEquals("{\"empty\":[]}", MAPPER.writeValueAsString(array)); // note: value of setting may be cached when constructing serializer, need a new instance - ObjectMapper m = new ObjectMapper(); - m.configure(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS, false); + ObjectMapper m = jsonMapperBuilder() + .configure(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS, false) + .build(); assertEquals("{}", m.writeValueAsString(list)); assertEquals("{}", m.writeValueAsString(array)); } @@ -273,8 +276,9 @@ public void testStaticList() throws IOException assertEquals(aposToQuotes("{'list':['a','b','c']}"), json); // but then with default typing - ObjectMapper mapper = new ObjectMapper(); - mapper.enableDefaultTyping(DefaultTyping.NON_FINAL); + final ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.NON_FINAL) + .build(); json = mapper.writeValueAsString(w); assertEquals(aposToQuotes(String.format("['%s',{'list':['%s',['a','b','c']]}]", w.getClass().getName(), w.list.getClass().getName())), json); diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/jdk/DateSerializationTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/DateSerializationTest.java index e865b900e6..ab10bc0d90 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/jdk/DateSerializationTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/DateSerializationTest.java @@ -114,16 +114,19 @@ public void testDateNumeric() throws IOException public void testDateISO8601() throws IOException { - ObjectMapper mapper = new ObjectMapper(); - mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); + ObjectMapper mapper = jsonMapperBuilder() + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) + .build(); - serialize(mapper, judate(1970, 1, 1, 02, 00, 00, 0, "GMT+2"), "1970-01-01T00:00:00.000+0000"); - serialize(mapper, judate(1970, 1, 1, 00, 00, 00, 0, "UTC"), "1970-01-01T00:00:00.000+0000"); + serialize( mapper, judate(1970, 1, 1, 02, 00, 00, 0, "GMT+2"), "1970-01-01T00:00:00.000+00:00"); + serialize( mapper, judate(1970, 1, 1, 00, 00, 00, 0, "UTC"), "1970-01-01T00:00:00.000+00:00"); + serialize(mapper, judate(1970, 1, 1, 02, 00, 00, 0, "GMT+2"), "1970-01-01T00:00:00.000+00:00"); + serialize(mapper, judate(1970, 1, 1, 00, 00, 00, 0, "UTC"), "1970-01-01T00:00:00.000+00:00"); // 22-Nov-2018, tatu: Also ensure we use padding... - serialize(mapper, judate(911, 1, 1, 00, 00, 00, 0, "UTC"), "0911-01-01T00:00:00.000+0000"); - serialize(mapper, judate(87, 1, 1, 00, 00, 00, 0, "UTC"), "0087-01-01T00:00:00.000+0000"); - serialize(mapper, judate(1, 1, 1, 00, 00, 00, 0, "UTC"), "0001-01-01T00:00:00.000+0000"); + serialize(mapper, judate(911, 1, 1, 00, 00, 00, 0, "UTC"), "0911-01-01T00:00:00.000+00:00"); + serialize(mapper, judate(87, 1, 1, 00, 00, 00, 0, "UTC"), "0087-01-01T00:00:00.000+00:00"); + serialize(mapper, judate(1, 1, 1, 00, 00, 00, 0, "UTC"), "0001-01-01T00:00:00.000+00:00"); } // [databind#2167]: beyond year 9999 needs special handling @@ -131,9 +134,9 @@ public void testDateISO8601_10k() throws IOException { ObjectWriter w = MAPPER.writer() .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); - serialize(w, judate(10204, 1, 1, 00, 00, 00, 0, "UTC"), "+10204-01-01T00:00:00.000+0000"); + serialize(w, judate(10204, 1, 1, 00, 00, 00, 0, "UTC"), "+10204-01-01T00:00:00.000+00:00"); // and although specification lacks for beyond 5 digits (well, actually even 5...), let's do our best: - serialize(w, judate(123456, 1, 1, 00, 00, 00, 0, "UTC"), "+123456-01-01T00:00:00.000+0000"); + serialize(w, judate(123456, 1, 1, 00, 00, 00, 0, "UTC"), "+123456-01-01T00:00:00.000+00:00"); } // [databind#2167]: dates before Common Era (CE), that is, BCE, need special care: @@ -145,23 +148,23 @@ public void testDateISO8601_BCE() throws IOException // First: I _think_ BCE-1 is what you get with year 0, and should become "+0000" // and from further back in time, it'll be "-0001" (BCE-2) etc) - serialize(w, judate(0, 1, 1, 00, 00, 00, 0, "UTC"), "+0000-01-01T00:00:00.000+0000"); - serialize(w, judate(-1, 1, 1, 00, 00, 00, 0, "UTC"), "-0001-01-01T00:00:00.000+0000"); - serialize(w, judate(-49, 1, 1, 00, 00, 00, 0, "UTC"), "-0049-01-01T00:00:00.000+0000"); // All hail Caesar - serialize(w, judate(-264, 1, 1, 00, 00, 00, 0, "UTC"), "-0264-01-01T00:00:00.000+0000"); // Carthage FTW? + serialize(w, judate(0, 1, 1, 00, 00, 00, 0, "UTC"), "+0000-01-01T00:00:00.000+00:00"); + serialize(w, judate(-1, 1, 1, 00, 00, 00, 0, "UTC"), "-0001-01-01T00:00:00.000+00:00"); + serialize(w, judate(-49, 1, 1, 00, 00, 00, 0, "UTC"), "-0049-01-01T00:00:00.000+00:00"); // All hail Caesar + serialize(w, judate(-264, 1, 1, 00, 00, 00, 0, "UTC"), "-0264-01-01T00:00:00.000+00:00"); // Carthage FTW? } - + /** * Use a default TZ other than UTC. Dates must be serialized using that TZ. */ public void testDateISO8601_customTZ() throws IOException { - ObjectMapper mapper = new ObjectMapper(); - mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); - mapper.setTimeZone(TimeZone.getTimeZone("GMT+2")); - - serialize( mapper, judate(1970, 1, 1, 00, 00, 00, 0, "GMT+2"), "1970-01-01T00:00:00.000+0200"); - serialize( mapper, judate(1970, 1, 1, 00, 00, 00, 0, "UTC"), "1970-01-01T02:00:00.000+0200"); + ObjectMapper mapper = jsonMapperBuilder() + .defaultTimeZone(TimeZone.getTimeZone("GMT+2")) + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) + .build(); + serialize( mapper, judate(1970, 1, 1, 00, 00, 00, 0, "GMT+2"), "1970-01-01T00:00:00.000+02:00"); + serialize( mapper, judate(1970, 1, 1, 00, 00, 00, 0, "UTC"), "1970-01-01T02:00:00.000+02:00"); } /** @@ -172,27 +175,28 @@ public void testDateISO8601_customTZ() throws IOException public void testDateISO8601_colonInTZ() throws IOException { StdDateFormat dateFormat = new StdDateFormat(); - assertFalse(dateFormat.isColonIncludedInTimeZone()); - dateFormat = dateFormat.withColonInTimeZone(true); assertTrue(dateFormat.isColonIncludedInTimeZone()); + dateFormat = dateFormat.withColonInTimeZone(false); + assertFalse(dateFormat.isColonIncludedInTimeZone()); - ObjectMapper mapper = new ObjectMapper(); - mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); - mapper.setDateFormat(dateFormat); - - serialize( mapper, judate(1970, 1, 1, 02, 00, 00, 0, "GMT+2"), "1970-01-01T00:00:00.000+00:00"); - serialize( mapper, judate(1970, 1, 1, 00, 00, 00, 0, "UTC"), "1970-01-01T00:00:00.000+00:00"); + ObjectMapper mapper = jsonMapperBuilder() + .defaultDateFormat(dateFormat) + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) + .build(); + serialize( mapper, judate(1970, 1, 1, 02, 00, 00, 0, "GMT+2"), "1970-01-01T00:00:00.000+0000"); + serialize( mapper, judate(1970, 1, 1, 00, 00, 00, 0, "UTC"), "1970-01-01T00:00:00.000+0000"); } public void testDateOther() throws IOException { - ObjectMapper mapper = new ObjectMapper(); DateFormat df = new SimpleDateFormat("yyyy-MM-dd'X'HH:mm:ss"); - mapper.setDateFormat(df); - mapper.setTimeZone(TimeZone.getTimeZone("PST")); + ObjectMapper mapper = jsonMapperBuilder() + .defaultDateFormat(df) + .defaultTimeZone(TimeZone.getTimeZone("PST")) + .build(); // let's hit epoch start, offset by a bit - serialize( mapper, judate(1970, 1, 1, 00, 00, 00, 0, "UTC"), "1969-12-31X16:00:00"); + serialize(mapper, judate(1970, 1, 1, 00, 00, 00, 0, "UTC"), "1969-12-31X16:00:00"); } public void testTimeZone() throws IOException @@ -232,39 +236,41 @@ public void testDatesAsMapKeys() throws IOException assertFalse(mapper.isEnabled(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS)); map.put(new Date(0L), Integer.valueOf(1)); // by default will serialize as ISO-8601 values... - assertEquals("{\"1970-01-01T00:00:00.000+0000\":1}", mapper.writeValueAsString(map)); + assertEquals("{\"1970-01-01T00:00:00.000+00:00\":1}", mapper.writeValueAsString(map)); // but can change to use timestamps too - mapper.configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS, true); + mapper = jsonMapperBuilder() + .configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS, true) + .build(); assertEquals("{\"0\":1}", mapper.writeValueAsString(map)); } public void testDateWithJsonFormat() throws Exception { - ObjectMapper mapper = new ObjectMapper(); String json; // first: test overriding writing as timestamp - mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); - json = mapper.writeValueAsString(new DateAsNumberBean(0L)); + json = MAPPER.writer().without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(new DateAsNumberBean(0L)); assertEquals(aposToQuotes("{'date':0}"), json); // then reverse - mapper.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); - json = mapper.writer().with(getUTCTimeZone()).writeValueAsString(new DateAsStringBean(0L)); + json = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .with(getUTCTimeZone()).writeValueAsString(new DateAsStringBean(0L)); assertEquals("{\"date\":\"1970-01-01\"}", json); // and with different DateFormat; CET is one hour ahead of GMT - json = mapper.writeValueAsString(new DateInCETBean(0L)); + json = MAPPER.writeValueAsString(new DateInCETBean(0L)); assertEquals("{\"date\":\"1970-01-01,01:00\"}", json); // and for [Issue#423] as well: - json = mapper.writer().with(getUTCTimeZone()).writeValueAsString(new CalendarAsStringBean(0L)); + json = MAPPER.writer().with(getUTCTimeZone()).writeValueAsString(new CalendarAsStringBean(0L)); assertEquals("{\"value\":\"1970-01-01\"}", json); // and with default (ISO8601) format (databind#1109) - json = mapper.writeValueAsString(new DateAsDefaultStringBean(0L)); - assertEquals("{\"date\":\"1970-01-01T00:00:00.000+0000\"}", json); + json = MAPPER.writeValueAsString(new DateAsDefaultStringBean(0L)); + assertEquals("{\"date\":\"1970-01-01T00:00:00.000+00:00\"}", json); } /** @@ -273,16 +279,21 @@ public void testDateWithJsonFormat() throws Exception */ public void testWithTimeZoneOverride() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd/HH:mm z")); - mapper.setTimeZone(TimeZone.getTimeZone("PST")); - mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); - + ObjectMapper mapper = jsonMapperBuilder() + .defaultDateFormat(new SimpleDateFormat("yyyy-MM-dd/HH:mm z")) + .defaultTimeZone(TimeZone.getTimeZone("PST")) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .build(); + // pacific time is GMT-8; so midnight becomes 16:00 previous day: serialize( mapper, judate(1969, 12, 31, 16, 00, 00, 00, "PST"), "1969-12-31/16:00 PST"); // Let's also verify that Locale won't matter too much... - mapper.setLocale(Locale.FRANCE); + mapper = jsonMapperBuilder() + .defaultDateFormat(new SimpleDateFormat("yyyy-MM-dd/HH:mm z")) + .defaultTimeZone(TimeZone.getTimeZone("PST")) + .defaultLocale(Locale.FRANCE) + .build(); serialize( mapper, judate(1969, 12, 31, 16, 00, 00, 00, "PST"), "1969-12-31/16:00 PST"); // Also: should be able to dynamically change timezone: @@ -298,53 +309,63 @@ public void testWithTimeZoneOverride() throws Exception */ public void testDateDefaultShape() throws Exception { - ObjectMapper mapper = new ObjectMapper(); // No @JsonFormat => default to user config - mapper.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); - String json = mapper.writeValueAsString(new DateAsDefaultBean(0L)); + String json = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(new DateAsDefaultBean(0L)); assertEquals(aposToQuotes("{'date':0}"), json); - mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); - json = mapper.writeValueAsString(new DateAsDefaultBean(0L)); - assertEquals(aposToQuotes("{'date':'1970-01-01T00:00:00.000+0000'}"), json); + json = MAPPER.writer() + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(new DateAsDefaultBean(0L)); + assertEquals(aposToQuotes("{'date':'1970-01-01T00:00:00.000+00:00'}"), json); // Empty @JsonFormat => default to user config - mapper.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); - json = mapper.writeValueAsString(new DateAsDefaultBeanWithEmptyJsonFormat(0L)); + json = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(new DateAsDefaultBeanWithEmptyJsonFormat(0L)); assertEquals(aposToQuotes("{'date':0}"), json); - mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); - json = mapper.writeValueAsString(new DateAsDefaultBeanWithEmptyJsonFormat(0L)); - assertEquals(aposToQuotes("{'date':'1970-01-01T00:00:00.000+0000'}"), json); + json = MAPPER.writer() + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(new DateAsDefaultBeanWithEmptyJsonFormat(0L)); + assertEquals(aposToQuotes("{'date':'1970-01-01T00:00:00.000+00:00'}"), json); // @JsonFormat with Shape.ANY and pattern => STRING shape, regardless of user config - mapper.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); - json = mapper.writeValueAsString(new DateAsDefaultBeanWithPattern(0L)); + json = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(new DateAsDefaultBeanWithPattern(0L)); assertEquals(aposToQuotes("{'date':'1970-01-01'}"), json); - mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); - json = mapper.writeValueAsString(new DateAsDefaultBeanWithPattern(0L)); + json = MAPPER.writer() + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(new DateAsDefaultBeanWithPattern(0L)); assertEquals(aposToQuotes("{'date':'1970-01-01'}"), json); // @JsonFormat with Shape.ANY and locale => STRING shape, regardless of user config - mapper.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); - json = mapper.writeValueAsString(new DateAsDefaultBeanWithLocale(0L)); - assertEquals(aposToQuotes("{'date':'1970-01-01T00:00:00.000+0000'}"), json); - mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); - json = mapper.writeValueAsString(new DateAsDefaultBeanWithLocale(0L)); - assertEquals(aposToQuotes("{'date':'1970-01-01T00:00:00.000+0000'}"), json); + json = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(new DateAsDefaultBeanWithLocale(0L)); + assertEquals(aposToQuotes("{'date':'1970-01-01T00:00:00.000+00:00'}"), json); + json = MAPPER.writer() + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(new DateAsDefaultBeanWithLocale(0L)); + assertEquals(aposToQuotes("{'date':'1970-01-01T00:00:00.000+00:00'}"), json); // @JsonFormat with Shape.ANY and timezone => STRING shape, regardless of user config - mapper.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); - json = mapper.writeValueAsString(new DateAsDefaultBeanWithTimezone(0L)); - assertEquals(aposToQuotes("{'date':'1970-01-01T01:00:00.000+0100'}"), json); - mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); - json = mapper.writeValueAsString(new DateAsDefaultBeanWithTimezone(0L)); - assertEquals(aposToQuotes("{'date':'1970-01-01T01:00:00.000+0100'}"), json); + json = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(new DateAsDefaultBeanWithTimezone(0L)); + assertEquals(aposToQuotes("{'date':'1970-01-01T01:00:00.000+01:00'}"), json); + json = MAPPER.writer() + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(new DateAsDefaultBeanWithTimezone(0L)); + assertEquals(aposToQuotes("{'date':'1970-01-01T01:00:00.000+01:00'}"), json); } // [databind#1648]: contextual default format should be used public void testFormatWithoutPattern() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd'X'HH:mm:ss")); + ObjectMapper mapper = jsonMapperBuilder() + .defaultDateFormat(new SimpleDateFormat("yyyy-MM-dd'X'HH:mm:ss")) + .build(); String json = mapper.writeValueAsString(new DateAsDefaultBeanWithTimezone(0L)); assertEquals(aposToQuotes("{'date':'1970-01-01X01:00:00'}"), json); } diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/jdk/JDKTypeSerializationTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/JDKTypeSerializationTest.java index b93d9a9a73..02375d3331 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/jdk/JDKTypeSerializationTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/JDKTypeSerializationTest.java @@ -10,7 +10,8 @@ import java.util.regex.Pattern; import com.fasterxml.jackson.annotation.JsonFormat; -import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.StreamWriteFeature; +import com.fasterxml.jackson.core.json.JsonFactory; import com.fasterxml.jackson.databind.*; /** @@ -44,9 +45,9 @@ public void testBigDecimal() throws Exception public void testBigDecimalAsPlainString() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - - mapper.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN); + final ObjectMapper mapper = new ObjectMapper(JsonFactory.builder() + .enable(StreamWriteFeature.WRITE_BIGDECIMAL_AS_PLAIN) + .build()); Map map = new HashMap(); String PI_STR = "3.00000000"; map.put("pi", new BigDecimal(PI_STR)); @@ -83,10 +84,11 @@ public void testCurrency() throws IOException public void testLocale() throws IOException { assertEquals(quote("en"), MAPPER.writeValueAsString(new Locale("en"))); - assertEquals(quote("es_ES"), MAPPER.writeValueAsString(new Locale("es", "ES"))); - assertEquals(quote("fi_FI_savo"), MAPPER.writeValueAsString(new Locale("FI", "fi", "savo"))); + assertEquals(quote("es-ES"), MAPPER.writeValueAsString(new Locale("es", "ES"))); + // 15-Feb-2017, tatu: wrt [databind#1600], can only assume this is expected... + assertEquals(quote("fi-FI-x-lvariant-savo"), MAPPER.writeValueAsString(new Locale("FI", "fi", "savo"))); - assertEquals(quote("en_US"), MAPPER.writeValueAsString(Locale.US)); + assertEquals(quote("en-US"), MAPPER.writeValueAsString(Locale.US)); // [databind#1123] assertEquals(quote(""), MAPPER.writeValueAsString(Locale.ROOT)); @@ -98,9 +100,10 @@ public void testInetAddress() throws IOException InetAddress input = InetAddress.getByName("google.com"); assertEquals(quote("google.com"), MAPPER.writeValueAsString(input)); - ObjectMapper mapper = new ObjectMapper(); - mapper.configOverride(InetAddress.class) - .setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.NUMBER)); + ObjectMapper mapper = jsonMapperBuilder() + .withConfigOverride(InetAddress.class, + o -> o.setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.NUMBER))) + .build(); String json = mapper.writeValueAsString(input); assertEquals(quote(input.getHostAddress()), json); diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/jdk/MapKeySerializationTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/MapKeySerializationTest.java index 8731deb605..c15557f3b2 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/jdk/MapKeySerializationTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/MapKeySerializationTest.java @@ -7,8 +7,9 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; + import com.fasterxml.jackson.core.Base64Variants; -import com.fasterxml.jackson.core.JsonGenerator; + import com.fasterxml.jackson.databind.*; @SuppressWarnings("serial") @@ -37,15 +38,6 @@ public String toString() { static class WatMap extends HashMap { } - static class DefaultKeySerializer extends JsonSerializer - { - @Override - public void serialize(Object value, JsonGenerator g, SerializerProvider provider) throws IOException - { - g.writeFieldName("DEFAULT:"+value); - } - } - /* /********************************************************** /* Test methods @@ -73,15 +65,6 @@ public void testClassKey() throws IOException assertEquals(aposToQuotes("{'java.lang.String':2}"), json); } - public void testDefaultKeySerializer() throws IOException - { - ObjectMapper m = new ObjectMapper(); - m.getSerializerProvider().setDefaultKeySerializer(new DefaultKeySerializer()); - Map map = new HashMap(); - map.put("a", "b"); - assertEquals("{\"DEFAULT:a\":\"b\"}", m.writeValueAsString(map)); - } - // [databind#1552] public void testMapsWithBinaryKeys() throws Exception { diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/jdk/NumberSerTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/NumberSerTest.java index d99eaf4098..3c406d68f2 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/jdk/NumberSerTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/NumberSerTest.java @@ -112,13 +112,16 @@ public void testNumbersAsString() throws Exception public void testConfigOverridesForNumbers() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.configOverride(Integer.TYPE) // for `int` - .setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.STRING)); - mapper.configOverride(Double.TYPE) // for `double` - .setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.STRING)); - mapper.configOverride(BigDecimal.class) - .setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.STRING)); + ObjectMapper mapper = jsonMapperBuilder() + .withAllConfigOverrides(all -> { // could have used separate but test for funsies + all.findOrCreateOverride(Integer.TYPE) // for `int` + .setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.STRING)); + all.findOrCreateOverride(Double.TYPE) // for `double` + .setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.STRING)); + all.findOrCreateOverride(BigDecimal.class) + .setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.STRING)); + }) + .build(); assertEquals(aposToQuotes("{'i':'3'}"), mapper.writeValueAsString(new IntWrapper(3))); diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/jdk/SqlDateSerializationTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/SqlDateSerializationTest.java index 3f8dbbaf68..da5b1d16f7 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/jdk/SqlDateSerializationTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/SqlDateSerializationTest.java @@ -85,9 +85,10 @@ public void testSqlTimestamp() throws IOException public void testPatternWithSqlDate() throws Exception { - ObjectMapper mapper = new ObjectMapper(); // `java.sql.Date` applies system default zone (and not UTC) - mapper.setTimeZone(TimeZone.getDefault()); + ObjectMapper mapper = jsonMapperBuilder() + .defaultTimeZone(TimeZone.getDefault()) + .build(); Person i = new Person(); i.dateOfBirth = java.sql.Date.valueOf("1980-04-14"); @@ -98,9 +99,12 @@ public void testPatternWithSqlDate() throws Exception // [databind#2064] public void testSqlDateConfigOverride() throws Exception { - ObjectMapper mapper = newObjectMapper(); - mapper.configOverride(java.sql.Date.class) - .setFormat(JsonFormat.Value.forPattern("yyyy+MM+dd")); + // `java.sql.Date` applies system default zone (and not UTC) + final ObjectMapper mapper = jsonMapperBuilder() + .defaultTimeZone(TimeZone.getDefault()) + .withConfigOverride(java.sql.Date.class, + o -> o.setFormat(JsonFormat.Value.forPattern("yyyy+MM+dd"))) + .build(); assertEquals("\"1980+04+14\"", mapper.writeValueAsString(java.sql.Date.valueOf("1980-04-14"))); } diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/jdk/UntypedSerializationTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/UntypedSerializationTest.java index 9117e79200..7cfa15f153 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/jdk/UntypedSerializationTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/UntypedSerializationTest.java @@ -26,80 +26,77 @@ public void testFromArray() doc.add(struct); doc.add(Boolean.FALSE); - ObjectMapper mapper = new ObjectMapper(); - JsonFactory f = new JsonFactory(); + ObjectMapper mapper = objectMapper(); // loop more than once, just to ensure caching works ok (during second round) for (int i = 0; i < 3; ++i) { String str = mapper.writeValueAsString(doc); - JsonParser jp = f.createParser(str); - assertEquals(JsonToken.START_ARRAY, jp.nextToken()); + JsonParser p = mapper.createParser(str); + assertEquals(JsonToken.START_ARRAY, p.nextToken()); - assertEquals(JsonToken.VALUE_STRING, jp.nextToken()); - assertEquals("Elem1", getAndVerifyText(jp)); + assertEquals(JsonToken.VALUE_STRING, p.nextToken()); + assertEquals("Elem1", getAndVerifyText(p)); - assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); - assertEquals(3, jp.getIntValue()); + assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken()); + assertEquals(3, p.getIntValue()); - assertEquals(JsonToken.START_OBJECT, jp.nextToken()); - assertEquals(JsonToken.FIELD_NAME, jp.nextToken()); - assertEquals("first", getAndVerifyText(jp)); + assertEquals(JsonToken.START_OBJECT, p.nextToken()); + assertEquals(JsonToken.FIELD_NAME, p.nextToken()); + assertEquals("first", getAndVerifyText(p)); - assertEquals(JsonToken.VALUE_TRUE, jp.nextToken()); - assertEquals(JsonToken.FIELD_NAME, jp.nextToken()); - assertEquals("Second", getAndVerifyText(jp)); + assertEquals(JsonToken.VALUE_TRUE, p.nextToken()); + assertEquals(JsonToken.FIELD_NAME, p.nextToken()); + assertEquals("Second", getAndVerifyText(p)); - if (jp.nextToken() != JsonToken.START_ARRAY) { + if (p.nextToken() != JsonToken.START_ARRAY) { fail("Expected START_ARRAY: JSON == '"+str+"'"); } - assertEquals(JsonToken.END_ARRAY, jp.nextToken()); - assertEquals(JsonToken.END_OBJECT, jp.nextToken()); + assertEquals(JsonToken.END_ARRAY, p.nextToken()); + assertEquals(JsonToken.END_OBJECT, p.nextToken()); - assertEquals(JsonToken.VALUE_FALSE, jp.nextToken()); + assertEquals(JsonToken.VALUE_FALSE, p.nextToken()); - assertEquals(JsonToken.END_ARRAY, jp.nextToken()); - assertNull(jp.nextToken()); - jp.close(); + assertEquals(JsonToken.END_ARRAY, p.nextToken()); + assertNull(p.nextToken()); + p.close(); } } - public void testFromMap() - throws Exception + public void testFromMap() throws Exception { LinkedHashMap doc = new LinkedHashMap(); - JsonFactory f = new JsonFactory(); doc.put("a1", "\"text\""); doc.put("int", Integer.valueOf(137)); doc.put("foo bar", Long.valueOf(1234567890L)); - ObjectMapper mapper = new ObjectMapper(); + ObjectMapper mapper = objectMapper(); for (int i = 0; i < 3; ++i) { String str = mapper.writeValueAsString(doc); - JsonParser jp = f.createParser(str); + JsonParser p = mapper.createParser(str); - assertEquals(JsonToken.START_OBJECT, jp.nextToken()); + assertEquals(JsonToken.START_OBJECT, p.nextToken()); - assertEquals(JsonToken.FIELD_NAME, jp.nextToken()); - assertEquals("a1", getAndVerifyText(jp)); - assertEquals(JsonToken.VALUE_STRING, jp.nextToken()); - assertEquals("\"text\"", getAndVerifyText(jp)); + assertEquals(JsonToken.FIELD_NAME, p.nextToken()); + assertEquals("a1", getAndVerifyText(p)); + assertEquals(JsonToken.VALUE_STRING, p.nextToken()); + assertEquals("\"text\"", getAndVerifyText(p)); - assertEquals(JsonToken.FIELD_NAME, jp.nextToken()); - assertEquals("int", getAndVerifyText(jp)); - assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); - assertEquals(137, jp.getIntValue()); + assertEquals(JsonToken.FIELD_NAME, p.nextToken()); + assertEquals("int", getAndVerifyText(p)); + assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken()); + assertEquals(137, p.getIntValue()); - assertEquals(JsonToken.FIELD_NAME, jp.nextToken()); - assertEquals("foo bar", getAndVerifyText(jp)); - assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); - assertEquals(1234567890L, jp.getLongValue()); + assertEquals(JsonToken.FIELD_NAME, p.nextToken()); + assertEquals("foo bar", getAndVerifyText(p)); + assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken()); + assertEquals(1234567890L, p.getLongValue()); - assertEquals(JsonToken.END_OBJECT, jp.nextToken()); + assertEquals(JsonToken.END_OBJECT, p.nextToken()); - assertNull(jp.nextToken()); - jp.close(); + assertNull(p.nextToken()); + p.close(); } } } diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/FormatFeatureAcceptSingleTest.java b/src/test/java/com/fasterxml/jackson/databind/struct/FormatFeatureAcceptSingleTest.java index 3aca91eb0a..3ab3e10bef 100644 --- a/src/test/java/com/fasterxml/jackson/databind/struct/FormatFeatureAcceptSingleTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/struct/FormatFeatureAcceptSingleTest.java @@ -88,9 +88,11 @@ public void testSingleStringArrayRead() throws Exception { assertEquals("first", result.values[0]); // and then without annotation, but with global override - ObjectMapper mapper = new ObjectMapper(); - mapper.configOverride(String[].class).setFormat(JsonFormat.Value.empty() - .withFeature(JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)); + ObjectMapper mapper = jsonMapperBuilder() + .withConfigOverride(String[].class, + o -> o.setFormat(JsonFormat.Value.empty() + .withFeature(JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY))) + .build(); StringArrayNotAnnoted result2 = mapper.readValue(json, StringArrayNotAnnoted.class); assertNotNull(result2.values); assertEquals(1, result2.values.length); diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/FormatFeatureUnwrapSingleTest.java b/src/test/java/com/fasterxml/jackson/databind/struct/FormatFeatureUnwrapSingleTest.java index ab8b38c4d5..bf99ced682 100644 --- a/src/test/java/com/fasterxml/jackson/databind/struct/FormatFeatureUnwrapSingleTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/struct/FormatFeatureUnwrapSingleTest.java @@ -143,9 +143,11 @@ public void testWithArrayTypes() throws Exception .writeValueAsString(new WrapWriteWithArrays())); // And then without SerializationFeature but with config override: - ObjectMapper mapper = new ObjectMapper(); - mapper.configOverride(String[].class).setFormat(JsonFormat.Value.empty() - .withFeature(JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)); + ObjectMapper mapper = jsonMapperBuilder() + .withConfigOverride(String[].class, + v -> v.setFormat(JsonFormat.Value.empty() + .withFeature(JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED))) + .build(); assertEquals(aposToQuotes("{'values':'a'}"), mapper.writeValueAsString(new StringArrayNotAnnoted("a"))); } diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/PojoAsArray646Test.java b/src/test/java/com/fasterxml/jackson/databind/struct/PojoAsArray646Test.java index efa0bc918d..c9d176defe 100644 --- a/src/test/java/com/fasterxml/jackson/databind/struct/PojoAsArray646Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/struct/PojoAsArray646Test.java @@ -75,12 +75,12 @@ public void setNestedItems(List nestedItems) { } /* - /********************************************************** - /* Tests - /********************************************************** + /********************************************************************** + /* Test methods + /********************************************************************** */ - private final ObjectMapper MAPPER = new ObjectMapper(); + private final ObjectMapper MAPPER = objectMapper(); public void testWithCustomTypeId() throws Exception { diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/PojoAsArrayRoundtripTest.java b/src/test/java/com/fasterxml/jackson/databind/struct/PojoAsArrayRoundtripTest.java new file mode 100644 index 0000000000..6b0a05d495 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/struct/PojoAsArrayRoundtripTest.java @@ -0,0 +1,171 @@ +package com.fasterxml.jackson.databind.struct; + +import java.util.ArrayList; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.BaseMapTest; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class PojoAsArrayRoundtripTest extends BaseMapTest +{ + @JsonFormat(shape=JsonFormat.Shape.ARRAY) + @JsonPropertyOrder({"content", "images"}) + static class MediaItemAsArray + { + public enum Player { JAVA, FLASH; } + public enum Size { SMALL, LARGE; } + + private List _photos; + private Content _content; + + public MediaItemAsArray() { } + + public MediaItemAsArray(Content c) + { + _content = c; + } + + public void addPhoto(Photo p) { + if (_photos == null) { + _photos = new ArrayList(); + } + _photos.add(p); + } + + public List getImages() { return _photos; } + public void setImages(List p) { _photos = p; } + + public Content getContent() { return _content; } + public void setContent(Content c) { _content = c; } + + @JsonFormat(shape=JsonFormat.Shape.ARRAY) + @JsonPropertyOrder({"uri","title","width","height","size"}) + static class Photo + { + private String _uri; + private String _title; + private int _width; + private int _height; + private Size _size; + + public Photo() {} + public Photo(String uri, String title, int w, int h, Size s) + { + _uri = uri; + _title = title; + _width = w; + _height = h; + _size = s; + } + + public String getUri() { return _uri; } + public String getTitle() { return _title; } + public int getWidth() { return _width; } + public int getHeight() { return _height; } + public Size getSize() { return _size; } + + public void setUri(String u) { _uri = u; } + public void setTitle(String t) { _title = t; } + public void setWidth(int w) { _width = w; } + public void setHeight(int h) { _height = h; } + public void setSize(Size s) { _size = s; } + } + + @JsonFormat(shape=JsonFormat.Shape.ARRAY) + @JsonPropertyOrder({"uri","title","width","height","format","duration","size","bitrate","persons","player","copyright"}) + public static class Content + { + private Player _player; + private String _uri; + private String _title; + private int _width; + private int _height; + private String _format; + private long _duration; + private long _size; + private int _bitrate; + private List _persons; + private String _copyright; + + public Content() { } + + public void addPerson(String p) { + if (_persons == null) { + _persons = new ArrayList(); + } + _persons.add(p); + } + + public Player getPlayer() { return _player; } + public String getUri() { return _uri; } + public String getTitle() { return _title; } + public int getWidth() { return _width; } + public int getHeight() { return _height; } + public String getFormat() { return _format; } + public long getDuration() { return _duration; } + public long getSize() { return _size; } + public int getBitrate() { return _bitrate; } + public List getPersons() { return _persons; } + public String getCopyright() { return _copyright; } + + public void setPlayer(Player p) { _player = p; } + public void setUri(String u) { _uri = u; } + public void setTitle(String t) { _title = t; } + public void setWidth(int w) { _width = w; } + public void setHeight(int h) { _height = h; } + public void setFormat(String f) { _format = f; } + public void setDuration(long d) { _duration = d; } + public void setSize(long s) { _size = s; } + public void setBitrate(int b) { _bitrate = b; } + public void setPersons(List p) { _persons = p; } + public void setCopyright(String c) { _copyright = c; } + } + } + + /* + /********************************************************************** + /* Test methods + /********************************************************************** + */ + + private final ObjectMapper MAPPER = objectMapper(); + + public void testMedaItemRoundtrip() throws Exception + { + MediaItemAsArray.Content c = new MediaItemAsArray.Content(); + c.setBitrate(9600); + c.setCopyright("none"); + c.setDuration(360000L); + c.setFormat("lzf"); + c.setHeight(640); + c.setSize(128000L); + c.setTitle("Amazing Stuff For Something Or Oth\u00CBr!"); + c.setUri("http://multi.fario.us/index.html"); + c.setWidth(1400); + + c.addPerson("Joe Sixp\u00e2ck"); + c.addPerson("Ezekiel"); + c.addPerson("Sponge-Bob Squarepant\u00DF"); + + MediaItemAsArray input = new MediaItemAsArray(c); + input.addPhoto(new MediaItemAsArray.Photo()); + input.addPhoto(new MediaItemAsArray.Photo()); + input.addPhoto(new MediaItemAsArray.Photo()); + + String json = MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(input); + + MediaItemAsArray output = MAPPER.readValue(new java.io.StringReader(json), MediaItemAsArray.class); + assertNotNull(output); + + assertNotNull(output.getImages()); + assertEquals(input.getImages().size(), output.getImages().size()); + assertNotNull(output.getContent()); + assertEquals(input.getContent().getTitle(), output.getContent().getTitle()); + assertEquals(input.getContent().getUri(), output.getContent().getUri()); + + // compare re-serialization as a simple check as well + assertEquals(json, MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(output)); + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/ScalarCoercionTest.java b/src/test/java/com/fasterxml/jackson/databind/struct/ScalarCoercionTest.java index 9a08e52d98..0e3162fd3e 100644 --- a/src/test/java/com/fasterxml/jackson/databind/struct/ScalarCoercionTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/struct/ScalarCoercionTest.java @@ -10,12 +10,12 @@ // for [databind#1106] public class ScalarCoercionTest extends BaseMapTest { - private final ObjectMapper COERCING_MAPPER = objectMapperBuilder() - .enable(MapperFeature.ALLOW_COERCION_OF_SCALARS) + private final ObjectMapper COERCING_MAPPER = jsonMapperBuilder() + .enable(DeserializationFeature.ALLOW_COERCION_OF_SCALARS) .build(); - private final ObjectMapper NOT_COERCING_MAPPER = objectMapperBuilder() - .disable(MapperFeature.ALLOW_COERCION_OF_SCALARS) + private final ObjectMapper NOT_COERCING_MAPPER = jsonMapperBuilder() + .disable(DeserializationFeature.ALLOW_COERCION_OF_SCALARS) .build(); /* @@ -189,7 +189,7 @@ private void _verifyCoerceFail(String input, Class type) throws IOException } catch (MismatchedInputException e) { verifyException(e, "Cannot coerce "); verifyException(e, " for type `"); - verifyException(e, "enable `MapperFeature.ALLOW_COERCION_OF_SCALARS` to allow"); + verifyException(e, "enable `DeserializationFeature.ALLOW_COERCION_OF_SCALARS` to allow"); } } } diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/SingleValueAsArrayTest.java b/src/test/java/com/fasterxml/jackson/databind/struct/SingleValueAsArrayTest.java index f0f93546e1..24f990f3a7 100644 --- a/src/test/java/com/fasterxml/jackson/databind/struct/SingleValueAsArrayTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/struct/SingleValueAsArrayTest.java @@ -64,10 +64,9 @@ public Bean1421B(T value) { /********************************************************** */ - private final ObjectMapper MAPPER = new ObjectMapper(); - { - MAPPER.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY); - } + private final ObjectMapper MAPPER = jsonMapperBuilder() + .enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY) + .build(); public void testSuccessfulDeserializationOfObjectWithChainedArrayCreators() throws IOException { diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/TestForwardReference.java b/src/test/java/com/fasterxml/jackson/databind/struct/TestForwardReference.java index 87a5ce1e0b..482e91d8ee 100644 --- a/src/test/java/com/fasterxml/jackson/databind/struct/TestForwardReference.java +++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestForwardReference.java @@ -14,10 +14,11 @@ */ public class TestForwardReference extends BaseMapTest { - private final ObjectMapper MAPPER = new ObjectMapper() - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) - .enable(SerializationFeature.INDENT_OUTPUT) - .setSerializationInclusion(JsonInclude.Include.NON_NULL); + private final ObjectMapper MAPPER = jsonMapperBuilder() + .changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_NULL)) + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .enable(SerializationFeature.INDENT_OUTPUT) + .build(); /** Tests that we can read a hierarchical structure with forward references*/ public void testForwardRef() throws IOException { 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 391cd3cb82..193ce1ecde 100644 --- a/src/test/java/com/fasterxml/jackson/databind/struct/TestPOJOAsArray.java +++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestPOJOAsArray.java @@ -194,16 +194,18 @@ public void testNullColumn() throws Exception */ public void testSerializeAsArrayWithSingleProperty() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.enable(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED); + ObjectMapper mapper = jsonMapperBuilder() + .enable(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED) + .build(); 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); + ObjectMapper mapper = jsonMapperBuilder() + .enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY) + .build(); SingleBean result = mapper.readValue("[\"foobar\"]", SingleBean.class); assertNotNull(result); assertEquals("foobar", result.name); @@ -221,8 +223,9 @@ public void testAnnotationOverride() throws Exception assertEquals("{\"value\":{\"x\":1,\"y\":2}}", MAPPER.writeValueAsString(new A())); // but override should change it: - ObjectMapper mapper2 = new ObjectMapper(); - mapper2.setAnnotationIntrospector(new ForceArraysIntrospector()); + ObjectMapper mapper2 = jsonMapperBuilder() + .annotationIntrospector(new ForceArraysIntrospector()) + .build(); assertEquals("[[1,2]]", mapper2.writeValueAsString(new A())); // and allow reading back, too @@ -251,9 +254,10 @@ public void testSimpleWithIndex() throws Exception public void testWithConfigOverrides() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - mapper.configOverride(NonAnnotatedXY.class) - .setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.ARRAY)); + ObjectMapper mapper = jsonMapperBuilder() + .withConfigOverride(NonAnnotatedXY.class, + o -> o.setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.ARRAY))) + .build(); String json = mapper.writeValueAsString(new NonAnnotatedXY(2, 3)); assertEquals("[2,3]", json); diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrapped.java b/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrapped.java index 7c70c0e440..84249a77fd 100644 --- a/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrapped.java +++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrapped.java @@ -238,7 +238,7 @@ public void testUnwrappedAsPropertyIndicator() throws Exception // [databind#1493]: case-insensitive handling public void testCaseInsensitiveUnwrap() throws Exception { - ObjectMapper mapper = objectMapperBuilder() + ObjectMapper mapper = jsonMapperBuilder() .enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES) .build(); Person p = mapper.readValue("{ }", Person.class); diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrappedWithPrefix.java b/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrappedWithPrefix.java index 29e742fb4a..bc06156ded 100644 --- a/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrappedWithPrefix.java +++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrappedWithPrefix.java @@ -170,7 +170,7 @@ public void testHierarchicConfigSerialize() throws Exception /********************************************************** */ - public void testPrefixedUnwrapping() throws Exception + public void testPrefixedUnwrapDeserialize() throws Exception { PrefixUnwrap bean = MAPPER.readValue("{\"name\":\"Axel\",\"_x\":4,\"_y\":7}", PrefixUnwrap.class); assertNotNull(bean); @@ -180,7 +180,7 @@ public void testPrefixedUnwrapping() throws Exception assertEquals(7, bean.location.y); } - public void testDeepPrefixedUnwrappingDeserialize() throws Exception + public void testDeepPrefixedUnwrapDeserialize() throws Exception { DeepPrefixUnwrap bean = MAPPER.readValue("{\"u.name\":\"Bubba\",\"u._x\":2,\"u._y\":3}", DeepPrefixUnwrap.class); diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrappedWithTypeInfo.java b/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrappedWithTypeInfo.java index 5a5f2cc056..6c1dc18ce7 100644 --- a/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrappedWithTypeInfo.java +++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrappedWithTypeInfo.java @@ -76,8 +76,9 @@ public void testUnwrappedWithTypeInfoAndFeatureDisabled() throws Exception inner.setP2("202"); outer.setInner(inner); - ObjectMapper mapper = new ObjectMapper(); - mapper = mapper.disable(SerializationFeature.FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS); + ObjectMapper mapper = jsonMapperBuilder() + .disable(SerializationFeature.FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS) + .build(); String json = mapper.writeValueAsString(outer); assertEquals("{\"@type\":\"OuterType\",\"p1\":\"101\",\"p2\":\"202\"}", json); diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/UnwrapSingleArrayScalarsTest.java b/src/test/java/com/fasterxml/jackson/databind/struct/UnwrapSingleArrayScalarsTest.java index 4352835ab1..d858e47630 100644 --- a/src/test/java/com/fasterxml/jackson/databind/struct/UnwrapSingleArrayScalarsTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/struct/UnwrapSingleArrayScalarsTest.java @@ -35,8 +35,9 @@ static class BooleanBean { public void testBooleanPrimitiveArrayUnwrap() throws Exception { // [databind#381] - final ObjectMapper mapper = new ObjectMapper(); - mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); + ObjectMapper mapper = jsonMapperBuilder() + .enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .build(); BooleanBean result = mapper.readValue(new StringReader("{\"v\":[true]}"), BooleanBean.class); assertTrue(result._v); @@ -72,8 +73,9 @@ public void testSingleElementScalarArrays() throws Exception { final byte byteTest = (byte) 43; final char charTest = 'c'; - final ObjectMapper mapper = new ObjectMapper(); - mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); + ObjectMapper mapper = jsonMapperBuilder() + .enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .build(); final int intValue = mapper.readValue(asArray(intTest), Integer.TYPE); assertEquals(intTest, intValue); @@ -121,8 +123,9 @@ public void testSingleElementScalarArrays() throws Exception { } public void testSingleElementArrayDisabled() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); + final ObjectMapper mapper = jsonMapperBuilder() + .disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .build(); try { mapper.readValue("[42]", Integer.class); fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled"); @@ -248,8 +251,9 @@ public void testSingleString() throws Exception public void testSingleStringWrapped() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); + ObjectMapper mapper = jsonMapperBuilder() + .disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .build(); String value = "FOO!"; try { @@ -260,7 +264,9 @@ public void testSingleStringWrapped() throws Exception verifyException(exp, "out of START_ARRAY"); } - mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); + mapper = jsonMapperBuilder() + .enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .build(); try { mapper.readValue("[\""+value+"\",\""+value+"\"]", String.class); @@ -274,8 +280,9 @@ public void testSingleStringWrapped() throws Exception public void testBigDecimal() throws Exception { - final ObjectMapper mapper = objectMapper(); - mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); + ObjectMapper mapper = jsonMapperBuilder() + .disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .build(); BigDecimal value = new BigDecimal("0.001"); BigDecimal result = mapper.readValue(value.toString(), BigDecimal.class); @@ -287,8 +294,10 @@ public void testBigDecimal() throws Exception verifyException(exp, "Cannot deserialize"); verifyException(exp, "out of START_ARRAY"); } - - mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); + + mapper = jsonMapperBuilder() + .enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .build(); result = mapper.readValue("[" + value.toString() + "]", BigDecimal.class); assertEquals(value, result); @@ -302,9 +311,10 @@ public void testBigDecimal() throws Exception public void testBigInteger() throws Exception { - final ObjectMapper mapper = objectMapper(); - mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); - + ObjectMapper mapper = jsonMapperBuilder() + .disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .build(); + BigInteger value = new BigInteger("-1234567890123456789012345567809"); BigInteger result = mapper.readValue(value.toString(), BigInteger.class); assertEquals(value, result); @@ -317,7 +327,9 @@ public void testBigInteger() throws Exception verifyException(exp, "out of START_ARRAY"); } - mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); + mapper = jsonMapperBuilder() + .enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .build(); result = mapper.readValue("[" + value.toString() + "]", BigInteger.class); assertEquals(value, result); diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/UnwrappedCreatorParam265Test.java b/src/test/java/com/fasterxml/jackson/databind/struct/UnwrappedCreatorParam265Test.java index 62010d8ab1..159352aab2 100644 --- a/src/test/java/com/fasterxml/jackson/databind/struct/UnwrappedCreatorParam265Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/struct/UnwrappedCreatorParam265Test.java @@ -78,7 +78,7 @@ public void testUnwrappedWithUnnamedCreatorParam() throws Exception /*JPersonWithoutName result =*/ mapper.readValue(json, JPersonWithoutName.class); fail("Should not pass"); } catch (InvalidDefinitionException e) { - verifyException(e, "Cannot define Creator parameter"); + verifyException(e, "Cannot define Creator property"); verifyException(e, "@JsonUnwrapped"); } } diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/UnwrappedWithView1559Test.java b/src/test/java/com/fasterxml/jackson/databind/struct/UnwrappedWithView1559Test.java index baf29adfdb..6fd217ddd6 100644 --- a/src/test/java/com/fasterxml/jackson/databind/struct/UnwrappedWithView1559Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/struct/UnwrappedWithView1559Test.java @@ -24,13 +24,16 @@ static final class Status { */ // for [databind#1559] - public void testCanSerializeSimpleWithDefaultView() throws Exception { - String json = objectMapperBuilder().configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false) + public void testCanSerializeSimpleWithDefaultView() throws Exception + { + String json = jsonMapperBuilder() + .disable(MapperFeature.DEFAULT_VIEW_INCLUSION) .build() .writeValueAsString(new Health()); assertEquals(aposToQuotes("{}"), json); // and just in case this, although won't matter wrt output - json = objectMapperBuilder().configure(MapperFeature.DEFAULT_VIEW_INCLUSION, true) + json = jsonMapperBuilder() + .enable(MapperFeature.DEFAULT_VIEW_INCLUSION) .build() .writeValueAsString(new Health()); assertEquals(aposToQuotes("{}"), json); diff --git a/src/test/java/com/fasterxml/jackson/databind/testutil/MediaItem.java b/src/test/java/com/fasterxml/jackson/databind/testutil/MediaItem.java deleted file mode 100644 index a619ce846b..0000000000 --- a/src/test/java/com/fasterxml/jackson/databind/testutil/MediaItem.java +++ /dev/null @@ -1,126 +0,0 @@ -package com.fasterxml.jackson.databind.testutil; - -import java.util.*; - -import com.fasterxml.jackson.annotation.JsonFormat; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; - -@JsonFormat(shape=JsonFormat.Shape.ARRAY) -@JsonPropertyOrder({"content", "images"}) -public class MediaItem -{ - public enum Player { JAVA, FLASH; } - public enum Size { SMALL, LARGE; } - - private List _photos; - private Content _content; - - public MediaItem() { } - - public MediaItem(Content c) - { - _content = c; - } - - public void addPhoto(Photo p) { - if (_photos == null) { - _photos = new ArrayList(); - } - _photos.add(p); - } - - public List getImages() { return _photos; } - public void setImages(List p) { _photos = p; } - - public Content getContent() { return _content; } - public void setContent(Content c) { _content = c; } - - /* - /********************************************************** - /* Helper types - /********************************************************** - */ - - @JsonFormat(shape=JsonFormat.Shape.ARRAY) - @JsonPropertyOrder({"uri","title","width","height","size"}) - public static class Photo - { - private String _uri; - private String _title; - private int _width; - private int _height; - private Size _size; - - public Photo() {} - public Photo(String uri, String title, int w, int h, Size s) - { - _uri = uri; - _title = title; - _width = w; - _height = h; - _size = s; - } - - public String getUri() { return _uri; } - public String getTitle() { return _title; } - public int getWidth() { return _width; } - public int getHeight() { return _height; } - public Size getSize() { return _size; } - - public void setUri(String u) { _uri = u; } - public void setTitle(String t) { _title = t; } - public void setWidth(int w) { _width = w; } - public void setHeight(int h) { _height = h; } - public void setSize(Size s) { _size = s; } - } - - @JsonFormat(shape=JsonFormat.Shape.ARRAY) - @JsonPropertyOrder({"uri","title","width","height","format","duration","size","bitrate","persons","player","copyright"}) - public static class Content - { - private Player _player; - private String _uri; - private String _title; - private int _width; - private int _height; - private String _format; - private long _duration; - private long _size; - private int _bitrate; - private List _persons; - private String _copyright; - - public Content() { } - - public void addPerson(String p) { - if (_persons == null) { - _persons = new ArrayList(); - } - _persons.add(p); - } - - public Player getPlayer() { return _player; } - public String getUri() { return _uri; } - public String getTitle() { return _title; } - public int getWidth() { return _width; } - public int getHeight() { return _height; } - public String getFormat() { return _format; } - public long getDuration() { return _duration; } - public long getSize() { return _size; } - public int getBitrate() { return _bitrate; } - public List getPersons() { return _persons; } - public String getCopyright() { return _copyright; } - - public void setPlayer(Player p) { _player = p; } - public void setUri(String u) { _uri = u; } - public void setTitle(String t) { _title = t; } - public void setWidth(int w) { _width = w; } - public void setHeight(int h) { _height = h; } - public void setFormat(String f) { _format = f; } - public void setDuration(long d) { _duration = d; } - public void setSize(long s) { _size = s; } - public void setBitrate(int b) { _bitrate = b; } - public void setPersons(List p) { _persons = p; } - public void setCopyright(String c) { _copyright = c; } - } -} diff --git a/src/test/java/com/fasterxml/jackson/databind/type/ContainerTypesTest.java b/src/test/java/com/fasterxml/jackson/databind/type/ContainerTypesTest.java index e91f827343..8e8c0823e1 100644 --- a/src/test/java/com/fasterxml/jackson/databind/type/ContainerTypesTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/type/ContainerTypesTest.java @@ -7,7 +7,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.type.CollectionType; import com.fasterxml.jackson.databind.type.MapType; -import com.fasterxml.jackson.databind.util.LRUMap; +import com.fasterxml.jackson.databind.util.SimpleLookupCache; // for [databind#1415] public class ContainerTypesTest extends BaseMapTest @@ -44,7 +44,7 @@ public void testImplicitCollectionType() throws Exception // [databind#1725] public void testMissingCollectionType() throws Exception { - TypeFactory tf = MAPPER.getTypeFactory().withCache(new LRUMap(4, 8)); + TypeFactory tf = MAPPER.getTypeFactory().withCache(new SimpleLookupCache(4, 8)); JavaType t = tf.constructParametricType(List.class, HashMap.class); assertEquals(CollectionType.class, t.getClass()); assertEquals(List.class, t.getRawClass()); diff --git a/src/test/java/com/fasterxml/jackson/databind/type/DeprecatedConstructType1456Test.java b/src/test/java/com/fasterxml/jackson/databind/type/DeprecatedConstructType1456Test.java deleted file mode 100644 index 8d3e58caba..0000000000 --- a/src/test/java/com/fasterxml/jackson/databind/type/DeprecatedConstructType1456Test.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.fasterxml.jackson.databind.type; - -import java.lang.reflect.Method; -import java.lang.reflect.Type; - -import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.introspect.AnnotatedClass; -import com.fasterxml.jackson.databind.introspect.AnnotatedMethod; -import com.fasterxml.jackson.databind.introspect.AnnotatedParameter; - -// Tests for [databind#1456]: resolution using methods deprecated -// in 2.7, but used to work in 2.6 -public class DeprecatedConstructType1456Test extends BaseMapTest -{ - public static class BaseController { - public void process(Entity entity) {} - } - - public static class ImplController extends BaseController {} - - public static class BaseEntity {} - - public static class ImplEntity extends BaseEntity {} - - private final ObjectMapper MAPPER = new ObjectMapper(); - - @SuppressWarnings("deprecation") - public void testGenericResolutionUsingDeprecated() throws Exception - { - Method proceed = BaseController.class.getMethod("process", BaseEntity.class); - Type entityType = proceed.getGenericParameterTypes()[0]; - - JavaType resolvedType = MAPPER.getTypeFactory().constructType(entityType, ImplController.class); - assertEquals(ImplEntity.class, resolvedType.getRawClass()); - } - - // and this is how new code should resolve types if at all possible - public void testGenericParameterViaClass() throws Exception - { - BeanDescription desc = MAPPER.getDeserializationConfig().introspect( - MAPPER.constructType(ImplController.class)); - AnnotatedClass ac = desc.getClassInfo(); - AnnotatedMethod m = ac.findMethod("process", new Class[] { BaseEntity.class }); - assertNotNull(m); - assertEquals(1, m.getParameterCount()); - AnnotatedParameter param = m.getParameter(0); - assertEquals(ImplEntity.class, param.getType().getRawClass()); - } -} diff --git a/src/test/java/com/fasterxml/jackson/databind/type/NestedTypes1604Test.java b/src/test/java/com/fasterxml/jackson/databind/type/NestedTypes1604Test.java index bf77b68941..e6adf5cc6a 100644 --- a/src/test/java/com/fasterxml/jackson/databind/type/NestedTypes1604Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/type/NestedTypes1604Test.java @@ -89,7 +89,7 @@ public DataList getInner() { } } - private final ObjectMapper objectMapper = newObjectMapper(); + private final ObjectMapper objectMapper = newJsonMapper(); public void testIssue1604Simple() throws Exception { diff --git a/src/test/java/com/fasterxml/jackson/databind/type/RecursiveType1658Test.java b/src/test/java/com/fasterxml/jackson/databind/type/RecursiveType1658Test.java index c211cd969e..4458d08c0d 100644 --- a/src/test/java/com/fasterxml/jackson/databind/type/RecursiveType1658Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/type/RecursiveType1658Test.java @@ -29,17 +29,13 @@ public List> getLeafTrees() { public void testRecursive1658() throws Exception { Tree t = new Tree(Arrays.asList("hello", "world")); - ObjectMapper mapper = new ObjectMapper(); - - final TypeResolverBuilder typer = new StdTypeResolverBuilder() - .init(JsonTypeInfo.Id.CLASS, null) - .inclusion(JsonTypeInfo.As.PROPERTY); - mapper.setDefaultTyping(typer); - + final TypeResolverBuilder typer = new StdTypeResolverBuilder(JsonTypeInfo.Id.CLASS, + JsonTypeInfo.As.PROPERTY, null); + ObjectMapper mapper = jsonMapperBuilder() + .setDefaultTyping(typer) + .build(); String res = mapper.writeValueAsString(t); - Tree tRead = mapper.readValue(res, Tree.class); - assertNotNull(tRead); } } diff --git a/src/test/java/com/fasterxml/jackson/databind/type/TestAnnotatedClass.java b/src/test/java/com/fasterxml/jackson/databind/type/TestAnnotatedClass.java index c5fb1a5568..e7f76a0b6c 100644 --- a/src/test/java/com/fasterxml/jackson/databind/type/TestAnnotatedClass.java +++ b/src/test/java/com/fasterxml/jackson/databind/type/TestAnnotatedClass.java @@ -79,7 +79,7 @@ private Bean1005(int i) {} public void testFieldIntrospection() { - SerializationConfig config = MAPPER.getSerializationConfig(); + SerializationConfig config = MAPPER.serializationConfig(); JavaType t = MAPPER.constructType(FieldBean.class); AnnotatedClass ac = AnnotatedClassResolver.resolve(config, t, config); // AnnotatedClass does not ignore non-visible fields, yet @@ -98,7 +98,7 @@ public void testConstructorIntrospection() // Need this call to ensure there is a synthetic constructor being generated // (not really needed otherwise) Bean1005 bean = new Bean1005(13); - SerializationConfig config = MAPPER.getSerializationConfig(); + SerializationConfig config = MAPPER.serializationConfig(); JavaType t = MAPPER.constructType(bean.getClass()); AnnotatedClass ac = AnnotatedClassResolver.resolve(config, t, config); assertEquals(1, ac.getConstructors().size()); @@ -106,7 +106,7 @@ public void testConstructorIntrospection() public void testArrayTypeIntrospection() throws Exception { - AnnotatedClass ac = AnnotatedClassResolver.resolve(MAPPER.getSerializationConfig(), + AnnotatedClass ac = AnnotatedClassResolver.resolve(MAPPER.serializationConfig(), MAPPER.constructType(int[].class), null); // 09-Jun-2017, tatu: During 2.9 development, access methods were failing at // certain points so @@ -116,7 +116,7 @@ public void testArrayTypeIntrospection() throws Exception public void testIntrospectionWithRawClass() throws Exception { - AnnotatedClass ac = AnnotatedClassResolver.resolveWithoutSuperTypes(MAPPER.getSerializationConfig(), + AnnotatedClass ac = AnnotatedClassResolver.resolveWithoutSuperTypes(MAPPER.serializationConfig(), String.class, null); // 09-Jun-2017, tatu: During 2.9 development, access methods were failing at // certain points so 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 ffe0b293d8..eb9cc0b883 100644 --- a/src/test/java/com/fasterxml/jackson/databind/type/TestJavaType.java +++ b/src/test/java/com/fasterxml/jackson/databind/type/TestJavaType.java @@ -90,23 +90,6 @@ public void testSimpleClass() assertEquals("Lcom/fasterxml/jackson/databind/type/TestJavaType$BaseType;", baseType.getErasedSignature()); } - @SuppressWarnings("deprecation") - public void testDeprecated() - { - TypeFactory tf = TypeFactory.defaultInstance(); - JavaType baseType = tf.constructType(BaseType.class); - assertTrue(baseType.hasRawClass(BaseType.class)); - assertNull(baseType.getParameterSource()); - assertNull(baseType.getContentTypeHandler()); - assertNull(baseType.getContentValueHandler()); - assertFalse(baseType.hasValueHandler()); - assertFalse(baseType.hasHandlers()); - - assertSame(baseType, baseType.forcedNarrowBy(BaseType.class)); - JavaType sub = baseType.forcedNarrowBy(SubType.class); - assertTrue(sub.hasRawClass(SubType.class)); - } - public void testArrayType() { TypeFactory tf = TypeFactory.defaultInstance(); @@ -170,10 +153,13 @@ public void testEnumType() public void testClassKey() { ClassKey key = new ClassKey(String.class); - assertEquals(0, key.compareTo(key)); + ClassKey keyToo = key; + int selfComparisonResult = key.compareTo(keyToo); + assertEquals(0, selfComparisonResult); assertTrue(key.equals(key)); assertFalse(key.equals(null)); - assertFalse(key.equals("foo")); + Object bogus = "foo"; + assertFalse(key.equals(bogus)); assertFalse(key.equals(new ClassKey(Integer.class))); assertEquals(String.class.getName(), key.toString()); } 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 60bcee8dd0..0cedfa543f 100644 --- a/src/test/java/com/fasterxml/jackson/databind/type/TestTypeFactory.java +++ b/src/test/java/com/fasterxml/jackson/databind/type/TestTypeFactory.java @@ -155,12 +155,11 @@ public void testIterator() * Test for verifying that parametric types can be constructed * programmatically */ - @SuppressWarnings("deprecation") public void testParametricTypes() { TypeFactory tf = TypeFactory.defaultInstance(); // first, simple class based - JavaType t = tf.constructParametrizedType(ArrayList.class, Collection.class, String.class); // ArrayList + JavaType t = tf.constructParametricType(ArrayList.class, String.class); // ArrayList assertEquals(CollectionType.class, t.getClass()); JavaType strC = tf.constructType(String.class); assertEquals(1, t.containedTypeCount()); @@ -168,7 +167,7 @@ public void testParametricTypes() assertNull(t.containedType(1)); // Then using JavaType - JavaType t2 = tf.constructParametrizedType(Map.class, Map.class, strC, t); // Map> + JavaType t2 = tf.constructParametricType(Map.class, strC, t); // Map> // should actually produce a MapType assertEquals(MapType.class, t2.getClass()); assertEquals(2, t2.containedTypeCount()); @@ -176,28 +175,38 @@ public void testParametricTypes() assertEquals(t, t2.containedType(1)); assertNull(t2.containedType(2)); - // and then custom generic type as well - JavaType custom = tf.constructParametrizedType(SingleArgGeneric.class, SingleArgGeneric.class, - String.class); + // Then using TypeBindings + JavaType t3 = tf.constructParametricType(HashSet.class, t.getBindings()); // HashSet + assertEquals(CollectionType.class, t3.getClass()); + assertEquals(1, t3.containedTypeCount()); + assertEquals(strC, t3.containedType(0)); + assertNull(t3.containedType(1)); + + // Then custom generic type as well + JavaType custom = tf.constructParametricType(SingleArgGeneric.class, String.class); assertEquals(SimpleType.class, custom.getClass()); assertEquals(1, custom.containedTypeCount()); assertEquals(strC, custom.containedType(0)); assertNull(custom.containedType(1)); - // should also be able to access variable name: - assertEquals("X", custom.containedTypeName(0)); + // and then custom generic type from TypeBindings + JavaType custom2 = tf.constructParametricType(SingleArgGeneric.class, t.getBindings()); + assertEquals(SimpleType.class, custom2.getClass()); + assertEquals(1, custom2.containedTypeCount()); + assertEquals(strC, custom2.containedType(0)); + assertNull(custom2.containedType(1)); // And finally, ensure that we can't create invalid combinations try { // Maps must take 2 type parameters, not just one - tf.constructParametrizedType(Map.class, Map.class, strC); + tf.constructParametricType(Map.class, strC); } catch (IllegalArgumentException e) { verifyException(e, "Cannot create TypeBindings for class java.util.Map"); } try { // Type only accepts one type param - tf.constructParametrizedType(SingleArgGeneric.class, SingleArgGeneric.class, strC, strC); + tf.constructParametricType(SingleArgGeneric.class, strC, strC); } catch (IllegalArgumentException e) { verifyException(e, "Cannot create TypeBindings for class "); } diff --git a/src/test/java/com/fasterxml/jackson/databind/type/TestTypeFactoryWithClassLoader.java b/src/test/java/com/fasterxml/jackson/databind/type/TestTypeFactoryWithClassLoader.java index fa787baaa6..288dc533eb 100644 --- a/src/test/java/com/fasterxml/jackson/databind/type/TestTypeFactoryWithClassLoader.java +++ b/src/test/java/com/fasterxml/jackson/databind/type/TestTypeFactoryWithClassLoader.java @@ -18,35 +18,58 @@ @RunWith(PowerMockRunner.class) @PrepareForTest(TypeFactory.class) -public class TestTypeFactoryWithClassLoader { - @Mock - private TypeModifier typeModifier; - private static ClassLoader classLoader; - private static ClassLoader threadClassLoader; - private static String aClassName; - private ObjectMapper mapper; - - @BeforeClass - public static void beforeClass() { +public class TestTypeFactoryWithClassLoader +{ + public static class AClass + { + private String _foo, _bar; + protected final static Class thisClass = new Object() { + }.getClass().getEnclosingClass(); + + public AClass() { } + public AClass(String foo, String bar) { + _foo = foo; + _bar = bar; + } + public String getFoo() { return _foo; } + public String getBar() { return _bar; } + + public void setFoo(String foo) { _foo = foo; } + public void setBar(String bar) { _bar = bar; } + public static String getStaticClassName() { + return thisClass.getCanonicalName().replace("."+thisClass.getSimpleName(), "$"+thisClass.getSimpleName()); + } + } + + @Mock + private TypeModifier typeModifier; + + private static ClassLoader classLoader; + private static ClassLoader threadClassLoader; + private static String aClassName; + private ObjectMapper mapper; + + @BeforeClass + public static void beforeClass() { classLoader = AClass.class.getClassLoader(); aClassName = AClass.getStaticClassName(); threadClassLoader = Thread.currentThread().getContextClassLoader(); Assert.assertNotNull(threadClassLoader); - } + } - @Before - public void before() { - mapper = new ObjectMapper(); - } + @Before + public void before() { + mapper = new ObjectMapper(); + } - @After - public void after() { - Thread.currentThread().setContextClassLoader(threadClassLoader); - mapper = null; - } + @After + public void after() { + Thread.currentThread().setContextClassLoader(threadClassLoader); + mapper = null; + } - @Test - public void testUsesCorrectClassLoaderWhenThreadClassLoaderIsNull() throws ClassNotFoundException { + @Test + public void testUsesCorrectClassLoaderWhenThreadClassLoaderIsNull() throws ClassNotFoundException { Thread.currentThread().setContextClassLoader(null); TypeFactory spySut = spy(mapper.getTypeFactory().withModifier(typeModifier).withClassLoader(classLoader)); Class clazz = spySut.findClass(aClassName); @@ -124,24 +147,4 @@ public void testUsesFallBackClassLoaderIfNoThreadClassLoaderAndNoWithClassLoader verify(spySut).classForName(any(String.class)); } - public static class AClass - { - private String _foo, _bar; - protected final static Class thisClass = new Object() { - }.getClass().getEnclosingClass(); - - public AClass() { } - public AClass(String foo, String bar) { - _foo = foo; - _bar = bar; - } - public String getFoo() { return _foo; } - public String getBar() { return _bar; } - - public void setFoo(String foo) { _foo = foo; } - public void setBar(String bar) { _bar = bar; } - public static String getStaticClassName() { - return thisClass.getCanonicalName().replace("."+thisClass.getSimpleName(), "$"+thisClass.getSimpleName()); - } - } } diff --git a/src/test/java/com/fasterxml/jackson/databind/util/BeanUtilTest.java b/src/test/java/com/fasterxml/jackson/databind/util/BeanUtilTest.java index afd1732926..dcf25a184d 100644 --- a/src/test/java/com/fasterxml/jackson/databind/util/BeanUtilTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/util/BeanUtilTest.java @@ -37,10 +37,8 @@ public void set() { } public void testNameMangle() { - assertEquals("foo", BeanUtil.legacyManglePropertyName("getFoo", 3)); assertEquals("foo", BeanUtil.stdManglePropertyName("getFoo", 3)); - assertEquals("url", BeanUtil.legacyManglePropertyName("getURL", 3)); assertEquals("URL", BeanUtil.stdManglePropertyName("getURL", 3)); } @@ -86,9 +84,9 @@ public void testOkNameForGetter() throws Exception public void testOkNameForSetter() throws Exception { - _testOkNameForSetter("setFoo", "foo"); - _testOkNameForSetter("notSetter", null); - _testOkNameForSetter("set", null); + _testOkNameForMutator("setFoo", "foo"); + _testOkNameForMutator("notSetter", null); + _testOkNameForMutator("set", null); } /* @@ -98,49 +96,32 @@ public void testOkNameForSetter() throws Exception */ private void _testIsGetter(String name, String expName) throws Exception { - _testIsGetter(name, expName, true); - _testIsGetter(name, expName, false); - } - - private void _testIsGetter(String name, String expName, boolean useStd) throws Exception - { AnnotatedMethod m = _method(IsGetters.class, name); if (expName == null) { - assertNull(BeanUtil.okNameForIsGetter(m, name, useStd)); + assertNull(BeanUtil.okNameForIsGetter(m, name)); } else { - assertEquals(expName, BeanUtil.okNameForIsGetter(m, name, useStd)); + assertEquals(expName, BeanUtil.okNameForIsGetter(m, name)); } } private void _testOkNameForGetter(String name, String expName) throws Exception { - _testOkNameForGetter(name, expName, true); - _testOkNameForGetter(name, expName, false); - } - - private void _testOkNameForGetter(String name, String expName, boolean useStd) throws Exception { AnnotatedMethod m = _method(Getters.class, name); if (expName == null) { - assertNull(BeanUtil.okNameForGetter(m, useStd)); + assertNull(BeanUtil.okNameForGetter(m)); } else { - assertEquals(expName, BeanUtil.okNameForGetter(m, useStd)); + assertEquals(expName, BeanUtil.okNameForGetter(m)); } } - private void _testOkNameForSetter(String name, String expName) throws Exception { - _testOkNameForSetter(name, expName, true); - _testOkNameForSetter(name, expName, false); - } - - @SuppressWarnings("deprecation") - private void _testOkNameForSetter(String name, String expName, boolean useStd) throws Exception { + private void _testOkNameForMutator(String name, String expName) throws Exception { AnnotatedMethod m = _method(Setters.class, name); if (expName == null) { - assertNull(BeanUtil.okNameForSetter(m, useStd)); + assertNull(BeanUtil.okNameForMutator(m, "set")); } else { - assertEquals(expName, BeanUtil.okNameForSetter(m, useStd)); + assertEquals(expName, BeanUtil.okNameForMutator(m, "set")); } } - + private AnnotatedMethod _method(Class cls, String name, Class...parameterTypes) throws Exception { return new AnnotatedMethod(null, cls.getMethod(name, parameterTypes), null, null); } diff --git a/src/test/java/com/fasterxml/jackson/databind/util/EnumValuesTest.java b/src/test/java/com/fasterxml/jackson/databind/util/EnumValuesTest.java index ccc3ea2634..5e5deb7b56 100644 --- a/src/test/java/com/fasterxml/jackson/databind/util/EnumValuesTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/util/EnumValuesTest.java @@ -26,7 +26,7 @@ enum ABC { @SuppressWarnings("unchecked") public void testConstructFromName() { - SerializationConfig cfg = MAPPER.getSerializationConfig() + SerializationConfig cfg = MAPPER.serializationConfig() .without(SerializationFeature.WRITE_ENUMS_USING_TO_STRING); Class> enumClass = (Class>)(Class) ABC.class; EnumValues values = EnumValues.construct(cfg, enumClass); @@ -39,7 +39,7 @@ public void testConstructFromName() { @SuppressWarnings("unchecked") public void testConstructWithToString() { - SerializationConfig cfg = MAPPER.getSerializationConfig() + SerializationConfig cfg = MAPPER.serializationConfig() .with(SerializationFeature.WRITE_ENUMS_USING_TO_STRING); Class> enumClass = (Class>)(Class) ABC.class; EnumValues values = EnumValues.construct(cfg, enumClass); diff --git a/src/test/java/com/fasterxml/jackson/databind/util/ISO8601DateFormatTest.java b/src/test/java/com/fasterxml/jackson/databind/util/ISO8601DateFormatTest.java deleted file mode 100644 index 20e941270c..0000000000 --- a/src/test/java/com/fasterxml/jackson/databind/util/ISO8601DateFormatTest.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.fasterxml.jackson.databind.util; - -import java.text.DateFormat; -import java.util.*; - - -import com.fasterxml.jackson.databind.BaseMapTest; - -@SuppressWarnings("deprecation") -public class ISO8601DateFormatTest extends BaseMapTest -{ - private ISO8601DateFormat df; - private Date date; - - @Override - public void setUp() - { - Calendar cal = new GregorianCalendar(2007, 8 - 1, 13, 19, 51, 23); - cal.setTimeZone(TimeZone.getTimeZone("GMT")); - cal.set(Calendar.MILLISECOND, 0); - date = cal.getTime(); - df = new ISO8601DateFormat(); - } - - public void testFormat() { - String result = df.format(date); - assertEquals("2007-08-13T19:51:23Z", result); - } - - public void testParse() throws Exception { - Date result = df.parse("2007-08-13T19:51:23Z"); - assertEquals(date, result); - - // Test parsing date-only values with and without a timezone designation - Date dateOnly = df.parse("2007-08-14"); - Calendar cal = new GregorianCalendar(2007, 8-1, 14); - assertEquals(cal.getTime(), dateOnly); - - dateOnly = df.parse("2007-08-14Z"); - cal = new GregorianCalendar(2007, 8-1, 14); - cal.setTimeZone(TimeZone.getTimeZone("GMT")); - assertEquals(cal.getTime(), dateOnly); - } - - public void testPartialParse() throws Exception { - java.text.ParsePosition pos = new java.text.ParsePosition(0); - String timestamp = "2007-08-13T19:51:23Z"; - Date result = df.parse(timestamp + "hello", pos); - - assertEquals(date, result); - assertEquals(timestamp.length(), pos.getIndex()); - } - - public void testCloneObject() throws Exception { - DateFormat clone = (DateFormat)df.clone(); - assertSame(df, clone); - } - - public void testHashCodeEquals() throws Exception { - // for [databind#1130] - DateFormat defaultDF = StdDateFormat.instance; - defaultDF.hashCode(); - assertTrue(defaultDF.equals(defaultDF)); - } -} diff --git a/src/test/java/com/fasterxml/jackson/databind/util/ISO8601UtilsTest.java b/src/test/java/com/fasterxml/jackson/databind/util/ISO8601UtilsTest.java deleted file mode 100644 index 5ce6751362..0000000000 --- a/src/test/java/com/fasterxml/jackson/databind/util/ISO8601UtilsTest.java +++ /dev/null @@ -1,201 +0,0 @@ -package com.fasterxml.jackson.databind.util; - -import com.fasterxml.jackson.databind.BaseMapTest; - -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 java.util.concurrent.TimeUnit; - -@SuppressWarnings("deprecation") -public class ISO8601UtilsTest extends BaseMapTest -{ - private Date date; - private Date dateWithoutTime; - private Date dateZeroMillis; - private Date dateZeroSecondAndMillis; - - @Override - 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() { - String result = ISO8601Utils.format(date); - assertEquals("2007-08-13T19:51:23Z", result); - } - - public void testFormatMillis() { - String result = ISO8601Utils.format(date, true); - assertEquals("2007-08-13T19:51:23.789Z", result); - - result = ISO8601Utils.format(date, false); - assertEquals("2007-08-13T19:51:23Z", result); - } - - public void testFormatTimeZone() { - String result = ISO8601Utils.format(date, false, TimeZone.getTimeZone("GMT+02:00")); - assertEquals("2007-08-13T21:51:23+02:00", result); - result = ISO8601Utils.format(date, true, TimeZone.getTimeZone("GMT+02:00")); - assertEquals("2007-08-13T21:51:23.789+02:00", result); - result = ISO8601Utils.format(date, true, TimeZone.getTimeZone("GMT")); - assertEquals("2007-08-13T19:51:23.789Z", result); - } - - public void testParse() throws java.text.ParseException { - Date d = ISO8601Utils.parse("2007-08-13T19:51:23.789Z", new ParsePosition(0)); - assertEquals(date, d); - - d = ISO8601Utils.parse("2007-08-13T19:51:23Z", new ParsePosition(0)); - assertEquals(dateZeroMillis, d); - - d = ISO8601Utils.parse("2007-08-13T21:51:23.789+02:00", new ParsePosition(0)); - 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 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); - } - - public void testParseRfc3339Examples() throws java.text.ParseException { - // Two digit milliseconds. - Date d = ISO8601Utils.parse("1985-04-12T23:20:50.52Z", new ParsePosition(0)); - assertEquals(newDate(1985, 4, 12, 23, 20, 50, 520, 0), d); - - d = ISO8601Utils.parse("1996-12-19T16:39:57-08:00", new ParsePosition(0)); - assertEquals(newDate(1996, 12, 19, 16, 39, 57, 0, -8 * 60), d); - - // Truncated leap second. - d = ISO8601Utils.parse("1990-12-31T23:59:60Z", new ParsePosition(0)); - assertEquals(newDate(1990, 12, 31, 23, 59, 59, 0, 0), d); - - // Truncated leap second. - d = ISO8601Utils.parse("1990-12-31T15:59:60-08:00", new ParsePosition(0)); - assertEquals(newDate(1990, 12, 31, 15, 59, 59, 0, -8 * 60), d); - - // Two digit milliseconds. - d = ISO8601Utils.parse("1937-01-01T12:00:27.87+00:20", new ParsePosition(0)); - assertEquals(newDate(1937, 1, 1, 12, 0, 27, 870, 20), d); - } - - public void testFractionalSeconds() throws java.text.ParseException { - Date d = ISO8601Utils.parse("1970-01-01T00:00:00.9Z", new ParsePosition(0)); - assertEquals(newDate(1970, 1, 1, 0, 0, 0, 900, 0), d); - - d = ISO8601Utils.parse("1970-01-01T00:00:00.09Z", new ParsePosition(0)); - assertEquals(newDate(1970, 1, 1, 0, 0, 0, 90, 0), d); - - d = ISO8601Utils.parse("1970-01-01T00:00:00.009Z", new ParsePosition(0)); - assertEquals(newDate(1970, 1, 1, 0, 0, 0, 9, 0), d); - - d = ISO8601Utils.parse("1970-01-01T00:00:00.0009Z", new ParsePosition(0)); - assertEquals(newDate(1970, 1, 1, 0, 0, 0, 0, 0), d); - - d = ISO8601Utils.parse("1970-01-01T00:00:00.2147483647Z", new ParsePosition(0)); - assertEquals(newDate(1970, 1, 1, 0, 0, 0, 214, 0), d); - - d = ISO8601Utils.parse("1970-01-01T00:00:00.2147483648Z", new ParsePosition(0)); - assertEquals(newDate(1970, 1, 1, 0, 0, 0, 214, 0), d); - - d = ISO8601Utils.parse("1970-01-01T00:00:00.9+02:00", new ParsePosition(0)); - assertEquals(newDate(1970, 1, 1, 0, 0, 0, 900, 2 * 60), d); - - d = ISO8601Utils.parse("1970-01-01T00:00:00.09+02:00", new ParsePosition(0)); - assertEquals(newDate(1970, 1, 1, 0, 0, 0, 90, 2 * 60), d); - - d = ISO8601Utils.parse("1970-01-01T00:00:00.009+02:00", new ParsePosition(0)); - assertEquals(newDate(1970, 1, 1, 0, 0, 0, 9, 2 * 60), d); - - d = ISO8601Utils.parse("1970-01-01T00:00:00.0009+02:00", new ParsePosition(0)); - assertEquals(newDate(1970, 1, 1, 0, 0, 0, 0, 2 * 60), d); - - d = ISO8601Utils.parse("1970-01-01T00:00:00.2147483648+02:00", new ParsePosition(0)); - assertEquals(newDate(1970, 1, 1, 0, 0, 0, 214, 2 * 60), d); - } - - public void testDecimalWithoutDecimalPointButNoFractionalSeconds() throws java.text.ParseException { - try { - ISO8601Utils.parse("1970-01-01T00:00:00.Z", new ParsePosition(0)); - fail(); - } catch (ParseException expected) { - } - } - - private Date newDate(int year, int month, int day, int hour, - int minute, int second, int millis, int timezoneOffsetMinutes) { - Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("GMT")); - calendar.set(year, month - 1, day, hour, minute, second); - calendar.set(Calendar.MILLISECOND, millis); - return new Date(calendar.getTimeInMillis() - TimeUnit.MINUTES.toMillis(timezoneOffsetMinutes)); - } -} diff --git a/src/test/java/com/fasterxml/jackson/databind/util/JsonParserSequenceTest.java b/src/test/java/com/fasterxml/jackson/databind/util/JsonParserSequenceTest.java index b7b62280a5..81461043bb 100644 --- a/src/test/java/com/fasterxml/jackson/databind/util/JsonParserSequenceTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/util/JsonParserSequenceTest.java @@ -4,12 +4,9 @@ import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.core.util.JsonParserSequence; import com.fasterxml.jackson.databind.BaseMapTest; -import com.fasterxml.jackson.databind.ObjectMapper; - -public class JsonParserSequenceTest extends BaseMapTest { - - private final ObjectMapper MAPPER = objectMapper(); +public class JsonParserSequenceTest extends BaseMapTest +{ /** * Verifies fix for [core#372] */ @@ -17,14 +14,14 @@ public class JsonParserSequenceTest extends BaseMapTest { public void testJsonParserSequenceOverridesSkipChildren() throws Exception { // Create parser from TokenBuffer containing an incomplete JSON object - TokenBuffer buf1 = new TokenBuffer(MAPPER, false); + TokenBuffer buf1 = TokenBuffer.forGeneration(); buf1.writeStartObject(); buf1.writeFieldName("foo"); buf1.writeStartObject(); JsonParser parser1 = buf1.asParser(); // Create parser from second TokenBuffer that completes the object started by the first buffer - TokenBuffer buf2 = new TokenBuffer(MAPPER, false); + TokenBuffer buf2 = TokenBuffer.forGeneration(); buf2.writeEndObject(); buf2.writeEndObject(); JsonParser parser2 = buf2.asParser(); diff --git a/src/test/java/com/fasterxml/jackson/databind/util/TestStdDateFormat.java b/src/test/java/com/fasterxml/jackson/databind/util/TestStdDateFormat.java index ec9954576c..58c577da5d 100644 --- a/src/test/java/com/fasterxml/jackson/databind/util/TestStdDateFormat.java +++ b/src/test/java/com/fasterxml/jackson/databind/util/TestStdDateFormat.java @@ -8,17 +8,8 @@ import com.fasterxml.jackson.databind.BaseMapTest; import com.fasterxml.jackson.databind.util.StdDateFormat; -public class TestStdDateFormat - extends BaseMapTest +public class TestStdDateFormat extends BaseMapTest { - @SuppressWarnings("deprecation") - public void testFactories() { - TimeZone tz = TimeZone.getTimeZone("GMT"); - Locale loc = Locale.US; - assertNotNull(StdDateFormat.getISO8601Format(tz, loc)); - assertNotNull(StdDateFormat.getRFC1123Format(tz, loc)); - } - // [databind#803] public void testLenientDefaults() throws Exception { 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 297313e6b3..22b548ccec 100644 --- a/src/test/java/com/fasterxml/jackson/databind/util/TestTokenBuffer.java +++ b/src/test/java/com/fasterxml/jackson/databind/util/TestTokenBuffer.java @@ -31,21 +31,16 @@ public void testBasicConfig() throws IOException { TokenBuffer buf; - buf = new TokenBuffer(MAPPER, false); + buf = TokenBuffer.forGeneration(); assertEquals(MAPPER.version(), buf.version()); - assertSame(MAPPER, buf.getCodec()); assertNotNull(buf.getOutputContext()); assertFalse(buf.isClosed()); - buf.setCodec(null); - assertNull(buf.getCodec()); - - assertFalse(buf.isEnabled(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN)); - buf.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN); - assertTrue(buf.isEnabled(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN)); - buf.disable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN); - assertFalse(buf.isEnabled(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN)); - + assertFalse(buf.isEnabled(StreamWriteFeature.WRITE_BIGDECIMAL_AS_PLAIN)); + buf.enable(StreamWriteFeature.WRITE_BIGDECIMAL_AS_PLAIN); + assertTrue(buf.isEnabled(StreamWriteFeature.WRITE_BIGDECIMAL_AS_PLAIN)); + buf.disable(StreamWriteFeature.WRITE_BIGDECIMAL_AS_PLAIN); + assertFalse(buf.isEnabled(StreamWriteFeature.WRITE_BIGDECIMAL_AS_PLAIN)); buf.close(); assertTrue(buf.isClosed()); } @@ -55,11 +50,11 @@ public void testBasicConfig() throws IOException */ public void testSimpleWrites() throws IOException { - TokenBuffer buf = new TokenBuffer(null, false); // no ObjectCodec + TokenBuffer buf = TokenBuffer.forGeneration(); // First, with empty buffer JsonParser p = buf.asParser(); - assertNull(p.getCurrentToken()); + assertNull(p.currentToken()); assertNull(p.nextToken()); p.close(); @@ -67,7 +62,7 @@ public void testSimpleWrites() throws IOException buf.writeString("abc"); p = buf.asParser(); - assertNull(p.getCurrentToken()); + assertNull(p.currentToken()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("abc", p.getText()); assertNull(p.nextToken()); @@ -76,7 +71,7 @@ public void testSimpleWrites() throws IOException // Then, let's append at root level buf.writeNumber(13); p = buf.asParser(); - assertNull(p.getCurrentToken()); + assertNull(p.currentToken()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(13, p.getIntValue()); @@ -88,7 +83,7 @@ public void testSimpleWrites() throws IOException // For 2.9, explicit "isNaN" check public void testSimpleNumberWrites() throws IOException { - TokenBuffer buf = new TokenBuffer(null, false); + TokenBuffer buf = TokenBuffer.forGeneration(); double[] values1 = new double[] { 0.25, Double.NaN, -2.0, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY @@ -107,7 +102,7 @@ public void testSimpleNumberWrites() throws IOException } JsonParser p = buf.asParser(); - assertNull(p.getCurrentToken()); + assertNull(p.currentToken()); for (double v : values1) { assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); @@ -140,7 +135,7 @@ public void testNumberOverflowInt() throws IOException p.getIntValue(); fail("Expected failure for `int` overflow"); } catch (InputCoercionException e) { - verifyException(e, "Numeric value ("+big+") out of range of int"); + verifyException(e, "Numeric value ("+big+") out of range of `int`"); } } } @@ -155,7 +150,7 @@ public void testNumberOverflowInt() throws IOException p.getIntValue(); fail("Expected failure for `int` overflow"); } catch (InputCoercionException e) { - verifyException(e, "Numeric value ("+big+") out of range of int"); + verifyException(e, "Numeric value ("+big+") out of range of `int`"); } } } @@ -173,7 +168,7 @@ public void testNumberOverflowLong() throws IOException p.getLongValue(); fail("Expected failure for `long` overflow"); } catch (InputCoercionException e) { - verifyException(e, "Numeric value ("+big+") out of range of long"); + verifyException(e, "Numeric value ("+big+") out of range of `long`"); } } } @@ -181,13 +176,13 @@ public void testNumberOverflowLong() throws IOException public void testParentContext() throws IOException { - TokenBuffer buf = new TokenBuffer(null, false); // no ObjectCodec + TokenBuffer buf = TokenBuffer.forGeneration(); buf.writeStartObject(); buf.writeFieldName("b"); buf.writeStartObject(); buf.writeFieldName("c"); //This assertion succeeds as expected - assertEquals("b", buf.getOutputContext().getParent().getCurrentName()); + assertEquals("b", buf.getOutputContext().getParent().currentName()); buf.writeString("cval"); buf.writeEndObject(); buf.writeEndObject(); @@ -196,7 +191,7 @@ public void testParentContext() throws IOException public void testSimpleArray() throws IOException { - TokenBuffer buf = new TokenBuffer(null, false); // no ObjectCodec + TokenBuffer buf = TokenBuffer.forGeneration(); // First, empty array assertTrue(buf.getOutputContext().inRoot()); @@ -206,7 +201,7 @@ public void testSimpleArray() throws IOException assertTrue(buf.getOutputContext().inRoot()); JsonParser p = buf.asParser(); - assertNull(p.getCurrentToken()); + assertNull(p.currentToken()); assertTrue(p.getParsingContext().inRoot()); assertToken(JsonToken.START_ARRAY, p.nextToken()); assertTrue(p.getParsingContext().inArray()); @@ -217,7 +212,7 @@ public void testSimpleArray() throws IOException buf.close(); // Then one with simple contents - buf = new TokenBuffer(null, false); + buf = TokenBuffer.forGeneration(); buf.writeStartArray(); buf.writeBoolean(true); buf.writeNull(); @@ -233,7 +228,7 @@ public void testSimpleArray() throws IOException buf.close(); // And finally, with array-in-array - buf = new TokenBuffer(null, false); + buf = TokenBuffer.forGeneration(); buf.writeStartArray(); buf.writeStartArray(); buf.writeBinary(new byte[3]); @@ -257,7 +252,7 @@ public void testSimpleArray() throws IOException public void testSimpleObject() throws IOException { - TokenBuffer buf = new TokenBuffer(null, false); + TokenBuffer buf = TokenBuffer.forGeneration(); // First, empty JSON Object assertTrue(buf.getOutputContext().inRoot()); @@ -267,7 +262,7 @@ public void testSimpleObject() throws IOException assertTrue(buf.getOutputContext().inRoot()); JsonParser p = buf.asParser(); - assertNull(p.getCurrentToken()); + assertNull(p.currentToken()); assertTrue(p.getParsingContext().inRoot()); assertToken(JsonToken.START_OBJECT, p.nextToken()); assertTrue(p.getParsingContext().inObject()); @@ -278,28 +273,28 @@ public void testSimpleObject() throws IOException buf.close(); // Then one with simple contents - buf = new TokenBuffer(null, false); + buf = TokenBuffer.forGeneration(); buf.writeStartObject(); buf.writeNumberField("num", 1.25); buf.writeEndObject(); p = buf.asParser(); - assertNull(p.getCurrentToken()); + assertNull(p.currentToken()); assertToken(JsonToken.START_OBJECT, p.nextToken()); - assertNull(p.getCurrentName()); + assertNull(p.currentName()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); - assertEquals("num", p.getCurrentName()); + assertEquals("num", p.currentName()); // and override should also work: p.overrideCurrentName("bah"); - assertEquals("bah", p.getCurrentName()); + assertEquals("bah", p.currentName()); assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); assertEquals(1.25, p.getDoubleValue()); // should still have access to (overridden) name - assertEquals("bah", p.getCurrentName()); + assertEquals("bah", p.currentName()); assertToken(JsonToken.END_OBJECT, p.nextToken()); // but not any more - assertNull(p.getCurrentName()); + assertNull(p.currentName()); assertNull(p.nextToken()); p.close(); buf.close(); @@ -313,7 +308,7 @@ public void testWithJSONSampleDoc() throws Exception { // First, copy events from known good source (StringReader) JsonParser p = createParserUsingReader(SAMPLE_DOC_JSON_SPEC); - TokenBuffer tb = new TokenBuffer(null, false); + TokenBuffer tb = TokenBuffer.forGeneration(); while (p.nextToken() != null) { tb.copyCurrentEvent(p); } @@ -334,12 +329,12 @@ public void testWithJSONSampleDoc() throws Exception public void testAppend() throws IOException { - TokenBuffer buf1 = new TokenBuffer(null, false); + TokenBuffer buf1 = TokenBuffer.forGeneration(); buf1.writeStartObject(); buf1.writeFieldName("a"); buf1.writeBoolean(true); - TokenBuffer buf2 = new TokenBuffer(null, false); + TokenBuffer buf2 = TokenBuffer.forGeneration(); buf2.writeFieldName("b"); buf2.writeNumber(13); buf2.writeEndObject(); @@ -350,10 +345,10 @@ public void testAppend() throws IOException JsonParser p = buf1.asParser(); assertToken(JsonToken.START_OBJECT, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); - assertEquals("a", p.getCurrentName()); + assertEquals("a", p.currentName()); assertToken(JsonToken.VALUE_TRUE, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); - assertEquals("b", p.getCurrentName()); + assertEquals("b", p.currentName()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(13, p.getIntValue()); assertToken(JsonToken.END_OBJECT, p.nextToken()); @@ -374,7 +369,7 @@ public void testWithUUID() throws IOException "591b2869-146e-41d7-8048-e8131f1fdec5", "82994ac2-7b23-49f2-8cc5-e24cf6ed77be", }) { - TokenBuffer buf = new TokenBuffer(MAPPER, false); // no ObjectCodec + TokenBuffer buf = TokenBuffer.forGeneration(); UUID uuid = UUID.fromString(value); MAPPER.writeValue(buf, uuid); buf.close(); @@ -401,9 +396,9 @@ public void testWithUUID() throws IOException // for [databind#984]: ensure output context handling identical public void testOutputContext() throws IOException { - TokenBuffer buf = new TokenBuffer(null, false); // no ObjectCodec + TokenBuffer buf = TokenBuffer.forGeneration(); StringWriter w = new StringWriter(); - JsonGenerator gen = MAPPER.getFactory().createGenerator(w); + JsonGenerator gen = MAPPER.createGenerator(w); // test content: [{"a":1,"b":{"c":2}},{"a":2,"b":{"c":3}}] @@ -460,7 +455,7 @@ private void _verifyOutputContext(JsonGenerator gen1, JsonGenerator gen2) _verifyOutputContext(gen1.getOutputContext(), gen2.getOutputContext()); } - private void _verifyOutputContext(JsonStreamContext ctxt1, JsonStreamContext ctxt2) + private void _verifyOutputContext(TokenStreamContext ctxt1, TokenStreamContext ctxt2) { if (ctxt1 == null) { if (ctxt2 == null) { @@ -476,8 +471,8 @@ private void _verifyOutputContext(JsonStreamContext ctxt1, JsonStreamContext ctx if (ctxt1.inObject()) { assertTrue(ctxt2.inObject()); - String str1 = ctxt1.getCurrentName(); - String str2 = ctxt2.getCurrentName(); + String str1 = ctxt1.currentName(); + String str2 = ctxt2.currentName(); if ((str1 != str2) && !str1.equals(str2)) { fail("Expected name '"+str2+"' (JsonParser), TokenBuffer had '"+str1+"'"); @@ -492,7 +487,7 @@ private void _verifyOutputContext(JsonStreamContext ctxt1, JsonStreamContext ctx // [databind#1253] public void testParentSiblingContext() throws IOException { - TokenBuffer buf = new TokenBuffer(null, false); // no ObjectCodec + TokenBuffer buf = TokenBuffer.forGeneration(); // {"a":{},"b":{"c":"cval"}} @@ -505,7 +500,7 @@ public void testParentSiblingContext() throws IOException buf.writeStartObject(); buf.writeFieldName("c"); //This assertion fails (because of 'a') - assertEquals("b", buf.getOutputContext().getParent().getCurrentName()); + assertEquals("b", buf.getOutputContext().getParent().currentName()); buf.writeString("cval"); buf.writeEndObject(); buf.writeEndObject(); @@ -517,11 +512,11 @@ public void testBasicSerialize() throws IOException TokenBuffer buf; // let's see how empty works... - buf = new TokenBuffer(MAPPER, false); + buf = TokenBuffer.forGeneration(); assertEquals("", MAPPER.writeValueAsString(buf)); buf.close(); - buf = new TokenBuffer(MAPPER, false); + buf = TokenBuffer.forGeneration(); buf.writeStartArray(); buf.writeBoolean(true); buf.writeBoolean(false); @@ -533,7 +528,7 @@ public void testBasicSerialize() throws IOException assertEquals(aposToQuotes("[true,false,"+l+",4,0.5]"), MAPPER.writeValueAsString(buf)); buf.close(); - buf = new TokenBuffer(MAPPER, false); + buf = TokenBuffer.forGeneration(); buf.writeStartObject(); buf.writeFieldName(new SerializedString("foo")); buf.writeNull(); @@ -554,7 +549,7 @@ public void testBasicSerialize() throws IOException public void testWithJsonParserSequenceSimple() throws IOException { // Let's join a TokenBuffer with JsonParser first - TokenBuffer buf = new TokenBuffer(null, false); + TokenBuffer buf = TokenBuffer.forGeneration(); buf.writeStartArray(); buf.writeString("test"); JsonParser p = createParserUsingReader("[ true, null ]"); @@ -565,8 +560,8 @@ public void testWithJsonParserSequenceSimple() throws IOException assertFalse(p.isClosed()); assertFalse(seq.hasCurrentToken()); - assertNull(seq.getCurrentToken()); - assertNull(seq.getCurrentName()); + assertNull(seq.currentToken()); + assertNull(seq.currentName()); assertToken(JsonToken.START_ARRAY, seq.nextToken()); assertToken(JsonToken.VALUE_STRING, seq.nextToken()); @@ -603,13 +598,13 @@ public void testWithJsonParserSequenceSimple() throws IOException @SuppressWarnings("resource") public void testWithMultipleJsonParserSequences() throws IOException { - TokenBuffer buf1 = new TokenBuffer(null, false); + TokenBuffer buf1 = TokenBuffer.forGeneration(); buf1.writeStartArray(); - TokenBuffer buf2 = new TokenBuffer(null, false); + TokenBuffer buf2 = TokenBuffer.forGeneration(); buf2.writeString("a"); - TokenBuffer buf3 = new TokenBuffer(null, false); + TokenBuffer buf3 = TokenBuffer.forGeneration(); buf3.writeNumber(13); - TokenBuffer buf4 = new TokenBuffer(null, false); + TokenBuffer buf4 = TokenBuffer.forGeneration(); buf4.writeEndArray(); JsonParserSequence seq1 = JsonParserSequence.createFlattened(false, buf1.asParser(), buf2.asParser()); @@ -637,7 +632,7 @@ public void testWithMultipleJsonParserSequences() throws IOException public void testRawValues() throws Exception { final String RAW = "{\"a\":1}"; - TokenBuffer buf = new TokenBuffer(null, false); + TokenBuffer buf = TokenBuffer.forGeneration(); buf.writeRawValue(RAW); // first: raw value won't be transformed in any way: JsonParser p = buf.asParser(); @@ -654,7 +649,7 @@ public void testRawValues() throws Exception // [databind#1730] public void testEmbeddedObjectCoerceCheck() throws Exception { - TokenBuffer buf = new TokenBuffer(null, false); + TokenBuffer buf = TokenBuffer.forGeneration(); Object inputPojo = new Sub1730(); buf.writeEmbeddedObject(inputPojo); diff --git a/src/test/java/com/fasterxml/jackson/databind/views/TestViewDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/views/TestViewDeserialization.java index 53a8af65b2..ac0602fc5b 100644 --- a/src/test/java/com/fasterxml/jackson/databind/views/TestViewDeserialization.java +++ b/src/test/java/com/fasterxml/jackson/databind/views/TestViewDeserialization.java @@ -103,7 +103,7 @@ public void testWithoutDefaultInclusion() throws Exception assertEquals(3, bean.a); assertEquals(9, bean.b); - ObjectMapper myMapper = objectMapperBuilder() + ObjectMapper myMapper = jsonMapperBuilder() .disable(MapperFeature.DEFAULT_VIEW_INCLUSION) .build(); diff --git a/src/test/java/com/fasterxml/jackson/databind/views/TestViewSerialization.java b/src/test/java/com/fasterxml/jackson/databind/views/TestViewSerialization.java index f0a999af31..35814bc7f6 100644 --- a/src/test/java/com/fasterxml/jackson/databind/views/TestViewSerialization.java +++ b/src/test/java/com/fasterxml/jackson/databind/views/TestViewSerialization.java @@ -140,11 +140,11 @@ public void testDefaultExclusion() throws IOException assertEquals("2", map.get("b")); // but can also change (but not necessarily on the fly...) - ObjectMapper mapper = objectMapperBuilder() - .configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false) + ObjectMapper mapper = jsonMapperBuilder() + .disable(MapperFeature.DEFAULT_VIEW_INCLUSION) .build(); - // with this setting, only explicit inclusions count: + // with this setting, only explicit inclusions count: json = mapper.writerWithView(ViewA.class).writeValueAsString(bean); map = mapper.readValue(json, Map.class); assertEquals(1, map.size()); @@ -181,9 +181,10 @@ public void testVisibility() throws Exception // [JACKSON-868] public void test868() throws IOException { - ObjectMapper mapper = new ObjectMapper(); - mapper.setSerializationInclusion(JsonInclude.Include.NON_DEFAULT); - String json = mapper.writerWithView(OtherView.class).writeValueAsString(new Foo()); - assertEquals(json, "{}"); + ObjectMapper mapper = jsonMapperBuilder() + .changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_DEFAULT)) + .build(); + assertEquals("{}", + mapper.writerWithView(OtherView.class).writeValueAsString(new Foo())); } } diff --git a/src/test/java/com/fasterxml/jackson/databind/views/TestViewsSerialization2.java b/src/test/java/com/fasterxml/jackson/databind/views/TestViewsSerialization2.java index cbc7a7dc04..a7db5b5325 100644 --- a/src/test/java/com/fasterxml/jackson/databind/views/TestViewsSerialization2.java +++ b/src/test/java/com/fasterxml/jackson/databind/views/TestViewsSerialization2.java @@ -143,10 +143,10 @@ public void testDataBindingUsageWithoutView( ) throws Exception private ObjectMapper createMapper() { - ObjectMapper mapper = objectMapperBuilder() - .configure( SerializationFeature.FAIL_ON_EMPTY_BEANS, false ) - .serializationInclusion(JsonInclude.Include.NON_NULL ) - .configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false ) + ObjectMapper mapper = jsonMapperBuilder() + .changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_NULL)) + .disable(MapperFeature.DEFAULT_VIEW_INCLUSION) + .disable( SerializationFeature.FAIL_ON_EMPTY_BEANS) .build(); return mapper; } @@ -156,5 +156,4 @@ private String serializeWithObjectMapper(Object object, Class map) { // [databind#1877] public void testEnumAsIndexMapKey() throws Exception { - ObjectMapper mapper = newObjectMapper(); + ObjectMapper mapper = newJsonMapper(); Map map = new HashMap<>(); map.put(Type.OTHER, "hello world"); diff --git a/src/test/java/com/fasterxml/jackson/failing/KevinFail1410Test.java b/src/test/java/com/fasterxml/jackson/failing/ExternalTypeIdDup1410Test.java similarity index 92% rename from src/test/java/com/fasterxml/jackson/failing/KevinFail1410Test.java rename to src/test/java/com/fasterxml/jackson/failing/ExternalTypeIdDup1410Test.java index 8cba59c933..477412693d 100644 --- a/src/test/java/com/fasterxml/jackson/failing/KevinFail1410Test.java +++ b/src/test/java/com/fasterxml/jackson/failing/ExternalTypeIdDup1410Test.java @@ -4,7 +4,10 @@ import com.fasterxml.jackson.databind.*; -public class KevinFail1410Test extends BaseMapTest +// [databind#1410] +// Problem with External Type Id, existing regular property: should not write +// twice, but currently does. +public class ExternalTypeIdDup1410Test extends BaseMapTest { enum EnvironmentEventSource { BACKEND; } diff --git a/src/test/java/com/fasterxml/jackson/failing/ExternalTypeIdWithUnwrapped2039Test.java b/src/test/java/com/fasterxml/jackson/failing/ExternalTypeIdWithUnwrapped2039Test.java index a81f6aba6c..182caa786b 100644 --- a/src/test/java/com/fasterxml/jackson/failing/ExternalTypeIdWithUnwrapped2039Test.java +++ b/src/test/java/com/fasterxml/jackson/failing/ExternalTypeIdWithUnwrapped2039Test.java @@ -36,7 +36,7 @@ public static class SubA2039 extends SubType2039 { public void testExternalWithUnwrapped2039() throws Exception { - final ObjectMapper mapper = newObjectMapper(); + final ObjectMapper mapper = newJsonMapper(); final String json = aposToQuotes("{\n" +"'text': 'this is A',\n" diff --git a/src/test/java/com/fasterxml/jackson/failing/ImplicitParamsForCreator806Test.java b/src/test/java/com/fasterxml/jackson/failing/ImplicitParamsForCreator806Test.java index 890f576539..cf0a4461b7 100644 --- a/src/test/java/com/fasterxml/jackson/failing/ImplicitParamsForCreator806Test.java +++ b/src/test/java/com/fasterxml/jackson/failing/ImplicitParamsForCreator806Test.java @@ -32,21 +32,20 @@ public XY(int x, int y) { } /* - /********************************************************** + /********************************************************************** /* Test methods - /********************************************************** + /********************************************************************** */ - private final ObjectMapper MAPPER = newObjectMapper() - .setAnnotationIntrospector(new MyParamIntrospector()) - .setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE) - ; - // for [databind#806]: problem is that renaming occurs too late for implicitly detected // Creators public void testImplicitNameWithNamingStrategy() throws Exception { - XY value = MAPPER.readValue(aposToQuotes("{'param_name0':1,'param_name1':2}"), XY.class); + ObjectMapper mapper = jsonMapperBuilder() + .propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE) + .annotationIntrospector(new MyParamIntrospector()) + .build(); + XY value = mapper.readValue(aposToQuotes("{'param_name0':1,'param_name1':2}"), XY.class); assertNotNull(value); assertEquals(1, value.x); assertEquals(2, value.y); diff --git a/src/test/java/com/fasterxml/jackson/failing/InnerClassNonStaticCore384Test.java b/src/test/java/com/fasterxml/jackson/failing/InnerClassNonStaticCore384Test.java index 0ef5e76911..a6bc90eb1c 100644 --- a/src/test/java/com/fasterxml/jackson/failing/InnerClassNonStaticCore384Test.java +++ b/src/test/java/com/fasterxml/jackson/failing/InnerClassNonStaticCore384Test.java @@ -170,8 +170,9 @@ public int hashCode() { */ public void testHierarchy() throws IOException { - ObjectMapper mapper = new ObjectMapper(); - mapper.enableDefaultTyping(); + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping() + .build(); Fleet fleet = initVehicle(); diff --git a/src/test/java/com/fasterxml/jackson/failing/MapFormatShape1419Test.java b/src/test/java/com/fasterxml/jackson/failing/MapFormatShape1419Test.java new file mode 100644 index 0000000000..ff6d731afb --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/failing/MapFormatShape1419Test.java @@ -0,0 +1,79 @@ +package com.fasterxml.jackson.failing; + +import java.util.*; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.*; + +@SuppressWarnings("serial") +public class MapFormatShape1419Test extends BaseMapTest +{ + @JsonPropertyOrder({ "extra" }) + static class Map476Base extends LinkedHashMap { + public int extra = 13; + } + + @JsonFormat(shape=JsonFormat.Shape.POJO) + static class Map476AsPOJO extends Map476Base { } + + @JsonPropertyOrder({ "a", "b", "c" }) + @JsonInclude(JsonInclude.Include.NON_NULL) + static class Bean476Container + { + public Map476AsPOJO a; + public Map476Base b; + @JsonFormat(shape=JsonFormat.Shape.POJO) + public Map476Base c; + + public Bean476Container(int forA, int forB, int forC) { + if (forA != 0) { + a = new Map476AsPOJO(); + a.put("value", forA); + } + if (forB != 0) { + b = new Map476Base(); + b.put("value", forB); + } + if (forC != 0) { + c = new Map476Base(); + c.put("value", forC); + } + } + } + + static class Bean476Override + { + @JsonFormat(shape=JsonFormat.Shape.NATURAL) + public Map476AsPOJO stuff; + + public Bean476Override(int value) { + stuff = new Map476AsPOJO(); + stuff.put("value", value); + } + } + + /* + /********************************************************** + /* Test methods, serialization + /********************************************************** + */ + + final private ObjectMapper MAPPER = objectMapper(); + + // Can't yet use per-property overrides at all, see [databind#1419] + public void testSerializeAsPOJOViaProperty() throws Exception + { + String result = MAPPER.writeValueAsString(new Bean476Container(1,0,3)); + assertEquals(aposToQuotes("{'a':{'extra':13,'empty':false},'c':{'empty':false,'value':3}}"), + result); + } + + public void testSerializeNaturalViaOverride() throws Exception + { + String result = MAPPER.writeValueAsString(new Bean476Override(123)); + assertEquals(aposToQuotes("{'stuff':{'value':123}}"), + result); + } +} diff --git a/src/test/java/com/fasterxml/jackson/failing/NoTypeInfo1654Test.java b/src/test/java/com/fasterxml/jackson/failing/NoTypeInfo1654Test.java index b39d44ac41..8de6249d4f 100644 --- a/src/test/java/com/fasterxml/jackson/failing/NoTypeInfo1654Test.java +++ b/src/test/java/com/fasterxml/jackson/failing/NoTypeInfo1654Test.java @@ -52,7 +52,7 @@ public Value1654 deserialize(JsonParser p, DeserializationContext ctxt) throws I // [databind#1654] public void testNoTypeElementOverride() throws Exception { - final ObjectMapper mapper = newObjectMapper(); + final ObjectMapper mapper = newJsonMapper(); // First: regular typed case String json = mapper.writeValueAsString(new Value1654TypedContainer( diff --git a/src/test/java/com/fasterxml/jackson/failing/NodeContext2049Test.java b/src/test/java/com/fasterxml/jackson/failing/NodeContext2049Test.java index f60b9f381b..978e1089af 100644 --- a/src/test/java/com/fasterxml/jackson/failing/NodeContext2049Test.java +++ b/src/test/java/com/fasterxml/jackson/failing/NodeContext2049Test.java @@ -45,6 +45,11 @@ public String getValueTypeDesc() { public Object createUsingDefault(DeserializationContext ctxt) throws IOException { return new ArrayList<>(); } + + @Override + public Class getValueClass() { + return List.class; + } } static class ParentSettingDeserializerModifier extends BeanDeserializerModifier { @@ -71,7 +76,7 @@ public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOE if (retValue instanceof HasParent) { HasParent obj = (HasParent) retValue; Parent parent = null; - JsonStreamContext parsingContext = jp.getParsingContext(); + TokenStreamContext parsingContext = jp.getParsingContext(); while (parent == null && parsingContext != null) { Object currentValue = parsingContext.getCurrentValue(); if (currentValue != null && currentValue instanceof Parent) { @@ -93,8 +98,8 @@ protected JsonDeserializer newDelegatingInstance(JsonDeserializer newDeleg } - static class ParentSettingDeserializerContextual extends JsonDeserializer implements ContextualDeserializer { - + static class ParentSettingDeserializerContextual extends JsonDeserializer + { @Override public JsonDeserializer createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException { @@ -122,32 +127,33 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOEx // TODO Auto-generated method stub return null; } + } - } - /* /********************************************************************** /* Test methods /********************************************************************** */ - + private ObjectMapper objectMapper; { - objectMapper = new ObjectMapper(); - objectMapper.registerModule(new com.fasterxml.jackson.databind.Module() { - @Override - public String getModuleName() { - return "parentSetting"; - } - @Override - public Version version() { - return Version.unknownVersion(); - } - @Override - public void setupModule(SetupContext context) { - context.addBeanDeserializerModifier(new ParentSettingDeserializerModifier()); - } - }); + com.fasterxml.jackson.databind.Module module = new com.fasterxml.jackson.databind.Module() { + @Override + public String getModuleName() { + return "parentSetting"; + } + @Override + public Version version() { + return Version.unknownVersion(); + } + @Override + public void setupModule(SetupContext context) { + context.addDeserializerModifier(new ParentSettingDeserializerModifier()); + } + }; + objectMapper = jsonMapperBuilder() + .addModule(module) + .build(); } final static String JSON = "{\n" + diff --git a/src/test/java/com/fasterxml/jackson/failing/NullConversionWithCreatorTest.java b/src/test/java/com/fasterxml/jackson/failing/NullConversionWithCreatorTest.java index 39e4043598..58bb1072bf 100644 --- a/src/test/java/com/fasterxml/jackson/failing/NullConversionWithCreatorTest.java +++ b/src/test/java/com/fasterxml/jackson/failing/NullConversionWithCreatorTest.java @@ -37,7 +37,7 @@ public FailFromNullViaCreator(@JsonSetter(nulls=Nulls.FAIL) /* Test methods /********************************************************** */ - private final ObjectMapper MAPPER = newObjectMapper(); + private final ObjectMapper MAPPER = newJsonMapper(); // [databind#2024] public void testEmptyFromNullViaCreator() throws Exception diff --git a/src/test/java/com/fasterxml/jackson/failing/NumberNodes1770Test.java b/src/test/java/com/fasterxml/jackson/failing/NumberNodes1770Test.java index 93e3602a9f..bec4231a30 100644 --- a/src/test/java/com/fasterxml/jackson/failing/NumberNodes1770Test.java +++ b/src/test/java/com/fasterxml/jackson/failing/NumberNodes1770Test.java @@ -8,7 +8,7 @@ */ public class NumberNodes1770Test extends BaseMapTest { - private final ObjectMapper MAPPER = newObjectMapper(); + private final ObjectMapper MAPPER = newJsonMapper(); // Related to [databind#1770] public void testBigDecimalCoercion() throws Exception diff --git a/src/test/java/com/fasterxml/jackson/failing/ObjectIdWithBuilder1496Test.java b/src/test/java/com/fasterxml/jackson/failing/ObjectIdWithBuilder1496Test.java index 8338ed2f03..fea7b4fdb0 100644 --- a/src/test/java/com/fasterxml/jackson/failing/ObjectIdWithBuilder1496Test.java +++ b/src/test/java/com/fasterxml/jackson/failing/ObjectIdWithBuilder1496Test.java @@ -57,7 +57,7 @@ public POJO readFromCacheOrBuild() { /********************************************************** */ - private final ObjectMapper MAPPER = newObjectMapper(); + private final ObjectMapper MAPPER = newJsonMapper(); public void testBuilderId1496() throws Exception { diff --git a/src/test/java/com/fasterxml/jackson/failing/ObjectIdWithInjectable639Test.java b/src/test/java/com/fasterxml/jackson/failing/ObjectIdWithInjectable639Test.java index 30dfcf3f72..0d6231557a 100644 --- a/src/test/java/com/fasterxml/jackson/failing/ObjectIdWithInjectable639Test.java +++ b/src/test/java/com/fasterxml/jackson/failing/ObjectIdWithInjectable639Test.java @@ -30,9 +30,10 @@ public Child2(@JsonProperty("parent") Parent2 parent) { // for [databind#639] public void testObjectIdWithInjectable() throws Exception { - ObjectMapper mapper = new ObjectMapper() - .setInjectableValues(new InjectableValues.Std(). - addValue("context", "Stuff")); + ObjectMapper mapper = jsonMapperBuilder() + .injectableValues(new InjectableValues.Std(). + addValue("context", "Stuff")) + .build(); Parent2 parent2 = new Parent2("foo"); Child2 child2 = new Child2(parent2); parent2.child = child2; diff --git a/src/test/java/com/fasterxml/jackson/failing/RecursiveIgnoreProperties1755Test.java b/src/test/java/com/fasterxml/jackson/failing/RecursiveIgnoreProperties1755Test.java index 9090429cd5..55b9078e05 100644 --- a/src/test/java/com/fasterxml/jackson/failing/RecursiveIgnoreProperties1755Test.java +++ b/src/test/java/com/fasterxml/jackson/failing/RecursiveIgnoreProperties1755Test.java @@ -31,7 +31,7 @@ static class KeyValue { // for [databind#1755] - private final ObjectMapper MAPPER = newObjectMapper(); + private final ObjectMapper MAPPER = newJsonMapper(); public void testRecursiveIgnore1755() throws Exception { diff --git a/src/test/java/com/fasterxml/jackson/failing/RequireSetterForGetter736Test.java b/src/test/java/com/fasterxml/jackson/failing/RequireSetterForGetter736Test.java index cec06ae190..cf4ff26be2 100644 --- a/src/test/java/com/fasterxml/jackson/failing/RequireSetterForGetter736Test.java +++ b/src/test/java/com/fasterxml/jackson/failing/RequireSetterForGetter736Test.java @@ -29,10 +29,12 @@ public int getReadonly() { // for [databind#736] public void testNeedForSetters() throws Exception { - ObjectMapper mapper = objectMapperBuilder() - .visibility(PropertyAccessor.ALL, Visibility.NONE) - .visibility(PropertyAccessor.GETTER, Visibility.PUBLIC_ONLY) - .visibility(PropertyAccessor.SETTER, Visibility.PUBLIC_ONLY) + ObjectMapper mapper = jsonMapperBuilder() + .changeDefaultVisibility(vc -> vc + .withVisibility(PropertyAccessor.ALL, Visibility.NONE) + .withVisibility(PropertyAccessor.GETTER, Visibility.PUBLIC_ONLY) + .withVisibility(PropertyAccessor.SETTER, Visibility.PUBLIC_ONLY) + ) .enable(MapperFeature.REQUIRE_SETTERS_FOR_GETTERS) .build(); DataB dataB = new DataB(); diff --git a/src/test/java/com/fasterxml/jackson/failing/SkipInjectableIntrospection962Test.java b/src/test/java/com/fasterxml/jackson/failing/SkipInjectableIntrospection962Test.java index 88f283a1f9..c225ad08f5 100644 --- a/src/test/java/com/fasterxml/jackson/failing/SkipInjectableIntrospection962Test.java +++ b/src/test/java/com/fasterxml/jackson/failing/SkipInjectableIntrospection962Test.java @@ -48,8 +48,9 @@ public String getB() { public void testInjected() throws Exception { InjectMe im = new InjectMe(true); - ObjectMapper mapper = new ObjectMapper() - .setInjectableValues(new InjectableValues.Std().addValue(InjectMe.class, im)); + ObjectMapper mapper = jsonMapperBuilder() + .injectableValues(new InjectableValues.Std().addValue(InjectMe.class, im)) + .build(); String test = "{\"b\":\"bbb\"}"; Injectee actual = mapper.readValue(test, Injectee.class); diff --git a/src/test/java/com/fasterxml/jackson/failing/StaticTyping1515Test.java b/src/test/java/com/fasterxml/jackson/failing/StaticTyping1515Test.java index 1eb6e46ef3..7cc8fbb2d3 100644 --- a/src/test/java/com/fasterxml/jackson/failing/StaticTyping1515Test.java +++ b/src/test/java/com/fasterxml/jackson/failing/StaticTyping1515Test.java @@ -58,7 +58,7 @@ static class Issue515Lists { /********************************************************** */ - private final ObjectMapper STAT_MAPPER = objectMapperBuilder() + private final ObjectMapper STAT_MAPPER = jsonMapperBuilder() .enable(MapperFeature.USE_STATIC_TYPING) .build(); diff --git a/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdWithInjectables538.java b/src/test/java/com/fasterxml/jackson/failing/TestObjectIdWithInjectables538.java similarity index 83% rename from src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdWithInjectables538.java rename to src/test/java/com/fasterxml/jackson/failing/TestObjectIdWithInjectables538.java index 9f94257c51..0486fce5f8 100644 --- a/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdWithInjectables538.java +++ b/src/test/java/com/fasterxml/jackson/failing/TestObjectIdWithInjectables538.java @@ -1,4 +1,4 @@ -package com.fasterxml.jackson.databind.objectid; +package com.fasterxml.jackson.failing; import com.fasterxml.jackson.annotation.*; @@ -31,6 +31,9 @@ public B(@JacksonInject("i2") String injected) { private final ObjectMapper MAPPER = new ObjectMapper(); + // 26-Sep-2017, tatu: With Jackson 3.x and inclusion of parameter-names for creators + // we face a new failure since explicit name of injectables is sort of ignored. + // Needs to be fixed as part of rewrite of the whole property introspection. public void testWithInjectables538() throws Exception { A a = new A("a"); diff --git a/src/test/java/com/fasterxml/jackson/failing/TestObjectIdWithInjectables639.java b/src/test/java/com/fasterxml/jackson/failing/TestObjectIdWithInjectables639.java index 3d85f2c32b..27a8e9621a 100644 --- a/src/test/java/com/fasterxml/jackson/failing/TestObjectIdWithInjectables639.java +++ b/src/test/java/com/fasterxml/jackson/failing/TestObjectIdWithInjectables639.java @@ -60,11 +60,12 @@ public Child2(@JacksonInject Context context, public void testObjectIdWithInjectables() throws Exception { - ObjectMapper mapper = new ObjectMapper(); Context context = new Context(); InjectableValues iv = new InjectableValues.Std(). addValue(Context.class, context); - mapper.setInjectableValues(iv); + ObjectMapper mapper = jsonMapperBuilder() + .injectableValues(iv) + .build(); Parent1 parent1 = new Parent1(); Child1 child1 = new Child1(parent1); diff --git a/src/test/java/com/fasterxml/jackson/failing/TestObjectIdWithUnwrapping1298.java b/src/test/java/com/fasterxml/jackson/failing/TestObjectIdWithUnwrapping1298.java index 25261f3515..ec9f31762d 100644 --- a/src/test/java/com/fasterxml/jackson/failing/TestObjectIdWithUnwrapping1298.java +++ b/src/test/java/com/fasterxml/jackson/failing/TestObjectIdWithUnwrapping1298.java @@ -6,9 +6,9 @@ import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.core.StreamWriteFeature; +import com.fasterxml.jackson.core.json.JsonFactory; import com.fasterxml.jackson.databind.BaseMapTest; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.json.JsonMapper; // Test case for https://github.com/FasterXML/jackson-databind/issues/1298 public class TestObjectIdWithUnwrapping1298 extends BaseMapTest @@ -45,10 +45,9 @@ public Child(@JsonProperty("name") String name) { public void testObjectIdWithRepeatedChild() throws Exception { - ObjectMapper mapper = JsonMapper.builder() - // to keep output faithful to original, prevent auto-closing... - .disable(StreamWriteFeature.AUTO_CLOSE_CONTENT) - .build(); + ObjectMapper mapper = new ObjectMapper(JsonFactory.builder() + .disable(StreamWriteFeature.AUTO_CLOSE_CONTENT).build()); + // to keep output faithful to original, prevent auto-closing... // Equivalent to Spring _embedded for Bean w/ List property ListOfParents parents = new ListOfParents(); diff --git a/src/test/java/com/fasterxml/jackson/failing/TestSetterlessProperties501.java b/src/test/java/com/fasterxml/jackson/failing/TestSetterlessProperties501.java index 8363e755d0..2fe1c61c94 100644 --- a/src/test/java/com/fasterxml/jackson/failing/TestSetterlessProperties501.java +++ b/src/test/java/com/fasterxml/jackson/failing/TestSetterlessProperties501.java @@ -46,17 +46,16 @@ public Map getMap() { /********************************************************** */ - // For [Issue#501] + // For [databind#501] public void testSetterlessWithPolymorphic() throws Exception { Issue501Bean input = new Issue501Bean("a", new Poly(13)); - ObjectMapper m = new ObjectMapper(); - assertTrue(m.isEnabled(MapperFeature.USE_GETTERS_AS_SETTERS)); - m.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); + ObjectMapper mapper = jsonMapperBuilder() + .enableDefaultTyping(DefaultTyping.NON_FINAL) + .build(); + String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(input); - String json = m.writerWithDefaultPrettyPrinter().writeValueAsString(input); - - Issue501Bean output = m.readValue(json, Issue501Bean.class); + Issue501Bean output = mapper.readValue(json, Issue501Bean.class); assertNotNull(output); assertEquals(1, output.l.size()); diff --git a/src/test/java/com/fasterxml/jackson/failing/TestUnwrappedWithUnknown650.java b/src/test/java/com/fasterxml/jackson/failing/TestUnwrappedWithUnknown650.java index 6229491bd4..783e3c29a6 100644 --- a/src/test/java/com/fasterxml/jackson/failing/TestUnwrappedWithUnknown650.java +++ b/src/test/java/com/fasterxml/jackson/failing/TestUnwrappedWithUnknown650.java @@ -23,7 +23,7 @@ public void testFailOnUnknownPropertyUnwrapped() throws Exception final String JSON = "{'field': 'value', 'bad':'bad value'}"; try { MAPPER.readValue(aposToQuotes(JSON), A.class); - fail("Exception was not thrown on unkown property"); + fail("Exception was not thrown on unknown property"); } catch (JsonMappingException e) { verifyException(e, "Unrecognized field"); } diff --git a/src/test/java/perf/ManualReadPerfUntyped.java b/src/test/java/perf/ManualReadPerfUntyped.java index 6a49ca3d64..9e367e82fc 100644 --- a/src/test/java/perf/ManualReadPerfUntyped.java +++ b/src/test/java/perf/ManualReadPerfUntyped.java @@ -2,7 +2,7 @@ import java.util.Map; -import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.json.JsonFactory; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.json.JsonMapper; @@ -20,10 +20,11 @@ public static void main(String[] args) throws Exception byte[] data = readAll(args[0]); boolean doIntern = true; + JsonFactory f = JsonFactory.builder() - .configure(JsonFactory.Feature.CANONICALIZE_FIELD_NAMES, doIntern) - .configure(JsonFactory.Feature.INTERN_FIELD_NAMES, doIntern) - .build(); + .configure(JsonFactory.Feature.CANONICALIZE_FIELD_NAMES, doIntern) + .configure(JsonFactory.Feature.INTERN_FIELD_NAMES, doIntern) + .build(); JsonMapper m = new JsonMapper(f); diff --git a/src/test/java/perf/ManualReadPerfUntypedReader.java b/src/test/java/perf/ManualReadPerfUntypedReader.java index 08a03e85a0..186dd48bbd 100644 --- a/src/test/java/perf/ManualReadPerfUntypedReader.java +++ b/src/test/java/perf/ManualReadPerfUntypedReader.java @@ -3,6 +3,7 @@ import java.io.*; import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.json.JsonFactory; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.json.JsonMapper; @@ -22,10 +23,9 @@ public static void main(String[] args) throws Exception boolean doIntern = true; JsonFactory f = JsonFactory.builder() - .configure(JsonFactory.Feature.CANONICALIZE_FIELD_NAMES, doIntern) - .configure(JsonFactory.Feature.INTERN_FIELD_NAMES, doIntern) - .build(); - + .configure(JsonFactory.Feature.CANONICALIZE_FIELD_NAMES, doIntern) + .configure(JsonFactory.Feature.INTERN_FIELD_NAMES, doIntern) + .build(); JsonMapper m = new JsonMapper(f); Object input1 = m.readValue(data, Object.class); JsonNode input2 = m.readTree(data); @@ -52,9 +52,8 @@ protected double testDeser2(int reps, String input, ObjectReader reader) throws protected final double _testRawDeser(int reps, String json, ObjectReader reader) throws IOException { long start = System.nanoTime(); - final JsonFactory f = reader.getFactory(); while (--reps >= 0) { - JsonParser p = f.createParser(json); + JsonParser p = reader.createParser(json); JsonToken t; while ((t = p.nextToken()) != null) { if (t == JsonToken.VALUE_STRING) { @@ -66,7 +65,7 @@ protected final double _testRawDeser(int reps, String json, ObjectReader reader) } p.close(); } - hash = f.hashCode(); + hash = (int) start; return _msecsFromNanos(System.nanoTime() - start); } } diff --git a/src/test/java/perf/ManualReadPerfUntypedStream.java b/src/test/java/perf/ManualReadPerfUntypedStream.java index cab09f3f44..d8456f1814 100644 --- a/src/test/java/perf/ManualReadPerfUntypedStream.java +++ b/src/test/java/perf/ManualReadPerfUntypedStream.java @@ -3,6 +3,7 @@ import java.io.*; import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.json.JsonFactory; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.json.JsonMapper; @@ -20,12 +21,10 @@ public static void main(String[] args) throws Exception byte[] data = readAll(args[0]); boolean doIntern = true; - JsonFactory f = JsonFactory.builder() .configure(JsonFactory.Feature.CANONICALIZE_FIELD_NAMES, doIntern) .configure(JsonFactory.Feature.INTERN_FIELD_NAMES, doIntern) .build(); - JsonMapper m = new JsonMapper(f); Object input1 = m.readValue(data, Object.class); JsonNode input2 = m.readTree(data); @@ -52,9 +51,8 @@ protected double testDeser2(int reps, byte[] input, ObjectReader reader) throws protected final double _testRawDeser(int reps, byte[] json, ObjectReader reader) throws IOException { long start = System.nanoTime(); - final JsonFactory f = reader.getFactory(); while (--reps >= 0) { - JsonParser p = f.createParser(new ByteArrayInputStream(json)); + JsonParser p = reader.createParser(new ByteArrayInputStream(json)); JsonToken t; while ((t = p.nextToken()) != null) { if (t == JsonToken.VALUE_STRING) { @@ -66,7 +64,7 @@ protected final double _testRawDeser(int reps, byte[] json, ObjectReader reader) } p.close(); } - hash = f.hashCode(); + hash = (int) start; return _msecsFromNanos(System.nanoTime() - start); } } diff --git a/src/test/java/perf/ManualReadPerfWithMedia.java b/src/test/java/perf/ManualReadPerfWithMedia.java index 4c9e6b1e1d..4f0f64e791 100644 --- a/src/test/java/perf/ManualReadPerfWithMedia.java +++ b/src/test/java/perf/ManualReadPerfWithMedia.java @@ -1,6 +1,7 @@ package perf; import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.introspect.Annotated; import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector; import com.fasterxml.jackson.databind.json.JsonMapper; @@ -32,9 +33,10 @@ public static void main(String[] args) throws Exception input.addPhoto(new MediaItem.Photo("http://a.com", "title1", 200, 100, MediaItem.Size.LARGE)); input.addPhoto(new MediaItem.Photo("http://b.org", "title2", 640, 480, MediaItem.Size.SMALL)); - JsonMapper m1 = new JsonMapper(); - m1.setAnnotationIntrospector(new NoFormatIntrospector()); - JsonMapper m2 = new JsonMapper(); + ObjectMapper m1 = JsonMapper.builder() + .annotationIntrospector(new NoFormatIntrospector()) + .build(); + ObjectMapper m2 = new JsonMapper(); new ManualReadPerfWithRecord().testFromBytes(m1, "JSON-as-Object", input, MediaItem.class, m2, "JSON-as-Array", input, MediaItem.class); }