How to use model in query string for Retrofit 2? - android

I am using a class model like below to hold the search data information,
data class SearchDataModel (
var keyword: String? = "",
var from: String? = "",
var to: String? = "",
var page: Int? = null,
var sortby: String? = null,
var orderby: String? = null,
var itemsperpage: Int? = null
)
For posting data we can use the call,
#POST("/data/save")
fun saveData(#Body postData: PostDataModel)
How to achieve the same for query string something like the one below?
#GET("/data/search")
fun searchData(#QueryString searchData: SearchDataModel)
I'm trying to prevent having lots of parameter in the function and have an optional query string parameter.

You should use URL encoding and pass Map into query.
#FormUrlEncoded
#GET("/data/search")
fun searchData(#FieldMap searchData: Map<String, String>)
Then have a method in your SearchDataModel to add properties to the Map<String, String> and pass it to searchData function.
data class SearchDataModel (
var keyword: String? = "",
var from: String? = "",
var to: String? = "",
var page: Int? = null,
var sortby: String? = null,
var orderby: String? = null,
var itemsperpage: Int? = null
fun toMap(): Map<String, String> {
return mapOf(
"keyword" to keyword,
"from" to from,
"to" to to,
"page" to page,
"sortby" to sortby,
"orderby" to orderby,
"itemsperpage" to itemsperpage
)
}
)
Use it like that: searchData(searchData: yourData.toMap())
Good luck :)

You can use #QueryMap annotation of Retrofit2.
Service interface should be like that;
interface YourService {
#GET("<yourEndPoint>")
fun fetchYourThings(
#QueryMap things: MutableMap<String, Any>
): Single<YourResponse>
}
model class;
data class Thing(
val data: String,
val data1: Int,
val data2: Boolean,
) {
fun toMap(): MutableMap<String, Any> = mutableMapOf(
"data" to data,
"data1" to data1,
"data2" to data2,
)
}
call your service from DataSource
class YourRemoteDataSource #Inject constructor(
private val yourService: YourService,
) : YourDataSource.Remote {
override fun fetchThings(
key: Things
): Observable<YourResponse> =
yourService
.fetchThingss(
key.toMap(),
).toObservable()
}

Related

Moshi JSON adapter with generic type

I want to use a Moshi adapter with a generic type.
Here is my generic type adapter code,
fun <T> getObjectFromJson(typeOfObject: Class<T>, jsonString: String): T? {
val moshi = Moshi.Builder().build()
val jsonAdapter: JsonAdapter<T> = moshi.adapter<T>(
typeOfObject::class.java
)
return jsonAdapter.fromJson(jsonString)!!
}
This code is not working. It is throwing an error,
Platform class java.lang.Class requires explicit JsonAdapter to be registered
But, If I don’t use a generic type like this,
fun getObjectFromJson(jsonString: String): UserProfile? {
val moshi = Moshi.Builder().build()
val jsonAdapter: JsonAdapter<UserProfile> = moshi.adapter<UserProfile>(
UserProfile::class.java
)
return jsonAdapter.fromJson(jsonString)!!
}
Then the code is working fine.
Here is the UserProfile class,
#Parcelize
#JsonClass(generateAdapter = true)
data class UserProfile(
#get:Json(name = "p_contact")
val pContact: String? = null,
#get:Json(name = "profile_pic")
var profilePic: String? = null,
#get:Json(name = "lname")
val lname: String? = null,
#get:Json(name = "token")
var token: String? = null,
#get:Json(name = "fname")
val fname: String? = null,
#SerializedName("_id")
#get:Json(name = "_id")
var id: String? = null,
#get:Json(name = "email")
var email: String? = null,
#SerializedName("refresh_token")
#get:Json(name = "refresh_token")
var refreshToken: String? = null
) : Parcelable
The typeOfObject is an instance of the Class<T> class already, you are calling ::class.java on it unnecessary: it returns Class<Class> and that's not what you want.
Just change
val jsonAdapter: JsonAdapter<T> = moshi.adapter<T>(typeOfObject::class.java)
to
val jsonAdapter: JsonAdapter<T> = moshi.adapter<T>(typeOfObject)
By the way: creating a new Moshi instance on each deserialization is suboptimal. You should reuse it.

Moshi: Platform class java.lang.Class requires explicit JsonAdapter to be registered

I'm using a Moshi adapter to get the object from a JSON string. But, I'm getting an error,
java.lang.IllegalArgumentException: Platform class java.lang.Class requires explicit JsonAdapter to be registered
All the data members of the UserProfile class are only String type then why it is asking to create an explicit JsonAdapter?
MoshiDataConverter
class MoshiDataConverter() {
fun <T> getObjectFromJson(typeOfObject: Class<T>, jsonString: String): T? {
val moshi = Moshi.Builder().build()
val jsonAdapter: JsonAdapter<T> = moshi.adapter<T>(
typeOfObject::class.java
)
return jsonAdapter.fromJson(jsonString)!!
}
}
getObject method
fun <T> getObject(#Keys key: String?, typeOfObject: Class<T>?): T? {
val value = getString(key, null) ?: return null
return MoshiDataConverter().getObjectFromJson(typeOfObject!!, value)
}
UserProfile.kt
#Parcelize
#JsonClass(generateAdapter = true)
data class UserProfile(
#get:Json(name = "p_contact")
val pContact: String? = null,
#get:Json(name = "profile_pic")
var profilePic: String? = null,
#get:Json(name = "lname")
val lname: String? = null,
#get:Json(name = "token")
var token: String? = null,
#get:Json(name = "fname")
val fname: String? = null,
#SerializedName("_id")
#get:Json(name = "_id")
var id: String? = null,
#get:Json(name = "email")
var email: String? = null,
#SerializedName("refresh_token")
#get:Json(name = "refresh_token")
var refreshToken: String? = null
) : Parcelable
Your typeOfObject parameter is already a class reference, so you don't need to call ::class.java on it before passing it to the Moshi adapter.
Try replacing
val jsonAdapter: JsonAdapter<T> = moshi.adapter<T>(
typeOfObject::class.java
)
with
val jsonAdapter: JsonAdapter<T> = moshi.adapter<T>(typeOfObject)

Json object response in kotlin

I am facing issue in get response in kotlin. My json response is:
{
"message":"Success",
"status":false,
"data":[
{
"id":"1",
"username":"doctor",
"phone":null,
"speciality":"General Physician",
"name":"Doctor",
"firstname":null,
"lastname":null,
"gender":"",
"age":null,
"dobirth":null,
"email":"doctor#live.com",
"country":"Pakistan",
"state":"Punjab",
"city":"Lahore",
"address":null,
"affiliation":"",
"degree":"MBBS",
"bio":null,
"password":"$2y$10$KF1zBxe07nPBW.0hFWiFfOjIur4cYYfP.LlQlujjcHq4WmQMLGWLK",
"remember_token":"UlekRgPJqWPx9AczdW2D7cyjiWkyU4mDpGYkR2QYovjsDCaVTt7adnQmSJQo",
"image":"1496739459-ariba.jpg",
"license_owner":"0",
"status":"0",
"switch_role":"1",
"invitation_code":"",
"created_at":"2018-10-01 07:55:47",
"updated_at":"2018-01-26 00:02:50",
"license_purchase_id":"0",
"profile_active":"0",
"pmdc":"",
"flag":"1"
},
{
"id":"2",
"username":"khawarshah",
"phone":null,
"speciality":"",
"name":"Syed Khawar",
"firstname":null,
"lastname":null,
"gender":"",
"age":null,
"dobirth":null,
"email":"jjshjasd#tech4lifeenterprises.com",
"country":"",
"state":"",
"city":"",
"address":null,
"affiliation":"",
"degree":"",
"bio":null,
"password":"$2y$10$3nG\/43tUdA2QKzinBPvA4.zqQHfxmR8sZ0LICQ3xg6LLr6mFYZq7q",
"remember_token":"teuzY7HKubHdQg9TXA3zgDJmszrNPm2vBg1226JmDPhk0APZuEafIUpNGKJ4",
"image":"",
"license_owner":"0",
"status":"1",
"switch_role":"1",
"invitation_code":"",
"created_at":"2019-04-19 07:08:10",
"updated_at":"2019-04-19 11:08:10",
"license_purchase_id":"0",
"profile_active":"0",
"pmdc":"",
"flag":"1"
}
]
}
ApiClient class and ApiFactory is
data class Users (
#Expose
#SerializedName("message")
val message: String,
#Expose
#SerializedName("status")
val status: String,
#Expose
#SerializedName("id")
val id: String,
#Expose
#SerializedName("username")
val username: String,
#Expose
#SerializedName("speciality")
val speciality: String,
#Expose
#SerializedName("firstname")
val firstname: String,
#Expose
#SerializedName("name")
val name: String,
#Expose
#SerializedName("lastname")
val lastname: String,
#Expose
#SerializedName("gender")
val gender: String,
#Expose
#SerializedName("age")
val age: String,
#Expose
#SerializedName("dobirth")
val dobirth: String,
#Expose
#SerializedName("email")
val email: String,
#Expose
#SerializedName("image")
val image: String,
#Expose
#SerializedName("switch_role")
val switch_role: String
)
And Main fragment is
class HomeFragment : Fragment() {
private lateinit var homeViewModel: HomeViewModel
lateinit var progerssProgressDialog: ProgressDialog
var dataList = ArrayList<Users>()
lateinit var recyclerView: RecyclerView
lateinit var adapter:ListAdapter
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
homeViewModel =
ViewModelProviders.of(this).get(HomeViewModel::class.java)
val root = inflater.inflate(R.layout.fragment_home, container, false)
val RecyclerView: RecyclerView = root.findViewById(R.id.recyclerView)
recyclerView = root.findViewById(R.id.recyclerView)
recyclerView.adapter= ListAdapter(dataList,requireContext())
recyclerView.layoutManager=LinearLayoutManager(requireContext(),LinearLayoutManager.VERTICAL,false)
progerssProgressDialog=ProgressDialog(requireContext())
progerssProgressDialog.setTitle("Loading")
progerssProgressDialog.setCancelable(false)
progerssProgressDialog.show()
getData()
return root
}
private fun getData() {
val call: Call<List<Users>> = ApiClient.getClient.getPhotos()
call.enqueue(object : Callback<List<Users>> {
override fun onResponse(call: Call<List<Users>>?, response: Response<List<Users>>?) {
progerssProgressDialog.dismiss()
dataList.addAll(response!!.body()!!)
recyclerView.adapter?.notifyDataSetChanged()
}
override fun onFailure(call: Call<List<Users>>, t: Throwable) {
progerssProgressDialog.dismiss()
}
})
}
}
but did not get response in recyclerview. what is the mistake?
Your data classes and JSON structure don't match.
Use JsonToKotlinClass plugin to create data class from json in Android Studio
Problem is with your data class "Users".. Check below data class for your reference:
data class Response(
#field:SerializedName("data")
val data: List<DataItem?>? = null,
#field:SerializedName("message")
val message: String? = null,
#field:SerializedName("status")
val status: Boolean? = null)
data class DataItem(
#field:SerializedName("country")
val country: String? = null,
#field:SerializedName("firstname")
val firstname: Any? = null,
#field:SerializedName("invitation_code")
val invitationCode: String? = null,
#field:SerializedName("flag")
val flag: String? = null,
#field:SerializedName("gender")
val gender: String? = null,
#field:SerializedName("city")
val city: String? = null,
#field:SerializedName("pmdc")
val pmdc: String? = null,
#field:SerializedName("bio")
val bio: Any? = null,
#field:SerializedName("created_at")
val createdAt: String? = null,
#field:SerializedName("switch_role")
val switchRole: String? = null,
#field:SerializedName("speciality")
val speciality: String? = null,
#field:SerializedName("password")
val password: String? = null,
#field:SerializedName("updated_at")
val updatedAt: String? = null,
#field:SerializedName("affiliation")
val affiliation: String? = null,
#field:SerializedName("license_owner")
val licenseOwner: String? = null,
#field:SerializedName("id")
val id: String? = null,
#field:SerializedName("state")
val state: String? = null,
#field:SerializedName("remember_token")
val rememberToken: String? = null,
#field:SerializedName("email")
val email: String? = null,
#field:SerializedName("image")
val image: String? = null,
#field:SerializedName("license_purchase_id")
val licensePurchaseId: String? = null,
#field:SerializedName("address")
val address: Any? = null,
#field:SerializedName("profile_active")
val profileActive: String? = null,
#field:SerializedName("degree")
val degree: String? = null,
#field:SerializedName("lastname")
val lastname: Any? = null,
#field:SerializedName("phone")
val phone: Any? = null,
#field:SerializedName("name")
val name: String? = null,
#field:SerializedName("dobirth")
val dobirth: Any? = null,
#field:SerializedName("age")
val age: Any? = null,
#field:SerializedName("username")
val username: String? = null,
#field:SerializedName("status")
val status: String? = null
)
Your Users class does not match the structure of your json, so you won't be able to parse it. Looking at your json, it's more like:
data class ResponseMessage(
val message: String,
val status: Boolean,
val data: List<User>
)
data class User(
val id: Long,
val username: String,
...
)

