Spring配置中JSON字符串转多层嵌套Map报错的解决方法
Spring 解析${STRING_JSON}占位符时,默认将从环境变量读取到的值当做普通字符串处理,不会自动识别JSON格式内容并反序列化为目标嵌套Map结构,因此绑定到Map<String, Map<String, Map<String, String>>>类型字段时会直接抛出类型转换异常。
根据实际场景选一种实现即可:
方案1:自定义配置绑定转换器(无侵入,全局生效)
注册一个专门用于配置属性绑定的类型转换器,Spring在给BaseImagesProperties赋值时会自动调用该转换器把JSON字符串转为目标Map类型,原有业务代码不需要修改。
代码实现如下:import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.boot.context.properties.ConfigurationPropertiesBinding; import org.springframework.core.convert.converter.Converter; import org.springframework.stereotype.Component; import java.util.Map; @Component @ConfigurationPropertiesBinding public class JsonToNestedMapConverter implements Converter<String, Map<String, Map<String, Map<String, String>>>> { private final ObjectMapper objectMapper; // 注入Spring全局ObjectMapper,复用项目已有的序列化、反序列化配置 public JsonToNestedMapConverter(ObjectMapper objectMapper) { this.objectMapper = objectMapper; } @Override public Map<String, Map<String, Map<String, String>>> convert(String jsonSource) { try { return objectMapper.readValue(jsonSource, new TypeReference<>() {}); } catch (Exception e) { throw new IllegalArgumentException("配置项JSON格式非法,转嵌套Map失败", e); } } }注册完成后直接重启应用即可正常完成配置绑定,不需要修改原有
BaseImagesProperties的逻辑。方案2:类内手动解析(轻量,无需额外注册组件)
如果不想新增全局转换器,可以先把配置值接收为字符串,在类初始化阶段手动完成JSON解析。
修改原有配置类代码如下:import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.annotation.PostConstruct; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; import java.util.Map; @Data @Configuration @ConfigurationProperties("my-config") public class BaseImagesProperties { // 先接收原始JSON字符串,和配置项key对应 private String stringWithJson; private String otherOption; // 存储解析后的结构化配置,不参与配置绑定 private Map<String, Map<String, Map<String, String>>> configMap; // 所有属性注入完成后自动执行解析 @PostConstruct private void parseJsonConfig() throws Exception { ObjectMapper objectMapper = new ObjectMapper(); this.configMap = objectMapper.readValue(this.stringWithJson, new TypeReference<>() {}); } public String getLink(String group, String element) { return configMap.get(group).get(element).get("link"); } }注:Spring Boot 2.x版本使用
javax.annotation.PostConstruct替换上述代码中的jakarta.annotation.PostConstruct即可。
该方案解析逻辑仅在当前配置类初始化时执行一次,无额外性能损耗。方案3:直接使用YAML原生结构化配置(最简洁,适合非环境变量注入场景)
如果不需要通过环境变量动态传入整段JSON,可以直接把配置结构写在application.yaml中,Spring会自动完成嵌套结构到Map的绑定,不需要任何额外转换逻辑:my-config: string-with-json: group1: group11: name: element1 link: <LINK> group12: name: element2 link: <LINK> group2: group21: name: element3 link: <LINK> other-option: string
注意:方案1、2默认依赖Spring Boot自带的Jackson组件,不需要额外引入依赖。
内容的提问来源于stack exchange,提问作者Grigory Skvortsov

