jon_wu
jon_wu

Reputation: 1152

How to filter properties on a self reference using Jackson?

Let's say I have a class like this that I want to serialize with Jackson.

public class Product {
    int id;
    String name;
    List<Product> similarProducts;
}

How do I end up with something like this?

{
    "id": 1,
    "name": "Product 1",
    "similarProducts": [
        {
            "id": 2,
            "name": "Product 2"
        },
        {
            "id": 3,
            "name": "Product 3"
        }
    ]
}

I've see how to use @JsonProperty to do this by class or @JsonView to select things too but that also seems like it's by class (am I mistaken)? I'm not sure how to get this to work when I have a class that's referencing itself and the parent has many properties, while the child only has a few.

Imagine this is an ecommerce site and you want to get the name and URL of similar products from a single JSON payload, but you don't need any other details of those similar products (children).

Upvotes: 2

Views: 1298

Answers (2)

matt.early
matt.early

Reputation: 225

You can also accomplish this with a custom Serializer if you ALWAYS want this behavior (seems in this case it could be dangerous to not include the filter since errors will result in the self reference case)

Model:

public class Product {
    public int id;
    public String name;

    @JsonSerialize(contentUsing=ProductSerializer.class)
    public List<Product> similarProducts = Lists.newArrayList();
}

Serializer:

public class ProductSerializer extends JsonSerializer<Product> {

    @Override
    public void serialize(Product product, JsonGenerator jgen, SerializerProvider provider)
            throws IOException, JsonProcessingException {
        jgen.writeStartObject();
        jgen.writeNumberField("id", product.id);
        jgen.writeStringField("name", product.name);
        jgen.writeEndObject();
    }
}

Example:

Product p1 = new Product();
Product p2 = new Product();
p1.similarProducts = Lists.newArrayList(p2);
p2.similarProducts = Lists.newArrayList(p1);
p1.id = 1;
p2.id = 2;
p1.name = "p1";
p2.name = "p2";
ObjectMapper mapper = new ObjectMapper();

System.out.println(mapper.writeValueAsString(p1));

Output:

{"id":1,"name":"p1","similarProducts":[{"id":2,"name":"p2"}]}

The drawback with this approach is that you would need to add new properties to the serializer as they are added.

Upvotes: 0

Ernest Sadykov
Ernest Sadykov

Reputation: 831

You can serialize using @JsonFilter annotation.

New version of your model:

public class Product {
    int id;
    String name;

    @JsonFilter("productView")
    List<Product> similarProducts;

    // getters and setters
}

Serialization process:

 FilterProvider filters = new SimpleFilterProvider()
            .addFilter(
                "productView", 
                SimpleBeanPropertyFilter.serializeAllExcept("similarProducts")
            );
 String res = new ObjectMapper()
            .writer(filters)
            .writeValueAsString(product);
 System.out.println(res);

Example

Code:

Product child1 = new Product();
child1.id = 2;
child1.name = "sfd";
Product grandchild1 = new Product();
grandchild1.id = 4;
grandchild1.name = "werrwe";
child1.similarProducts = Collections.singletonList(grandchild1);
Product child2 = new Product();
child2.id = 2;
child2.name = "sfd";

Product product = new Product();
product.id = 1;
product.name = "product";

product.similarProducts = Arrays.asList(child1, child2);

FilterProvider filters = new SimpleFilterProvider().addFilter("productView",
            SimpleBeanPropertyFilter.serializeAllExcept("similarProducts"));

String res = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT)
            .writer(filters)
            .writeValueAsString(product);

System.out.println(res);

Output:

{
  "id" : 1,
  "name" : "product",
  "similarProducts" : [ {
    "id" : 2,
    "name" : "sfd"
   }, {
    "id" : 2,
    "name" : "sfd"
   } ]
}

See also

Blog post about filtering properties in jackson: Every day Jackson usage, part 3: Filtering properties

Upvotes: 2

Related Questions