42

I need to exclude some fields by names before rendering. The list of fields is dynamic, so I can't use annotations.

I've tried to create custom serializer but I can't get field name there.

In GSON I've used ExclusionStrategy, but Jackson has no such functionality. Is there an equivalent?

kryger
  • 12,906
  • 8
  • 44
  • 65
Roman
  • 855
  • 1
  • 8
  • 15
  • 1
    are you trying to exclude properties at runtime? For example you have a map, and want to exclude some properties inside it? – eugen Dec 07 '12 at 15:07
  • 1
    yes, but it should be dot notation exclusion like `order.id` `order.content.items` – Roman Dec 10 '12 at 08:59

5 Answers5

43

The below example of excluding fields by name is from my blog post, Gson v Jackson - Part 4. (Search for the PropertyFilterMixIn.) This example demonstrates using a FilterProvider with a SimpleBeanPropertyFilter to serializeAllExcept a user-specified list of field names.

@JsonFilter("filter properties by name")  
class PropertyFilterMixIn {}  

class Bar  
{  
  public String id = "42";  
  public String name = "Fred";  
  public String color = "blue";  
  public Foo foo = new Foo();  
}  

class Foo  
{  
  public String id = "99";  
  public String size = "big";  
  public String height = "tall";  
}  

public class JacksonFoo  
{  
  public static void main(String[] args) throws Exception  
  {  
    ObjectMapper mapper = new ObjectMapper();  
    mapper.getSerializationConfig().addMixInAnnotations(  
        Object.class, PropertyFilterMixIn.class);  

    String[] ignorableFieldNames = { "id", "color" };  
    FilterProvider filters = new SimpleFilterProvider()  
      .addFilter("filter properties by name",   
          SimpleBeanPropertyFilter.serializeAllExcept(  
              ignorableFieldNames));  
    ObjectWriter writer = mapper.writer(filters);  

    System.out.println(writer.writeValueAsString(new Bar()));  
    // output:  
    // {"name":"James","foo":{"size":"big","height":"tall"}}  
  }  
} 

(Note: The relevant API may have changed slightly with a recent Jackson release.)

While the example does use a seemingly unnecessary annotation, the annotation is not applied to the fields to be excluded. (To help get the API changed to simplify the necessary configuration a bit, please don't hesitate to vote for implementation of issue JACKSON-274.

Programmer Bruce
  • 64,977
  • 7
  • 99
  • 97
  • 1
    Yes, this works fine. Thank you. But I still can't make complicated exclusions like `something.items` (excluse items with root of something) – Roman Dec 10 '12 at 09:01
  • 4
    I searching for same feature you need, the given solution if not enough neither for my case, since using what suggested will exclude fields with same name also in the nested objects. And I want to exclude them only at top level. So it is needed a way to specify at what level the property has to be ignored. – carlo.polisini Aug 21 '13 at 10:31
  • 1
    Well is the solution found now ? – StackHola Jan 17 '14 at 14:37
  • 3
    Is there any way to store filtered items somewhere in props to use it with Spring MVC, or maybe how to access serialization flow in Spring MVC? –  Nov 03 '14 at 17:42
  • Thanx Man! Works like a charm. – dimson Apr 05 '18 at 10:42
  • looks like addMixInAnnotations() is depricated, any chance we can get a working update to this? – Derek Hannah Oct 17 '18 at 14:43
  • @Roman try placing the JsonFilter annotation on the class that contains the fields your trying to ignore – kmek Apr 05 '19 at 06:32
  • 4
    In jackson 2.5.x replace `mapper.getSerializationConfig().addMixInAnnotations( Object.class, PropertyFilterMixIn.class);` with `mapper.addMixIn(Object.class, PropertyFilterMixIn.class);` – raisercostin Oct 26 '19 at 19:22
14

I wrote a library to deal with a similar use case. I needed to programmatically ignore fields based on the user requesting data. The normal Jackson options were just too heavy-handed, and I hated the way it made my code look.

The library makes this a whole lot easier to understand. It allows you to simply do this:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.monitorjbl.json.JsonView;
import com.monitorjbl.json.JsonViewSerializer;
import static com.monitorjbl.json.Match.match;

//initialize jackson
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(JsonView.class, new JsonViewSerializer());
mapper.registerModule(module);

 //get a list of the objects
List<MyObject> list = myObjectService.list();

String json;
if(user.getRole().equals('ADMIN')){
    json = mapper.writeValueAsString(list);
} else {
    json = mapper.writeValueAsString(JsonView.with(list)
        .onClass(MyObject.class, match()
           .exclude("*")
           .include("name")));
}

System.out.println(json);

The code is available on GitHub, hope it helps!

monitorjbl
  • 4,280
  • 3
  • 36
  • 45
11

If you have filters defined on two or more pojo, you can try this:

@JsonFilter("filterAClass") 
class AClass  
{       
  public String id = "42";  
  public String name = "Fred";  
  public String color = "blue";
  public int sal = 56;
  public BClass bclass = new BClass();  
}  

//@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
@JsonFilter("filterBClass") 
class BClass  
{  

  public String id = "99";  
  public String size = "90";  
  public String height = "tall";  
  public String nulcheck =null;  
}  
public class MultipleFilterConcept {
    public static void main(String[] args) throws Exception  
      {   
        ObjectMapper mapper = new ObjectMapper();
     // Exclude Null Fields
        mapper.setSerializationInclusion(Inclusion.NON_NULL);
        String[] ignorableFieldNames = { "id", "color" };  
        String[] ignorableFieldNames1 = { "height","size" };  
        FilterProvider filters = new SimpleFilterProvider()  
          .addFilter("filterAClass",SimpleBeanPropertyFilter.serializeAllExcept(ignorableFieldNames))
          .addFilter("filterBClass", SimpleBeanPropertyFilter.serializeAllExcept(ignorableFieldNames1));  
        ObjectWriter writer = mapper.writer(filters);
       System.out.println(writer.writeValueAsString(new AClass())); 

      }
}
riccardo.cardin
  • 7,971
  • 5
  • 57
  • 106
6

Jackson relies on annotations for most things like this; but you do not have to directly annotate value classes. You can also use "mix-in annotations" (see http://www.cowtowncoder.com/blog/archives/2009/08/entry_305.html).

And then there are a few options you can use beyond basic @JsonIgnore (per-property) or @JsonIgnoreProperties (per-class), see http://www.cowtowncoder.com/blog/archives/2011/02/entry_443.html

StaxMan
  • 113,358
  • 34
  • 211
  • 239
  • 1
    I'm talking about runtime exclusion. I've got request param what fields to exclude and have to exclude it from response – Roman Dec 11 '12 at 12:45
  • 1
    Then your best bet is to build it yourself using tree model; or implement "JSON Filter" (from second article). Fortunately @{Programmer Bruce} showed how to dlatter. – StaxMan Dec 12 '12 at 06:18
  • @JsonIgnore can also be applied to a getter method. – Max Peng Feb 28 '17 at 08:55
4

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}

More information on Squiggly Filter is available on github.

Ryan Bohn
  • 93
  • 5