From 18ae16e6ac0256144de2cbcaea08ac1736fc707c Mon Sep 17 00:00:00 2001 From: Ahmed Maruf <81756975+ohMaruf@users.noreply.github.com> Date: Tue, 27 May 2025 10:56:37 +0200 Subject: [PATCH] fix(json): prevent double-serialization of already valid JSON strings in JsonParser.toJson If the input to JsonParser.toJson is a String that is already a valid JSON (e.g. a JSON array or object), the method now returns the string as-is, instead of re-serializing it into a quoted string. This avoids issues with deserialization errors, especially when tools expect structured types like List. test(json): add unit test to ensure toJson skips serialization of valid JSON strings Signed-off-by: Ahmed Maruf <81756975+ohMaruf@users.noreply.github.com> --- .../ai/util/json/JsonParser.java | 18 +++++++++++++++++- .../MethodToolCallbackGenericTypesTest.java | 6 +++--- .../ai/util/json/JsonParserTests.java | 7 +++++++ 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/spring-ai-model/src/main/java/org/springframework/ai/util/json/JsonParser.java b/spring-ai-model/src/main/java/org/springframework/ai/util/json/JsonParser.java index 88a31dd43..b54488031 100644 --- a/spring-ai-model/src/main/java/org/springframework/ai/util/json/JsonParser.java +++ b/spring-ai-model/src/main/java/org/springframework/ai/util/json/JsonParser.java @@ -100,9 +100,25 @@ public final class JsonParser { } /** - * Converts a Java object to a JSON string. + * Checks if a string is a valid JSON string. + */ + private static boolean isValidJson(String input) { + try { + OBJECT_MAPPER.readTree(input); + return true; + } + catch (JsonProcessingException e) { + return false; + } + } + + /** + * Converts a Java object to a JSON string if it's not already a valid JSON string. */ public static String toJson(@Nullable Object object) { + if (object instanceof String && isValidJson((String) object)) { + return (String) object; + } try { return OBJECT_MAPPER.writeValueAsString(object); } diff --git a/spring-ai-model/src/test/java/org/springframework/ai/tool/method/MethodToolCallbackGenericTypesTest.java b/spring-ai-model/src/test/java/org/springframework/ai/tool/method/MethodToolCallbackGenericTypesTest.java index 9b1baeac4..b99faa71a 100644 --- a/spring-ai-model/src/test/java/org/springframework/ai/tool/method/MethodToolCallbackGenericTypesTest.java +++ b/spring-ai-model/src/test/java/org/springframework/ai/tool/method/MethodToolCallbackGenericTypesTest.java @@ -63,7 +63,7 @@ class MethodToolCallbackGenericTypesTest { String result = callback.call(toolInput); // Verify the result - assertThat(result).isEqualTo("\"3 strings processed: [one, two, three]\""); + assertThat(result).isEqualTo("3 strings processed: [one, two, three]"); } @Test @@ -97,7 +97,7 @@ class MethodToolCallbackGenericTypesTest { String result = callback.call(toolInput); // Verify the result - assertThat(result).isEqualTo("\"3 entries processed: {one=1, two=2, three=3}\""); + assertThat(result).isEqualTo("3 entries processed: {one=1, two=2, three=3}"); } @Test @@ -134,7 +134,7 @@ class MethodToolCallbackGenericTypesTest { String result = callback.call(toolInput); // Verify the result - assertThat(result).isEqualTo("\"2 maps processed: [{a=1, b=2}, {c=3, d=4}]\""); + assertThat(result).isEqualTo("2 maps processed: [{a=1, b=2}, {c=3, d=4}]"); } /** diff --git a/spring-ai-model/src/test/java/org/springframework/ai/util/json/JsonParserTests.java b/spring-ai-model/src/test/java/org/springframework/ai/util/json/JsonParserTests.java index 1d0fbcc3a..61e073b70 100644 --- a/spring-ai-model/src/test/java/org/springframework/ai/util/json/JsonParserTests.java +++ b/spring-ai-model/src/test/java/org/springframework/ai/util/json/JsonParserTests.java @@ -255,6 +255,13 @@ class JsonParserTests { assertThat(value).isEqualTo(1_500_000_000_000L); } + @Test + void doesNotDoubleSerializeValidJsonString() { + String input = "[1,2,3]"; + String result = JsonParser.toJson(input); + assertThat(input).isEqualTo(result); + } + record TestRecord(String name, Integer age) { }