aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBaptiste Pernet <pernetmu@gmail.com>2020-07-23 19:27:54 -0700
committerGitHub <noreply@github.com>2020-07-23 19:27:54 -0700
commitd9c0332e5ad76d7e4f35d9906f0b8e94a5237627 (patch)
tree7079a25712fd088846e3c81b30ea87f21032f93a
parent5758c6bce6773108beeadd8090641f21778ba5e0 (diff)
downloadjackson-databind-d9c0332e5ad76d7e4f35d9906f0b8e94a5237627.tar.gz
FasterXML/jackson-databind#1296 @JsonIncludeProperties (#2771)
Implement #1296 (add and support `@JsonIncludeProperties`)
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java12
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfig.java11
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java8
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java5
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java47
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java65
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBuilder.java29
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java38
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/deser/BuilderBasedDeserializer.java35
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayBuilderDeserializer.java4
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayDeserializer.java4
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanPropertyMap.java18
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/deser/std/MapDeserializer.java76
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair.java10
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java10
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/ser/BasicSerializerFactory.java7
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializer.java9
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java24
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/ser/impl/BeanAsArraySerializer.java11
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/ser/impl/UnwrappingBeanSerializer.java11
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/ser/std/BeanSerializerBase.java41
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/ser/std/MapSerializer.java118
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/util/IgnorePropertiesUtil.java31
-rw-r--r--src/test/java/com/fasterxml/jackson/databind/deser/IncludeWithDeserTest.java202
-rw-r--r--src/test/java/com/fasterxml/jackson/databind/ser/filter/IncludePropsForSerTest.java185
25 files changed, 931 insertions, 80 deletions
diff --git a/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java b/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java
index d8035f031..0cf03fb23 100644
--- a/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java
+++ b/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java
@@ -279,6 +279,18 @@ public abstract class AnnotationIntrospector
public Boolean isIgnorableType(AnnotatedClass ac) { return null; }
/**
+ * Method for finding information about properties to include.
+ *
+ * @param ac Annotated class to introspect
+ *
+ * @since 2.12
+ */
+ public JsonIncludeProperties.Value findPropertyInclusions(Annotated ac)
+ {
+ return JsonIncludeProperties.Value.all();
+ }
+
+ /**
* Method for finding if annotated class has associated filter; and if so,
* to return id that is used to locate filter.
*
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 64afde82e..0b22a22ca 100644
--- a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfig.java
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfig.java
@@ -499,6 +499,17 @@ public abstract class MapperConfig<T extends MapperConfig<T>>
AnnotatedClass actualClass);
/**
+ * Helper method that may be called to see if there are property inclusion
+ * definitions from annotations (via {@link AnnotatedClass}).
+ *
+ * TODO: config override.
+ *
+ * @since 2.12
+ */
+ public abstract JsonIncludeProperties.Value getDefaultPropertyInclusions(Class<?> baseType,
+ AnnotatedClass actualClass);
+
+ /**
* Accessor for object used for determining whether specific property elements
* (method, constructors, fields) can be auto-detected based on
* their visibility (access modifiers). Can be changed to allow
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 2514e8eb7..7a2e7b0d7 100644
--- a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java
@@ -666,6 +666,14 @@ public abstract class MapperConfigBase<CFG extends ConfigFeature,
}
@Override
+ public final JsonIncludeProperties.Value getDefaultPropertyInclusions(Class<?> baseType,
+ AnnotatedClass actualClass)
+ {
+ AnnotationIntrospector intr = getAnnotationIntrospector();
+ return (intr == null) ? null : intr.findPropertyInclusions(actualClass);
+ }
+
+ @Override
public final VisibilityChecker<?> getDefaultVisibilityChecker()
{
VisibilityChecker<?> vchecker = _configOverrides.getDefaultVisibility();
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 7890921dc..bd572a3fd 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java
@@ -10,6 +10,7 @@ import java.util.concurrent.atomic.AtomicReference;
import com.fasterxml.jackson.annotation.JacksonInject;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonIncludeProperties;
import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.annotation.Nulls;
import com.fasterxml.jackson.annotation.JsonCreator.Mode;
@@ -1385,6 +1386,10 @@ nonAnnotatedParamIndex, ctor);
Set<String> ignored = (ignorals == null) ? null
: ignorals.findIgnoredForDeserialization();
md.setIgnorableProperties(ignored);
+ JsonIncludeProperties.Value inclusions = config.getDefaultPropertyInclusions(Map.class,
+ beanDesc.getClassInfo());
+ Set<String> included = inclusions == null ? null : inclusions.getIncluded();
+ md.setIncludableProperties(included);
deser = md;
}
}
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 ced28f0a9..81dbc97d5 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java
@@ -8,6 +8,7 @@ import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.cfg.CoercionAction;
import com.fasterxml.jackson.databind.deser.impl.*;
import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId.Referring;
+import com.fasterxml.jackson.databind.util.IgnorePropertiesUtil;
import com.fasterxml.jackson.databind.util.NameTransformer;
import com.fasterxml.jackson.databind.util.TokenBuffer;
@@ -56,14 +57,31 @@ public class BeanDeserializer
/**
* Constructor used by {@link BeanDeserializerBuilder}.
+ *
+ * @deprecated in 2.12, remove from 3.0
*/
+ @Deprecated
public BeanDeserializer(BeanDeserializerBuilder builder, BeanDescription beanDesc,
BeanPropertyMap properties, Map<String, SettableBeanProperty> backRefs,
HashSet<String> ignorableProps, boolean ignoreAllUnknown,
boolean hasViews)
{
super(builder, beanDesc, properties, backRefs,
- ignorableProps, ignoreAllUnknown, hasViews);
+ ignorableProps, ignoreAllUnknown, null, hasViews);
+ }
+
+ /**
+ * Constructor used by {@link BeanDeserializerBuilder}.
+ *
+ * @since 2.12
+ */
+ public BeanDeserializer(BeanDeserializerBuilder builder, BeanDescription beanDesc,
+ BeanPropertyMap properties, Map<String, SettableBeanProperty> backRefs,
+ HashSet<String> ignorableProps, boolean ignoreAllUnknown, Set<String> includableProps,
+ boolean hasViews)
+ {
+ super(builder, beanDesc, properties, backRefs,
+ ignorableProps, ignoreAllUnknown, includableProps, hasViews);
}
/**
@@ -86,10 +104,21 @@ public class BeanDeserializer
super(src, oir);
}
+ /**
+ * @deprecated in 2.12, remove from 3.0
+ */
+ @Deprecated
public BeanDeserializer(BeanDeserializerBase src, Set<String> ignorableProps) {
super(src, ignorableProps);
}
+ /**
+ * @since 2.12
+ */
+ public BeanDeserializer(BeanDeserializerBase src, Set<String> ignorableProps, Set<String> includableProps) {
+ super(src, ignorableProps, includableProps);
+ }
+
public BeanDeserializer(BeanDeserializerBase src, BeanPropertyMap props) {
super(src, props);
}
@@ -119,8 +148,8 @@ public class BeanDeserializer
}
@Override
- public BeanDeserializer withIgnorableProperties(Set<String> ignorableProps) {
- return new BeanDeserializer(this, ignorableProps);
+ public BeanDeserializer withIgnorableProperties(Set<String> ignorableProps, Set<String> includableProps) {
+ return new BeanDeserializer(this, ignorableProps, includableProps);
}
@Override
@@ -464,7 +493,7 @@ public class BeanDeserializer
continue;
}
// Things marked as ignorable should not be passed to any setter
- if (_ignorableProps != null && _ignorableProps.contains(propName)) {
+ if (IgnorePropertiesUtil.shouldIgnore(propName, _ignorableProps, _includableProps)) {
handleIgnoredProperty(p, ctxt, handledType(), propName);
continue;
}
@@ -694,7 +723,7 @@ public class BeanDeserializer
continue;
}
// Things marked as ignorable should not be passed to any setter
- if (_ignorableProps != null && _ignorableProps.contains(propName)) {
+ if (IgnorePropertiesUtil.shouldIgnore(propName, _ignorableProps, _includableProps)) {
handleIgnoredProperty(p, ctxt, bean, propName);
continue;
}
@@ -751,7 +780,7 @@ public class BeanDeserializer
}
continue;
}
- if (_ignorableProps != null && _ignorableProps.contains(propName)) {
+ if (IgnorePropertiesUtil.shouldIgnore(propName, _ignorableProps, _includableProps)) {
handleIgnoredProperty(p, ctxt, bean, propName);
continue;
}
@@ -850,7 +879,7 @@ public class BeanDeserializer
continue;
}
// Things marked as ignorable should not be passed to any setter
- if (_ignorableProps != null && _ignorableProps.contains(propName)) {
+ if (IgnorePropertiesUtil.shouldIgnore(propName, _ignorableProps, _includableProps)) {
handleIgnoredProperty(p, ctxt, handledType(), propName);
continue;
}
@@ -942,7 +971,7 @@ public class BeanDeserializer
continue;
}
// ignorable things should be ignored
- if (_ignorableProps != null && _ignorableProps.contains(propName)) {
+ if (IgnorePropertiesUtil.shouldIgnore(propName, _ignorableProps, _includableProps)) {
handleIgnoredProperty(p, ctxt, bean, propName);
continue;
}
@@ -1033,7 +1062,7 @@ public class BeanDeserializer
continue;
}
// Things marked as ignorable should not be passed to any setter
- if (_ignorableProps != null && _ignorableProps.contains(propName)) {
+ if (IgnorePropertiesUtil.shouldIgnore(propName, _ignorableProps, _includableProps)) {
handleIgnoredProperty(p, ctxt, handledType(), propName);
continue;
}
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 b059782e9..4a0fb1f14 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java
@@ -136,6 +136,11 @@ public abstract class BeanDeserializerBase
final protected Set<String> _ignorableProps;
/**
+ * Keep track of the the properties that needs to be specifically included.
+ */
+ final protected Set<String> _includableProps;
+
+ /**
* Flag that can be set to ignore and skip unknown properties.
* If set, will not throw an exception for unknown properties.
*/
@@ -201,6 +206,7 @@ public abstract class BeanDeserializerBase
BeanDescription beanDesc,
BeanPropertyMap properties, Map<String, SettableBeanProperty> backRefs,
Set<String> ignorableProps, boolean ignoreAllUnknown,
+ Set<String> includableProps,
boolean hasViews)
{
super(beanDesc.getType());
@@ -211,6 +217,7 @@ public abstract class BeanDeserializerBase
_backRefs = backRefs;
_ignorableProps = ignorableProps;
_ignoreAllUnknown = ignoreAllUnknown;
+ _includableProps = includableProps;
_anySetter = builder.getAnySetter();
List<ValueInjector> injectables = builder.getInjectables();
@@ -263,6 +270,7 @@ public abstract class BeanDeserializerBase
_backRefs = src._backRefs;
_ignorableProps = src._ignorableProps;
_ignoreAllUnknown = ignoreAllUnknown;
+ _includableProps = src._includableProps;
_anySetter = src._anySetter;
_injectables = src._injectables;
_objectIdReader = src._objectIdReader;
@@ -288,6 +296,7 @@ public abstract class BeanDeserializerBase
_backRefs = src._backRefs;
_ignorableProps = src._ignorableProps;
_ignoreAllUnknown = (unwrapper != null) || src._ignoreAllUnknown;
+ _includableProps = src._includableProps;
_anySetter = src._anySetter;
_injectables = src._injectables;
_objectIdReader = src._objectIdReader;
@@ -325,6 +334,7 @@ public abstract class BeanDeserializerBase
_backRefs = src._backRefs;
_ignorableProps = src._ignorableProps;
_ignoreAllUnknown = src._ignoreAllUnknown;
+ _includableProps = src._includableProps;
_anySetter = src._anySetter;
_injectables = src._injectables;
@@ -352,6 +362,14 @@ public abstract class BeanDeserializerBase
public BeanDeserializerBase(BeanDeserializerBase src, Set<String> ignorableProps)
{
+ this(src, ignorableProps, src._includableProps);
+ }
+
+ /**
+ * @since 2.12
+ */
+ public BeanDeserializerBase(BeanDeserializerBase src, Set<String> ignorableProps, Set<String> includableProps)
+ {
super(src._beanType);
_beanType = src._beanType;
@@ -362,6 +380,7 @@ public abstract class BeanDeserializerBase
_backRefs = src._backRefs;
_ignorableProps = ignorableProps;
_ignoreAllUnknown = src._ignoreAllUnknown;
+ _includableProps = includableProps;
_anySetter = src._anySetter;
_injectables = src._injectables;
@@ -375,9 +394,10 @@ public abstract class BeanDeserializerBase
// 01-May-2016, tatu: [databind#1217]: Remove properties from mapping,
// to avoid them being deserialized
- _beanProperties = src._beanProperties.withoutProperties(ignorableProps);
+ _beanProperties = src._beanProperties.withoutProperties(ignorableProps, includableProps);
}
+
/**
* @since 2.8
*/
@@ -394,6 +414,7 @@ public abstract class BeanDeserializerBase
_backRefs = src._backRefs;
_ignorableProps = src._ignorableProps;
_ignoreAllUnknown = src._ignoreAllUnknown;
+ _includableProps = src._includableProps;
_anySetter = src._anySetter;
_injectables = src._injectables;
_objectIdReader = src._objectIdReader;
@@ -411,7 +432,21 @@ public abstract class BeanDeserializerBase
public abstract BeanDeserializerBase withObjectIdReader(ObjectIdReader oir);
- public abstract BeanDeserializerBase withIgnorableProperties(Set<String> ignorableProps);
+ public BeanDeserializerBase withIgnorableProperties(Set<String> ignorableProps) {
+ return withIgnorableProperties(ignorableProps, _includableProps);
+ }
+
+ /**
+ * @since 2.12
+ */
+ public abstract BeanDeserializerBase withIgnorableProperties(Set<String> ignorableProps, Set<String> includableProps);
+
+ /**
+ * @since 2.12
+ */
+ public BeanDeserializerBase withIncludableProperties(Set<String> includableProperties) {
+ return withIgnorableProperties(_ignorableProps, includableProperties);
+ }
// NOTE! To be made `abstract` in 2.12 or later
/**
@@ -422,7 +457,7 @@ public abstract class BeanDeserializerBase
if (ignoreUnknown == _ignoreAllUnknown) {
return this;
}
- return withIgnorableProperties(_ignorableProps);
+ return withIgnorableProperties(_ignorableProps, _includableProps);
}
/**
@@ -469,10 +504,10 @@ public abstract class BeanDeserializerBase
// 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,
// at this point, but is needed for further processing down the line
- if (_ignorableProps != null) {
+ if (_ignorableProps != null || _includableProps != null) {
for (int i = 0, end = creatorProps.length; i < end; ++i) {
SettableBeanProperty prop = creatorProps[i];
- if (_ignorableProps.contains(prop.getName())) {
+ if (IgnorePropertiesUtil.shouldIgnore(prop.getName(), _ignorableProps, _includableProps)) {
creatorProps[i].markAsIgnorable();
}
}
@@ -773,6 +808,21 @@ public abstract class BeanDeserializerBase
contextual = contextual.withIgnoreAllUnknown(true);
}
}
+ JsonIncludeProperties.Value inclusions = intr.findPropertyInclusions(accessor);
+ if (inclusions != null) {
+ Set<String> included = inclusions.getIncluded();
+ Set<String> prev = contextual._includableProps;
+ if (prev != null && included != null) {
+ Set<String> newIncluded = new HashSet<>();
+ // Make the intersection with the previously included properties.
+ for(String prop : prev) {
+ if (included.contains(prop)) {
+ newIncluded.add(prop);
+ }
+ }
+ contextual = contextual.withIncludableProperties(newIncluded);
+ }
+ }
}
// One more thing: are we asked to serialize POJO as array?
@@ -1601,7 +1651,8 @@ public abstract class BeanDeserializerBase
Object beanOrBuilder, String propName)
throws IOException
{
- if (_ignorableProps != null && _ignorableProps.contains(propName)) {
+
+ if (IgnorePropertiesUtil.shouldIgnore(propName, _ignorableProps, _includableProps)) {
handleIgnoredProperty(p, ctxt, beanOrBuilder, propName);
} else if (_anySetter != null) {
try {
@@ -1629,7 +1680,7 @@ public abstract class BeanDeserializerBase
p.skipChildren();
return;
}
- if (_ignorableProps != null && _ignorableProps.contains(propName)) {
+ if (IgnorePropertiesUtil.shouldIgnore(propName, _ignorableProps, _includableProps)) {
handleIgnoredProperty(p, ctxt, beanOrClass, propName);
}
// Otherwise use default handling (call handler(s); if not
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 702edd847..5f5f493c4 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBuilder.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBuilder.java
@@ -12,6 +12,7 @@ import com.fasterxml.jackson.databind.deser.impl.ValueInjector;
import com.fasterxml.jackson.databind.introspect.*;
import com.fasterxml.jackson.databind.util.Annotations;
import com.fasterxml.jackson.databind.util.ClassUtil;
+import com.fasterxml.jackson.databind.util.IgnorePropertiesUtil;
/**
* Builder class used for aggregating deserialization information about
@@ -73,6 +74,12 @@ public class BeanDeserializerBuilder
protected HashSet<String> _ignorableProps;
/**
+ * Set of names of properties that are recognized and are set to be included for deserialization
+ * purposes (null deactivate this, empty includes nothing).
+ */
+ protected HashSet<String> _includableProps;
+
+ /**
* Object that will handle value instantiation for the bean type.
*/
protected ValueInstantiator _valueInstantiator;
@@ -136,7 +143,8 @@ public class BeanDeserializerBuilder
_injectables = _copy(src._injectables);
_backRefProperties = _copy(src._backRefProperties);
// Hmmh. Should we create defensive copies here? For now, not yet
- _ignorableProps = src._ignorableProps;
+ _ignorableProps = src._ignorableProps;
+ _includableProps = src._includableProps;
_valueInstantiator = src._valueInstantiator;
_objectIdReader = src._objectIdReader;
@@ -236,6 +244,19 @@ public class BeanDeserializerBuilder
}
/**
+ * Method that will add property name as one of the properties that will be included.
+ *
+ * @since 2.12
+ */
+ public void addIncludable(String propName)
+ {
+ if (_includableProps == null) {
+ _includableProps = new HashSet<>();
+ }
+ _includableProps.add(propName);
+ }
+
+ /**
* Method called by deserializer factory, when a "creator property"
* (something that is passed via constructor- or factory method argument;
* instead of setter or field).
@@ -333,7 +354,7 @@ public class BeanDeserializerBuilder
* @since 2.9.4
*/
public boolean hasIgnorable(String name) {
- return (_ignorableProps != null) && _ignorableProps.contains(name);
+ return IgnorePropertiesUtil.shouldIgnore(name, _ignorableProps, _includableProps);
}
/*
@@ -379,7 +400,7 @@ public class BeanDeserializerBuilder
}
return new BeanDeserializer(this,
- _beanDesc, propertyMap, _backRefProperties, _ignorableProps, _ignoreAllUnknown,
+ _beanDesc, propertyMap, _backRefProperties, _ignorableProps, _ignoreAllUnknown, _includableProps,
anyViews);
}
@@ -463,7 +484,7 @@ public class BeanDeserializerBuilder
BeanPropertyMap propertyMap, boolean anyViews) {
return new BuilderBasedDeserializer(this,
_beanDesc, valueType, propertyMap, _backRefProperties, _ignorableProps, _ignoreAllUnknown,
- anyViews);
+ _includableProps, anyViews);
}
/*
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 fcf93b1fa..c9f6e597e 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java
@@ -14,6 +14,7 @@ import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.jsontype.impl.SubTypeValidator;
import com.fasterxml.jackson.databind.util.BeanUtil;
import com.fasterxml.jackson.databind.util.ClassUtil;
+import com.fasterxml.jackson.databind.util.IgnorePropertiesUtil;
import com.fasterxml.jackson.databind.util.SimpleBeanPropertyDefinition;
/**
@@ -510,6 +511,18 @@ public class BeanDeserializerFactory
} else {
ignored = Collections.emptySet();
}
+ JsonIncludeProperties.Value inclusions = ctxt.getConfig()
+ .getDefaultPropertyInclusions(beanDesc.getBeanClass(),
+ beanDesc.getClassInfo());
+ Set<String> included = null;
+ if (inclusions != null) {
+ included = inclusions.getIncluded();
+ if (included != null) {
+ for(String propName : included) {
+ builder.addIncludable(propName);
+ }
+ }
+ }
// Also, do we have a fallback "any" setter?
AnnotatedMember anySetter = beanDesc.findAnySetterAccessor();
@@ -532,7 +545,7 @@ public class BeanDeserializerFactory
// Ok: let's then filter out property definitions
List<BeanPropertyDefinition> propDefs = filterBeanProps(ctxt,
- beanDesc, builder, beanDesc.findProperties(), ignored);
+ beanDesc, builder, beanDesc.findProperties(), ignored, included);
// After which we can let custom code change the set
if (_factoryConfig.hasDeserializerModifiers()) {
for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) {
@@ -644,20 +657,41 @@ public class BeanDeserializerFactory
* as well as properties that have "ignorable types".
* Note that this will not remove properties that have no
* setters.
+ *
+ * @deprecated in 2.12, remove from 3.0
*/
+ @Deprecated
protected List<BeanPropertyDefinition> filterBeanProps(DeserializationContext ctxt,
BeanDescription beanDesc, BeanDeserializerBuilder builder,
List<BeanPropertyDefinition> propDefsIn,
Set<String> ignored)
throws JsonMappingException
{
+ return filterBeanProps(ctxt, beanDesc, builder, propDefsIn, ignored, null);
+ }
+
+ /**
+ * Helper method called to filter out explicit ignored properties,
+ * as well as properties that have "ignorable types".
+ * Note that this will not remove properties that have no
+ * setters.
+ *
+ * @since 2.12
+ */
+ protected List<BeanPropertyDefinition> filterBeanProps(DeserializationContext ctxt,
+ BeanDescription beanDesc, BeanDeserializerBuilder builder,
+ List<BeanPropertyDefinition> propDefsIn,
+ Set<String> ignored,
+ Set<String> included)
+ {
ArrayList<BeanPropertyDefinition> result = new ArrayList<BeanPropertyDefinition>(
Math.max(4, propDefsIn.size()));
HashMap<Class<?>,Boolean> ignoredTypes = new HashMap<Class<?>,Boolean>();
// These are all valid setters, but we do need to introspect bit more
for (BeanPropertyDefinition property : propDefsIn) {
String name = property.getName();
- if (ignored.contains(name)) { // explicit ignoral using @JsonIgnoreProperties needs to block entries
+ // explicit ignoral using @JsonIgnoreProperties of @JsonIncludeProperties needs to block entries
+ if (IgnorePropertiesUtil.shouldIgnore(name, ignored, included)) {
continue;
}
if (!property.hasConstructorParameter()) { // never skip constructor params
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 fa15ed319..44bff9d3c 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/BuilderBasedDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BuilderBasedDeserializer.java
@@ -8,6 +8,7 @@ import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.cfg.CoercionAction;
import com.fasterxml.jackson.databind.deser.impl.*;
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
+import com.fasterxml.jackson.databind.util.IgnorePropertiesUtil;
import com.fasterxml.jackson.databind.util.NameTransformer;
import com.fasterxml.jackson.databind.util.TokenBuffer;
@@ -51,8 +52,20 @@ public class BuilderBasedDeserializer
Set<String> ignorableProps, boolean ignoreAllUnknown,
boolean hasViews)
{
+ this(builder, beanDesc, targetType, properties, backRefs, ignorableProps, ignoreAllUnknown, null, hasViews);
+ }
+
+ /**
+ * @since 2.12
+ */
+ public BuilderBasedDeserializer(BeanDeserializerBuilder builder,
+ BeanDescription beanDesc, JavaType targetType,
+ BeanPropertyMap properties, Map<String, SettableBeanProperty> backRefs,
+ Set<String> ignorableProps, boolean ignoreAllUnknown, Set<String> includableProps,
+ boolean hasViews)
+ {
super(builder, beanDesc, properties, backRefs,
- ignorableProps, ignoreAllUnknown, hasViews);
+ ignorableProps, ignoreAllUnknown, includableProps, hasViews);
_targetType = targetType;
_buildMethod = builder.getBuildMethod();
// 05-Mar-2012, tatu: Cannot really make Object Ids work with builders, not yet anyway
@@ -106,7 +119,11 @@ public class BuilderBasedDeserializer
}
public BuilderBasedDeserializer(BuilderBasedDeserializer src, Set<String> ignorableProps) {
- super(src, ignorableProps);
+ this(src, ignorableProps, src._includableProps);
+ }
+
+ public BuilderBasedDeserializer(BuilderBasedDeserializer src, Set<String> ignorableProps, Set<String> includableProps) {
+ super(src, ignorableProps, includableProps);
_buildMethod = src._buildMethod;
_targetType = src._targetType;
}
@@ -133,8 +150,8 @@ public class BuilderBasedDeserializer
}
@Override
- public BeanDeserializerBase withIgnorableProperties(Set<String> ignorableProps) {
- return new BuilderBasedDeserializer(this, ignorableProps);
+ public BeanDeserializerBase withIgnorableProperties(Set<String> ignorableProps, Set<String> includableProps) {
+ return new BuilderBasedDeserializer(this, ignorableProps, includableProps);
}
@Override
@@ -396,7 +413,7 @@ public class BuilderBasedDeserializer
}
// As per [JACKSON-313], things marked as ignorable should not be
// passed to any setter
- if (_ignorableProps != null && _ignorableProps.contains(propName)) {
+ if (IgnorePropertiesUtil.shouldIgnore(propName, _ignorableProps, _includableProps)) {
handleIgnoredProperty(p, ctxt, handledType(), propName);
continue;
}
@@ -599,7 +616,7 @@ public class BuilderBasedDeserializer
continue;
}
// ignorable things should be ignored
- if (_ignorableProps != null && _ignorableProps.contains(propName)) {
+ if (IgnorePropertiesUtil.shouldIgnore(propName, _ignorableProps, _includableProps)) {
handleIgnoredProperty(p, ctxt, bean, propName);
continue;
}
@@ -665,7 +682,7 @@ public class BuilderBasedDeserializer
buffer.bufferProperty(prop, prop.deserialize(p, ctxt));
continue;
}
- if (_ignorableProps != null && _ignorableProps.contains(propName)) {
+ if (IgnorePropertiesUtil.shouldIgnore(propName, _ignorableProps, _includableProps)) {
handleIgnoredProperty(p, ctxt, handledType(), propName);
continue;
}
@@ -710,7 +727,7 @@ public class BuilderBasedDeserializer
}
continue;
}
- if (_ignorableProps != null && _ignorableProps.contains(propName)) {
+ if (IgnorePropertiesUtil.shouldIgnore(propName, _ignorableProps, _includableProps)) {
handleIgnoredProperty(p, ctxt, builder, propName);
continue;
}
@@ -770,7 +787,7 @@ public class BuilderBasedDeserializer
continue;
}
// ignorable things should be ignored
- if (_ignorableProps != null && _ignorableProps.contains(propName)) {
+ if (IgnorePropertiesUtil.shouldIgnore(propName, _ignorableProps, _includableProps)) {
handleIgnoredProperty(p, ctxt, bean, propName);
continue;
}
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 3c9cf8b07..73ab84871 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
@@ -77,8 +77,8 @@ public class BeanAsArrayBuilderDeserializer
}
@Override
- public BeanDeserializerBase withIgnorableProperties(Set<String> ignorableProps) {
- return new BeanAsArrayBuilderDeserializer(_delegate.withIgnorableProperties(ignorableProps),
+ public BeanDeserializerBase withIgnorableProperties(Set<String> ignorableProps, Set<String> includableProps) {
+ return new BeanAsArrayBuilderDeserializer(_delegate.withIgnorableProperties(ignorableProps, includableProps),
_targetType, _orderedProperties, _buildMethod);
}
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 f0116727b..aad5b1f52 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
@@ -67,8 +67,8 @@ public class BeanAsArrayDeserializer
}
@Override
- public BeanDeserializerBase withIgnorableProperties(Set<String> ignorableProps) {
- return new BeanAsArrayDeserializer(_delegate.withIgnorableProperties(ignorableProps),
+ public BeanDeserializerBase withIgnorableProperties(Set<String> ignorableProps, Set<String> includableProps) {
+ return new BeanAsArrayDeserializer(_delegate.withIgnorableProperties(ignorableProps, includableProps),
_orderedProperties);
}
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 68e17ad0b..50248c600 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
@@ -15,6 +15,7 @@ import com.fasterxml.jackson.databind.PropertyName;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
import com.fasterxml.jackson.databind.util.ClassUtil;
+import com.fasterxml.jackson.databind.util.IgnorePropertiesUtil;
import com.fasterxml.jackson.databind.util.NameTransformer;
/**
@@ -382,7 +383,20 @@ public class BeanPropertyMap
*/
public BeanPropertyMap withoutProperties(Collection<String> toExclude)
{
- if (toExclude.isEmpty()) {
+ return withoutProperties(toExclude, null);
+ }
+
+ /**
+ * 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, or including only the one marked
+ * as included
+ *
+ * @since 2.12
+ */
+ public BeanPropertyMap withoutProperties(Collection<String> toExclude, Collection<String> toInclude)
+ {
+ if ((toExclude == null || toExclude.isEmpty()) && toInclude == null) {
return this;
}
final int len = _propsInOrder.length;
@@ -394,7 +408,7 @@ public class BeanPropertyMap
// 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())) {
+ if (!IgnorePropertiesUtil.shouldIgnore(prop.getName(), toExclude, toInclude)) {
newProps.add(prop);
}
}
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 cba73fb4a..8216cd1cc 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
@@ -5,6 +5,7 @@ import java.util.*;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonIncludeProperties;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
@@ -17,6 +18,7 @@ import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.type.LogicalType;
import com.fasterxml.jackson.databind.util.ArrayBuilders;
+import com.fasterxml.jackson.databind.util.IgnorePropertiesUtil;
/**
* Basic deserializer that can take JSON "Object" structure and
@@ -86,6 +88,7 @@ public class MapDeserializer
// // Any properties to ignore if seen?
protected Set<String> _ignorableProperties;
+ protected Set<String> _includableProperties;
/*
/**********************************************************
@@ -124,6 +127,7 @@ public class MapDeserializer
_hasDefaultCreator = src._hasDefaultCreator;
// should we make a copy here?
_ignorableProperties = src._ignorableProperties;
+ _includableProperties = src._includableProperties;
_standardStringKey = src._standardStringKey;
}
@@ -134,6 +138,19 @@ public class MapDeserializer
NullValueProvider nuller,
Set<String> ignorable)
{
+ this(src, keyDeser,valueDeser, valueTypeDeser, nuller, ignorable, null);
+ }
+
+ /**
+ * @since 2.12
+ */
+ protected MapDeserializer(MapDeserializer src,
+ KeyDeserializer keyDeser, JsonDeserializer<Object> valueDeser,
+ TypeDeserializer valueTypeDeser,
+ NullValueProvider nuller,
+ Set<String> ignorable,
+ Set<String> includable)
+ {
super(src, nuller, src._unwrapSingle);
_keyDeserializer = keyDeser;
_valueDeserializer = valueDeser;
@@ -143,6 +160,7 @@ public class MapDeserializer
_delegateDeserializer = src._delegateDeserializer;
_hasDefaultCreator = src._hasDefaultCreator;
_ignorableProperties = ignorable;
+ _includableProperties = includable;
_standardStringKey = _isStdKeyDeser(_containerType, keyDeser);
}
@@ -157,15 +175,26 @@ public class MapDeserializer
NullValueProvider nuller,
Set<String> ignorable)
{
-
+ return withResolved(keyDeser, valueTypeDeser, valueDeser, nuller, ignorable, _includableProperties);
+ }
+
+ /**
+ * @since 2.12
+ */
+ protected MapDeserializer withResolved(KeyDeserializer keyDeser,
+ TypeDeserializer valueTypeDeser, JsonDeserializer<?> valueDeser,
+ NullValueProvider nuller,
+ Set<String> ignorable, Set<String> includable)
+ {
+
if ((_keyDeserializer == keyDeser) && (_valueDeserializer == valueDeser)
&& (_valueTypeDeserializer == valueTypeDeser) && (_nullProvider == nuller)
- && (_ignorableProperties == ignorable)) {
+ && (_ignorableProperties == ignorable) && (_includableProperties == includable)) {
return this;
}
return new MapDeserializer(this,
keyDeser, (JsonDeserializer<Object>) valueDeser, valueTypeDeser,
- nuller, ignorable);
+ nuller, ignorable, includable);
}
/**
@@ -186,6 +215,10 @@ public class MapDeserializer
&& isDefaultKeyDeserializer(keyDeser));
}
+ /**
+ * @deprecated in 2.12, remove from 3.0
+ */
+ @Deprecated
public void setIgnorableProperties(String[] ignorable) {
_ignorableProperties = (ignorable == null || ignorable.length == 0) ?
null : ArrayBuilders.arrayToSet(ignorable);
@@ -196,6 +229,10 @@ public class MapDeserializer
null : ignorable;
}
+ public void setIncludableProperties(Set<String> includable) {
+ _includableProperties = includable;
+ }
+
/*
/**********************************************************
/* Validation, post-processing (ResolvableDeserializer)
@@ -269,6 +306,7 @@ public class MapDeserializer
vtd = vtd.forProperty(property);
}
Set<String> ignored = _ignorableProperties;
+ Set<String> included = _includableProperties;
AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
if (_neitherNull(intr, property)) {
AnnotatedMember member = property.getMember();
@@ -283,10 +321,27 @@ public class MapDeserializer
}
}
}
+ JsonIncludeProperties.Value inclusions = intr.findPropertyInclusions(member);
+ if (inclusions != null) {
+ Set<String> includedToAdd = inclusions.getIncluded();
+ if (includedToAdd != null) {
+ Set<String> newIncluded = new HashSet<>();
+ if (included == null) {
+ newIncluded = new HashSet<>(includedToAdd);
+ } else {
+ for (String str : includedToAdd) {
+ if (included.contains(str)) {
+ newIncluded.add(str);
+ }
+ }
+ }
+ included = newIncluded;
+ }
+ }
}
}
return withResolved(keyDeser, vtd, valueDeser,
- findContentNullProvider(ctxt, property, valueDeser), ignored);
+ findContentNullProvider(ctxt, property, valueDeser), ignored, included);
}
/*
@@ -331,7 +386,8 @@ public class MapDeserializer
return (_valueDeserializer == null)
&& (_keyDeserializer == null)
&& (_valueTypeDeserializer == null)
- && (_ignorableProperties == null);
+ && (_ignorableProperties == null)
+ && (_includableProperties == null);
}
@Override // since 2.12
@@ -458,7 +514,7 @@ public class MapDeserializer
Object key = keyDes.deserializeKey(keyStr, ctxt);
// And then the value...
JsonToken t = p.nextToken();
- if (_ignorableProperties != null && _ignorableProperties.contains(keyStr)) {
+ if (IgnorePropertiesUtil.shouldIgnore(keyStr, _ignorableProperties, _includableProperties)) {
p.skipChildren();
continue;
}
@@ -520,7 +576,7 @@ public class MapDeserializer
for (; key != null; key = p.nextFieldName()) {
JsonToken t = p.nextToken();
- if (_ignorableProperties != null && _ignorableProperties.contains(key)) {
+ if (IgnorePropertiesUtil.shouldIgnore(key, _ignorableProperties, _includableProperties)) {
p.skipChildren();
continue;
}
@@ -572,7 +628,7 @@ public class MapDeserializer
for (; key != null; key = p.nextFieldName()) {
JsonToken t = p.nextToken(); // to get to value
- if (_ignorableProperties != null && _ignorableProperties.contains(key)) {
+ if (IgnorePropertiesUtil.shouldIgnore(key, _ignorableProperties, _includableProperties)) {
p.skipChildren(); // and skip it (in case of array/object)
continue;
}
@@ -661,7 +717,7 @@ public class MapDeserializer
Object key = keyDes.deserializeKey(keyStr, ctxt);
// And then the value...
JsonToken t = p.nextToken();
- if (_ignorableProperties != null && _ignorableProperties.contains(keyStr)) {
+ if (IgnorePropertiesUtil.shouldIgnore(keyStr, _ignorableProperties, _includableProperties)) {
p.skipChildren();
continue;
}
@@ -728,7 +784,7 @@ public class MapDeserializer
for (; key != null; key = p.nextFieldName()) {
JsonToken t = p.nextToken();
- if (_ignorableProperties != null && _ignorableProperties.contains(key)) {
+ if (IgnorePropertiesUtil.shouldIgnore(key, _ignorableProperties, _includableProperties)) {
p.skipChildren();
continue;
}
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 d69bbd3cd..3ba0ab9a5 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair.java
@@ -10,6 +10,7 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonIncludeProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.core.Version;
@@ -126,6 +127,15 @@ public class AnnotationIntrospectorPair
}
@Override
+ public JsonIncludeProperties.Value findPropertyInclusions(Annotated a)
+ {
+ JsonIncludeProperties.Value v2 = _secondary.findPropertyInclusions(a);
+ JsonIncludeProperties.Value v1 = _primary.findPropertyInclusions(a);
+ return (v2 == null) // shouldn't occur but
+ ? v1 : v2.withOverrides(v1);
+ }
+
+ @Override
public Boolean isIgnorableType(AnnotatedClass ac)
{
Boolean result = _primary.isIgnorableType(ac);
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 731102df8..268ee7f49 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java
@@ -307,6 +307,16 @@ public class JacksonAnnotationIntrospector
JsonIgnoreType ignore = _findAnnotation(ac, JsonIgnoreType.class);
return (ignore == null) ? null : ignore.value();
}
+
+ @Override
+ public JsonIncludeProperties.Value findPropertyInclusions(Annotated a)
+ {
+ JsonIncludeProperties v = _findAnnotation(a, JsonIncludeProperties.class);
+ if (v == null) {
+ return JsonIncludeProperties.Value.all();
+ }
+ return JsonIncludeProperties.Value.from(v);
+ }
@Override
public Object findFilterId(Annotated a) {
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 889a72470..d37ef546c 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/BasicSerializerFactory.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/BasicSerializerFactory.java
@@ -11,6 +11,7 @@ import java.util.concurrent.atomic.AtomicReference;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonIncludeProperties;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.cfg.SerializerFactoryConfig;
@@ -823,7 +824,11 @@ public abstract class BasicSerializerFactory
beanDesc.getClassInfo());
Set<String> ignored = (ignorals == null) ? null
: ignorals.findIgnoredForSerialization();
- MapSerializer mapSer = MapSerializer.construct(ignored,
+ JsonIncludeProperties.Value inclusions = config.getDefaultPropertyInclusions(Map.class,
+ beanDesc.getClassInfo());
+ Set<String> included = (inclusions == null) ? null
+ : inclusions.getIncluded();
+ MapSerializer mapSer = MapSerializer.construct(ignored, included,
type, staticTyping, elementTypeSerializer,
keySerializer, elementValueSerializer, filterId);
ser = _checkMapContentInclusion(prov, beanDesc, mapSer);
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 501f16d7f..b202b47f6 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializer.java
@@ -68,6 +68,10 @@ public class BeanSerializer
super(src, toIgnore);
}
+ protected BeanSerializer(BeanSerializerBase src, Set<String> toIgnore, Set<String> toInclude) {
+ super(src, toIgnore, toInclude);
+ }
+
// @since 2.11.1
protected BeanSerializer(BeanSerializerBase src,
BeanPropertyWriter[] properties, BeanPropertyWriter[] filteredProperties) {
@@ -120,6 +124,11 @@ public class BeanSerializer
return new BeanSerializer(this, toIgnore);
}
+ @Override
+ protected BeanSerializerBase withIgnorals(Set<String> toIgnore, Set<String> toInclude) {
+ return new BeanSerializer(this, toIgnore, toInclude);
+ }
+
@Override // @since 2.11.1
protected BeanSerializerBase withProperties(BeanPropertyWriter[] properties,
BeanPropertyWriter[] filteredProperties) {
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 07a969208..81c494dee 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java
@@ -3,6 +3,7 @@ package com.fasterxml.jackson.databind.ser;
import java.util.*;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonIncludeProperties;
import com.fasterxml.jackson.annotation.ObjectIdGenerator;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
@@ -23,6 +24,7 @@ import com.fasterxml.jackson.databind.type.ReferenceType;
import com.fasterxml.jackson.databind.util.BeanUtil;
import com.fasterxml.jackson.databind.util.ClassUtil;
import com.fasterxml.jackson.databind.util.Converter;
+import com.fasterxml.jackson.databind.util.IgnorePropertiesUtil;
/**
* Factory class that can provide serializers for any regular Java beans
@@ -635,17 +637,25 @@ public class BeanSerializerFactory
// just use it as is.
JsonIgnoreProperties.Value ignorals = config.getDefaultPropertyIgnorals(beanDesc.getBeanClass(),
beanDesc.getClassInfo());
+ Set<String> ignored = null;
if (ignorals != null) {
- Set<String> ignored = ignorals.findIgnoredForSerialization();
- if (!ignored.isEmpty()) {
- Iterator<BeanPropertyWriter> it = props.iterator();
- while (it.hasNext()) {
- if (ignored.contains(it.next().getName())) {
- it.remove();
- }
+ ignored = ignorals.findIgnoredForSerialization();
+ }
+ JsonIncludeProperties.Value inclusions = config.getDefaultPropertyInclusions(beanDesc.getBeanClass(),
+ beanDesc.getClassInfo());
+ Set<String> included = null;
+ if (inclusions != null) {
+ included = inclusions.getIncluded();
+ }
+ if (included != null || (ignored != null && !ignored.isEmpty())) {
+ Iterator<BeanPropertyWriter> it = props.iterator();
+ while (it.hasNext()) {
+ if (IgnorePropertiesUtil.shouldIgnore(it.next().getName(), ignored, included)) {
+ it.remove();
}
}
}
+
return props;
}
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 2abf7649d..86b16366d 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
@@ -67,7 +67,11 @@ public class BeanAsArraySerializer
}
protected BeanAsArraySerializer(BeanSerializerBase src, Set<String> toIgnore) {
- super(src, toIgnore);
+ this(src, toIgnore, null);
+ }
+
+ protected BeanAsArraySerializer(BeanSerializerBase src, Set<String> toIgnore, Set<String> toInclude) {
+ super(src, toIgnore, toInclude);
_defaultSerializer = src;
}
@@ -112,6 +116,11 @@ public class BeanAsArraySerializer
return new BeanAsArraySerializer(this, toIgnore);
}
+ @Override
+ protected BeanAsArraySerializer withIgnorals(Set<String> toIgnore, Set<String> toInclude) {
+ return new BeanAsArraySerializer(this, toIgnore, toInclude);
+ }
+
@Override // @since 2.11.1
protected BeanSerializerBase withProperties(BeanPropertyWriter[] properties,
BeanPropertyWriter[] filteredProperties) {
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 32418233f..dd50a6b19 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
@@ -51,7 +51,11 @@ public class UnwrappingBeanSerializer
}
protected UnwrappingBeanSerializer(UnwrappingBeanSerializer src, Set<String> toIgnore) {
- super(src, toIgnore);
+ this(src, toIgnore, null);
+ }
+
+ protected UnwrappingBeanSerializer(UnwrappingBeanSerializer src, Set<String> toIgnore, Set<String> toInclude) {
+ super(src, toIgnore, toInclude);
_nameTransformer = src._nameTransformer;
}
@@ -94,6 +98,11 @@ public class UnwrappingBeanSerializer
return new UnwrappingBeanSerializer(this, toIgnore);
}
+ @Override
+ protected BeanSerializerBase withIgnorals(Set<String> toIgnore, Set<String> toInclude) {
+ return new UnwrappingBeanSerializer(this, toIgnore, toInclude);
+ }
+
@Override // @since 2.11.1
protected BeanSerializerBase withProperties(BeanPropertyWriter[] properties,
BeanPropertyWriter[] filteredProperties) {
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 95621c9a5..afadc8a22 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
@@ -26,6 +26,7 @@ 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.IgnorePropertiesUtil;
import com.fasterxml.jackson.databind.util.NameTransformer;
/**
@@ -175,10 +176,14 @@ public abstract class BeanSerializerBase
@Deprecated // since 2.8, remove soon
protected BeanSerializerBase(BeanSerializerBase src, String[] toIgnore)
{
- this(src, ArrayBuilders.arrayToSet(toIgnore));
+ this(src, ArrayBuilders.arrayToSet(toIgnore), null);
}
-
- protected BeanSerializerBase(BeanSerializerBase src, Set<String> toIgnore)
+
+ protected BeanSerializerBase(BeanSerializerBase src, Set<String> toIgnore) {
+ this(src, toIgnore, null);
+ }
+
+ protected BeanSerializerBase(BeanSerializerBase src, Set<String> toIgnore, Set<String> toInclude)
{
super(src._handledType);
@@ -193,7 +198,7 @@ public abstract class BeanSerializerBase
for (int i = 0; i < len; ++i) {
BeanPropertyWriter bpw = propsIn[i];
// should be ignored?
- if ((toIgnore != null) && toIgnore.contains(bpw.getName())) {
+ if (IgnorePropertiesUtil.shouldIgnore(bpw.getName(), toIgnore, toInclude)) {
continue;
}
propsOut.add(bpw);
@@ -226,7 +231,25 @@ public abstract class BeanSerializerBase
* @since 2.8
*/
protected abstract BeanSerializerBase withIgnorals(Set<String> toIgnore);
-
+
+ /**
+ * Mutant factory used for creating a new instance with additional
+ * set of properties to ignore or include (from properties this instance otherwise has)
+ *
+ * @since 2.12
+ */
+ protected abstract BeanSerializerBase withIgnorals(Set<String> toIgnore, Set<String> toInclude);
+
+ /**
+ * Mutant factory used for creating a new instance with additional
+ * set of properties to ignore or include (from properties this instance otherwise has)
+ *
+ * @since 2.12
+ */
+ protected BeanSerializerBase withInclusions(Set<String> toInclude) {
+ return withIgnorals(Collections.<String>emptySet(), toInclude);
+ }
+
/**
* Mutant factory used for creating a new instance with additional
* set of properties to ignore (from properties this instance otherwise has)
@@ -476,6 +499,7 @@ public abstract class BeanSerializerBase
// at a later point
int idPropOrigIndex = 0;
Set<String> ignoredProps = null;
+ Set<String> includedProps = null;
Object newFilterId = null;
// Then we may have an override for Object Id
@@ -484,6 +508,10 @@ public abstract class BeanSerializerBase
if (ignorals != null) {
ignoredProps = ignorals.findIgnoredForSerialization();
}
+ JsonIncludeProperties.Value inclusions = intr.findPropertyInclusions(accessor);
+ if (inclusions != null) {
+ includedProps = inclusions.getIncluded();
+ }
ObjectIdInfo objectIdInfo = intr.findObjectIdInfo(accessor);
if (objectIdInfo == null) {
// no ObjectId override, but maybe ObjectIdRef?
@@ -569,8 +597,9 @@ public abstract class BeanSerializerBase
}
}
// And possibly add more properties to ignore
+ contextual = contextual.withInclusions(includedProps);
if ((ignoredProps != null) && !ignoredProps.isEmpty()) {
- contextual = contextual.withIgnorals(ignoredProps);
+ contextual = contextual.withIgnorals(ignoredProps, includedProps);
}
if (newFilterId != null) {
contextual = contextual.withFilterId(newFilterId);
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 66056ff7a..34992f4f2 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
@@ -7,6 +7,7 @@ import java.util.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonIncludeProperties;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.type.WritableTypeId;
import com.fasterxml.jackson.databind.*;
@@ -15,6 +16,7 @@ import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonMapFormatVisitor;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.ContainerSerializer;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.fasterxml.jackson.databind.ser.PropertyFilter;
@@ -23,6 +25,7 @@ import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.databind.util.ArrayBuilders;
import com.fasterxml.jackson.databind.util.BeanUtil;
import com.fasterxml.jackson.databind.util.ClassUtil;
+import com.fasterxml.jackson.databind.util.IgnorePropertiesUtil;
/**
* Standard serializer implementation for serializing {link java.util.Map} types.
@@ -110,6 +113,11 @@ public class MapSerializer
protected final Set<String> _ignoredEntries;
/**
+ * Set of entries to include during serialization, if null, it is ignored, empty will include nothing.
+ */
+ protected final Set<String> _includedEntries;
+
+ /**
* Id of the property filter to use, if any; null if none.
*
* @since 2.3
@@ -157,10 +165,10 @@ public class MapSerializer
*/
/**
- * @since 2.5
+ * @since 2.12
*/
@SuppressWarnings("unchecked")
- protected MapSerializer(Set<String> ignoredEntries,
+ protected MapSerializer(Set<String> ignoredEntries, Set<String> includedEntries,
JavaType keyType, JavaType valueType, boolean valueTypeIsStatic,
TypeSerializer vts,
JsonSerializer<?> keySerializer, JsonSerializer<?> valueSerializer)
@@ -168,6 +176,7 @@ public class MapSerializer
super(Map.class, false);
_ignoredEntries = ((ignoredEntries == null) || ignoredEntries.isEmpty())
? null : ignoredEntries;
+ _includedEntries = includedEntries;
_keyType = keyType;
_valueType = valueType;
_valueTypeIsStatic = valueTypeIsStatic;
@@ -182,14 +191,34 @@ public class MapSerializer
_suppressNulls = false;
}
+ /**
+ * @since 2.5
+ * @deprecated in 2.12, remove from 3.0
+ */
+ @Deprecated
+ protected MapSerializer(Set<String> ignoredEntries,
+ JavaType keyType, JavaType valueType, boolean valueTypeIsStatic,
+ TypeSerializer vts,
+ JsonSerializer<?> keySerializer, JsonSerializer<?> valueSerializer)
+ {
+ this(ignoredEntries, null,
+ keyType, valueType, valueTypeIsStatic,
+ vts,
+ keySerializer, valueSerializer);
+ }
+
+ /**
+ * @since 2.12
+ */
@SuppressWarnings("unchecked")
protected MapSerializer(MapSerializer src, BeanProperty property,
JsonSerializer<?> keySerializer, JsonSerializer<?> valueSerializer,
- Set<String> ignoredEntries)
+ Set<String> ignoredEntries, Set<String> includedEntries)
{
super(Map.class, false);
_ignoredEntries = ((ignoredEntries == null) || ignoredEntries.isEmpty())
? null : ignoredEntries;
+ _includedEntries = includedEntries;
_keyType = src._keyType;
_valueType = src._valueType;
_valueTypeIsStatic = src._valueTypeIsStatic;
@@ -206,6 +235,18 @@ public class MapSerializer
}
/**
+ * @deprecated in 2.12, remove from 3.0
+ */
+ @SuppressWarnings("unchecked")
+ @Deprecated
+ protected MapSerializer(MapSerializer src, BeanProperty property,
+ JsonSerializer<?> keySerializer, JsonSerializer<?> valueSerializer,
+ Set<String> ignoredEntries)
+ {
+ this(src, property, keySerializer, valueSerializer, ignoredEntries, null);
+ }
+
+ /**
* @since 2.9
*/
protected MapSerializer(MapSerializer src, TypeSerializer vts,
@@ -213,6 +254,7 @@ public class MapSerializer
{
super(Map.class, false);
_ignoredEntries = src._ignoredEntries;
+ _includedEntries = src._includedEntries;
_keyType = src._keyType;
_valueType = src._valueType;
_valueTypeIsStatic = src._valueTypeIsStatic;
@@ -233,6 +275,7 @@ public class MapSerializer
{
super(Map.class, false);
_ignoredEntries = src._ignoredEntries;
+ _includedEntries = src._includedEntries;
_keyType = src._keyType;
_valueType = src._valueType;
_valueTypeIsStatic = src._valueTypeIsStatic;
@@ -258,20 +301,30 @@ public class MapSerializer
}
/**
- * @since 2.4
+ * @since 2.12
*/
public MapSerializer withResolved(BeanProperty property,
- JsonSerializer<?> keySerializer, JsonSerializer<?> valueSerializer,
- Set<String> ignored, boolean sortKeys)
+ JsonSerializer<?> keySerializer, JsonSerializer<?> valueSerializer,
+ Set<String> ignored, Set<String> included, boolean sortKeys)
{
_ensureOverride("withResolved");
- MapSerializer ser = new MapSerializer(this, property, keySerializer, valueSerializer, ignored);
+ MapSerializer ser = new MapSerializer(this, property, keySerializer, valueSerializer, ignored, included);
if (sortKeys != ser._sortKeys) {
ser = new MapSerializer(ser, _filterId, sortKeys);
}
return ser;
}
+ /**
+ * @since 2.4
+ */
+ public MapSerializer withResolved(BeanProperty property,
+ JsonSerializer<?> keySerializer, JsonSerializer<?> valueSerializer,
+ Set<String> ignored, boolean sortKeys)
+ {
+ return withResolved(property, keySerializer, valueSerializer, ignored, null, sortKeys);
+ }
+
@Override
public MapSerializer withFilterId(Object filterId) {
if (_filterId == filterId) {
@@ -296,9 +349,9 @@ public class MapSerializer
}
/**
- * @since 2.8
+ * @since 2.12
*/
- public static MapSerializer construct(Set<String> ignoredEntries, JavaType mapType,
+ public static MapSerializer construct(Set<String> ignoredEntries, Set<String> includedEntries, JavaType mapType,
boolean staticValueType, TypeSerializer vts,
JsonSerializer<Object> keySerializer, JsonSerializer<Object> valueSerializer,
Object filterId)
@@ -326,7 +379,7 @@ public class MapSerializer
staticValueType = false;
}
}
- MapSerializer ser = new MapSerializer(ignoredEntries, keyType, valueType, staticValueType, vts,
+ MapSerializer ser = new MapSerializer(ignoredEntries, includedEntries, keyType, valueType, staticValueType, vts,
keySerializer, valueSerializer);
if (filterId != null) {
ser = ser.withFilterId(filterId);
@@ -335,6 +388,17 @@ public class MapSerializer
}
/**
+ * @since 2.8
+ */
+ public static MapSerializer construct(Set<String> ignoredEntries, JavaType mapType,
+ boolean staticValueType, TypeSerializer vts,
+ JsonSerializer<Object> keySerializer, JsonSerializer<Object> valueSerializer,
+ Object filterId)
+ {
+ return construct(ignoredEntries, null, mapType, staticValueType, vts, keySerializer, valueSerializer, filterId);
+ }
+
+ /**
* @since 2.9
*/
protected void _ensureOverride(String method) {
@@ -439,8 +503,10 @@ public class MapSerializer
keySer = provider.handleSecondaryContextualization(keySer, property);
}
Set<String> ignored = _ignoredEntries;
+ Set<String> included = _includedEntries;
boolean sortKeys = false;
if (_neitherNull(propertyAcc, intr)) {
+ // ignorals
JsonIgnoreProperties.Value ignorals = intr.findPropertyIgnorals(propertyAcc);
if (ignorals != null){
Set<String> newIgnored = ignorals.findIgnoredForSerialization();
@@ -451,6 +517,18 @@ public class MapSerializer
}
}
}
+ // inclusions
+ JsonIncludeProperties.Value inclusions = intr.findPropertyInclusions(propertyAcc);
+ if (inclusions != null) {
+ Set<String> newIncluded = inclusions.getIncluded();
+ if (newIncluded != null) {
+ included = (included == null) ? new HashSet<String>() : new HashSet<String>(included);
+ for (String str : newIncluded) {
+ included.add(str);
+ }
+ }
+ }
+ // sort key
Boolean b = intr.findSerializationSortAlphabetically(propertyAcc);
sortKeys = Boolean.TRUE.equals(b);
}
@@ -461,7 +539,7 @@ public class MapSerializer
sortKeys = B.booleanValue();
}
}
- MapSerializer mser = withResolved(property, keySer, ser, ignored, sortKeys);
+ MapSerializer mser = withResolved(property, keySer, ser, ignored, included, sortKeys);
// [databind#307]: allow filtering
if (property != null) {
@@ -698,6 +776,7 @@ public class MapSerializer
}
final JsonSerializer<Object> keySerializer = _keySerializer;
final Set<String> ignored = _ignoredEntries;
+ final Set<String> included = _includedEntries;
Object keyElem = null;
try {
@@ -709,7 +788,7 @@ public class MapSerializer
provider.findNullKeySerializer(_keyType, _property).serialize(null, gen, provider);
} else {
// One twist: is entry ignorable? If so, skip
- if ((ignored != null) && ignored.contains(keyElem)) {
+ if (IgnorePropertiesUtil.shouldIgnore(keyElem, ignored, included)) {
continue;
}
keySerializer.serialize(keyElem, gen, provider);
@@ -743,6 +822,7 @@ public class MapSerializer
return;
}
final Set<String> ignored = _ignoredEntries;
+ final Set<String> included = _includedEntries;
final boolean checkEmpty = (MARKER_FOR_EMPTY == suppressableValue);
for (Map.Entry<?,?> entry : value.entrySet()) {
@@ -752,7 +832,7 @@ public class MapSerializer
if (keyElem == null) {
keySerializer = provider.findNullKeySerializer(_keyType, _property);
} else {
- if (ignored != null && ignored.contains(keyElem)) continue;
+ if (IgnorePropertiesUtil.shouldIgnore(keyElem, ignored, included)) continue;
keySerializer = _keySerializer;
}
@@ -801,11 +881,12 @@ public class MapSerializer
{
final JsonSerializer<Object> keySerializer = _keySerializer;
final Set<String> ignored = _ignoredEntries;
+ final Set<String> included = _includedEntries;
final TypeSerializer typeSer = _valueTypeSerializer;
for (Map.Entry<?,?> entry : value.entrySet()) {
Object keyElem = entry.getKey();
- if (ignored != null && ignored.contains(keyElem)) continue;
+ if (IgnorePropertiesUtil.shouldIgnore(keyElem, ignored, included)) continue;
if (keyElem == null) {
provider.findNullKeySerializer(_keyType, _property).serialize(null, gen, provider);
@@ -841,13 +922,14 @@ public class MapSerializer
throws IOException
{
final Set<String> ignored = _ignoredEntries;
+ final Set<String> included = _includedEntries;
final MapProperty prop = new MapProperty(_valueTypeSerializer, _property);
final boolean checkEmpty = (MARKER_FOR_EMPTY == suppressableValue);
for (Map.Entry<?,?> entry : value.entrySet()) {
// First, serialize key; unless ignorable by key
final Object keyElem = entry.getKey();
- if (ignored != null && ignored.contains(keyElem)) continue;
+ if (IgnorePropertiesUtil.shouldIgnore(keyElem, ignored, included)) continue;
JsonSerializer<Object> keySerializer;
if (keyElem == null) {
@@ -899,6 +981,7 @@ public class MapSerializer
throws IOException
{
final Set<String> ignored = _ignoredEntries;
+ final Set<String> included = _includedEntries;
final boolean checkEmpty = (MARKER_FOR_EMPTY == suppressableValue);
for (Map.Entry<?,?> entry : value.entrySet()) {
@@ -908,7 +991,7 @@ public class MapSerializer
keySerializer = provider.findNullKeySerializer(_keyType, _property);
} else {
// One twist: is entry ignorable? If so, skip
- if (ignored != null && ignored.contains(keyElem)) continue;
+ if (IgnorePropertiesUtil.shouldIgnore(keyElem, ignored, included)) continue;
keySerializer = _keySerializer;
}
final Object valueElem = entry.getValue();
@@ -959,13 +1042,14 @@ public class MapSerializer
throws IOException
{
final Set<String> ignored = _ignoredEntries;
+ final Set<String> included = _includedEntries;
final MapProperty prop = new MapProperty(_valueTypeSerializer, _property);
final boolean checkEmpty = (MARKER_FOR_EMPTY == suppressableValue);
for (Map.Entry<?,?> entry : value.entrySet()) {
// First, serialize key; unless ignorable by key
final Object keyElem = entry.getKey();
- if (ignored != null && ignored.contains(keyElem)) continue;
+ if (IgnorePropertiesUtil.shouldIgnore(keyElem, ignored, included)) continue;
JsonSerializer<Object> keySerializer;
if (keyElem == null) {
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/IgnorePropertiesUtil.java b/src/main/java/com/fasterxml/jackson/databind/util/IgnorePropertiesUtil.java
new file mode 100644
index 000000000..75766150d
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/util/IgnorePropertiesUtil.java
@@ -0,0 +1,31 @@
+package com.fasterxml.jackson.databind.util;
+
+import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
+
+import java.util.Collection;
+import java.util.Set;
+
+public class IgnorePropertiesUtil
+{
+ /**
+ * Decide if we need to ignore a property or not, given a set of field to ignore and a set of field to include.
+ *
+ * @since 2.12
+ */
+ public static boolean shouldIgnore(Object value, Collection<String> toIgnore, Collection<String> toInclude) {
+ if (toIgnore == null && toInclude ==null) {
+ return false;
+ }
+
+ if (toInclude == null) {
+ return toIgnore.contains(value);
+ }
+
+ if (toIgnore == null) {
+ return !toInclude.contains(value);
+ }
+
+ // NOTE: conflict between both, JsonIncludeProperties will take priority.
+ return !toInclude.contains(value) || toIgnore.contains(value);
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/IncludeWithDeserTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/IncludeWithDeserTest.java
new file mode 100644
index 000000000..0d10e8b07
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/IncludeWithDeserTest.java
@@ -0,0 +1,202 @@
+package com.fasterxml.jackson.databind.deser;
+
+import com.fasterxml.jackson.annotation.JsonAnySetter;
+import com.fasterxml.jackson.annotation.JsonIdentityInfo;
+import com.fasterxml.jackson.annotation.JsonIncludeProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.ObjectIdGenerators;
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectReader;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This unit test suite that tests use of {@link com.fasterxml.jackson.annotation.JsonIncludeProperties}
+ * annotation with deserialization.
+ */
+public class IncludeWithDeserTest
+ extends BaseMapTest
+{
+ @JsonIncludeProperties({"y", "z"})
+ static class OnlyYAndZ
+ {
+ int _x = 0;
+ int _y = 0;
+ int _z = 0;
+
+ public void setX(int value)
+ {
+ _x = value;
+ }
+
+ public void setY(int value)
+ {
+ _y = value;
+ }
+
+ public void setZ(int value)
+ {
+ _z = value;
+ }
+
+ @JsonProperty("y")
+ void replacementForY(int value)
+ {
+ _y = value * 2;
+ }
+ }
+
+ @JsonIncludeProperties({"y", "z"})
+ static class OnlyY
+ {
+ public int x;
+
+ public int y = 1;
+ }
+
+ static class OnlyYWrapperForOnlyYAndZ
+ {
+ @JsonIncludeProperties("y")
+ public OnlyYAndZ onlyY;
+ }
+
+ // for [databind#1060]
+ static class IncludeForListValuesY
+ {
+ @JsonIncludeProperties({"y"})
+ //@JsonIgnoreProperties({"z"})
+ public List<OnlyYAndZ> onlyYs;
+
+ public IncludeForListValuesY()
+ {
+ onlyYs = Arrays.asList(new OnlyYAndZ());
+ }
+ }
+
+ @JsonIncludeProperties({"@class", "a"})
+ static class MyMap extends HashMap<String, String>
+ {
+ }
+
+ static class MapWrapper
+ {
+ @JsonIncludeProperties({"a"})
+ public final HashMap<String, Integer> value = new HashMap<String, Integer>();
+ }
+
+ @JsonIncludeProperties({"foo", "bar"})
+ @JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class)
+ static class AnySetterObjectId
+ {
+ protected Map<String, AnySetterObjectId> values = new HashMap<String, AnySetterObjectId>();
+
+ @JsonAnySetter
+ public void anySet(String field, AnySetterObjectId value)
+ {
+ // Ensure that it is never called with null because of unresolved reference.
+ assertNotNull(value);
+ values.put(field, value);
+ }
+ }
+
+ /*
+ /**********************************************************
+ /* Test methods
+ /**********************************************************
+ */
+
+ private final ObjectMapper MAPPER = objectMapper();
+
+ public void testSimpleInclude() throws Exception
+ {
+ OnlyYAndZ result = MAPPER.readValue(
+ aposToQuotes("{ 'x':1, '_x': 1, 'y':2, 'z':3 }"),
+ OnlyYAndZ.class);
+ assertEquals(0, result._x);
+ assertEquals(4, result._y);
+ assertEquals(3, result._z);
+ }
+
+ public void testIncludeIgnoredAndUnrecognizedField() throws Exception
+ {
+ ObjectReader r = MAPPER.readerFor(OnlyY.class);
+
+ // First, fine to get "y" only:
+ OnlyY result = r.readValue(aposToQuotes("{'x':3, 'y': 4}"));
+ assertEquals(0, result.x);
+ assertEquals(4, result.y);
+
+ // but fail on ignored properties.
+ r = r.with(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES);
+ try {
+ r.readValue(aposToQuotes("{'x':3, 'y': 4, 'z': 5}"));
+ fail("Should fail");
+ } catch (JsonMappingException e) {
+ verifyException(e, "Ignored field");
+ }
+
+ // or fail on unrecognized properties
+ try {
+ r.readValue(aposToQuotes("{'y': 3, 'z':2 }"));
+ fail("Should fail");
+ } catch (JsonMappingException e) {
+ verifyException(e, "Unrecognized field");
+ }
+
+ // or success with the both settings disabled.
+ r = r.without(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
+ r = r.without(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES);
+ r.readValue(aposToQuotes("{'y': 3, 'z':2 }"));
+ assertEquals(4, result.y);
+ }
+
+
+ public void testMergeInclude() throws Exception
+ {
+ OnlyYWrapperForOnlyYAndZ onlyY = MAPPER.readValue(
+ aposToQuotes("{'onlyY': {'x': 2, 'y':3, 'z': 4}}"),
+ OnlyYWrapperForOnlyYAndZ.class
+ );
+ assertEquals(0, onlyY.onlyY._x);
+ assertEquals(6, onlyY.onlyY._y);
+ assertEquals(0, onlyY.onlyY._z);
+ }
+
+ public void testListInclude() throws Exception
+ {
+ IncludeForListValuesY result = MAPPER.readValue(
+ aposToQuotes("{'onlyYs':[{ 'x':1, 'y' : 2, 'z': 3 }]}"),
+ IncludeForListValuesY.class);
+ assertEquals(0, result.onlyYs.get(0)._x);
+ assertEquals(4, result.onlyYs.get(0)._y);
+ assertEquals(0, result.onlyYs.get(0)._z);
+ }
+
+ public void testMapWrapper() throws Exception
+ {
+ MapWrapper result = MAPPER.readValue(aposToQuotes("{'value': {'a': 2, 'b': 3}}"), MapWrapper.class);
+ assertEquals(2, result.value.get("a").intValue());
+ assertFalse(result.value.containsKey("b"));
+ }
+
+ public void testMyMap() throws Exception
+ {
+ MyMap result = MAPPER.readValue(aposToQuotes("{'a': 2, 'b': 3}"), MyMap.class);
+ assertEquals("2", result.get("a"));
+ assertFalse(result.containsKey("b"));
+ }
+
+ public void testForwardReferenceAnySetterComboWithInclude() throws Exception
+ {
+ String json = aposToQuotes("{'@id':1, 'foo':2, 'foo2':2, 'bar':{'@id':2, 'foo':1}}");
+ AnySetterObjectId value = MAPPER.readValue(json, AnySetterObjectId.class);
+ assertSame(value.values.get("bar"), value.values.get("foo"));
+ assertNull(value.values.get("foo2"));
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/filter/IncludePropsForSerTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/filter/IncludePropsForSerTest.java
new file mode 100644
index 000000000..952f1eb3c
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/filter/IncludePropsForSerTest.java
@@ -0,0 +1,185 @@
+package com.fasterxml.jackson.databind.ser.filter;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonIncludeProperties;
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class IncludePropsForSerTest extends BaseMapTest
+{
+ @JsonIncludeProperties({"a", "d"})
+ static class IncludeSome
+ {
+ public int a = 3;
+ public String b = "x";
+
+ public int getC()
+ {
+ return -6;
+ }
+
+ public String getD()
+ {
+ return "abc";
+ }
+ }
+
+ @SuppressWarnings("serial")
+ @JsonIncludeProperties({"@class", "a"})
+ static class MyMap extends HashMap<String, String> { }
+
+ //allow use of @JsonIncludeProperties for properties
+ static class WrapperWithPropInclude
+ {
+ @JsonIncludeProperties({"y"})
+ public XY value = new XY();
+ }
+
+ static class XY
+ {
+ public int x = 1;
+ public int y = 2;
+ }
+
+ static class WrapperWithPropInclude2
+ {
+ @JsonIncludeProperties("x")
+ public XYZ value = new XYZ();
+ }
+
+ static class WrapperWithPropIgnore
+ {
+ @JsonIgnoreProperties("y")
+ public XYZ value = new XYZ();
+ }
+
+ @JsonIncludeProperties({"x", "y"})
+ static class XYZ
+ {
+ public int x = 1;
+ public int y = 2;
+ public int z = 3;
+ }
+
+ // also ought to work without full typing?
+ static class WrapperWithPropIncludeUntyped
+ {
+ @JsonIncludeProperties({"x"})
+ public Object value = new XYZ();
+ }
+
+ static class MapWrapper
+ {
+ @JsonIncludeProperties({"a"})
+ public final HashMap<String, Integer> value = new HashMap<String, Integer>();
+
+ {
+ value.put("a", 1);
+ value.put("b", 2);
+ }
+ }
+
+ // for [databind#1060]
+ static class IncludeForListValuesXY
+ {
+ @JsonIncludeProperties({"x"})
+ public List<XY> coordinates;
+
+ public IncludeForListValuesXY()
+ {
+ coordinates = Arrays.asList(new XY());
+ }
+ }
+
+ static class IncludeForListValuesXYZ
+ {
+ @JsonIncludeProperties({"x"})
+ public List<XYZ> coordinates;
+
+ public IncludeForListValuesXYZ()
+ {
+ coordinates = Arrays.asList(new XYZ());
+ }
+ }
+
+ /*
+ /****************************************************************
+ /* Unit tests
+ /****************************************************************
+ */
+
+ private final ObjectMapper MAPPER = objectMapper();
+
+ public void testExplicitIncludeWithBean() throws Exception
+ {
+ IncludeSome value = new IncludeSome();
+ Map<String, Object> result = writeAndMap(MAPPER, value);
+ assertEquals(2, result.size());
+ // verify that specified fields are ignored
+ assertFalse(result.containsKey("b"));
+ assertFalse(result.containsKey("c"));
+ // and that others are not
+ assertEquals(Integer.valueOf(value.a), result.get("a"));
+ assertEquals(value.getD(), result.get("d"));
+ }
+
+ public void testExplicitIncludeWithMap() throws Exception
+ {
+ // test simulating need to filter out metadata like class name
+ MyMap value = new MyMap();
+ value.put("a", "b");
+ value.put("c", "d");
+ value.put("@class", MyMap.class.getName());
+ Map<String, Object> result = writeAndMap(MAPPER, value);
+ assertEquals(2, result.size());
+ assertEquals(MyMap.class.getName(), result.get("@class"));
+ assertEquals(value.get("a"), result.get("a"));
+ }
+
+ public void testIncludeViaOnlyProps() throws Exception
+ {
+ assertEquals("{\"value\":{\"y\":2}}",
+ MAPPER.writeValueAsString(new WrapperWithPropInclude()));
+ }
+
+ // Also: should be fine even if nominal type is `java.lang.Object`
+ public void testIncludeViaPropForUntyped() throws Exception
+ {
+ assertEquals("{\"value\":{\"x\":1}}",
+ MAPPER.writeValueAsString(new WrapperWithPropIncludeUntyped()));
+ }
+
+ public void testIncludeWithMapProperty() throws Exception
+ {
+ assertEquals("{\"value\":{\"a\":1}}", MAPPER.writeValueAsString(new MapWrapper()));
+ }
+
+ public void testIncludeViaPropsAndClass() throws Exception
+ {
+ assertEquals("{\"value\":{\"x\":1}}",
+ MAPPER.writeValueAsString(new WrapperWithPropInclude2()));
+ }
+
+ // for [databind#1060]
+ // Ensure that `@JsonIncludeProperties` applies to POJOs within lists, too
+ public void testIncludeForListValues() throws Exception
+ {
+ // should apply to elements
+ assertEquals(aposToQuotes("{'coordinates':[{'x':1}]}"),
+ MAPPER.writeValueAsString(new IncludeForListValuesXY()));
+
+ // and combine values too
+ assertEquals(aposToQuotes("{'coordinates':[{'x':1}]}"),
+ MAPPER.writeValueAsString(new IncludeForListValuesXYZ()));
+ }
+
+ public void testIgnoreWithInclude() throws Exception
+ {
+ assertEquals("{\"value\":{\"x\":1}}", MAPPER.writeValueAsString(new WrapperWithPropIgnore()));
+ }
+}