Saturday, February 05, 2011

Every day Jackson usage, part 3: Filtering properties

(part of continuing series on covering common usage patterns with Jackson JSON processor; previous entry covered handling open (extensible) content)

One of first things users eventually want to configure when using Jackson is (re)defining which Java object properties to serialize (written out) and which not.

1. What gets serialized by default?

Properties of an object are initially determined by process called auto-detection: all member methods and fields are checked to find:

  1. "Getter" methods: all no-argument public member methods which return a value, and conform to naming convention of "getXxx" (or "isXxx", iff return type is boolean; called "is-getter") are considered to infer existence of property with name "xxx" (where property name is inferred using bean convention, i.e. the leading capitali letter(s) is changed to lower case
  2. field properties: all public member fields are considered to represent properties, using field name as is.

In case both a getter and a field is found for same logical property, getter method has precedence and is used (field is ignored).

Set of properties introspected using this process are considered to be the base set of properties. But the auto-detection process itself can be configured differently, and there are multiple annotations and configuration settings that can further change actual effective set of properties to serialize.

2. Changing auto-detection defaults: @JsonAutoDetect

If the default auto-detection visibility limits (fields and member methods needing to be public) are not to your liking, it is easy to change them by using one of following method:

  • @JsonAutoDetect annotation can be defined for classes; properties "fieldVisibility", "getterVisibility" and "isGetterVisibility" define minimum visibility needed to include property (for fields, getters and is-getters, respectively). It is possible to, for example, include all field properties regardess of visibility (@JsonAutoDetect(fieldVisibility=Visibility.ANY)); or to disable getter-method auto-detection (@JsonAutoDetect(getterVisibility=Visibility.NONE)), and combinations there-of. Note that this annotation (just like any other Jackson annotation) can be applied as a mix-in annotation, without having to modify type directly; and can be added to a base type to apply to all subtypes.
  • ObjectMapper.setVisibilityChecker() can be used to define customized minimum visibility detection

Changing minimum auto-detection visibility limits is an easy to way to increase number of properties discovered (for example by exposing all member fields; similar to how libraries like XStream and Gson work by default), or to prevent any and all auto-detection (i.e. to force explicit annotations using @JsonProperty or @JsonGetter annotation).

As an example, to serialize all fields (and use no getter methods), you could do:

  @JsonAutoDetect(fieldVisibility=Visibility.ANY,
                  getterVisibility=Visibility.NONE, isGetterVisibility=Visibility.NONE)
  public class FieldsOnlyBean {
    private String name; // will now be used instead of getName()
    public String getName() { throw new Error(); } // never used!
  }

3. Explicitly ignoring properties: @JsonIgnore, @JsonIgnoreProperties

Given set of auto-detect potential properties is the starting point; and it can further be modified by per-property annotations:

  • @JsonProperty (and @JsonGetter, @JsonAnyGetter) can be used to indicate that a field or method is to be consider a property field or getter method, even if it isn't auto-detected.
  • @JsonIgnore can be used to forcibly prevent inclusion, regardless of auto-detection (or other annotations)

In addition, there is per-class annotation @JsonIgnoreProperties that can be used alternatively to list names of logical properties NOT to include for serialization; it may be easier to use via mix-in annotations than per-property annotations (although both can be used via mix-in annotations).

So you could do:

  @JsonIgnoreProperties({ "internal" })
  public class Bean {
    public Settings getInternal() { ... } // ignored
    @JsonIgnore public Settinger getBogus(); // likewise ignored
    public String getName(); // but this would be serialized
}

4. Defining profiles for dynamic ignoral: JSON Views (@JsonView)

So far configuration methods have been applied statically; meaning that a property will either always be included (except for special case of possibly suppressing null-values), or never included.

JSON views are a way to define more dynamic inclusion/exclusion strategy. The idea is to define inclusion rules for properties, by associating logical views (classes used as identifiers; this allows use of hierarchic views) with properties, using @JsonView annotations; and then specifying which view is to be used for serialization. This is often used to define smaller "public" set of properties, and larger "private" or "confidential" set of properties. See @JsonView wiki page for usage example.

5. Ignoring all properties with specified type: @JsonIgnoreType

In addition to defining rules on per-property basis, there are times when it makes sense to just prevent serialization of any auto-detected properties for given type(s). For example, many frameworks add specific accessors for types they generate, which return objects that should not be serialized.

For example, let's say that an Object-Relational Mapper always adds "public Schema getSchema()" accessors for all value classes. And if this is metadata that is not part of serializable state, we can prevent its inclusion in serialization by adding @JsonIgnoreType annotation on Schema type (or its supertype). This is often easiest done using mix-in annotations.

6. Fully dynamic filtering: @JsonFilter

Although JSON views allow somewhat dynamic filtering, definitions of filters are still static. This means that it is only possible to dynamically choose from a static set of views.

JSON Filters are a way to implement fully dynamic filtering. The way this is done is by defining logical filter a property uses, with @JsonFilter("id") annotation, but specifying actual filter (and its configuration) using ObjectWriter. Filters themselves are obtained via FilterProviders which can be fully custom, or based on simple implementations. Check out JSON Filter wiki page for details.

7 . Most extreme way to filter out properties: BeanSerializerModifier

And if ability to define custom filters is not enough, the ultimate in configurability is ability to modify configuration and configuration of BeanSerializer instances. This makes it possible to do all kinds of modifications (changing order in which properties are serialized; adding, removing or renaming properties; replacing serializer altogether with a custom instance and so on): you can completely re-wire or replace serialization of regular POJO ("bean") types.

This is achieved by adding a BeanSerializerModifier: the simplest way to do this is by using Module interface. Details of using BeanSerializerModifier are more advanced topic; I hope to cover it separately in future. The basic idea is that BeanSerializerModifier instance defines callbacks that Jackson BeanSerializerFactory calls during construction of a serializer.

blog comments powered by Disqus

Sponsored By


Related Blogs

(by Author (topics))

Powered By

About me

  • I am known as Cowtowncoder
  • Contact me at@yahoo.com
Check my profile to learn more.