使用Jackson重复JSON字段

56

我在使用Spring与Jackson进行JSON(反)序列化,但是遇到了某些情况下字段出现两次的问题。

我有一个抽象类:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "mimeType")
@JsonSubTypes({
    @JsonSubTypes.Type(value = ImageBookmarkJsonModel.class, name = "image/jpeg"),
    @JsonSubTypes.Type(value = EpubBookmarkJsonModel.class, name = "application/epub+zip")
})
public abstract class AbstractBookmarkJsonModel extends AbstractJsonModel {
    protected String mimeType;
    // Removed other fields for brevity

    public String getMimeType() {
        return mimeType;
    }

    public void setMimeType(String mimeType) {
        this.mimeType = mimeType;
    }

    @Override
    public String toString() {
        ObjectMapper mapper = new ObjectMapper();

        try {
            return mapper.writeValueAsString(this);
        } catch (IOException e) {
            throw new IllegalStateException("Cannot convert object of type " + this.getClass().toString() + " to JSON", e);
        }
    }
}

一个具体的类继承了抽象类:

public class EpubBookmarkJsonModel extends AbstractBookmarkJsonModel {
    private static final long serialVersionUID = 1L;
    // Removed other fields for brevity

    public EpubBookmarkJsonModel() {
        this.mimeType = "application/epub+zip";
    }
}

问题是当我序列化这个JSON时,我得到了一个重复的mimeType字段:

{
  "mimeType": "application/epub+zip",
  "mimeType": "application/epub+zip",
  "userId": 24,
  "acid": "ACID-000000000029087",
  "added": "2013-08-14T12:02:17Z",
  "epubBookmarkId": 34,
  "cfi": "epubcfi(/6/4!/2/68)",
  "context": "CONTEXT"
}
我尝试使用之前的回答,参考以下链接:链接1链接2来使用@JsonAutoDetect注解,指定只使用类上的字段,并在ObjectMapper上设置相同的注解,但这并没有解决问题。
注解:
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
        setterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.NONE,
        isGetterVisibility = JsonAutoDetect.Visibility.NONE)

对象映射器:

    ObjectMapper mapper = new ObjectMapper();
    mapper.getSerializationConfig().getDefaultVisibilityChecker()
            .withFieldVisibility(JsonAutoDetect.Visibility.ANY)
            .withGetterVisibility(JsonAutoDetect.Visibility.NONE)
            .withSetterVisibility(JsonAutoDetect.Visibility.NONE)
            .withCreatorVisibility(JsonAutoDetect.Visibility.NONE);

我不知道这是否有帮助,但是如果您从AbstractBookmarkJsonModel中删除注释@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "mimeType"),那么您的JSON中将只有一个mimeType - Katona
5个回答

74

我通过在@ JsonTypeInfo注解中使用JsonTypeInfo.As.EXISTING_PROPERTY来解决了这个问题。

该项目是开源的,可以在此处检查:ANS.java


5
使用@JsonTypeInfo的visible=true选项是一个好的解决方法(如果不使用,序列化后该属性将为null)。 - c-toesca
3
ANs.java 出了问题。 - Santosh Joshi
1
看起来他们把主分支切换到了node.js。这里是我最初链接的旧ANS.java代码的链接: https://github.com/washingtonpost/ans-schema/blob/0.3.0/src/main/java/com/washingtonpost/arc/ans/v0_3/model/ANS.java - Thomas Vaughan
这不可能是一个解决方案,因为解决方案不依赖于如此容易断开的链接。另外,为什么你不编辑答案而是在评论中发布更正后的链接呢?请在你的回答中包含与你的链接相关的部分。我本来可以自己做的,但你链接的文件根本没有包含字符串“existing”的任何实例,所以。 - Fulluphigh
FYI,因为他的代码不再可见,它会像这样:@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "mimeType") - Rick Hanton
显示剩余2条评论

12

我遇到了与重复输出相同的问题。我找到了一个解决方案,不需要另一个属性,并允许我不删除原始属性。首先,我将JsonTypeInfo的visible标志设置为true。然后,在属性声明和getter(但不是setter)中添加了JsonIgnore注释。至此,JSON的输出正确,类型属性只有一个键。

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "mimeType")
@JsonSubTypes({
    @JsonSubTypes.Type(value = ImageBookmarkJsonModel.class, name = "image/jpeg"),
    @JsonSubTypes.Type(value = EpubBookmarkJsonModel.class, name = "application/epub+zip")
})
public abstract class AbstractBookmarkJsonModel extends AbstractJsonModel {
    @JsonIgnore
    @JsonProperty("mimeType")
    protected String mimeType;

