0

I have a class Order:

@Data
@Entity
public class Order {

  private List<Project> projects;

  // more fields
}

I have a two API methods in my controller:

  @GetMapping
  public ResponseEntity<List<Order>> getOrders() {
    return ResponseEntity.ok(orderService.getOrders());
  }

  @GetMapping("/{id}")
  public ResponseEntity<Order> getOrder(@PathVariable long id) {
    return ResponseEntity.ok(orderService.getOrder(id));
  }

So in this case projects is always sent via JSON, if its present its just getting serialized, if its not present its getting fetched lazily and then serialized. I could avoid it being serialized by annotating the field with @JsonIgnore. But the problem is that i want to send it sometimes and sometimes i dont. For example in getOrders() i dont want the projects to be serialized. In getOrder(...) i would want projects to be serialized. Is there any way to tell during runtime either inside custom code or by an annotation that i want to send it in one specific case and not in another case? The only thing i figured out is that - shortly before serializing - i can initialize projects with null and annotate the entity with @JsonInclude(JsonInclude.Include.NON_NULL). That way it wouldnt be sent and if i want to send it i can just avoid initializing it with null. But obviously i dont want to iterate over each Order in O(n) just to initialize its projects with null.

M.Dietz
  • 900
  • 10
  • 29
  • You could use a [Jackson JSON view](https://www.baeldung.com/jackson-json-view-annotation) to indicate when the attribute is to be deserialized – Michiel Jan 26 '20 at 20:24
  • I think Jackson's ```JsonDeserializer``` is specifically for such purposes. Have you tried that? You may check out [this StackOverflow question](https://stackoverflow.com/questions/19158345/custom-json-deserialization-with-jackson). – Sree Kumar Jan 26 '20 at 20:31
  • Create a view model object and parse to it – Sully Jan 26 '20 at 21:00

1 Answers1

2

This is easy to achieve using "JSON Views".

First, define some classes to represent each view (e.g. internal/external):

public class OrderViews {
    public static class OnlySomeFields {}
    public static class AllFields extends OnlySomeFields {}
}

Next, on your class, assign a view to each field:

public class Order {

    @JsonView(OrderViews.OnlySomeFields.class)
    private String foo;

    @JsonView(OrderViews.AllFields.class)
    private String bar;

    // getters/setters/etc
}

Then, in your controller, you can specify which view to use for each method:

@RestController
public class MyController {

    @JsonView(OrderViews.AllFields.class)
    @GetMapping("/with-all-fields")
    public Order getOrderAllFields() {
        return orderService.getOrder();
    }

    @JsonView(OrderViews.OnlySomeFields.class)
    @GetMapping("/with-some-fields")
    public Order getOrderAllFields() {
        return orderService.getOrder();
    }

}

With this setup, navigating to /with-all-fields returns a JSON containing foo and bar, while navigating to /with-some-fields returns a JSON only containing foo.

You can use this technique to selectively serialize specific fields, and should be able to apply it to your use case.

cameron1024
  • 9,083
  • 2
  • 16
  • 36
  • Lets say `foo` has a child `moo`. Is it possible to specify two `order` views, one which has `foo` as a child and `foo` has `moo` as a child. And another one wihch has `foo` as a child but `foo` doesnt have `moo` as a child here? So i want to define two `order` views with the same children, but different children of children. – M.Dietz Jan 26 '20 at 22:51
  • Ok, i just figured it out. I can just annotate the child of the child field with a view class from the orders views too. – M.Dietz Jan 26 '20 at 23:02
  • Yes, you would use the same technique but on your `Project` class. It is a Jackson feature, rather than a Spring feature as such, and works recursively using Jackson's standard serialization interface. – cameron1024 Jan 27 '20 at 01:51