【问题标题】:Jackson and Lombok: MismatchedInputException when Attributes are ObjectsJackson 和 Lombok:当属性是对象时出现 MismatchedInputException
【发布时间】:2020-06-16 09:35:37
【问题描述】:

我有一个如下所示的 Job 类:

@Entity
@Table
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@Data
@NoArgsConstructor
public class Job implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    @Column(nullable = false)
    private String job;

    @ManyToOne
    @JsonIgnoreProperties("jobs")
    private Job next;

以及创造工作的方法:

    @PostMapping("/jobs")
    public ResponseEntity<Job> createJob(@Valid @RequestBody Job job) throws URISyntaxException {
        Job result = jobRepository.save(job);
        return ...
    }

直到这里一切正常。现在将 @AllArgsConstructor 添加到 Job 类会导致 MismatchedInputException。

Bad Request: JSON parse error: Cannot construct instance of `xxx.domain.Job` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `xxx.domain.Job` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator) 

我尝试将 lombok.anyConstructor.addConstructorProperties = true 添加到 lombok.config 文件中,如我找到的可能答案中所述。但这只会导致以下错误:

[INFO] --- maven-compiler-plugin:3.8.0:testCompile (default-testCompile) @ project-name ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 27 source files to /path/target/test-classes
Unknown key 'lombok.anyConstructor.addConstructorProperties' (/path/lombok.config:2)

我尝试使用旧版本的 lombok,因为我在更新 lombok 时发现了一些解决此问题的 github 问题。另外,没有成功(试过1.16.18版)

我还尝试删除private Job next 属性,该属性使代码正常工作而没有任何错误。所以这一定是个问题?

