Can I dynamically disable JsonAnySetter so that I can have strict rules from an API perspective?

1,245 views
Skip to first unread message

Brent Ryan

unread,
Sep 9, 2016, 6:07:47 PM9/9/16
to jackson-user
We're looking to see if there's a way to use the same model with @JsonAnySetter jackson pattern for serialization to backend data storage, while allowing us to use the same models for the inbound API requests that are more strict.  So we want FAIL_ON_UNKNOWN_PROPERTIES set to true and @JsonAnySetter to be disabled from the API perspective.

Is there a way to do this or do you have to have 2 separate models or use mix-ins?  Looking for the preferred approach to dealing with this.

Example class:

public class Data {

  @JsonIgnore
  private Map<String, Object> additionalProperties = new HashMap<String, Object>();

  @JsonAnyGetter
  public Map<String, Object> getAdditionalProperties() {
    return this.additionalProperties;
  }

  @JsonAnySetter
  public void setAdditionalProperty(String name, Object value) {
    this.additionalProperties.put(name, value);
  }

}

Is there a way to disable a feature in ObjectMapper that turns off the JsonAnySetter/JsonAnyGetter for 1 use case, but then enables it for others?

Brent Ryan

unread,
Sep 9, 2016, 6:50:48 PM9/9/16
to jackson-user
I'm going to attempt to do this by just creating a DisableAdditionalPropertiesMixin class that I can apply.  I'm not sure this will work and it kinda sucks because it means that the only way for it to work for everything is to apply this to every Object in the hierarchy.

Any other ideas here?

Brent Ryan

unread,
Sep 10, 2016, 8:10:28 PM9/10/16
to jackson-user
So using a mixin did NOT work.  Here's the mixin:

interface DisableAdditionalPropertiesMixIn {

    @JsonIgnore
    Map<String, Object> getAdditionalProperties();

    @JsonIgnore
    void setAdditionalProperty(String name, Object value);
}

m.addMixInAnnotations(Data.class, DisableAdditionalPropertiesMixIn.class);

This basically did nothing.  My guess is that mixins only allow you to add OR override any existing annotation, but not remove it/ignore it unless the annotation itself supports some flag you can set to disable it.

So instead, I did this:


public class IgnoreAdditionalPropertiesInspector extends JacksonAnnotationIntrospector {

    @Override
    public boolean hasAnySetterAnnotation(AnnotatedMethod am) {
        return false;
    }

    @Override
    public boolean hasAnyGetterAnnotation(AnnotatedMethod am) {
        return false;
    }

}

JacksonAnnotationIntrospector introspector = new IgnoreAdditionalPropertiesInspector();
m.setAnnotationIntrospector(introspector);


This works! woohoo!

However, I'm curious if there's any better way to achieve this or is this the recommended approach?

Thanks!

Brent Ryan

unread,
Sep 10, 2016, 8:57:29 PM9/10/16
to jackson-user
Please note that this works with Jackson 2.3.3.  I'm not sure what's changed since that version so it might need to be adjusted with later versions.

Tatu Saloranta

unread,
Sep 10, 2016, 10:17:45 PM9/10/16
to jackso...@googlegroups.com
On Sat, Sep 10, 2016 at 5:10 PM, Brent Ryan <brent...@gmail.com> wrote:
So using a mixin did NOT work.  Here's the mixin:

interface DisableAdditionalPropertiesMixIn {

    @JsonIgnore
    Map<String, Object> getAdditionalProperties();

    @JsonIgnore
    void setAdditionalProperty(String name, Object value);
}

m.addMixInAnnotations(Data.class, DisableAdditionalPropertiesMixIn.class);

This basically did nothing.  My guess is that mixins only allow you to add OR override any existing annotation, but not remove it/ignore it unless the annotation itself supports some flag you can set to disable it.

Mix-in annotations are very simple: they only allow you to add a "missing" annotation, or replace existing one. But there is no per-annotation logic to replace other annotations: for example, just because you add `@JsonIgnore` does not remove `@JsonAnySetter` or `@JsonAnyGetter`.

