I have an Android application that uses a RestFUL web service as its backend. I already implemented Retrofit for sending registration data and it works.
Now I need to get a big load of objects, but it's not working yet. I noticed Gson only parses a few attributes (name, type, location), but most of them were not (specially id).
How does Gson actually parse it? Do I actually have to implement it? From what I thought I just needed the name of the attributes to match and it would do all the work for me.
This is how I am building my Gson and RestAdapter:
Gson gson = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.registerTypeAdapter(Date.class, new DateTypeAdapter())
.create();
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(PACURL.getHost())
// .setLogLevel(RestAdapter.LogLevel.FULL)
.setConverter(new GsonConverter(gson))
.build();
My request definition:
#POST("/pois/{category}")
void getPois(#Path("category") String category,
#Query("lat") Double lat,
#Query("long") Double lon,
Callback<WrapperPois> callback);
And the actual call:
NetworkInterface nt = restAdapter.create(NetworkInterface.class);
nt.getPois(category,
location.getLatitude(),
location.getLongitude(), callback);
I used Retrofit expecting to ease my work, so please let me know if I have to actually write the deserialization myself. Any comments on how Gson default converter works is very appreciated.
Thanks!
Edit:
My WrapperPois class:
public class WrapperPois {
public ArrayList<Poi> results;
static class MyResponse {
public ArrayList<Poi> array;
}
}
And the Poi contains:
protected String objectId;
protected String url;
protected String type;
...
Related
There is an issue in the retrofit library that I am facing in the dynamic data JSON response.
For example, if any key in the JSON is of int type and due to some reason the particular key type is changed to string. Now corresponding to the first JSON response when the key was of int type the model class was created. Now my question is I want to handle the updated string format JSON. How should I solve this task?
Below is the ApiClient class of the retrofit.
public class ApiClient
{
public static final String BASE_URL = "http:***********";
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;
}
}
I have got some solutions which use the rest adapter but I want to solve using the above format. How I should do please guide.
I want to get the json file containing the elevation of a given latitude and longitude (and also providing the api key). I have been reading a lot and find out that Retrofit is the best option in Android, but I don't know how to specify the params.
public interface ServiceApi {
//url format
//"https://maps.googleapis.com/maps/api/elevation/json?locations="
// latitude+","+longitude+"&key="+key;
#GET("")
public void getJSON(Callback<List<JsonElevation>> jsonElevationCallback);
}
I don't have an Elevations API key to test this with but using Retrofit's documentation, something like this should work.
#GET("/maps/api/elevation/json")
public Call<List<JsonElevation>> getJSON(#Query("locations") String latAndLng,
#Query("key") String key);
Then to call it:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://maps.googleapis.com/")
.addConverterFactory(GsonConverterFactory.create(new Gson()))
.build();
ServiceApi serviceApi = retrofit.create(ServiceApi.class);
String latAndLng = String.format("%f,%f", latitude, longitude);
Call<List<JsonElevation>> elevations = serviceApi.getJSON(latAndLng, key);
elevations.enqueue(); // for asychronous response
//or
elevations.execute(); // for synchronous response
I have dynamic JSON, here is example: http://pastebin.com/QMWRZTrD
How I can parse it with Retrofit?
I failed to generate POJO classes, since I have dynamic fields like "5411" and "5412".
EDIT:
I solved it by using Map, since first value is always integer, and second is list of objects.
#FormUrlEncoded
#POST("history.php")
Observable<Map<Integer, List<Vehicle>>> getHistory(#Field("uredjaji") String vehicleId, #Field("startDate") String startDATE, #Field("endDate")
you can use Map to serialize and deserialize it in case of Random keys.
Observable<Map<Integer, List<YourObject>>>
You can get retrofit api call to return String in your RestApi Interface like
Call<String> method(#Path(..)...);
And for that to work you would need to add the scalars converter factory to where you create your Retrofit object.
First you would need to import it:
compile 'com.squareup.retrofit2:converter-scalars:2.1.0'
And then add it:
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("https://your.base.url/")
.build();
And then in onResponse
public void onResponse(Call<String> call, Response<String> response) {
if (response.isSuccessful()) {
Type mapType = new TypeToken<Map<String,List<SomeClass>>() {}.getType(); // define generic type
Map<String,List<SomeClass>> result= gson.fromJson(response.body(), mapType);
} else {
}
}
Also,check out this site it has great tutorials on Retrofit.
I'm trying to deserialize json from worldbank.com to a pojo without any success. The json looks like:
[{"page":1,"pages":7,"per_page":"50","total":304},[{"id":"ABW","iso2Code":"AW","name":"Aruba","region":{"id":"LCN","value":"Latin America & Caribbean "},
and can be found via: http://api.worldbank.org/countries/?format=json
and im running into problems with gson telling me:
WorldBankDemo: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 52 path $[1]
Any clues as to how i can solve this? Preferably without changing from gson since that is the lib used by the networking lib I'm using (retrofit)
WorldBankDataService service = ServiceFactory.createRetrofitService(WorldBankDataService.class, WorldBankDataService.SERVICE_ENDPOINT);
service.getCountries()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<WorldBank[]>() {
#Override
public final void onCompleted() {
// do nothing
}
#Override
public final void onError(Throwable e) {
Log.e("WorldBankDemo", e.getMessage());
}
#Override
public final void onNext(WorldBank[] response) {
Log.d("TAG", "resp: "+response);
//mCardAdapter.addData(response);
}
});
public class ServiceFactory {
/**
* Creates a retrofit service from an arbitrary class (clazz)
* #param clazz Java interface of the retrofit service
* #param endPoint REST endpoint url
* #return retrofit service with defined endpoint
*/
public static <T> T createRetrofitService(final Class<T> clazz, final
String endPoint) {
final RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(endPoint)
.setLogLevel(RestAdapter.LogLevel.FULL)
.build();
T service = restAdapter.create(clazz);
return service;
}
}
public class WorldBank {
int page;
int pages;
String per_page;
int total;
//Country[] countrys;
}
JSON is not constructed well(especially for auto parsing), Array can contain objects or arrays but not both at same level, in the above JSON structure it starts with Array in that the first element is an object and second element is an array, so this kind of JSON structure is not recommended for auto parsing, if at all you want to continue with same JSON response you can go for manual parsing or change response structure.
It's actually a JSON array. so you can't use class. try this:
YourPojo[] objects = gson.fromJson(jsonString, YourPojo[].class)
works like a charm
try this way
Gson gson = new Gson();
String jsonOutput = "Your JSON String";
Type listType = new TypeToken<List<ApiResponse>>(){}.getType();
List<ApiResponse> posts = (List<ApiResponse>) gson.fromJson(jsonOutput, listType);
and ApiResponse is like
public class ApiResponse{
WorldBank object1;
ArrayList<Country> objects2;
}
I haven't try this on my end, but it will be similar like that.
You can use gson to customize using this dependency
compile 'org.immutables:gson:2.3.1'
But slightly different way while invoking the rest client
For instance .If we have to get a list of countries declare an interface
public interface GetAllAPI {
#GET("/all")
List<Country> getCountries();
}
Now rest client will be
public List<Country> GetAllCountries() {
Gson gson = new GsonBuilder().create();
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(service_url)
.setConverter(new GsonConverter(gson))
.build();
GetAllAPI service = restAdapter.create(GetAllAPI.class);
List<Country> countrylist = service.getCountries();
return countrylist;
}
Getting the results from API will be
List<Country> countrylist = service.getCountries();
You have to customize this implementation for specific requirement. This is an idea how to implement Gson with Retrofit
Go through this for more clarification
Decided to give up and use another api, the world bank api just sucks :(
I have a WCF Service that returns a List of objects to an Android app. One of the properties is a DateTime property. The return format is JSON and I am getting the date in this format /Date(1441117247253+0200)/ on the Android side. I am using com.squareup.retrofit:retrofit:1.9.0 to get the data from my service.
I have no clue how to use Retrofit to create a the string date into a Date object. I had a look at this http://square.github.io/retrofit/ under the Custom Converters section, but do not know how to go further. This is what I've tried but I do not know how to implement the converter class.
creating the restAdapter like so:
restAdapter = new RestAdapter.Builder()
.setConverter(new DotNetDateConverter())
.setLogLevel(RestAdapter.LogLevel.FULL)
.setEndpoint(API).build();
and the DotNetDateConverter Class which I do not know how to implement further:
public class DotNetDateConverter implements Converter
{
#Override
public Object fromBody(TypedInput body, Type type) throws ConversionException
{
return null;
}
#Override
public TypedOutput toBody(Object object)
{
return null;
}
}
there are other fields in the response which are fine, but how do I change the /Date(1441117247253+0200)/ to a proper java.util.Date object? Without the converter I get com.google.gson.JsonSyntaxException:/Date(1441117247253+0200)/ obviously because the string cannot be converter to a date.
Any assistance would greatly be appreciated.
Ok, so after some digging around, I came across this. My approach was wrong. So I ended up creating a Converter like this:
public class DotNetDateConverter implements JsonDeserializer<Date>
{
#Override
public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
{
String s = json.getAsJsonPrimitive().getAsString();
long l = Long.parseLong(s.substring(s.indexOf("(")+1, s.indexOf("+")));
Date d = new Date(l);
return d;
}
}
I also had to register it like so:
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Date.class, new DotNetDateConverter());
restAdapter = new RestAdapter.Builder()
.setConverter(new GsonConverter(gsonBuilder.create()))
.setLogLevel(RestAdapter.LogLevel.FULL)
.setEndpoint(API).build();
I had to change the code a bit to accommodate my specific scenario, that being dates come in as /Date(1441117247253+0200)/ with the time zone. Maybe someone else might find some use for this...