Jersey’s Entity Filtering meets Jackson

Support for Entity Filtering in Jersey introduces a convenient facility for reducing the amount of data exchanged over the wire between client and server without a need to create specialized data view components. The main idea behind this feature is to give you means that will let you selectively filter out any non-relevant data from the model before sending the data to the other party.

Entity Data Filtering is not a new feature but so far the support was limited to MOXy JSON provider. Since Jersey 2.16, we support also Jackson (2.x) JSON processor and in this article we’re going to take a look into it in more detail.

What do I need?

Assuming that you’re already using Jackson 2.x support in your application via jersey-media-json-jackson module all you have to do is to update the version to at least 2.16 (-SNAPSHOT since Jersey 2.16 is not released at the time of writing this article). A new module, jersey-entity-filtering, would be transitively added to your class-path. With this setup you have all the dependencies needed to leverage the support of Entity Filtering.

Registering/Enabling Entity Filtering feature is done by registering

in your application, For example,

How can I filter data with it?

Certainly, the most important question. Entity Filtering can be used in your application in three ways, via

Each of these ways comes with a fully working example already available on GitHub. The examples are, by default, configured to work with MOXy JSON provider but enabling Jackson instead of MOXy is very simple.

There are exactly the following lines

in an Jersey application class present in every example. Comment the first highlighted line and uncomment the second highlighted line in the class. That’s all, now you’re using Jackson to work with JSON in your application.

Entity Filtering Annotations

Full example available on GitHub: entity-filtering

In it’s nature the Entity Filtering feature is designed in a very similar way the Name Binding in JAX-RS is. Simply create an annotation that could be attached to domain classes and resource methods (resource classes), bind these two concepts together and let the framework act upon it. Nothing too difficult. In fact, the simplest scenario consists of

  • registering Jersey’s EntityFilteringFeature (and media provider) in your application
  • creating custom filtering annotation, and
  • applying this annotation to model and resource methods.

The first step is described in previous sections. The more interesting second step is to create a custom annotation and make Jersey to recognize it as entity filtering annotation. This is done by using meta-annotation @EntityFiltering.

So far so good. And now, the third step. Use this new annotation to annotate the model …

… and a resource class.

And … done! So what do we have here?

We defined two views on our Project class – basic and detailed. “Basic” (or default) view is implicit and contains all non-annotated fields (id, name, description). Detailed view is explicit and is defined by placing @ProjectDetailedView over some of the fields (classes/getters/setters/property-accessors). This view contains all of the basic view fields (since there are no other constraints defined) as well as annotated fields (tasks, users).

To bind (or assign) views together with resource methods we also use entity filtering annotations. If a resource method should return a detailed view on our model (returned instance) we simply annotate the method with an entity filtering annotation (see ProjectsResource#getDetailedProjects()). If we don’t annotate a resource method with any entity filtering annotation then basic (or default) view is assumed and only the fields tied with this view are sent over the wire.

To summarize this, even though the same instance of Project class is used to create response for both basic and detailed view the actual data sent over the wire differ for each of these two views.

Basic View on a Project (JSON representation):

Detailed View on a Project (JSON representation):

More information about this approach is available in Jersey User Guide:

Security Annotations

Full example available on GitHub: entity-filtering-security

I have already written an article on Filtering JAX-RS Entities with Standard Security Annotations so if you’re interested in filtering data based on user roles, please, read that blog post.

Query Parameters (Dynamic Filtering)

Full example available on GitHub: entity-filtering-selectable

Filtering the content sent over the wire dynamically based on query parameters is another commonly required use case. We need to register SelectableEntityFilteringFeature in the application and optionally define the name of the query parameter (via SelectableEntityFilteringFeature.QUERY_PARAM_NAME property) that would be used to list desired fields for further processing (marshalling).

And that’s all from the code point of view. Easy, isn’t it.

So lets rather take a look at some examples. Assume our domain model contains following classes:

The resource class is very simple as well:

As you can see no entity filtering annotations are used this time and even though the same instance of Person class is used to create response the given values of select query parameter are used to select the fields that would be transferred over the wire.

For people/1234?select=familyName,givenName the response looks like:

And for people/1234?select=familyName,givenName,addresses.phoneNumber.number the response would be:

More information about this approach is available in Jersey User Guide:

 

Hope you’ll enjoy the Entity Data Filtering feature with Jackson JSON provider and if you have any comments or suggestions, please comment in the discussion below or send me a note at michal@dejavu.sk.

  • Rui Araújo

    What if I want to select the entity filter programmatically with a single resource method? Is it possible?

  • sam

    Hi

    I am facing small problem with ‘entity filtering selectable’
    https://github.com/jersey/jersey/tree/master/examples/entity-filtering-selectable

    If i try to use Map testMap = new HashMap(); in entity say Person.java in above example
    and i populate the field in PersonResource.java as following
    person.setTestMap(new HashMap());
    person.getTestMap().put(“fist”, “last”);

    Here is response in unit test of github example
    > GET http://localhost:9998/people/1234
    > {“givenName”:”Andrew”,”familyName”:”Dowd”,”honorificSuffix”:”PhD”,”honorificPrefix”:”Mr.”,”region”:”1st Level Region”,”addresses”:[{“streetAddress”:”1234 fake st.”,”region”:”2nd Level Region”,”phoneNumber”:{“areaCode”:”540″,”number”:”867-5309″}}],”phoneNumbers”:{“HOME”:{“areaCode”:”804″,”number”:”867-5309″}},”testMap”:{}}

    ==> “testMap”:{}

    it does not return the key-value pairs of map.
    Any solution for this.

  • Scott

    Great article and feature. Can you use EntityFilteringFeature and SelectableEntityFilteringFeature at the same time. I had EntityFilteringFeaturing working great but when I registered SelectableEntityFilteringFeature my other filtering stopped working. Can they both be registered and used at the same time? This is how I registered them

    // EntityFiltering
    rc.register(EntityFilteringFeature.class);
    rc.register(new MoxyJsonConfig().setFormattedOutput(true).resolver());

    // Register entity-filtering selectable feature.
    rc.register(SelectableEntityFilteringFeature.class);
    rc.property(SelectableEntityFilteringFeature.QUERY_PARAM_NAME, “select”);

    • Patrick

      I am having teh same issue. Was anybody able to figure out how to get both features working together?