Related
I'm trying to get data from API. My app crashes and gives me this message:
kotlinx.serialization.json.internal.JsonDecodingException: Expected
start of the array '[', but had 'EOF' instead
JSON input: .....f2afd0-71e8-5229-9ac9-17d602cd35e4"}],"meta":{"hits":3934}}}
I'm facing this error since a week and I can't find out a solution for it. I need help.
This is the json rsponse:
{"copyright":"Copyright (c) 2022 The New York Times Company. All Rights Reserved.","response":{"docs":[{"abstract":"A career in health journalism has taught me that when it comes to living well, it’s the inner workout that counts the most.","web_url":"https://www.nytimes.com/2022/03/31/well/mind/health-fitness-lessons.html","snippet":"A career in health journalism has taught me that when it comes to living well, it’s the inner workout that counts the most.","lead_paragraph":"When I first started writing about health more than 20 years ago, my columns mostly focused on the physical body: A healthy diet, exercise and screening for disease were regular topics.","source":"The New York Times","multimedia":[{"rank":0,"subtype":"xlarge","caption":null,"credit":null,"type":"image","url":"images/2022/04/05/well/31Well-NL-InnerFitness/31Well-NL-InnerFitness-articleLarge.jpg","height":400,"width":600,"subType":"xlarge","crop_name":"articleLarge","legacy":{"xlarge":"images/2022/04/05/well/31Well-NL-InnerFitness/31Well-NL-InnerFitness-articleLarge.jpg","xlargewidth":600,"xlargeheight":400}},{"rank":0,"subtype":"jumbo","caption":null,"credit":null,"type":"image","url":"images/2022/04/05/well/31Well-NL-InnerFitness/31Well-NL-InnerFitness-jumbo.jpg","height":682,"width":1024,"subType":"jumbo","crop_name":"jumbo","legacy":{}},{"rank":0,"subtype":"superJumbo","caption":null,"credit":null,"type":"image","url":"images/2022/04/05/well/31Well-NL-InnerFitness/31Well-NL-InnerFitness-superJumbo.jpg","height":1166,"width":1750,"subType":"superJumbo","crop_name":"superJumbo","legacy":{}},{"rank":0,"subtype":"thumbnail","caption":null,"credit":null,"type":"image","url":"images/2022/04/05/well/31Well-NL-InnerFitness/31Well-NL-InnerFitness-thumbStandard.jpg","height":75,"width":75,"subType":"thumbnail","crop_name":"thumbStandard","legacy":{"thumbnail":"images/2022/04/05/well/31Well-NL-InnerFitness/31Well-NL-InnerFitness-thumbStandard.jpg","thumbnailwidth":75,"thumbnailheight":75}},{"rank":0,"subtype":"thumbLarge","caption":null,"credit":null,"type":"image","url":"images/2022/04/05/well/31Well-NL-InnerFitness/31Well-NL-InnerFitness-thumbLarge.jpg","height":150,"width":150,"subType":"thumbLarge","crop_name":"thumbLarge","legacy":{}}],"headline":{"main":"For Better Health, Try Fitness From the Inside Out","kicker":null,"content_kicker":null,"print_headline":"","name":null,"seo":null,"sub":null},"keywords":[{"name":"subject","value":"Content Type: Service","rank":1,"major":"N"},{"name":"subject","value":"Anxiety and Stress","rank":2,"major":"N"},{"name":"subject","value":"Meditation","rank":3,"major":"N"},{"name":"subject","value":"Exercise","rank":4,"major":"N"}],"pub_date":"2022-04-01T00:00:08+0000","document_type":"article","news_desk":"Well","section_name":"Well","subsection_name":"Mind","byline":{"original":"By Tara Parker-Pope","person":[{"firstname":"Tara","middlename":null,"lastname":"Parker-Pope","qualifier":null,"title":null,"role":"reported","organization":"","rank":1}],"organization":null},"type_of_material":"News","_id":"nyt://article/0115da20-943c-52dc-8992-817faa170aa1","word_count":1235,"uri":"nyt://article/0115da20-943c-52dc-8992-817faa170aa1"},{"abstract":"He was famous for his portraits of supermodels and his close relationship with Princess Diana. His images are part of the fashion canon.","web_url":"https://www.nytimes.com/2022/03/31/style/patrick-demarchelier-dead.html","snippet":"He was famous for his portraits of supermodels and his close relationship with Princess Diana. His images are part of the fashion canon.","lead_paragraph":"Patrick Demarchelier, a photographer whose work helped define fashion and celebrity in the late 20th and early 21st centuries, died on Thursday. He was 78.","print_section":"A","print_page":"26","source":"The New York Times","multimedia":[{"rank":0,"subtype":"xlarge","caption":null,"credit":null,"type":"image","url":"images/2022/04/03/obituaries/02DEMARCHELIER-print2/00DEMARCHELIER-diana-articleLarge.jpg","height":817,"width":600,"subType":"xlarge","crop_name":"articleLarge","legacy":{"xlarge":"images/2022/04/03/obituaries/02DEMARCHELIER-print2/00DEMARCHELIER-diana-articleLarge.jpg","xlargewidth":600,"xlargeheight":817}},{"rank":0,"subtype":"jumbo","caption":null,"credit":null,"type":"image","url":"images/2022/04/03/obituaries/02DEMARCHELIER-print2/00DEMARCHELIER-diana-jumbo.jpg","height":1024,"width":752,"subType":"jumbo","crop_name":"jumbo","legacy":{}},{"rank":0,"subtype":"superJumbo","caption":null,"credit":null,"type":"image","url":"images/2022/04/03/obituaries/02DEMARCHELIER-print2/00DEMARCHELIER-diana-superJumbo.jpg","height":2048,"width":1504,"subType":"superJumbo","crop_name":"superJumbo","legacy":{}},{"rank":0,"subtype":"thumbnail","caption":null,"credit":null,"type":"image","url":"images/2022/04/03/obituaries/02DEMARCHELIER-print2/00DEMARCHELIER-diana-thumbStandard-v2.jpg","height":75,"width":75,"subType":"thumbnail","crop_name":"thumbStandard","legacy":{"thumbnail":"images/2022/04/03/obituaries/02DEMARCHELIER-print2/00DEMARCHELIER-diana-thumbStandard-.....
These are some of the data classes
#Serializable
data class Article(
#SerialName("copyright")
val copyright: String?,
#SerialName("response")
val response: Response?
)
#Serializable
data class Response(
#SerialName("docs")
val docs: List<Doc>?,
#SerialName("meta")
val meta: Meta?
)
#Serializable
data class Doc(
#SerialName("abstract")
val `abstract`: String?,
#SerialName("byline")
val byline: Byline?,
#SerialName("document_type")
val documentType: String?,
#SerialName("headline")
val headline: Headline?,
#SerialName("_id")
val id: String?,
#SerialName("keywords")
val keywords: List<Keyword>?,
#SerialName("lead_paragraph")
val leadParagraph: String?,
#SerialName("multimedia")
val multimedia: List<Multimedia>?,
#SerialName("news_desk")
val newsDesk: String?,
#SerialName("print_page")
val printPage: String?,
#SerialName("print_section")
val printSection: String?,
#SerialName("pub_date")
val pubDate: String?,
#SerialName("section_name")
val sectionName: String?,
#SerialName("snippet")
val snippet: String?,
#SerialName("source")
val source: String?,
#SerialName("subsection_name")
val subsectionName: String?,
#SerialName("type_of_material")
val typeOfMaterial: String?,
#SerialName("uri")
val uri: String?,
#SerialName("web_url")
val webUrl: String?,
#SerialName("word_count")
val wordCount: Int?
)
This is the provide function for the retrofit instance:
#ExperimentalSerializationApi
#Provides
#Singleton
fun provideRetrofitInstance(okHttpClient: OkHttpClient): Retrofit {
val contentType = "application/json".toMediaType()
return Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(json.asConverterFactory(contentType))
.build()
}
you should be using this plugin
this plugin makes your work easy
after installing
and paste all JSON
before using read all documents Link
I recommend using Gson Or moshi to Convert Json Response.
Add dependency in build.Gradle (moshi or gson)
configure retrofit .addConverterFactory(MoshiConverterFactory.create(moshi)) for moshi
Annotate data class with #Json for moshi or #SerializableName for Gson.
Hope it helps !
I'm currently making my first custom Android app for a project and I can't resolve the following issue. (I made a lot of research but didn't find anything like this)
2020-02-15 11:41:30.075 10337-10337/com.example.quickmatch I/SigninFragmentViewModel: Required value 'surname' missing at $post
I'm trying to post this custom object to my backend server online :
#JsonClass(generateAdapter = true)
data class PlayerObject(
val id : Int?,
#Json(name = "surname") val surname : String,
#Json(name = "first_name") val firstName : String,
val pseudo : String,
#Json(name = "mdp") val password : String,
#Json(name = "mail_address") val mailAddress : String,
#Json(name = "phone_number") val phoneNumber : String?,
#Json(name = "scored_goals") val scoredGoals : Int,
#Json(name = "conceded_goals") val concededGoals : Int,
#Json(name = "matches_played") val matchesPlayed : Int,
val victories : Int,
val avatar : String?,
val bio : String?
)
I'm using Retrofit2 and Moshi with coroutines :
implementation "com.squareup.retrofit2:retrofit:2.5.0"
implementation "com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2"
implementation "com.squareup.retrofit2:converter-moshi:2.5.0"
Here is my Retrofit instance :
/* Create Moshi object which will parse the responses */
private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
/* Retrofit builder with converter for response and base url */
private val retrofit = Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.baseUrl(BASE_URL)
.build()
To send my POST request I made this method (It should send me back the player created if successful) :
#POST("...")
fun addPlayer(#Body player: PlayerObject) : Deferred<PlayerObject>
And I call it passing this Object :
var newPlayerObject = PlayerObject(null, name, firstName, pseudo, password, mailAddress, phoneNumber, 0, 0, 0, 0, null, null )
I am getting the non-null values from EditTexts with a basic binding.editText.text.toString()
The big problem is that I logged every value (name, surname, etc...) both in my ViewModel before the request and in my UI in the button onClickListener that triggers the ViewModel and everything looks fine and matches what I type in the Edits but when the app posts it seems that every attribute of the object is set to null. Since surname is not nullable I get the previous error. I tried to make it nullable and it is the same issue with the next attributes.
Also note that passing an object with every attribute null works.
I've found specifying use-site targets helpful for fields in a Kotlin data class.
What happens if you try modifying your annotations to the following format?
#field:Json(name = "surname") val surname : String
I finally fixed this issue. The problem was created by the phone_number field which I wanted to be optional. Indeed, when the corresponding EditText was empty when posting, the value wasn't put to null but something "empty" (not just ""), that explain the parsing error. So I just added a condition to set this field at null if the EditText isn't filled.
I am writing a simple Android app in Kotlin, which will show prayers divided into categories to user. There are 5 JSON files in assets folder, each of them has just around 10 KiB.
I use Klaxon for parsing the JSON files into those two data classes:
data class Prayer(val prayerName: String, val verseTitle: String, val verseBody: String,
val prayerLine: String, val prayerBody: String, val prayerEnding: String)
data class PrayerCategory(val title: String, val bgImage: String, val headerImage: String,
val prayers : List<Prayer>)
Here is the code I use for parsing the prayers:
private fun loadPrayerNames(jsonFile: String) {
val millis = measureTimeMillis {
val input = assets.open("${jsonFile}.json")
val prayerCategory = Klaxon().parse<PrayerCategory>(input)
if (prayerCategory != null) {
for (prayer in prayerCategory.prayers) {
val prayerName = prayer.prayerName
prayersMap[prayerName] = prayer
}
}
}
println("Loading prayer category took $millis ms.")
}
As you can see, there is just one access to assets. No assets.list(), no bullshit.
And as you noticed, I have measured the time.. make your guesses.. Here is the debug output:
Loading prayer category took 3427 ms.
Yup, that's right. Loading and parsing 10KiB big JSON took 3.5 SECONDS! I repeat. No rocket science involved. Just parsing 10 KiB JSON. 3.5 seconds..... Hmm..
Btw, I am testing it on Nokia 6.1, which is a pretty snappy phone.
So.. my questions:
What causes this behaviour?
Is there any way how to speed this up other than building a database for storing about 50 prayers?
I will be very thankful for your help!
Android assets seem to have bad reputation when it comes to performance. However, my testing proved that in this situation, it was Klaxon library who was responsible.
After finding major performance problem in Klaxon (see https://github.com/cbeust/klaxon/issues/154), which is still not fixed, I have tried recommended alternative: Moshi (https://github.com/square/moshi).
Moshi did improve performance, but parsing my JSON still took about 1 second.
After these experiments, I have resorted to the good old fashioned parsing using JSONObject:
data class Prayer(val prayerName: String, val verseTitle: String, val verseBody: String,
val prayerLine: String, val prayerBody: String, val prayerEnding: String) {
companion object {
fun parseJson(json: JSONObject) : Prayer = Prayer(json.getString("prayerName"),
json.getString("verseTitle"), json.getString("verseBody"),
json.getString("prayerLine"), json.getString("prayerBody"),
json.getString("prayerEnding"))
}
}
data class PrayerCategory(val title: String, val bgImage: String, val headerImage: String,
val prayers : List<Prayer>) {
companion object {
fun parseJson(json: JSONObject): PrayerCategory {
val prayers = ArrayList<Prayer>()
val prayersArray = json.getJSONArray("prayers")
for(i in 0 until prayersArray.length()) {
prayers.add(Prayer.parseJson(prayersArray.getJSONObject(i)))
}
return PrayerCategory(json.getString("title"), json.getString("bgImage"),
json.getString("headerImage"), prayers)
}
}
}
This has reduced parsing time from 3427 ms to 13ms. Case closed. ;-)
I am trying to create a time stamp using the firebase server value.
In the documentation in says to use
firebase.database.ServerValue.TIMESTAMP
*edit: before I didn't realize I was looking at the JS documentation. I've relinked it to the Android ones though they don't seem to specify a format there. I'm still looking.
But it return unresolved referance "firebase"
I've looked at other similar question, and they offered other formats like Firebase.ServerValue.TIMESTAMP and i've tried them but they don't work either and I think they're outdated.
My other firebase services are working just fine (Authorization, Database & Storage) so I can't understand why am I getting this error.
I am trying o achive that to create a simple timestamp the I will later, after pulled from the server, would convert to a nicer format with PrettyTime.
This is the part of the code the is giving me the error:
class NewQuestionActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_new_question)
val questionTitle : EditText = findViewById(R.id.new_question_title)
val questionDetails = findViewById<EditText>(R.id.new_question_details)
val questionTags = findViewById<EditText>(R.id.new_question_tags)
val questionButton = findViewById<Button>(R.id.new_question_btn)
questionButton.setOnClickListener {
postQuestion(questionTitle.text.toString(), questionDetails.text.toString(), questionTags.text.toString(), firebase.database.ServerValue.TIMESTAMP)
}
}
private fun postQuestion(title : String, details : String, tags : String, timestamp : String) {
val uid = FirebaseAuth.getInstance().uid
val ref = FirebaseDatabase.getInstance().getReference("/questions").push()
val newQuestion = Question(title, details, tags, timestamp)
ref.setValue(newQuestion)
.addOnSuccessListener {
Log.d("postQuestionActivity", "Saved question to Firebase Database")
}.addOnFailureListener {
Log.d("postQuestionActivity", "Failed to save question to database")
}
}
}
It is very odd a class start with lower case. I guess the most suitable approach is to allow the IDE to make the import for you, try with
ServerValue.TIMESTAMP
I'm integrating with the Room persistence library. I have a data class in Kotlin like:
#Entity(tableName = "story")
data class Story (
#PrimaryKey val id: Long,
val by: String,
val descendants: Int,
val score: Int,
val time: Long,
val title: String,
val type: String,
val url: String
)
The #Entity and #PrimaryKey annotations are for the Room library. When I try to build, it is failing with error:
Error:Cannot find setter for field.
Error:Execution failed for task ':app:compileDebugJavaWithJavac'.
> Compilation failed; see the compiler error output for details.
I also tried providing a default constructor:
#Entity(tableName = "story")
data class Story (
#PrimaryKey val id: Long,
val by: String,
val descendants: Int,
val score: Int,
val time: Long,
val title: String,
val type: String,
val url: String
) {
constructor() : this(0, "", 0, 0, 0, "", "", "")
}
But this doesn't work as well. A thing to note is that it works if I convert this Kotlin class into a Java class with getters and setters. Any help is appreciated!
Since your fields are marked with val, they are effectively final and don't have setter fields.
Try switching out the val with var.
You might also need to initialize the fields.
#Entity(tableName = "story")
data class Story (
#PrimaryKey var id: Long? = null,
var by: String = "",
var descendants: Int = 0,
var score: Int = 0,
var time: Long = 0L,
var title: String = "",
var type: String = "",
var url: String = ""
)
EDIT
The above solution is a general fix for this error in Kotlin when using Kotlin with other Java libraries like Hibernate where i've seen this as well. If you want to keep immutability with Room, see some of the other answers which may be more specific to your case.
In some cases immutability with Java libraries is simply not working at all and while making sad developer noises, you have to switch that val for a var unfortunately.
Hey I don't know if everyone know or not, but you can not have column which is starting from is into Room.
For example you can't have like this
#Entity(tableName = "user")
data class User (
#PrimaryKey var id: Long? = null,
var userName: String = "",
var isConnectedToFB: Boolean = false,
)
If you have #Ignore field in the data class constructor you need to move it to class body like this:
#Entity(primaryKeys = ["id"])
data class User(
#field:SerializedName("id")
val id: Int,
#field:SerializedName("name")
val name: String,
#field:SerializedName("age")
val age: Int
) {
#Ignore
val testme: String?
}
All kudos go to marianperca on GitHub: https://github.com/android/architecture-components-samples/issues/421#issuecomment-442763610
There is an issue in room db library java code generation.
I was using optional field isFavorite. It gives me same error then I change my field name to favorite then compiled.
before
var isFavorite: Int? = 0,
after changing working fine
var favorite: Int? = 0,
Thanks
According to https://stackoverflow.com/a/46753804/2914140 if you have an autogenerated primary key, you should write so:
#Entity(tableName = "story")
data class Story (
val by: String,
val descendants: Int,
val score: Int,
val time: Long,
val title: String,
val type: String,
val url: String
) {
#PrimaryKey(autoGenerate = true)
var id: Int = 0
}
Note that #PrimaryKey is written inside the class body and contains modifier var.
If you later want to update a row in a database with different parameters, use these lines:
val newStory = story.copy(by = "new author", title = "new title") // Cannot use "id" in object cloning
newStory.id = story.id
dao.update(newStory)
UPDATE
I still don't use AndroidX, and Room is 'android.arch.persistence.room:runtime:1.1.1'.
You can extend this class from Serializable. But if you want to extend it from Parcelable, you will get a warning (over id variable): Property would not be serialized inro a 'Parcel'. Add '#IgnoredOnParcel' annotation to remove this warning:
Then I moved an id from the body to the constructor. In Kotlin I use #Parcelize to create Parcelable classes:
#Parcelize
#Entity(tableName = "story")
data class Story (
#PrimaryKey(autoGenerate = true)
var id: Int = 0,
val by: String,
val descendants: Int,
val score: Int,
val time: Long,
val title: String,
val type: String,
val url: String
) : Parcelable
Had this error in Java.
You cannot have a column starting with is or is_ in Java.
Try renaming the column.
Another solution:
You either have to pass the field in the constructor and initialize it with the constructor argument, or create a setter for it.
Example:
public MyEntity(String name, ...) {
this.name = name;
...
}
public void setName(String name) {
this.name = name;
}
This error will be thrown if your column starts with Is:
#ColumnInfo(name = "IsHandicapLeague")
#NonNull
var isHandicapLeague: String = "Y"
Add a default set() function to eliminate
fun setIsHandicapLeague(flag:String) {
isHandicapLeague = flag
}
Just make the variables mutable, change val into var for Kotlin, Or private into public for Java
This is a bug and is fixed in Room 2.1.0-alpha01
https://developer.android.com/jetpack/docs/release-notes#october_8_2018
Bug Fixes
Room will now properly use Kotlin’s primary constructor in
data classes avoiding the need to declare the fields as vars.
b/105769985
I've found that another cause of this compilation error can be due to the use of the Room's #Ignore annotation on fields of your entity data class:
#Entity(tableName = "foo")
data class Foo(
// Okay
#PrimaryKey
val id: String,
// Okay
val bar: String,
// Annotation causes compilation error, all fields of data class report
// the "Cannot find setter for field" error when Ignore is present
#Ignore
val causeserror: String
)
The same error also seems to happens when using the #Transient annotation.
I've noticed this issue using version 2.2.2 of Room:
// build.gradle file
dependencies {
...
kapt "androidx.room:room-compiler:2.2.2"
...
}
Hope that helps someone!
You can try to rename id variable to another name. It worked for me ;
var id: Long? = null
to
var workerId: Long? = null
If you have to name as id and you are using retrofit, then you may need to add SerializedName("id")
Another cause of this may be the naming of the field. If you use any of the pre-defined keywords, you will get the same error.
For instance, you can not name your column "is_active".
Reference: http://www.sqlite.org/lang_keywords.html
It seems like Room and Kotlin versions need to be matched. I have same issue with Room 2.3.0 and Kotlin 1.6.10 but it's ok with Kotlin 1.5.20. It looks ok after I updated Room to 2.4.2.
https://youtrack.jetbrains.com/issue/KT-45883
Also there is a possible solution to use #JvmOverloads constructor for better Java compability.
Updating Room library to the latest version 2.4.2 solve the issue
The correct way to fix this issue would be simply updating to Room v2.4.3 or higher.
Workaround
If you're running on an older version of Room, one that uses an old version of the kotlinx-metadata-jvm library which doesn't understand 1.5.x metadata, a simple workaround would be adding the following line to your build.gradle:
kapt "org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.5.0"
Source: https://youtrack.jetbrains.com/issue/KT-45883/KAPT-Cannot-find-setter-for-field-compiling-projects-with-Room-db-breaks-using-150-M2
Just an update if somebody comes across this thread in 2019, after spending hours digging online on why this should work, but it doesn't.
Using val works as expected if you are using the AndroidX version ( androidx.room:room-<any>:2.*) but it doesn't when using the old android.arch.persistence.room:<any>:1.1.1 and it seems that version 2.* wasn't released on this latter repo.
Edit: typos
If you want the val immutability available for your entity, it is possible.
You should update to AndroidX room current version.
Check for the related issue here it is marked as Won't Fix
Now they have release a fix related to the issue with version 2.0.0-beta01
Now you can use immutable val with default value e.g:
#Entity("tbl_abc")
data class Abc(
#PrimaryKey
val id: Int = 0,
val isFavourite: Boolean = false
)
Previously, the above snippet will throw an error of Cannot find setter for field. Changing into var is a great workaround, but I prefer for the entity class to be immutable from outside invocation
You can now start your field with is but you can't have a number next to the is like : is2FooSelected, you have to rename to isTwoFooSelected.
I think that the variable we wrote as id is getting mixed up with the id in the system. Therefore, when I define it as uuid, my error is resolved. I think it will be solved too. Also, try using var instead of val.
#PrimaryKey(autoGenerate = true)
var uuid:Int=0
Just use var instead of val and if you are using private keyword, make it public.
#Entity(tableName = "story")
data class Story (
#PrimaryKey val id: Long,
var by: String,
var descendants: Int,
var score: Int,
var time: Long,
var title: String,
var type: String,
var url: String
)