katharsis-project / katharsis-framework

Katharsis adds powerful layer for RESTful endpoints providing implementenation of JSON:API standard
http://katharsis.io
Apache License 2.0
135 stars 65 forks source link

@JsonIgnore not working properly in hierarchies #445

Open csetera opened 7 years ago

csetera commented 7 years ago

We are attempting to update our Katharsis dependency from 2.6.0 to 3.0.2. Unfortunately, it appears that serialization behavior has been changed/broken somewhere in between those versions. This is causing marshalling of our objects via Katharsis to be busted.

We have two use cases which both appear to be broken. Imagine if you will a few definitions:

public interface MyInterface {
  @JsonIgnore
  List<Something> getSomethings();
}
public abstract class MyAbstractClass {
  @JsonIgnore
  public SomethingElse getSomethingElse() {
    return null;
  }
}

and finally:

@JsonApiResource(type="things")
public class Thing extends MyAbstractClass implements MyInterface {
  public List<Something> getSomethings() {
    return new ArrayList<>();
  }

  public SomethingElse getSomethingElse() {
    return new SomethingElse();
  }

  public int getValue() {
    return 10;
  }  
}

In version 2.6.0, marshalling of instances of Thing would marshal out only "value" as an attribute. "somethings" and "somethingElse" would not be marshalled. With 3.0.2, this behavior has changed on the inherited @JsonIgnore annotations causing them to be marshalled. This means that all of our implementation objects are leaking out during serialization.

It appears that this problem originates at least in part from io.katharsis.core.internal.resource.AnnotationResourceInformationBuilder.getResourceFields(Class<?>). This method calls io.katharsis.core.internal.utils.ClassUtils.getClassGetters(Class<?>). This method appears to have two shortcomings relative to previous behavior:

I'm not entirely sure how to fix this in the current code base. The reality is that the method at the leaf level is the one that should be invoked. However, the annotations need to be gathered all the way up the inheritance tree. It appears that io.katharsis.core.internal.resource.AnnotationResourceInformationBuilder.AnnotatedResourceField is capable of representing the annotations separately, but it needs to be built up by including inherited members somehow.

I will continue to look at this as time permits, but I wanted to get the issue written up in hope that someone on the core team might have insight into the problem and any potential solutions.

csetera commented 7 years ago

Another (semi-related) issue we are also seeing relative to serialization:

public abstract BaseObject {
  @Transient 
  protected Set<UserOperation> operations;

  @JsonGetter("user-operations")
  @JsonInclude(Include.NON_NULL)
  public Set<UserOperation> getIUserOperations() {
    return (operations == null) ? null : Collections.unmodifiableSet(operations);
  }
  public void setUserOperations(Set<UserOperation> operations) {
    this.operations = operations;
  }
}
@JsonApiResource("more-things")
public MoreThing extends BaseObject {
}

The @JsonGetter is being used to make sure that incoming information on the API is always ignored. In this case, we are seeing both of the annotations ignored:

In this case, I'm not entirely sure what is going on. I don't see any reference to this annotations in the Katharsis code leading me to believe that they are not being handled at all. In this case not being handled appears to be completely ignored.