杰克逊序列化会将一个Java变量输出为两个JSON行。

huangapple go评论76阅读模式
英文:

Jackson serialization outputting two json rows for one java variable

问题

以下是翻译好的内容:

我在将代码从Java 8和Spring Boot 1.X迁移到11和2.X时遇到了一个奇怪的问题。之前,我发现Jackson会将private boolean isAvailable序列化为available: falseisAvailable: false。为了增加更多困惑,Lombok也参与其中,所以我无法看到getter方法,也不清楚是否会引起冲突。以下是Java类的代码:

@Data
public class Availability {

    @JsonProperty("isAvailable")
    private boolean isAvailable;

}

而Jackson对象映射器生成的JSON如下所示:

"Availability" : [{
          "isAvailable" : false,
          "available" : false }]

这是使用Jackson 2.8时的情况。现在使用Jackson 2.10,从ObjectMapper生成的JSON输出中只有isAvailable: false

"Availability" : [{
          "isAvailable" : false }]

我认为只匹配Java变量名是有道理的,但遗憾的是合同中同时存在两个版本,我不想改变合同。有些地方使用了"is",有些地方没有。我知道可以使用自定义序列化器,但不确定如何做以及是否有必要。

英文:

I have a strange problem as I'm migrating code from Java 8 and Sprint Boot 1.X to 11 and 2.X. Previously, I discovered that Jackson was serializing private boolean isAvailable to both available: false and isAvailable: false. To add more confusion, Lombok is along for the ride so I don't have visibility on the getter and if that's causing a conflict. Below is the Java class code:

@Data
public class Availability{

    @JsonProperty("isAvailable")
    private boolean isAvailable;

}

And the reulsting JSON from the Jackson Object mapper was:

"Availability" : [{
          "isAvailable" : false,
          "available" : false }]

This was with Jackson 2.8. Now with Jackson 2.10, I am only getting isAvailable: false as the output in the JSON from the ObjectMapper.

"Availability" : [{
          "isAvailable" : false }]

It makes sense to me that it should match the Java variable name only, but the contract sadly has both versions, and I don't want to change the contract. Some are using the is, and some are not. I know a custom serializer is possible, but not sure how to do that or if it's necessary.

答案1

得分: 2

这是 lombok + 字段的交互。

让我们来看一个假设的类(这里完全没有使用 lombok,或者说 jackson):

class Example {
    public boolean isAvailable() {
        return true;
    }
}

根据 JavaBean 规范,Example 类有一个名为 available 的单一只读布尔类型属性。这是因为任何以 is 开头(然后是一个大写字母)并且返回布尔类型的无参方法被视为访问器,要从方法名中去掉 is 才能得到属性名。

所以这是从这一端来看的。现在让我们从另一端来看,你正在编写一个类,出于某种原因,你决定字段的名称应为 isAvailable。真正的问题是:根据 JavaBean 规范,你希望这个 '属性' 被命名为什么?如果它真的应该被称为 isAvailable,只有一种方法可以实现这一点:

class Example {
    public boolean isIsAvailable() {
        return true;
    }
}

是的,isIsAvailable。看起来很奇怪,但这就是规则所要求的,如果你想让属性本身被称为 isAvailable。Lombok 决定(来源:我是核心贡献者)当你在一个名为 isAvailable 的字段上使用 @Getter 注解时,没有人真正希望有一个名为 isIsAvailable 的方法。

因此,现在你有一个类,在 jackson 看来,它实际上看起来像是:

class Availability {
    @JsonProperty("isAvailable") private boolean isAvailable;

    public boolean isAvailable() { return this.isAvailable; }
}

只是应用了字面规则,没有特殊的智能处理,这意味着 Jackson 会说:好的,这里有 2 个不同的属性,available(通过调用 isAvailable() 获得),以及 isAvailable 字段。对于 beanspec 风格的 getter,会去掉 is/get,但对于字段则不会,尤其是如果你在上面加上了 @JsonProperty("isAvailable")

这就是这一切发生的背景。

一个解决方案可能是将这个方法改成:

@JsonProperty("available")
public boolean getAvailable() { return this.available; }

我认为这会在 JSON 输出中得到两种变体。

英文:

It's lombok + the field, interacting.

Let's take this hypothetical class (no lombok, or for that matter jackson involved at all here):

class Example {
    public boolean isAvailable() {
        return true;
    }
}

As per the beanspec, the Example class has a single read-only bean property of type boolean called available. That's because any no-args method that returns boolean and starts with is (and then a capital), counts as an accessor, and to get to the name of it, strip the is off.

So that's looking at it from that end. Now let's look at it from the other end, you writing a class where for whatever reason you've decided that the field is to be named isAvailable. The real question is: What do you want the 'property' to be named (as per beanspec)? If it really is to be called isAvailable, there's only one way to accomplish that:

class Example {
    public boolean isIsAvailable() {
        return true;
    }
}

Yes, isIsAvailable. Looks daft, but, that's what the rules decree if you want the property itself to called isAvailable. Lombok decided (SOURCE: I am a core contributor) that nobody really wants a method named isIsAvailable when you stick a @Getter annotation on a field named isAvailable.

So, now you have a class that effectively looks like, as far as jackson is concerned:

class Availability {
    @JsonProperty("isAvailable") private boolean isAvailable;

    public boolean isAvailable() { return this.isAvailable; }
}

Using no particular intelligence, just applying the literal rules, means that Jackson says: Okay, there are 2 different properties here, available (obtained by invoking isAvailable()), and the isAvailable field. For beanspec-style getters, is/get is stripped off, for fields it's not, especially if you stick a @JsonProperty("isAvailable") on there.

That's the context for why this is all happening.

A solution could be to make this method:

@JsonProperty("available")
public boolean getAvailable() { return this.available; }

I think that'll get you both variants in your JSON output.

huangapple
  • 本文由 发表于 2020年8月15日 05:32:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/63420258.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定