Jackson(德国)在HTTP响应和测试用例之间的反序列化不准确。

huangapple go评论85阅读模式
英文:

Jackson (de)serialization not accurate between HTTP responses and test cases

问题

我有一个 Spring Boot 2.3.2.RELEASE 的 WebFlux 应用程序。我有一些 JPA 实体,它们作为 RESTful API 响应的一部分返回。

我使用 @WebFluxTest 的测试检查 HTTP 响应和契约,针对 JSON 架构进行检查。我在这些测试中注入了 Jackson 的 ObjectMapper 和 Spring 的 WebTestClient,以便根据相应/预期的 JSON 架构检查 HTTP 响应。

问题是:如果在应用程序运行时使用任何 HTTP 客户端,没有元素的集合(在 Java 端)会被序列化为 JSON 的空数组 —— 无论理论上是否正确,我都期望是这样的;但是在测试用例中,相同的空集合被序列化为具有 null 值。

所以我想知道为什么会有这种不同?我期望在任何时候都得到相同的 JSON 字符串。

我知道我没有使用不同的 ObjectMapper 设置 —— 或者至少我认为没有。我没有为该类型有任何自定义的 Spring Bean,因此我使用的是 Spring Boot 默认注入的内容,所以无论是运行中的应用程序还是运行测试时都应该是相同的。关于 Jackson,唯一的定制是在 application.yml 文件中的应用程序级别完成的:

spring:
  ...
  jackson:
    property-naming-strategy: LOWER_CAMEL_CASE
    serialization:
      write-date-timestamps-as-nanoseconds: false
      write-dates-as-timestamps: true
  ...

对于 JSON 架构实现,我使用的是 com.networknt:json-schema-validator:1.0.43 库。


以下是一个测试用例的摘录:

@WebFluxTest(controllers = [ExamController::class])
internal class ExamControllerTest {
  @Autowired
  private lateinit var webClient: WebTestClient

  @Autowired
  private lateinit var mapper: ObjectMapper

  @Test
  @Disabled
  fun getById_ValidateResponseAgainstSchema() {
    // 重要提示:如果您在此处进行任何更新,请同时对 getById_WhenRecordExists 进行相应的更改
    webClient.get()
        .uri("/exams/10001030")
        .accept(MediaType.APPLICATION_JSON)
        .exchange()
        .expectStatus().isOk
        .expectBody()
        .consumeWith { result ->
          val schemaFactory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909)
          this::class.java.getResourceAsStream("/json-schemas/exam/exam-details.json").use {
            Assertions.assertThat(schemaFactory.getSchema(it).validate(mapper.readTree(result.responseBody))).isEmpty()
          }
        }
  }

  // ...
}

测试数据是在使用 Testcontainers 中的 Docker 容器中创建/填充的。

英文:

I have a Spring Boot 2.3.2.RELEASE WebFlux application. I have some JPA entities that are returned as part of the RESTful API responses.

The tests that I have (using @WebFluxTest) check the HTTP response(s) and contract — against a JSON schema. I'm injecting a Jackson's ObjecMapper and a Spring WebTestClient in those tests to check the HTTP response(s) against the corresponding/expected JSON schema.

The problem is: if I use any HTTP client when the application is running, the collections (on the Java side) with no elements are serialized into JSON as empty arrays — that's what I would expect regardless of whether that's right or wrong theoretically; but on the test cases, the same empty collections are being serialized with null values.

So I wonder why would that be different? I would expect the same JSON string at any point.

I know I'm not using different ObjectMapper settings — or that's what I think. I don't have any custom Spring beans for that type, so I'm using whatever Spring Boot injects by default, so it must be the same for the running application as well as when running the tests. The only customization for Jackson is done at the application level in the application.yml:

spring:
  ...
  jackson:
    property-naming-strategy: LOWER_CAMEL_CASE
    serialization:
      write-date-timestamps-as-nanoseconds: false
      write-dates-as-timestamps: true
  ...

> I'm using the com.networknt:json-schema-validator:1.0.43 library for the JSON schema implementation.


Excerpt of one of the test cases:

@WebFluxTest(controllers = [ExamController::class])
internal class ExamControllerTest {
  @Autowired
  private lateinit var webClient: WebTestClient

  @Autowired
  private lateinit var mapper: ObjectMapper

  @Test
  @Disabled
  fun getById_ValidateResponseAgainstSchema() {
    // IMPORTANT: If you update anything here, make the corresponding changes also to getById_WhenRecordExists
    webClient.get()
        .uri("/exams/10001030")
        .accept(MediaType.APPLICATION_JSON)
        .exchange()
        .expectStatus().isOk
        .expectBody()
        .consumeWith { result ->
          val schemaFactory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909)
          this::class.java.getResourceAsStream("/json-schemas/exam/exam-details.json").use {
            Assertions.assertThat(schemaFactory.getSchema(it).validate(mapper.readTree(result.responseBody))).isEmpty()
          }
        }
  }

  // ...
}

The test data is created/seeded in a Docker container using Testcontainers.

答案1

得分: 1

禁用 DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT,这样 Jackson 就不会将空数组反序列化为 null 值。

英文:

Disable the DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT, so that Jackson doesn‘t deserialize empty arrays as null values

huangapple
  • 本文由 发表于 2020年10月1日 00:54:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/64142276.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定