Spring Cloud Stream:Spring Boot 3.x:JsonProperty、JsonIgnoreProperties 在消息转换器中与 Jackson 的行为异常

问题描述 投票:0回答:1

我目前正在开发 Spring Boot 应用程序(版本 3.0.6)并使用 Spring Cloud(版本 2022.0.2)。我有两个不同的端点(“/mvc”和“/message”),它们使用两个不同的请求对象(DummyMessage1DummyMessage2),我在使用 Jackson 的 JsonProperty 和 JsonIgnoreProperties 注释时遇到了一些问题。

在这两种情况下,我都希望从响应中抑制numbers并获得tokens作为响应。

我已经使用 JsonIgnoreProperties 来抑制响应中的numbers

@JsonIgnoreProperties(ignoreUnknown = true, value = {"numbers"}, allowSetters = true)

/mvc
端点,一切都按预期工作。我正在为此端点使用
DummyMessage1
请求对象。
numbers
字段按预期被抑制响应。此外,
tokens
字段在 JSON 响应中成功返回并且不为空,因为它包含来自请求的令牌列表。

问题出现在

/message
端点,它使用
DummyMessage2
向kafka发布消息。
DummyMessage2
DummyMessage1
相同。

当我尝试将 JSON 发布到 Kafka 主题时,抛出了异常:

可以找到完整的堆栈跟踪here

java.lang.ClassCastException: class [B cannot be cast to class com.example.marshaller.model.DummyMessage2 ([B is in module java.base of loader 'bootstrap'; com.example.marshaller.model.DummyMessage2 is in unnamed module of loader 'app')
    at org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry$FunctionInvocationWrapper.invokeConsumer(SimpleFunctionRegistry.java:990) ~[spring-cloud-function-context-4.0.2.jar:4.0.2]

这里是请求对象:

DummyMessage1

@EqualsAndHashCode(callSuper = false)
@Getter
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true, value = {"numbers"}, allowSetters = true)
public class DummyMessage1 extends BaseRequest {

    private String numbers;

    //@JsonProperty(access = JsonProperty.Access.READ_ONLY)
    public List<String> getTokens() {
        if (StringUtils.isBlank(this.numbers)) return Collections.emptyList();
        return List.of(this.numbers.split(";\\s*"));
    }
}

DummyMessage2

@EqualsAndHashCode(callSuper = false)
@Getter
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true, value = {"numbers"}, allowSetters = true)
public class DummyMessage2 extends BaseRequest {

    private String numbers;

    @JsonProperty(access = JsonProperty.Access.READ_ONLY)
    public List<String> getTokens() {
        if (StringUtils.isBlank(this.numbers)) return Collections.emptyList();
        return List.of(this.numbers.split(";\\s*"));
    }
}

接下来,为了解决异常,我在 DummyMessage2getTokens() 方法上添加了注释 @JsonProperty(access = JsonProperty.Access.READ_ONLY)。然而,这并没有产生预期的结果。现在发布到 Kafka 的 JSON 没有包含所需的数字字段,而且 tokens 字段为空,这不是预期的。

为什么会这样?我希望 tokens 字段被填充,因为它在 /mvc 端点中。任何帮助或见解将不胜感激。

这里是重现问题的存储库:https://github.com/cricketbackground/marshaller

请注意,出于安全原因,kafka 代理和 kafka zk 节点有意未在回购中设置。

请求正文:


{
    "numbers":"12345; 3982934823; 3248923492834; 324923434"
}

使用方法:请看这里

java spring-boot jackson spring-cloud-stream spring-cloud-stream-binder-kafka
1个回答
0
投票

这种行为的原因在于您的 DummyMessage 配置。您有

String numbers
字段,但对于 Jackson,请忽略它。相反,您提供
getToken
方法。所以 Jackson 认为你的对象只有
List<String> tokens
字段,当你解组它时不理解你的字节。

让我们仔细看看:

当你调用你的

/mvc
端点时,只有编组发生,所以它有效。您有以下流程:

Request body (1)-> DummyMessage1 (2)-> Response body

{
  "numbers": "6469344427; 2017586291"
}

(1) ====> 

DummyMessage1(
  String numbers = "6469344427; 2017586291"
)

(2) ====> 

{
  "tokens": [
    "6469344427",
    "2017586291"
  ]
}

但是在你的

/message
端点,流程更长:

Request body (1)-> DummyMessage2 (2)-> Json (3)-> DummyMessage2

在第 3 步,在您的

DummyMessageConsumerService
中,spring 尝试从 JSON 中解组
DummyMessage2
并且不能这样做,因为 Json 只有字段“tokens”,但对象中没有这样的字段。

解决这个问题:

  1. @JsonProperty(access = JsonProperty.Access.READ_ONLY)
    方法中删除
    getTokens()

  2. 将以下设置器添加到您的

    DummyMessage2

public void setTokens(List<String> tokens) {
    this.numbers = String.join(";", tokens);
}

或者您可以使用

DummyMessage3
而不是
List<String> tokens
创建另一个 DTO 对象,例如
String numbers
。在那种情况下,流量将是
(1) -> DummyMessage2 (2)-> Json (3)-> DummyMessage3
.

© www.soinside.com 2019 - 2024. All rights reserved.