Jackson 2.10.0 (#27814)
* Update Jackson to 2.10.0 * Support configuration for new features introduced in Jackson 2.10 * WRITE_DURATIONS_AS_TIMESTAMPS is the new config used to serialize durations Previously WRITE_DATES_AS_TIMESTAMPS was used for both date/time and duration, but in Jackson 2.10 WRITE_DURATIONS_AS_TIMESTAMPS is used for durations, so it needs to be configured consistently with WRITE_DATES_AS_TIMESTAMPS.
This commit is contained in:
parent
619f821e8d
commit
92b9db5858
7 changed files with 288 additions and 55 deletions
|
|
@ -202,7 +202,7 @@ class JacksonSerializationBench {
|
|||
}
|
||||
serialization.jackson {
|
||||
compress-larger-than = 100000 b
|
||||
|
||||
|
||||
serialization-features {
|
||||
#WRITE_DATES_AS_TIMESTAMPS = off
|
||||
}
|
||||
|
|
|
|||
|
|
@ -394,7 +394,7 @@ Jackson are used aside from the the following that are changed in Akka's default
|
|||
|
||||
### Date/time format
|
||||
|
||||
`WRITE_DATES_AS_TIMESTAMPS` is by default disabled, which means that date/time fields are serialized in
|
||||
`WRITE_DATES_AS_TIMESTAMPS` and `WRITE_DURATIONS_AS_TIMESTAMPS` are by default disabled, which means that date/time fields are serialized in
|
||||
ISO-8601 (rfc3339) `yyyy-MM-dd'T'HH:mm:ss.SSSZ` format instead of numeric arrays. This is better for
|
||||
interoperability but it is slower. If you don't need the ISO format for interoperability with external systems
|
||||
you can change the following configuration for better performance of date/time fields.
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ akka.serialization.jackson {
|
|||
# For interoperability it's better to use the ISO format, i.e. WRITE_DATES_AS_TIMESTAMPS=off,
|
||||
# but WRITE_DATES_AS_TIMESTAMPS=on has better performance.
|
||||
WRITE_DATES_AS_TIMESTAMPS = off
|
||||
WRITE_DURATIONS_AS_TIMESTAMPS = off
|
||||
}
|
||||
|
||||
# Configuration of the ObjectMapper deserialization features.
|
||||
|
|
@ -94,6 +95,46 @@ akka.serialization.jackson {
|
|||
# }
|
||||
json-generator-features {}
|
||||
|
||||
# Configuration of the JsonFactory StreamReadFeature.
|
||||
# See com.fasterxml.jackson.core.StreamReadFeature
|
||||
# Enum values corresponding to the StreamReadFeatures and
|
||||
# their boolean value, for example:
|
||||
#
|
||||
# stream-read-features {
|
||||
# STRICT_DUPLICATE_DETECTION = on
|
||||
# }
|
||||
stream-read-features {}
|
||||
|
||||
# Configuration of the JsonFactory StreamWriteFeature.
|
||||
# See com.fasterxml.jackson.core.StreamWriteFeature
|
||||
# Enum values corresponding to the StreamWriteFeatures and
|
||||
# their boolean value, for example:
|
||||
#
|
||||
# stream-write-features {
|
||||
# WRITE_BIGDECIMAL_AS_PLAIN = on
|
||||
# }
|
||||
stream-write-features {}
|
||||
|
||||
# Configuration of the JsonFactory JsonReadFeature.
|
||||
# See com.fasterxml.jackson.core.json.JsonReadFeature
|
||||
# Enum values corresponding to the JsonReadFeatures and
|
||||
# their boolean value, for example:
|
||||
#
|
||||
# json-read-features {
|
||||
# ALLOW_SINGLE_QUOTES = on
|
||||
# }
|
||||
json-read-features {}
|
||||
|
||||
# Configuration of the JsonFactory JsonWriteFeature.
|
||||
# See com.fasterxml.jackson.core.json.JsonWriteFeature
|
||||
# Enum values corresponding to the JsonWriteFeatures and
|
||||
# their boolean value, for example:
|
||||
#
|
||||
# json-write-features {
|
||||
# WRITE_NUMBERS_AS_STRINGS = on
|
||||
# }
|
||||
json-write-features {}
|
||||
|
||||
# Additional classes that are allowed even if they are not defined in `serialization-bindings`.
|
||||
# This is useful when a class is not used for serialization any more and therefore removed
|
||||
# from `serialization-bindings`, but should still be possible to deserialize.
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import scala.collection.immutable
|
|||
import scala.compat.java8.OptionConverters._
|
||||
import scala.util.Failure
|
||||
import scala.util.Success
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import akka.actor.DynamicAccess
|
||||
import akka.actor.ExtendedActorSystem
|
||||
|
|
@ -27,6 +26,7 @@ import com.fasterxml.jackson.annotation.JsonAutoDetect
|
|||
import com.fasterxml.jackson.annotation.JsonCreator
|
||||
import com.fasterxml.jackson.annotation.PropertyAccessor
|
||||
import com.fasterxml.jackson.core.JsonFactory
|
||||
import com.fasterxml.jackson.core.JsonFactoryBuilder
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||
import com.fasterxml.jackson.databind.MapperFeature
|
||||
import com.fasterxml.jackson.databind.Module
|
||||
|
|
@ -36,6 +36,11 @@ import com.fasterxml.jackson.module.paramnames.ParameterNamesModule
|
|||
import com.typesafe.config.Config
|
||||
import com.fasterxml.jackson.core.JsonParser
|
||||
import com.fasterxml.jackson.core.JsonGenerator
|
||||
import com.fasterxml.jackson.core.StreamReadFeature
|
||||
import com.fasterxml.jackson.core.StreamWriteFeature
|
||||
import com.fasterxml.jackson.core.json.JsonReadFeature
|
||||
import com.fasterxml.jackson.core.json.JsonWriteFeature
|
||||
import com.fasterxml.jackson.databind.json.JsonMapper
|
||||
|
||||
object JacksonObjectMapperProvider extends ExtensionId[JacksonObjectMapperProvider] with ExtensionIdProvider {
|
||||
override def get(system: ActorSystem): JacksonObjectMapperProvider = super.get(system)
|
||||
|
|
@ -57,25 +62,65 @@ object JacksonObjectMapperProvider extends ExtensionId[JacksonObjectMapperProvid
|
|||
baseConf
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL API: Use [[JacksonObjectMapperProvider#create]]
|
||||
*
|
||||
* This is needed by one test in Lagom where the ObjectMapper is created without starting and ActorSystem.
|
||||
*/
|
||||
@InternalStableApi
|
||||
def createObjectMapper(
|
||||
private def createJsonFactory(
|
||||
bindingName: String,
|
||||
jsonFactory: Option[JsonFactory],
|
||||
objectMapperFactory: JacksonObjectMapperFactory,
|
||||
config: Config,
|
||||
dynamicAccess: DynamicAccess,
|
||||
log: Option[LoggingAdapter]) = {
|
||||
baseJsonFactory: Option[JsonFactory]): JsonFactory = {
|
||||
|
||||
import akka.util.ccompat.JavaConverters._
|
||||
val jsonFactoryBuilder = baseJsonFactory match {
|
||||
case Some(jsonFactory) => new JsonFactoryBuilder(jsonFactory)
|
||||
case None => new JsonFactoryBuilder()
|
||||
}
|
||||
|
||||
val mapper = objectMapperFactory.newObjectMapper(bindingName, jsonFactory)
|
||||
val configuredStreamReadFeatures =
|
||||
features(config, "stream-read-features").map {
|
||||
case (enumName, value) => StreamReadFeature.valueOf(enumName) -> value
|
||||
}
|
||||
val streamReadFeatures =
|
||||
objectMapperFactory.overrideConfiguredStreamReadFeatures(bindingName, configuredStreamReadFeatures)
|
||||
streamReadFeatures.foreach {
|
||||
case (feature, value) => jsonFactoryBuilder.configure(feature, value)
|
||||
}
|
||||
|
||||
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)
|
||||
val configuredStreamWriteFeatures =
|
||||
features(config, "stream-write-features").map {
|
||||
case (enumName, value) => StreamWriteFeature.valueOf(enumName) -> value
|
||||
}
|
||||
val streamWriteFeatures =
|
||||
objectMapperFactory.overrideConfiguredStreamWriteFeatures(bindingName, configuredStreamWriteFeatures)
|
||||
streamWriteFeatures.foreach {
|
||||
case (feature, value) => jsonFactoryBuilder.configure(feature, value)
|
||||
}
|
||||
|
||||
val configuredJsonReadFeatures =
|
||||
features(config, "json-read-features").map {
|
||||
case (enumName, value) => JsonReadFeature.valueOf(enumName) -> value
|
||||
}
|
||||
val jsonReadFeatures =
|
||||
objectMapperFactory.overrideConfiguredJsonReadFeatures(bindingName, configuredJsonReadFeatures)
|
||||
jsonReadFeatures.foreach {
|
||||
case (feature, value) => jsonFactoryBuilder.configure(feature, value)
|
||||
}
|
||||
|
||||
val configuredJsonWriteFeatures =
|
||||
features(config, "json-write-features").map {
|
||||
case (enumName, value) => JsonWriteFeature.valueOf(enumName) -> value
|
||||
}
|
||||
val jsonWriteFeatures =
|
||||
objectMapperFactory.overrideConfiguredJsonWriteFeatures(bindingName, configuredJsonWriteFeatures)
|
||||
jsonWriteFeatures.foreach {
|
||||
case (feature, value) => jsonFactoryBuilder.configure(feature, value)
|
||||
}
|
||||
|
||||
jsonFactoryBuilder.build()
|
||||
}
|
||||
|
||||
private def configureObjectMapperFeatures(
|
||||
bindingName: String,
|
||||
objectMapper: ObjectMapper,
|
||||
objectMapperFactory: JacksonObjectMapperFactory,
|
||||
config: Config): Unit = {
|
||||
|
||||
val configuredSerializationFeatures =
|
||||
features(config, "serialization-features").map {
|
||||
|
|
@ -84,7 +129,7 @@ object JacksonObjectMapperProvider extends ExtensionId[JacksonObjectMapperProvid
|
|||
val serializationFeatures =
|
||||
objectMapperFactory.overrideConfiguredSerializationFeatures(bindingName, configuredSerializationFeatures)
|
||||
serializationFeatures.foreach {
|
||||
case (feature, value) => mapper.configure(feature, value)
|
||||
case (feature, value) => objectMapper.configure(feature, value)
|
||||
}
|
||||
|
||||
val configuredDeserializationFeatures =
|
||||
|
|
@ -94,7 +139,7 @@ object JacksonObjectMapperProvider extends ExtensionId[JacksonObjectMapperProvid
|
|||
val deserializationFeatures =
|
||||
objectMapperFactory.overrideConfiguredDeserializationFeatures(bindingName, configuredDeserializationFeatures)
|
||||
deserializationFeatures.foreach {
|
||||
case (feature, value) => mapper.configure(feature, value)
|
||||
case (feature, value) => objectMapper.configure(feature, value)
|
||||
}
|
||||
|
||||
val configuredMapperFeatures = features(config, "mapper-features").map {
|
||||
|
|
@ -102,7 +147,7 @@ object JacksonObjectMapperProvider extends ExtensionId[JacksonObjectMapperProvid
|
|||
}
|
||||
val mapperFeatures = objectMapperFactory.overrideConfiguredMapperFeatures(bindingName, configuredMapperFeatures)
|
||||
mapperFeatures.foreach {
|
||||
case (feature, value) => mapper.configure(feature, value)
|
||||
case (feature, value) => objectMapper.configure(feature, value)
|
||||
}
|
||||
|
||||
val configuredJsonParserFeatures = features(config, "json-parser-features").map {
|
||||
|
|
@ -111,7 +156,7 @@ object JacksonObjectMapperProvider extends ExtensionId[JacksonObjectMapperProvid
|
|||
val jsonParserFeatures =
|
||||
objectMapperFactory.overrideConfiguredJsonParserFeatures(bindingName, configuredJsonParserFeatures)
|
||||
jsonParserFeatures.foreach {
|
||||
case (feature, value) => mapper.configure(feature, value)
|
||||
case (feature, value) => objectMapper.configure(feature, value)
|
||||
}
|
||||
|
||||
val configuredJsonGeneratorFeatures = features(config, "json-generator-features").map {
|
||||
|
|
@ -120,8 +165,19 @@ object JacksonObjectMapperProvider extends ExtensionId[JacksonObjectMapperProvid
|
|||
val jsonGeneratorFeatures =
|
||||
objectMapperFactory.overrideConfiguredJsonGeneratorFeatures(bindingName, configuredJsonGeneratorFeatures)
|
||||
jsonGeneratorFeatures.foreach {
|
||||
case (feature, value) => mapper.configure(feature, value)
|
||||
case (feature, value) => objectMapper.configure(feature, value)
|
||||
}
|
||||
}
|
||||
|
||||
private def configureObjectMapperModules(
|
||||
bindingName: String,
|
||||
objectMapper: ObjectMapper,
|
||||
objectMapperFactory: JacksonObjectMapperFactory,
|
||||
config: Config,
|
||||
dynamicAccess: DynamicAccess,
|
||||
log: Option[LoggingAdapter]): Unit = {
|
||||
|
||||
import akka.util.ccompat.JavaConverters._
|
||||
|
||||
val configuredModules = config.getStringList("jackson-modules").asScala
|
||||
val modules1 =
|
||||
|
|
@ -154,11 +210,32 @@ object JacksonObjectMapperProvider extends ExtensionId[JacksonObjectMapperProvid
|
|||
val modules3 = objectMapperFactory.overrideConfiguredModules(bindingName, modules2)
|
||||
|
||||
modules3.foreach { module =>
|
||||
mapper.registerModule(module)
|
||||
objectMapper.registerModule(module)
|
||||
log.foreach(_.debug("Registered Jackson module [{}]", module.getClass.getName))
|
||||
}
|
||||
}
|
||||
|
||||
mapper
|
||||
/**
|
||||
* INTERNAL API: Use [[JacksonObjectMapperProvider#create]]
|
||||
*
|
||||
* This is needed by one test in Lagom where the ObjectMapper is created without starting and ActorSystem.
|
||||
*/
|
||||
@InternalStableApi
|
||||
def createObjectMapper(
|
||||
bindingName: String,
|
||||
jsonFactory: Option[JsonFactory],
|
||||
objectMapperFactory: JacksonObjectMapperFactory,
|
||||
config: Config,
|
||||
dynamicAccess: DynamicAccess,
|
||||
log: Option[LoggingAdapter]): ObjectMapper = {
|
||||
|
||||
val configuredJsonFactory = createJsonFactory(bindingName, objectMapperFactory, config, jsonFactory)
|
||||
val mapper = objectMapperFactory.newObjectMapper(bindingName, configuredJsonFactory)
|
||||
|
||||
configureObjectMapperFeatures(bindingName, mapper, objectMapperFactory, config)
|
||||
configureObjectMapperModules(bindingName, mapper, objectMapperFactory, config, dynamicAccess, log)
|
||||
|
||||
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)
|
||||
}
|
||||
|
||||
private def isModuleEnabled(fqcn: String, dynamicAccess: DynamicAccess): Boolean =
|
||||
|
|
@ -300,8 +377,25 @@ class JacksonObjectMapperFactory {
|
|||
* @param jsonFactory optional `JsonFactory` such as `CBORFactory`, for plain JSON `None` (defaults)
|
||||
* can be used
|
||||
*/
|
||||
def newObjectMapper(@unused bindingName: String, jsonFactory: Option[JsonFactory]): ObjectMapper =
|
||||
new ObjectMapper(jsonFactory.orNull)
|
||||
def newObjectMapper(@unused bindingName: String, jsonFactory: JsonFactory): ObjectMapper =
|
||||
JsonMapper.builder(jsonFactory).build()
|
||||
|
||||
/**
|
||||
* After construction of the `ObjectMapper` the configured modules are added to
|
||||
* the mapper. These modules can be amended programatically by overriding this method and
|
||||
* return the modules that are to be applied to the `ObjectMapper`.
|
||||
*
|
||||
* When implementing a `JacksonObjectMapperFactory` with Java the `immutable.Seq` can be
|
||||
* created with [[akka.japi.Util.immutableSeq]].
|
||||
*
|
||||
* @param bindingName bindingName name of this `ObjectMapper`
|
||||
* @param configuredModules the list of `Modules` that were configured in
|
||||
* `akka.serialization.jackson.deserialization-features`
|
||||
*/
|
||||
def overrideConfiguredModules(
|
||||
@unused bindingName: String,
|
||||
configuredModules: immutable.Seq[Module]): immutable.Seq[Module] =
|
||||
configuredModules
|
||||
|
||||
/**
|
||||
* After construction of the `ObjectMapper` the configured serialization features are applied to
|
||||
|
|
@ -339,23 +433,6 @@ class JacksonObjectMapperFactory {
|
|||
: immutable.Seq[(DeserializationFeature, Boolean)] =
|
||||
configuredFeatures
|
||||
|
||||
/**
|
||||
* After construction of the `ObjectMapper` the configured modules are added to
|
||||
* the mapper. These modules can be amended programatically by overriding this method and
|
||||
* return the modules that are to be applied to the `ObjectMapper`.
|
||||
*
|
||||
* When implementing a `JacksonObjectMapperFactory` with Java the `immutable.Seq` can be
|
||||
* created with [[akka.japi.Util.immutableSeq]].
|
||||
*
|
||||
* @param bindingName bindingName name of this `ObjectMapper`
|
||||
* @param configuredModules the list of `Modules` that were configured in
|
||||
* `akka.serialization.jackson.deserialization-features`
|
||||
*/
|
||||
def overrideConfiguredModules(
|
||||
@unused bindingName: String,
|
||||
configuredModules: immutable.Seq[Module]): immutable.Seq[Module] =
|
||||
configuredModules
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
|
@ -395,4 +472,56 @@ class JacksonObjectMapperFactory {
|
|||
configuredFeatures: immutable.Seq[(JsonGenerator.Feature, Boolean)])
|
||||
: immutable.Seq[(JsonGenerator.Feature, Boolean)] =
|
||||
configuredFeatures
|
||||
|
||||
/**
|
||||
* `StreamReadFeature`s used to configure the `JsonFactoryBuilder` that, if provided, will later be used to create
|
||||
* an `ObjectMapper`. These features can be amended programmatically by overriding this method and return the features
|
||||
* that are to be applied to the `JsonFactoryBuilder`.
|
||||
*
|
||||
* @param bindingName bindingName name of this `ObjectMapper`
|
||||
* @param configuredFeatures the list of `StreamReadFeature` that were configured in `akka.serialization.jackson.stream-read-features`
|
||||
*/
|
||||
def overrideConfiguredStreamReadFeatures(
|
||||
@unused bindingName: String,
|
||||
configuredFeatures: immutable.Seq[(StreamReadFeature, Boolean)]): immutable.Seq[(StreamReadFeature, Boolean)] =
|
||||
configuredFeatures
|
||||
|
||||
/**
|
||||
* `StreamWriteFeature`s used to configure the `JsonFactoryBuilder` that, if provided, will later be used to create
|
||||
* an `ObjectMapper`. These features can be amended programmatically by overriding this method and return the features
|
||||
* that are to be applied to the `JsonFactoryBuilder`.
|
||||
*
|
||||
* @param bindingName bindingName name of this `ObjectMapper`
|
||||
* @param configuredFeatures the list of `StreamWriterFeature` that were configured in `akka.serialization.jackson.stream-write-features`
|
||||
*/
|
||||
def overrideConfiguredStreamWriteFeatures(
|
||||
@unused bindingName: String,
|
||||
configuredFeatures: immutable.Seq[(StreamWriteFeature, Boolean)]): immutable.Seq[(StreamWriteFeature, Boolean)] =
|
||||
configuredFeatures
|
||||
|
||||
/**
|
||||
* `JsonReadFeature`s used to configure the `JsonFactoryBuilder` that, if provided, will later be used to create
|
||||
* an `ObjectMapper`. These features can be amended programmatically by overriding this method and return the features
|
||||
* that are to be applied to the `JsonFactoryBuilder`.
|
||||
*
|
||||
* @param bindingName bindingName name of this `ObjectMapper`
|
||||
* @param configuredFeatures the list of `JsonReadFeature` that were configured in `akka.serialization.jackson.json-read-features`
|
||||
*/
|
||||
def overrideConfiguredJsonReadFeatures(
|
||||
@unused bindingName: String,
|
||||
configuredFeatures: immutable.Seq[(JsonReadFeature, Boolean)]): immutable.Seq[(JsonReadFeature, Boolean)] =
|
||||
configuredFeatures
|
||||
|
||||
/**
|
||||
* `JsonWriteFeature`s used to configure the `JsonFactoryBuilder` that, if provided, will later be used to create
|
||||
* an `ObjectMapper`. These features can be amended programmatically by overriding this method and return the features
|
||||
* that are to be applied to the `JsonFactoryBuilder`.
|
||||
*
|
||||
* @param bindingName bindingName name of this `ObjectMapper`
|
||||
* @param configuredFeatures the list of `JsonWriteFeature` that were configured in `akka.serialization.jackson.json-write-features`
|
||||
*/
|
||||
def overrideConfiguredJsonWriteFeatures(
|
||||
@unused bindingName: String,
|
||||
configuredFeatures: immutable.Seq[(JsonWriteFeature, Boolean)]): immutable.Seq[(JsonWriteFeature, Boolean)] =
|
||||
configuredFeatures
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ import java.util.logging.FileHandler
|
|||
import scala.collection.immutable
|
||||
import scala.concurrent.duration.FiniteDuration
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import akka.actor.ActorRef
|
||||
import akka.actor.ActorSystem
|
||||
import akka.actor.Address
|
||||
|
|
@ -48,6 +47,10 @@ import org.scalatest.Matchers
|
|||
import org.scalatest.WordSpecLike
|
||||
import com.fasterxml.jackson.core.JsonParser
|
||||
import com.fasterxml.jackson.core.JsonGenerator
|
||||
import com.fasterxml.jackson.core.StreamReadFeature
|
||||
import com.fasterxml.jackson.core.StreamWriteFeature
|
||||
import com.fasterxml.jackson.databind.json.JsonMapper
|
||||
import com.github.ghik.silencer.silent
|
||||
|
||||
object ScalaTestMessages {
|
||||
trait TestMessage
|
||||
|
|
@ -109,6 +112,7 @@ class ScalaTestEventMigration extends JacksonMigration {
|
|||
|
||||
class JacksonCborSerializerSpec extends JacksonSerializerSpec("jackson-cbor")
|
||||
|
||||
@silent // this test uses Jackson deprecated APIs
|
||||
class JacksonJsonSerializerSpec extends JacksonSerializerSpec("jackson-json") {
|
||||
|
||||
def serializeToJsonString(obj: AnyRef, sys: ActorSystem = system): String = {
|
||||
|
|
@ -161,6 +165,18 @@ class JacksonJsonSerializerSpec extends JacksonSerializerSpec("jackson-json") {
|
|||
|
||||
# on is Jackson's default
|
||||
json-generator-features.AUTO_CLOSE_TARGET = off
|
||||
|
||||
# off is Jackson's default
|
||||
stream-read-features.STRICT_DUPLICATE_DETECTION = on
|
||||
|
||||
# off is Jackson's default
|
||||
stream-write-features.WRITE_BIGDECIMAL_AS_PLAIN = on
|
||||
|
||||
# off is Jackson's default
|
||||
json-read-features.ALLOW_YAML_COMMENTS = on
|
||||
|
||||
# off is Jackson's default
|
||||
json-write-features.ESCAPE_NON_ASCII = on
|
||||
}
|
||||
""") { sys =>
|
||||
val identifiedObjectMapper =
|
||||
|
|
@ -174,7 +190,7 @@ class JacksonJsonSerializerSpec extends JacksonSerializerSpec("jackson-json") {
|
|||
namedObjectMapper.isEnabled(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS) should ===(false)
|
||||
|
||||
// Default mapper follows Jackson and reference.conf default configuration
|
||||
defaultObjectMapper.isEnabled(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS) should ===(true)
|
||||
defaultObjectMapper.isEnabled(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS) should ===(false)
|
||||
}
|
||||
|
||||
"support deserialization features" in {
|
||||
|
|
@ -209,14 +225,59 @@ class JacksonJsonSerializerSpec extends JacksonSerializerSpec("jackson-json") {
|
|||
defaultObjectMapper.isEnabled(JsonGenerator.Feature.AUTO_CLOSE_TARGET) should ===(true)
|
||||
}
|
||||
|
||||
"support stream read features" in {
|
||||
identifiedObjectMapper.isEnabled(StreamReadFeature.STRICT_DUPLICATE_DETECTION) should ===(true)
|
||||
namedObjectMapper.isEnabled(StreamReadFeature.STRICT_DUPLICATE_DETECTION) should ===(true)
|
||||
|
||||
// Default mapper follows Jackson and reference.conf default configuration
|
||||
defaultObjectMapper.isEnabled(StreamReadFeature.STRICT_DUPLICATE_DETECTION) should ===(false)
|
||||
}
|
||||
|
||||
"support stream write features" in {
|
||||
identifiedObjectMapper.isEnabled(StreamWriteFeature.WRITE_BIGDECIMAL_AS_PLAIN) should ===(true)
|
||||
namedObjectMapper.isEnabled(StreamWriteFeature.WRITE_BIGDECIMAL_AS_PLAIN) should ===(true)
|
||||
|
||||
// Default mapper follows Jackson and reference.conf default configuration
|
||||
defaultObjectMapper.isEnabled(StreamWriteFeature.WRITE_BIGDECIMAL_AS_PLAIN) should ===(false)
|
||||
}
|
||||
|
||||
"support json read features" in {
|
||||
// ATTENTION: this is trick. Although we are configuring `json-read-features`, Jackson
|
||||
// does not provides a way to check for `StreamReadFeature`s, so we need to check for
|
||||
// `JsonParser.Feature`.ALLOW_YAML_COMMENTS.
|
||||
// Same applies for json-write-features and JsonGenerator.Feature.
|
||||
identifiedObjectMapper.isEnabled(JsonParser.Feature.ALLOW_YAML_COMMENTS) should ===(true)
|
||||
namedObjectMapper.isEnabled(JsonParser.Feature.ALLOW_YAML_COMMENTS) should ===(true)
|
||||
|
||||
// Default mapper follows Jackson and reference.conf default configuration
|
||||
defaultObjectMapper.isEnabled(JsonParser.Feature.ALLOW_YAML_COMMENTS) should ===(false)
|
||||
}
|
||||
|
||||
"support json write features" in {
|
||||
// ATTENTION: this is trickier than `json-read-features` vs JsonParser.Feature
|
||||
// since the JsonWriteFeature replaces deprecated APIs in JsonGenerator.Feature.
|
||||
// But just like the test for `json-read-features` there is no API to check for
|
||||
// `JsonWriteFeature`s, so we need to use the deprecated APIs.
|
||||
identifiedObjectMapper.isEnabled(JsonGenerator.Feature.ESCAPE_NON_ASCII) should ===(true)
|
||||
namedObjectMapper.isEnabled(JsonGenerator.Feature.ESCAPE_NON_ASCII) should ===(true)
|
||||
|
||||
// Default mapper follows Jackson and reference.conf default configuration
|
||||
defaultObjectMapper.isEnabled(JsonGenerator.Feature.ESCAPE_NON_ASCII) should ===(false)
|
||||
}
|
||||
|
||||
"fallback to defaults when object mapper is not configured" in {
|
||||
val notConfigured = JacksonObjectMapperProvider(sys).getOrCreate("jackson-not-configured", None)
|
||||
// Use Jacksons and Akka defaults
|
||||
notConfigured.isEnabled(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS) should ===(true)
|
||||
notConfigured.isEnabled(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS) should ===(false)
|
||||
notConfigured.isEnabled(DeserializationFeature.EAGER_DESERIALIZER_FETCH) should ===(true)
|
||||
notConfigured.isEnabled(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY) should ===(false)
|
||||
notConfigured.isEnabled(JsonParser.Feature.ALLOW_COMMENTS) should ===(false)
|
||||
notConfigured.isEnabled(JsonGenerator.Feature.AUTO_CLOSE_TARGET) should ===(true)
|
||||
|
||||
notConfigured.isEnabled(StreamReadFeature.STRICT_DUPLICATE_DETECTION) should ===(false)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -253,6 +314,7 @@ class JacksonJsonSerializerSpec extends JacksonSerializerSpec("jackson-json") {
|
|||
withSystem("""
|
||||
akka.serialization.jackson.serialization-features {
|
||||
WRITE_DATES_AS_TIMESTAMPS = on
|
||||
WRITE_DURATIONS_AS_TIMESTAMPS = on
|
||||
}
|
||||
""") { sys =>
|
||||
val msg = new TimeCommand(LocalDateTime.of(2019, 4, 29, 23, 15, 3, 12345), Duration.of(5, ChronoUnit.SECONDS))
|
||||
|
|
@ -304,9 +366,9 @@ class JacksonJsonSerializerSpec extends JacksonSerializerSpec("jackson-json") {
|
|||
|
||||
"be possible to create custom ObjectMapper" in {
|
||||
val customJacksonObjectMapperFactory = new JacksonObjectMapperFactory {
|
||||
override def newObjectMapper(bindingName: String, jsonFactory: Option[JsonFactory]): ObjectMapper = {
|
||||
override def newObjectMapper(bindingName: String, jsonFactory: JsonFactory): ObjectMapper = {
|
||||
if (bindingName == "jackson-json") {
|
||||
val mapper = new ObjectMapper(jsonFactory.orNull)
|
||||
val mapper: ObjectMapper = JsonMapper.builder(jsonFactory).build()
|
||||
// some customer configuration of the mapper
|
||||
mapper.setLocale(Locale.US)
|
||||
mapper
|
||||
|
|
@ -354,7 +416,7 @@ class JacksonJsonSerializerSpec extends JacksonSerializerSpec("jackson-json") {
|
|||
configuredFeatures: immutable.Seq[(JsonGenerator.Feature, Boolean)])
|
||||
: immutable.Seq[(JsonGenerator.Feature, Boolean)] =
|
||||
if (bindingName == "jackson-json")
|
||||
configuredFeatures :+ (JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS -> true)
|
||||
configuredFeatures :+ (JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN -> true)
|
||||
else
|
||||
super.overrideConfiguredJsonGeneratorFeatures(bindingName, configuredFeatures)
|
||||
}
|
||||
|
|
@ -370,14 +432,14 @@ class JacksonJsonSerializerSpec extends JacksonSerializerSpec("jackson-json") {
|
|||
mapper.isEnabled(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY) should ===(true)
|
||||
mapper.isEnabled(JsonParser.Feature.ALLOW_SINGLE_QUOTES) should ===(true)
|
||||
mapper.isEnabled(SerializationFeature.INDENT_OUTPUT) should ===(true)
|
||||
mapper.isEnabled(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS) should ===(true)
|
||||
mapper.isEnabled(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN) should ===(true)
|
||||
|
||||
val msg = InstantCommand(Instant.ofEpochMilli(1559907792075L))
|
||||
val json = serializeToJsonString(msg, sys)
|
||||
// using the custom ObjectMapper with pretty printing enabled, and no JavaTimeModule
|
||||
json should include(""" "instant" : {""")
|
||||
json should include(""" "nanos" : "75000000",""")
|
||||
json should include(""" "seconds" : "1559907792"""")
|
||||
json should include(""" "nanos" : 75000000,""")
|
||||
json should include(""" "seconds" : 1559907792""")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -120,13 +120,14 @@ object SerializationDocSpec {
|
|||
#//#date-time
|
||||
akka.serialization.jackson.serialization-features {
|
||||
WRITE_DATES_AS_TIMESTAMPS = on
|
||||
WRITE_DURATIONS_AS_TIMESTAMPS = on
|
||||
}
|
||||
#//#date-time
|
||||
"""
|
||||
|
||||
val configWhitelist = """
|
||||
#//#whitelist-class-prefix
|
||||
akka.serialization.jackson.whitelist-class-prefix =
|
||||
akka.serialization.jackson.whitelist-class-prefix =
|
||||
["com.myservice.event.OrderAdded", "com.myservice.command"]
|
||||
#//#whitelist-class-prefix
|
||||
"""
|
||||
|
|
@ -144,7 +145,7 @@ class SerializationDocSpec
|
|||
"jdoc.akka.serialization.jackson.v2c.ItemAdded" = "jdoc.akka.serialization.jackson.v2c.ItemAddedMigration"
|
||||
"jdoc.akka.serialization.jackson.v2a.Customer" = "jdoc.akka.serialization.jackson.v2a.CustomerMigration"
|
||||
"jdoc.akka.serialization.jackson.v1.OrderAdded" = "jdoc.akka.serialization.jackson.v2a.OrderPlacedMigration"
|
||||
|
||||
|
||||
# migrations for Scala classes
|
||||
"doc.akka.serialization.jackson.v2b.ItemAdded" = "doc.akka.serialization.jackson.v2b.ItemAddedMigration"
|
||||
"doc.akka.serialization.jackson.v2c.ItemAdded" = "doc.akka.serialization.jackson.v2c.ItemAddedMigration"
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ object Dependencies {
|
|||
// needs to be inline with the aeron version
|
||||
val agronaVersion = "1.0.7"
|
||||
val nettyVersion = "3.10.6.Final"
|
||||
val jacksonVersion = "2.9.10"
|
||||
val jacksonDatabindVersion = "2.9.10"
|
||||
val jacksonVersion = "2.10.0"
|
||||
val jacksonDatabindVersion = "2.10.0"
|
||||
val protobufJavaVersion = "3.9.2"
|
||||
val logbackVersion = "1.2.3"
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue