Skip to content

Offer a way to access enclosing AnnotatedClass from AnnotatedMember #4141

Open
@SimonCockx

Description

@SimonCockx

Is your feature request related to a problem? Please describe.

The AnnotatedMember interface does not expose a method to access its enclosing AnnotatedClass.

Example use case: a custom annotation introspector

@Pojo(prefix="Prefix_")
public class Foo {
    @Field("Name")
    public int getField() {
        return 42;
    }
}

Suppose I would like to implement the AnnotationIntrospector#findNameForSerialization method to return "Prefix_Name" for the annotated method getField.

Solution 1: access annotation directly on the declaring Class<?> object

    @Override
    public PropertyName findNameForSerialization(Annotated a) {
        if (a.hasAnnotation(Field.class)) {
            String name = a.getAnnotation(Field.class).value();
            String prefix = "";
            // Try to access the prefix from the enclosing class.
            if (a instanceof AnnotatedMember) {
                AnnotatedMember m = (AnnotatedMember) a;
                Class<?> declaringClass = m.getDeclaringClass();
                if (declaringClass.isAnnotationPresent(Pojo.class)) {
                    prefix = declaringClass.getAnnotation(Pojo.class).prefix();
                }
            }
            return new PropertyName(prefix + name);
        }
        return null;
    }

Notice that, whereas I can access annotations of the Annotated input using the conventional Jackson API, I have to go through the basic Java Class API to access the annotation of the enclosing class.

This works for this particular use case. However, it does not work when inheritance is involved, e.g., for the following pojo:

@Pojo(prefix="Prefix_")
public interface Foo {
    int getField();
    
    public class FooImpl implements Foo {
        @Field("Name")
        @Override
        public int getField() {
            return 42;
        }
    }
}

It would be better if I would be able to somehow access the declaring AnnotatedClass object instead. This is what I try to do in the following solution.

Solution 2: access annotation on the declaring AnnotatedClass object.

    @Override
    public PropertyName findNameForSerialization(Annotated a) {
        if (a.hasAnnotation(Field.class)) {
            String name = a.getAnnotation(Field.class).value();
            String prefix = "";
            // Try to access the prefix from the enclosing class.
            if (a instanceof AnnotatedMember) {
                AnnotatedMember m = (AnnotatedMember) a;
                AnnotatedClass declaringClass = (AnnotatedClass) m.getTypeContext(); // Note: `getTypeContext` is deprecated.
                if (declaringClass.hasAnnotation(Pojo.class)) {
                    prefix = declaringClass.getAnnotation(Pojo.class).prefix();
                }
            }
            return new PropertyName(prefix + name);
        }
        return null;
    }

While this seems to work perfectly (even for the inheritance use case), I have to use a deprecated method getTypeContext, and I have to cast that result into an AnnotatedClass. I don't see a way around this though. For now, I am using this as a workaround, but ideally it would be possible to do this in a non-deprecated direct way.

Describe the solution you'd like

AnnotatedMember should expose a method called getAnnotatedDeclaringClass() that returns the enclosing AnnotatedClass.

Usage example

Taking my use case from above, it could be implemented as follows:

    @Override
    public PropertyName findNameForSerialization(Annotated a) {
        if (a.hasAnnotation(Field.class)) {
            String name = a.getAnnotation(Field.class).value();
            String prefix = "";
            // Try to access the prefix from the enclosing class.
            if (a instanceof AnnotatedMember) {
                AnnotatedMember m = (AnnotatedMember) a;
                AnnotatedClass declaringClass = m.getAnnotatedDeclaringClass(); // Example usage
                if (declaringClass.hasAnnotation(Pojo.class)) {
                    prefix = declaringClass.getAnnotation(Pojo.class).prefix();
                }
            }
            return new PropertyName(prefix + name);
        }
        return null;
    }

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.xIssues to be only tackled for Jackson 3.x, not 2.x

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions