#JsonValue annotation in Kotlin's enum class' value property - jackson

I'm unable to apply the Jackson's #JsonValue annotation on the value parameter of enum class:
enum class CancellationReason(#JsonValue val code: String) {
CUSTOMER_RESIGNED("20"),
ERRORS_IN_FOO("21"),
ERRORS_IN_BAR("24");
}
The error message states: This annotation in not applicable to target 'value parameter'. What's the problem?

You can upgrade jackson-module-kotlin to version 2.9.0, and the error will be gone, because the #JsonValue annotation gets a target FIELD in that version.
Alternatively, fix that by specifying the annotation use-site target by adding #get::
enum class CancellationReason(#get:JsonValue val code: String) {
CUSTOMER_RESIGNED("20"),
ERRORS_IN_FOO("21"),
ERRORS_IN_BAR("24");
}

As per this document, you can serialize the enum constant (enum object) as Json in Kotlin by annotating the enum property with #JsonValue or #JsonFormat (if you use jackson library version < 2.9).
As hotkey mentioned, upgrade your Jackson library to 2.9.0. Alternatively you can use #JsonFormat annotation
enum class Event(#JsonFormat(shape = JsonFormat.Shape.OBJECT) val event: String) {
START("start"),
END("end"),
RESCHEDULE("rescheduled")
}

Related

How to read kotlin annotation

