I'm trying to parse a JSON I know almost nothing about.
Example of a JSON response
{
"response": {
"content":{
"xxxxxx": "xxxxx",
"xxxxx": "xxxxxx",
...... indeterminate times
}
}
}
I tried to create an adapter, but nothing works :
#FromJson
fun fromJson(json: Map<String, String>): MyResponse {
Log.d("JSON", json.toString())
return MyResponse(...)
}
Is it possible to achieve this with Moshi? If so, what should I do?
Related
I'm developing an Android app using Retrofit to connect to a Spring Boot server.
When I update data, there are extra slashes and double quotes on the server.
This is the output of POST method. "open"
This is the output of PUT method. "\"open\""
I read a similar article and I'm guessing I encode twice, but I don't know where I'm doing it. Please help me.
This is the service class of Android.
#PUT("/posts/close/update/{id}")
fun updateClose(#Path("id") id: Long, #Body close: String): Call<ResponseBody>
This is the view.
onClick = {
val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
val retrofit = Retrofit.Builder()
.baseUrl("https://*****.com")
.addConverterFactory(MoshiConverterFactory.create(moshi))
.build()
thread {
try {
val service: PostService =
retrofit.create(PostService::class.java)
service.updateClose(6, "open")
.enqueue(object : Callback<ResponseBody> {
override fun onResponse(
call: Call<ResponseBody>,
response: Response<ResponseBody>
) {
Log.d("Response is", "${response.body()}")
}
override fun onFailure(
call: Call<ResponseBody>,
t: Throwable
) {
Log.d("Hi", "error")
}
})
} catch (e: Exception) {
Log.d("response-weather", "debug $e")
}
}
This is the repository of Spring Boot.
#Modifying
#Transactional
#Query("UPDATE posts SET close = :close where post_id = :id", nativeQuery = true)
fun updateClose(#Param("id") id: Long, #Param("close") close: String)
Thank you very much.
There is nothing wrong with the data or the android side.
Strings in JSON must be written in double quotes. For more info refer this page.
Your JSON data is {"name": "Ken", "uid": "12345"}
In order to use double quotes inside a string you have to escape it via a backslash. For more info refer this question.
That's the reason for the extra backslashes.
I tried to load the json string via python and it worked like a charm. Attaching screenshot for reference. So any backend you would be using will be able to parse the JSON String.
Finally, I got the codes which work fine.
Service Class of Android.
#PUT("/posts/close/update/{id}")
fun updateClose(#Path("id") id: Long, #Query("close") close: String): Call<ResponseBody>
Controller class of Spring Boot. Before, I used #RequestBody instead of #RequestParam.
#PutMapping("/posts/close/update/{id}")
fun updateClose(#PathVariable id: Long, #RequestParam close: String) = postService.updateClose(id, close)
This question already has answers here:
Send Post Request with params using Retrofit
(6 answers)
Closed 1 year ago.
I Have JSON like this and i want to post this data using retrofit android
{
"status": "",
"message": "",
"data": {
"request": {
"textData": "123"
}
}
}
and i don't know how to post this data, does anyone want to help me answer it?
You can do it by creating a POJO or data class (in kotlin) of your request which makes doing things like this easier.
MyRequest.kt
data class MyRequest(
var status: String,
var message: String,
var data: MyData
)
data class MyData(
var request: RequestData
)
data class RequestData(
var textData: String
)
MyApiInterface.kt
interface MyApiInterface {
#POST("/my_url_endpoint")
fun myRequest(
#Body request: MyRequest
): Call<Unit>
}
MyActivity.kt
....
val request = MyRequest(
"Ok",
"My Message",
MyData(
request = RequestData("Hello World")
)
)
RetrofitClient.api.myRequest(request).enqueue(object: Callback<Unit> {
override fun onResponse(call: Call<Unit>, response: Response<Unit>) {
// TODO: some task
}
override fun onFailure(call: Call<Unit>, t: Throwable) {
// TODO: some task
}
})
....
after doing this request if you have added logging interceptor you can check that request being made with following body.
{"data":{"request":{"textData":"Hello World"}},"message":"My Message","status":"Ok"}
How to post raw jsonArray string in kotlin using retrofit
im having timeout response on onFailure method
here is sample of string array i want to post
[{"username":"username4"},{"username":"username2"}]
here is my endpoint definition
#Headers("Content-Type: application/json;charset=UTF-8")
#POST("insert/createuser")
fun postuser(#Body logs:String ):Call<ArrRes>
here are my classes
class ArrRes{
#SerializedName("username")
#Expose
var username: String = ""
#SerializedName("message")
#Expose
var message: String = ""
#SerializedName("status")
#Expose
var status: String = ""
}
here is my posting method
var obj = JSONObject();
var arr = JSONArray();
for (i in 0 until 5){
obj.put("username","username${i}");
arr.put(obj);
}
Log.i("app:sync","${arr.toString()}")
mService!!.postuser(arr.toString()).enqueue(
object : Callback<LogResponse> {
override fun onFailure(call: Call<LogResponse>, t: Throwable) {
Log.i("app:retro:service", "onFailure ${t.message}")
}
override fun onResponse(call: Call<LogResponse>, response: Response<LogResponse>) {
if (response.isSuccessful()) {
Log.i("app:retro:service", "onResponse true ${response.body()!!.toString()}")
} else {
Log.i("app:retro:service", "onResponse false ${response.raw().toString()}")
}
}
}
)
here is sample success post using postman
Thanks for helping :)
I solve this issue by adding this dependencies:
implementation 'com.squareup.retrofit2:converter-scalars:$version'
There are multiple existing Retrofit converters for various data formats. You can serialize and deserialize Java objects to JSON or XML or any other data format and vice versa. Within the available converters, you’ll also find a Retrofit Scalars Converter that does the job of parsing any Java primitive to be put within the request body. Conversion applies to both directions: requests and responses.
https://futurestud.io/tutorials/retrofit-2-how-to-send-plain-text-request-body
I have REST POST call create order as Observable<OrderResponse>, when order create call is successful everything is fine, but then server returns error I get com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 2 path $ because Gson does not know how to handle json that have different fields than my response model
Server error response:
[{
"code": 99,
"message": "Please check your request"
}]
OrderResponse
data class OrderResponse(
#Expose
var orderId: String,
#Expose
var redirectUrl: String,
#Expose
var validUntil: Long
)
RxJava subscription
repository.requestCreateNewOrder(newOrder)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<OrderResponse> {
override fun onComplete() {
}
override fun onSubscribe(d: Disposable) {
compositeDisposable.add(d)
}
override fun onNext(t: OrderResponse) {
}
override fun onError(t: Throwable) {
//HERE I GET JsonSyntaxException
}
})
Retrofit Service
interface OrderService {
#Headers(
"Content-Type: application/x-www-form-urlencoded ",
"Connection: Keep-Alive",
"Accept-Encoding: application/json",
"User-Agent: Fiddler"
)
#FormUrlEncoded
#POST("/createOrder")
fun createOrder(#Field("orderId") orderId: String,
#Field("payCurrency") payCurrency: String,
#Field("payAmount") payAmount: Double,
#Header("Content-Length") length: Int): Observable<OrderResponse>}
Anyone have any suggestions for how to pass retrofit or gson the error model to know how to handle it
As you are using GSON to parse the JSON.
Your JSON sucessfull response will be something like
{
"orderId": 1,
"redirectUrl": "url",
"validUntil": 12414194
}
while for error response your JSON response start with Array.
[{
"code": 99,
"message": "Please check your request"
}]
So tell to server guy to not add the error response in Array [].
If you are getting the response as Array then you have to use list.
repository.requestCreateNewOrder(newOrder)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<List<OrderResponse>> {
override fun onComplete() {
}
override fun onSubscribe(d: Disposable) {
compositeDisposable.add(d)
}
override fun onNext(t: OrderResponse) {
}
override fun onError(t: Throwable) {
//HERE I GET JsonSyntaxException
}
})
In Observer response you have to add as List if your JSON response start with Array like error.
Solution : Correct it from backend for not adding Error Response in ARRAY.
As per your code you are only handling the success response
but you need to handle your error response too for this you need to ensure that your API should send you error codes different then 200 (success response generally greater then 200 and less then 300 i.e code >= 200 && code < 300 ) because retrofit consider 200-299 as success
You can achieve this simply by changing your observable return type to
Observable<Response< OrderResponse>>
and after receiving response from server simply check
if (orderResponse.isSuccessful()) {
//here you can handle success by using orderResponse.getbody()
} else {
// here you can display error message and if you further want
// to parse error response from server then use below function
errorOrderResponseHandling(orderResponse);
}
you want to further parse response into error model(in this example OrderAPIError is model class for error response) then below is the function
private void errorOrderResponseHandling(Response<OrderResponse> orderResponse) {
OrderAPIError orderAPIError = null;
try {
orderAPIError = new Gson().fromJson(String.valueOf(new
JSONObject(orderResponse.errorBody().string())), OrderAPIError.class);
// further orderAPIError object you can fetch server message and display
it to user as per your requirement
} catch (JSONException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
Above example is in android not in kotlin but you can get idea and use function accordingly in kotlin
I'm using Retrofit to make a POST Request in my web server.
However, I can't seem to get the response body when the response status is 422 (unprocessable entity). The response body is always null.
I want to know if I'm doing something wrong or if there's a workaround for this. Because I'm using the same json in the request with Postman, and it returns the body normally.
This is the method:
#Headers("Content-Type: application/vnd.api+json")
#POST("my_endpoint")
Call<JsonObject> postEntry(#Header("Authorization") String authorization, #Body JsonObject json);
The body is a JsonObject, I'm not serializing like the documentation say. But I don't think this is the problem.
By default, when your server is returning an error code response.body() is always null. What you are looking for is response.errorBody(). A common approach would be something like this:
#Override
public void onResponse(Response<JsonObject> response, Retrofit retrofit) {
if (response.isSuccess()) {
response.body(); // do something with this
} else {
response.errorBody(); // do something with that
}
}
If you need something advanced take a look at Interceptors and how to use them
I got the same error. My API was working using POSTMAN request but not working from Android retrofit call.
At first I was trying using #Field but it was getting error but later I've tried with #Body and it worked.
Sample Retrofit interface call
#POST("api/v1/app/instance")
Call<InstanceResponse> updateTokenValue(
#HeaderMap Map<String, String> headers,
#Body String body);
and API calling code is:
Map<String, String> headerMap=new HashMap<>();
headerMap.put("Accept", "application/json");
headerMap.put("Content-Type", "application/json");
headerMap.put("X-Authorization","access_token");
Map<String, String> fields = new HashMap<>();
fields.put("app_name", "video");
fields.put("app_version", "2.0.0");
fields.put("firebase_token", "token");
fields.put("primary", "1");
ApiInterface apiInterface = ApiClient.getApiClient().create(ApiInterface.class);
Call<InstanceResponse> call = apiInterface.updateTokenValue(
headerMap,new Gson().toJson(fields));
Well in this case you'll have to convert the response.
Have a look at this link
All the steps are already provided in the link above.
For Kotlin users here is the code solution.
ErrorResponse.kt (This obviously depends on your error response)
import com.squareup.moshi.Json
data class ErrorResponse(
#Json(name="name")
val name: String? = null,
#Json(name="message")
val message: String? = null,
#Json(name="errors")
val errors: Errors? = null,
#Json(name="statusCode")
val statusCode: Int? = null
)
ApiFactory.kt (Let me know if you need the entire code)
fun parseError(response: Response<*>): ErrorResponse {
val converter = ApiFactory.retrofit()
.responseBodyConverter<ErrorResponse>(
ErrorResponse::class.java, arrayOfNulls<Annotation>(0)
)
val error: ErrorResponse
try {
error = converter.convert(response.errorBody()!!)!!
} catch (e: IOException) {
e.printStackTrace()
return ErrorResponse()
}
return error
}
and in the Presenter (I use MVP)
GlobalScope.launch(Dispatchers.Main) {
try {
val response = ApiFactory.apiService.LOGIN(username, password)
.await()
val body = response.body()
body?.let {
// Handle success or any other stuff
if (it.statusCode == 200) {
mView.onSuccess(it.data!!)
}
} ?:
// This is the else part where your body is null
// Here is how you use it.
// Pass the response for error handling
mView.showMessage(ApiFactory.parseError(response).message!!)
} catch (e: Exception) {
e.printStackTrace()
}
}
And thats how you roll it!
That's All Folks!