json - jackson :反序列化后如何进行验证?

标签 json kotlin jackson introspection jackson-modules

我正在使用 Jackson with Kotlin序列化和反序列化各种 Kotlin 数据类es。

我的 data classes 是非常简单的对象,可以使用标准的 Jackson ObjectMapper 轻松序列化和反序列化,except 我想确保一些后验证作为反序列化的一部分完成。例如,我想确保 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 (!!!),使用那个阅读类(class),然后进行后期验证。

把我带到这个……

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" }
    }""");
}

确实有效;它为测试用例中不需要的 someField 值抛出 RuntimeException

这似乎是错误的

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

它有一种非常糟糕的代码味道。我正在创建一个全新的 ObjectMapper,我必须重新注册模块以进行时间戳反序列化。

问题

感觉必须一种更明智、更少冗余的方法来反序列化 Kotlin 数据类(或 Java POJO),然后对对象,而无需完全重写反序列化机制。

有吗?我想不出一种简单的方法来从反序列化器中推迟 ObjectMapper 的默认反序列化行为。

最佳答案

已请求用于做事验证的通用工具:

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

虽然还没有实现。据推测,这将不仅限于基于注释,而且将允许可插入的后处理(加上无论如何都可以通过子类化 AnnotationIntrospector 来“伪造”注释)。

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

关于json - jackson :反序列化后如何进行验证?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64760102/

相关文章:

javascript - 从外部api获取数据

javascript - 如何使用jquery从 Angular 服务获取json数据

android - 我一次可以观察多少个 Stateflow?

java - 在 Web 服务中接受多种 JSON 格式

java - 是否有一种通用的方法来反序列化 Jackson 中的单值对象(没有自定义反序列化器)

android - 使用 robospice google http 客户端在 Post 中发送 JSON

jquery - 使用 YQL for Facebook 获取 JSON 数据

android - IllegalStateException : parcel. readString() 不能为空

android - 协程流程 : Not sure how to convert a Cursor to this method's return type

java - Spring 启动 : Different ObjectMapper instances for Request and Response