I have annotation classes
annotation class Endpoint(val name: String)
#Target(AnnotationTarget.TYPE)
annotation class JsonObjectKey(val name: String)
These are used like so
#Endpoint("my-endpoint")
fun myEndpoint(): #JsonObjectKey("key") String {
return "value"
}
With a method: java.lang.reflect.Method object representing myEndpoint,
I am able to access the Endpoint-Annotation, but I fail to access the JsonObjectKey-Annotatation.
method.annotations only contains Endpoint
method.annotatedReturnType.annotations is empty
How to read JsonObjectKey (key in this scenario)?
Intention is to generate JsonObject {"key": "value"}
Use the Kotlin reflection API!
For example, this works:
fun main() {
println(::myEndpoint.returnType.annotations)
}
#Target(AnnotationTarget.TYPE)
annotation class JsonObjectKey(val name: String)
fun myEndpoint(): #JsonObjectKey("key") String {
return "value"
}
This outputs:
[#JsonObjectKey(name=key)]
If you only have a java.lang.reflect.Method for some reason, you can convert it to a KFunction using kotlinFunction:
println(someJavaMethod?.kotlinFunction?.returnType?.annotations)
It appears that annotations annotated in this position in Java:
public static #JsonObjectKey(name = "foo") String foo()
are very different from those annotated in this position in Kotlin:
fun foo(): #JsonObjectKey("key") String = ""
These seem to be two different positions. Kotlin reflection cannot see the annotation on the Java return type, and Java reflection cannot see the annotation on the Kotlin return type.
Compare how the annotations appear in the class file. For the Java method, the annotation shows up in the RuntimeVisibleTypeAnnotations attribute:
RuntimeVisibleTypeAnnotations:
0: #23(#24=s#20): METHOD_RETURN
JsonObjectKey(
name="foo"
)
On the other hand, the Kotlin method instead has the annotation stored as part of the kotlin.Metadata annotation:
RuntimeVisibleAnnotations:
0: #86(#87=[I#88,I#89,I#90],#91=I#92,#93=I#94,#95=[s#96],#97=[s#5,s#98,s#76,s#98,s#99,s#100,s#101,s#102])
kotlin.Metadata(
mv=[1,6,0]
k=2
xi=48
d1=["..."]
d2=["main","","myEndpoint","","LJsonObjectKey;","name","key","myproject"]
)

Can you define alternative shorthands for Kotlin Annotations?

I am using annotations and reflection to create a parser for some custom made files used in the project I work with
I have this annotation that will be used to annotate most data class constructor parameters
annotation class Element(val name: String = "",val type: ElementType = ElementType.Value)
the enum ElementType has these values
enum class XElementType {
Value,
Attribute,
Ignore
}
is there a way to create a shorthand or alternate so that instead of using
#Element(type=ElementType.Ignore)
val ignoredVariable:String
I can use something like
#IgnoreElement
val ignoredVariable:String
which will resolve to Element("",ElementType.Ignore) ?

Kotlin annotation only for annotation parameters

I'd like to have a Kotlin annotation that can only be used as a parameter on another annotation. Which target should I use for it?
// #Target <- ?
annotation class MyConfigurationEntry(
val option,
val value
)
#Target(AnnotationTarget.FUNCTION)
annotation class MyConfiguration(vararg val entries: MyConfigurationEntry)
I believe it is #Target(AnnotationTarget.ANNOTATION_CLASS) if you click on that constant you will read:
Annotation class only
Kotlin version 1.3.41

What is legitimate way to get annotations of a pure Kotlin property via reflection, are they always missing?

I'm trying to get annotations from Kotlin data class
package some.meaningless.package.name
import kotlin.reflect.full.memberProperties
annotation class MyAnnotation()
#MyAnnotation
data class TestDto(#MyAnnotation val answer: Int = 42)
fun main(args: Array<String>) {
TestDto::class.memberProperties.forEach { p -> println(p.annotations) }
println(TestDto::class.annotations)
}
I need to process class annotation to make a custom name serialization of GSON however no matter how I declare annotation class it never gets detected
The program always outputs
[]
[#some.meaningless.package.name.MyAnnotation()]
which means only class level annotations are present
Ok,
it seems that the culprit was, that Kotlin annotations have default #Target(AnnotationTarget.CLASS) which is not stressed enough in documentation.
After I added #Target to the annotation class it now works properly
#Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY)
annotation class MyAnnotation()
Now it prints out
[#some.meaningless.package.name.MyAnnotation()]
[#some.meaningless.package.name.MyAnnotation()]
As a side affect it will force the compiler to check that the annotation is applied as required, in current version of Kotlin, if explicit #Targetis not present only class level annotations are kept but no validity checks performed.
As Kotlin reference said as below:
If you don't specify a use-site target, the target is chosen according to the #Target annotation of the annotation being used. If there are multiple applicable targets, the first applicable target from the following: param > property > field.
To make the annotation annotated on a property, you should use site target, for example:
#MyAnnotation
data class TestDto(#property:MyAnnotation val answer: Int = 42)
However, annotations with property target in Kotlin are not visible to Java, so you should double the annotation, for example:
#MyAnnotation // v--- used for property v--- used for params in Java
data class TestDto(#property:MyAnnotation #MyAnnotation val answer: Int = 42)

Use of Parceler with Kotlin data class with constructor for serialization

Is there a way to use Parceler with Kotlin data classes and constructor for serialization without using #ParcelProperty annotation for each field?
If I try and use library like this:
#Parcel
data class Valve #ParcelConstructor constructor(val size: Int)
I get Error:Parceler: No corresponding property found for constructor parameter arg0. But if I add #ParcelProperty("size") it works just fine.
Why is that?
Update:
There are other another way to use this library.
I could just remove #ParcelConstructor annotation, but then I will get error
Error:Parceler: No #ParcelConstructor annotated constructor and no default empty bean constructor found.
I think (haven't tested it) I also could make all constructor parameters optional and add #JvmOverloads but that has a side effect that I have to check all properties of the class if they are null or not.
Update 2:
This is what worked for me:
#Parcel
data class Valve(val size: Int? = null)
In short generated Java class must have default empty constructor. One way to achieve that is to do as above - all variables should have default values.
According to the docs, Parceler by default works with public fields. But a usual Kotlin data class (as in your example) is rather a "traditional getter/setter bean", since every Kotlin property is represented by a private field and a getter/[setter].
TL; DR: I think this will work:
#Parcel(Serialization.BEAN)
data class Valve(val size: Int = 10)
Note the default value, it allows Kotlin to automatically generate an additional empty constructor, which is required by the Java Been specification.
Another way would be to mark the constructor that we already have:
#Parcel(Serialization.BEAN)
data class Driver #ParcelConstructor constructor(val name: String)
The specific document: https://github.com/johncarl81/parceler#gettersetter-serialization
I know this question already has an answer, but for future viewers who are also struggling to get Parceler to work with kotlin data objects, I wrote a new annotation processor to generate the Parcelable boilerplate for Kotlin data classes. It's designed to massively reduce the boilerplate code in making your data classes Parcelable:
https://github.com/grandstaish/paperparcel
Usage:
Annotate your data class with #PaperParcel, implement PaperParcelable, and add a JVM static instance of the generated CREATOR e.g.:
#PaperParcel
data class Example(
val test: Int,
...
) : PaperParcelable {
companion object {
#JvmField val CREATOR = PaperParcelExample.CREATOR
}
}
Now your data class is Parcelable and can be passed directly to a Bundle or Intent
Edit: Update with latest API
Just add the default constructor:
#Parcel
data class Valve(val size: Int) {
constructor() : this(0)
}
if you use Kotlin 1.1.4 or above it's easier to use #Parcelize annotation
For doing this first add this to build.gradle
android {
//other codes
//for using latest experimental build of Android Extensions
androidExtensions {
experimental = true
}
}
Then change your class like this
#Parcelize
data class Valve(val size: Int? = null) : Parcelable