Getting this error : java.lang.RuntimeException: org.simpleframework.xml.core.MethodException: Annotation #org.simpleframework.xml.Element(data=false, name=, required=false, type=void) must mark a set or get method
This is the same problem as: kotlin data class + bean validation jsr 303
You need to use Annotation use-site targets since the default for an annotation on a property is prioritized as:
parameter (if declared in constructor)
property (if the target site allows, but only Kotlin created annotations can do this)
field (likely what happened here, which isn't what you wanted).
Use get or set target to place the annotation on the getter or setter. Here it is for the getter:
#Root(name = "response")
public class User() {
#get:Element public var result: String? = null
#get:Element public var token: String? = null
#get:Element public var uid: String? = null
}
See the linked answer for the details.
See the question response: Here
Related
I have this Firestore document Quiz_android that looks list this:
It is a simple array with maps in it. Now I would like to bind those results to some objects in Kotlin. Therefore I have made the following:
data class QuizBody(
val questions: List<Question>
)
data class Question(
val question: String,
val answers: List<String>,
val answer: Int
)
A Quizbody is just all the questions for the quiz in a list, and in that list, I have classes of Question which should be able to store all the data from the call.
But how do I bind the result from the call to those objects?
suspend fun getQuestions(quizToGet: String) {
try {
//firestore has support for coroutines via the extra dependency we've added :)
withTimeout(5_000) {
firestore.collection("Quizzes").document(quizToGet).get()
.addOnCompleteListener { task ->
if (task.isSuccessful) {
val result = task.result
if (result.exists()) {
val myObject = result.toObject(QuizBody::class.java)
println(myObject)
}
}
}
}
} catch (e: Exception) {
throw QuizRetrievalError("Retrieving a specific quiz was unsuccessful")
}
}
I have made this but this does not work.
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.hva.madlevel7task2, PID: 3995
java.lang.RuntimeException: Could not deserialize object. Class com.hva.madlevel7task2.model.QuizBody does not define a no-argument constructor. If you are using ProGuard, make sure these constructors are not stripped
Edit:
I have updated the data class:
data class QuizBody(
var questions: List<Question>? = null
)
data class Question(
var question: String? = null,
var answers: List<String>? = null,
var answer: Int? = null
)
suspend fun getQuestions(quizToGet: String) it still the same, now I get this in the console:
I/QuizViewModel: function: getListQuestions
W/Firestore: (24.1.1) [CustomClassMapper]: No setter/field for Questions found on class com.hva.madlevel7task2.model.QuizBody (fields/setters are case sensitive!)
I/System.out: QuizBody(questions=null)
The following error:
java.lang.RuntimeException: Could not deserialize object. Class com.hva.madlevel7task2.model.QuizBody does not define a no-argument constructor.
Is very straightforward in my opinion. Your class "QuizBody" does not have a no-argument constructor. When you try to deserialize an object that comes from a Firestore database, the Android SDKs require that the class should mandatorily have a default no-argument constructor.
In Kotlin, the data classes don't provide a default no-arg constructor if all the properties of the class are declared with val. For such properties, Kotlin requires that their values be specified in the constructor since they can't possibly change later. So this is required because we need to ensure the compiler that all the properties have an initial value. You can provide to all of the properties an initial value of null or any other value you find more appropriate. So your classes should look like this:
data class QuizBody(
var questions: List<Question>? = null
👆 👆
)
data class Question(
var question: String? = null,
var answers: List<String>? = null,
var answer: Int? = null
)
Now adding the properties in the constructor, Kotlin will automatically generate a default no-argument constructor. In this way, the Firebase Android SDK will be able to use to generate setters for each property. If don't make this change, you won't be able to use automatic deserialization. You'll have to read the value for each property out of the DocumentSnapshot object and pass them all to Kotlin's constructor.
Edit:
if (task.isSuccessful) {
val document = task.result
if (document.exists()) {
val myObject = document.toObject(QuizBody::class.java)
println(myObject)
}
}
Everything looks good. Need help in finding a mistake in code.
By the logs that is the snapshot from firebase
DataSnapshot
{ key = SecondGame,
value = {background=https://firebasestorage.googleapis.com/v0/b/fantasygameapp11.appspot.com/o/background_black.jpg?alt=media&token=b3ec1477-6b52-48b4-9296-f57f63f26837, description=SecondGameDescription, tag=https://firebasestorage.googleapis.com/v0/b/fantasygameapp11.appspot.com/o/hot_icon.png?alt=media&token=65516b45-1aca-4cac-9a39-3eddefffe499,
title=SecondGame, type=regular} }
That is the model
data class GameUnit (val background: String, val description: String, val tag: String, val title: String, val type: String)
Thats the code of the response
mReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
GameUnit post = dataSnapshot.getValue(GameUnit.class);
}
I know it might be already asked but I need to find the issue first of all. Is it also possible the problem is that model is in Kotlin but firebase response in Java?
Error
com.google.firebase.database.DatabaseException: Class com.model.GameUnit does not define a no-argument constructor. If you are using ProGuard, make sure these constructors are not stripped.
at com.google.firebase.database.core.utilities.encoding.CustomClassMapper$BeanMapper.deserialize(com.google.firebase:firebase-database##17.0.0:552)
at com.google.firebase.database.core.utilities.encoding.CustomClassMapper$BeanMapper.deserialize(com.google.firebase:firebase-database##17.0.0:545)
at com.google.firebase.database.core.utilities.encoding.CustomClassMapper.convertBean(com.google.firebase:firebase-database##17.0.0:415)
at com.google.firebase.database.core.utilities.encoding.CustomClassMapper.deserializeToClass(com.google.firebase:firebase-database##17.0.0:214)
at com.google.firebase.database.core.utilities.encoding.CustomClassMapper.convertToCustomClass(com.google.firebase:firebase-database##17.0.0:79)
at com.google.firebase.database.DataSnapshot.getValue(com.google.firebase:firebase-database##17.0.0:212)
at com.adultgaming.fantasygameapp.utils.FirebaseManager$1.onDataChange(FirebaseManager.java:47)
at com.google.firebase.database.core.ValueEventRegistration.fireEvent(com.google.firebase:firebase-database##17.0.0:75)
at com.google.firebase.database.core.view.DataEvent.fire(com.google.firebase:firebase-database##17.0.0:63)
at com.google.firebase.database.core.view.EventRaiser$1.run(com.google.firebase:firebase-database##17.0.0:55)
The deserializer that comes with the Realtime Database (and also Cloud Firestore) Android SDKs requires that the class you pass to it look like a JavaBean type object. This means that it must have a default no-arg constuructor, as you can tell from the error message, and also setter methods that map to each database field.
Kotlin data classes don't provide a default no-arg constructor in order to ensure that all of its fields have an initial value. You can tell Kotlin that it's OK for all of the fields not to have an initial value by giving null or some other value as a default value:
data class GameUnit (
var background: String = null,
var description: String = null,
var tag: String = null,
var title: String = null,
var type: String = null
)
For the above data class, Kotlin will generate a default no-arg constructor for the Firebase SDK to use. It will also generate setter methods for each var. Note that each property is var and provides a default null value.
If this is not what you want your data class to look like, you won't be able to use automatic deserialization. You will have to read each value out of the snapshot, make sure they are each not null, and pass them all to the constructor that Kotlin provides.
I'm using Jackson 2.9.2 and Retrofit 2.1.0 for some POST operation with a JSONArray as HTML-Header parameter.
The API defines a value which is aId. No matter what I try, my JSON property is always converted to lowercase (aid).
I tested my same code with abId, and it works... Anyone a clue, where my configuration is wrong or which convention(?) is against this property name?
//ObjectMapper initialization
ObjectMapper().disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
//the data class
import com.fasterxml.jackson.annotation.JsonProperty
data class MyClass(
#JsonProperty
val aId: String? = null, //<-- not working
#JsonProperty
val abId: String? = null //working!
)
//Retrofit call
import retrofit2.http.Body
#POST("log")
fun sendLog(#Body logs: List<MyClass>): Call<MyCall>
//JSON Result in HTML Header
[{
"aid":"some_value", //should be "aId"
"abId":"some_value" //is correct
}]
I tried with following Annotations:
#SerializedName("aId")
#JsonProperty("aId")
#JsonRawValue
#JsonAlias
Try this #get:JsonProperty("aId")
See Michael Ziober' posted link for answer Usage of Jackson #JsonProperty annotation for kotlin data classes
Described issue is a result of Jackson's default bahaviour to not scan private fields. This behaviour can be change with #JsonAutoDetect
#JsonAutoDetect(fieldVisibility = Visibility.ANY)
data class MyClass(
#JsonProperty
val aId: String? = null,
#JsonProperty
val abId: String? = null
)
I want to create custom request parser, i want to do this by annotating fields and getting value by reflection but I can get annotation only from the class field, the code below doesn't work for data class or constructor in a class, any idea what is wrong?
open class RequestParser {
fun getRequestWithTag(): String {
var requestString = "<RequestData>"
val declaredMemberProperties = this::class.declaredMemberProperties
declaredMemberProperties.filter {
it.findAnnotation<RequestField>() != null
}.forEach { filteredMemberProperties ->
requestString += "<${filteredMemberProperties.name.firstLetterToUpperCase()}>${filteredMemberProperties.getter.call(this)}</${filteredMemberProperties.name.firstLetterToUpperCase()}>"
}
requestString += "</RequestData>"
return requestString
}
}
#Retention(AnnotationRetention.RUNTIME)
#Target(
FIELD,
PROPERTY,
PROPERTY_GETTER,
VALUE_PARAMETER,
PROPERTY_SETTER,
CONSTRUCTOR,
FUNCTION)
public annotation class RequestField
//model example
data class RequestTest(
#RequestField val name: String
) : RequestParser()
//using example
RequestTest("name").getRequestWithTag()
An attribute in a data class constructor is many things, a constructor parameter, a getter, a setter and a field. So you need to set use-site targets to express what you actually mean.
class Example(#field:Ann val foo, // annotate Java field
#get:Ann val bar, // annotate Java getter
#param:Ann val quux) // annotate Java constructor parameter
See also https://kotlinlang.org/docs/reference/annotations.html#annotation-use-site-targets
So in your case I would try the following:
data class RequestTest(
#property:RequestField val name: String
) : RequestParser()
With property I am able to get the annotation from this::class.declaredMemberProperties
If you put field you would be able to get it via this::class.java.declaredFields
I'm switching to Kotlin for Android, but I'm struggling to understand the behavior of generics and Bound Class References.
In java I can serialize an object using Moshi's lib with the following lines:
Moshi moshi = new Moshi.Builder().build();
String string = moshi.adapter(CredentialsResponse.class).toJson(body);
And in Kotlin:
val moshi = Moshi.Builder().build()
var string = moshi.adapter(CredentialsResponse::class.java).toJson(body)
If I want to get the class from an instance, I found two options, but one is not working, and I can't understand why:
This code works:
fun testStack(body: CredentialsResponse) {
val moshi = Moshi.Builder().build()
var string = moshi.adapter(body.javaClass).toJson(body)
}
but this code shows a type mismatch error
fun testStack(body: CredentialsResponse) {
val moshi = Moshi.Builder().build()
var string = moshi.adapter(body::class.java).toJson(body)
}
AFAIK, this call is allowed since 1.1 (https://kotlinlang.org/docs/reference/reflection.html#bound-class-references-since-11), so what am I missing?
There's a subtle difference between the two:
class K
val javaClass: JsonAdapter<K> = moshi.adapter(body.javaClass)
val classJava: JsonAdapter<out K> = moshi.adapter(body::class.java)
Note that body::class.java is marked with out
By calling moshi.adapter(body::class.java).toJson(body) you're try to pass body as in parameter
The difference is, as #AlexeySoshin noted, that the unbound class reference Foo::class is typed with the exact type of the referenced class KClass<Foo>, and the bound one is typed with an out-projection: KClass<out Foo>.
There is a strong reason for this difference. When you reference a class by its name, you can be sure that the class token the reference evaluates to designates exactly the referenced type.
But, when you get a bound class reference for an expression typed as Foo, the expression may evaluate to an instance of Foo's subtype, and the correct type for the type token is KClass<out Foo>, meaning exactly that the actual type argument may be Foo or its subtype.
See this answer for another detailed explanation of the difference between bound and unbound class references: (link)