OpenAPITools / jackson-databind-nullable

JsonNullable wrapper class and Jackson module to support meaningful null values
Apache License 2.0
99 stars 31 forks source link

Does it work with current SpringBootStarter 3.1.x ? #54

Closed StefanSchubert closed 1 year ago

StefanSchubert commented 1 year ago

Hi there,

I'm using the module (version 0.2.6) in combination with the openapi-generator plugin.

However I seem to have problems to map json responses from the called system (via restTemplate), which does not provide the JsonNullable json representation but the plain attributes and crashes when trying to map a String to JsonNullable on the generated DTO.

My jackson mapper doesn't seem to be convinced to do such a mapping, this is the config I'm using

@Configuration
public class JacksonMapperConfiguration {

    @Bean
    public RestTemplate createRestTemplate() {
        RestTemplate restTemplate = new RestTemplate();
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        converter.setObjectMapper(jackson2ObjectMapperBuilder().build());
        restTemplate.getMessageConverters().add(0, converter);
        return restTemplate;
    }

   @Bean
    public JsonNullableModule jsonNullableModule() {
        return new JsonNullableModule();
    }

    @Bean
    public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ISO_INSTANT;
        Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
        SimpleModule module = new SimpleModule();
        module.addDeserializer(JsonNullable.class, new JsonNullableDeserializer(JsonNullable.class));

        builder
            .modules(module, jsonNullableModule(), new JavaTimeModule())
            .serializationInclusion(JsonInclude.Include.NON_NULL)
            .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
            .serializerByType(OffsetDateTime.class,
                    new CustomOffsetDateTimeSerializer(dateTimeFormatter));

        return builder;

    }

and this is the JsonNullabeDeserializer:

public class JsonNullableDeserializer extends JsonDeserializer<JsonNullable<?>> {
    private final Class<?> targetClass;

    public JsonNullableDeserializer(Class<?> targetClass) {
        this.targetClass = targetClass;
    }

    @Override
    public JsonNullable deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
        JsonToken jsonToken = jsonParser.currentToken();
        if (jsonToken == JsonToken.VALUE_NULL) {
            return JsonNullable.undefined();
        } else {
            Object value = jsonParser.getCodec().readValue(jsonParser, this.targetClass);
            return JsonNullable.of(value);
        }
    }
}

Having this setup I encounter the following error:

There was an error while trying report the recently mapped BRIDs
org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class org.openapitools.jackson.nullable.JsonNullable]
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:403)
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:354)
    at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:103)
    at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:1132)
    at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:1115)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:865)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:764)
    at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:646)

[....]

Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `org.openapitools.jackson.nullable.JsonNullable` (no Creators, like default constructor, exist): no String-argument constructor/factory method to deserialize from String value ('simulated response from the Mock')

To me it looks like the Deserializer isn't called at all. Hm, I set a breakpoint into the firstline of the provided deserializer and it doesn't stop. So it might be more a configuration issue of the Jackson2ObjektMapperBuilder....

StefanSchubert commented 1 year ago

NONSENCE! Sorry for bothering. The problem sat as usual in front of the keyboard. I hat also another ConfigClass in my project, which I introduced to add an Intercepter to restTemplates for being able, do monitor outgoing traffic via micrometer/prometheus. This one also defined a RestTemplate Bean which the classloader picks up first! No wonder that my Jackson Config doesn't seem to have an effect.