使用 Jackson 属性累积状态作为序列化的副产品
Using a Jackson attribute to accumulate state as a byproduct of serialization
这是我的场景:
- 我有一个来自各种 类 的 POJO 的深层组合树。我需要编写一个实用程序来动态处理这棵树,而无需深入了解 class/composition 结构
- 我的 POJO 中的某些属性使用自定义注释
@PIIData("phone-number")
进行注释,声明 属性 可能包含 PII,以及可选的 PII 类型(例如 phone 号码)
- 作为序列化根对象的副产品,我想根据 JSON 路径
累积 PII 位置的注册表
所需的数据结构:
path
type
household.primaryEmail
email-address
household.members[0].cellNumber
phone-number
household.members[0].firstName
first-name
household.members[1].cellNumber
phone-number
我不关心使用的具体 pathing/location 语言(JSON 指针,Json 路径)。
我可以通过反思和维护我自己的路径来实现这一点,但感觉就像我应该能够用 Jackson 做的事情,因为它已经在进行遍历了。我很确定使用 Jackson 的属性功能是附加我将累积数据结构的对象的正确方法。但是,我想不出一种在运行时获取路径的方法。这是我当前的 Scala 尝试(hackily?)建立在一个过滤器之上,该过滤器通过一个 mixin 应用于所有对象:
object Test {
@JsonFilter("pii")
class PiiMixin {
}
class PiiAccumulator {
val state = mutable.ArrayBuffer[String]()
def accumulate(test: String): Unit = state += test
}
def main(args: Array[String]): Unit = {
val filter = new SimpleBeanPropertyFilter() {
override def serializeAsField(pojo: Any, jgen: JsonGenerator, provider: SerializerProvider, writer: PropertyWriter): Unit = {
if (writer.getAnnotation(classOf[PiiData]) != null) {
provider.getAttribute("pii-accumulator").asInstanceOf[PiiAccumulator].accumulate(writer.getFullName.toString)
}
super.serializeAsField(pojo, jgen, provider, writer)
}
override def include(writer: BeanPropertyWriter): Boolean = true
override def include(writer: PropertyWriter): Boolean = true
}
val provider = new SimpleFilterProvider().addFilter("pii", filter)
val mapper = new ObjectMapper()
mapper.addMixIn(classOf[Object], classOf[PiiMixin])
val accum = new PiiAccumulator()
mapper.writer(provider)
.withAttributes("pii-accumulator", accum)
.writeValueAsString(null) // Pass in any arbitrary object here
}
}
此代码使我能够动态缓冲包含 PII 的 属性 名称列表,但我不知道如何在生成的 JSON 文档中获取它们的位置。也许 Jackson 架构以某种方式阻止了在运行时知道这一点。有没有其他地方我可以挂钩做这样的事情,也许在转换为 JsonNode 时?
谢谢!
好的,找到了。您可以在序列化期间通过 JsonGenerator.getOutputContext.pathAsPointer() 访问递归 path/location。因此,将我上面的代码更改为以下内容:
if (writer.getAnnotation(classOf[PIIData]) != null) {
provider.getAttribute("pii").asInstanceOf[PiiAccumulator]
.accumulate(jgen.getOutputContext.pathAsPointer().toString + "/" + writer.getName)
}
我能够动态缓冲生成的 JSON 文档中的特殊位置列表,以进行进一步的动态处理。
这是我的场景:
- 我有一个来自各种 类 的 POJO 的深层组合树。我需要编写一个实用程序来动态处理这棵树,而无需深入了解 class/composition 结构
- 我的 POJO 中的某些属性使用自定义注释
@PIIData("phone-number")
进行注释,声明 属性 可能包含 PII,以及可选的 PII 类型(例如 phone 号码) - 作为序列化根对象的副产品,我想根据 JSON 路径 累积 PII 位置的注册表
所需的数据结构:
path | type |
---|---|
household.primaryEmail | email-address |
household.members[0].cellNumber | phone-number |
household.members[0].firstName | first-name |
household.members[1].cellNumber | phone-number |
我不关心使用的具体 pathing/location 语言(JSON 指针,Json 路径)。
我可以通过反思和维护我自己的路径来实现这一点,但感觉就像我应该能够用 Jackson 做的事情,因为它已经在进行遍历了。我很确定使用 Jackson 的属性功能是附加我将累积数据结构的对象的正确方法。但是,我想不出一种在运行时获取路径的方法。这是我当前的 Scala 尝试(hackily?)建立在一个过滤器之上,该过滤器通过一个 mixin 应用于所有对象:
object Test {
@JsonFilter("pii")
class PiiMixin {
}
class PiiAccumulator {
val state = mutable.ArrayBuffer[String]()
def accumulate(test: String): Unit = state += test
}
def main(args: Array[String]): Unit = {
val filter = new SimpleBeanPropertyFilter() {
override def serializeAsField(pojo: Any, jgen: JsonGenerator, provider: SerializerProvider, writer: PropertyWriter): Unit = {
if (writer.getAnnotation(classOf[PiiData]) != null) {
provider.getAttribute("pii-accumulator").asInstanceOf[PiiAccumulator].accumulate(writer.getFullName.toString)
}
super.serializeAsField(pojo, jgen, provider, writer)
}
override def include(writer: BeanPropertyWriter): Boolean = true
override def include(writer: PropertyWriter): Boolean = true
}
val provider = new SimpleFilterProvider().addFilter("pii", filter)
val mapper = new ObjectMapper()
mapper.addMixIn(classOf[Object], classOf[PiiMixin])
val accum = new PiiAccumulator()
mapper.writer(provider)
.withAttributes("pii-accumulator", accum)
.writeValueAsString(null) // Pass in any arbitrary object here
}
}
此代码使我能够动态缓冲包含 PII 的 属性 名称列表,但我不知道如何在生成的 JSON 文档中获取它们的位置。也许 Jackson 架构以某种方式阻止了在运行时知道这一点。有没有其他地方我可以挂钩做这样的事情,也许在转换为 JsonNode 时?
谢谢!
好的,找到了。您可以在序列化期间通过 JsonGenerator.getOutputContext.pathAsPointer() 访问递归 path/location。因此,将我上面的代码更改为以下内容:
if (writer.getAnnotation(classOf[PIIData]) != null) {
provider.getAttribute("pii").asInstanceOf[PiiAccumulator]
.accumulate(jgen.getOutputContext.pathAsPointer().toString + "/" + writer.getName)
}
我能够动态缓冲生成的 JSON 文档中的特殊位置列表,以进行进一步的动态处理。