json Jackson反序列化为Record with List遇到SetterlessProperty问题

oknrviil  于 2023-05-19  发布在  其他
关注(0)|答案(1)|浏览(179)

问题:如何使用简洁/惯用的现代Java从接收到的JSON中捕获(可选)List字段media_links_txt,将其作为null或其实际值?

功能愿望是:

  • 一些属性具有显式的@JsonGetter@JsonSetter字段名,因为Record同时用作反序列化目标和响应模型(API客户端要求不同的字段名作为传入JSON)
  • optionalStuff字段是…well可选地存在于反序列化流中

代码如下:

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。这让我很困惑。。

  • 记录一开始没有setter,所以它尝试使用构造函数,我目前认为这是好的和理想的(它可以被分配一个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')

  • 等等...只有String的记录上没有String参数构造函数?我相当肯定应该有,但我可能错过了一些东西,并希望学习这样!
    问题:如何使用简洁/惯用的现代Java从接收到的JSON中捕获(可选)List字段media_links_txt,作为null或其实际值?

使用的版本为:

  • jackson-databind 2.13.5
  • Spring框架5.3.27(通过 Spring Boot 启动器2.7.11)

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

dohp0rv5

dohp0rv51#

JsonSetter s中的value参数需要与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) {
}

相关问题