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
Related
I looking for a way to send a zip file for a server using retrofit in kotlin. Some functions are deprecated that's make this more dificulte. When I star the request the response is NULL, there is no error.
That is the function who start the request:
fun sendZipeFile() {
val retrofitClient = NetworkUtils
.getRetrofitInstance("http://url.com")
val sendZipeFileEndPoint = retrofitClient.create(SendZipeFile::class.java)
var file = File("/storage/emulated/0/Download/file.zip")
val requestImageFile = file?.asRequestBody("multipart/form-data".toMediaType())
val callback = startZipeFileUpload.zipeFileUpload("application/zip","Atualização", requestImageFile, "0.0.2")
callback.enqueue(object : Callback<UploadZipFile> {
override fun onFailure(call: Call<UploadZipFile>, t: Throwable) {
t.message
t.fillInStackTrace()
}
override fun onResponse(call: Call<UploadZipFile>, response: Response<UploadZipFile>) {
val responseString = response.body()
}
})
}
Below is an interface
interface SendZipeFile{
#Headers("Content-Type: multipart/form-data")
#Multipart
#POST("updateModelsAndParameters/" +
"")
fun zipeFileUpload(
#Header("Content-Type") contentType: String?,
#Part("description") description: String?,
#Part("Zip") file: RequestBody?,
#Part("flag")flag: String?,
): Call<UploadZipFile>
}
And the data class
import com.google.gson.annotations.SerializedName
data class UploadZipFile(
#SerializedName("status_code") var statusCode: Int? = null,
#SerializedName("message") var message: String? = null,
#SerializedName("elapsed_ms") var elapsedMs: Double? = null,
#SerializedName("response") var resposta: Boolean? = null,
)
i have problem about calling object, i think the problem is because nested json i convert to gson with pojo, but i do not know what code i must change to solved the problem
i receive this when start the app
E/MainActivity: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $
can you help me to figure out what i must to change so the problem "Expected BEGIN_OBJECT but was BEGIN_ARRAY" can solved?
this is my original json json
this is my data class
data class JadwalItem(
#field:SerializedName("jadwal")
val jadwal: Jadwal? = null,
#field:SerializedName("query")
val query: Query? = null,
#field:SerializedName("status")
val status: String? = null
)
data class Data(
#field:SerializedName("imsak")
val imsak: String? = null,
#field:SerializedName("isya")
val isya: String? = null,
#field:SerializedName("dzuhur")
val dzuhur: String? = null,
#field:SerializedName("dhuha")
val dhuha: String? = null,
#field:SerializedName("subuh")
val subuh: String? = null,
#field:SerializedName("terbit")
val terbit: String? = null,
#field:SerializedName("ashar")
val ashar: String? = null,
#field:SerializedName("tanggal")
val tanggal: String? = null,
#field:SerializedName("maghrib")
val maghrib: String? = null
)
data class Jadwal(
#field:SerializedName("data")
val data: Data? = null,
#field:SerializedName("status")
val status: String? = null
)
data class Query(
#field:SerializedName("kota")
val kota: String? = null,
#field:SerializedName("format")
val format: String? = null,
#field:SerializedName("tanggal")
val tanggal: String? = null
)
this is my service
#GET("tanggal/{year}-{monthNumber}-{day}")
fun getPosts(
#Path("year") year: String,
#Path("monthNumber") monthNumber: String,
#Path("day") day: String
): Call<Posts>
this is my posts.kt
class Posts : ArrayList<Data>()
and this is i call service in mainactivity
val dateNow = Calendar.getInstance().time
val day = DateFormat.format("dd", dateNow) as String // 20
val monthNumber = DateFormat.format("MM", dateNow) as String // 06
val year = DateFormat.format("yyyy", dateNow) as String // 2013
val retrofit = Retrofit.Builder()
.baseUrl("https://api.banghasan.com/sholat/format/json/jadwal/kota/663/")
.addConverterFactory(GsonConverterFactory.create())
.build()
val service: Service = retrofit.create(Service::class.java)
service.getPosts(year,monthNumber,day).enqueue(object : Callback<Posts> {
override fun onResponse(call: Call<Posts>, response: Response<Posts>) {
val body = response.body()
actionBar?.title = "Latihan"
}
override fun onFailure(call: Call<Posts>, t: Throwable) {
Log.e("MainActivity", t.message.toString())
}
})
The JSON response is an array of "Post" objects, right? When your "getPosts" method return "Posts" object, you are expecting a JSON that represents "Post" object, not an array of it.
Change your:
class Posts : ArrayList<Data>()
to
data class Posts (val status: String, val query: Query, val jadwal: Jadwal) //Don't forget to use #SerializedName
Here you are telling your code that you expect "Post" response from API and "Post" is nothing more than an array of "Data"
Create a new class
data class ListOfPost(val posts: List<Posts>)
and your getPosts method will return Call ListOfPost
I'm calling data from Breaking bad API https://www.breakingbadapi.com/api/character/random
I'm unable to get data. I think it's because the main Response file has square brackets that I need to call first. But I don't know how to call it. Can I get some help?
Here's my API interface
interface APIRequest {
#GET("character/random")
suspend fun getInfo() : Response<List<ResponseBB>>
}
ResponseBB Class
data class ResponseBB(
#field:SerializedName("ResponseBB")
val responseBB: List<ResponseBBItem?>? = null
)
data class ResponseBBItem(
#field:SerializedName("birthday")
val birthday: Any? = null,
#field:SerializedName("img")
val img: String? = null,
#field:SerializedName("better_call_saul_appearance")
val betterCallSaulAppearance: Any? = null,
#field:SerializedName("occupation")
val occupation: List<String?>? = null,
#field:SerializedName("appearance")
val appearance: List<Int?>? = null,
#field:SerializedName("portrayed")
val portrayed: String? = null,
#field:SerializedName("name")
val name: String? = null,
#field:SerializedName("nickname")
val nickname: String? = null,
#field:SerializedName("char_id")
val charId: Int? = null,
#field:SerializedName("category")
val category: String? = null,
#field:SerializedName("status")
val status: String? = null
)
Client object
object Client {
val gson = GsonBuilder().create()
val retrofit = Retrofit.Builder()
.baseUrl("https://www.breakingbadapi.com/api/")
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
val api = retrofit.create(APIRequest::class.java)
}
Here's my function to call result in the main activity
class MainActivity : AppCompatActivity() {
private var TAG = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
getCharacterInfo()
linearLayout.setOnClickListener {
getCharacterInfo()
}
}
private fun getCharacterInfo() {
GlobalScope.launch(Dispatchers.IO) {
try {
val response = Client.api.getInfo()
if (response.isSuccessful) {
val data = response.body()
Log.d(TAG, data.toString())
withContext(Dispatchers.Main) {
Picasso.get().load(data!!.img).into(ivImage)
tvName.text = data.name
tvOccupation.text = data.toString()
tvActor.text = data.toString()
tvAppearance.text = data.appearance.toString()
tvStatus.text = data.status
}
}
}
catch (e:Exception){
withContext(Dispatchers.Main){
Toast.makeText(applicationContext, "Cannot Load Data" , Toast.LENGTH_LONG).show()
}
}
}
}
}
I see that you try to use coroutines in retrofit, I recommend that you do not work with Response, change it to call and remove the suspend.
interface APIRequest {
#GET("character/random")
fun getInfo() : Call<List<ResponseBB>>
}
In your Global Scope you can call it this way:
GlobalScope.launch {
try{
val response = Client.api.getInfo().await()
}catch(e:Exception){}
}
you can use the version 2.9.0 in retrofit and gson Converter
So I'm trying to fetch an API with endpoint https://someURL.com/switching-product/switch?orderID=A001,
This is the returned JSON format as seen from Postman.
So I use this Interface in the NetworkConfig.kt class to fetch this endpoint:
//Endpoint: https://someURL.com/switching-product/switch?orderID=A001
interface getOutstandingOrderDetail{
#GET("switch")
fun getOutstandingOrderDetail(#Query("orderID") orderID: String): Call<OutstandingOrderDetailPOJODataClassData>
}
and store it in this data class (Generated by an Android Extension that generate POJO from JSON):
data class OutstandingOrderDetailPOJODataClass(
#field:SerializedName("data")
val data: OutstandingOrderDetailPOJODataClassData? = null,
#field:SerializedName("error")
val error: Error? = null
)
data class OutstandingOrderDetailPOJODataClassData(
#field:SerializedName("Header")
val header: OutstandingOrderDetailPOJODataClassHeader? = null,
#field:SerializedName("Detail")
val detail: List<OutstandingOrderDetailPOJODataClassDetailItem?>? = null
)
data class OutstandingOrderDetailPOJODataClassHeader(
#field:SerializedName("buyer_address")
val buyerAddress: String? = null,
#field:SerializedName("total_price")
val totalPrice: Int? = null,
#field:SerializedName("buyer_name")
val buyerName: String? = null,
#field:SerializedName("status_confirmed_yn")
val statusConfirmedYn: String? = null,
#field:SerializedName("order_date")
val orderDate: String? = null,
#field:SerializedName("outlet_id")
val outletId: String? = null,
#field:SerializedName("nip")
val nip: String? = null,
#field:SerializedName("jumlah_product")
val jumlahProduct: Int? = null,
#field:SerializedName("last_update")
val lastUpdate: String? = null,
#field:SerializedName("phone_number")
val phoneNumber: String? = null,
#field:SerializedName("order_running_id")
val orderRunningId: Int? = null,
#field:SerializedName("status_tagged_yn")
val statusTaggedYn: String? = null,
#field:SerializedName("order_id")
val orderId: String? = null
)
data class OutstandingOrderDetailPOJODataClassError(
#field:SerializedName("msg")
val msg: String? = null,
#field:SerializedName("code")
val code: Int? = null,
#field:SerializedName("status")
val status: Boolean? = null
)
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
)
This is a code snippet of the fragment where I fetch the URL data:
private fun fetchOrderedItemListData() {
NetworkConfig()
.getOutstandingDetailOrderedItemListService()
.getOutstandingOrderDetailOrderedItemList("A001")
.enqueue(object :
Callback<OutstandingOrderDetailPOJODataClassData> {
override fun onFailure(
call: Call<OutstandingOrderDetailPOJODataClassData>,
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<OutstandingOrderDetailPOJODataClassData>,
response: Response<OutstandingOrderDetailPOJODataClassData>
) {
Log.i("Order", "Ordered Item FEtched -> \n ${response.body()}") <= This is for debugging purpose
binding.rvOrderedItemList.adapter =
response.body()
?.let { OrderedItemListAdapter(it, this#OrderDetailFragment) }
binding.rvOrderedItemList.layoutManager =
LinearLayoutManager((activity as AppCompatActivity))
}
})
}
As you can see above I use Log.i to try to print the response.body() in the LogCat, However it returns null in the Log Cat. You can see it here.
Am I missing something ? If there's any detail that I left out for this question, feel free to ask.
Your Retrofit function is set up as:
#GET("switch")
fun getOutstandingOrderDetail(#Query("orderID") orderID: String): Call<OutstandingOrderDetailPOJODataClassData>
The data type in the Call<> needs to match your JSON. You have data classes that match your JSON... but the root of that hierarchy is OutstandingOrderDetailPOJODataClass, not OutstandingOrderDetailPOJODataClassData. So, change your Retrofit function to:
#GET("switch")
fun getOutstandingOrderDetail(#Query("orderID") orderID: String): Call<OutstandingOrderDetailPOJODataClass>
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()
}