java.lang.IllegalArgumentException: Illegal URL with retrofit, hostname = null - java

The GET request to tenants/client/{hostname} returns a json object with the property baseApiHostname.
#GET("api/tenants/client/{hostname}")
Call<HostName> getHostname(#Path("hostname") String hostname);
baseApiHostname is successful retrieved. That hostname which is used to create the rest of tenant requests from.
Log.v("base url","testing "+preferenceManager.getTenantHostname());
retrofit = new Retrofit.Builder()
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create(builder.create()))
.baseUrl(preferenceManager.getTenantHostname())
.build();
I received the error "java.lang.IllegalArgumentException: Illegal URL".
stacktrace message
After attaching debugger I realized that hostname = null.
debugger message
Call<HostName> call = hostNameApiService.getHostname(tenant);
call.enqueue(new Callback<HostName>() {
#Override
public void onResponse(Call<HostName> call, Response<HostName> response) {
if (response.isSuccessful()) {
String hostname = response.body().getBaseApiHostname();
preferenceManager.setTenantHostname(hostname);
retrieveCampaign();
} else {
Toast.makeText(StartSurveyActivity.this, "Error Retrieving Hostname", Toast.LENGTH_SHORT).show();
}
}
Here is the Hostname model class
#SerializedName("id")
private String id;
#SerializedName("name")
private String name;
#SerializedName("baseApiHostName")
private String baseApiHostname;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getBaseApiHostname() {
return baseApiHostname;
}
public void setBaseApiHostname(String baseApiHostname) {
this.baseApiHostname = baseApiHostname;
}
If there's any other detail required that will aid in helping myself and other developers who may have faced a similar problem, please don't hesistate to ask.
Thanks in advance guys.

GSON is not successfully serializing any response to the field you're retrieving, so there is some misalign in how you are setting up gson to parse your json result.
Given your object and your sample JSON, your field in JSON is "baseApiHostname" whereas in your object your GSON annotation is for serialized name "baseApiHostName".

You are setting null in your Retrofit.Builder for your base URL. What is the URL for your server? You need to set that for the first time. Then you can change it after that if need be but it can't be null the first time.

Related

How to map various possible types of a JSON field to a POJO attribute?

I have a POJO which is used for mapping the values received from rabbitmq. The messages are sent from other third-party services which I don't have control over. The problem is that these services don't send the certain fields in a consistent manner.
For example, in the case of an error field, this field will only have information when the third-party service wants to send a message to inform that there is an error. Some services may send in their JSON message to the queue this way:
{
id: 123,
name: "PersonA",
error: null
}
And then there are also services that may send their JSON message this way:
{
id: 123,
name: "PersonA",
error: []
}
And there are also services that may just omit the field entirely:
{
id: 123,
name: "PersonA"
}
Of course, if there is an error, that error field will contain fields within it:
{
id: 123,
name: "PersonA",
error: {
message: "Cannot find PersonA"
}
}
So, there is a chance that the error field can be null, undefined, array or an object.
The problem is, I can only define a single type in my POJO for the error field.
public class MyMessage {
private int id;
private MessageError error;
private String name;
#JsonCreator
public MyMessage(
#JsonProperty("id") int id,
#JsonProperty("error") MessageError error, // error is a MessageError object already but what is sent in could be null, undefined, an array or an object
#JsonProperty("name") String name
) {
this.id = id;
this.error = error;
this.name = name;
}
}
public class MessageError {
private String message;
#JsonCreator
public MessageError(
#JsonProperty("message") String message
) {
this.message = message;
}
}
I'm using Spring with Jackson.
In this case, how can I handle and map all the possible values that maybe assigned to the error field in the message to the POJO when it gets deserialised?
If you don't have any markers in the json data to identify its source and thus its shape, I would try to set error field type to object, and if it doesn't work or fit, to "json raw" as explained in answers of How can I include raw JSON in an object using Jackson?
So you can proceed the error field in the appropriate way according to the content you would discover at runtime, peeking from expected ones you detailed above.
The simplest way to resolve this problem is to define error field as a common Object type
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"id",
"name",
"error"
})
public class MyMessage {
#JsonProperty("id")
private int id;
#JsonProperty("name")
private String name;
#JsonProperty("error")
private Object error;
#JsonProperty("id")
public int getId() {
return id;
}
#JsonProperty("id")
public void setId(int id) {
this.id = id;
}
#JsonProperty("name")
public String getName() {
return name;
}
#JsonProperty("name")
public void setName(String name) {
this.name = name;
}
#JsonProperty("error")
public Object getError() {
return error;
}
#JsonProperty("error")
public void setError(Object error) {
this.error = error;
}
}
And then you have to map it to another DTO:
// someplace where you will map it
MyMessage message = source.getMessageFromQueue();
if(message.getError() == null) {
// do something
} else {
Object error = message.getError();
handleError(error);
}
private void handleError(Object error) {
if(error instanceOf MessageError) {
handleMessageError((MessageError) error) // handle as known MessageError object
} else if(error instanceOf String) {
handleStringError((String) error) // handle as String, write in logs maybe
} else if(error instanceOf List) {
handleErrors((String) error) // handle as List errors
} else {
handleUndefinedError()
}
}

