support jackson enum features (#1845)

* support jackson enum features

* Update JacksonObjectMapperProvider.scala

* Update JacksonSerializerSpec.scala

* Update JacksonSerializerSpec.scala

* Update JacksonSerializerSpec.scala
This commit is contained in:
PJ Fanning 2025-05-23 16:00:11 +01:00 committed by GitHub
parent fdbd1b3569
commit 9ab6856965
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 54 additions and 1 deletions

View file

@ -92,6 +92,16 @@ pekko.serialization.jackson {
FAIL_ON_UNKNOWN_PROPERTIES = off
}
# Configuration of the ObjectMapper deserialization features.
# See com.fasterxml.jackson.databind.cfg.EnumFeature
# Enum values corresponding to the EnumFeature and their
# boolean values, for example:
#
# enum-features {
# READ_ENUM_KEYS_USING_INDEX = off
# }
enum-features {}
# Configuration of the ObjectMapper mapper features.
# See com.fasterxml.jackson.databind.MapperFeature
# Enum values corresponding to the MapperFeature and their

View file

@ -38,6 +38,7 @@ import com.fasterxml.jackson.databind.{
ObjectMapper,
SerializationFeature
}
import com.fasterxml.jackson.databind.cfg.EnumFeature
import com.fasterxml.jackson.databind.json.JsonMapper
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule
import com.typesafe.config.Config
@ -195,6 +196,14 @@ object JacksonObjectMapperProvider extends ExtensionId[JacksonObjectMapperProvid
case (feature, value) => objectMapper.configure(feature, value)
}
val configuredEnumFeatures = features(config, "enum-features").map {
case (enumName, value) => EnumFeature.valueOf(enumName) -> value
}
val enumFeatures = objectMapperFactory.overrideConfiguredEnumFeatures(bindingName, configuredEnumFeatures)
enumFeatures.foreach {
case (feature, value) => objectMapper.configure(feature, value)
}
val configuredMapperFeatures = features(config, "mapper-features").map {
case (enumName, value) => MapperFeature.valueOf(enumName) -> value
}
@ -512,6 +521,24 @@ class JacksonObjectMapperFactory {
: immutable.Seq[(DeserializationFeature, Boolean)] =
configuredFeatures
/**
* After construction of the `ObjectMapper` the configured enum features are applied to
* the mapper. These features can be amended programatically by overriding this method and
* return the features that are to be applied to the `ObjectMapper`.
*
* When implementing a `JacksonObjectMapperFactory` with Java the `immutable.Seq` can be
* created with [[pekko.japi.Util.immutableSeq]].
*
* @param bindingName bindingName name of this `ObjectMapper`
* @param configuredFeatures the list of `DateTimeFeature` that were configured in
* `pekko.serialization.jackson3.enum-features`
*/
def overrideConfiguredEnumFeatures(
@unused bindingName: String,
configuredFeatures: immutable.Seq[(EnumFeature, Boolean)])
: immutable.Seq[(EnumFeature, Boolean)] =
configuredFeatures
/**
* After construction of the `ObjectMapper` the configured mapper features are applied to
* the mapper. These features can be amended programmatically by overriding this method and

View file

@ -24,6 +24,7 @@ import java.util.Locale
import java.util.Optional
import java.util.UUID
import java.util.logging.FileHandler
import scala.annotation.nowarn
import scala.collection.immutable
import scala.concurrent.duration.FiniteDuration
import scala.concurrent.duration._
@ -44,13 +45,13 @@ import com.fasterxml.jackson.databind.SerializationFeature
import com.fasterxml.jackson.databind.SerializerProvider
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
import com.fasterxml.jackson.databind.annotation.JsonSerialize
import com.fasterxml.jackson.databind.cfg.EnumFeature
import com.fasterxml.jackson.databind.exc.InvalidTypeIdException
import com.fasterxml.jackson.databind.json.JsonMapper
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.module.scala.JsonScalaEnumeration
import scala.annotation.nowarn
import com.typesafe.config.ConfigFactory
import org.scalatest.BeforeAndAfterAll
import org.scalatest.matchers.should.Matchers
@ -240,6 +241,9 @@ class JacksonJsonSerializerSpec extends JacksonSerializerSpec("jackson-json") {
# off is Jackson's default
json-write-features.ESCAPE_NON_ASCII = on
# off is Jackson's default
enum-features.READ_ENUM_KEYS_USING_INDEX = on
}
""") { sys =>
val identifiedObjectMapper =
@ -328,6 +332,17 @@ class JacksonJsonSerializerSpec extends JacksonSerializerSpec("jackson-json") {
defaultObjectMapper.isEnabled(JsonGenerator.Feature.ESCAPE_NON_ASCII) should ===(false)
}
"support enum features" in {
identifiedObjectMapper.getDeserializationConfig().isEnabled(
EnumFeature.READ_ENUM_KEYS_USING_INDEX) should ===(true)
namedObjectMapper.getDeserializationConfig().isEnabled(EnumFeature.READ_ENUM_KEYS_USING_INDEX) should ===(
true)
// Default mapper follows Jackson and reference.conf default configuration
defaultObjectMapper.getDeserializationConfig().isEnabled(EnumFeature.READ_ENUM_KEYS_USING_INDEX) should ===(
false)
}
"fallback to defaults when object mapper is not configured" in {
val notConfigured = JacksonObjectMapperProvider(sys).getOrCreate("jackson-not-configured", None)
// Use Jacksons and Pekko defaults
@ -341,6 +356,7 @@ class JacksonJsonSerializerSpec extends JacksonSerializerSpec("jackson-json") {
notConfigured.isEnabled(StreamWriteFeature.WRITE_BIGDECIMAL_AS_PLAIN) should ===(false)
notConfigured.isEnabled(JsonParser.Feature.ALLOW_YAML_COMMENTS) should ===(false)
notConfigured.isEnabled(JsonGenerator.Feature.ESCAPE_NON_ASCII) should ===(false)
notConfigured.getDeserializationConfig().isEnabled(EnumFeature.READ_ENUM_KEYS_USING_INDEX) should ===(false)
}
}
}