Jackson 反序列化为带有 List 的 Record 会遇到 SetterlessProperty 问题

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

问题:如何使用简洁/惯用的现代 Java 将接收到的 JSON 中的(可选)列表字段

media_links_txt
捕获为 null 或其实际值?

功能愿望是:

  • 某些属性具有显式
    @JsonGetter
    @JsonSetter
    字段名称,因为 Record 既用作反序列化目标又用作响应模型(API 客户端需要不同的字段名称作为传入 JSON)
  • optionalStuff
    字段......可以选择出现在反序列化流程中

代码如下:

import com.fasterxml.jackson.annotation.*;
import org.springframework.lang.NonNull;

import java.util.List;

@JsonIgnoreProperties(ignoreUnknown = true)
public record TestResponse(@NonNull Response response) {

    @JsonIgnoreProperties(ignoreUnknown = true)
    public record Response(@NonNull List<Doc> docs,
                           @NonNull Integer numFound,
                           @NonNull Integer start) {

        public record Doc(@NonNull String id,
                              @NonNull @JsonGetter("content_t") @JsonSetter("content") String content,
                              @NonNull @JsonGetter("title_t") @JsonSetter("title") String title,
                              @JsonGetter("media_links_txt") @JsonSetter("mediaLinks") List<String> mediaLinks) {
        }
    }
}

当此代码收到如下所示的 JSON 时:

{
  "response" : {
    "docs" : [ {
      "content_t" : "Test content 1",
      "title_t" : "Test title 1",
      "media_links_txt" : [ "d7810883-42de-4280-a286-5bb14e517ce3", "fa60db0a-0e28-4c9c-8092-4eafe57251cd" ],
      "id" : "54870dc265c855c516bcd0a2f93f2267"
    }, {
      "content_t" : "Test content 2",
      "title_t" : "Test content 2",
      "id" : "a4a38aa64427d63aab0c4412eb659586"
    } ],
    "numFound" : 2,
    "start" : 0
}

出现以下错误消息:

Should never call set() on setterless property
并指向
media_links_txt
。这让我很困惑...

  • 记录一开始就没有设置器,所以它尝试使用构造函数,我目前认为这是很好的并且是理想的(可以为它分配一个
    null
    值,
    mediaLinks
    是我认为我写的)
  • 但是它可以毫无问题地构造
    docs
    字段(也是一个 List)?

所以我尝试用下面的代码来欺骗它(也许它需要非默认类型)

import com.fasterxml.jackson.annotation.*;
import org.springframework.lang.NonNull;

import java.util.List;

@JsonIgnoreProperties(ignoreUnknown = true)
public record TestResponse(@NonNull Response response) {

    @JsonIgnoreProperties(ignoreUnknown = true)
    public record Response(@NonNull List<Doc> docs,
                           @NonNull Integer numFound,
                           @NonNull Integer start) {

        public record Doc(@NonNull String id,
                              @NonNull @JsonGetter("content_t") @JsonSetter("content") String content,
                              @NonNull @JsonGetter("title_t") @JsonSetter("title") String title,
                              @JsonGetter("media_links_txt") @JsonSetter("mediaLinks") List<StringClone> mediaLinks) {

            public record StringClone(@NonNull String mediaLinksRetry) {
            }
        }
    }
}

抛出:

Cannot construct instance of StringClone (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('d7810883-42de-4280-a286-5bb14e517ce3')

  • 等等...纯字符串记录上没有字符串参数构造函数?我相当确定应该有,但我可能会遗漏一些东西,并且想学习!

问题:如何使用简洁/惯用的现代 Java 将接收到的 JSON 中的(可选)列表字段

media_links_txt
捕获为 null 或其实际值?

使用的版本是:

  • 杰克逊数据绑定 2.13.5
  • spring框架5.3.27(通过spring boot启动器2.7.11)

我认为https://github.com/FasterXML/jackson-databind/issues/2692可能描述了这个问题,但目前还没有适当的解决方法。

java json jackson deserialization record
2个回答
1
投票

JsonSetter
中的值参数需要与 JSON 中的字段名称匹配。我假设 Jackson 认识到该类型是一条记录,并且如果 ctor 参数名称与 JSON 字段名称对齐,则能够调用构造函数。

即,如果您像这样更改

JsonSetter
注释:

 public record Doc(String id,
                  @JsonGetter("content_t") @JsonSetter("content_t") String content,
                  @JsonGetter("title_t") @JsonSetter("title_t") String title,
                  @JsonGetter("media_links_txt") @JsonSetter("media_links_txt") List<String> mediaLinks) {
}

然后它似乎起作用了,至少对我来说是这样。这可以通过用单个

JsonGetter
注释替换
JsonSetter
/
JsonProperty
对来进一步简化:

public record Doc(String id,
                  @JsonProperty("content_t") String content,
                  @JsonProperty("title_t") String title,
                  @JsonProperty("media_links_txt") List<String> mediaLinks) {
}

0
投票

在记录上配置此注释应该可以使其工作:

@JsonAutoDetect(fieldVisibility = ANY, getterVisibility = ANY, setterVisibility = ANY, creatorVisibility = ANY)
© www.soinside.com 2019 - 2024. All rights reserved.