Parse two different API response using Retrofit 2 in Android

I am facing a problem while parsing API response using Retrofit 2.
The API's are already in production and I cannot request a change in API.
Following are two different responses I am getting from server
Success response:
{
"status":0,
"empId":121,
"message":"Data available",
"data":{
"name":"Sam",
"designation": "Software Engineer",
"mob": "1255565456"
}
}
Failure response
{
"status":10,
"empId":121,
"message":"No data available",
"data":""
}
Parsing Classes
class Response{
public int status;
public String message;
public int empId;
public Student data;
}
class Student{
public String name;
public String designation;
public String mob;
}
I am able to parse the success response. But getting the following exception for the failure case.
java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING
try this ,
Object getrow = null;
try {
getrow = this.// your object
LinkedTreeMap<Object, Object> tree = (LinkedTreeMap) getrow;
String name = tree.get(<your body>).toString()
} catch (Exception e) {
e.printStackTrace();
}
The way that you can handle this situation is by treating 'data' as a generic object rather than as a String or 'Student'.
p̶u̶b̶l̶i̶c̶ ̶S̶t̶u̶d̶e̶n̶t̶ ̶d̶a̶t̶a̶;̶
public Object data;
While Using data add a check like this
if(data instanceof String){
String parsedData=data.toString();
}else{
Student parsedData= (Student) data;
}
Make Student as an inner class or Response class. and Retrofit will parse the response and will give you the object.
class Response
{
public int status;
public String message;
public int empId;
public Student data;
Class Data
{
public String name;
public String designation;
public String mob;
}
}

Method always return empty list

I am using mvp Design pattern in android development and i am using retrofit2 with it ... when i run the function to get me information from a the web it get the information but returns null list
the Response.Body come with response that mean that the code works
the model function
List<SearchDdb> searchResult;
private Context context;
public MainModel(Context context){this.context=context;}
public List<SearchDdb> searchUser(String name) {
final MainPresenter presenter = new MainPresenter(context);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://jsonplaceholder.typicode.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
Db db = retrofit.create(Db.class);
Call<List<SearchDdb>> call = db.getUsers(name);
call.enqueue(new Callback<List<SearchDdb>>() {
#Override
public void onResponse(Call<List<SearchDdb>> call, Response<List<SearchDdb>> response) {
if(!response.isSuccessful()){
presenter.showDialog();
}
searchResult = response.body();
}
#Override
public void onFailure(Call<List<SearchDdb>> call, Throwable t) {
}
});
return searchResult;
}
the SearchDdb file
private int id;
private String name;
private String grade;
private String age;
private String address;
public int getId() {
return id;
}
public String getName() {
return name;
}
public String getGrade() {
return grade;
}
public String getAge() {
return age;
}
public String getAddress() {
return address;
}
}
How i call the function
List<SearchDdb> ressult = presenter.searchUser("1");
You cant call it like List<SearchDdb> ressult = presenter.searchUser("1"); because searchUser method makes an async request via retrofit. This way searchUse returns null searchResult because response has not come yet.
Define a LiveData in your viewmodel class and observe it in your activity/fragment. When response comes from api in
onResponse in searchUser post that response to live data and use it where you observe.
If you want to see a tutorial you can look at this article.
Here is another example.

Android Java Spring JSON response has $ in key. How to parse?