However... aside from mix-ins, perhaps behavior of having both `@JsonIgnore` and any-setter/any-getter annotation ought to mean that the accessor is ignored, and not considered any-setter/any-getter?
This logic would reside at code that determines actual bean definition, outside of mix-in handler, and I don't think ignorals are currently considered.

One other related comment on disabling annotations: it is actually possible to also "undo" ignoral by using mix-in for

   @JsonIgnore(false)

since @JsonIgnore does take boolean value (with default of `true`): it determines if ignorals is enabled or not. And the only reason for that is to allow overriding an existing ignoral either by sub-class (annotations are inherited, so sub-classes can override annotations), or by mix-ins.

Come to think of this now, another possibly better improvement, to allow disabling, would be to make @JsonAnyGetter and @JsonAnySetter to also use boolean `value`, similar to @JsonIgnore.

If so, you could add, via mix-in:

@JsonAnySetter(false)

to effectively disable usage.
 
So instead, I did this:


public class IgnoreAdditionalPropertiesInspector extends JacksonAnnotationIntrospector {

    @Override
    public boolean hasAnySetterAnnotation(AnnotatedMethod am) {
        return false;
    }

    @Override
    public boolean hasAnyGetterAnnotation(AnnotatedMethod am) {
        return false;
    }

}

JacksonAnnotationIntrospector introspector = new IgnoreAdditionalPropertiesInspector();
m.setAnnotationIntrospector(introspector);


This works! woohoo!

Yes, that makes sense to me.
 

However, I'm curious if there's any better way to achieve this or is this the recommended approach?

I think your approach is very sensible and something I could have recommended myself.

But I think this does also raise the question of whether override system for any-properties could and should be improved.

Could you file an issue (RFE) for this at:


so that we could perhaps get it added in 2.9? I know it is not something you need, with AnnotationIntrospector, but seems a worthwhile and quite straight-forward addition.

-+ Tatu +-
 

Thanks!


On Friday, September 9, 2016 at 6:50:48 PM UTC-4, Brent Ryan wrote:
I'm going to attempt to do this by just creating a DisableAdditionalPropertiesMixin class that I can apply.  I'm not sure this will work and it kinda sucks because it means that the only way for it to work for everything is to apply this to every Object in the hierarchy.

Any other ideas here?


On Friday, September 9, 2016 at 6:07:47 PM UTC-4, Brent Ryan wrote:
We're looking to see if there's a way to use the same model with @JsonAnySetter jackson pattern for serialization to backend data storage, while allowing us to use the same models for the inbound API requests that are more strict.  So we want FAIL_ON_UNKNOWN_PROPERTIES set to true and @JsonAnySetter to be disabled from the API perspective.

Is there a way to do this or do you have to have 2 separate models or use mix-ins?  Looking for the preferred approach to dealing with this.

Example class:

public class Data {

  @JsonIgnore
  private Map<String, Object> additionalProperties = new HashMap<String, Object>();

  @JsonAnyGetter
  public Map<String, Object> getAdditionalProperties() {
    return this.additionalProperties;
  }

  @JsonAnySetter
  public void setAdditionalProperty(String name, Object value) {
    this.additionalProperties.put(name, value);
  }

}

Is there a way to disable a feature in ObjectMapper that turns off the JsonAnySetter/JsonAnyGetter for 1 use case, but then enables it for others?

--
You received this message because you are subscribed to the Google Groups "jackson-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jackson-user+unsubscribe@googlegroups.com.
To post to this group, send email to jackso...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Brent Ryan

unread,
Sep 17, 2016, 12:30:14 PM9/17/16
to jackson-user
To unsubscribe from this group and stop receiving emails from it, send an email to jackson-user...@googlegroups.com.

Tatu Saloranta

unread,
Sep 17, 2016, 4:05:28 PM9/17/16
to jackso...@googlegroups.com
Excellent, thank you.

-+ Tatu +-

To unsubscribe from this group and stop receiving emails from it, send an email to jackson-user+unsubscribe@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages