【问题标题】:Jackson deserialize single item into list杰克逊将单个项目反序列化为列表
【发布时间】:2016-05-14 01:34:51
【问题描述】:

我正在尝试使用一项服务,该服务为我提供了一个实体,该实体具有一个字段,它是一个数组。

{
  "id": "23233",
  "items": [
    {
      "name": "item 1"
    },
    {
      "name": "item 2"
    }
  ]
}

但是当数组包含单个项目时,返回项目本身,而不是一个元素的数组。

{
  "id": "43567",
  "items": {
      "name": "item only"
    }
}

在这种情况下,Jackson 无法转换为我的 Java 对象。

public class ResponseItem {

   private String id;
   private List<Item> items;

   //Getters and setters...
}

有没有直接的解决方案?

【问题讨论】:

  • 该服务的行为似乎很奇怪。有机会告诉提供商解决这个问题吗?
  • @Thomas 不幸的是没有。这是一家拥有数百名用户的大型 SaaS 提供商,而我只是其中之一。
  • 我已经为 JSON:API 添加了标签,因为这是可能发现这种令人讨厌的行为的上下文。

标签: java json jackson json-api


【解决方案1】:

@JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) 似乎不适用于@JsonValue

这意味着如果您想将其中任何一个反序列化为列表,它将不起作用:

{
  "key": true
}
[
  {
    "key": true
  },
  {
    "key": false
  }
]

作为一种解决方案,可以像这样创建自定义反序列化器:

@JsonDeserialize(using = CustomDeserializer.class)
public record ListHolder(List<Item> items) {}

public class CustomDeserializer extends JsonDeserializer<ListHolder> {

    @Override
    public ListHolder deserialize(JsonParser parser, DeserializationContext context) throws IOException {
        List<Item> items;

        if (parser.isExpectedStartArrayToken()) {
            items = parser.readValueAs(new TypeReference<>() {});
        } else {
            items = List.of(parser.readValueAs(Item.class));
        }

        return new ListHolder(items);
    }
}

【讨论】:

    【解决方案2】:

    我认为注释更适合代码。

    @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
    private List<Item> items;
    

    【讨论】:

    • 如果这不起作用并且您正在使用 lombok:您必须将此字段设为非final 并使用@NoArgsContructor。它不适用于final 字段。
    • 不幸的是,它似乎不适用于@JsonValue
    【解决方案3】:

    您不是第一个提出这个问题的人。好像很老了。

    看完这个问题,可以使用DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY

    查看文档: http://fasterxml.github.io/jackson-databind/javadoc/2.5/com/fasterxml/jackson/databind/DeserializationFeature.html#ACCEPT_SINGLE_VALUE_AS_ARRAY

    您需要将此杰克逊功能添加到您的对象映射器中。

    希望对你有帮助。

    【讨论】:

      【解决方案4】:

      可以使用自定义的 JsonDeserializer 来解决这个问题。

      例如

      class CustomDeserializer extends JsonDeserializer<List<Item>> {
      
          @Override
          public List<Item> deserialize(JsonParser jsonParser, DeserializationContext context)
                  throws IOException, JsonProcessingException {
      
              JsonNode node = jsonParser.readValueAsTree();
      
              List<Item> items = new ArrayList<>();
              ObjectMapper mapper = new ObjectMapper();
      
              if (node.size() == 1) {
                  Item item = mapper.readValue(node.toString(), Item.class);
                  items.add(item);
      
              } else {
                  for (int i = 0; i < node.size(); i++) {
                      Item item = mapper.readValue(node.get(i).toString(), Item.class);
                      items.add(item);
                  }
              }
      
              return items;
          }
      
      }
      

      你需要告诉杰克逊用它来反序列化items,比如:

      @JsonDeserialize(using = CustomDeserializer.class)
      private List<Item> items;
      

      之后它将起作用。快乐编码:)

      【讨论】:

      • 这很糟糕。不适用于您可能希望将单个值作为多个级别的数组的深层结构。使用单独的映射器以默认方式反序列化整个 List 分支。每次 List 反序列化都会实例化额外的映射器。
      猜你喜欢
      • 2013-05-20
      • 2015-04-07
      • 1970-01-01
      • 2013-11-04
      • 2017-11-09
      • 2012-11-19
      • 2016-10-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多