diff options
Diffstat (limited to 'src/main/java')
23 files changed, 544 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); + } +} |