null values sent in response json by graphql-spqr

76 views Asked by At

We have configured our spring boot app to not send null values back in json but we are still getting the null values back. The setup to not send null values using jackson ObjectMapper is in BootstrapConfig like this:

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
..
...

    @Bean
    public ObjectMapper objectMapper() {
        final SimpleDateFormat zuluDateFormat = new SimpleDateFormat(DATE_FORMAT, Locale.US);
        zuluDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
        final ObjectMapper jsonMapper = new ObjectMapper();
        jsonMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
        jsonMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        jsonMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        jsonMapper.registerModule(new JavaTimeModule());
        jsonMapper.setDateFormat(zuluDateFormat);
        jsonMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

        return jsonMapper;
    } 

We were earlier using: implementation io.leangen.graphql:spqr:0.12.1 but after upgrade to 0.12.3 we are getting same results.

When we print the gradle dependencies we do have com.google.code.gson:gson as a dependency of another library.

Is it possible spqr is using gson rather than jackson for object mapping. That seems to be the case as when I convert my entity to json in my EntityMutation class I DO NOT see any null values. And if I convert that json back to an entity and return that entity then the front end does not get null values.

Is there any way to ensure that jackson object mapper is used and not gson or am I missing something else in my setup?

1

There are 1 answers

2
kaqqao On

GraphQL SPQR isn't directly involved in serializing the result. This is squarely within Spring's turf. SPQR returns the result from a regular Spring controller method, and it's up to Spring to do what it wills. For what it's worth, I quickly tested with your exact example, and I got nulls filtered out. So I think your problem is unrelated to SPQR. One thing that might happen is that something in your is calling ApplicationConversionService.configure(FormatterRegistry). If this is called more than once, and Spring's autoconfiguration calls it once already, it will register 2 ObjectMappers, and it's unpredictable which one gets used by Spring. You can inspect FormatterRegistry to see if it looks normal.

That said, while GraphQL SPQR isn't involved in serialization, it is involved in deserialization, and it indeed does not automatically use the framework-wide ObjectMapper instance, but instead has its own. This is mostly because deserialization needs can vary greatly depending on the context (what it is acceptable in one context may not be acceptable in another) and detecting conflicting expectations can be awful to debug.

But, if you're sure your client won't be negatively affected by the changes, all you need to do to tell SPQR to inherit the configuration is:

@Bean
public JacksonValueMapperFactory jacksonValueMapper(ObjectMapper mapper) {
    return JacksonValueMapperFactory.builder()
            .withPrototype(mapper)
            .build();
}

With this, all settings on the framework-wide ObjectMapper will also apply to your GraphQL API. But, be extremely careful when doing this: ObjectMapper in SPQR is used to detect deserializable fields for GraphQL input types, read input values etc, so a change that seems innocuous in one context can have unforeseen consequences elsewhere (this is why SPQR has its own instance by default). But, again, SPQR isn't involved in serialization, so you do not need to to customize the bean mentioned above.