【问题标题】:Jackson deserialization of lombok enhanced class: Why it does work and why it doesn't work?lombok增强类的Jackson反序列化:为什么有效,为什么无效?
【发布时间】:2019-03-13 11:27:07
【问题描述】:

背景:我在 spring-admin 项目中发现“功能失调”代码:“无法构造注册实例(没有创建者,如默认构造,存在)”。所以我写了自定义反序列化器并报告了这个问题。但报告被拒绝,因为它据称有效。重新测试后,它现在似乎可以工作了。没有意义。所以我想知道为什么该代码有效。

但这就是问题所在。当我编写类似的测试类时,它在我的项目中不起作用。即使我从字面上获取“正在工作的”注册类的代码,并在自己的项目中尝试它,也根本不会反序列化。然后,使用几乎相同的类,它可以工作。这没有任何意义。

https://github.com/codecentric/spring-boot-admin/blob/master/spring-boot-admin-server/src/main/java/de/codecentric/boot/admin/server/domain/values/Registration.java

以下帖子解释了 lombok-jackson 组合的工作原理,但在这里不起作用。我完全困惑,这是令人难以置信的荒谬情况,(不必要的)简化会产生极好的复杂性。不过我想了解一下,以后还能遇到这种情况。

Jackson Deserialization Fails because of non-default constructor created by lombok

所以有一些易于使用的东西:这里我们有漂亮且工作的纯杰克逊:

public class TestTO_pureJackson {
    private final String a;
    private final String b;

    @JsonCreator
    private TestTO_pureJackson(@JsonProperty("a") String a, @JsonProperty("b") String b) {
        this.a = a;
        this.b = b;
    }
}

在这里,我们没有处理 lombok 等效项(即使我删除了一个字段,因此它与后一个示例“相同”):

@lombok.Data
public class TestTO {
    private final String a;
    private final String b;

    @lombok.Builder(builderClassName = "Builder")
    private TestTO(String a, String b) {
        this.a = a;
        this.b = b;
    }

    public static TestTO.Builder create(String a) {
        return builder().a(a);
    }
}

我们正在尝试反序列化:

{"a": "a", "b": "b"}

谁能理解幕后的魔力,并帮助我理解这里出了什么问题?

<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <version>1.18.2</version>
  <scope>provided</scope>
</dependency>

为了让它变得更加荒谬(你真的看到 TestTO 有什么显着的不同吗???),下面的代码可以工作:

@lombok.Data
public class Pair {
    private final String left;
    private final String right;

    @lombok.Builder(builderClassName = "Builder")
    private Pair(String pairId) {
        left = pairId.substring(0, 3).toUpperCase(Locale.US);
        right = pairId.substring(3).toUpperCase(Locale.US);
    }
}

主要方法:

public class PairTest {

    public static final String DATA = "[\"btcusd\",\"ltcusd\",\"ltcbtc\"]";

    public static void main(String[] args) throws IOException {
        ObjectMapper objectMapper = new ObjectMapper();
        Pair[] pairs = objectMapper.readValue(DATA, Pair[].class);

        for (Pair pair : pairs) {
            System.out.println(pair);
        }
    }
}

谁能看到,为什么 2 个几乎相同的 TO 类的行为不同?

【问题讨论】:

  • 在任何项目中是否有lombok.config?如果有,它的内容是什么?
  • yes: config.stopBubbling=true lombok.accessors.chain=true 但我担心这可能是由于违反 java bean 规范引起的,所以我也尝试了没有 lombok.accessors.chain=true ,它的行为也是一样的。

标签: jackson lombok


【解决方案1】:

TestTO 不起作用,因为没有 Jackson 可以使用的构造函数。它不能使用两个参数的构造函数,因为它不知道哪个 JSON 字段应该用于哪个参数(因为在编译过程中会删除参数名称)。 对于 lombok 生成的构造函数,您可以通过建议 Lombok 生成 @ConstructorProperties 注释来解决这个问题。只需添加

lombok.anyConstructor.addConstructorProperties=true

到您的lombok.config。在您使用手动构造函数的情况下,您也可以简单地添加 @JsonPropertys。
(请注意,Jackson 不会自动使用构建器;您必须使用 @JsonDeserialize 和 @JsonPOJOBuilder 明确告诉 Jackson。)

TestTO_pureJackson 有效,因为@JsonProperty 在运行时可用,Jackson 使用它来确定映射。

Pair 有效,因为有一个可用的构造函数:Jackson 不必猜测哪个参数属于哪个字段,因为只有一个。请注意,这仅适用于 String、int、long 或 boolean 单参数构造函数。
如果已经有一个构造函数(参见documentation of @Data),Lombok 不会生成任何额外的构造函数(这里:双参数构造函数),所以这是类中唯一的构造函数。

【讨论】:

  • 感谢您的回答,我的意思是非常非常,它清除了很多东西。一切正常,我得到了所有这些。除了一件事。好的,...addConstructorProperties 必须设置为 true,只有在此之后,只有 lombok 创建的构造函数才能与 jackson 一起使用。好的,我知道了。但是如果注册 @Data 类已经明确声明了 6 个 arg 构造函数,因此 lombok 不会创建另一个构造函数。并且明确声明一个没有杰克逊注释。然而它对他们有效。不适合我。完整项目:github.com/alfonz19/lombok-mystery检查类 ExplicitlyCreatedConstructor
  • 我自己检查了一下:ExplicitlyCreatedConstructor not 对我也有用。这就是我所期望的。也许他们在 Eclipse 中安装了旧的 Lombok 版本,导致生成不同的代码?
猜你喜欢
  • 2021-05-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-23
  • 2014-06-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多