FasterXML / jackson-databind

General data-binding package for Jackson (2.x): works on streaming API (core) implementation(s)
Apache License 2.0
3.51k stars 1.37k forks source link

`com.fasterxml.jackson.databind.introspect.Annotated.getAnnotation()` method does not return annotation for a Record field #3974

Open mumukiller opened 1 year ago

mumukiller commented 1 year ago

Sorry if it's duplicate but i did not find anything like that

Describe the bug When using custom annotation, Annotated.getAnnotation() method does not return annotation for a field marked with this annotation.

Version information 2.15.0

To Reproduce This simple test can help:

class AnnotationIssueApplicationTests {

    @Test
    void mask() {
        var entity = new TestEntity("Secret", "Doe");

        ObjectMapper mapper = new ObjectMapper();
        mapper.setAnnotationIntrospector(
                AnnotationIntrospectorPair.pair(
                        mapper.getSerializationConfig().getAnnotationIntrospector(),
                        new MaskSensitiveDataAnnotationIntrospector()));
        try {
            assertNotNull("Should be not null", mapper.writeValueAsString(entity));
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface SensitiveField {}

    private record TestEntity(
            @SensitiveField String mySecretFieldPassword,
            String name) {}

    private static class MaskSensitiveDataAnnotationIntrospector extends NopAnnotationIntrospector {

        @Override
        public Object findSerializer(Annotated am) {
            SensitiveField annotation = am.getAnnotation(SensitiveField.class);
            //When i use jackson-core:2.14.1 the test below passes successfully
           //When i use jackson-core:2.15.0 the test fails
            if (am.getName().equals("mySecretFieldPassword") && annotation == null){
                throw new RuntimeException("This should not happen");
            };
            return null;
        }
    }
}

Expected behavior When i use jackson-core:2.14.1 the test below passes successfully When i use jackson-core:2.15.0 the test fails

Additional context This happened when i updated Spring Boot from 3.0.2 to 3.1.0 This is my graddle build

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.1.0'
    id 'io.spring.dependency-management' version '1.1.0'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

repositories {
    mavenCentral()
}

dependencies {
implementation 'com.fasterxml.jackson.core:jackson-annotations'
implementation 'com.fasterxml.jackson.core:jackson-core'
//this fixes the issue    implementation 'com.fasterxml.jackson.core:jackson-databind:2.14.1'

    implementation 'org.springframework.boot:spring-boot-starter'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
    useJUnitPlatform()
}
yihtserns commented 1 year ago

Fix/workaround

Include ElementType.METHOD in your annotation:

@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SensitiveField {}

...so that the accessor method will get the annotation directly, instead of getting it from the field during "annotation merging" process.


Caused by #3737, specifically: https://github.com/FasterXML/jackson-databind/blob/2cea7c9ce9cbdcb14e22838ea07937c376b7ab78/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java#L1012-L1013

...leading to the fields being removed by: https://github.com/FasterXML/jackson-databind/blob/2cea7c9ce9cbdcb14e22838ea07937c376b7ab78/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertyBuilder.java#L954-L955

When I created #3894 to refine/partially revert #3737, I did not also propagate the changes to _removeUnwantedAccessor because I don't understand what it is doing.

cowtowncoder commented 1 year ago

One more suggestion: while it may not make a difference, it's probably worth checking if this still occurs with 2.15.2, given there are a few changes related to Record annotation handling.