deserialize json with gson - android

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 :(

Related

Retrofit - Get Raw non JSON Array

I am using Retrofit2 for the first time and have a problem to get a simple Array in non JSON format.
Error: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 3 path $[0]
This means its not an JSON Object since it does not start with "{"
I tried adding the ScalarsConverter but it doesent seems to work.
Api: https://chasing-coins.com/api/v1/coins
Interface:
public interface Retro_coins {
#GET("api/v1/coins")
Call<List<Coinlist>> getCoinlist();
}
Class:
public class Coinlist {
private List coinlist;
public List getCoinlist() {
return coinlist;
}
}
Retrofit initialization and call:
String API_BASE_URL = "https://chasing-coins.com/";
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addConverterFactory(ScalarsConverterFactory.create())
;
Retrofit retrofit = builder.client(httpClient.build()).build();
Retro_coins client = retrofit.create(Retro_coins.class);
// Fetch list
Call<List<Coinlist>> call =
client.getCoinlist();
// Execute the call asynchronously. Get a positive or negative callback.
call.enqueue(new Callback<List<Coinlist>>() {
#Override
public void onResponse(Call<List<Coinlist>> call, Response<List<Coinlist>> response) {
// The network call was a success and we got a response
Log.w("Yes", response.toString());
}
#Override
public void onFailure(Call<List<Coinlist>> call, Throwable t) {
Log.w("no", t.toString());
}
});
Thanks!
When you are using private List coinlist;, Gson expects the object to be
{
"coinlist":"[]"
}
where as what you are providing is just
["String","String","String"]
furthermore when you use Call<List<Coinlist>> you are expecting the data to be
[
{
"coinlist":"[]"
}
]
Just change your call from Call<List<Coinlist>> to Call<List<String>>. That should fix your problem. Let me know if you need more clarification
Your request Returning String. So you need to Change the Response to String or Need to change your request Call to String.

Multiple converters for a json response with retrofit

I'm trying to create a dynamic response based converter
using retrofit, As for now I have 2 different answers returning from the server - one represents a failure and one represent a valid response How can I try and parse two different objects using the same adapter\callabck?
You can parse it as a java bean if data are json data.
You can use Gson to parse it.
1 Add lib
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.google.code.gson:gson:2.7'
2 Create Retrofit
private Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Api.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
Just add a Gson converter.
For example,
// success
{"retcode":0,"result":{"vfwebqq":"xxxx"}}
// failed
{"retcode":100,"result":{}}
3 Create a bean to receive data.
public class Result {
public String retcode;
public Info result;
public static class Info {
public String vfwebqq;
}
}
4 Then you can return a bean object in retrofit interface.
#GET("xxx")
Result getHome();
Actually I'm not quite in what are you talking about and what exact issue you are facing. But the first thing that pops out of my head is just to provide custom JsonDeserializer. It should look like smth like this :
public class CustomDeserializer implements JsonDeserializer<List<CustomData>> {
#Override
public List<CustomData> deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
List<CustomData> customDataSet = new ArrayList<>();
Iterator<JsonElement> iterator = ((JsonObject) json).get("data").getAsJsonObject().get(
"records").getAsJsonArray().iterator();
while (iterator.hasNext()) {
JsonElement element = iterator.next();
CustomData customData = ServiceGenerator.mGson.fromJson(element, CustomData.class);
customDataSet.add(customData);
}
return customDataSet;
}
}
That's just a custom parser class example which is applied to RetrofitBuilder just to make life easier(maybe).
Afterwards you need to :
Type listType = new TypeToken<List<CustomData>>() {
}.getType();
mGson = new GsonBuilder().registerTypeAdapter(listType, new CustomDeserializer()).create();
builder =
new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(mGson))
.baseUrl(API_BASE_URL);
retrofit = builder.build();
Based on your question, i believe this site helps with your challenge:
https://futurestud.io/tutorials/retrofit-2-introduction-to-multiple-converters

How to preparse JSON results in Retrofit2 (before sending to GsonConverterFactory)

