From 796ce3d4b2cf9944e64185af965e1cc802b85e21 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 5 Nov 2024 16:10:36 -0800 Subject: [PATCH] Throw an exception if the same name is written to JSON more than once Update `JsonValueWriter` to track written names and throw an exception if there is a duplicate. Closes gh-43041 --- .../springframework/boot/json/JsonValueWriter.java | 10 ++++++++++ .../boot/json/JsonValueWriterTests.java | 11 +++++++++++ 2 files changed, 21 insertions(+) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/json/JsonValueWriter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/json/JsonValueWriter.java index e8a277efd03..dbd8237aebd 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/json/JsonValueWriter.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/json/JsonValueWriter.java @@ -21,7 +21,9 @@ import java.io.UncheckedIOException; import java.util.ArrayDeque; import java.util.Arrays; import java.util.Deque; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Predicate; @@ -223,6 +225,8 @@ class JsonValueWriter { ActiveSeries activeSeries = this.activeSeries.peek(); Assert.notNull(activeSeries, "No series has been started"); activeSeries.incrementIndexAndAddCommaIfRequired(); + Assert.state(activeSeries.addName(processedName), + () -> "The name '" + processedName + "' has already been written"); writeString(processedName); append(":"); write(value); @@ -359,10 +363,16 @@ class JsonValueWriter { private int index; + private Set names = new HashSet<>(); + private ActiveSeries(Series series) { this.series = series; } + boolean addName(String processedName) { + return this.names.add(processedName); + } + MemberPath updatePath(MemberPath path) { return (this.series != Series.ARRAY) ? path : path.child(this.index); } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/json/JsonValueWriterTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/json/JsonValueWriterTests.java index 1f4441a3045..94939db8da5 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/json/JsonValueWriterTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/json/JsonValueWriterTests.java @@ -29,6 +29,7 @@ import org.springframework.boot.json.JsonValueWriter.Series; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; /** * Tests for {@link JsonValueWriter} . @@ -193,6 +194,16 @@ class JsonValueWriterTests { {"a":"A","b":"B"}"""); } + @Test + void writePairsWhenDuplicateThrowsException() { + assertThatIllegalStateException().isThrownBy(() -> doWrite((valueWriter) -> { + valueWriter.start(Series.OBJECT); + valueWriter.writePairs(Map.of("a", "A")::forEach); + valueWriter.writePairs(Map.of("a", "B")::forEach); + valueWriter.end(Series.OBJECT); + })).withMessage("The name 'a' has already been written"); + } + @Test void writeArray() { List list = List.of("a", "b", "c");