php : reading json arraylist and saving data in mysql? - java

I want to read arraylist from android into php in order to store in database, but I'm not able to find exact code for it. Can anybody guide me in the direction to solve this problem ?
Here is my java code for creating the arraylist
private void loadCart()
{
productList.clear();
Cursor cursor = dbHelper.getCarProducts();
cursor.moveToFirst();
do {
CartProduct cartProduct = new CartProduct();
cartProduct.setProductName("Name: "+cursor.getString(cursor.getColumnIndex("_Name")));
cartProduct.setProductCost("Cost: "+cursor.getString(cursor.getColumnIndex("_Cost")));
cartProduct.setProductPrice("Price: "+cursor.getString(cursor.getColumnIndex("_Price")));
cartProduct.setProductQuantity("Quantity: "+cursor.getString(cursor.getColumnIndex("_Quantity")));
productList.add(cartProduct);
}while(cursor.moveToNext());
}
I'm using retrofit2 in order to send the arraylist to the server, but as I have seen in other question here I'm not able to get the url for the file_get_contents ?

Here you go...
Step 1: Add retrofit dependency in your gradle.app
compile 'com.squareup.retrofit:retrofit:1.9.0'
Step 2: Make an RestClient class like below.
public class RestClient {
private static final String BASE_URL = DataConstants.TEST_URL; //Place your web service URL here
private ApiInterface apiService;
public RestClient()
{
RequestInterceptor requestInterceptor = new RequestInterceptor() {
#Override
public void intercept(RequestFacade request) {
request.addHeader("Accept", "application/json");
}
};
RestAdapter restAdapter = new RestAdapter.Builder()
.setLogLevel(RestAdapter.LogLevel.FULL)
.setRequestInterceptor(requestInterceptor)
.setEndpoint(BASE_URL)
.build();
apiService = restAdapter.create(ApiInterface.class);
}
public ApiInterface getApiService()
{
return apiService;
}
}
Step 3: Make an Interface for POST URL.
public interface ApiInterface {
#POST("/sendData")
void sendData(#Body JsonObject jsonObject,
Callback<DataModel> dataModelCallback);
}
Step 4: Make an POJO class like below.
public class DataModel{
private String success;
public String getSuccess() {
return success;
}
public void setSuccess(String success) {
this.success = success;
}
}
Step 5: Make an call of webservice from your activity like below.
private void callWebService(String user_id) {
try {//TODO SEND
final Utility utility = new Utility(this);
utility.showProgressDialog();
JsonObject myJsonData = new JsonObject();
myJsonData.addProperty("user_id", user_id);
Gson gsonData = new GsonBuilder().create();
JsonArray dataArray = new JsonArray();
dataArray = gsonData.toJsonTree(productList).getAsJsonArray(); //Here you want to add your array list i.e productList
myJsonData.add("assign_to", jaAssignee);
new RestClient().getApiService().sendData(myJsonData, new Callback<DataModel>() {
#Override
public void success(DataModel dataModel, Response response) {
utility.hideProgressDialog();
try {
String success = dataModel.getSuccess();
if (success.equalsIgnoreCase("Success")) {
//Do what you want to do
}
} catch (Exception e) {
}
}
#Override
public void failure(RetrofitError error) {
utility.hideProgressDialog();
}
});
} catch (Exception e) {
}
}
Hope this will help you!

Send all your data in one go. Create JsonArray and add each object by creating JsonObject. After its done upload all the data in one go. You'll just have to decode that array in php.
Advantage of using this is, you can manage the response in retrofit very well (being it asynchronous)

you can parse json value in php by
<?php
header('Content-type: application/json');
$jsonString = file_get_contents('php://input');
$jsonArray = json_decode($jsonString,true);
print_r($jsonArray);/* print array */
?>

