Skip to content

Commit 83e2785

Browse files
authored
Add KotlinModule support and tests for schema generation in BeanOutputConverter (#3900)
Fixes #3900 Auto-cherry-pick to 1.0.x Signed-off-by: Dmitry Sulman <[email protected]>
1 parent 8d701fd commit 83e2785

File tree

2 files changed

+65
-0
lines changed

2 files changed

+65
-0
lines changed

spring-ai-model/src/main/java/org/springframework/ai/converter/BeanOutputConverter.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@
3737
import org.slf4j.Logger;
3838
import org.slf4j.LoggerFactory;
3939

40+
import org.springframework.ai.model.KotlinModule;
4041
import org.springframework.ai.util.JacksonUtils;
42+
import org.springframework.core.KotlinDetector;
4143
import org.springframework.core.ParameterizedTypeReference;
4244
import org.springframework.lang.NonNull;
4345

@@ -136,6 +138,11 @@ private void generateSchema() {
136138
com.github.victools.jsonschema.generator.OptionPreset.PLAIN_JSON)
137139
.with(jacksonModule)
138140
.with(Option.FORBIDDEN_ADDITIONAL_PROPERTIES_BY_DEFAULT);
141+
142+
if (KotlinDetector.isKotlinReflectPresent()) {
143+
configBuilder.with(new KotlinModule());
144+
}
145+
139146
SchemaGeneratorConfig config = configBuilder.build();
140147
SchemaGenerator generator = new SchemaGenerator(config);
141148
JsonNode jsonNode = generator.generateSchema(this.type);
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package org.springframework.ai.converter
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper
4+
import org.assertj.core.api.Assertions.assertThat
5+
import org.junit.jupiter.api.Test
6+
7+
class KotlinBeanOutputConverterTests {
8+
9+
private data class Foo(val bar: String, val baz: String?)
10+
private data class FooWithDefault(val bar: String, val baz: Int = 10)
11+
12+
private val objectMapper = ObjectMapper()
13+
14+
@Test
15+
fun `test Kotlin data class schema generation using getJsonSchema`() {
16+
val converter = BeanOutputConverter(Foo::class.java)
17+
18+
val schemaJson = converter.jsonSchema
19+
20+
val schemaNode = objectMapper.readTree(schemaJson)
21+
22+
val required = schemaNode["required"]
23+
assertThat(required).isNotNull
24+
assertThat(required.toString()).contains("bar")
25+
assertThat(required.toString()).doesNotContain("baz")
26+
27+
val properties = schemaNode["properties"]
28+
assertThat(properties["bar"]["type"].asText()).isEqualTo("string")
29+
30+
val bazTypeNode = properties["baz"]["type"]
31+
if (bazTypeNode.isArray) {
32+
assertThat(bazTypeNode.toString()).contains("string")
33+
assertThat(bazTypeNode.toString()).contains("null")
34+
} else {
35+
assertThat(bazTypeNode.asText()).isEqualTo("string")
36+
}
37+
}
38+
39+
@Test
40+
fun `test Kotlin data class with default values`() {
41+
val converter = BeanOutputConverter(FooWithDefault::class.java)
42+
43+
val schemaJson = converter.jsonSchema
44+
45+
val schemaNode = objectMapper.readTree(schemaJson)
46+
47+
val required = schemaNode["required"]
48+
assertThat(required).isNotNull
49+
assertThat(required.toString()).contains("bar")
50+
assertThat(required.toString()).doesNotContain("baz")
51+
52+
val properties = schemaNode["properties"]
53+
assertThat(properties["bar"]["type"].asText()).isEqualTo("string")
54+
55+
val bazTypeNode = properties["baz"]["type"]
56+
assertThat(bazTypeNode.asText()).isEqualTo("integer")
57+
}
58+
}

0 commit comments

Comments
 (0)