杰克逊将单个项反序列化为列表

26

我正在尝试使用一个服务,该服务提供了一个实体,其中包含一个字段,该字段是一个数组。

{
  "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
很遗憾,Thomas。这是一个有数百个用户的大型SaaS提供商,而我只是其中之一。 - Javier Alvarez
我已经添加了JSON:API标签,因为这是可能发现这种令人讨厌的行为的上下文。 - chrylis -cautiouslyoptimistic-
4个回答

45

20

我认为注释更适合这段代码。

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

1
如果这个方法不起作用并且您正在使用lombok:您必须将此字段设置为非“final”,并使用@NoArgsContructor。它不适用于“final”字段。 - lilalinux
1
不幸的是,它似乎无法与@JsonValue一起使用。 - Florens

2
一个自定义的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;
    }

}

您需要告诉Jackson使用以下代码对items进行反序列化:

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

之后它就会正常工作。祝你编程愉快 :)


1
这很糟糕。它不能处理深层结构,其中您可能希望在多个级别上将单个值作为数组。使用单独的映射器以默认方式反序列化整个List<Item>分支。每次对List进行反序列化时都会实例化附加映射器。 - Radu Simionescu

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

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