将自定义运行时逻辑应用于 SpringBoot @RestController 响应中的 JSON 序列化
我们正在构建一个应用程序,它可以从简化的对象模型中生成数百个独特的 JSON 有效负载结构,并且我们希望避免向 Java 代码库添加数百个 POJO(每个独特的有效负载结构 1 个)。
我们构建了一种方法来构建、解析有效负载结构的简单字符串规范并将其覆盖到 POJO,并遍历其树以匹配任何 @JsonProperty
字段。下面是一个示例规范:
String spec = """
firstName
lastName
addresses.line1
addresses.city
children.firstName
children.schedule.bedtime
"""
这将在运行时覆盖在 Person
POJO 上,并且它将遍历并获取指定的字段和数组。尽管 Person
、Child
和 Address
POJO 中有更多字段,但此特定消息请求应该只填充我们指定的字段。其 JSON 输出应该是:
{
"firstName": "Mickey",
"lastName": "Mouse",
"addresses": [
{
"line1": "123 Disneyland Way",
"city": "Anaheim"
},
{
"line2": "345 Disneyworld Drive",
"city": "Orlando"
}
],
"children": [
"firstName": "Junior",
"schedule": [
"bedtime": "20:00"
]
]
}
请耐心等待...似乎只有 1 个迪士尼角色有孩子 (Ariel)
目前,我们仅测试了我们的规范和 POJO 的遍历以查找位置该规范适用。但我们不知道如何将其连接到 JSON 序列化过程中。
我读过的关于 Jackson/JSON/Spring 序列化器和反序列化器的所有内容似乎只需要 1 个输入 - POJO。有没有一种方法可以使用使用 2 个输入(POJO + Spec)的自定义序列化器,其中 Spec 在运行时被识别(从而消除了为每个 Spec 创建“包装器”POJO 和/或序列化器的需要)?
为了让事情变得更具挑战性,我们希望简化我们的 @RestController
方法,以仅包含我们的新注释 @APIMessageSpec(
)
以及 >@ResponseBody
注释,然后让 Spring 调用我们的自定义序列化过程,传入 POJO 和我们的 @APIMessageSpec
的详细信息(或者可能让我们子类化 @ResponseBody
> 到在其参数中参数化 Spec 信息,这样我们就不需要 2 个注释)?提前致谢! 迈克尔
We are building an app that may produce hundreds of unique JSON payload structures from our streamlined object model and we want to avoid adding hundreds of POJOs to our Java codebase (1 per unique payload structure).
We built a way to build, parse and overlay a simple string spec of the payload structure to a POJO and to walk its tree to match any @JsonProperty
fields. Here's an example spec:
String spec = """
firstName
lastName
addresses.line1
addresses.city
children.firstName
children.schedule.bedtime
"""
This would be overlaid on a Person
POJO at runtime and it would traverse and get the fields and arrays specified. Though the Person
, Child
and Address
POJOs have plenty more fields in them, this specific message request should only populate the ones we specified. The JSON output of this should be:
{
"firstName": "Mickey",
"lastName": "Mouse",
"addresses": [
{
"line1": "123 Disneyland Way",
"city": "Anaheim"
},
{
"line2": "345 Disneyworld Drive",
"city": "Orlando"
}
],
"children": [
"firstName": "Junior",
"schedule": [
"bedtime": "20:00"
]
]
}
bear with me here...seems like only 1 Disney character has any children (Ariel)
Currently, we have only tested our Specs and the traversal of POJOs to find where the Spec applies. But we cannot figure out how to wire this into the JSON serialization process.
Everything I've read about Jackson/JSON/Spring serializers and deserializers seem to only take 1 input - the POJO. Is there a way to use custom serializers that use 2 inputs (POJO + Spec), where Spec is identified at runtime (thus eliminating the need to create a "wrapper" POJO per and/or serializer per Spec)?
To make things even more challenging, we want to simplify our @RestController
methods to simply include our new annotation, @APIMessageSpec(<details go here>)
alongside the @ResponseBody
annotation and then have Spring invoke our custom serialization process, passing in the POJO and details of our @APIMessageSpec
(or possibly for us to subclass @ResponseBody
to parameterize the Spec info in its arguments, so that we don't need 2 annotations)?.
Thanks in advance!
Michael
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我在发布此内容后不久就找到了解决方案!
我们所有的数据/对象模型 POJO 在其祖先中共享一个超类,
BaseModelEntity
(我们在其中放置一些常见字段,如 UUID、上次更新用户/时间戳等)。我们创建了
ModelJsonSerializer extends JsonSerializer< ;BaseModelEntity>
用于我们的自定义序列化规则。我们选择了一个带有
@Autowired ObjectMapper objectMapper
的 bean 来添加以下方法:我们有一个静态
APISpecRegistry
类,我们在其中加载所有规范 [这些规范都是我们自定义的子类HashMap 中的 >APISpec
超类]。在我们的
@RestController
方法上,或者我们应用程序中的任何地方,我们用@JsonView()
注释返回类型。为了处理我们的任何数据模型对象,它成功找到
ModelJsonSerializer
作为我们数据类型BaseModelEntity
的正确对象。在 ModelJsonSerializer 中,我们有以下方法:
注意:APISpec.getFields() 是我们将规范从字符串格式解析为要遍历的 POJO 字段层次结构的地方。
我们通过
serializers.getActiveView()
获取当前的@JsonView
,从该视图获取我们的APISpec
类,并将其用作查找的关键 然后,在我们的自定义 4 参数
serialize
方法中,我们将每个字段的 API 规则与相应的字段交叉引用字段值来自我们的BaseModelEntity
实例,并忽略任何不在我们规范中的BaseModelEntity
字段。提示:在幕后,我们使用 Java 反射来查找带有与
中的字段匹配的
。因此,您可以想象,我们的@JsonProperty
注释的java.lang.reflect.Field
。 API规范Person
对象中有以下字段:这太神奇了!
I figured out a solution not long after posting this!
All of our data/object model POJOs share a superclass in their ancestry,
BaseModelEntity
(where we put some common fields like UUID, last update user/timestamp, etc.)We created
ModelJsonSerializer extends JsonSerializer<BaseModelEntity>
for our custom serialization rules.We picked a bean with
@Autowired ObjectMapper objectMapper
to add the following method:We have a static
APISpecRegistry
class where we load all of our specs [which all subclass our customAPISpec
superclass] in a HashMap.On our
@RestController
's methods, or anywhere in our application for that matter, we annotate the return type with@JsonView(<our_Spec_class>)
When Jackson identifies and starts to process any of our data model objects it successfully finds
ModelJsonSerializer
as the correct one for our data typeBaseModelEntity
.In
ModelJsonSerializer
, we have the following method:NOTE:
APISpec.getFields()
is where we parsed our spec from string format to a hierarchy of POJO fields to traverse.We get the current
@JsonView
viaserializers.getActiveView()
, get ourAPISpec
class from that view and use that as the key to lookup theAPISpec
object from ourAPISpecRegistry
Then, in our custom 4-argument
serialize
method, we cross-reference our API rules for each field with the corresponding field values from ourBaseModelEntity
instance and ignore anyBaseModelEntity
field that's not in our Spec.TIP: Underneath the covers, we use Java reflection to find
java.lang.reflect.Field
s with a@JsonProperty
annotation that matches up to the field in ourAPISpec
. So, you can image, we have the following fields in ourPerson
object:It's MAGIC!