Below I prepared some of the code I use when I execute query to server.
Response from the server returns list with books in json format. One of the fields params contains list in json array.
How could I force Moshi to treat this field as a raw string?
Here is an explanation of my code.
This is a pseudo-class from where I call instance of Retrofit.
public final class RestApi {
private static final String API_ENDPOINT_KEY = "...";
private final MyApi myApi;
private RestApi() {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
OkHttpClient client = builder.build();
final Retrofit retrofit = (new Retrofit.Builder())
.baseUrl(API_ENDPOINT_KEY)
.client(client)
.addConverterFactory(MoshiConverterFactory.create())
.build();
myApi = retrofit.create(MyApi.class);
}
public static RestApi getInstance() {
return RestApiGazeta.InstanceHolder.INSTASNCE;
}
private static final class InstanceHolder {
private static final RestApi INSTASNCE = new RestApi();
}
}
This is the Book class with prepared fields for Moshi.
#Generated("com.robohorse.robopojogenerator")
public class Book{
#Json(name = "pages")
private int pages;
#Json(name = "params")
private Params params;
}
This is the query for Retrofit for querying list of books.
#GET("android/getBooks.json")
Call<List<Book>> getBooks(
#Query("section") String section
);
What I have tried was I changed return type in Book class type Params to String but I was returning exception about Expected a string but was BEGIN_ARRAY at path $[0].params.
Okay, so I've read Moshi documentation and it seems to be easy to convert any field to json string. I don't know how to parse it on the fly but here is the solution how to do it after You get ready (in my case) Book object.
Moshi moshi = new Moshi.Builder().build();
JsonAdapter<Params> jsonAdapter = moshi.adapter(Params.class);
String json = jsonAdapter.toJson(body.getParams());
That's it.
Related
In my code, I want to send post request with basic auth.
Here is my postman screenshot :
here is my apiInterface class
#FormUrlEncoded
#POST("GetBarcodeDetail")
Call<PreliminaryGoodsAcceptResponse> PRELIMINARY_GOODS_ACCEPT_RESPONSE_CALL(#Field("ProcName") String procName, #Field("Barcode") String barcode, #Field("LangCode") String langCode);
here is my apiclient
public class ApiClient {
public static final String BASE_URL = "http://192.**********";
private static Retrofit retrofit = null;
private static OkHttpClient sClient;
public static Retrofit getClient() {
if(sClient == null) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
sClient = new OkHttpClient.Builder()
.addInterceptor(new HttpLoggingInterceptor(HttpLoggingInterceptor.Logger.DEFAULT))
.addInterceptor(interceptor)
.build();
}
if (retrofit==null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(sClient)
.build();
}
return retrofit;
}
}
My question is how can i send post request,using header :
Header Username : EBA Token :
34242353453456563DSFS
This is so far the easiest method i have ever tried for "Basic Authentication".
Use the below code to generate the auth header (API/Repository class)
var basic = Credentials.basic("YOUR_USERNAME", "YOUR_PASSWORD")
Pass this as header to the webservice call (API/Repository class)
var retrofitCall = myWebservice.getNewsFeed(basic)
Add the basic header as parameter (Retrofit Webservice interface class)
#GET("newsfeed/daily")
fun getNewsFeed(#Header("Authorization") h1:String):Call<NewsFeedResponse>
Sorry, my code is in Kotlin, but can be easily translated to Java.
References: https://mobikul.com/basic-authentication-retrofit-android/
make header like this way..
private Retrofit getClient(final Context context) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder client = new OkHttpClient.Builder();
client.readTimeout(60, TimeUnit.SECONDS);
client.writeTimeout(60, TimeUnit.SECONDS);
client.connectTimeout(60, TimeUnit.SECONDS);
client.addInterceptor(interceptor);
client.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if (context == null) {
request = request
.newBuilder()
.build();
} else {
request = request
.newBuilder()
.addHeader("Authorization", "Bearer " + AppSetting.getStringSharedPref(context, Constants.USER_KEY_TOKEN, ""))
.build();
}
return chain.proceed(request);
}
});
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client.build())
.addConverterFactory(GsonConverterFactory.create())
.build();
return retrofit;
}
Use Header annotation
#FormUrlEncoded
#POST("GetBarcodeDetail")
Call<PreliminaryGoodsAcceptResponse> PRELIMINARY_GOODS_ACCEPT_RESPONSE_CALL(#Header("Authorization") token: String,#Field("ProcName") String procName, #Field("Barcode") String barcode, #Field("LangCode") String langCode);
Simple-Retrofit-API-request-and-Data-Loading Here I just add the project where create the API call to access data from database using retrofit library; which is leading library to access data on network. And display the accessed data in the List format. Create the Simple Android Studio Project with Empty Activity. Create the Adapter and activity item to show normal lists in android app. Now Create the App class extending Application, as Application class is a singleton that you can access from any activity or anywhere else you have a Context object.
You can check the more details about Application class from https://github.com/codepath/android_guides/wiki/Understanding-the-Android-Application-Class Why extend an Application class? https://developer.android.com/reference/android/app/Application.html
Add android:name=".YourApplication" i.e. class name extending the Application class in android. and class will be like public class YourApplication extends Application Init the Retrofit in Application class
//network code start
//init http logger
httpLoggingInterceptor = new HttpLoggingInterceptor();
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
// init client client = new OkHttpClient.Builder()
.addInterceptor(httpLoggingInterceptor)
.addInterceptor(new Interceptor() {
#Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Request request2 = request.newBuilder().build();
return chain.proceed(request2);
}
}).connectTimeout(30, TimeUnit.SECONDS).writeTimeout(30, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS).build();
Gson gson = new GsonBuilder().setLenient().create();
Retrofit mRetrofit = new Retrofit.Builder().baseUrl(Constants.API_BASE_URL).client(client).addConverterFactory(GsonConverterFactory.create(gson)).build();
mWebservice = mRetrofit.create(Webservice.class);
While Constants.API_BASE_URL is base url Create the Webervice.class where you can call the API with parameters e.g. In case of GET Method:
#GET("webservices/GetAllClientsDemoRetro.php")
Call updateChatStatus();
In case of POST method:
#FormUrlEncoded
#Headers({"Content-Type: application/x-www-form-urlencoded"})
#POST("webservices/GetAllClientsDemoRetro.php")
Call updateChatStatus();
You can See the more in details About Retrofit on Official API declaration here: http://square.github.io/retrofit/
We can parse the values with POJO i.e. Setter and Getter, using the Parceble class. Since parsing key name should be equal to the value we are receiving from the JSON response. POJO class should be declared like public class ClientData implements Parcelable { then declare the keys in the class, key values means
public class ClientData implements Parcelable
{
public String client_id;
public String company_name;
public String address_line;
public String city;
public String pincode;
public String state;
public String country;
}
Now using Alt+Enter i.e. select the option Add Parceble Implementation and press enter. Then automatically parceble class will be added. Also you have to add Setter and Getter method in class using Alt + Insert. Note: Don’t add the Setter and Getter methods for CREATER: Creater<> method If you want to use different key that JSON response key, then you should use Serialization. When I was using same key then its is like public String client_id; But when I am using the Serialization, then I can use like #Serializattion(“client_id”) public String ClientID; Now last but not a list, We call the API using retrofit, and use the response to view the Item in list-
RetroFitApplication.getWebservice().updateChatStatus().enqueue(new Callback() {
#Override public void onResponse(Call call, Response response) {
Log.d("retrofilt success", "" + response.body());
if (response.body() != null) {
clientResponceData = response.body();
Gson gson = new Gson();
String body = gson.toJson(response.body());
Log.d("retrofilt success2", "clientData" + clientResponceData.getResponse());
if (clientResponceData.getResponse() != null) {
initRV();
}
} else {
// Empty Client List Toast.makeText(ClientList.this, "Empty List", Toast.LENGTH_SHORT).show();
}
}
#Override public void onFailure(Call call, Throwable t) {
Log.d("retrofilt error", "" + t);
Toast.makeText(ClientList.this, "No Internet Connection", Toast.LENGTH_SHORT).show();
}
});
By using the Construction in Adapter, we can use the values from the response. Guys I added this repository to get the Entire idea of calling the API and get the response from server using the Retrofit Library. I write this entire documents in details with simple word.
I am using Retrofit to send and receive requests to my server.
I Have a model like below and I have to send it to my server but some variables in this model have not to send to the server.
public class SelectedListModel implements Serializable {
#SerializedName("p_id")
#Expose
private Long pId;
#SerializedName("p_qty")
#Expose
private Double pQty;
#Expose(serialize = false , deserialize = false)
private String pName; //Have not to send to server
#Expose(serialize = false , deserialize = false)
private String pPrice; //Have not to send to server
#Expose(serialize = false , deserialize = false)
private String pImageUrl; //Have not to send to server
}
and because of that, I am getting 400 in my responses from the server.
I used of #Expose(serialize = false, deserialize = false) in order to Ignore variables that have not to send to the server.
But it doesn't work.
Is there any way to do this or I have to create another model just for my server?
Use transient keywork for that
public class SelectedListModel implements Serializable {
#SerializedName("p_id")
#Expose
private Long pId;
#SerializedName("p_qty")
#Expose
private Double pQty;
//#Expose(serialize = false , deserialize = false)
private transient String pName; //Have not to send to server
//#Expose(serialize = false , deserialize = false)
private transient String pPrice; //Have not to send to server
//#Expose(serialize = false , deserialize = false)
private transient String pImageUrl; //Have not to send to server
}
and no need to use #Expose(serialize = false , deserialize = false), into those fields which needed to be excluded.
Read Why does Java have transient fields? and Why use the `transient` keyword in java? for more details.
You can use transient keyword to ignore fields when requesting api calls
JAVA:
transient String name;
KOTLIN:
#Transient
var name: String
change your Retrofit adapter code like this (I hope you're using retrofit2)
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
I have a back-end server that works like this
"api/videos?search_object={"cat_id" :2, "channel_id" : 3, etc}
Basily you can give a search object as input and it will filter the list base on that object. Now I want to use this service with Retrofit with something like this
#GET("videos")
Call<VideoListResponse> listVideos(#Query("search_object") VideoSearchObject videoSearchObject);
But the above code doesn't work, I can first convert VideoSearchModel to JSON string that pass it to retrofit like this
#GET("videos")
Call<VideoListResponse> listVideos(#Query("search_object") String jsonString);
I wonder if there is a better more clear way? Any suggestions will be appreciated.
Retrofit 2 supports it. All you have to do is implementing a custom converter factory with the stringConverter() method overridden.
Consider the following Retrofit-friendly interface with a custom annotation:
#Target(PARAMETER)
#Retention(RUNTIME)
#interface ToJson {
}
interface IService {
#GET("api/videos")
Call<Void> get(
#ToJson #Query("X") Map<String, Object> request
);
}
The annotation is used to denote an argument that must be converted to a string.
Mock OkHttpClient to always respond with "HTTP 200 OK" and dump request URLs:
private static final OkHttpClient mockHttpClient = new OkHttpClient.Builder()
.addInterceptor(chain -> {
System.out.println(chain.request().url());
return new Response.Builder()
.request(chain.request())
.protocol(HTTP_1_0)
.code(HTTP_OK)
.body(ResponseBody.create(MediaType.parse("application/json"), "OK"))
.build();
})
.build();
private static final Gson gson = new Gson();
private static final Retrofit retrofit = new Retrofit.Builder()
.client(mockHttpClient)
.baseUrl("http://whatever")
.addConverterFactory(new Converter.Factory() {
#Override
public Converter<?, String> stringConverter(final Type type, final Annotation[] annotations, final Retrofit retrofit) {
if ( !hasToJson(annotations) ) {
return super.stringConverter(type, annotations, retrofit);
}
return value -> gson.toJson(value, type);
}
private boolean hasToJson(final Annotation[] annotations) {
for ( final Annotation annotation : annotations ) {
if ( annotation instanceof ToJson ) {
return true;
}
}
return false;
}
})
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
To test it you can simply invoke the service interface method:
final IService service = retrofit.create(IService.class);
service.get(ImmutableMap.of("k1", "v1", "k2", "v2")).execute();
Result:
http://whatever/api/videos?X={%22k1%22:%22v1%22,%22k2%22:%22v2%22}
Where the X parameter argument is an encoded representation of {"k1":"v1","k2":"v2"}.
You can try the below code, it works for me
#GET("api")
Call<Response> method(#Query("") JSONObject param);
Map<String, String> map = new HashMap<>();
map.put("key", "value");
JSONObject json = new JSONObject(map);
"api/videos?search_object={"cat_id" :2, "channel_id" : 3, etc}
Basically you can give a search object as input
No, you do not give an object as input. You provide multiple parameters enclosed in { } so that it looks like an object (a JavaScript object that is, not a Java object). In reality it is just a string.
The constructed url is just a bunch of characters. There is no such thing as an "object" in an url.
Keep doing it like #Query("search_object") String jsonString. Although you might also want to rename the parameter from jsonString to searchString, since that is what it is. It is not a JSON string. A JSON string would have all " characters escaped like \".
I'm using GSON with Retrofit to send a JSON object in the request body.
I want to include transient fields in serialization, so I called excludeFieldsWithModifiers(Modifier.STATIC), but they're still not included in the JSON request body.
// Retrofit Initialization
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.excludeFieldsWithoutExposeAnnotation();
gsonBuilder.excludeFieldsWithModifiers(Modifier.STATIC);
retrofitBuilder = new Retrofit.Builder();
retrofitBuilder.baseUrl(BASE_URL);
retrofitBuilder.addConverterFactory(GsonConverterFactory.create(gsonBuilder.create()));
Retrofit retrofit = retrofitBuilder.build();
// Field to be serialized
#Expose(deserialize = false, serialize = true)
#SerializedName("Name")
private transient String name;
Use transient keyword
transient public Drawable icon;
class Response<T>(
JHeader header;
T result;
)
class JHeader(
Int success;
List<String> error;
)
class Character{
#SerializedName("id_") Int id;
#SerializedName("levelA") String level
#SerializedName("a3") unit: String = "
Character(Int id, String level) {
this.id = id
this.level= level
}
}
Retrofit.Builder()
.addCallAdapterFactory(rxAdapter)
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.client(httpClient.build())
.build()
public Observable<Response<List<Character>>> getCharacterById(characterID: Int){
return apiService.getCharacter(characterID)
.subscribeOn(Schedulers.io())
}
And i have a custom type adapter made for Character.
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Character.class, new CharacterTypeAdapter());
Gson gson = gsonBuilder.create();
Gson is completly ignoring my type adapter while deserializing the list so i guess because of generic type erasure, the List gets treated like a List Object or something, so my adapter wont be used.
Using Retrofit2. Is there any way i can do his with generics, without having to create custom classes for Lists every time i need to parse a response which contains a list?