Skip to content

NPE deserializing Recursive Structure When Ignoring Properties (same as #1755) #4417

Closed
@hankolerd

Description

@hankolerd

Search before asking

  • I searched in the issues and found nothing similar.

Describe the bug

Disclaimer; This may be duplicate of #1755 - But thought I should raise separately in case root-cause is different, as that is raised against 2.9.

Starting with Jackson-Databind 2.12.0, attempting to deserialize a recursive data structure with a custom JacksonAnnotationIntrospector configured to ignore properties, for example:

new JacksonAnnotationIntrospector() {
   @Override
   public Value findPropertyIgnoralByName(MapperConfig<?> config, Annotated a) {
      return JsonIgnoreProperties.Value.forIgnoredProperties("CanBeAnyValue");
   }
}

A NullPointerException will be thrown during deserialization:

Caused by: java.lang.NullPointerException: Cannot invoke "com.fasterxml.jackson.databind.JsonDeserializer.getObjectIdReader()" because "valueDes" is null
	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer._deserializeFromArray(CollectionDeserializer.java:333)
	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:244)
	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:28)
	at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:324)

Changing the Value object to allow Setters will avoid the NullPointerException from occurring:

- return JsonIgnoreProperties.Value.forIgnoredProperties("CanBeAnyValue");
+ return JsonIgnoreProperties.Value.forIgnoredProperties("CanBeAnyValue").withAllowSetters();

Version Information

Affects starting from 2.12.0, confirmed issue still present through 2.17.0-rc1.

Reproduction

Here is a sample project to reproduce the issue
reproduce-project.zip

pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>sample</groupId>
  <artifactId>test</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <dependencies>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.12.0</version>
    </dependency>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-api</artifactId>
      <version>5.10.2</version>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <configuration>
        <source>17</source>
        <target>17</target>
      </configuration>    
      </plugin>
    </plugins>
  </build>
</project>
src/test/java/test/ReproduceTest.java
package sample;

import java.util.List;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties.Value;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;

class ReproduceTest {

    /**
     * A simple recursive data structure to reproduce the issue
     */
    public static class Item {
        List<Item> items;
        public List<Item> getItems() {
            return items;
        }        
        public void setItems(List<Item> items) {
            this.items = items;
        }
    }
    
    // Sample JSON has one recursion of the items list
    String testJson = """
        {"items": [{"items": []}]}
        """;

    /**
     * This test will fail with error:
     * 
     * <pre>
        com.fasterxml.jackson.databind.JsonMappingException: 
          Cannot invoke "com.fasterxml.jackson.databind.JsonDeserializer.getObjectIdReader()" because "valueDes" is null (through reference chain: test.ReproduceTest$Item["items"]->java.util.ArrayList[0]->test.ReproduceTest$Item["items"])
     * </pre>
     */
    @Test
    void testReproduceJackonMapperNPE() throws Exception {
        runTest(new JacksonAnnotationIntrospector() {
            @Override
            public Value findPropertyIgnoralByName(MapperConfig<?> config, Annotated a) {
               return JsonIgnoreProperties.Value.forIgnoredProperties("CanBeAnyValue");
            }
        });
    }

    /**
     * This test will pass without exception when using withAllowSetters()
     */
    @Test
    void testAllowingSettersAvoidsNPE() throws Exception {
        runTest(new JacksonAnnotationIntrospector() {
            @Override
            public Value findPropertyIgnoralByName(MapperConfig<?> config, Annotated a) {
                return JsonIgnoreProperties.Value.forIgnoredProperties("CanBeAnyValue").withAllowSetters();
            }
        });
    }
    
    private void runTest(JacksonAnnotationIntrospector introspector) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        mapper.setAnnotationIntrospector(introspector);
        Item result = mapper.readValue(testJson, Item.class);
        Assertions.assertEquals(1, result.getItems().size(), 1);
    }
}

Build with Java 17 and mvn clean test

Expected behavior

I thought object should deserialize without hitting NPE without needing to explicitly configure withAllowSetters().

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    duplicateDuplicate of an existing (usually earlier) issuehas-failing-testIndicates that there exists a test case (under `failing/`) to reproduce the issue

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions