Configure visibility (#29797)

This commit is contained in:
Ignasi Marimon-Clos 2020-11-10 09:41:44 +01:00 committed by GitHub
parent 0d6282cffe
commit 24d01a101a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 108 additions and 1 deletions

View file

@ -126,6 +126,20 @@ akka.serialization.jackson {
# }
json-write-features {}
# Configuration of the JsonFactory Visibility.
# See com.fasterxml.jackson.annotation.PropertyAccessor
# and com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility
# Enum values. For example, to serialize only public fields
# overwrite the default values with:
#
# visibility {
# FIELD = PUBLIC_ONLY
# }
# Default: all fields (including private and protected) are serialized.
visibility {
FIELD = ANY
}
# Deprecated, use `allowed-class-prefix` instead
whitelist-class-prefix = []

View file

@ -179,6 +179,25 @@ object JacksonObjectMapperProvider extends ExtensionId[JacksonObjectMapperProvid
}
}
private def configureObjectVisibility(
bindingName: String,
objectMapper: ObjectMapper,
objectMapperFactory: JacksonObjectMapperFactory,
config: Config): Unit = {
val configuredVisibility: immutable.Seq[(PropertyAccessor, JsonAutoDetect.Visibility)] =
configPairs(config, "visibility").map {
case (property, visibility) =>
PropertyAccessor.valueOf(property) -> JsonAutoDetect.Visibility.valueOf(visibility)
}
val visibility =
objectMapperFactory.overrideConfiguredVisibility(bindingName, configuredVisibility)
visibility.foreach {
case (property, visibility) => objectMapper.setVisibility(property, visibility)
}
}
private def configureObjectMapperModules(
bindingName: String,
objectMapper: ObjectMapper,
@ -244,8 +263,9 @@ object JacksonObjectMapperProvider extends ExtensionId[JacksonObjectMapperProvid
configureObjectMapperFeatures(bindingName, mapper, objectMapperFactory, config)
configureObjectMapperModules(bindingName, mapper, objectMapperFactory, config, dynamicAccess, log)
configureObjectVisibility(bindingName, mapper, objectMapperFactory, config)
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)
mapper
}
private def isModuleEnabled(fqcn: String, dynamicAccess: DynamicAccess): Boolean =
@ -264,6 +284,12 @@ object JacksonObjectMapperProvider extends ExtensionId[JacksonObjectMapperProvid
val cfg = config.getConfig(section)
cfg.root.keySet().asScala.map(key => key -> cfg.getBoolean(key)).toList
}
private def configPairs(config: Config, section: String): immutable.Seq[(String, String)] = {
import akka.util.ccompat.JavaConverters._
val cfg = config.getConfig(section)
cfg.root.keySet().asScala.map(key => key -> cfg.getString(key)).toList
}
}
/**
@ -534,4 +560,20 @@ class JacksonObjectMapperFactory {
@unused bindingName: String,
configuredFeatures: immutable.Seq[(JsonWriteFeature, Boolean)]): immutable.Seq[(JsonWriteFeature, Boolean)] =
configuredFeatures
/**
* Visibility settings used to configure the `JsonFactoryBuilder` that, if provided, will later be used to create
* an `ObjectMapper`. These settings can be amended programmatically by overriding this method and return the values
* that are to be applied to the `JsonFactoryBuilder`.
*
* @param bindingName bindingName name of this `ObjectMapper`
* @param configuredFeatures the list of `PropertyAccessor`/`JsonAutoDetect.Visibility` that were configured in
* `akka.serialization.jackson.visibility`
*/
def overrideConfiguredVisibility(
@unused bindingName: String,
configuredFeatures: immutable.Seq[(PropertyAccessor, JsonAutoDetect.Visibility)])
: immutable.Seq[(PropertyAccessor, JsonAutoDetect.Visibility)] =
configuredFeatures
}

View file

@ -528,4 +528,15 @@ public interface JavaTestMessages {
return name != null ? name.hashCode() : 0;
}
}
// A class with non-public fields
final class ClassWithVisibility {
public final String publicField = "1234";
final String defaultField = "abcd";
protected final String protectedField = "vwxyz";
private final String privateField = "ABCD";
@JsonCreator
public ClassWithVisibility() {}
}
}

View file

@ -411,6 +411,46 @@ class JacksonJsonSerializerSpec extends JacksonSerializerSpec("jackson-json") {
"be possible to create custom ObjectMapper" in {
pending
}
"be possible to tune the visibility at ObjectMapper level (FIELD, PUBLIC_ONLY)" in {
withSystem("""
akka.actor {
serialization-bindings {
"akka.serialization.jackson.JavaTestMessages$ClassWithVisibility" = jackson-json
}
}
akka.serialization.jackson.visibility {
FIELD = PUBLIC_ONLY
}
""") { sys =>
val msg = new ClassWithVisibility();
val json = serializeToJsonString(msg, sys)
val expected = """{"publicField":"1234"}"""
json should ===(expected)
}
}
// This test ensures the default behavior in Akka 2.6 series
// (that is "FIELD = ANY") stays consistent
"be possible to tune the visibility at ObjectMapper level (Akka default)" in {
withSystem("""
akka.actor {
serialization-bindings {
"akka.serialization.jackson.JavaTestMessages$ClassWithVisibility" = jackson-json
}
}
akka.serialization.jackson.visibility {
## No overrides
}
""") { sys =>
val msg = new ClassWithVisibility();
val json = serializeToJsonString(msg, sys)
val expected =
"""{"publicField":"1234","defaultField":"abcd","protectedField":"vwxyz","privateField":"ABCD"}""".stripMargin
json should ===(expected)
}
}
}
"JacksonJsonSerializer with Scala message classes" must {