【问题讨论】:

    标签: java json spring jackson lombok


    【解决方案1】:

    总结

    你可能不想要任何形式的@ConstructorProperties。

    由于不幸的原因,密钥lombok.anyConstructor.addConstructorProperties = true 不再可用。正确的密钥是 lombok.anyConstructor.suppressConstructorProperties = false,尽管您可能不想实际应用此配置密钥。

    修复可能是更新您的构建工具以告诉 javac 在类文件中生成参数名称信息。

    你不想要@ConstructorProperties

    @ConstructorProperties 注释的“要点”有两个方面:允许运行时代码(java 代码,使用反射 API)检查构造函数参数的名称,以及如果所有可用的都是编译(类)文件,而不是源(java)文件,则允许编译时工具找出构造函数参数的名称。

    但是,为此,@ConstructorProperties 已过时。如今,大多数编译器配置已更新为默认在类文件中包含参数名称,并且反射 API 已更新,为您提供了一种简单的方法来获取这些。例如:

    public class Example {
        public Example(String a, int b) {}
        public static void main(String[] args) throws Exception {
            System.out.println(Example.class
                .getConstructor(String.class, int.class)
                .getParameters()[1]
                .getName());
        }
    }
    

    然后在命令行上:

    javac Example.java
    java Example
    arg1
    
    javac -parameters Example.java
    java Example
    b
    

    您可能希望在构建中包含上述源文件作为快速检查,以确保您用于构建源文件的任何构建系统都设置了-parameters 选项。它应该打印b,如果没有(如果它打印arg1),请快速搜索如何在编译期间制作maven 或gradle 或任何相关的发出参数名称信息。

    现在杰克逊有机会只使用这些信息(而不是依赖@ConstructorProperties)。我实际上不知道它是否确实如此,但我会假设:这是实现它的“新”方式。

    为什么你不想要它

    ...因为历史证明,Java 社区不能信任它。

    注解一开始是一个简单、清晰的设计(您可以使用它以注解形式传递参数的名称),几乎立刻,整个社区就搞砸了:android 和 GWT 都会拒绝编译您的代码如果这个注释甚至在你的 java 文件中。

    然后,有一段时间,一切都很好,所有可以编译以.java 扩展名结尾的文件中的代码的所有最新版本至少都会默默地忽略这个注释。

    但随后带有模块系统的 java9 出现了,在 oracle 的无限智慧下,他们决定....把这个注解塞进... ...java.desktop 模块???直到今天我完全不知道那里发生了什么,因为这似乎是一个 wildly 不适合它的模块。但是,这确实意味着如果您想在模块化构建中以任何形式使用ConstructorProperties(即根目录中有module-info.java 文件的任何东西),您需要将requires java.desktop; 添加到它,除非这往往意味着一些事情这毫无意义(例如:您的模块无法在无头 JVM 上运行)。

    这有两点:[1] 为什么 lombok 配置和构造函数属性的行为如此混乱,以及 [2] 为什么我建议您不要使用 @ConstructorProperties。

    龙目岛的配置

    如果不管上面的建议你真的想使用ConstructorProperties,在lombok中正确的配置是:

    lombok.anyConstructor.suppressConstructorProperties = false
    

    由于上述历史,lombok 中用于生成@ConstructorProperties 的配置键经历了一些不幸的疯狂之旅。对于这件事给您带来的困难,我深表歉意。 (我是龙目岛的核心贡献者)。

    最初,addConstructorProperties 存在(相对简单得多:始终生成此注解,并且根本没有配置密钥),因为 android 和 GWT 无法处理它的存在,并且据我们估计,更多人使用 android/ GWT+lombok,而不是依赖于生成 @ConstructorProperties 的人,因此我们决定适当的默认行为应该是在生成构造函数时不包含注释。

    然后,当 GWT/Android 解决了这个问题时,力量平衡发生了变化:我们现在意识到“默认生成它”更有可能帮助而不是伤害。我们真的非常希望我们的属性始终“默认为 false”,因此,我们将键重命名为 lombok.anyConstructor.suppressConstructorProperties。

    然后在更高版本的 lombok 中,在放弃“我们希望所有事物都默认为 false”的格言之后,我们将默认行为改回原来的样子:Do NOT generate @987654346 @ 默认。如果您使用模块化构建,这样做会导致代码根本无法编译,这是正确的,因为注释被放置在 java.desktop 模块中。虽然整个 Java 社区继续在很大程度上避免使用拼图,但仍有少数人使用它,这足以改变权力平衡。此外,我们现在很清楚,@ConstructorProperties 作为一种机制注定要失败,使用-parameters 切换是正确的、不那么令人头疼的解决方案。

    我们预计 lombok 的行为不会第三次改变。

    【讨论】:

    • 我一直认为这是相反的,lombok.anyConstructor.addConstructorProperties 是新的配置键,或者至少是最近版本的键。
    • 啊!好吧,这只是一团糟......相关的提交是 2017 年的 github.com/rzwitserloot/lombok/commit/d7c019c07 和 2014 年的 github.com/rzwitserloot/lombok/commit/d41ef4bf8。 - 但是,addConstructorProperties 确实过时了。我发誓 addCP 是旧的...
    • 我添加了 -parameters 选项,它正在正确打印参数的名称,但仍然抛出 MismatchedInputException。
    • 哎呀,那我就不知所措了,对杰克逊有更多了解的人必须插话来帮助你:(
    【解决方案2】:

    我发现了这个 github 问题:https://github.com/meltmedia/jackson-crypto/issues/6

    如那里所述,我通过设置解决了问题:

    jackson:
        mapper:
            INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES: false
    

    Jackson Doc 说:

    功能主要用于帮助与 Lombok 等框架的互操作性 生成 ConstructorProperties 注释但不一定 意味着构造函数应该用作创建者 反序列化。

    【讨论】:

      猜你喜欢
      • 2019-06-24
      • 2019-09-16
      • 2021-01-25
      • 1970-01-01
      • 1970-01-01
      • 2019-09-17
      • 1970-01-01
      • 2021-08-25
      • 2016-08-22
      相关资源
      最近更新 更多