diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedYamlLoader.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedYamlLoader.java index 30959fab34..b2a87f8792 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedYamlLoader.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedYamlLoader.java @@ -63,12 +63,18 @@ class OriginTrackedYamlLoader extends YamlProcessor { @Override protected Yaml createYaml() { - BaseConstructor constructor = new OriginTrackingConstructor(); + LoaderOptions loaderOptions = new LoaderOptions(); + loaderOptions.setAllowDuplicateKeys(false); + loaderOptions.setMaxAliasesForCollections(Integer.MAX_VALUE); + loaderOptions.setAllowRecursiveKeys(true); + return createYaml(loaderOptions); + } + + private Yaml createYaml(LoaderOptions loaderOptions) { + BaseConstructor constructor = new OriginTrackingConstructor(loaderOptions); Representer representer = new Representer(); DumperOptions dumperOptions = new DumperOptions(); LimitedResolver resolver = new LimitedResolver(); - LoaderOptions loaderOptions = new LoaderOptions(); - loaderOptions.setAllowDuplicateKeys(false); return new Yaml(constructor, representer, dumperOptions, loaderOptions, resolver); } @@ -83,6 +89,10 @@ class OriginTrackedYamlLoader extends YamlProcessor { */ private class OriginTrackingConstructor extends SafeConstructor { + OriginTrackingConstructor(LoaderOptions loadingConfig) { + super(loadingConfig); + } + @Override public Object getData() throws NoSuchElementException { Object data = super.getData(); diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/OriginTrackedYamlLoaderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/OriginTrackedYamlLoaderTests.java index bd39af6e0c..4252c1ad22 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/OriginTrackedYamlLoaderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/OriginTrackedYamlLoaderTests.java @@ -135,6 +135,35 @@ class OriginTrackedYamlLoaderTests { assertThat(loaded).isEmpty(); } + @Test + void loadWhenLargeNumberOfNodesLoadsYaml() { + StringBuilder yaml = new StringBuilder(); + int size = 500; + yaml.append("defs:\n"); + for (int i = 0; i < size; i++) { + yaml.append(" - def" + i + ": &def" + i + "\n"); + yaml.append(" - value: " + i + "\n"); + } + yaml.append("refs:\n"); + for (int i = 0; i < size; i++) { + yaml.append(" ref" + i + ":\n"); + yaml.append(" - value: *def" + i + "\n"); + } + Resource resource = new ByteArrayResource(yaml.toString().getBytes(StandardCharsets.UTF_8)); + this.loader = new OriginTrackedYamlLoader(resource); + Map loaded = this.loader.load().get(0); + assertThat(loaded).hasSize(size * 2); + } + + @Test + void loadWhenRecursiveLoadsYaml() { + Resource resource = new ClassPathResource("recursive.yml", getClass()); + this.loader = new OriginTrackedYamlLoader(resource); + Map loaded = this.loader.load().get(0); + assertThat(loaded.get("test.a.spring")).hasToString("a"); + assertThat(loaded.get("test.b.boot")).hasToString("b"); + } + private OriginTrackedValue getValue(String name) { if (this.result == null) { this.result = this.loader.load(); diff --git a/spring-boot-project/spring-boot/src/test/resources/org/springframework/boot/env/recursive.yml b/spring-boot-project/spring-boot/src/test/resources/org/springframework/boot/env/recursive.yml new file mode 100644 index 0000000000..3d4ca7d182 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/resources/org/springframework/boot/env/recursive.yml @@ -0,0 +1,7 @@ +&def1 +*def1: a +test: + a: + spring: 'a' + b: + boot: 'b'