From 71c4e1f7dc0f13609506114781b48d0d33004478 Mon Sep 17 00:00:00 2001 From: CHANHAN <130114269+chanani@users.noreply.github.com> Date: Tue, 14 Apr 2026 09:42:21 +0900 Subject: [PATCH 1/2] Add RESPECT_JSONPROPERTY_ORDER to JsonSchemaGenerator Align JacksonSchemaModule configuration with BeanOutputConverter to honor @JsonPropertyOrder when generating JSON See gh-5797 Signed-off-by: CHANHAN <130114269+chanani@users.noreply.github.com> --- .../util/json/schema/JsonSchemaGenerator.java | 3 +- .../util/json/JsonSchemaGeneratorTests.java | 51 +++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/spring-ai-model/src/main/java/org/springframework/ai/util/json/schema/JsonSchemaGenerator.java b/spring-ai-model/src/main/java/org/springframework/ai/util/json/schema/JsonSchemaGenerator.java index 036fca1216..c9fd4056d3 100644 --- a/spring-ai-model/src/main/java/org/springframework/ai/util/json/schema/JsonSchemaGenerator.java +++ b/spring-ai-model/src/main/java/org/springframework/ai/util/json/schema/JsonSchemaGenerator.java @@ -90,7 +90,8 @@ public final class JsonSchemaGenerator { * Initialize JSON Schema generators. */ static { - Module jacksonModule = new JacksonSchemaModule(JacksonOption.RESPECT_JSONPROPERTY_REQUIRED); + Module jacksonModule = new JacksonSchemaModule(JacksonOption.RESPECT_JSONPROPERTY_REQUIRED, + JacksonOption.RESPECT_JSONPROPERTY_ORDER); Module openApiModule = new Swagger2Module(); Module springAiSchemaModule = PROPERTY_REQUIRED_BY_DEFAULT ? new SpringAiSchemaModule() : new SpringAiSchemaModule(SpringAiSchemaModule.Option.PROPERTY_REQUIRED_FALSE_BY_DEFAULT); diff --git a/spring-ai-model/src/test/java/org/springframework/ai/util/json/JsonSchemaGeneratorTests.java b/spring-ai-model/src/test/java/org/springframework/ai/util/json/JsonSchemaGeneratorTests.java index 4bedc5677e..28cb36e966 100644 --- a/spring-ai-model/src/test/java/org/springframework/ai/util/json/JsonSchemaGeneratorTests.java +++ b/spring-ai-model/src/test/java/org/springframework/ai/util/json/JsonSchemaGeneratorTests.java @@ -26,6 +26,7 @@ import com.fasterxml.jackson.annotation.JsonClassDescription; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyDescription; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; import io.swagger.v3.oas.annotations.media.Schema; import org.junit.jupiter.api.Test; import tools.jackson.databind.JsonNode; @@ -700,6 +701,33 @@ void throwExceptionWhenTypeIsNull() { .hasMessage("type cannot be null"); } + @Test + void generateSchemaForTypeWithJsonPropertyOrder() { + String schema = JsonSchemaGenerator.generateForType(OrderedPerson.class); + String expectedJsonSchema = """ + { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "id": { + "type": "integer", + "format": "int32" + } + }, + "required": [ "name", "email", "id" ], + "additionalProperties": false + } + """; + + assertThat(schema).isEqualToIgnoringWhitespace(expectedJsonSchema); + } + static class TestMethods { public void simpleMethod(String name, int age) { @@ -809,4 +837,27 @@ public void setEmail(String email) { } + @JsonPropertyOrder({ "name", "email", "id" }) + static class OrderedPerson { + + private int id; + + private String name; + + private String email; + + public int getId() { return this.id; } + + public void setId(int id) { this.id = id; } + + public String getName() { return this.name; } + + public void setName(String name) { this.name = name; } + + public String getEmail() { return this.email; } + + public void setEmail(String email) { this.email = email; } + + } + } From e4c97e1b6cbddc85613a97ef456c1b435edd2244 Mon Sep 17 00:00:00 2001 From: CHANHAN <130114269+chanani@users.noreply.github.com> Date: Tue, 14 Apr 2026 09:48:49 +0900 Subject: [PATCH 2/2] Apply Spring Java format Signed-off-by: CHANHAN <130114269+chanani@users.noreply.github.com> --- .../util/json/JsonSchemaGeneratorTests.java | 62 +++++++++++-------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/spring-ai-model/src/test/java/org/springframework/ai/util/json/JsonSchemaGeneratorTests.java b/spring-ai-model/src/test/java/org/springframework/ai/util/json/JsonSchemaGeneratorTests.java index 28cb36e966..8fb2d18edf 100644 --- a/spring-ai-model/src/test/java/org/springframework/ai/util/json/JsonSchemaGeneratorTests.java +++ b/spring-ai-model/src/test/java/org/springframework/ai/util/json/JsonSchemaGeneratorTests.java @@ -705,25 +705,25 @@ void throwExceptionWhenTypeIsNull() { void generateSchemaForTypeWithJsonPropertyOrder() { String schema = JsonSchemaGenerator.generateForType(OrderedPerson.class); String expectedJsonSchema = """ - { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "email": { - "type": "string" - }, - "id": { - "type": "integer", - "format": "int32" - } - }, - "required": [ "name", "email", "id" ], - "additionalProperties": false - } - """; + { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "id": { + "type": "integer", + "format": "int32" + } + }, + "required": [ "name", "email", "id" ], + "additionalProperties": false + } + """; assertThat(schema).isEqualToIgnoringWhitespace(expectedJsonSchema); } @@ -846,17 +846,29 @@ static class OrderedPerson { private String email; - public int getId() { return this.id; } + public int getId() { + return this.id; + } - public void setId(int id) { this.id = id; } + public void setId(int id) { + this.id = id; + } - public String getName() { return this.name; } + public String getName() { + return this.name; + } - public void setName(String name) { this.name = name; } + public void setName(String name) { + this.name = name; + } - public String getEmail() { return this.email; } + public String getEmail() { + return this.email; + } - public void setEmail(String email) { this.email = email; } + public void setEmail(String email) { + this.email = email; + } }