微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

杰克逊:反序列化后如何验证?

如何解决杰克逊:反序列化后如何验证?

我正在使用Jackson with Kotlin来序列化和反序列化各种Kotlin data class es。

我的data class是非常简单的对象,可以使用标准的Jackson ObjectMapper来轻松地进行序列化和反序列化,除了,我想确保进行一些后验证作为反序列化的一部分。例如,我要确保Thing.someField >= 0

data class InnerThing(
    val foo: String
);

data class Thing(
    val someField: Int,// must not be negative
    val innerThing: InnerThing
);

实现此目的的最简单方法似乎是针对所讨论的类重写StdDeserializer

class ThingDeserializer : StdDeserializer<Thing>(Thing::class.java) {
    override fun deserialize(p: JsonParser,ctxt: DeserializationContext): Thing {
        // Defer to the superclass to do the actual deserialization
        // DOES NOT WORK because StdDeserializer.deserialize() is abstract
        val t: Thing = super.deserialize(p,ctxt);

        if (thing.someField < 0) {
            throw RuntimeException("someField value must be >= 0");
        }

        return t;
    }
}

…,但是不起作用,因为StdDeserializer.deserialize()是抽象的。这导致我进入this related question。从自定义反序列化器中似乎很难遵循认的反序列化行为。

据我所知,遵循认反序列化行为的最直接方法是完全创建一个 单独的ObjectMapper(!!!),使用 that 读取类,然后进行后验证。

引领我前进……

class ThingDeserializer : StdDeserializer<Thing>(Thing::class.java) {

    // Create a whole separate ObjectMapper that doesn't override
    // the deserializer for Thing:
    private val defaultMapper = jacksonObjectMapper();

    init {
        // Need to reregister modules for things like ISO8601
        // timestamp parsing:
        defaultMapper.registerModule(JavaTimeModule());
    }    

    override fun deserialize(p: JsonParser,ctxt: DeserializationContext): Thing {
        // Use the non-overridden ObjectMapper to deserialize the object:
        val t: Thing = defaultMapper.readValue(p,Thing::class.java);

        // ... and then do validation afterwards
        if (thing.someField < 0) {
            throw RuntimeException("someField value must be >= 0");
        }

        return t;
    }
}

fun main(args: Array<String>) {
    val mapper = jacksonObjectMapper();
    mapper.registerModule(JavaTimeModule());
    mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,false);

    mapper.registerModule(
        SimpleModule().apply {
          addDeserializer(Thing::class.java,ThingDeserializer()) 
        }
    );

    val test: Thing = mapper.readValue("""{
      "someField": -1,"innerThing": { "foo": "bar" }
    }""");
}

确实有效;它会在测试用例中抛出RuntimeException的不必要值someField

这似乎是错误

以上真的是进行此验证的合理方法吗?

它具有非常不好的代码气味。我正在创建一个全新的ObjectMapper,并且必须为时间戳反序列化之类的东西重新注册模块。

问题

感觉必须 一种更精简,更少冗余的方法来反序列化Kotlin data class(或Java POJO),然后对对象的内容进行后验证,而无需完全重写反序列化机制。

在吗?我想不出一种简单的方法来从反序列化器内遵循ObjectMapper认反序列化行为。

解决方法

已请求用于验证服务的通用工具:

https://github.com/FasterXML/jackson-databind/issues/2045

尽管尚未实施。大概这不仅限于基于注释,还可以进行可插拔的后处理(加上始终可以通过对AnnotationIntrospector进行子类化来始终“伪造”注释)。

除此之外(尚不可用),对于POJO,如果可以通过它传递所有属性(构造函数或工厂方法),则@JsonCreator的使用效果很好。但是,对于验证第三方类型而言,效果不是很好。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。