After almost searching for 3+ weeks and n+ hours of frustration, i've finally found an working solution to my problem and sharing it with people so they could benefit from it (as i was blocked from another a/c due to asking too many questions and down votes ) , in order to send your array list to your server we need to use another library and the best fit according to my need is async-http library by loop j and these are the steps to import and use the library in your program :-
1.)import the library into your project by writing this statement in your bulid.gradle(app module):-
compile 'com.loopj.android:android-async-http:1.4.9'
2.)Create the following variables in order to use them in your program:-
AsyncHttpClient (a client to send data to your server)
RequestPrarms (Data sent to your server for parsing and further operations )
String url(Link to your server where actually operations occurs)
3.) Now we use these variables and run the program :-
p
arams.put("OrderSummary", <your array list>);
httpClient.post(APIURL.API_URL,params, new AsyncHttpResponseHandler() {
#Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
//your operations on success
}
#Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
Toast.makeText(getApplicationContext(),error.toString(),Toast.LENGTH_LONG).show();
}
});
i hope this clears your doubts somewhat i'm still creating my php side in order to insert data in the database

Related

send json to Laravel using postman and android

i am trying to send json using postman to Lavavel but i facing this error.
enter image description here
this is my json code:
{
"email":"test#test.com",
"password":"testtest"
}
and this is Laravel codes :
Route::get('/r','test#store');
and
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use DB;
use Log;
class test extends Controller
{
public function store(Request $request)
{
$email = $request->input('email');
$password = $request->input('password');
Log::info('test');
Log::info($email);
Log::info($password);
DB::table('login')->insert([
['email' => $email],
['password' => $password]
]);
}
}
also i trying using android for send data using volley and so checked Laravel logs :
Column 'email' cannot be null (this is Laravel logs)
and on android Logs:
E/Volley: [299] BasicNetwork.performRequest: Unexpected response code 500 for http://192.168.1.4:8000/r
D/error: com.android.volley.ServerErro
my android code is :
public class ApiService {
private final Context context;
public ApiService(Context context){
this.context=context;
}
public void loginUser(String email, String password, final OnLoginResponse onLoginResponse){
JSONObject requestJsonObject=new JSONObject();
try {
requestJsonObject.put("email",email);
requestJsonObject.put("password",password);
JsonObjectRequest request=new JsonObjectRequest(Request.Method.GET, "http://192.168.1.4:8000/r",requestJsonObject , new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
Log.d("response",response.toString());
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.d("error",error.toString());
}
}) {
#Override
public Map getHeaders() throws AuthFailureError {
HashMap headers = new HashMap();
headers.put("Content-Type", "application/json");
return headers;
}
};
request.setRetryPolicy(new DefaultRetryPolicy(18000,DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
Volley.newRequestQueue(context).add(request);
} catch (JSONException e) {
Log.e(TAG, "loginUser: "+e.toString());
}
}
public interface OnLoginResponse{
void onResponse(boolean success);
}
}
I hope this helps people trying to search on how to send JSON data to laravel not only specific to android applications but to all. The goal of this solution is to identify whether you can send a JSON data to laravel or not.
First of all you have to download postman from https://www.getpostman.com/ to test if your API is really working or not.
Create a post request using postman. Be sure that you follow the example data below
Be sure that you set your Routes that would associate to the controller
This is the controller part that will show the JSON data you sent if it was successfully accepted or not.
And also, if ever you are trying to send POST data to laravel, by default they provided a CSRF Token which is applicable for the forms if you are going to use the MVC of laravel. For the meantime, we are going to take this down and comment it out. Just go to app/http/kernel.php
and now you'll get the following result from the code earlier
$json = json_decode($request['json']);
echo $json->{'email'};
echo "\n";
echo $json->{'password'};
We tested that we were able to send data to laravel. I hope this truly helps.
Wen you want to send data, you will want to use POST or PUT method on your postman, specially if you are sending a body, that means that you are sending data. Get method is used to retrieve data from a service.
Take a look into CRUD functions for more information.
Your postman should look something like this
Last in your android code try to change this line
JsonObjectRequest request=new JsonObjectRequest(Request.Method.GET, "http://192.168.1.4:8000/r",requestJsonObject , new Response.Listener<JSONObject>() {
to use Request.Method.POST

How to convert cURL to retrofit correct form?

Sorry for my English. I want use this service. For determine the language of the text.
Request(Curl):
curl -X POST -d "outputMode=json" --data-urlencode text#ibm.txt -d "url=http://www.ibm.com/us-en/" "https://gateway-a.watsonplatform.net/calls/text/TextGetLanguage?apikey=%API_KEY%"
I use Retrofit for request.
public interface LanguageDetectionApi {
public static final String ROOT_URL = "https://gateway-a.watsonplatform.net/calls/";
#POST("/text/TextGetLanguage")
Call<List<PostModel>> getData(#Query("apikey") String apikey, #Query("text") String text);
}
Create retrofit object:
public class App extends Application {
private static LanguageDetectionApi _languageDetectionApi;
private Retrofit _retrofit;
#Override
public void onCreate() {
super.onCreate();
_retrofit = new Retrofit.Builder()
.baseUrl(_languageDetectionApi.ROOT_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
_languageDetectionApi = _retrofit.create(LanguageDetectionApi.class);
}
public static LanguageDetectionApi getLanguageDetectionApi() {
return _languageDetectionApi;
}
}
And send request:
App app = new App();
app.onCreate();
app.getLanguageDetectionApi().getData("4978e60252ae102dfe1341146bb8cc3ec4bbbd78", textForRecognition).enqueue(new Callback<List<PostModel>>() {
#Override
public void onResponse(Call<List<PostModel>> call, Response<List<PostModel>> response) {
List<PostModel> posts = new ArrayList<>();
posts.addAll(response.body());
}
#Override
public void onFailure(Call<List<PostModel>> call, Throwable t) {
Toast.makeText(MainActivity.this, "An error occurred during networking", Toast.LENGTH_SHORT).show();
}
});
PostModel i generated in site http://www.jsonschema2pojo.org/.
Questions:
No response comes to me, although apikey are exactly valid.
How to specify in the interface parametr "outputMode=json"?
And I translated correctly cURL to LanguageDetectionApi?
It seems to me that the whole mistake in the class LanguageDetectionApi. Can you help deal with this? Thank you!
change url code like below:
public interface LanguageDetectionApi {
public static final String ROOT_URL = "https://gateway-a.watsonplatform.net";
#POST("/calls/text/TextGetLanguage")
Call<List<PostModel>> getData(#Query("apikey") String apikey, #Query("text") String text);
}
base url should be ony host name.

Retrofit2 Handle condition when status code 200 but json structure different than datamodel class

I'm using Retrofit2 and RxJava2CallAdapterFactory.
The API I consume returns status code always as 200 and for success and response JSON string the JSON structure is entirely different. Since the status code is always 200 the onResponse() method is called always. Hence, I'm not able to extract error msgs from the JSON in the error condition.
Solution 1:
I use ScalarsConverterFactory to get response String and manually use Gson to parse the response .
How to get response as String using retrofit without using GSON or any other library in android
Problem with this solution: I'm planning to use RxJava2CallAdapterFactory for that the retrofit method should return DataModel Class.
I need to find the best solution for this problem, in way I can keep returning the data model classes from Retrofit method & somehow I identify the error condition from response (identify the response JSON does not match the data model) and then parse the error JSON into a data model.
Retrofit Client
public static Retrofit getClient(String url) {
if (apiClient == null) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient httpClient = new OkHttpClient.Builder().addInterceptor(interceptor).build();
apiClient = new Retrofit.Builder()
.baseUrl(url)
/*addCallAdapterFactory for RX Recyclerviews*/
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
/* add ScalarsConverterFactory to get json string as response */
// .addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
// .addConverterFactory(GsonConverterFactory.create(gson))
.client(httpClient)
.build();
}
return apiClient;
}
Method
public static void getLoginAPIResponse(String username, String password, String sourceId, String uuid, final HttpCallback httpCallback) {
baseUrl = AppPreference.getParam(UiUtils.getContext(), SPConstants.BASE_URL, "").toString();
ApiInterface apiService =
ApiClient.getClient(baseUrl).create(ApiInterface.class);
Call<LoginBean> call = apiService.getLoginResponse(queryParams);
call.enqueue(new Callback<LoginBean>() {
#Override
public void onResponse(Call<LoginBean> call, Response<LoginBean> response) {
if (response.body().isObjectNull()) {
httpCallback.resultCallback(APIConstants.API_LOGIN, HttpCallback.REQUEST_TYPE_GET,
HttpCallback.RETURN_TYPE_FAILURE, 0, null);
return;
}
httpCallback.resultCallback(APIConstants.API_LOGIN, HttpCallback.REQUEST_TYPE_GET,
HttpCallback.RETURN_TYPE_SUCCESS, response.code(), response.body());
}
#Override
public void onFailure(Call<LoginBean> call, Throwable t) {
// Log error here since request failed
httpCallback.resultCallback(APIConstants.API_APP_VERIFICATION, HttpCallback.REQUEST_TYPE_GET,
HttpCallback.RETURN_TYPE_FAILURE, 0, t);
t.printStackTrace();
}
});
}
Interface
#GET("App/login")
Call<LoginBean> getLoginResponse(#QueryMap Map<String, String> queryMap);
PS :
The API cannot change for now, as some other applications are consuming it.
Gson parser does not return a null object instance for me to understand that there is json structure and datamodel mismatch.
RestAdapter is deprecated in Retrofit 2
I'm looking for the best approach to resolve this , preferably avoid manually json parsing and take most advantage of retrofit and RX adapters.
EDIT
Response code 200 hence
response.isSuccessful() == true
response.body() != null is also true as Gson never creates a null instance or throws any exception if there is mismatch of JSON structure
response.errorBody() == null at all times as response sent as input stream from the server.
if (response.isSuccessful() && response.body() != null) {
//control always here as status code 200 for error condition also
}else if(response.errorBody()!=null){
//control never reaches here
}
EDIT 2
SOLUTION
The solution is based on anstaendig answer
I have created a base generic class to further this answer.
Since I have multiple apis and data models I have to create deserilizers for each
BASE API BEAN
public class BaseApiBean<T> {
#Nullable
private T responseBean;
#Nullable
private ErrorBean errorBean;
public BaseApiBean(T responseBean, ErrorBean errorBean) {
this.responseBean = responseBean;
this.errorBean = errorBean;
}
public T getResponseBean() {
return responseBean;
}
public void setResponseBean(T responseBean) {
this.responseBean = responseBean;
}
public ErrorBean getErrorBean() {
return errorBean;
}
public void setErrorBean(ErrorBean errorBean) {
this.errorBean = errorBean;
}
}
BASE DESERIALIZER
public abstract class BaseDeserializer implements JsonDeserializer<BaseApiBean> {
#Override
public BaseApiBean deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
// Get JsonObject
final JsonObject jsonObject = json.getAsJsonObject();
if (jsonObject.has("result")) {
/* {"result":"404"}*/
ErrorBean errorMessage = new Gson().fromJson(jsonObject, ErrorBean.class);
return getResponseBean(errorMessage);
} else {
return getResponseBean(jsonObject);
}
}
public abstract BaseApiBean getResponseBean(ErrorBean errorBean);
public abstract BaseApiBean getResponseBean(JsonObject jsonObject);
}
Custom Deserializer for each API
public class LoginDeserializer extends BaseDeserializer {
#Override
public BaseApiBean getResponseBean(ErrorBean errorBean) {
return new LoginResponse(null, errorBean);
}
#Override
public BaseApiBean getResponseBean(JsonObject jsonObject) {
LoginBean loginBean = (new Gson().fromJson(jsonObject, LoginBean.class));
return new LoginResponse(loginBean, null);
}
}
CUSTOM RESPONSE BEAN
public class LoginResponse extends BaseApiBean<LoginBean> {
public LoginResponse(LoginBean responseBean, ErrorBean errorBean) {
super(responseBean, errorBean);
}
}
CLIENT
public class ApiClient {
private static Retrofit apiClient = null;
private static Retrofit apiClientForFeedBack = null;
private static LoginDeserializer loginDeserializer = new LoginDeserializer();
private static AppVerificationDeserializer appVerificationDeserializer = new AppVerificationDeserializer();
public static Retrofit getClient(String url) {
if (apiClient == null) {
GsonBuilder gsonBuilder=new GsonBuilder();
gsonBuilder.registerTypeAdapter(LoginResponse.class,
loginDeserializer);
gsonBuilder.registerTypeAdapter(AppVerificationResponse.class,
appVerificationDeserializer);
Gson gson= gsonBuilder.create();
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient httpClient = new OkHttpClient.Builder().addInterceptor(interceptor)
.retryOnConnectionFailure(true)
.connectTimeout(15, TimeUnit.SECONDS)
.build();
apiClient = new Retrofit.Builder()
.baseUrl(url)
/*addCallAdapterFactory for RX Recyclerviews*/
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
/* add ScalarsConverterFactory to get json string as response */
// .addConverterFactory(ScalarsConverterFactory.create())
// .addConverterFactory(GsonConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.client(httpClient)
.build();
}
return apiClient;
}
HANDLE RESPONSE
public static void getLoginAPIResponse(String username, String password, String sourceId, String uuid, final HttpCallback httpCallback) {
baseUrl = AppPreference.getParam(getContext(), SPConstants.MT4_BASE_URL, "").toString();
ApiInterface apiService =
ApiClient.getClient(baseUrl).create(ApiInterface.class);
HashMap<String, String> queryParams = new HashMap<>();
queryParams.put(APIConstants.KEY_EMAIL, sourceId + username.toLowerCase());
queryParams.put(APIConstants.KEY_PASSWORD, Utils.encodePwd(password));
Call<LoginResponse> call = apiService.getLoginResponse(queryParams);
call.enqueue(new Callback<LoginResponse>() {
#Override
public void onResponse(Call<LoginResponse> call, Response<LoginResponse> response) {
if (response.body().getResponseBean()==null) {
httpCallback.resultCallback(APIConstants.API_LOGIN, HttpCallback.REQUEST_TYPE_GET,
HttpCallback.RETURN_TYPE_FAILURE, 0, response.body().getErrorBean());
return;
}
httpCallback.resultCallback(APIConstants.API_LOGIN, HttpCallback.REQUEST_TYPE_GET,
HttpCallback.RETURN_TYPE_SUCCESS, response.code(), response.body().getResponseBean());
}
#Override
public void onFailure(Call<LoginResponse> call, Throwable t) {
// Log error here since request failed
httpCallback.resultCallback(APIConstants.API_APP_VERIFICATION, HttpCallback.REQUEST_TYPE_GET,
HttpCallback.RETURN_TYPE_FAILURE, 0, t);
t.printStackTrace();
}
});
}
So you have two different successful (status code 200) responses from the same endpoint. One being the actual data model and one being an error (both as a json structure like this?:
Valid LoginBean response:
{
"id": 1234,
"something": "something"
}
Error response
{
"error": "error message"
}
What you can do is have an entity that wraps both cases and use a custom deserializer.
class LoginBeanResponse {
#Nullable private final LoginBean loginBean;
#Nullable private final ErrorMessage errorMessage;
LoginBeanResponse(#Nullable LoginBean loginBean, #Nullable ErrorMessage errorMessage) {
this.loginBean = loginBean;
this.errorMessage = errorMessage;
}
// Add getters and whatever you need
}
A wrapper for the error:
class ErrorMessage {
String errorMessage;
// And whatever else you need
// ...
}
Then you need a JsonDeserializer:
public class LoginBeanResponseDeserializer implements JsonDeserializer<LoginBeanResponse> {
#Override
public LoginBeanResponse deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
// Based on the structure you check if the data is valid or not
// Example for the above defined structures:
// Get JsonObject
final JsonObject jsonObject = json.getAsJsonObject();
if (jsonObject.has("error") {
ErrorMessage errorMessage = new Gson().fromJson(jsonObject, ErrorMessage.class);
return new LoginBeanResponse(null, errorMessage)
} else {
LoginBean loginBean = new Gson().fromJson(jsonObject, LoginBean.class):
return new LoginBeanResponse(loginBean, null);
}
}
}
Then add this deserializer to the GsonConverterFactory:
GsonBuilder gsonBuilder = new GsonBuilder().registerTypeAdapter(LoginBeanResponse.class, new LoginBeanResponseDeserializer()).create():
apiClient = new Retrofit.Builder()
.baseUrl(url)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gsonBuilder))
.client(httpClient)
.build();
This is the only way I can think of making this work. But as already mentioned this kind of API design is just wrong because status codes are there for a reason. I still hope this helps.
EDIT: What you can then do inside the class where you make the call to that Retrofit (if you already converted from Call<LoginBeanResponse> to Single<LoginBeanResponse> with RxJava) is actually return a proper error. Something like:
Single<LoginBean> getLoginResponse(Map<String, String> queryMap) {
restApi.getLoginResponse(queryMap)
.map(loginBeanResponse -> { if(loginBeanResponse.isError()) {
Single.error(new Throwable(loginBeanResponse.getError().getErrorMessage()))
} else {
Single.just(loginBeanReponse.getLoginBean())
}})
}
You can simply do that by doing this
try
{
String error = response.errorBody().string();
error = error.replace("\"", "");
Toast.makeText(getContext(), error, Toast.LENGTH_LONG).show();
}
catch (IOException e)
{
e.printStackTrace();
}
One possible solution is to make Gson fail on unknown properties. There seems to be an issue raised already(https://github.com/google/gson/issues/188). You can use the workaround provided in the issue page. So the steps are as follows:
Add the workaround ValidatorAdapterFactory to the code base:
public class ValidatorAdapterFactory implements TypeAdapterFactory {
#Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
// If the type adapter is a reflective type adapter, we want to modify the implementation using reflection. The
// trick is to replace the Map object used to lookup the property name. Instead of returning null if the
// property is not found, we throw a Json exception to terminate the deserialization.
TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
// Check if the type adapter is a reflective, cause this solution only work for reflection.
if (delegate instanceof ReflectiveTypeAdapterFactory.Adapter) {
try {
// Get reference to the existing boundFields.
Field f = delegate.getClass().getDeclaredField("boundFields");
f.setAccessible(true);
Map boundFields = (Map) f.get(delegate);
// Then replace it with our implementation throwing exception if the value is null.
boundFields = new LinkedHashMap(boundFields) {
#Override
public Object get(Object key) {
Object value = super.get(key);
if (value == null) {
throw new JsonParseException("invalid property name: " + key);
}
return value;
}
};
// Finally, push our custom map back using reflection.
f.set(delegate, boundFields);
} catch (Exception e) {
// Should never happen if the implementation doesn't change.
throw new IllegalStateException(e);
}
}
return delegate;
}
}
Build a Gson object with this TypeAdaptorFactory:
Gson gson = new GsonBuilder().registerTypeAdapterFactory(new ValidatorAdapterFactory()).create()
And then use this gson instance in GsonConverterFactory like below:
apiClient = new Retrofit.Builder()
.baseUrl(url)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson)) //Made change here
.client(httpClient)
.build();
This should throw an error if the unmarshalling step finds an unknown property, in this case the error response structure.
Here is another attempt. General idea: create a custom Converter.Factory based on GsonConverterFactory and a custom Converter<ResponseBody, T> converter based on GsonRequestBodyConverter to parse whole body 2 times: first time as error and second time as actual expected response type. In this way we can parse error in a single place and still preserve friendly external API. This is actually similar to #anstaendig answer but with much less boilerplate: no need for additional wrapper bean class for each response and other similar stuff.
First class ServerError that is a model for your "error JSON" and custom exception ServerErrorException so you can get all the details
public class ServerError
{
// add here actual format of your error JSON
public String errorMsg;
}
public class ServerErrorException extends RuntimeException
{
private final ServerError serverError;
public ServerErrorException(ServerError serverError)
{
super(serverError.errorMsg);
this.serverError = serverError;
}
public ServerError getServerError()
{
return serverError;
}
}
Obviously you should change the ServerError class to match your actual data format.
And here is the main class GsonBodyWithErrorConverterFactory:
public class GsonBodyWithErrorConverterFactory extends Converter.Factory
{
private final Gson gson;
private final GsonConverterFactory delegate;
private final TypeAdapter<ServerError> errorTypeAdapter;
public GsonBodyWithErrorConverterFactory()
{
this.gson = new Gson();
this.delegate = GsonConverterFactory.create(gson);
this.errorTypeAdapter = gson.getAdapter(TypeToken.get(ServerError.class));
}
#Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit)
{
return new GsonBodyWithErrorConverter<>(gson.getAdapter(TypeToken.get(type)));
}
#Override
public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit)
{
return delegate.requestBodyConverter(type, parameterAnnotations, methodAnnotations, retrofit);
}
#Override
public Converter<?, String> stringConverter(Type type, Annotation[] annotations, Retrofit retrofit)
{
return delegate.stringConverter(type, annotations, retrofit);
}
class GsonBodyWithErrorConverter<T> implements Converter<ResponseBody, T>
{
private final TypeAdapter<T> adapter;
GsonBodyWithErrorConverter(TypeAdapter<T> adapter)
{
this.adapter = adapter;
}
#Override
public T convert(ResponseBody value) throws IOException
{
// buffer whole response so we can safely read it twice
String contents = value.string();
try
{
// first parse response as an error
ServerError serverError = null;
try
{
JsonReader jsonErrorReader = gson.newJsonReader(new StringReader(contents));
serverError = errorTypeAdapter.read(jsonErrorReader);
}
catch (Exception e)
{
// ignore and try to read as actually required type
}
// checked that error object was parsed and contains some data
if ((serverError != null) && (serverError.errorMsg != null))
throw new ServerErrorException(serverError);
JsonReader jsonReader = gson.newJsonReader(new StringReader(contents));
return adapter.read(jsonReader);
}
finally
{
value.close();
}
}
}
}
The basic idea is that the factory delegates other calls to the standard GsonConverterFactory but intercepts responseBodyConverter to create a custom GsonBodyWithErrorConverter. The GsonBodyWithErrorConverter is doing the main trick:
First it reads whole response as String. This is required to ensure response body is buffered so we can safely re-read it 2 times. If your response actually might contain some binary you should read and buffer the response as binary and unfortunately retrofit2.Utils.buffer is not a public method but you can create a similar one yourself. I just read the body as a String as it should work in simple cases.
Create a jsonErrorReader from the buffered body and try to read the body as a ServerError. If we can do it, we've got an error so throw our custom ServerErrorException. If we can't read it in that format - just ignore exception as it is probably just normal successful response
Actually try to read the buffered body (second time) as the requested type and return it.
Note that if your actual error format is not JSON you still can do all the same stuff. You just need to change the error parsing logic inside GsonBodyWithErrorConverter.convert to anything custom you need.
So now in your code you can use it as following
.addConverterFactory(new GsonBodyWithErrorConverterFactory()) // use custom factory
//.addConverterFactory(GsonConverterFactory.create()) //old, remove
Note: I haven't actually tried this code so there might be bugs but I hope you get the idea.

Where to insert API key into POST request in Android studio

Just want to know where do I insert the API key for the server in my code below:
public class GetCurrentJob extends Job {
Context context;
GetFeedback feedback;
protected GetCurrentJob(Context context, GetFeedback fb) {
super(new Params(PRIORITY.HIGH).requireNetwork());
feedback = fb;
this.context = context;
}
#Override
public void onAdded() {
}
#Override
public void onRun() throws Throwable {
//POST feedback to server... require API key. How?
Response<String> response = Ion.with(context)
.load("POST", URLbuilder.getURL())
.setStringBody(feedback.toJson())
.asString()
.withResponse()
.get();
//Toast.makeText(context, "post", Toast.LENGTH_SHORT).show();
if (response.getHeaders().code() != HttpURLConnection.HTTP_OK) {
Log.d("test", "error in request " + String.valueOf(response.getResult()));
return;
}
else
{
Log.d("test", "success" + String.valueOf(response.getResult()));
}
}
#Override
protected void onCancel(int cancelReason, #Nullable Throwable throwable) {
}
#Override
protected RetryConstraint shouldReRunOnThrowable(#NonNull Throwable throwable, int runCount, int maxRunCount) {
return null;
}
}
My URLbuilder class:
public class URLbuilder {
private static final String SERVER = "http://jxapp-s-ticket.cloudapp.net/jxapp_ticket/upload/api/http.php/tickets.json";
public static String getURL(){
return Uri.parse(SERVER).buildUpon().toString();
}
}
Just a little information:
My app takes user feedback and returns the feedback to the server, then the server will generate a ticket to the user.
I am able to generate a response from the server, namely from the log. But the log generates: "error in request Valid API key required".
I need a way to insert the API key but I do not know how (am quite new to Android Studio as well as POST and GET operations)!
Do help if possible! Thanks!
Response<String> response = Ion.with(context)
.load("POST", URLbuilder.getURL())
.setHeader("x-api"," API KEY HERE ")
.setStringBody(feedback.toJson())
.asString()
.withResponse()
.get();
Should anyone encounter the same problem this is how i solved this.
Modified the ion formatting by adding a header with the API key.
All credits to a senior of mine.

Using Post in Retrofit

Can you anyone help me giving good example on how to use retrofit for posting large data from my local DB to mysql server.
Currently I am using async-http API and strangely there is always memory error coming up. I am looking for better API that wont give memory error while uploading huge text of data.
This is my setup:
List<TextDetails> unTextList = dbvalue.getTextData();
for (TextDetails td : unTextList)
{
String textID = td.getSerialNumber();
String textSMS = td.getText();
String textAddress = td.getFulladdress();
String textDate = td.getFulldate();
String textException = td.getExceptiontext();
textDetailsBackUpDataOnline(textID , textSMS, textAddress, textDate, textException);
}
private void textDetailsBackUpDataOnline(final String textID ,
String textSMS, String textAddress, String textDate, String textException)
{
final String uploadWebsite = url_backup_text_details;
RequestParams requestParams = new RequestParams();
requestParams.put("textSMS", textSMS);
requestParams.put("textAddress", textAddress);
requestParams.put("textDate", textDate);
requestParams.put("textException", textException);
Text_HttpClient.post(uploadWebsite, requestParams, new AsyncHttpResponseHandler()
{
#Override
public void onSuccess(int statusCode, org.apache.http.Header[] headers, byte[] responseBody)
{
Log.e("textID", "= how many times");
}
#Override
public void onFailure(int statusCode, org.apache.http.Header[] headers, byte[] errorResponse, Throwable e)
{
e.printStackTrace(System.out);
}
});
}
Text_HttpClient class has the following:
public class Text_HttpClient
{
private static AsyncHttpClient client = new AsyncHttpClient();
public static void get(String url, RequestParams params, AsyncHttpResponseHandler responseHandler)
{
client.get(url, params, responseHandler);
}
public static void post(String url, RequestParams requestParams, AsyncHttpResponseHandler responseHandler)
{
client.post(url, requestParams, responseHandler);
}
}
1) Write service interface:
public interface ArticleGetListService {
#FormUrlEncoded // Request will have "application/x-www-form-urlencoded" MIME type
#POST("/api/Article/ArticleGetList")
public void getArticleList(#Field("LanguageCode") String languageCode,
#Field("CategoryId") String categoryId,
#Field("Token") String token,
Callback<ArticleViewPojo> response); //POJO: The json retrieved from the server is added to this class.
}
Here my Rest service requires 3 Parameters, change it as your need.
2) Write POJO for converting JSON returned from Rest Api into java class object so you can use data.
Just copy your JSON into this site, choose JSON source type, annotation as Gson. It will generate POJO for your JSON automatically.
3)On your Main Activity
RestAdapter restAdapter = new RestAdapter.Builder()
.setLogLevel(RestAdapter.LogLevel.FULL)
.setEndpoint(baseUrl)
.build();
ArticleGetListService articleGetListService = restAdapter.create(ArticleGetListService.class);
Callback<ArticleViewPojo> callback = new Callback<ArticleViewPojo>() {
#Override
public void success(ArticleViewPojo model, Response response) {
//use model which is data returned to you
}
#Override
public void failure(RetrofitError error) {
//handle error
}
};
//START REST CALL
articleGetListService.getArticleList(languageCode, categoryId, token, callback);
//above parameters are those written in service interface at 1
//Whole Url is baseUrl+ArticleGetListService in above example

Categories