-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Offer a way to access enclosing AnnotatedClass
from AnnotatedMember
#4141
Comments
|
Quick note: my first thinking is -- no, I don't think adding back-link is a good idea. Further, defaulting should NOT be implemented by methods of I will need to think about a bit more as I do see potential benefits, but as @JooHyukKim pointed out, classes are quite tightly coupled and adding linkage may be problematic in other ways. EDIT: thought I should clarify part of "defaulting should not be implemented" -- this refers to the idea that |
@JooHyukKim, @cowtowncoder Thanks for the quick reply. Details below.
Actual use case: There is a modelling language called Rosetta, which generates Java code of the form
based on a
We have a use case where clients need to be able to customize the serialised attribute names. Our current approach is to use a configuration file, e.g.,
I would like to define a mapper that uses this configuration in such a way that
My current approach was to create an annotation processor that does the following:
It's step 1 that I'm having trouble with. (finding the
I'm looking for a general purpose solution that will work for any generated Java class based on a Rosetta model, so this is not really possible.
Since the Java code generator for Rosetta is currently independent of any serialisation framework, I don't see this happening soon.
Could you elaborate this please? I'm not sure I get what you mean.
Keen to hear your ideas. :) If you can think of another approach that allows me to do something similar, I'm all ears of course. |
Why is your custom code not traversing through the class/interface hierarchy to look for the annotation? E.g.: @Override
public PropertyName findNameForSerialization(Annotated a) {
if (a.hasAnnotation(Field.class)) {
String name = a.getAnnotation(Field.class).value();
String prefix = "";
if (a instanceof AnnotatedMember) {
AnnotatedMember m = (AnnotatedMember) a;
Class<?> declaringClass = m.getDeclaringClass();
// TODO: Cache for performance
List<? extends Class<?>> classes = Stream.iterate(
declaringClass,
clazz -> clazz.getSuperclass() != null,
clazz -> (Class) clazz.getSuperclass())
.toList();
// Try find in class & superclasses
Pojo pojoAnnotation = classes.stream()
.map(clazz -> clazz.getAnnotation(Pojo.class))
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
if (pojoAnnotation == null) {
// Try find in any interface
pojoAnnotation = classes.stream()
.flatMap(clazz -> Stream.of(clazz.getInterfaces()))
.map(interfaceClass -> interfaceClass.getAnnotation(Pojo.class))
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
}
if (pojoAnnotation != null) {
prefix = pojoAnnotation.prefix();
}
}
return new PropertyName(prefix + name);
}
return null;
} |
@yihtserns If using |
In the case where the same annotation is on multiple interfaces, which one wins? |
@yihtserns Probably somewhat arbitrary, I'd have to check the logic. In general. "closer" one (as per class inheritance hierarchy). For classes it follows expected hierarchy. |
My thought is that if the user has special needs/purpose, it would be better/safer for them to have control over the logic/rule for whatever they are doing, rather than relying on a 3rd party lib's behaviour. 🤔 |
I'm fine with reusing the same inheritance mechanism that Jackson uses under the hood. I think diverging from that behaviour would only cause confusion, where users expect the same logic to apply as regular Jackson annotations. That being said, I of course agree with @yihtserns that a manual implementation would also work. The point of this feature request is however convenience: I don't want to bother implementing some highly optimised code to traverse classes and interfaces if Jackson already implements it. :) |
The problem with relying on that kind of undocumented (thus unguaranteed) behaviour is, users of your code may be in trouble if Jackson decides to change the behaviour/rule. 🤷 Anyway, @cowtowncoder why was |
Try checking out the commit |
I think you could say this about users of Jackson as well. If anybody relies on Jackson annotations combined with inheritance (which I would guess is common practice - correct me if I'm wrong), their code might be in trouble if Jackson decides to change the behaviour. If this is a concern, then IMO this is more of a Jackson docs issue, and the solution would be to add documentation about it. Anyway, my main point is that this is a feature request for convenience. If the consensus is that Jackson's inheritance mechanism is for internal use only, then so be it; otherwise I would like to reuse it. :) |
I'm just sharing a word of caution since you're writing an API/lib for other people to use by relying on a behaviour that not under your control. But yeah it is not a big deal even if the behaviour changed and break your end users' usage, you can always rewrite the API/lib code anyway. Just to be clear, I'm not objecting to this feature request, just sharing some things you may want to consider about the potential complication when it comes to annotation resolution in class hierarchy (as you can see in the two loops in my sample code - if multiple superclasses/interfaces annotated, who should win: should you decide, or should Jackson decide, or should it be random, or it is not a concern in your use case, etc) esp when dealing with interfaces (the reason why |
Ok, lots to unpack here. I will try to explain my thinking wrt annotation inheritance firsts.
And here's the precedence as I remember it. Let's consider type hierarchy like:
Mix-ins are "mixed in" at level right above target class. So, we get:
|
And then on why But as I recall it, access to the entity is something that:
And in particular here, it would not necessarily have meant "class in which member is declared" -- it depends on type declaration. For example, for local type variable binding like:
context for type for Field So it is and was not intended to be containing entity for |
@cowtowncoder Thank you for the extensive context. What is your advice? Add an additional method to |
@SimonCockx I don't know, to be honest. I will not be adding an accessor in Jackson 2.16, for sure (partly due to timeline). |
@cowtowncoder Thanks. That sounds perfectly reasonable. |
I don't get it. So the answer is stick to using the deprecated |
No, please do not use |
From all the comments above, have you resolved your issue, @SimonCockx? Asking because this issue seems open ended 🤔 |
@JooHyukKim Just to be clear: this is a feature request - not an issue. I started off with working code. As described in this thread, there exist two alternative implementations for supporting annotation inheritence:
My conclusion from this thread - correct me if anyone disagrees - is that an accessor for the enclosing
|
I see, thank you for pointing out and also explanation 👍🏼👍🏼 I meant "issue" as more generic term "Github Issue", one that has a UI tab in Github. And I missed what you shared. |
AnnotatedClass
from AnnotatedMember
AnnotatedClass
from AnnotatedMember
Just to make sure my comment was understood correctly: I did not say it is planned to be added, but rather that it is a possibility. That is, I am not ruling it out. |
Is your feature request related to a problem? Please describe.
The
AnnotatedMember
interface does not expose a method to access its enclosingAnnotatedClass
.Example use case: a custom annotation introspector
Suppose I would like to implement the
AnnotationIntrospector#findNameForSerialization
method to return"Prefix_Name"
for the annotated methodgetField
.Solution 1: access annotation directly on the declaring
Class<?>
objectNotice that, whereas I can access annotations of the
Annotated
input using the conventional Jackson API, I have to go through the basic JavaClass
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:
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.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 anAnnotatedClass
. 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 calledgetAnnotatedDeclaringClass()
that returns the enclosingAnnotatedClass
.Usage example
Taking my use case from above, it could be implemented as follows:
The text was updated successfully, but these errors were encountered: