Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Kotlin annotations #358

Open
moranlefler opened this issue Jun 5, 2023 · 9 comments
Open

Support Kotlin annotations #358

moranlefler opened this issue Jun 5, 2023 · 9 comments

Comments

@moranlefler
Copy link

It seems that the Jackson module ignores Jackson annotations in my Kotlin code, e.g.:

 data class TestNamedProperty( @JsonProperty("my_text")  val text: String)

The generator generates a schema with the property name text instead of my_text. I traced the generation with a debugger, and it seems that the method getPropertyNameOverrideBasedOnJsonPropertyAnnotation in the Jackson module does not see the annotation in the Java class that I created from Kotlin using TestNamedProperty::class.java.
I suspect that it has to do with how Kotlin and Java annotations differ - https://stackoverflow.com/questions/72559287/how-to-read-kotlin-annotation

I guess that means that jsonschema-generator cannot be used from Kotlin, which is a bummer for my use case :(

@moranlefler
Copy link
Author

It looks like I jumped to a conclusion too quickly as this doesn't seem to be a general problem with Kotlin annotations, as I was able to successfully create a correct schema for a Kotline class like so:

class TestNamedProperty2 {
    @JsonProperty("my_text")
    val text: String = ""
  }

So it seems that the issue is specific to Kotlin data classes. I am writing a schema generator that uses jsonschema-generator and adds a bit of logic and configurations. Many of its (internal) users use data class so that is a hard requirement. Is this something that I can help fix? any pointers for how to approach this?

@moranlefler
Copy link
Author

@CarstenWickner
Copy link
Member

Hi @moranlefler,

It's very unfortunate that the annotation goes to the constructor parameter in this scenario.
Currently, those are not taken into account, but they apparently would have to in order to handle data classes correctly.

As starting point, you can extend the JacksonModule and override its getPropertyNameOverrideBasedOnJsonPropertyAnnotation() method (that's why it has protected visibility 😉) to check the constructor parameters as well.

protected String getPropertyNameOverrideBasedOnJsonPropertyAnnotation(MemberScope<?, ?> member) {

Preferably, the handling would be generic enough to allow being included via a PR.
Looking forward to your feedback there. 😃

@moranlefler
Copy link
Author

Created a PR for this. Feedback welcomed!
#359

@bfreuden
Copy link

@moranlefler
Have you tried adding @get to the annotation?
Like this:

class TestNamedProperty2 {
    @get:JsonProperty("my_text")
    val text: String = ""
  }

I stumbled upon the same issue using swagger 2 annotations and that did the job.

@CarstenWickner
Copy link
Member

CarstenWickner commented Sep 16, 2023

If this is indeed setting the annotations on the getter methods instead of the constructor, that'd be awesome as no change would be required here.

I'd merely include this in the documentation then, to share this with other Kotlin users.
Relevant Kotlin documentation would be this then:
https://kotlinlang.org/docs/annotations.html#annotation-use-site-targets

Thanks for sharing!

@bfreuden
Copy link

This is indeed setting the annotations on getter methods as shown by the output of javap.

private data class MyParams(
    @get:Schema(title="Jinja input", description = "Whole document if left empty, otherwise 'metadata.input' syntax accepted", defaultValue = " ")
    val input: String = "",
)
javap -v -classpath build/classes/kotlin/test org.bfreuden.MyParams

  public final java.lang.String getInput();
    descriptor: ()Ljava/lang/String;
    flags: (0x0011) ACC_PUBLIC, ACC_FINAL
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #29                 // Field input:Ljava/lang/String;
         4: areturn
      LineNumberTable:
        line 26: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lorg.bfreuden/MyParams;
    RuntimeVisibleAnnotations:
      0: #43(#44=s#51,#48=s#52,#53=s#54)
        io.swagger.v3.oas.annotations.media.Schema(
          title="Jinja input"
          description="Whole document if left empty, otherwise \'metadata.input\' syntax accepted"
          defaultValue=" "
        )
    RuntimeInvisibleAnnotations:
      0: #11()
        org.jetbrains.annotations.NotNull

 

@SquirrelGrip
Copy link

I too would love to see this available in kotlin.

What I have found is by adding a @get:JsonProperty or @field:JsonProperty to you data class properties fixes the issue, however what about the kotlin classes that are out of your control.

This also has a general problem with kotlin data classes, as the @min from jakarta.validation-api also cannot work effectively. @get:Min works and hence the problem is a kotlin issue, not that kotlin is going to fix it.

Possible work around...
Suggest a new module specifically for kotlin data classes.
Might consider using the kotlin reflection instead of java reflection (eg. ::class instead of ::class.java) to gather the information.

@bfreuden
Copy link

however what about the kotlin classes that are out of your control.

@SquirrelGrip I'm not sure I'm getting your point: if a Java class is out of your control then you cannot add annotations (min, JsonProperty) to it in order to tweak the behavior of the json schema generation, right? So why would it be required for Kotlin?

In my understanding there is no issue with Kotlin and no issue with jsonschema-generator (and this ticket should be closed :)).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

4 participants