This commit is contained in:
Mengqi Xu 2025-10-07 23:10:35 +03:00 committed by GitHub
commit f9c9ff00d3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 62 additions and 14 deletions

View File

@ -56,6 +56,7 @@ import org.springframework.util.StringUtils;
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Sam Brannen * @author Sam Brannen
* @author Brian Clozel * @author Brian Clozel
* @author Mengqi Xu
* @since 4.1 * @since 4.1
*/ */
public abstract class YamlProcessor { public abstract class YamlProcessor {
@ -244,13 +245,7 @@ public abstract class YamlProcessor {
if (value instanceof Map) { if (value instanceof Map) {
value = asMap(value); value = asMap(value);
} }
if (key instanceof CharSequence) {
result.put(key.toString(), value); result.put(key.toString(), value);
}
else {
// It has to be a map key in this case
result.put("[" + key.toString() + "]", value);
}
}); });
return result; return result;
} }
@ -305,17 +300,20 @@ public abstract class YamlProcessor {
*/ */
protected final Map<String, Object> getFlattenedMap(Map<String, Object> source) { protected final Map<String, Object> getFlattenedMap(Map<String, Object> source) {
Map<String, Object> result = new LinkedHashMap<>(); Map<String, Object> result = new LinkedHashMap<>();
buildFlattenedMap(result, source, null); buildFlattenedMap(result, source, null, false);
return result; return result;
} }
@SuppressWarnings({"rawtypes", "unchecked"}) @SuppressWarnings({"rawtypes", "unchecked"})
private void buildFlattenedMap(Map<String, Object> result, Map<String, Object> source, @Nullable String path) { private void buildFlattenedMap(Map<String, Object> result, Map<String, Object> source, @Nullable String path, boolean isIndexedKey) {
source.forEach((key, value) -> { source.forEach((key, value) -> {
if (StringUtils.hasText(path)) { if (StringUtils.hasText(path)) {
if (key.startsWith("[")) { if (isIndexedKey) {
key = path + key; key = path + key;
} }
else if (key.startsWith("[") || key.endsWith("]")) {
key = path + '[' + key + ']';
}
else { else {
key = path + '.' + key; key = path + '.' + key;
} }
@ -325,7 +323,7 @@ public abstract class YamlProcessor {
} }
else if (value instanceof Map map) { else if (value instanceof Map map) {
// Need a compound key // Need a compound key
buildFlattenedMap(result, map, key); buildFlattenedMap(result, map, key, false);
} }
else if (value instanceof Collection collection) { else if (value instanceof Collection collection) {
// Need a compound key // Need a compound key
@ -336,7 +334,7 @@ public abstract class YamlProcessor {
int count = 0; int count = 0;
for (Object object : collection) { for (Object object : collection) {
buildFlattenedMap(result, Collections.singletonMap( buildFlattenedMap(result, Collections.singletonMap(
"[" + (count++) + "]", object), key); "[" + (count++) + "]", object), key, true);
} }
} }
} }

View File

@ -128,4 +128,18 @@ class YamlMapFactoryBeanTests {
this.factory.getObject().get("mymap")); this.factory.getObject().get("mymap"));
} }
@Test
void testMapWithIntegerKey() {
this.factory.setResources(new ByteArrayResource("foo:\n 1: bar".getBytes()));
Map<String, Object> map = this.factory.getObject();
assertThat(map).hasSize(1);
assertThat(map.containsKey("foo")).isTrue();
Object object = map.get("foo");
assertThat(object).isInstanceOf(LinkedHashMap.class);
@SuppressWarnings("unchecked")
Map<String, Object> sub = (Map<String, Object>) object;
assertThat(sub.containsKey("1")).isTrue();
assertThat(sub.get("1")).isEqualTo("bar");
}
} }

View File

@ -99,7 +99,7 @@ class YamlProcessorTests {
void integerKeyBehaves() { void integerKeyBehaves() {
setYaml("foo: bar\n1: bar"); setYaml("foo: bar\n1: bar");
this.processor.process((properties, map) -> { this.processor.process((properties, map) -> {
assertThat(properties.get("[1]")).isEqualTo("bar"); assertThat(properties.get("1")).isEqualTo("bar");
assertThat(properties).hasSize(2); assertThat(properties).hasSize(2);
}); });
} }
@ -108,7 +108,7 @@ class YamlProcessorTests {
void integerDeepKeyBehaves() { void integerDeepKeyBehaves() {
setYaml("foo:\n 1: bar"); setYaml("foo:\n 1: bar");
this.processor.process((properties, map) -> { this.processor.process((properties, map) -> {
assertThat(properties.get("foo[1]")).isEqualTo("bar"); assertThat(properties.get("foo.1")).isEqualTo("bar");
assertThat(properties).hasSize(1); assertThat(properties).hasSize(1);
}); });
} }

View File

@ -165,6 +165,42 @@ class YamlPropertiesFactoryBeanTests {
assertThat(properties.getProperty("one")).isEqualTo("two"); assertThat(properties.getProperty("one")).isEqualTo("two");
} }
@Test // gh-27020
void loadResourceWithEscapedKey() {
String yaml = "root:\n" +
" webservices:\n" +
" \"[domain.test:8080]\":\n" +
" - username: foo\n" +
" password: bar\n";
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
factory.setResources(
new ByteArrayResource(yaml.getBytes()),
new ByteArrayResource("indexed:\n \"[0]\": foo\n \"[1]\": bar".getBytes()),
new ByteArrayResource("indexed2:\n - \"[a]\": foo\n \"[b]\": bar".getBytes()),
new ByteArrayResource("only-left-bracket:\n \"[/key1/\": foo".getBytes()),
new ByteArrayResource("only-right-bracket:\n \"/key1/]\": foo".getBytes()),
new ByteArrayResource("special-bracket:\n \"][/key1/][\": foo".getBytes()),
new ByteArrayResource("number-key:\n 1: foo".getBytes()));
Properties properties = factory.getObject();
assertThat(properties.getProperty("root.webservices[[domain.test:8080]][0].username")).isEqualTo("foo");
assertThat(properties.getProperty("root.webservices[[domain.test:8080]][0].password")).isEqualTo("bar");
assertThat(properties.getProperty("indexed[[0]]")).isEqualTo("foo");
assertThat(properties.getProperty("indexed[[1]]")).isEqualTo("bar");
assertThat(properties.getProperty("indexed2[0][[a]]")).isEqualTo("foo");
assertThat(properties.getProperty("indexed2[0][[b]]")).isEqualTo("bar");
assertThat(properties.getProperty("only-left-bracket[[/key1/]")).isEqualTo("foo");
assertThat(properties.getProperty("only-right-bracket[/key1/]]")).isEqualTo("foo");
assertThat(properties.getProperty("special-bracket.][/key1/][")).isEqualTo("foo");
assertThat(properties.getProperty("number-key.1")).isEqualTo("foo");
}
@Test @Test
void loadNonExistentResource() { void loadNonExistentResource() {
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();