杰克逊(Jackson)对龙目岛增强类的反序列化:为什么它起作用,为什么不起作用?

时间:2018-10-08 18:36:16

标签: jackson lombok

背景:我在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

以下文章介绍了龙目岛-杰克逊组合的工作原理,但此处不起作用。我完全感到困惑,这是令人难以置信的荒谬情况,其中(不必要的)简化会带来极高的复杂性。但我想了解一下,因为以后我可以再次遇到这种情况。

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);
        }
    }
}

有人能看到为什么两个几乎相同的TO类的行为不同吗?

1 个答案:

答案 0 :(得分:2)

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

lombok.anyConstructor.addConstructorProperties=true

您的lombok.config。对于手动构造函数,您还可以简单地添加@JsonProperty
(请注意,Jackson不会自动使用构建器;您必须使用@JsonDeserialize@JsonPOJOBuilder明确地告诉Jackson。)

TestTO_pureJackson之所以有效,是因为@JsonProperty在运行时可用,并且由Jackson用来确定映射。

Pair之所以有效,是因为有一个可用的构造函数:Jackson不必猜测哪个参数属于哪个字段,因为只有一个。请注意,这仅适用于Stringintlongboolean单参构造函数。
Lombok不会生成任何其他构造函数(此处为two-args构造函数)(如果已有的话)(请参见documentation of @Data),因此这是类中唯一的构造函数。