I have a simple class
#JsonClass(generateAdapter = true)
data class Person(
val name: String,
val age: Int
) {
val country: String = "None"
}
but when converting to json, Moshi skips country property.
How to include it in the resulting json?
Sadly, it's not possible.
More info and explanation here.
Related
Having trouble with parsing one single field from JSON response after enabling minify, with minify disabled all works correctly:
retrofit API call:
#FormUrlEncoded
#POST("/api/test")
fun test(#Field <some String fields>
): Observable<Response<TestListing>>
wrapped in repo
override fun test(<some String fields>): Observable<Response<TestListing>> {
return api.test(<some String fields>)
.subscribeOn(schedulers.io())
}
model:
data class TestListing (
#Json(name = "success") val success:Int,
#Json(name = "user") val user: TestUser?
)
TestUser class
data class TestUser(
#Json(name = "id") val id: Int,
#Json(name = "email") val email: String,
#Json(name = "name") val name: String,
#Json(name = "key") val remix_userkey: String,
#Json(name = "downloads_limit") val downloads_limit: Int?,
<some other fields>
)
and finally calling it in a viewModel
fun test(<some String fields>){
compositeDisposable.add(testRepo.test(<some String variables>)
.subscribeOn(schedulers.io())
.observeOn(schedulers.main())
.subscribe ({ testList ->
testListDebug.postValue(testList)
if (testList.isSuccessful) user.postValue(userList.body()?.user)
else {<some error posting>}
})
{ throwable -> <some actions>})
}
So without minifyEnabled it parses this JSON
{"success":1,"user":{"id":"123456","email":"test#test.com","name":"Test","remix_userkey":"abcd123abcd","downloads_limit":15}}
correctly, after I enable minify - id field is always 0.
Same JSON, but somehow it wraps in retrofit Response already with id=0 in the body(all other fields are parsed correctly)
example of testListDebug value from debugger after API call
Tried adding all library rules in proguard-rules.pro file, but with no effect; also tried adding #Keep annotation to TestUser class and renaming id field
Where I can dig from here? Is it something regarding Moshi or Retrofit/Okhttp?
Figured it out - needed to keep a custom moshi annotation class, which was used for parsing some field(which sometimes Int and sometimes Boolean) in other API calls and which was not used here. After adding keep annotation to it id is parsed fine
Very strange behavior since this annotation was not used here
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'm trying to read a json response from a webservice, but without success.
This is the json i receive:
{
"rsp": {
"#code": "0",
"#message": ""
},
"listOfStrings":[]
}
And this is relative data class where i parse response
data class Response(
val rsp : Rsp,
val listOfStrings : List<String>
)
data class Rsp(
#Json(name = "#code")
val code : String,
#Json(name = "#message")
val message : String
)
But it seems that moshi for some reason it's not able to parse json into object, because i always get Response object with all null fields.
So what's wrong? May the "#" character of json response fields cause problems?
UPDATE
Now i can parse correctly response by change #Json annotation into #field:Json:
data class Rsp(
#field:Json(name = "#code")
val code : String,
#field:Json(name = "#message")
val message : String
)
But i'm curious to know why it works.
#field:Json is required if you want moshi-kotlin to work with proguard according to the discussion here: https://github.com/square/moshi/issues/315
Try this model and let me know if it works:
#Parcelize
data class Response(
#Json(name = "rsp")
val rsp: Rsp,
#Json(name = "listOfStrings")
val listOfStrings: List<String>
) : Parcelable {
#Parcelize
data class Rsp(
#Json(name = "#code")
val code: String,
#Json(name = "#message")
val message: String
) : Parcelable
}
Edit:
If it didn't work, try to add back-slash behind those field names that have #.
Like: #Json(name = "\#code").
UPDATE AFTER QUESTION GOT UPDATE:
You need to add moshi-kotlin dependency and then using KotlinJsonAdapterFactory
val moshi = Moshi.Builder()
// ... add your own JsonAdapters and factories ...
.add(KotlinJsonAdapterFactory())
.build()
Then moshi couldn't ignore #Json.
I wonder what is the way to convert a Kotlin data class into its equivalent json string. Json keys should be configurable.
Let's say I have a class,
data class Student(name: String?, roll: Int?, mark: Int?) {
}
I want to make a Json from this Student object where keys will be,
stundent_name, stundent_roll, stundent_mark
Moreover, I may also need to make a json from list of student with key students. How can I do so? I know using Gson I can create object from json string. How to do the reverse?
data class Student(
#SerializedName("stundent_name")
val name: String?,
#SerializedName("stundent_roll")
val roll: Int?,
#SerializedName("stundent_mark")
val mark: Int?
)
And the code for convertion is:
val gson = Gson()
val student = Student("John", 1, 5)
gson.toJson(student)
This code makes String like this:
{"stundent_mark":5,"stundent_name":"John","stundent_roll":1}
And if you need to create JsonArray, just do the same with your List of students:
gson.toJson(list)
So I've been having some trouble with an api that I want to consume from my application when dealing with multiple types from the keys and values of the response json.
Let me show you the json response:
{
"error":[
],
"result":{
"field1":[
[
1544258160,
"57.15",
"57.15",
"57.15",
"57.15",
"0.00",
"0.00000000",
0
],
[
1544258220,
"56.89",
"56.89",
"56.89",
"56.89",
"56.89",
"2.94406281",
1
]
],
"field2":1544301240
}
}
and here is the representation of the pojo class:
data class Response(val error: List<String>, val result: LinkedTreeMap<String, List<List<Result>>>)
data class Result(
val time: Double,
val open: String,
val high: String,
val low: String,
val close: String,
val vwap: String,
val volume: String,
val count: Double
)
I know that the current structure fails to represent the json format. but I have run out of ideas.
btw the stack error is saying this:
Expected BEGIN_OBJECT but was NUMBER
edit: adding a bit more context
I'm using Gsonconverter for the retrofit builder.
val retrofit = Retrofit.Builder().baseUrl(API_URL).client(client)
.addConverterFactory(GsonConverterFactory.create()).build()
You could build a Gson JsonDeserializer for the filed1, filed2 types.
It is more manually written code, but this way you can check the type of the filed and call the right deserializer.
yeah,what make the error is your response field(result),it's different type.however.you just use the first type Response(val error: List<String>, val result:LinkedTreeMap<String, List<List<Result>>> receive all the json.
in my view,you can use below ways to solve it.
First,redefine you receive model,maybe you can use the Gson JsonObject and when deal with value remember check the type.
Second,discuss with your background server engineer,agree on the every type response.may be the "field2" can be "field2:[[]]"
Next,change your model.may be you define below it
#Serialized("field1")
LinkedTreeMap<String, List<List<Result>>> field;
#Serialized("field2")
String fields;
hope to help you.
Your problem is here:
"field2":1544301240
You defined like below:
val result: LinkedTreeMap<String, List<List<Result>>>
but instead of that you get number!!!
EDIT
Just try this:
data class Response(val error: List<String>, val result: YourModel)
data class YourModel(val field1: LinkedTreeMap<String, List<List<Result>>>, val field2: Double)
data class Result(
val time: Double,
val open: String,
val high: String,
val low: String,
val close: String,
val vwap: String,
val volume: String,
val count: Double
)
it has been a while but still i already came to a conclusion of what i need.
data class OHLCResponse(
private val error: List<String>,
val result: LinkedTreeMap<String, Any>)
turns out that on the LinkedTreeMap class i just needed to pass the second class type parameter as Any and it can be down casted to anything that you need.
also i leave here a nice tool that can help you map your json responses into kotlin plain objects/DTOs:
https://app.quicktype.io/
thanks all for your responses.
I solved the problem with such a solution in adapter class .
override fun getItemCount(): Int = leagueTableList[0].size
override fun onBindViewHolder(holder: LeagueTableViewHolder, position: Int) {
val gs = Gson()
val js = gs.toJson(leagueTableList[0][position])
val standing = gs.fromJson(js, Standing::class.java)
holder.view.table = standing
holder.bind(standing,onItemClick)
}