jackson 将自定义运行时逻辑应用于SpringBoot @RestController响应中的JSON序列化

3z6pesqy  于 2022-11-09  发布在  Spring
关注(0)|答案(1)|浏览(268)

我们正在构建一个应用程序,它可能会从我们的简化对象模型中生成数百个唯一的JSON有效负载结构,我们希望避免向Java代码库中添加数百个POJO(每个唯一的有效负载结构一个)。
我们构建了一种方法来构建、解析负载结构的简单字符串规范,并将其覆盖到POJO中,然后遍历其树以匹配任何@JsonProperty字段。

String spec = """
  firstName
  lastName
  addresses.line1
  addresses.city
  children.firstName
  children.schedule.bedtime
"""

这将在运行时覆盖在Person POJO上,它将遍历并获取指定的字段和数组。尽管PersonChildAddress 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"
      ]
    ]
  }
  • 请原谅我...似乎只有一个迪士尼角色有孩子(Ariel)*

目前,我们只测试了我们的Spec和POJO的遍历,以找到Spec的适用之处,但是我们还不能弄清楚如何将其连接到JSON序列化过程中。
我所读到的关于Jackson/JSON/Spring序列化器和反序列化器的所有内容似乎都只接受1个输入--POJO。有没有办法使用使用2个输入(POJO + Spec)的自定义序列化器,其中Spec在运行时被标识(从而消除了为每个Spec创建一个“ Package 器”POJO和/或序列化器的需要)?
为了使事情变得更有挑战性,我们希望简化我们的@RestController方法,以简单地包括我们的新注解@APIMessageSpec(<details go here>)以及@ResponseBody注解,然后让Spring调用我们的自定义序列化过程,传入POJO和我们的@APIMessageSpec的细节(或者我们可以将@ResponseBody子类化,以参数化其参数中的Spec信息,这样我们就不需要2个注解了)。
提前感谢!Michael

bjp0bcyl

bjp0bcyl1#

我想出了一个解决办法后不久,张贴这一点!
我们所有的数据/对象模型POJO在它们的祖先中共享一个超类BaseModelEntity(我们在其中放置了一些公共字段,如UUID、上次更新用户/时间戳等)。
我们为自定义序列化规则创建了ModelJsonSerializer extends JsonSerializer<BaseModelEntity>
我们挑选了一个带有@Autowired ObjectMapper objectMapper的bean来添加以下方法:

@PostConstruct
    public void init() {
        SimpleModule simpleModule = new SimpleModule("OurCustomModelJsonModule", new Version(1, 0, 0, null, null, null));
        simpleModule.addSerializer(BaseModelEntity.class, new ModelJsonSerializer());
        objectMapper.registerModule(simpleModule);
    }

我们有一个静态的APISpecRegistry类,我们在其中将所有的规范[它们都是我们自定义的APISpec超类的子类]加载到一个HashMap中。
@RestController的方法上,或者在应用程序中的任何地方,我们用@JsonView(<our_Spec_class>)注解返回类型。
当Jackson识别并开始处理我们的任何数据模型对象时,它成功地找到了ModelJsonSerializer作为我们的数据类型BaseModelEntity的正确对象。
ModelJsonSerializer中,我们有以下方法:

@Override
    public void serialize(BaseModelEntity value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        Class c = serializers.getActiveView();
        if(c != null && APISpec.class.isAssignableFrom(c)) {
            List<Field> fields = ((APISpec)APISpecRegistry.getSpec(c).get()).getFields();
            serialize(value, gen, serializers, fields);
        } else {
            // default to out-of-the-box JSON serializers
        }
    }

注意:APISpec.getFields()是我们将规范从字符串格式解析为要遍历的POJO字段层次结构的地方。
我们通过serializers.getActiveView()获取当前的@JsonView,从该视图中获取APISpec类,并将其用作从APISpecRegistry中查找APISpec对象的键
然后,在我们的自定义4参数serialize方法中,我们将每个字段的API规则与BaseModelEntity示例中的相应字段值进行交叉引用,并忽略不在我们的规范中的任何BaseModelEntity字段。
提示:在底层,我们使用Java反射找到java.lang.reflect.Field,它带有@JsonProperty注解,与APISpec中的字段匹配。

@JsonProperty("firstName")
private String fName;

@JsonProperty("lastName")
private String lName;

@JsonProperty("addresses")
List<Address> addresses;

@JsonProperty("children")
List<Child> kiddos;

太神奇了!

相关问题