diff --git a/jsonschema-module-jackson/pom.xml b/jsonschema-module-jackson/pom.xml
index 59216688..63539cf9 100644
--- a/jsonschema-module-jackson/pom.xml
+++ b/jsonschema-module-jackson/pom.xml
@@ -16,6 +16,7 @@
com.github.victools.jsonschema.module.jackson
+ 1.9.0-Beta
@@ -34,13 +35,21 @@
jackson-annotations
provided
+
+ org.jetbrains.kotlin
+ kotlin-stdlib
+ ${kotlin.version}
+
+
+ org.jetbrains.kotlin
+ kotlin-test
+ ${kotlin.version}
+ test
+
-
- maven-compiler-plugin
-
maven-checkstyle-plugin
@@ -54,7 +63,20 @@
org.moditect
moditect-maven-plugin
+
+ org.jetbrains.kotlin
+ kotlin-maven-plugin
+ ${kotlin.version}
+ true
+
+ 1.8
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
-
\ No newline at end of file
+
diff --git a/jsonschema-module-jackson/src/main/java/com/github/victools/jsonschema/module/jackson/JacksonModule.java b/jsonschema-module-jackson/src/main/java/com/github/victools/jsonschema/module/jackson/JacksonModule.java
index a698d6dc..c518db83 100644
--- a/jsonschema-module-jackson/src/main/java/com/github/victools/jsonschema/module/jackson/JacksonModule.java
+++ b/jsonschema-module-jackson/src/main/java/com/github/victools/jsonschema/module/jackson/JacksonModule.java
@@ -196,13 +196,24 @@ protected String getPropertyNameOverrideBasedOnJsonPropertyAnnotation(MemberScop
if (annotation != null) {
String nameOverride = annotation.value();
// check for invalid overrides
- if (nameOverride != null && !nameOverride.isEmpty() && !nameOverride.equals(member.getDeclaredName())) {
+ if (isValidNameOverride(member, nameOverride)) {
return nameOverride;
}
}
return null;
}
+ /**
+ * Checks whether the potential name override is valid for the specified member.
+ *
+ * @param member field/method to override
+ * @param nameOverride the name that will override the original name of the member
+ * @return true if the specified override is valid for the member, false otherwise
+ */
+ protected static boolean isValidNameOverride(MemberScope, ?> member, String nameOverride) {
+ return nameOverride != null && !nameOverride.isEmpty() && !nameOverride.equals(member.getDeclaredName());
+ }
+
/**
* Alter the declaring name of the given field as per the declaring type's {@link JsonNaming} annotation.
*
diff --git a/jsonschema-module-jackson/src/main/java/com/github/victools/jsonschema/module/jackson/KotlinJacksonModule.java b/jsonschema-module-jackson/src/main/java/com/github/victools/jsonschema/module/jackson/KotlinJacksonModule.java
new file mode 100644
index 00000000..f16bf1bd
--- /dev/null
+++ b/jsonschema-module-jackson/src/main/java/com/github/victools/jsonschema/module/jackson/KotlinJacksonModule.java
@@ -0,0 +1,62 @@
+package com.github.victools.jsonschema.module.jackson;
+
+import com.fasterxml.classmate.members.RawField;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.github.victools.jsonschema.generator.MemberScope;
+import java.lang.reflect.Parameter;
+import java.util.List;
+import java.util.OptionalInt;
+import kotlin.Metadata;
+
+public class KotlinJacksonModule extends JacksonModule {
+ /**
+ * Look up an alternative name for a member in the constructor parameter list.
+ * When the Kotlin compiler compiles a Kotlin data class, it creates a constructor with a parameter
+ * for each field in the data class. The parameters have generic name such as "arg0", "arg1", etc.
+ * In order to find the appropriate constructor parameter, the method first determines the index of
+ * specified member within its type member list and uses the parameter with the same index.
+ * In order to avoid erroneous overrides, this method verifies that the specified member is indeed of a
+ * Kotlin class.
+ *
+ * @param member field/method to look-up alternative property name for
+ * @return alternative property name or the base class implementation return value
+ */
+ @Override
+ protected String getPropertyNameOverrideBasedOnJsonPropertyAnnotation(MemberScope, ?> member) {
+ if (isKotlinType(member)) {
+ OptionalInt memberIndex = getMemberIndex(member);
+ if (memberIndex.isPresent()) {
+ Parameter[] parameters = getConstructorParameters(member);
+ Parameter parameter = parameters[memberIndex.getAsInt()];
+ JsonProperty jsonPropertyAnnotation = parameter.getAnnotation(JsonProperty.class);
+ if (jsonPropertyAnnotation != null) {
+ String nameOverride = jsonPropertyAnnotation.value();
+ if (isValidNameOverride(member, nameOverride)) {
+ return nameOverride;
+ }
+ }
+ }
+ }
+ return super.getPropertyNameOverrideBasedOnJsonPropertyAnnotation(member);
+
+ }
+
+ private OptionalInt getMemberIndex(MemberScope, ?> member) {
+ List memberFields = member.getDeclaringType().getMemberFields();
+ for (int i = 0; i < memberFields.size(); i++) {
+ if (memberFields.get(i).getName().equals(member.getName())) {
+ return OptionalInt.of(i);
+ }
+ }
+ return OptionalInt.empty();
+ }
+
+ private static Parameter[] getConstructorParameters(MemberScope, ?> member) {
+ return member.getDeclaringType().getConstructors().get(0).getRawMember().getParameters();
+ }
+
+ private static boolean isKotlinType(MemberScope, ?> member) {
+ return member.getDeclaringType().getErasedType().isAnnotationPresent(Metadata.class);
+ }
+
+}
diff --git a/jsonschema-module-jackson/src/test/java/com/github/victools/jsonschema/module/jackson/KotlinJacksonModuleTest.kt b/jsonschema-module-jackson/src/test/java/com/github/victools/jsonschema/module/jackson/KotlinJacksonModuleTest.kt
new file mode 100644
index 00000000..db412eaf
--- /dev/null
+++ b/jsonschema-module-jackson/src/test/java/com/github/victools/jsonschema/module/jackson/KotlinJacksonModuleTest.kt
@@ -0,0 +1,29 @@
+package com.github.victools.jsonschema.module.jackson
+
+import com.fasterxml.jackson.annotation.JsonProperty
+import com.github.victools.jsonschema.generator.*
+import org.junit.jupiter.api.Assertions
+import org.junit.jupiter.api.Test
+import java.util.*
+
+class KotlinJacksonModuleTest {
+ data class TestJsonProperty(
+ @JsonProperty("my_text") val text: String,
+ @JsonProperty("my_number") val number: Int)
+
+ @Test
+ fun `naming override in kotlin data class with JsonProperty`() {
+ val config = SchemaGeneratorConfigBuilder(SchemaVersion.DRAFT_2020_12, OptionPreset.PLAIN_JSON)
+ .with(KotlinJacksonModule())
+ .build()
+
+ val generator = SchemaGenerator(config)
+ val result = generator.generateSchema(TestJsonProperty::class.java)
+ val propertiesNode = result[config.getKeyword(SchemaKeyword.TAG_PROPERTIES)]
+ val propertyNames: MutableSet = TreeSet()
+ propertiesNode.fieldNames().forEachRemaining { e: String -> propertyNames.add(e) }
+ Assertions.assertTrue(propertyNames.contains("my_text"))
+ Assertions.assertTrue(propertyNames.contains("my_number"))
+ }
+
+}