    @JsonIgnore
    @JsonProperty("mimeType")
    public String getMimeType() {
        return mimeType;
    }

    @JsonProperty("mimeType")
    public void setMimeType(String mimeType) {
        this.mimeType = mimeType;
    }

}

需要注意的是,这是使用 fasterxml jackson jackson-databind 2.1.1 版本的情况。

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.1.1</version>
</dependency>

9
这种行为是由放置在AbstractBookmarkJsonModel类上的注解引起的。
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "mimeType")
@JsonSubTypes({
    @JsonSubTypes.Type(value = ImageBookmarkJsonModel.class, name = "image/jpeg"),
    @JsonSubTypes.Type(value = EpubBookmarkJsonModel.class, name = "application/epub+zip")
})
@JsonTypeInfo告诉Jackson将逻辑类型名称(JsonTypeInfo.Id.NAME)序列化为属性(JsonTypeInfo.As.PROPERTY),属性名为mimeTypeproperty="mimeType")。使用@JsonSubTypes.Type将逻辑名称application/epub+zip分配给EpubBookmarkJsonModel
在序列化方面,Jackson将逻辑名称序列化为属性mimeType = "application/epub+zip",然后是对象的属性,包括其中一个mimeType,它恰好与逻辑名称application/epub+zip具有相同的值(在构造函数中分配)。
我认为在@JsonTypeInfo注释中将mimeType更改为objectType甚至更好的是删除mimeType字段,因为Jackson将通过类型信息序列化来处理它。

1
+1 这是正确的答案,Jackson 在序列化的 JSON 输出中添加了一个额外的属性来标识子类型。在您的情况下,您尝试使用现有的属性,您不应该这样做。我知道您不能从类中删除 mimeType 属性,因为我认为您需要它。只需使用另一个子类型信息属性名称,如 subtypeName,并将子类型名称用作 "ImageBookmark" 和 "EpubBookmark" 等内容。这是 Jackson 的多态类型处理的预期用法。 - Frank Olschewski
3
我不认为这是一个充分的答案,因为这个解决方案需要Jackson的用户改变他们的数据模型来适应Jackson工作的一些内部细节(即更改子类型字段的名称)。此外,在高层次上,我不知道为什么任何人都想要输出中有重复的键,所以就我而言,这是一个错误!现有的EXISTING_PROPERTY解决方案对我们有效。 - Charles Capps
@CharlesCapps 嗯,也许你是对的,但我不认为mimeType是数据模型的一部分,它更像是一个_类型标志_,仅出于JSON序列化而存在。如果不必序列化模型,我认为它不会存在。 - Katona
是的,我只是一般性地说。在我们的情况下,我们经常会在数据模型中有一些枚举,用于指示某个东西的类型。我们不应该因为Jackson的一些奇怪的内部细节导致重复字段被发出而改变我们的字段名称。此外,如果子类实现了基类的抽象方法,我认为这是一个逻辑字段。 - Charles Capps

1

我遇到了同样的问题。我们正在使用Lombok,我通过使用@ JsonProperty访问解决了这个问题:

  @Getter
  @Setter
  @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) // Prevents duplication when serializing to JSON (subtype discriminator property)
  private RuleType ruleType;

我喜欢这个解决方案,因为:

  • 不依赖于其他库
  • 不需要更改我的数据模型
  • 可以使用Lombok(减少混乱)

是的,需要visible=true,以便属性可以被填充。在我们的情况下,它是一个带有一些行为分派的枚举,所以我们肯定需要它来实现我们的目的,并且能够将其用于Json子类型区分也很棒。

@JsonTypeInfo(
  use = JsonTypeInfo.Id.NAME,
  property = "ruleType",
  visible = true)

1

这个问题可能只出现在旧版本的jacson库中,通过将你的@JsonProperty(value = "Your_CUSTOM_Name")从字段移动到getter方法中即可简单解决。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接