Implement #1029 (add support for `@JsonAlias`)
diff --git a/release-notes/VERSION b/release-notes/VERSION
index 15cd103..aba37ee 100644
--- a/release-notes/VERSION
+++ b/release-notes/VERSION
@@ -19,6 +19,7 @@
#865: `JsonFormat.Shape.OBJECT` ignored when class implements `Map.Entry`
#888: Allow specifying custom exclusion comparator via `@JsonInclude`,
using `JsonInclude.Include.CUSTOM`
+#1029: Add a way to define property name aliases
#1035: `@JsonAnySetter` assumes key of `String`, does not consider declared type.
(reported by Michael F)
#1284: Make `StdKeySerializers` use new `JsonGenerator.writeFieldId()` for `int`/`long` keys
diff --git a/src/main/java/com/fasterxml/jackson/databind/BeanProperty.java b/src/main/java/com/fasterxml/jackson/databind/BeanProperty.java
index ac35793..b68bf24 100644
--- a/src/main/java/com/fasterxml/jackson/databind/BeanProperty.java
+++ b/src/main/java/com/fasterxml/jackson/databind/BeanProperty.java
@@ -1,6 +1,8 @@
package com.fasterxml.jackson.databind;
import java.lang.annotation.Annotation;
+import java.util.Collections;
+import java.util.List;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonFormat.Value;
@@ -164,6 +166,16 @@
*/
public JsonInclude.Value findPropertyInclusion(MapperConfig<?> config, Class<?> baseType);
+ /**
+ * Method for accessing set of possible alternate names that are accepted
+ * during deserialization.
+ *
+ * @return List (possibly empty) of alternate names; never null
+ *
+ * @since 2.9
+ */
+ public List<PropertyName> findAliases(MapperConfig<?> config);
+
/*
/**********************************************************
/* Schema/introspection support
@@ -300,6 +312,13 @@
return v0.withOverrides(v);
}
+ @Override
+ public List<PropertyName> findAliases(MapperConfig<?> config) {
+ // 26-Feb-2017, tatu: Do we really need to allow actual definition?
+ // For now, let's not.
+ return Collections.emptyList();
+ }
+
@Override public String getName() { return _name.getSimpleName(); }
@Override public PropertyName getFullName() { return _name; }
@Override public JavaType getType() { return _type; }
@@ -401,6 +420,11 @@
}
@Override
+ public List<PropertyName> findAliases(MapperConfig<?> config) {
+ return Collections.emptyList();
+ }
+
+ @Override
public void depositSchemaProperty(JsonObjectFormatVisitor objectVisitor,
SerializerProvider provider) throws JsonMappingException {
}
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 6adfa31..83dba24 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java
@@ -586,7 +586,8 @@
// And now that we know CreatorProperty instances are also resolved can finally create the creator:
if (creatorProps != null) {
- _propertyBasedCreator = PropertyBasedCreator.construct(ctxt, _valueInstantiator, creatorProps);
+ _propertyBasedCreator = PropertyBasedCreator.construct(ctxt, _valueInstantiator,
+ creatorProps, _beanProperties);
}
if (extTypes != null) {
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 e374fb2..43fdd24 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBuilder.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBuilder.java
@@ -340,7 +340,8 @@
_fixAccess(props);
BeanPropertyMap propertyMap = BeanPropertyMap.construct(props,
- _config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES));
+ _config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES),
+ _collectAliases(props));
propertyMap.assignIndexes();
// view processing must be enabled if:
@@ -416,7 +417,8 @@
Collection<SettableBeanProperty> props = _properties.values();
_fixAccess(props);
BeanPropertyMap propertyMap = BeanPropertyMap.construct(props,
- _config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES));
+ _config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES),
+ _collectAliases(props));
propertyMap.assignIndexes();
boolean anyViews = !_config.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION);
@@ -449,7 +451,7 @@
/**********************************************************
*/
- private void _fixAccess(Collection<SettableBeanProperty> mainProps)
+ protected void _fixAccess(Collection<SettableBeanProperty> mainProps)
{
/* 07-Sep-2016, tatu: Ideally we should be able to avoid forcing
* access to properties that are likely ignored, but due to
@@ -488,4 +490,26 @@
_buildMethod.fixAccess(_config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
}
}
+
+ protected Map<String,List<PropertyName>> _collectAliases(Collection<SettableBeanProperty> props)
+ {
+ Map<String,List<PropertyName>> mapping = null;
+ AnnotationIntrospector intr = _config.getAnnotationIntrospector();
+ if (intr != null) {
+ for (SettableBeanProperty prop : props) {
+ List<PropertyName> aliases = intr.findPropertyAliases(prop.getMember());
+ if ((aliases == null) || aliases.isEmpty()) {
+ continue;
+ }
+ if (mapping == null) {
+ mapping = new HashMap<>();
+ }
+ mapping.put(prop.getName(), aliases);
+ }
+ }
+ if (mapping == null) {
+ return Collections.emptyMap();
+ }
+ return mapping;
+ }
}
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 ea0ef72..9308fb7 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
@@ -10,6 +10,7 @@
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;
@@ -56,22 +57,54 @@
*/
private SettableBeanProperty[] _propsInOrder;
- public BeanPropertyMap(boolean caseInsensitive, Collection<SettableBeanProperty> props)
+ /**
+ * 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
+ */
+ private final Map<String,List<PropertyName>> _aliasDefs;
+
+ /**
+ * Mapping from secondary names (aliases) to primary names.
+ *
+ * @since 2.9
+ */
+ private final Map<String,String> _aliasMapping;
+
+ /**
+ * @since 2.9
+ */
+ public BeanPropertyMap(boolean caseInsensitive, Collection<SettableBeanProperty> props,
+ Map<String,List<PropertyName>> aliasDefs)
{
_caseInsensitive = caseInsensitive;
_propsInOrder = props.toArray(new SettableBeanProperty[props.size()]);
+ _aliasDefs = aliasDefs;
+ _aliasMapping = _buildAliasMapping(aliasDefs);
init(props);
}
+ @Deprecated // since 2.8
+ public BeanPropertyMap(boolean caseInsensitive, Collection<SettableBeanProperty> props)
+ {
+ this(caseInsensitive, props, Collections.<String,List<PropertyName>>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.
+ // 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));
}
@@ -108,7 +141,7 @@
if (prop == null) {
continue;
}
-
+
String key = getPropertyName(prop);
int slot = _hashCode(key);
int ix = (slot<<1);
@@ -129,6 +162,8 @@
//System.err.println(" add '"+key+" at #"+(ix>>1)+"/"+size+" (hashed at "+slot+")");
hashed[ix] = key;
hashed[ix+1] = prop;
+
+ // and aliases
}
/*
for (int i = 0; i < hashed.length; i += 2) {
@@ -138,7 +173,7 @@
_hashArea = hashed;
_spillCount = spillCount;
}
-
+
private final static int findSize(int size)
{
if (size <= 5) {
@@ -158,10 +193,17 @@
/**
* @since 2.6
*/
- public static BeanPropertyMap construct(Collection<SettableBeanProperty> props, boolean caseInsensitive) {
- return new BeanPropertyMap(caseInsensitive, props);
+ public static BeanPropertyMap construct(Collection<SettableBeanProperty> props,
+ boolean caseInsensitive, Map<String,List<PropertyName>> aliasMapping) {
+ return new BeanPropertyMap(caseInsensitive, props, aliasMapping);
}
-
+
+ @Deprecated // since 2.9
+ public static BeanPropertyMap construct(Collection<SettableBeanProperty> props, boolean caseInsensitive) {
+ return construct(props, caseInsensitive,
+ Collections.<String,List<PropertyName>>emptyMap());
+ }
+
/**
* Fluent copy method that creates a new instance that is a copy
* of this instance except for one additional property that is
@@ -259,9 +301,16 @@
newProps.add(_rename(prop, transformer));
}
// should we try to re-index? Ordering probably changed but caller probably doesn't want changes...
- return new BeanPropertyMap(_caseInsensitive, newProps);
+ // 26-Feb-2017, tatu: Probably SHOULD handle renaming wrt Aliases?
+ return new BeanPropertyMap(_caseInsensitive, newProps, _aliasDefs);
}
+ /*
+ /**********************************************************
+ /* Public API, mutators
+ /**********************************************************
+ */
+
/**
* Mutant factory method that will use this instance as the base, and
* construct an instance that is otherwise same except for excluding
@@ -289,9 +338,9 @@
}
}
// should we try to re-index? Apparently no need
- return new BeanPropertyMap(_caseInsensitive, newProps);
+ return new BeanPropertyMap(_caseInsensitive, newProps, _aliasDefs);
}
-
+
/**
* Specialized method that can be used to replace an existing entry
* (note: entry MUST exist; otherwise exception is thrown) with
@@ -310,113 +359,6 @@
_propsInOrder[_findFromOrdered(prop)] = newProp;
}
- private List<SettableBeanProperty> properties() {
- ArrayList<SettableBeanProperty> p = new ArrayList<SettableBeanProperty>(_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;
- }
-
- /**
- * Accessor for traversing over all contained properties.
- */
- @Override
- public Iterator<SettableBeanProperty> iterator() {
- return properties().iterator();
- }
-
- /**
- * 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() {
- 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();
- }
-
- /**
- * @since 2.3
- */
- public SettableBeanProperty find(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];
- if ((prop != null) && (index == prop.getPropertyIndex())) {
- return prop;
- }
- }
- return null;
- }
-
- public SettableBeanProperty find(String key)
- {
- if (key == null) {
- throw new IllegalArgumentException("Can not 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) {
- return null;
- }
- // 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];
- }
- }
- }
- return null;
- }
-
- /*
- /**********************************************************
- /* Public API
- /**********************************************************
- */
-
- public int size() { return _size; }
-
/**
* Specialized method for removing specified existing entry.
* NOTE: entry MUST exist, otherwise an exception is thrown.
@@ -450,6 +392,181 @@
init(props);
}
+ /*
+ /**********************************************************
+ /* Public API, simple accessors
+ /**********************************************************
+ */
+
+ public int size() { return _size; }
+
+ /**
+ * @since 2.9
+ */
+ public boolean isCaseInsensitive() {
+ return _caseInsensitive;
+ }
+
+ /**
+ * @since 2.9
+ */
+ public boolean hasAliases() {
+ return !_aliasDefs.isEmpty();
+ }
+
+ /**
+ * Accessor for traversing over all contained properties.
+ */
+ @Override
+ public Iterator<SettableBeanProperty> iterator() {
+ return _properties().iterator();
+ }
+
+ private List<SettableBeanProperty> _properties() {
+ ArrayList<SettableBeanProperty> p = new ArrayList<SettableBeanProperty>(_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() {
+ 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
+ /**********************************************************
+ */
+
+ /**
+ * @since 2.3
+ */
+ public SettableBeanProperty find(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];
+ if ((prop != null) && (index == prop.getPropertyIndex())) {
+ return prop;
+ }
+ }
+ return null;
+ }
+
+ public SettableBeanProperty find(String key)
+ {
+ if (key == null) {
+ throw new IllegalArgumentException("Can not 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];
+ }
+ }
+ }
+ 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}
@@ -474,6 +591,12 @@
return true;
}
+ /*
+ /**********************************************************
+ /* Std method overrides
+ /**********************************************************
+ */
+
@Override
public String toString()
{
@@ -493,6 +616,11 @@
sb.append(')');
}
sb.append(']');
+ if (!_aliasDefs.isEmpty()) {
+ sb.append("(aliases: ");
+ sb.append(_aliasDefs);
+ sb.append(")");
+ }
return sb.toString();
}
@@ -598,4 +726,27 @@
*/
return key.hashCode() & _hashMask;
}
+
+ // @since 2.9
+ private Map<String,String> _buildAliasMapping(Map<String,List<PropertyName>> defs)
+ {
+ if ((defs == null) || defs.isEmpty()) {
+ return Collections.emptyMap();
+ }
+ Map<String,String> aliases = new HashMap<>();
+ for (Map.Entry<String,List<PropertyName>> 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/PropertyBasedCreator.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyBasedCreator.java
index f0c4093..4e7bc44 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
@@ -1,14 +1,11 @@
package com.fasterxml.jackson.databind.deser.impl;
import java.io.IOException;
-import java.util.Collection;
-import java.util.HashMap;
+import java.util.*;
import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JsonMappingException;
-import com.fasterxml.jackson.databind.MapperFeature;
+import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
import com.fasterxml.jackson.databind.deser.ValueInstantiator;
@@ -53,9 +50,11 @@
/**********************************************************
*/
- protected PropertyBasedCreator(ValueInstantiator valueInstantiator,
+ protected PropertyBasedCreator(DeserializationContext ctxt,
+ ValueInstantiator valueInstantiator,
SettableBeanProperty[] creatorProps,
- boolean caseInsensitive)
+ boolean caseInsensitive,
+ boolean addAliases)
{
_valueInstantiator = valueInstantiator;
if (caseInsensitive) {
@@ -66,6 +65,20 @@
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
+ if (addAliases) {
+ final DeserializationConfig config = ctxt.getConfig();
+ for (SettableBeanProperty prop : creatorProps) {
+ List<PropertyName> aliases = prop.findAliases(config);
+ if (!aliases.isEmpty()) {
+ for (PropertyName pn : aliases) {
+ _propertyLookup.put(pn.getSimpleName(), prop);
+ }
+ }
+ }
+ }
for (int i = 0; i < len; ++i) {
SettableBeanProperty prop = creatorProps[i];
_allProperties[i] = prop;
@@ -74,23 +87,61 @@
}
/**
- * Factory method used for building actual instances: resolves deserializers
- * and checks for "null values".
+ * 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[] srcProps)
+ ValueInstantiator valueInstantiator, SettableBeanProperty[] srcCreatorProps,
+ BeanPropertyMap allProperties)
throws JsonMappingException
{
- final int len = srcProps.length;
+ final int len = srcCreatorProps.length;
SettableBeanProperty[] creatorProps = new SettableBeanProperty[len];
for (int i = 0; i < len; ++i) {
- SettableBeanProperty prop = srcProps[i];
+ SettableBeanProperty prop = srcCreatorProps[i];
if (!prop.hasValueDeserializer()) {
prop = prop.withValueDeserializer(ctxt.findContextualValueDeserializer(prop.getType(), prop));
}
creatorProps[i] = prop;
}
- return new PropertyBasedCreator(valueInstantiator, creatorProps,
+ return new PropertyBasedCreator(ctxt, valueInstantiator, creatorProps,
+ allProperties.isCaseInsensitive(),
+ allProperties.hasAliases());
+ }
+
+ /**
+ * 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,
+ boolean caseInsensitive)
+ throws JsonMappingException
+ {
+ final int len = srcCreatorProps.length;
+ SettableBeanProperty[] creatorProps = new SettableBeanProperty[len];
+ for (int i = 0; i < len; ++i) {
+ SettableBeanProperty prop = srcCreatorProps[i];
+ if (!prop.hasValueDeserializer()) {
+ prop = prop.withValueDeserializer(ctxt.findContextualValueDeserializer(prop.getType(), prop));
+ }
+ creatorProps[i] = prop;
+ }
+ return new PropertyBasedCreator(ctxt, valueInstantiator, creatorProps,
+ 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));
}
@@ -158,7 +209,7 @@
/**
* Simple override of standard {@link java.util.HashMap} to support
- * case-insensitive access to creator properties.
+ * case-insensitive access to creator properties
*
* @since 2.8.5
*/
@@ -168,8 +219,7 @@
@Override
public SettableBeanProperty get(Object key0) {
- String key = (String) key0;
- return super.get(key.toLowerCase());
+ return super.get(((String) key0).toLowerCase());
}
@Override
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 7ec85e2..f1e0fbd 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
@@ -13,6 +13,7 @@
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
import com.fasterxml.jackson.databind.deser.ValueInstantiator;
@@ -119,7 +120,8 @@
value = p.getText();
} else if ((_creatorProps != null) && p.isExpectedStartObjectToken()) {
if (_propCreator == null) {
- _propCreator = PropertyBasedCreator.construct(ctxt, _valueInstantiator, _creatorProps);
+ _propCreator = PropertyBasedCreator.construct(ctxt, _valueInstantiator, _creatorProps,
+ ctxt.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES));
}
p.nextToken();
return deserializeEnumUsingPropertyBased(p, ctxt, _propCreator);
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 4c140a6..d4c6120 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
@@ -232,7 +232,8 @@
}
if (_valueInstantiator.canCreateFromObjectWith()) {
SettableBeanProperty[] creatorProps = _valueInstantiator.getFromObjectArguments(ctxt.getConfig());
- _propertyBasedCreator = PropertyBasedCreator.construct(ctxt, _valueInstantiator, creatorProps);
+ _propertyBasedCreator = PropertyBasedCreator.construct(ctxt, _valueInstantiator, creatorProps,
+ ctxt.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES));
}
_standardStringKey = _isStdKeyDeser(_containerType, _keyDeserializer);
}
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 f2945a7..b4d91e9 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/ConcreteBeanPropertyBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/ConcreteBeanPropertyBase.java
@@ -1,10 +1,14 @@
package com.fasterxml.jackson.databind.introspect;
+import java.util.Collections;
+import java.util.List;
+
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.AnnotationIntrospector;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.PropertyMetadata;
+import com.fasterxml.jackson.databind.PropertyName;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
/**
@@ -32,6 +36,11 @@
*/
protected transient JsonFormat.Value _propertyFormat;
+ /**
+ * @since 2.9
+ */
+ protected transient List<PropertyName> _aliases;
+
protected ConcreteBeanPropertyBase(PropertyMetadata md) {
_metadata = (md == null) ? PropertyMetadata.STD_REQUIRED_OR_OPTIONAL : md;
}
@@ -107,4 +116,21 @@
}
return v0.withOverrides(v);
}
+
+ @Override
+ public List<PropertyName> findAliases(MapperConfig<?> config)
+ {
+ List<PropertyName> aliases = _aliases;
+ if (aliases == null) {
+ AnnotationIntrospector intr = config.getAnnotationIntrospector();
+ if (intr != null) {
+ aliases = intr.findPropertyAliases(getMember());
+ }
+ if (aliases == null) {
+ aliases = Collections.emptyList();
+ }
+ _aliases = aliases;
+ }
+ return aliases;
+ }
}
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 307e15c..f6df4f8 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/PropertyAliasTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/PropertyAliasTest.java
@@ -33,22 +33,27 @@
// [databind#1029]
public void testSimpleAliases() throws Exception
{
- AliasBean bean = MAPPER.readValue(aposToQuotes("{'Name':'Foobar','a':3,'xyz':37}"),
+ AliasBean bean;
+
+ // first, one indicated by field annotation, set via field
+ bean = MAPPER.readValue(aposToQuotes("{'Name':'Foobar','a':3,'xyz':37}"),
AliasBean.class);
assertEquals("Foobar", bean.name);
assertEquals(3, bean._a);
assertEquals(37, bean._xyz);
- bean = MAPPER.readValue(aposToQuotes("{'name':'Foobar','A':3,'xyz':37}"),
- AliasBean.class);
- assertEquals("Foobar", bean.name);
- assertEquals(3, bean._a);
- assertEquals(37, bean._xyz);
-
+ // then method-bound one
bean = MAPPER.readValue(aposToQuotes("{'name':'Foobar','a':3,'Xyz':37}"),
AliasBean.class);
assertEquals("Foobar", bean.name);
assertEquals(3, bean._a);
assertEquals(37, bean._xyz);
+
+ // and finally, constructor-backed one
+ bean = MAPPER.readValue(aposToQuotes("{'name':'Foobar','A':3,'xyz':37}"),
+ AliasBean.class);
+ assertEquals("Foobar", bean.name);
+ assertEquals(3, bean._a);
+ assertEquals(37, bean._xyz);
}
}
diff --git a/src/test/java/com/fasterxml/jackson/databind/misc/BeanPropertyMapTest.java b/src/test/java/com/fasterxml/jackson/databind/misc/BeanPropertyMapTest.java
index eafa286..2240d5a 100644
--- a/src/test/java/com/fasterxml/jackson/databind/misc/BeanPropertyMapTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/misc/BeanPropertyMapTest.java
@@ -31,7 +31,8 @@
PropertyMetadata md = PropertyMetadata.STD_REQUIRED;
props.add(new ObjectIdValueProperty(new MyObjectIdReader("pk"), md));
props.add(new ObjectIdValueProperty(new MyObjectIdReader("firstName"), md));
- BeanPropertyMap propMap = new BeanPropertyMap(false, props);
+ BeanPropertyMap propMap = new BeanPropertyMap(false, props,
+ new HashMap<String,List<PropertyName>>());
propMap = propMap.withProperty(new ObjectIdValueProperty(new MyObjectIdReader("@id"), md));
assertNotNull(propMap);
}