Spring 将 YAML 反序列化为 LinkedHashMap 而不是 ArrayList

问题描述 投票:0回答:2

我观察到 Spring 反序列化和使用简单 ObjectMapper 的自定义反序列化之间的差异。

application.yml:

props:
 array:
 - name: foo1
   bar: bar1
 - name: foo2
   bar: bar2

在上面的属性文件中,当我们将“props”反序列化为通用 Map(而不是用户定义的对象)时,我们观察到:

  • props 成为
    LinkedHashMap
  • 的实例
  • 数组成为
    LinkedHashMap
    的实例,这是不正确的

如果我们用

new ObjectMapper(new YAMLFactory())
读取上面的属性,那么我们就会得到预期的行为,其中 array 是
ArrayList
的实例。 如果我们将属性映射到真实对象,并将
List
作为
array
属性的目标,我们也会得到预期的结果。

我无法理解Spring反序列化机制,看起来Spring没有使用ObjectMapper bean来反序列化(我覆盖了Bean,它没有改变任何东西)。

使用/重写哪个类来更改反序列化? 我查看了 Spring 核心转换器,但没有成功......

顺便说一句,在我看来,这看起来像是一个 Spring bug。 (春季 5.3.21,Java 17)

spring spring-boot properties jackson
2个回答
0
投票

我不知道重写 Spring 反序列化的好解决方案。

但是我在

@ConfigurationProperties
类中为我的通用地图道具创建了 setter 方法。 在这种方法中,如果映射中的所有键都是序列“0”,“1”,“2”,...,我手动将所需的映射转换为数组。 如果您可以拥有内部复杂结构,请不要忘记使用递归。


0
投票

我也观察到了这一点,并真诚地相信这是配置加载器中的某种错误,因为它为来自文件的配置与通过

Yaml.load(...)
方法手动加载的配置提供了不同的结果。

如果不清楚,示例中给出的 Yaml 具有以下 JSON 等效项:

{
  "props": {
    "array": [
      { "name": "foo1", "bar": "bar1" },
      { "name": "foo2", "bar": "bar2" }
    ],
  }
}

如果将 Yaml 直接加载到

Object
,则生成的类型树将是:

result: HashMap
> "props": HashMap
  > "array": ArrayList
    > [0]: HashMap
    > [1]: HashMap

但是,据观察,当将其作为 Spring Boot 配置加载时,它具有以下等效的 JSON:

{
  "props": {
    "array": {
      "0": { "name": "foo1", "bar": "bar1" },
      "1": { "name": "foo2", "bar": "bar2" }
    },
  }
}

并创建以下 Java 对象:

result: HashMap
> "props": HashMap
  > "array": HashMap
    > "0": HashMap
    > "1": HashMap

如果您的代码依赖于弱定义、非类型映射定义(这些定义可能源自配置文件或外部运行时提供的源),那么这会特别笨重。

为了解决这个问题,我使用这个简单的实用函数:

public ArrayList getAsArrayList(Map<String,Object> source, String key) {

  // Simple example, no null result checks!
  Object entries = source.get(key);
  if(entry instanceof ArrayList) {
    return (ArrayList)entry;
  }

  // Type safety up to you!
  Map<String,Object> entryMap = (Map<String,Object>)entries;

  return new ArrayList(IntStream.range(0, entryMap.size)
    .mapToObj(n -> entryMap.get(String.valueOf(n))).toList());
}

对于所提供的示例,它将使用如下所示的内容:

Map<String,Object> props = // ... loaded by config some way
ArrayList array = getAsArrayList(props, "array");

© www.soinside.com 2019 - 2024. All rights reserved.