I'm using Moshi 1.8.0 on Android, and I'm following the instructions on the Moshi documentation create custom fields: https://github.com/square/moshi#custom-field-names-with-json
This means that my request data class is like this:
data class GetStuff(
#Json(name = "RequestContext") val context: RequestContext,
)
but the issue is that the actual HTTP request gets sent like this:
{"context": "blah... }
What I'm expecting to happen is for my request to be like this instead:
{"RequestContext": "blah... }
This seems to work fine for the response, but I can't figure out how to make it work for the request.
Thank you!
Is this not how the #Json(name = "") annotation works for request?
Ugh, I'm an idiot.
I'll answer this for whoever needs this in the future (likely me again).
In order to convert Json to an data class, you have to change your API call to be have this annotation:
#MoshiDeserialization
I knew that, and that's the magic annotation that makes deserialization work.
However, I didn't know I also needed a second magic annotation for the serialization part to work as well:
#MoshiSerialization
Now it works.
Related
I have an API which its response is kind of dynamic. I mean sometimes it return a Jason object with "token" value, and sometimes it returns with "message" value. For handling this scenario I decided to have both field in my data class like below:
data class response {
val message:String;
val token:String;
}
Now I want to make both fields optional in Kotlin serialization. I mean, I want to tell Kotlin serialization that if you couldn't find token in response JSON it's ok to ignore it.
How can I achieve this?
All Kotlin properties with default values are automatically optional.
All I need to do is this:
data class response {
val message:String="";
val token:String="";
}
I am facing a problem when trying to send an Http post request to the backend of my application. I am trying to send a post request like this :
{"a":[{"data":"https://news.google.com/rss/search?q=Pallini&hl=el"}]}
and instead it is being send something like this:
{"a":[{"data\":\"https://news.google.com/rss/search?q=Pallini&hl=el"}]}
or:
{"a":[{\"data\":\"https://news.google.com/rss/search?q=Pallini&hl=el\"}]}
So, I have a list that contains strings and every time I add the string that I want it to be shown in the json array, the code is like this:
var arrayListForA: ArrayList<JsonElement>? = arrayListOf()
config.forEach {
arrayListForA?.add(it)
}
The config is an another list that contains the jsons object as strings.
My question is, if there is any way to create the http post request body in Kotlin with the use of classes, objects etc, in a more automated way ?! for example, instead of a list with strings, I could use a list with Data class objects.
val dataList : ArrayList<Data> = arrayListOf()
where Data class is :
#Parcelize
data class Data(
#Expose #SerializedName("data") val data: String?
) : Parcelable
Is there any solution/idea to send the body of the post request as I need it ?
You can use retrofit and okhttp for this in Android. Retrofit helps you deal with networking easily. Also you will be able to post a custom data model as body in the api request. The interface will look something like given below. You can read more about retrofit here. retrofit
#POST(Urls.PURCHASE)
fun purchase(#Body purchaseAddonReqModel: PurchaseReqModel):Single<BaseResponse<EmptyResponse>>
Here you can add your custom model by adding the #Body annotation
I would like to know what is the best way to integrate Retrofit with MoShi on my Android Studio project.
First of all, I use the moshi converter of retrofit :
implementation 'com.squareup.retrofit2:converter-moshi:2.9.0'
I use the popular plugin "Json to Kotlin class" for the generation of my POJO :
https://plugins.jetbrains.com/plugin/9960-json-to-kotlin-class-jsontokotlinclass-
When I create a new POJO, I use the annotation "MoShi (Reflect)" :
It generate me a Pojo with fields like this one :
#Json(name = "image_url")
val imageUrl: String?
The problem is Retrofit2 seem don't like underscore in my fields name because I get null results with the underscored names.
Searching in the Github issues, I found a solution. It work if I use this annotation :
#field:Json(name = "image_url")
val imageUrl: String?
So my questions are :
Using the #field:Json annotation is the best way to use MoShi with Retrofit ?
If yes, how to generate easily a Kotlin class with this annotation (using the same plugin or an other) ?
As apparent from this part of Moshi's documentation: "There’s no field naming strategy, versioning, instance creators, or long serialization policy. Instead of naming a field visibleCards and using a policy class to convert that to visible_cards, Moshi wants you to just name the field visible_cards as it appears in the JSON."
The preferred way is for you to not use the annotation, but instead name the fields the same way as in the JSON. With Kotlin you can use backticks for names that wouldn't otherwise be valid, e.g.val `image-url`: String?
Of course you wouldn't want to be working with such names in other parts of your code, and for that you should have separate objects for describing the data as it appears on the back-end, and another object for how it should appear in your application. We call those DTOs (Data-Transfer Objects.)
I'm using Moshi for an Android App, but when the payload change for any reason, example: the backend add a new object type, the app will crash because I need to tell Moshi to get this new object type.
return Moshi.Builder()
.add(Adapter1())
.add(PolymorphicJsonAdapterFactory
.of(BaseClass::class.java, "baseclass")
.withSubtype(EntityOne::class.java, "entityone")
.withSubtype(EntityTwo::class.java, "entitytwo")
)
.build()
All works well, but if a new entity is coming inside the payload, the app won't show any data.
Example: an "entitythree" is added, to avoid the app stop showing info, I need to come and add
.withSubtype(EntityThree::class.java, "entitythree")
How can I avoid this behaviour, if the payload add new entities, nothing happen and the app will continue working well?
Thanks in advance
Have a class denoting an unknown entity:
data class UnknownEntity : BaseClass {
val baseclass: String = "unknown"
}
Then, add to your factory:
PolymorphicJsonAdapterFactory
.of(BaseClass::class.java, "baseclass")
.withSubtype(EntityOne::class.java, "entityone")
.withSubtype(EntityTwo::class.java, "entitytwo")
.withDefaultValue(UnknownEntity())
Then in the rest of your logic, where you manipulate with the acquired entities, just ignore the UnknownEntity instances, and the app should work as before.
I have converted the following Swift code:
struct FooModel: Decodable {
public let id: String
public let bars: [[BarModel]]
}
to this Kotlin code:
data class FooModel (val id: String, val bars: List<List<BarModel>>)
The issue I am encountering, is my id is coming in null for the Kotlin code (via gson). Everything else in the Kotlin conversion is working fine and the entire JSON is populating all data classes, except for this tiny piece (the id variable).
I suspect my conversion here is the cause, any ideas?
If the id should be nullable do it like this:
data class FooModel (
val id: String?,
val bars: List<List<BarModel>>
)
The question mark makes this property nullable.
If the JSON you are getting is correct (the id value is there and coming to you as a string), your code should work. It's unclear what could be going wrong here if that's the case.
However, it is worth knowing that there is a big potential "gotcha" with Gson that you should be aware of: it's possible to declare a variable of a data class as non-nullable but still get a null after conversion. This can happen when an expected value is missing from the JSON response. In these cases Gson does not throw an error and I only found out later when I got a crash trying to access the non-nullable variable that should never have made it to me as null. I discovered this is a consequence of Gson using something like Class.newInstance() instead of a regular constructor when it creates these data classes, and then uses reflection to populate the data. More is written about this in another answer here: Why Kotlin data classes can have nulls in non-nullable fields with Gson?
Depending on your use case you might consider this to be a design flaw and a reason to avoid Gson in favor of other JSON serialization libraries. My personal favorite at the moment is Square's Moshi.
You can check if the value type you are getting from server matches with your variable id i.e. String on both the sides. Secondly you can try using SerializedName("id") included in library:
implementation 'com.google.code.gson:gson:2.9.0'