4

I have a JPA entity implementing an interface and I want to expose via jax-rs endpoint only the fields that are defined by that interface. Something that looks like:

public interface ConnectedAppExternal {
 
 String getId();

 String getName();

}
@Entity(name="connected_application")
public class ConnectedApp implements ConnectedAppExternal {

  @Id
  @Column(name="id")
  private id;

  @Column(name="name")
  private String name;

  @Column(name="internal_status")
  private String internalStatus;

...

 @Override
 public String getId(){
   return this.id;
 }

 @Override
 public String getName(){
   return this.name;
 }

 public String getInternalStatus(){
   return this.internalStatus;
 }

 public void setId(String id){
   this.id = id;
 }

 public void setName(String name){
   this.name = name;
 }

 public void setInternalStatus(String internalStatus){
   this.internalStatus= internalStatus;
 }

 ...
  
}

@Path("/applications")
public class ConnectedAppResource {

  @Inject
  ConnectedAppService appService;

...

  @GET("applications/{id}")
  @Produces("application/json")
  public ConnectedAppExternal getConnectedApplication(@PathParam("id") String id){
    return appService.getAppById(id);
  }

...

}

Even if I make my jax-rs resource @GET method return a ConnectedAppExternal response, Moxy will serialize the whole JPA entity with all its properties, so I end up either having to add @JsonbIgnore to every new internal entity field I do not want to expose; or defining a DTO containing the exposed interface fields only, which means a lot of mapping and alignment overhead (more code => more bugs / accidental leaks).

(I am pretty sure there is not, since I have seen the serializer code, but just asking for an alternative that works like this anyway;)

...or maybe at least avoid requiring an explicit @JsonbIgnore/@JsonbTransient exclusion of non-exposed fields due to Moxy serializing every field with a getter/setter by default, and instead require an explicit @JsonbProperty inclusion for a field to be JSON serialized / exposed?

NotGaeL
  • 8,344
  • 5
  • 40
  • 70

1 Answers1

2

As an approach you may declare JPA projection queries with required data and return Map<String, Object> type from your resource. Somthing like this:

@NamedQueries({
    @NamedQuery(name="ConnectedApp.findByName",
                query="SELECT c.id, c.internalStatus FROM ConnectedApp c WHERE c.name = :name")
}) 

See: https://dzone.com/articles/projection-queries-a-way-to-optimize-data-traffic or https://www.objectdb.com/java/jpa/query/named

Another approach would be to serialize only required properties with Jsonb, Somewhat like this:

@GET
@Path("applications/{id}")
@Produces(MediaType.APPLICATION_JSON)
public String getApp(@PathParam("id") String id) {
    JsonbConfig config = new JsonbConfig().withPropertyVisibilityStrategy(
        new PropertyVisibilityStrategy(){

                    @Override
                    public boolean isVisible(Field field) {
                        return List.of("id", "name").indexOf(field.getName()) != -1;
                    }

                    @Override
                    public boolean isVisible(Method method) {
                        return false;
                    }
                    
                });
    Jsonb jsonb = JsonbBuilder.newBuilder().withConfig(config).build();
    return jsonb.toJson(appService.getAppById(id));
}

please find example here: https://adambien.blog/roller/abien/entry/private_fields_serialization_with_json

S. Kadakov
  • 861
  • 1
  • 6
  • 15
  • I don't care as much about querying just the required properties. These are light records so there is no overhead on getting them whole. I do care about having to write specific DTO code or validation code. Your solution covers the former but not the later. And I still have to write extra code for the queries. Is there really no way to have the serializer ignore by default? – NotGaeL Oct 05 '21 at 10:29
  • Annotate your Entity bean with `@JsonbTransient / @XmlTransient` IS the way: you may mark properties not to be serialized declaratively. If you are looking for the way for custom serialization you may use javax.Json DTO. Something like that: https://adambien.blog/roller/abien/entry/serializing_a_collection_of_java or https://adambien.blog/roller/abien/entry/private_fields_serialization_with_json. Or even though you may return plain Map with jersey jackson feature: https://quarkus.io/guides/rest-json – S. Kadakov Oct 06 '21 at 07:13
  • That is what I am doing now, and it is not ignoring by default: Ignoring by default is about explicitly including properties, otherwise they get excluded; It is not about explicitly excluding them, otherwise they get included. That is unsafe and I want to avoid that. Without resorting to a bunch of extra DTO code that I'd also need to maintain and keep aligned with the sources they map. – NotGaeL Oct 06 '21 at 07:21
  • P.S: I know Jackson has some features that could be useful. E.g. I have used mixins and custom serializers before to expose classes I could not put jax-rs annotations on: It works just as well as DTOs. Even though, again, when applied to this question, that solution is a bunch of extra code I need to keep aligned and maintain. Anyway: This project uses eclipselink Moxy and I cannot (and don't want to, for a bunch of reasons) deviate from that. – NotGaeL Oct 06 '21 at 07:27
  • You may be onto something there though: The same custom `javax.json.bind.config.PropertyVisibilityStrategy` on `JsonbConfig` mechanism proposed by Adam Bien to have `JsonbBuilder` expose all private fields, could be used to expose nothing by default, by setting field visibility to true only when the field is explicitly annotated with `@JsonbProperty`. I just need to figure out how to have my own `PropertyVisibilityStrategy` implementation detect these `@JsonbProperty` annotations to set visibility to true on those explicitly exposed fields. – NotGaeL Oct 06 '21 at 09:43
  • So, if you edit your answer or write a new one proposing a `PropertyVisibilityStrategy` solution and pointing to that Adam Bien's blog post example, it'd be enough for me to accept it ^^ – NotGaeL Oct 06 '21 at 09:43
  • Nice! I'll use a generic version of that by looking for the field or method's presence/absence of `@JsonbProperty` to determine its visibility on a custom `PropertyVisibilityStrategy` implementation I can put that on a default `JsonbConfig` I can use on the default `JsonbBuilder` (Instead of having to define the builder on every response as you show in your example). This is exactly the kind of generic-config-based, jax-rs spec compliant solution I was looking for. – NotGaeL Oct 06 '21 at 13:08
  • good luck & happy coding! – S. Kadakov Oct 07 '21 at 14:18