Niranjan
Niranjan

Reputation: 2921

Dynamic property filter using Jackson 2

I am building a REST service platform in which we have to support following query pattern:

  1. format=summary which means we have to deserialize only the POJO attributes annotated with our custom annotation @Summary
  2. format=detail which means we have to deserialize only the POJO attributes annotated with our custom annotation @Detail
  3. fields=prop1,prop2,prop3 which means we have to deserialize the POJO attributes provided in the query.

I am using Jackson 2 (v2.3.0) I tried followings:

Location.java

@JsonFilter("customFilter")
public class Location implements Serializable {
@Summary
@Detail
private String id;

@Summary
@Detail
private String name;

@Summary
@Detail
private Address address;

// ... getters n setters

Address.java

@JsonFilter("customFilter")
public class Address implements Serializable {

  @Detail
  private String addressLine1;

  @Detail
  private String addressLine2;

  @Detail
  private String addressLine3;

  @Detail
  @Summary
  private String city;

  @Summary
  @Detail
  private String postalCode;

  // ... getters n setters

CustomFilter.java

public class CustomFilter extends SimpleBeanPropertyFilter {
  @Override
  protected boolean include(BeanPropertyWriter propertyWriter) {
    if(logger.isDebugEnabled()) {
      logger.debug("include(BeanPropertyWriter) method called..");
    }

    return this.deserialize(propertyWriter);
  }

  @Override
  protected boolean include(PropertyWriter propertyWriter) {
    if(logger.isDebugEnabled()) {
      logger.debug("include(PropertyWriter) method called..");
    }

    return this.deserialize((BeanPropertyWriter) propertyWriter);
  }

  private boolean deserialize(final BeanPropertyWriter beanPropertyWriter) {
    final String format = (String) AppContext.get("format");

    if(StringUtils.isNotBlank(format)) {
      return deserializeForAnnotation(format, beanPropertyWriter);
    } else {
      @SuppressWarnings("unchecked")
      final Set<String> fieldNames = (Set<String>) AppContext.get("fieldNames");
      if(null != fieldNames && !fieldNames.isEmpty()) {
        final String serializedPropertyName = beanPropertyWriter.getSerializedName().getValue();
        return fieldNames.contains(serializedPropertyName);
      }
    }

    return false;
  }

  private boolean deserializeForAnnotation(final String format, final BeanPropertyWriter beanPropertyWriter) {

    if(StringUtils.equalsIgnoreCase(format, "detail")) {
      return (null != beanPropertyWriter.getAnnotation(Detail.class));
    } else if(StringUtils.equalsIgnoreCase(format, "summary")) {
      return (null != beanPropertyWriter.getAnnotation(Summary.class));
    }

    return false;
  }
}

I am getting intended result with annotations, however my 3rd requirement to support property names to filter is not working.

Could someone help; if possible with some examples?

Upvotes: 3

Views: 3451

Answers (2)

Ryan Bohn
Ryan Bohn

Reputation: 93

I wrote a library called Squiggly Filter, which selects fields based on a subset of the Facebook Graph API syntax. For example, to select the zipCode of the address field of the user object, you would use the query string ?fields=address{zipCode}. One of the advantages of Squiggly Filter is that as long as you have access to the ObjectMapper that renders the json, you do not to have to modify the code of any of your controller methods.

Assuming, you are using the servlet API, you can do the following:

1) Register a filter

<filter> 
    <filter-name>squigglyFilter</filter-name>
    <filter-class>com.github.bohnman.squiggly.web.SquigglyRequestFilter</filter-class> 
</filter> 
<filter-mapping> 
    <filter-name>squigglyFilter</filter-name>
    <url-pattern>/**</url-pattern> 
</filter-mapping>

2) Initialize the ObjectMapper

Squiggly.init(objectMapper, new RequestSquigglyContextProvider());

3) You can now filter your json

curl https://yourhost/path/to/endpoint?fields=field1,field2{nested1,nested2}

You can also select fields based on annotations as well.

More information on Squiggly Filter is available on github.

Upvotes: 4

user1596371
user1596371

Reputation:

If you want to go down the route of having a custom object mappers for each set of fields then your best bet is to retain the created object mappers in a cache somewhere so that the next time a user requests the same fields the object mapper can be reused.

Your cache could be as simple as a Set<String,ObjectMapper>, with the key being the fields as passed in by the user.

Upvotes: 0

Related Questions