【问题标题】:JSON Jackson parse different keys into same fieldJSON Jackson 将不同的键解析到同一个字段中
【发布时间】:2013-11-03 02:07:14
【问题描述】:

我有一个 POJO,它有一个字段:

public class Media {
 private Asset asset;
}

将 json 响应解析为该资产 POJO 时,一切正常。但是,该资产附带的密钥略有不同。它可以是:

  @JsonProperty("cover_asset")

  @JsonProperty("asset")

有没有办法注释 POJO 以识别这种情况并反序列化到同一字段中。他们不可能出现在同一个响应中。

【问题讨论】:

标签: java json jackson


【解决方案1】:

好吧,由于您只关心反序列化,因此2.9 中引入的@JsonAlias 非常适合这种情况。你可以这样做:

@JsonAlias({"cover_asset", "asset"})
private Asset asset;

@JsonAlias docs:

可用于定义一个或多个替代名称的注解 对于一个属性,在反序列化过程中被接受作为替代 官方名字。 POJO期间也暴露了别名信息 内省,但在主要的序列化过程中没有影响 名称总是被使用。

注意:如果您正在使用它们,请确保更新所有相关的依赖项(annotationscoredatabind)。在没有其他人的情况下仅更新 annotations 会引发运行时错误。

【讨论】:

  • 不知道在定义别名后实际字段名称是否仍然被接受。如果是这样,即使@JsonAlias({"cover_asset"}) private Asset asset 也应该可以工作。
  • 这是一个更好更简洁的解决方案,但仅在 2.9 中可用
  • 支持默认名称,此时只需指定"cover_asset"即可。
【解决方案2】:

更简洁地说,我建议为此使用两个单独的 @JsonSetter 注释。这是一个工作示例。这意味着您的 java 类将只有一个用于属性的 getter 方法,而不是两个。您还可以将不希望向 Media 客户端公开的 setter 设为私有,并以特殊方式处理其中一个 json 键。

import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.databind.ObjectMapper;

@SuppressWarnings("unused")
public class Media {

    private Asset asset;

    @JsonGetter("asset")
    public Asset getAsset() {
        return asset;
    }

    @JsonSetter("asset")
    public void setAsset(Asset asset) {
        this.asset = asset;
    }

    @JsonSetter("cover_asset")
    private void setMediaAsset(Asset asset) {
        if (this.asset == null) {
            setAsset(asset);
        }
    }


    private static class Asset {
        @JsonProperty("foo")
        private String foo;
    }

    public static void main(String[] args) throws Exception {
        String withAsset = "{'asset': {'foo':'bar'}}";
        String withCoverAsset = "{'cover_asset': {'foo':'bar'}}";

        ObjectMapper mapper = new ObjectMapper();
        Media mediaFromAsset = mapper.readValue(withAsset.replace('\'','"'), Media.class);
        Media mediaFromCoverAsset = mapper.readValue(withCoverAsset.replace('\'','"'), Media.class);

        System.out.println(mediaFromAsset.asset.foo.equals(mediaFromCoverAsset.asset.foo));

    }
}

【讨论】:

  • 我这里有个问题——为什么另一个setter方法(setMediaAsset())是私有的?
  • @nareshgoty 使 Media 的公共 API 保持简单。没有充分的理由为同一个属性公开两个 setter 方法。私有方法只存在于杰克逊调用。
  • @nareshgoty 基本上以某种方式,您只会在响应 json 中看到一个我认为您想要实现的属性。在这种情况下,您将获得 "asset":""
【解决方案3】:

我建议对引用同一个 POJO 字段的两个属性名称使用 getter/setter。

public class Media {
    private Asset asset;

    @JsonProperty("cover_asset")
    public Asset getCoverAsset() {
      return asset;
    }

    public void setCoverAsset(Asset asset) {
      this.asset= asset;
    }

    @JsonProperty("asset")
    public Asset getAsset() {
      return asset;
    }

    public void setAsset(Asset asset) {
      this.asset= asset;
    }
}

另请参阅我对可能重复问题的回答: Different names of JSON property during serialization and deserialization

【讨论】:

  • 我看到了,但我试图完全避免这种情况。实际上它只是“资产”时作为两种不同的东西处理。然而,它确实让我离我想要实现的目标更近了一步。
【解决方案4】:

Vikas 与 JsonAlias 的精彩回答。

只是补充一点,您也可以从这两个世界中受益(JsonProperty&Alias)[自杰克逊 2.9 起]:

@JsonProperty("cover_asset")    
@JsonAlias({"asset", "cover_asset","amazing_asset"})
private Asset asset; 

Reference.

【讨论】:

  • 在这个例子中你需要"asset" 别名吗?
  • 效果很好,谢谢!请注意,您不必在别名中重复名称。 @JsonAlias({"asset","amazing_asset"}) 足以覆盖他们三个("asset", "cover_asset","amazing_asset")。
猜你喜欢
  • 2019-11-25
  • 2022-01-17
  • 1970-01-01
  • 1970-01-01
  • 2021-04-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多