Pleace, help me to parse JSON. I always get an object:
IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at
line 1 column 2 path $
I try to pars List<List<String>>, <List<String>> and <String> but get the same exeption.
Here is my JSON:
{"barcodes":[["1212"],["22222222222"],["22222321321"],["23565233665558488"],["2999300031242"],["6"]]}
Interface:
import java.util.List;
import retrofit2.Call;
import retrofit2.http.GET;
public interface RequestInterface {
#GET("barcodeinfo?getBarcodes")
Call<List<List<String>>> getBarcodeList();
}
obj:
public class SingleBarcode {
final String barcodes;
public SingleBarcode(String barcodes) {
this.barcodes = barcodes;
}
}
main:
void getRetrofitArray() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
RequestInterface service = retrofit.create(RequestInterface.class);
Call<List<List<String>>> call = service.getBarcodeList();
call.enqueue(new Callback<List<List<String>>>() {
#Override
public void onResponse(Call<List<List<String>>> call, Response<List<List<String>>> response) {
try {
List<List<String>> BarcodeData = response.body();
Log.d("MyLog", BarcodeData.size()+"");
} catch (Exception e) {
Log.d("MyLog", "There is an error");
e.printStackTrace();
}
}
#Override
public void onFailure(Call<List<List<String>>> call, Throwable t) {
Log.d("MyLog", "error " + t.toString());
}
});
}
use these POJO class
public class Result {
#SerializedName("barcodes")
#Expose
private List<List<String>> barcodes = new ArrayList<List<String>>();
/**
*
* #return
* The barcodes
*/
public List<List<String>> getBarcodes() {
return barcodes;
}
/**
*
* #param barcodes
* The barcodes
*/
public void setBarcodes(List<List<String>> barcodes) {
this.barcodes = barcodes;
}
}
use interface like this ...
#GET("barcodeinfo?getBarcodes")
Call<Result> getBarcodeList();
and call like this ....
Call<Result> call = service.getBarcodeList();
call.enqueue(new Callback<Result>() {
#Override
public void onResponse(Call<Result> call, Response<Result> response) {
Result r = response.body(); // you can initialize result r variable global if you have out side use of this response
}
#Override
public void onFailure(Call<Result> call, Throwable t) {
Log.d("MyLog", "error " + t.toString());
}
});
NOTE:- You are trying to access as LIST But in response it coming simple JSON OBJECT
Related
I want to use OkHttp library for networking in Android.
I started with the simple post example as written in their website:
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
Response response = client.newCall(request).execute();
return response.body().string();
}
With this call:
String response = post("http://www.roundsapp.com/post", json);
This call ends with NetworkOnMainThreadException.
I could wrap the call with an AsyncTask, but as far as I understand from the examples, the OkHttp library should have already taken care of that..
Am I doing something wrong?
You should use OkHttp's async method.
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
Call post(String url, String json, Callback callback) {
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
Call call = client.newCall(request);
call.enqueue(callback);
return call;
}
And then your response would be handled in the callback (OkHttp 2.x):
post("http://www.roundsapp.com/post", json, new Callback() {
#Override
public void onFailure(Request request, Throwable throwable) {
// Something went wrong
}
#Override public void onResponse(Response response) throws IOException {
if (response.isSuccessful()) {
String responseStr = response.body().string();
// Do what you want to do with the response.
} else {
// Request not successful
}
}
});
Or OkHttp 3.x/4.x:
post("http://www.roundsapp.com/post", "", new Callback() {
#Override
public void onFailure(Call call, IOException e) {
// Something went wrong
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
String responseStr = response.body().string();
// Do what you want to do with the response.
} else {
// Request not successful
}
}
});
Take a look at their recipes for more examples: http://square.github.io/okhttp/recipes/
According to the OkHttp docs:
It supports both synchronous blocking calls and async calls with callbacks.
Your example is on main thread and Android since version 3.0 throws that exception if you try to do network calls on main thread
Better option is to use it together with retrofit and Gson:
http://square.github.io/retrofit/
https://code.google.com/p/google-gson/
Here are the examples:
http://engineering.meetme.com/2014/03/best-practices-for-consuming-apis-on-android/
http://heriman.net/?p=5
If you follows these steps to implement OKHTTP, then definitely you'll call multiple API on multiple screen by applying only two lines of code
UpdateListener updateListener = new UpdateListener(HitAPIActivity.this, baseHTTPRequest);
updateListener.getJsonData();
Step 1:
baseHTTPRequest = new BaseHTTPRequest();
// baseHTTPRequest.setURL("https://api.geonames.org/citiesJSON?north=44.1&south=-9.9&east=-22.4&west=55.2&lang=de&username=demohttps://api.geonames.org/citiesJSON?north=44.1&south=-9.9&east=-22.4&west=55.2&lang=de&username=demo");
baseHTTPRequest.setURL("http://jsonparsing.parseapp.com/jsonData/moviesDemoItem.txt");
baseHTTPRequest.setRequestCode(reqType);
baseHTTPRequest.setCachedRequired(true);
UpdateListener updateListener = new UpdateListener(HitAPIActivity.this, baseHTTPRequest);
updateListener.executeRequest();
Step 2 : Create a request class
/**
* Created by Deepak Sharma on 4/7/16.
* This is a HTTP request class which has the basic parameters.
* If you wants to add some more parameters, please make a subclass of that class
* and add with your subclass. Don't modify this class.
*/
public class BaseHTTPRequest<T> {
private Context context;
private String URL;
private int requestCode;
private List<T> listParameters;
private String header;
private boolean isCachedRequired;
public Context getContext() {
return context;
}
public void setContext(Context context) {
this.context = context;
}
public void setURL(String URL) {
this.URL = URL;
}
public String getURL() {
return URL;
}
public int getRequestCode() {
return requestCode;
}
public void setRequestCode(int requestCode) {
this.requestCode = requestCode;
}
public List<T> getListParameters() {
return listParameters;
}
public void setListParameters(List<T> listParameters) {
this.listParameters = listParameters;
}
public String getHeader() {
return header;
}
public void setHeader(String header) {
this.header = header;
}
public boolean isCachedRequired() {
return isCachedRequired;
}
public void setCachedRequired(boolean cachedRequired) {
isCachedRequired = cachedRequired;
}
}
step 4 : Create a listener class
import android.util.Log;
import com.google.gson.Gson;
import java.io.IOException;
import dxswifi_direct.com.wifidirectcommunication.base.model.request.BaseHTTPRequest;
import okhttp3.Call;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Callback;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
/**
* Created by Deepak Sharma on 4/7/16.
* #email : dpsharma.sharma1#gmail.com
* This is a Simple java class which will help you for HTTP request/response and it will
* throw the response to your correspondance activity.
*/
public class UpdateListener {
private OnUpdateViewListener onUpdateViewListener;
OkHttpClient okHttpClient = new OkHttpClient();
BaseHTTPRequest mRequestModel;
private String mURL = null;
private Request mRequest = null;
public interface OnUpdateViewListener {
void updateView(String responseString, boolean isSuccess,int reqType);
}
public UpdateListener(OnUpdateViewListener onUpdateView, final BaseHTTPRequest requestModel) {
this.mRequestModel = requestModel;
this.onUpdateViewListener = onUpdateView;
if (requestModel.isCachedRequired())
{
/*File httpCacheDirectory = new File(requestModel.getContext().getCacheDir(), "responses");
Cache cache = null;
cache = new Cache(httpCacheDirectory, 10 * 1024 * 1024);
if (cache != null) {
okHttpClient.setCache(cache);
}*/
}
/*mURL = null;
if (requestModel.getListParameters()!=null && requestModel.getListParameters().size()>0)
{
HttpUrl.Builder urlBuilder = HttpUrl.parse(requestModel.getURL()).newBuilder();
List<RequestParameter> requestParameters = requestModel.getListParameters();
for (int i=0; i<requestParameters.size();i++)
{
urlBuilder.addQueryParameter(requestParameters.get(i).getKey(),requestParameters.get(i).getValue());
}
mURL = urlBuilder.build().toString();
}
else
{
mURL = requestModel.getURL();
}*/
mURL = requestModel.getURL();
if (mRequestModel.getListParameters()!=null && mRequestModel.getListParameters().size()>1)
{
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
mRequest = new Request.Builder()
.url(mURL)
.post(RequestBody.create(JSON, new Gson().toJson(BaseHTTPRequest.class)))
.build();
}
else
{
mRequest = new Request.Builder()
.url(mURL)
.build();
}
}
public void executeRequest()
{
Call call = okHttpClient.newCall(mRequest);
call.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
onUpdateViewListener.updateView(NetworkException.getErrorMessage(e), false, mRequestModel.getRequestCode());
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if (!response.isSuccessful()) {
// You can also throw your own custom exception
throw new IOException("Unexpected code " + response);
} else {
Log.i("Response:",response.toString());
Log.i("Response body:",response.body().toString());
Log.i("Response message:",response.message());
onUpdateViewListener.updateView(response.body().string(),true, mRequestModel.getRequestCode());
}
// do something wih the result
}
});
}
}
step 5 : From the activity you requesting, implement listener
public class HitAPIActivity extends AppCompatActivity implements View.OnClickListener, UpdateListener.OnUpdateViewListener{
#Override
public void updateView(final String responseString, boolean isSuccess, int reqType) {
if (isSuccess)
{
if (!responseString.contains("failure")
&& !responseString.contains("Error")) {
// Handle request on the basis of Request Type.
switch (reqType) {
case ApiConstants.GET_CONTACTS:
break;
default:
break;
}
}
}
}
I want to get the HttpResponse code and control the errors for display them to the user.
I have my static retrofit request:
public class NetworkClient {
//KEY: f663e4c56cc039c837109c82c78bbd69
public static Retrofit getRetrofit(){
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.themoviedb.org/3/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
// .client(OkHttpClient())
.build();
return retrofit;
}
And observable who takes the request returns me an observable:
public Observer<Response> getObserver(){
Observer<Response> response = new Observer<Response>() {
#Override
public void onSubscribe(Disposable d) {
Log.d("test", "onSubscribe");
}
#Override
public void onNext(Response response) {
Log.d("test", "onNext");
fragmentInterface.showMovies(response);
}
#Override
public void onError(Throwable e) {
Log.d("test", "onError");
e.printStackTrace();
if(e instanceof HttpException){
int errorCode = ((HttpException) e).response().code();
}
}
#Override
public void onComplete() {
Log.d("test", "onComplete");
}
};
return response;
}
I see i can get the error from the onError, but i see i can have a class to get all type errors.
I know this is a common question, but I did all of them, still no resolved. In my MainActivity, I had a call from an ServerService.java, like this:
String randomNumber = serverService.contactServer();
In the ServerService.java, the contactServer() will call the method which contains the .enqueue:
public String contactServer() {
return requestServerService();
}
And the requestServerService() contains the code:
public String requestServerService() {
Call<RequestAttributes> call = new RetrofitConfig().getServiceRequester().requestRandomNumber();
call.enqueue(new Callback<RequestAttributes>() {
#Override
public void onResponse(Call<RequestAttributes> call, Response<RequestAttributes> response) {
if (!response.isSuccessful()) {
Log.i("Err", "Err: " + response.code());
} else {
RequestAttributes requestAttributes = response.body();
returnedValue = requestAttributes.getRandomNumber();
Log.d("jsonAnswer", "O numero aleatorio é: " + returnedValue);
}
}
#Override
public void onFailure(Call<RequestAttributes> call, Throwable t) {
Log.e("Fail", "Failed: " + t.getMessage());
}
}); return returnedValue;
The error is the returnedValue returns null. I tried debbuging, but even it doesn't reach onReponse. I know the problem must be because .enqueue is asynchronous, but how can I resolve this problem and return the request to the mainActivity?
The config of Retrofit:
public RetrofitConfig() {
this.retrofit = new Retrofit.Builder()
.baseUrl("localhost:3000/")
.addConverterFactory(GsonConverterFactory.create())
.build();
}
public ServiceRequester getServiceRequester() {
return this.retrofit.create(ServiceRequester.class);
}
The POJO:
public class RequestAttributes {
#SerializedName("randomNumber")
private String randomNumber;
public String getRandomNumber() {
return randomNumber;
}
public void setRandomNumber(String randomNumber) {
this.randomNumber = randomNumber;
}
#Override
public String toString() {
return "RequestAttributes{" +
", randomNumber='" + randomNumber + '\'' +
'}';
}
}
And the request:
#GET("api/requestRandomNumber")
Call<RequestAttributes> requestRandomNumber();
The JSON answer if I request via browser:
{"randomNumber":"u845gq"}
You can pass callbacks from your MainActivity to contactServer() method
serverService.contactServer(new Callback<RequestAttributes>() {
#Override
public void onResponse(Call<RequestAttributes> call, Response<RequestAttributes> response) {
if (!response.isSuccessful()) {
Log.i("Err", "Err: " + response.code());
} else {
RequestAttributes requestAttributes = response.body();
String returnedValue = requestAttributes.getRandomNumber();
// Do what you want here with returnedValue. You are in the activity thread(MainThread or UIThread) for example someTextView.setText(returnedValue);
Log.d("jsonAnswer", "O numero aleatorio é: " + returnedValue);
}
}
#Override
public void onFailure(Call<RequestAttributes> call, Throwable t) {
Log.e("Fail", "Failed: " + t.getMessage());
}
});
Then make it void method, pass the callback to requestServerService() method
public void contactServer(Callback<RequestAttributes> callback) {
requestServerService(callback);
}
Then implement requestServerService() method like this:
public void requestServerService(Callback<RequestAttributes> callback) {
Call<RequestAttributes> call = new RetrofitConfig().getServiceRequester().requestRandomNumber();
call.enqueue(callback);
}
I have a function searchForTrips() which sends an API request and fetch some response in following way.
private void searchForTrips(){
int departurePortId = PORT_ID_LIST.get(departurePort);
int returnPortId = PORT_ID_LIST.get(returnPort);
int pax= Integer.parseInt(noOfPassengers);
String departureDatePARSED = DEPARTURE_DATE_VALUES.get(departureDate);
String returnDatePARSED = RETURN_DATE_VALUES.get(departureDate);
Call<TripSearchResponse> call = apiService.searchAvailableTrips(TripType,departurePortId,returnPortId,departureDatePARSED,returnDatePARSED,pax);
call.enqueue(new Callback<TripSearchResponse>() {
#Override
public void onResponse(Call<TripSearchResponse> call, Response<TripSearchResponse> response) {
int statusCode = response.code();
switch(statusCode){
case 200:
default:
Snackbar.make(findViewById(android.R.id.content),"Error loading data. Network Error.", Snackbar.LENGTH_LONG).show();
break;
}
}
#Override
public void onFailure(Call<TripSearchResponse> call, Throwable t) {
Log.i(TAG, t.getMessage());
Snackbar.make(findViewById(android.R.id.content),"Error loading data. Network Error.", Snackbar.LENGTH_LONG).show();
}
});
}
The purpose is to make this callback function reusable so I can call it from several activities and get requested data as I need. What is the best way to implement this?
try this way, its dynamic way and easy to use:
Create Retforit Interface:
public interface ApiEndpointInterface {
#Headers("Content-Type: application/json")
#POST(Constants.SERVICE_SEARCH_TRIP)
Call<JsonObject> searchForTrip(#Body TripRequest objTripRequest);
}
Create Retrofit Class:
public class AppEndPoint {
private static Retrofit objRetrofit;
public static ApiEndpointInterface getClient() {
if (objRetrofit == null){
objRetrofit = new Retrofit.Builder()
.baseUrl(Constants.SERVER_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return objRetrofit.create(ApiEndpointInterface.class);
}
}
Create this helper Classes/Interfaces to hold web service callback:
public enum ResponseState {
SUCCESS,
FAILURE,
NO_CONNECTION
}
public enum RequestType {
SEARCH_FOR_TRIP // add name for each web service
}
public class Response {
public ResponseState state;
public boolean hasError;
public RequestType requestType;
public JsonObject result;
}
public interface RestRequestInterface {
void Response(Response response);
Context getContext();
}
public class ResponseHolder { used to hold the Json response could be changed as your response
#SerializedName("is_successful")
#Expose
private boolean isSuccessful;
#SerializedName("error_message")
#Expose
private String errorMessage;
public boolean isSuccessful() {
return isSuccessful;
}
public void setSuccessful(boolean successful) {
isSuccessful = successful;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
}
public class AppClient {
private static ApiEndpointInterface objApiEndpointInterface;
private static Response objResponse;
private static Call<JsonObject> objCall;
// implement new method like below for each new web service
public static void searchForTrip(TripRequest objTripRequest, RestRequestInterface objRestRequestInterface) {
objResponse = new Response();
objResponse.state = ResponseState.FAILURE;
objResponse.hasError = true;
objResponse.requestType = RequestType.SEARCH_FOR_TRIP; // set type of the service from helper interface
objApiEndpointInterface = AppEndPoint.getClient();
objCall = objApiEndpointInterface.searchForTrip(objTripRequest);
handleCallBack(objRestRequestInterface);
}
private static void handleCallBack(final RestRequestInterface objRestRequestInterface) {
objCall.enqueue(new Callback<JsonObject>() {
#Override
public void onResponse(Call<JsonObject> call, retrofit2.Response<JsonObject> response) {
try {
ResponseHolder objResponseHolder = new Gson().fromJson(response.body(), ResponseHolder.class);
if (objResponseHolder.isSuccessful()) {
objResponse.state = ResponseState.SUCCESS;
objResponse.hasError = false;
objResponse.result = response.body();
} else {
objResponse.errorMessage = objResponseHolder.getErrorMessage();
}
objRestRequestInterface.Response(objResponse);
} catch (Exception objException) {
objResponse.errorMessage = objRestRequestInterface.getContext().getString(R.string.server_error);
objRestRequestInterface.Response(objResponse);
}
}
#Override
public void onFailure(Call<JsonObject> call, Throwable objThrowable) {
String errorMessage = "";
if (objThrowable instanceof IOException) {
errorMessage = objRestRequestInterface.getContext().getString(R.string.no_connection_error);
} else {
errorMessage = objRestRequestInterface.getContext().getString(R.string.server_error);
}
objResponse.errorMessage = errorMessage;
objRestRequestInterface.Response(objResponse);
}
});
}
}
then go to your activity of fragment and make the call like this:
public class MainActivity extends AppCompatActivity implements RestRequestInterface {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// initialize ids
// prepare to call web service
// 1.Initialize your object to be sent over web service
TripRequest objTripRequest = new TripRequest();
objTripRequest.id = 1;
// 2.Show loader
// 3.Make the call
AppClient.searchForTrip(objTripRequest, this);
}
#Override
public void Response(Response response) {
// hide loader
try {
if (response.state == ResponseState.SUCCESS && !response.hasError) {
// check the type of web service
if (response.requestType == RequestType.SEARCH_FOR_TRIP) {
// acces the return here from response.result
}
} else {
String errorMsg = response.hasError ? response.errorMessage : getString(R.string.no_connection_error);
// show the error to the user
}
} catch (Exception objException) {
// show the error to the user
}
}
#Override
public Context getContext() {
// do not forgit set the context here
// if fragment replace with getAcitvity();
return this;
}
}
My Retrofit API method is currently accepting one payload structure. However, the backend may return a different payload structure if there's any error in the request.
For example:
public void search(String term, final CallBack <ArrayList<String>> callBack) {
RetroGenerator.createService(APIServices.class).search(term).enqueue(new Callback<ArrayList<String>> () {
#Override
public void onResponse(Call<ArrayList<String>> call, Response<ArrayList<String>> response) {
if (response.isSuccessful()) {
callBack.onSuccess(response.body());
}
return;
}
callBack.onError();
}
#Override public void onFailure(Call<ArrayList<String>> call, Throwable t) {
callBack.onError();
}
});
}
The backend is returning an array of String values. However if an error occurs, backend may return the following payload structure:
{
"error": "Term can't be empty",
"code": 403
}
But the way my API method is setup, it only accepts one java model.
API Interface:
#FormUrlEncoded
#POST("api/v1/search.json")
Call<ArrayList<String>> search(#Field("term") String term);
Currently it's accepting only an ArrayList<String> and does not accept the custom error payload model. Given that I create a new model called Error:
public class Error {
public String error;
public int code;
}
How can I switch the retrofit API method's model when an error occurs?
You can have an ErrorUtils class to handle your unsuccessful responses:
public class ErrorUtils {
public static ApiError parseError(Response<?> response) {
Converter<ResponseBody, ApiError> converter = ServiceGenerator.retrofit().
responseBodyConverter(ApiError.class, new Annotation[0]);
ApiError apiError;
try {
apiError = converter.convert(response.errorBody());
} catch (IOException e) {
apiError = new ApiError();
}
return apiError;
}
}
Then when you find an unsuccessful response, just parse the response with the ErrorUtils class:
if (!response.isSuccessful()) {
// ...
ApiError apiError = ErrorUtils.parseError(response);
}
The ApiError class:
public class ApiError {
#SerializedName("error")
private String mErrorDescription;
#SerializedName("code")
private Integer mErrorCode;
public ApiError() {}
public void setErrorCode(Integer code) {
this.mErrorCode = code;
}
public Integer getErrorCode() {
return mErrorCode;
}
public String getErrorDescription() {
return mErrorDescription;
}
public void setErrorDescription(String errorDescription) {
mErrorDescription = errorDescription;
}
}