I'm new to Java. I'm using Spring to consume a REST api that outputs JSON. With the tutorials on the Spring website I can easily have the JSON response converted to an object of my desired class. The problem is now that one of the keys in the JSON response is $id. I cannot make a variable with a dollar sign in it. I assume I should define some configuration somewhere that such a name would be converted into something acceptable. I don't know how.
My Rest request code:
protected LoginResult doInBackground(Void... params) {
try {
Log.i(TAG, "Making Login request");
//TODO: Make this a setting
final String url = "https://someurl.com/api/login";
LoginCredentials login = new LoginCredentials("foo#bar.com", "qwerty123");
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
LoginResult result = restTemplate.postForObject(url, login, LoginResult.class);
Log.d(TAG, "Got the LoginResult.");
return result;
} catch (Exception e) {
//TODO: Exception handling
Log.e(TAG, e.getMessage(), e);
}
return null;
}
The resulting JSON looks something like this:
{
"_id":{
"$id":"98765432"
},
"name":"Person Guy",
"email":"foo#bar.com",
"roles":[
"user"
],
"active":true,
"created":{
"sec":1439117849,
"usec":856000
},
"session":{
"token":"12345678",
"user_id":"98765432",
"created":{
"sec":1439134272,
"usec":0
},
"last_extended":{
"sec":1439134272,
"usec":0
},
"expires":{
"sec":1439998272,
"usec":0
}
}
}
The $id part is where things get difficult. The LoginResult class looks like this:
#JsonIgnoreProperties(ignoreUnknown = true)
public class LoginResult {
private String name;
private String email;
private MongoId _id;
/* Getters and setters */
}
The MongoId class looks like this (The JsonIgnoreProperties is now added to avoid exceptions):
#JsonIgnoreProperties(ignoreUnknown = true)
public class MongoId {
private String id; //This is $id in the JSON response.
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
Any help would be largely appreciated.
You can use the #JsonProperty("$id") annotation in MongoId to tell how the JSON is mapped to your Java object:
#JsonIgnoreProperties(ignoreUnknown = true)
public class MongoId {
#JsonProperty("$id")
private String id; //This is $id in the JSON response.
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
Here is a quick overview for reference.

Retrofit Android Application GET JSON from Node.js Server

I solved the problem.
I was sending plain-text in the server.
Changed the line to read:
response.writeHead(200, {'Content-Type': 'application/json'});
Quick question. My friend recommended looking into Retrofit rather than using my ASync task for my REST android application. I am having one small problem since I am new to the system. The server runs using Node.js to send JSON objects by means of .stringify() and so when I retrieve an object it is a String rather than a JSON, and I cannot convert it to my List of the appropriate objects. I am guessing the error is due to a cast exception because it is sending a string because I get the exception:
com.google.gson.JsonSyntaxException
Any help would be great. Here is the code I thought would be appropriate.
Android Client Code Example below:
private void requestData() {
RestAdapter adapter = new RestAdapter.Builder().setEndpoint(ENDPOINT).build();
UnitAPI unit_api = adapter.create(UnitAPI.class);
unit_api.getUnits(new Callback<List<Unit>>() {
#Override
public void success(List<Unit> t, Response response) {
units = t;
updateDisplay();
}
#Override
public void failure(RetrofitError error) {
Log.d("Failure UnitGet()", error.getMessage());
}
});
}
Android Unit.class:
public class Unit {
private String ID;
private long bearing;
private long lat;
private long lng;
private String type;
public String getID() {
return ID;
}
public void setID(String ID){
this.ID = ID;
}
public long getBearing() {
return bearing;
}
public void setBearing(long bearing){
this.bearing = bearing;
}
public void setLat(long lat){
this.lat = lat;
}
public long getLat(){
return lat;
}
public void setLng(long lng){
this.lng = lng;
}
public long getLng(){
return lng;
}
public void setType(String type){
this.type = type;
}
public String getType(){
return type;
}
}
The server sends the .json file via the command:
if (method == "GET") {
response.write(JSON.stringify(unitsJSON));
}
Any thoughts on how I can convert it to a list of units from the string on the client side or a json object on the server side?
Thanks!
I solved the problem. I was sending plain-text in the server. Changed the line to read:
response.writeHead(200, {'Content-Type': 'application/json'});
I think you must add json-parser to package using npm
then
in response write
res.end(JSON.stringfy('your resp0onse here'));

Categories