Jackson中的自定义反序列化器和验证

本文讨论了在Jackson中自定义JSON反序列化器的重要性,特别是在输入验证方面。作者通过一个实例展示了如何编写自定义反序列化器,并分享了一次因未验证输入导致服务器性能问题和内存溢出的教训,强调了验证对于防止解析器进入不良状态至关重要。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

tl; dr:将输入验证添加到Jackson中的自定义json解串器很重要。

RHQ中,我们在几个地方使用了Json解析-直接在as7 / Wildfly插件中,或者通过RESTEasy 2.3.5间接在REST-api中使用,已经很繁重了。

现在,我们有一个bean Link ,看起来像:

public class Link {
   String rel;
   String href;
}

序列化的标准方法是

{ "rel":"edit", "href":"http://acme.org" }

由于我们需要其他格式,因此我编写了一个自定义序列化程序并将其附加到类上。

@JsonSerialize(using = LinkSerializer.class)
@JsonDeserialize(using = LinkDeserializer.class)
@Produces({"application/json","application/xml"})
public class Link {

    private String rel;
    private String href;

此自定义格式如下:

{
    "edit": {
        "href": "http://acme.org"
    }
}

由于客户端也可以发送链接,因此需要进行一些自定义反序列化。 解串器的第一个片段看起来像这样,效果很好:

public class LinkDeserializer extends JsonDeserializer>{

    @Override
    public Link deserialize(JsonParser jp,
        DeserializationContext ctxt) throws IOException
    {
        String tmp = jp.getText(); // {
        jp.nextToken(); // skip over { to the rel
        String rel = jp.getText();
        jp.nextToken(); // skip over  {
        […]

        Link link = new Link(rel,href);

        return link;
    }

现在,几天前发生的事情是,在某些测试中,我正在发送数据,而我们的服务器严重崩溃。 内存使用量增加,垃圾收集器花费了大量cpu时间,并且该调用最终因OutOfMemoryException终止。

经过一番调查,我发现客户端不是以我们的特殊格式发送Link对象,而是以我最初显示的原始格式发送。 进一步的研究表明,实际上, LinkDeserializer正在消耗流中的令牌,如上所示,然后还吞没了输入中的后续令牌。 因此,当它返回时,整个解析器的状态很差,然后尝试复制大数组,直到我们看到OOME。

得到这个之后,我更改了实现以添加验证并在无效输入时尽早提供援助,以使解析器在无效输入时不会陷入不良状态:

public Link deserialize(JsonParser jp,
        DeserializationContext ctxt) throws IOException
    {

        String tmp = jp.getText(); // {
        validate(jp, tmp,"{");
        jp.nextToken(); // skip over { to the rel
        String rel = jp.getText();
        validateText(jp, rel);
        jp.nextToken(); // skip over  {
        tmp = jp.getText();
        validate(jp, tmp,"{");
        […]

然后,那些validate*()简单地将令牌与传递的期望值进行比较,并对意外输入抛出Exception:

private void validate(JsonParser jsonParser, String input,
        String expected) throws JsonProcessingException {
        if (!input.equals(expected)) {
            throw new JsonParseException("Unexpected token: " + input,
               jsonParser.getTokenLocation());
        }
    }

验证也许可以进一步改进,但是您可以理解。

参考: Jackson中的Custom Deserializer,以及 JCG合作伙伴 Heiko Rupp在“ 一些要记住的博客”上的验证。

翻译自: https://www.javacodegeeks.com/2013/08/custom-deserializer-in-jackson-and-validation.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值