Skip to content

JacksonJsonProvider ignores DeserializationFeature.FAIL_ON_UNRESOLVED_OBJECT_IDS #189

@nlenoire

Description

@nlenoire

Hello,

It seems we are facing an issue with JacksonJsonProvider that silently ignores DeserializationFeature.FAIL_ON_UNRESOLVED_OBJECT_IDS set on the ObjectMapper.

The following test case reproduces the issue:

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "name", scope = Value.class)
    private static class Value {
        public String name;
        public Integer value;
    }

    private static class Owned {
        public String name;
        public Value optionalValue;

        Optional<Value> optionalValue() {
            return Optional.ofNullable(optionalValue);
        }
    }

    private static class Owner {
        public List<Owned> owned = new ArrayList<>();
        public List<Value> values = new ArrayList<>();
    }

    private final String payload = """
            {
                "owned": [
                    { "name": "foo", "optionalValue": "vFoo" },
                    { "name": "bar", "optionalValue": "this is not a valid ref to some value" },
                    { "name": "baz" },
                    { "name": "qux", "optionalValue": { "name": "vQux", "value": 3 } }
                ],
                "values": [
                    { "name": "vFoo", "value": 1 },
                    { "name": "vBar", "value": 2 }
                ]
            }
            """;

    @Test
    void should_deserialize_illegal_reference_when_configured_leniently() throws JsonProcessingException {
        final var objectMapper = new ObjectMapper();
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNRESOLVED_OBJECT_IDS, false);
        final var owner = objectMapper.readValue(payload, Owner.class);
        assertThat(owner.owned).map(o -> o.optionalValue().map(its -> its.value).orElse(null)).containsExactly(1, null, null, 3);
    }

    @Test
    void should_reject_illegal_reference_by_default() {
        final var objectMapper = new ObjectMapper();
        assertThatExceptionOfType(JsonProcessingException.class).isThrownBy(() -> objectMapper.readValue(payload, Owner.class));
    }

    @Test
    @Disabled("jackson json provider ignores object mapper configuration so this test fails")
    void should_honor_mapper_configuration() throws IOException {
        // Stuff to use json provider (not very friendly api)
        @SuppressWarnings("unchecked")
        final Class<Object> type = (Class<Object>) (Class<?>) Owner.class;
        final var httpHeaders = new MultivaluedHashMap<String, String>();
        final var annotations = new Annotation[] {};
        final var outputStream = new ByteArrayOutputStream(4096);
        outputStream.writeBytes(payload.getBytes(StandardCharsets.UTF_8));

        // begin test
        final var objectMapper = new ObjectMapper();
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNRESOLVED_OBJECT_IDS, true);
        final var jsonProvider = new JacksonJsonProvider(objectMapper);

        // Reverse comments in the following block to make the test pass despite the issue at hand
        try (final var inputStream = new ByteArrayInputStream(outputStream.toByteArray())) {
            assertThatExceptionOfType(JsonProcessingException.class)
                .isThrownBy(() -> jsonProvider.readFrom(type, type, annotations, MediaType.APPLICATION_JSON_TYPE, httpHeaders, inputStream));
            // final var object = jsonProvider.readFrom(type, type, annotations, MediaType.APPLICATION_JSON_TYPE, httpHeaders, inputStream);
            // final var owner = (Owner) object;
            // assertThat(owner.owned).map(o -> o.optionalValue().map(its -> its.value).orElse(null)).containsExactly(1, null, null, 3);
        }
    }

Thanks for your help.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions