Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move standard ObjectMapper customization to dedicated Customizer #40819

Merged
merged 1 commit into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
import io.quarkus.gizmo.ResultHandle;
import io.quarkus.jackson.JacksonMixin;
import io.quarkus.jackson.ObjectMapperCustomizer;
import io.quarkus.jackson.runtime.ConfigurationCustomizer;
import io.quarkus.jackson.runtime.JacksonBuildTimeConfig;
import io.quarkus.jackson.runtime.JacksonSupport;
import io.quarkus.jackson.runtime.JacksonSupportRecorder;
Expand Down Expand Up @@ -111,6 +112,8 @@ public class JacksonProcessor {
@BuildStep
void unremovable(Capabilities capabilities, BuildProducer<UnremovableBeanBuildItem> producer,
BuildProducer<AdditionalBeanBuildItem> additionalProducer) {
additionalProducer.produce(AdditionalBeanBuildItem.unremovableOf(ConfigurationCustomizer.class));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the correct way of doing it? Or should I use the producer instead of the additionalProducer?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should work just fine :)


if (capabilities.isPresent(Capability.VERTX_CORE)) {
producer.produce(UnremovableBeanBuildItem.beanTypes(ObjectMapper.class));
additionalProducer.produce(AdditionalBeanBuildItem.unremovableOf(VertxHybridPoolObjectMapperCustomizer.class));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
public interface ObjectMapperCustomizer extends Comparable<ObjectMapperCustomizer> {

int MINIMUM_PRIORITY = Integer.MIN_VALUE;
int MAXIMUM_PRIORITY = Integer.MAX_VALUE;
// we use this priority to give a chance to other customizers to override serializers / deserializers
// that might have been added by the modules that Quarkus registers automatically
// (Jackson will keep the last registered serializer / deserializer for a given type
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package io.quarkus.jackson.runtime;

import java.time.ZoneId;
import java.util.TimeZone;

import jakarta.inject.Inject;
import jakarta.inject.Singleton;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

import io.quarkus.jackson.ObjectMapperCustomizer;

@Singleton
public class ConfigurationCustomizer implements ObjectMapperCustomizer {
@Inject
JacksonBuildTimeConfig jacksonBuildTimeConfig;

@Inject
JacksonSupport jacksonSupport;

@Override
public void customize(ObjectMapper objectMapper) {
if (!jacksonBuildTimeConfig.failOnUnknownProperties) {
// this feature is enabled by default, so we disable it
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
}
geoand marked this conversation as resolved.
Show resolved Hide resolved
if (!jacksonBuildTimeConfig.failOnEmptyBeans) {
// this feature is enabled by default, so we disable it
objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
}
if (!jacksonBuildTimeConfig.writeDatesAsTimestamps) {
// this feature is enabled by default, so we disable it
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
}
if (!jacksonBuildTimeConfig.writeDurationsAsTimestamps) {
// this feature is enabled by default, so we disable it
objectMapper.disable(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS);
}
if (jacksonBuildTimeConfig.acceptCaseInsensitiveEnums) {
objectMapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS);
}
JsonInclude.Include serializationInclusion = jacksonBuildTimeConfig.serializationInclusion.orElse(null);
if (serializationInclusion != null) {
objectMapper.setSerializationInclusion(serializationInclusion);
}
ZoneId zoneId = jacksonBuildTimeConfig.timezone.orElse(null);
if ((zoneId != null) && !zoneId.getId().equals("UTC")) { // Jackson uses UTC as the default, so let's not reset it
objectMapper.setTimeZone(TimeZone.getTimeZone(zoneId));
}
if (jacksonSupport.configuredNamingStrategy().isPresent()) {
objectMapper.setPropertyNamingStrategy(jacksonSupport.configuredNamingStrategy().get());
}
}

@Override
public int priority() {
// we return the maximum possible priority to make sure these
// settings are always applied first, before any other customizers.
return ObjectMapperCustomizer.MAXIMUM_PRIORITY;
}
}
Original file line number Diff line number Diff line change
@@ -1,64 +1,27 @@
package io.quarkus.jackson.runtime;

import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.TimeZone;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;
import jakarta.inject.Singleton;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

import io.quarkus.arc.All;
import io.quarkus.arc.DefaultBean;
import io.quarkus.jackson.ObjectMapperCustomizer;

@ApplicationScoped
public class ObjectMapperProducer {

@DefaultBean
@Singleton
@Produces
public ObjectMapper objectMapper(@All List<ObjectMapperCustomizer> customizers,
JacksonBuildTimeConfig jacksonBuildTimeConfig, JacksonSupport jacksonSupport) {
ObjectMapper objectMapper = new ObjectMapper();
if (!jacksonBuildTimeConfig.failOnUnknownProperties) {
// this feature is enabled by default, so we disable it
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
}
if (!jacksonBuildTimeConfig.failOnEmptyBeans) {
// this feature is enabled by default, so we disable it
objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
}
if (!jacksonBuildTimeConfig.writeDatesAsTimestamps) {
// this feature is enabled by default, so we disable it
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
}
if (!jacksonBuildTimeConfig.writeDurationsAsTimestamps) {
// this feature is enabled by default, so we disable it
objectMapper.disable(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS);
}
if (jacksonBuildTimeConfig.acceptCaseInsensitiveEnums) {
objectMapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS);
}
JsonInclude.Include serializationInclusion = jacksonBuildTimeConfig.serializationInclusion.orElse(null);
if (serializationInclusion != null) {
objectMapper.setSerializationInclusion(serializationInclusion);
}
ZoneId zoneId = jacksonBuildTimeConfig.timezone.orElse(null);
if ((zoneId != null) && !zoneId.getId().equals("UTC")) { // Jackson uses UTC as the default, so let's not reset it
objectMapper.setTimeZone(TimeZone.getTimeZone(zoneId));
}
if (jacksonSupport.configuredNamingStrategy().isPresent()) {
objectMapper.setPropertyNamingStrategy(jacksonSupport.configuredNamingStrategy().get());
}
List<ObjectMapperCustomizer> sortedCustomizers = sortCustomizersInDescendingPriorityOrder(customizers);
for (ObjectMapperCustomizer customizer : sortedCustomizers) {
customizer.customize(objectMapper);
Expand Down
Loading