I have the following retrofit2 in Android with the scalar converter factory first and gson converter after but when I call a String Call and the response comes as a string it gives me the exeption "Not a JSON object: "success".
Am I missing anything?
Retrofit creation:
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.baseUrl(IPService.END)
.client(new OkHttpClient.Builder().build())
.build();
Call creation:
#POST("/friends/sendRequest")
Call<String> sendFriendRequest(...);
Call Execution:
Response<String> a = myApi.sendFriendRequest(...).execute();
Reponse body is "Success".
I think that this isn't possible to use Gson and Scalar. Retrofit can't recognize if it's plain string response or json string response.
My way is to use Scalar converter only, return everywhere String, and then manually convert it in stream using Gson.
Below example in Kotlin with RxJava
#GET("{path}")
fun getVersion(#Path("path") hash: String): Single<String>
api.getVersion()
.map { val gson = GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
.create()
gson.fromJson(it, VersionResponse::class.java)
}
I think that because Scalar can't work with wraped by Call
Related
I'm receiving an image from an API call to the server in the form of Byte data. But when I receive the Retrofit2 Response it causes the errors stated above. Here's my Application Retrofit Object btw:
object RetrofitInstance {
private val client = OkHttpClient.Builder().apply {
addInterceptor(MyInterceptor())
addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
}.build()
private val retrofit by lazy {
val gson = GsonBuilder().setLenient().create()
Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
}
val api : ApplicationAPI by lazy {
retrofit.create(ApplicationAPI::class.java)
}
I'm not sure why there's a Json Syntax Exception. I believe it may be because of the gson client I've created? Could someone please help me figure this out. Thank you so much in advance.
Try giving
addConverterFactory(MoshiConverterFactory.create())
and add the below line in build.gradle
implementation "com.squareup.retrofit2:converter-moshi:2.9.0"
This question already has answers here:
"Expected BEGIN_OBJECT but was STRING at line 1 column 1"
(21 answers)
Closed 1 year ago.
I'm trying to send JSON via a POST request to a rest API with Retrofit. The problem I am having is that I cannot seem to figure out/use the type of data(JSONArray, String, INT ...etc) that retrofit wants in order for it to POST to my rest API. As of now, I have only tried to hard code the JSON in a string and parse it to Retrofit in hopes of it POSTing to the rest API and messing around with what type I input I use to give to retrofit to POST
when I try and parse a String I get the following: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $ this error says that I am not parsing JSON. the problem is that I have no idea what to parse.
when I tried to parse either a JSONarray or a JSONobject or any other type of data my apps view would not load and my app would crash.
my code
interface SimpleApi {
#POST("posttest.php")
suspend fun postit(#Body post: String): Phone
}
this is the retrofit API that I. the String value is the one that I believe is giving me trouble, the Phone class is data class I am using for my recylerview.
private fun TestGet() {
val gson = GsonBuilder()
.setLenient()
.create()
val api = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
.create(SimpleApi::class.java)
GlobalScope.launch(Dispatchers.IO) {
val response = api.postit(test)//it wants a JSONarray
//the above wants some kind of JSON to work I was passing Strings instead
try {
//here lies the app logic and recylerview setup
}catch (e: Exception){
println("you messed up the connection some how")
}
}
}
this function is how I am calling my the rest API, parsing and returning the JSON and handling the business logic.
now the variable test has been a few things originally it was just a hardcoded string exactly like this private var test = "[\"text1\", \"text2\", \"text3\"]" and private var test = """["text1", "text2", "text3"]""" , this did not work, I have also tried to implement A GSON converter as stated above this did not work.
thank you for your time.
EDIT
with out the addConverterFactory(GsonConverterFactory.create(gson)) I get the following errors FATAL EXCEPTION: DefaultDispatcher-worker-1 and com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $
The line .addConverterFactory(GsonConverterFactory.create(gson)), when applied means that retrofit is treating each payload that you pas as a Json Object. So, when you pass a simple string you get that error.
A possible fix would be to convert your string array into a json array as explained here https://stackoverflow.com/a/10498107/404438.
Also check if the server response is a valid json, You could achieve this by adding logging interceptor.
Here is how you add the interceptor :
first, in your build.gradle:
implementation "com.squareup.retrofit2:retrofit:2.6.0"
implementation "com.squareup.retrofit2:converter-gson:2.6.0"
implementation "com.squareup.okhttp3:logging-interceptor:3.11.0"
Then:
val gson = GsonBuilder()
.setLenient()
.create()
val builder = OkHttpClient().newBuilder()
builder.readTimeout(120, TimeUnit.SECONDS)
builder.connectTimeout(30, TimeUnit.SECONDS)
if (BuildConfig.DEBUG) {
val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BASIC
builder.addInterceptor(interceptor)
}
val client = builder.build()
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.client(client)
.build()
I am using the following libraries for retrofit.
'com.squareup.retrofit2:retrofit:2.5.0'
'com.squareup.okhttp:okhttp:2.7.5'
'com.squareup.retrofit2:converter-gson:2.5.0'
'com.squareup.okhttp3:logging-interceptor:3.10.0'
How can I get the raw response in onResponse callback? I already searched for it and got a lots of solutions which doesn't help now. I tried response.raw().body.string() and response.body().source().toString() which throws can not read body from a converted body. I also tried response.body().string() but in this case .string() is unresolved. I can log the response using interceptor but I need that response in my onResponse() callback, not just printing in logcat.
My Retrofit Client:
public static ApiService getClient(Context context) {
if (retrofit == null) {
if (BuildConfig.FLAVOR.equalsIgnoreCase("dev")){
retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.client(okHttpClient)
.build();
}else {
retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.client(SelfSigningClientBuilder.createClient(context))
.build();
}
}
return retrofit.create(ApiService.class);
}
My Retyrofit Interface:
#retrofit2.http.POST("weekly_driver_earning_report/")
#retrofit2.http.FormUrlEncoded
Call<List<DailyEarnings>> getDriverWeeklyEarnings(#retrofit2.http.Field("access_token") String access_token, #retrofit2.http.Field("start_date") String start_date, #retrofit2.http.Field("end_date") String end_date);
Following this, I solved my problem later. I defined my Call type to specific model class and that's why response.body().string() was inaccessible, Changing my Call type from List to ResponseBody, I could access response.body().string().
Api Interface
#FormUrlEncoded
#POST("register.php")
Observable<String> registerUser(#Field("email") String email, #Field("password") String password);
In my MVP presenter
onCreate{
Observable<String> registerUserObservable=
apiInterface.registerUser("test#gmail.com", "1234");
registerUserObservable.subscribeOn("schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::handleResult, this::handleError);
}
//methods
private void handleResult(String response){
Log.d(TAG, response);
}
private void handleError(Throwable throwable){
Log.d(TAG, throwable.getMessage());
}
These are my code for retrofit and rxjava and I am suppose to post an email and password to register a user. The server should return a string on success and a string on failure too.
Gson gson = new GsonBuilder()
.setLenient()
.create();
retrofit = new retrofit2.Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
I added the gson setLenient code portion because it gives me
Use JsonReader.setLenient(true) to accept malformed JSON at line 2 column 1 path $ error if I don't have it. After adding this, I am getting JSON document was not fully consumed. error which I do not know how to solve it. Is this because of the return response from the server is a string?
Thanks in advance.
If your server sends normal string response not JSON string, you need to use another converter that reads string into String. Happily there is Scalars converter officially.
A Converter which supports converting strings and both primitives and their boxed types to text/plain bodies.
I have to call an api using retrofit 2 in android. but with no values. When I does that, it shows that there must be at least 1 #field. Below is the code I am using.
public interface gitAPI {
#FormUrlEncoded
#POST("/MembersWS.svc/GetMemberAvailability/MBR0011581")
Call<Questions[]> loadQuestions();
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://192.168.1.99:82")
.addConverterFactory(GsonConverterFactory.create())
.build();
// prepare call in Retrofit 2.0
gitAPI stackOverflowAPI = retrofit.create(gitAPI.class);
Call<Questions[]> call = stackOverflowAPI.loadQuestions();
call.execute();
Declare body value in your interface with next:
#Body RequestBody body and wrap String JSON object:
RequestBody body = RequestBody.create(MediaType.parse("application/json"), (new JsonObject()).toString());