我观察到 Spring 反序列化和使用简单 ObjectMapper 的自定义反序列化之间的差异。
application.yml:
props:
array:
- name: foo1
bar: bar1
- name: foo2
bar: bar2
在上面的属性文件中,当我们将“props”反序列化为通用 Map(而不是用户定义的对象)时,我们观察到:
LinkedHashMap
LinkedHashMap
的实例,这是不正确的如果我们用
new ObjectMapper(new YAMLFactory())
读取上面的属性,那么我们就会得到预期的行为,其中 array 是 ArrayList
的实例。
如果我们将属性映射到真实对象,并将 List
作为 array
属性的目标,我们也会得到预期的结果。
我无法理解Spring反序列化机制,看起来Spring没有使用ObjectMapper bean来反序列化(我覆盖了Bean,它没有改变任何东西)。
使用/重写哪个类来更改反序列化? 我查看了 Spring 核心转换器,但没有成功......
顺便说一句,在我看来,这看起来像是一个 Spring bug。 (春季 5.3.21,Java 17)
我不知道重写 Spring 反序列化的好解决方案。
但是我在
@ConfigurationProperties
类中为我的通用地图道具创建了 setter 方法。
在这种方法中,如果映射中的所有键都是序列“0”,“1”,“2”,...,我手动将所需的映射转换为数组。
如果您可以拥有内部复杂结构,请不要忘记使用递归。
我也观察到了这一点,并真诚地相信这是配置加载器中的某种错误,因为它为来自文件的配置与通过
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");