Android Kotlin Retrofit Post Request Inputted data does not sent

So I have to handle a POST request with its body data taken from some input in a form.
The Endpoint of this service is https://someUrl.com/switching-product/switch?orderID=A002&procode=0200011&nip=P19120
The Response returned from Postman is like this.
The body of this request is like this :
In this case, I have this Interface for handling a POST request:
///Endpoint: https://someUrl.com/switching-product/switch?orderID=A002&procode=0200011&nip=P19120
interface editProductDetail{
#FormUrlEncoded
#POST("switch")
fun editProductDetail(#Query("orderID") orderID: String,
#Query("procode") procode: String,
#Query("nip") nip : String,
#Field("procode_new") procodeNew: String,
#Field("item_qty_new") itemQtyNew: String,
#Field("item_price_new") itemPriceNew: String,
#Field("item_name_new") itemNameNew: String,
#Field("total_price_new") totalPriceNew: String): Call<OutstandingOrderDetailPOJODataClassDetailItem>
}
This is the data class I use:
data class OutstandingOrderDetailPOJODataClassDetailItem(
#field:SerializedName("item_price_new")
val itemPriceNew: Int? = null,
#field:SerializedName("item_name_new")
val itemNameNew: String? = null,
#field:SerializedName("total_price")
val totalPrice: Int? = null,
#field:SerializedName("item_price")
val itemPrice: Int? = null,
#field:SerializedName("item_name")
val itemName: String? = null,
#field:SerializedName("status_refund")
val statusRefund: String? = null,
#field:SerializedName("detail_id")
val detailId: Int? = null,
#field:SerializedName("procode_new")
val procodeNew: String? = null,
#field:SerializedName("refund_date")
val refundDate: String? = null,
#field:SerializedName("request_refund")
val requestRefund: String? = null,
#field:SerializedName("procode")
val procode: String? = null,
#field:SerializedName("last_update")
val lastUpdate: String? = null,
#field:SerializedName("item_qty_new")
val itemQtyNew: Int? = null,
#field:SerializedName("order_id")
val orderId: String? = null,
#field:SerializedName("total_price_new")
val totalPriceNew: Int? = null,
#field:SerializedName("item_qty")
val itemQty: Int? = null,
#field:SerializedName("refund")
val refund: Int? = null
)
As you can see above, the body has attributes that are included in the data class as well. (This data class is also used in a related service in the App that uses the inputted data of this service).
And this is the function to trigger the POST request:
NetworkConfig().editOutstandingOrderProductDetailService().editProductDetail(
selectedOrderId,
selectedProcode,
selectedNip,
procodeNew,
itemNewQty,
itemPriceNew,
itemNewName,
totalPriceNew
).enqueue(object :
Callback<OutstandingOrderDetailPOJODataClassDetailItem> {
override fun onFailure(call: Call<OutstandingOrderDetailPOJODataClassDetailItem>, t: Throwable) {
Log.i("Order", "It Failed!!")
if (call.isCanceled) {
Toast.makeText((activity as AppCompatActivity), "Request Aborted", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText((activity as AppCompatActivity), t.localizedMessage, Toast.LENGTH_SHORT).show()
}
}
override fun onResponse(
call: Call<OutstandingOrderDetailPOJODataClassDetailItem>,
response: Response<OutstandingOrderDetailPOJODataClassDetailItem>
) {
Log.i("Order", "Switching Process done!!!")
Log.i("Order", "Status: ${response.body()}")
}
})
From above, it prints the response in the logCat like this:
Am I missing something here? Or there's something I need to Change? If there's any detail that I missed to point out, just let me know!
Your request is a JSON object, not a formurl.
#Field tag is used when you want to pass your parameters as formurl
Use model class or JsonObject with #Body tag to pass parameters as JsonObject.
Model class Exmaple,
data class TestClass{
val text1: String,
val text2: String
}
Now Pass this class as request
#POST("URL")
fun apiName(#Body request: TestClass);
JSON Object Exmaple,
JSONObject paramObject = new JSONObject();
paramObject.put("key1", "value1");
paramObject.put("key1", "vaalue2");
Now pass it as String or JsonObject
#POST("URL")
fun apiName(#Body request: String); // pass as String

No args error retrofit request body

I am facing problem while sending json object body using retrofit to the server. Below is the error.
Failed to invoke public
com.nitesh.brill.saleslines._User_Classes.User_PojoClass.UpdatePreviousDetails()
with no args
code snippet
// Api endpoint
#Headers("Content-Type: application/json")
#POST("UpdatePreviousDetails/{Id}")
fun updatePreviousDetails(#Path("Id") Id: Int, #Body updateDetails :UpdatePreviousDetails): Call<UpdatePreviousDetails>
//pojo class
package com.nitesh.brill.saleslines._User_Classes.User_PojoClass
import java.util.*
/**
* Created by Nitesh Android on 16-08-2017.
*/
class UpdatePreviousDetails(
var CompanyName: String? = null!!,
var Designation: String? = null!!,
var DateOfJoin: Date? = null!!,
var DateOfLeaving: Date? = null!!,
var SectorPreviouslyWorked: String? = null!!,
var Id: Int? = null!!
) {
}
//sending data
val details = UpdatePreviousDetails("rr", "asm", date, date, "Pharmaceuticals",3)
val call = apiEndpointInterface!!.updatePreviousDetails(5, details)
call.enqueue(object :Callback<UpdatePreviousDetails> {
override fun onResponse(call: Call<UpdatePreviousDetails>?, response: Response<UpdatePreviousDetails>?) {
objUsefullData.showSnackBar("success")
UsefullData.Log("============="+response!!.body().toString())
}
override fun onFailure(call: Call<UpdatePreviousDetails>?, t: Throwable?) {
objUsefullData.showSnackBar("fail")
UsefullData.Log("============="+t)
}
})
I am using kotlin language
Your UpdatePreviousDetails class has to have a constructor with no params to enable Gson (inside Retrofit) to convert your object into JSON.
EDIT
class UpdatePreviousDetails() {
var CompanyName: String? = null
var Designation: String? = null
var DateOfJoin: Date? = null
var DateOfLeaving: Date? = null
var SectorPreviouslyWorked: String? = null
var Id: Int? = null
constructor(
CompanyName: String?,
Designation: String?,
DateOfJoin: Date?,
DateOfLeaving: Date?,
SectorPreviouslyWorked: String?,
Id: Int?
) : this() {
this.CompanyName = CompanyName
this.Designation = Designation
this.DateOfJoin = DateOfJoin
this.DateOfLeaving = DateOfLeaving
this.SectorPreviouslyWorked = SectorPreviouslyWorked
this.Id = Id
}
}

Categories

Resources