I am trying to consume a JSON using retrofit2 and GSON.
The following is the response provided by the server. Note that the value of "d" is a string of a valid JSON (once the slashes are removed).
{"d": "[{\"Number\":\"2121\",\"NumberOfAppearances\":2,\"Prizes\":
[{\"DrawDate\":\"\/Date(1439654400000)\/\",\"PrizeCode\":\"S\"},
{\"DrawDate\":\"\/Date(874771200000)\/\",\"PrizeCode\":\"S\"}]}]"}
Is there a way to use retrofit2 to preparse the the json during the call to retrofitService to get the objects inside the value of d?
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
//is there anything i can do here to do preparsing of the results?
.addConverterFactory(GsonConverterFactory.create())
.build();
IQueryLocations = retrofit.create(IMyQuery.class);
//currently GsonResults is the string value of d, instead of the JSON objects
Call<GsonResult> result = IMyQuery.doQuery("2121");
Ideally, I like to insert a method call before addConverterFactory to do the preparsing
the output of the preparsing method would be some thing like the following:
{"d": [{"Number":"2121","NumberOfAppearances":2,"Prizes":
[{"DrawDate": 1439654400000,"PrizeCode":"S"},
{"DrawDate": 874771200000,"PrizeCode":"S"}]}]}
It's not your ideal solution, but you can return a wrapper for the result data:
class WrappedGsonResult {
private static final Gson GSON = new Gson();
#SerializedName("d")
private String data;
GsonResult() {}
public GsonResult getData() {
return GSON.fromJson(this.data, GsonResult.class);
}
}
Then:
Call<WrappedGsonResult> result = IMyQuery.doQuery("2121");
result.enqueue(new Callback() {
#Override
public void onResponse(final Call<WrappedGsonResult> call, final Response<WrappedGsonResult> response) {
if (response.isSuccessful()) {
GsonResult result = response.body().getData();
// ...
} else {
// ...
}
}
// ...
});
To exclude double quotes, you need to use excludeFieldsWithoutExposeAnnotation() provided by GsonBuilder.
For example:
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
// Add Gson object
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
Hope this helps.

Retrofit encoding special characters

I am using retrofit with gson instead of android since its faster and more secure.
The problem is that retrofit is encoding special characters like = and ?, and the url I'm using cannot decode these characters.
This is my code:
api class:
public interface placeApi {
#GET("/{id}")
public void getFeed(#Path("id") TypedString id, Callback<PlaceModel> response);
}
Main class:
String url = "http://api.beirut.com/BeirutProfile.php?";
String next = "profileid=111";
//Creating adapter for retrofit with base url
RestAdapter restAdapter = new RestAdapter.Builder().setEndpoint(url).setRequestInterceptor(requestInterceptor).build();
//Creating service for the adapter
placeApi placeApi = restAdapter.create(placeApi.class);
placeApi.getFeed(id, new Callback<PlaceModel>() {
#Override
public void success(PlaceModel place, Response response) {
// System.out.println();
System.out.println(response.getUrl());
name.setText("Name: " + place.getName());
}
#Override
public void failure(RetrofitError error) {
System.out.println(error.getMessage());
}
});
I tried solving the problem using this gson method but it didn't work, most probably because it only includes only the first part of the url and not the one I am sending to the placeApi interface:
Gson gson = new GsonBuilder().disableHtmlEscaping().create();
and added this when creating the restadapter:
RestAdapter restAdapter = new RestAdapter.Builder().setEndpoint(url).setRequestInterceptor(requestInterceptor).setConverter(new GsonConverter(gson)).setConverter(new GsonConverter(gson)).build();
Any help please?
You must use Use #EncodedPath. like this:
public interface placeApi {
#GET("/{id}")
public void getFeed(#EncodedPath("id") TypedString id,
Callback<PlaceModel> response);
}
Note: The above works but now I am looking at the doc and it seems that the #EncodedPath is deprecated so use #PATH with its parameter instead:
public interface placeApi {
#GET("/{id}")
public void getFeed(#Path("id", encode=false) TypedString id,
Callback<PlaceModel> response);
}
ref: https://square.github.io/retrofit/2.x/retrofit/

Retrofit response not being entirely parsed

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;
...

Categories

Resources