Skip to content

FasterXML/jackson-databind#1296 @JsonIncludeProperties #2771

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Jul 24, 2020
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,18 @@ public JsonIgnoreProperties.Value findPropertyIgnorals(Annotated ac)
*/
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.
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,17 @@ public JsonInclude.Value getDefaultInclusion(Class<?> baseType,
public abstract JsonIgnoreProperties.Value getDefaultPropertyIgnorals(Class<?> baseType,
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,14 @@ public final JsonIgnoreProperties.Value getDefaultPropertyIgnorals(Class<?> base
return JsonIgnoreProperties.Value.merge(base, overrides);
}

@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()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
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;
Expand Down Expand Up @@ -1373,6 +1374,10 @@ public JsonDeserializer<?> createMapDeserializer(DeserializationContext ctxt,
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;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
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;

Expand Down Expand Up @@ -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);
}

/**
Expand All @@ -86,10 +104,21 @@ public BeanDeserializer(BeanDeserializerBase src, ObjectIdReader oir) {
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);
}
Expand Down Expand Up @@ -119,8 +148,8 @@ public BeanDeserializer withObjectIdReader(ObjectIdReader oir) {
}

@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
Expand Down Expand Up @@ -464,7 +493,7 @@ protected Object _deserializeUsingPropertyBased(final JsonParser p, final Deseri
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;
}
Expand Down Expand Up @@ -694,7 +723,7 @@ protected Object deserializeWithUnwrapped(JsonParser p, DeserializationContext c
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;
}
Expand Down Expand Up @@ -751,7 +780,7 @@ protected Object deserializeWithUnwrapped(JsonParser p, DeserializationContext c
}
continue;
}
if (_ignorableProps != null && _ignorableProps.contains(propName)) {
if (IgnorePropertiesUtil.shouldIgnore(propName, _ignorableProps, _includableProps)) {
handleIgnoredProperty(p, ctxt, bean, propName);
continue;
}
Expand Down Expand Up @@ -850,7 +879,7 @@ protected Object deserializeUsingPropertyBasedWithUnwrapped(JsonParser p, Deseri
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;
}
Expand Down Expand Up @@ -942,7 +971,7 @@ protected Object deserializeWithExternalTypeId(JsonParser p, DeserializationCont
continue;
}
// ignorable things should be ignored
if (_ignorableProps != null && _ignorableProps.contains(propName)) {
if (IgnorePropertiesUtil.shouldIgnore(propName, _ignorableProps, _includableProps)) {
handleIgnoredProperty(p, ctxt, bean, propName);
continue;
}
Expand Down Expand Up @@ -1033,7 +1062,7 @@ protected Object deserializeUsingPropertyBasedWithExternalTypeId(JsonParser p, D
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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,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.
Expand Down Expand Up @@ -201,6 +206,7 @@ protected BeanDeserializerBase(BeanDeserializerBuilder builder,
BeanDescription beanDesc,
BeanPropertyMap properties, Map<String, SettableBeanProperty> backRefs,
Set<String> ignorableProps, boolean ignoreAllUnknown,
Set<String> includableProps,
boolean hasViews)
{
super(beanDesc.getType());
Expand All @@ -211,6 +217,7 @@ protected BeanDeserializerBase(BeanDeserializerBuilder builder,
_backRefs = backRefs;
_ignorableProps = ignorableProps;
_ignoreAllUnknown = ignoreAllUnknown;
_includableProps = includableProps;

_anySetter = builder.getAnySetter();
List<ValueInjector> injectables = builder.getInjectables();
Expand Down Expand Up @@ -263,6 +270,7 @@ protected BeanDeserializerBase(BeanDeserializerBase src, boolean ignoreAllUnknow
_backRefs = src._backRefs;
_ignorableProps = src._ignorableProps;
_ignoreAllUnknown = ignoreAllUnknown;
_includableProps = src._includableProps;
_anySetter = src._anySetter;
_injectables = src._injectables;
_objectIdReader = src._objectIdReader;
Expand All @@ -288,6 +296,7 @@ protected BeanDeserializerBase(BeanDeserializerBase src, NameTransformer unwrapp
_backRefs = src._backRefs;
_ignorableProps = src._ignorableProps;
_ignoreAllUnknown = (unwrapper != null) || src._ignoreAllUnknown;
_includableProps = src._includableProps;
_anySetter = src._anySetter;
_injectables = src._injectables;
_objectIdReader = src._objectIdReader;
Expand Down Expand Up @@ -325,6 +334,7 @@ public BeanDeserializerBase(BeanDeserializerBase src, ObjectIdReader oir)
_backRefs = src._backRefs;
_ignorableProps = src._ignorableProps;
_ignoreAllUnknown = src._ignoreAllUnknown;
_includableProps = src._includableProps;
_anySetter = src._anySetter;
_injectables = src._injectables;

Expand All @@ -351,6 +361,14 @@ public BeanDeserializerBase(BeanDeserializerBase src, ObjectIdReader oir)
}

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;
Expand All @@ -362,6 +380,7 @@ public BeanDeserializerBase(BeanDeserializerBase src, Set<String> ignorableProps
_backRefs = src._backRefs;
_ignorableProps = ignorableProps;
_ignoreAllUnknown = src._ignoreAllUnknown;
_includableProps = includableProps;
_anySetter = src._anySetter;
_injectables = src._injectables;

Expand All @@ -375,9 +394,10 @@ public BeanDeserializerBase(BeanDeserializerBase src, Set<String> ignorableProps

// 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
*/
Expand All @@ -394,6 +414,7 @@ protected BeanDeserializerBase(BeanDeserializerBase src, BeanPropertyMap beanPro
_backRefs = src._backRefs;
_ignorableProps = src._ignorableProps;
_ignoreAllUnknown = src._ignoreAllUnknown;
_includableProps = src._includableProps;
_anySetter = src._anySetter;
_injectables = src._injectables;
_objectIdReader = src._objectIdReader;
Expand All @@ -411,7 +432,21 @@ protected BeanDeserializerBase(BeanDeserializerBase src, BeanPropertyMap beanPro

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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(note to self, mostly)

Makes sense to use different name, I think, withPropertyInclusions() (to indicate both ignoral and/or inclusion contained), for the new method.


/**
* @since 2.12
*/
public BeanDeserializerBase withIncludableProperties(Set<String> includableProperties) {
return withIgnorableProperties(_ignorableProps, includableProperties);
}

// NOTE! To be made `abstract` in 2.12 or later
/**
Expand All @@ -422,7 +457,7 @@ public BeanDeserializerBase withIgnoreAllUnknown(boolean ignoreUnknown) {
if (ignoreUnknown == _ignoreAllUnknown) {
return this;
}
return withIgnorableProperties(_ignorableProps);
return withIgnorableProperties(_ignorableProps, _includableProps);
}

/**
Expand Down Expand Up @@ -469,10 +504,10 @@ public void resolve(DeserializationContext ctxt) throws JsonMappingException
// 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();
}
}
Expand Down Expand Up @@ -773,6 +808,21 @@ public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not 100% sure if this is the right default: seems like there would be 3 possibilities:

  1. Intersection (must be in class defaults/annotations AND property)
  2. Union (add anything property defines)
  3. Overwrite/replace (use whatever property defines)

Now, adding "mode" or something in JsonIncludeProperties (and .Value) would allow specifying these modes easily if we want to. But even without adding that, I wonder what the most likely expected use case would be?
My guess would have been either (3) or (2).

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?
Expand Down Expand Up @@ -1587,7 +1637,8 @@ protected void handleUnknownVanilla(JsonParser p, DeserializationContext ctxt,
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 {
Expand Down Expand Up @@ -1615,7 +1666,7 @@ protected void handleUnknownProperty(JsonParser p, DeserializationContext ctxt,
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
Expand Down
Loading