This is my sample URL to the API end point:
https://api.projectoxford.ai/luis/v1/application?id=b0d5b503-eb2e-460a-b028-a3223aa93227&subscription-key=bc1cb297a94f4c9a9b58bcd36280466c&q=start%20test
Now, how do I create the base URL and define end point for use in Retrofit.
I've created model class for JSON, and this is how I defined base URL:
public class ApiClient {
public static final String BASE_URL = "https://api.projectoxford.ai/luis/v1/";
private static Retrofit retrofit = null;
public static Retrofit getClient() {
if (retrofit==null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
This is how I tried to define the end point:
public interface ApiInterface {
#GET("application")
Call<LuisPojo> getValues(#Query("id") String apiKey);
}
But how to add the remaining part, subscription-key and the search term in the end
start test
Append it in your getValues()
#GET("application")
Call<LuisPojo> getValues(#Query("id") String apiKey, #Query("subscription-key") String key, #Query("q") String q);
Related
I'm using Retrofit to make API call, When I handle the response I get the next error (Need to get the data from the API call) -
Attempt to invoke interface method 'java.lang.Object java.util.List.get(int)' on a null object reference
I don't know if I'm doing it right. Anyway here's my code.
Here's the url link: https://data.gov.il/api/
Retrofit call -
#GET("datastore_search?resource_id=2c33523f-87aa-44ec-a736-edbb0a82975e")
Call<Result> getRecords();
Retrofit base call -
private static Retrofit retrofit;
public static final String BASE_URL = "https://data.gov.il/api/action/";
public static Retrofit getRetrofitInstance() {
if (retrofit == null) {
retrofit = new retrofit2.Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
Model class -
public class Result {
#SerializedName("include_total")
#Expose
private Boolean includeTotal;
#SerializedName("resource_id")
#Expose
private String resourceId;
#SerializedName("fields")
#Expose
private List<Field> fields = null;
#SerializedName("records_format")
#Expose
private String recordsFormat;
#SerializedName("records")
#Expose
private List<Record> records = null;
#SerializedName("limit")
#Expose
private Integer limit;
#SerializedName("_links")
#Expose
private Links links;
#SerializedName("total")
#Expose
private Integer total;
public Boolean getIncludeTotal() {
return includeTotal;
}
public void setIncludeTotal(Boolean includeTotal) {
this.includeTotal = includeTotal;
}
public String getResourceId() {
return resourceId;
}
public void setResourceId(String resourceId) {
this.resourceId = resourceId;
}
public List<Field> getFields() {
return fields;
}
public void setFields(List<Field> fields) {
this.fields = fields;
}
public String getRecordsFormat() {
return recordsFormat;
}
public void setRecordsFormat(String recordsFormat) {
this.recordsFormat = recordsFormat;
}
public List<Record> getRecords() {
return records;
}
public void setRecords(List<Record> records) {
this.records = records;
}
...
Main Activity -
RecallService service = RetrofitClientInstance.getRetrofitInstance().create(RecallService.class);
Call<Result> records = service.getRecords();
records.enqueue(new Callback<Result>() {
#Override
public void onResponse(Call<Result> call, Response<Result> response) {
Log.d(TAG, String.valueOf(response.body().getRecords().get(0).getId())); // ERROR
}
#Override
public void onFailure(Call<Result> call, Throwable t) {
Log.e(TAG, t.getMessage());
}
});
The response, which you are getting from the API, doesn't fit the Result POJO.
The response you get from API is like below:
{
"help": "https://data.gov.il/api/3/action/help_show?name=datastore_search",
"success": true,
"result": {...}
}
By using the Result POJO, you are assuming that you get the response as below, which is a json inside the actual response json, and is not what you actually receive. So, just create a POJO which fairly represents the actual response.
{
"include_total": true,
"resource_id": "2c33523f-87aa-44ec-a736-edbb0a82975e",
"fields": [...],
"records_format": "objects",
"records":[...]
}
Try making a class like below (set the annotations yourself):
class Resp{
Result result;
}
Replace the class Result with Resp, like below and other usages:
#GET("datastore_search?resource_id=2c33523f-87aa-44ec-a736-edbb0a82975e")
Call<Resp> getRecords();
Then, finally you can do:
response.body().getResult().getRecords()
The API link you've shared returns the response in the format below:
{"help": "https://data.gov.il/api/3/action/help_show?name=datastore_search", "success": true, "result": {...}}
You are setting the response object to be of type Result which is actually a sub-element within the root element help in the json response. response.body() would include help and the result would be it's sub-element. Since it is not parsed correctly, you're getting a null response.
You will need to include the root element in your model class and update the API call to use that class type as the response type.
I am using from rx for connect to service with retrofit, bellow is RetrofitApi.java :
public class RetrofitApi {
private static PublicApi retrofit = null;
public static PublicApi getClient(String url) {
retrofit = new Retrofit.Builder()
.baseUrl(url)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build().create(PublicApi.class);
return retrofit;
}
}
And here is PublicApi.java :
public interface PublicApi {
#GET("/web_service/mobile/rest")
Observable<LastNews> lastNews(#Query("function") String function);
}
Bellow I am connecting to my service :
#Override
public void fetchLastNewsStartPage(RemoteDataSource.ResultListener<List<LastNews>> resultListener) {
PublicApi publicApi = RetrofitApi.getClient("https://xxx.xxx.xxx/web_service/");
CompositeDisposable mCompositeDisposable = new CompositeDisposable();
mCompositeDisposable.add(publicApi.lastNews("getLastNews")
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(this::handleResponse, this::handleError));
}
My problem is here, how can I send parameter to handleResponse and handleError. I need to send this RemoteDataSource.ResultListener<List<LastNews>> resultListener to handleResponse and handleError:
private void handleResponse(LastNews lastNewses) {
}
private void handleError(Throwable error) {
}
Just don't use method reference as it can only accept one parameter. You can achieve the result with a lambda expression. Instead of
this::handleResponse
write
lastNews -> handleResponse(lastNews, resultListener)
I am trying to use Retrofit and RxJava to make an API call within a custom view in an app that I am working on, but I encounter an incompatible type error when trying to subscribe to the Observable from my Retrofit API call.
My retrofit interface:
public interface ApiQueryInterface{
// Request method and URL specified in the annotation
// Callback for the parsed response is the last parameter
#GET("users/")
Observable<Users> getUsers (
#Query("key") String key,
#Query("address") String address
);
#GET("posts/")
Observable<Posts> getPosts (
#Query("key") String key,
#Query("address") String address
);
}
and the Retrofit call located within the onFinishInflate() of the custom view:
// Create RxJava adapter for synchronous call
RxJava2CallAdapterFactory rxAdapter = RxJava2CallAdapterFactory.create();
// Create Retrofit2 instance for API call
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(rxAdapter)
.build();
// Make API call using retrofit
final ApiQueryInterface apiQueryInterface = retrofit.create(ApiQueryInterface.class);
// API return type defined by interface
Observable<Users> query = apiQueryInterface
.getUsers(KEY, ADDRESS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Users>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(Users users) {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
}
When I build the project I hit an incompatible types error in the custom view on the line beginning with Observable<Users> query = ...:
Error:(60, 27) error: incompatible types: void cannot be converted to Observable<Users>
"Users" is a generic model class which matches the JSON object returned from the API
RxJava 1 returns a Subscription object not an Observable. RxJava 2 subscription returns void. That's why you are getting Error:(60, 27) error: incompatible types. You are getting the Disposable in the callback onSubscribe. If you need a reference to it, you can assign it to a class level member when the callback is invoked
Change returned object to Subscription
private Subscription subscription;
....
subscription = ApiClient.getInstance()
.getUsers(KEY, ADDRESS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<Users>>() {
#Override public void onCompleted() {
}
#Override public void onError(Throwable e) {
}
#Override public void onNext(List<Users> users) {
}
});
apiclient
public class ApiClient {
private static ApiClient instance;
private ApiQueryInterface apiqueryinterface;
private ApiClient() {
final Gson gson =
new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
final Retrofit retrofit = new Retrofit.Builder().baseUrl(BASE_URL)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
apiqueryinterface = retrofit.create(ApiQueryInterface.class);
}
public static ApiClient getInstance() {
if (instance == null) {
instance = new ApiClient();
}
return instance;
}
public Observable<List<Users>> getUsers(#NonNull String key, #NonNull String address) {
return apiqueryinterface.getUsers(key, address);
}
}
interface
public interface ApiQueryInterface{
// Request method and URL specified in the annotation
// Callback for the parsed response is the last parameter
#GET("users")
Observable<<List<Users>> getUsers (
#Query("key") String key,
#Query("address") String address
);
I have a class named UrlController The method is like:
public class UrlController {
private static final String BASE_URL = "http://api.site.mil/";
private static Retrofit retrofit;
private UrlController() {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
public static Retrofit getRetrofit() {
return retrofit;
}
}
Should I check if(retrofit==null) in the getRetrofit() method or it's that always not null?
Nothing can construct UrlController (except UrlController because your constructor is private) so retrofit is guaranteed to be null. I think you wanted a static initializer. Something like,
static {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
Since the method getRetrofit is static, hence it can be called, even when the object of the class has not been created.
Hence, you must check for it.
But even if you don't check, the method will return null. So you must check for null where you call this method.
Are you trying to create a Singleton?
Because right now you have a private constructor which can only be called within the class. So your retrofit field will always be null. Furthermore you probably want to use something like this:
public final class UrlController {
private static final String BASE_URL = "http://api.site.mil/";
private static volatile Retrofit instance;
private UrlController() {
instance = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
public static Retrofit getInstance() {
if (instance == null) {
synchronized (UrlController.class) {
if (instance == null) {
instance = new UrlController();
}
}
}
return instance;
}
}
With that you can simple call UrlController.getInstance() and you will never get a null by that.
I am writing an android app that will use Retrofit to make API requests.
I have a helper class like this:
public class ApiService {
public static final String TAG = ApiService.class.getSimpleName();
public static final String BASE_URL = "https://myapiurl.com";
public static void testApi(){
ApiEndpointInterface apiService = prepareService();
apiService.ping(new Callback<Response>() {
#Override
public void success(Response apiResponse, retrofit.client.Response response) {
Log.e(TAG, apiResponse.toString());
}
#Override
public void failure(RetrofitError error) {
Log.e("Retrofit:", error.toString());
}
});
}
private static ApiEndpointInterface prepareService() {
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(BASE_URL)
.build();
ApiEndpointInterface apiService =
restAdapter.create(ApiEndpointInterface.class);
restAdapter.setLogLevel(RestAdapter.LogLevel.FULL);
return apiService;
}
}
And my actual Retrofit implementation is simple:
public class ApiEndpointInterface {
#GET("/v1/myendpoint")
void ping(Callback<Response> cb);
}
The problem is, I cannot build the project, I get the error:
Error:(12, 10) error: missing method body, or declare abstract
Referring to my ApiEndpointInterface class.
Any idea what's going on?
Try public interface for your API declaration.
public interface ApiEndpointInterface {
#GET("/v1/myendpoint")
void ping(Callback<Response> cb);
}
Also, looks like you're creating your ApiEndpointInterface before telling the builder to set log level to full.
private static ApiEndpointInterface prepareService() {
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(BASE_URL)
.setLogLevel(RestAdapter.LogLevel.FULL);
.build();
ApiEndpointInterface apiService =
restAdapter.create(ApiEndpointInterface.class);
return apiService;
}
In case you update to okHttp Version 2.4.0 , you will get an exception for empty Body as latest version no more allows zero length request , in which case you would have to use the following syntax
public interface ApiEndpointInterface {
#GET("/v1/myendpoint")
void ping(Callback<Response> cb, #Body String dummy);
}
call
ApiEndpointInterface apiService =
restAdapter.create(ApiEndpointInterface.class);
apiService.ping(callback,"");
Ref
https://github.com/square/okhttp/issues/751