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.
Related
I am calling a REST service (not mine) using retrofit which either returns a list of objects (if there are multiple) or a single object (if one). I was able to find a similar issue here however the suggestion is to change the API which i don't have control of. I also read this thread which seems to be a good approach but is there a way to handle this using Retrofit?
While the answer from #pirho seems to be applicable, I found out a different and simple solution which worked for me. Hopefully it may help others as well.
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(JacksonConverterFactory.create(mapper))
.client(okHttpClient)
.build();
You can get the API response data as Map<String, JsonElement> in response and then parse it based on your requirement directly. As you can check here if JsonElement is JsonArray
for ex:
public fun parseData(val jsonElement:JsonElement){
val gson = Gson()
if(jsonElementFromServer.isJsonArray()){
//here you can just parse it into some list of array
}else{
//here you can parse using gson to single item element or model
}
}
JsonElement ref
Using Gson to get list of items or single model
As the author of the 2nd post you referred I also refer to the implementation of PostArrayOrSingleDeserializer described in that answer of mine.
When using Gson with Retrofit (Retrofit's converter-gson) you just need to register the adapter with custom Gson instance and build the Retrofit instance with that Gson instance, see below example helper class:
public class MyRetrofit {
public static MyAPI getMyApi() {
Gson gson = new GsonBuilder()
.registerTypeAdapter(Object.class,
new ObjectArrayOrSingleDeserializer())
.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://example.org")
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
return retrofit.create(MyAPI.class);
}
}
So the Object in the JsonDeserializer named ObjectArrayOrSingleDeserializer is the DTO you need to check for single instance or array. Replace Object with corresponding DTO and modify deserializer accordingly.
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
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.
I'm using Retrofit2.0 for making GET request to my REST URL. I don't need to pass any params to url for making the request.
How could on can make this type of request?
Here is my code what i 've done!
Interface ::
public interface AllRolesAPI {
#GET("/SportsApp/allroles")
Call<AllRolesParams> getAllRoles();
}
Class :::
I created a class using Pojo library it contains all the variables with setter and getter methods.
public void requestRoles() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(ENDPOINT)
.build();
AllRolesAPI allRolesParams = retrofit.create(AllRolesAPI.class);
Call<AllRolesParams> allRolesParamsCall = allRolesParams.getAllRoles();
allRolesParamsCall.enqueue(new Callback<AllRolesParams>() {
#Override
public void onResponse(Call<AllRolesParams> call, Response<AllRolesParams> response) {
//response.body().getErrDesc();
Log.v("SignupActivity", "Response :: " + response.body().getErrDesc());
}
#Override
public void onFailure(Call<AllRolesParams> call, Throwable t) {
Log.v("SignupActivity", "Failure :: ");
}
});
}
When I create a request like above I have got this error in console ::
java.lang.IllegalArgumentException: Unable to create converter for class com.acknotech.kiran.navigationdrawer.AllRolesParams.
If your API's responses are JSON, you need to add
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(ENDPOINT)
.addConverterFactory(GsonConverterFactory.create())
.build();
In order to be able to use GsonConverterFactory, you need to add a gradle dependency. Check this. In your case is
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
(2.1.0 is the latest version at the time of this writing)
Quoting official docs:
By default, Retrofit can only deserialize HTTP bodies into OkHttp's
ResponseBody type and it can only accept its RequestBody type for
#Body. Converters can be added to support other types. Six sibling
modules adapt popular serialization libraries for your convenience.
Gson: com.squareup.retrofit2:converter-gson
Jackson:com.squareup.retrofit2:converter-jackson
Moshi:com.squareup.retrofit2:converter-moshi
Protobuf:com.squareup.retrofit2:converter-protobuf
Wire:com.squareup.retrofit2:converter-wire
Simple XML:com.squareup.retrofit2:converter-simplexml
Scalars (primitives, boxed,and String): com.squareup.retrofit2:converter-scalars
You are trying to parse JSON without any converter. There are various converts you can use with Retrofit. Most Popular is Gson Converter from Google. To make your code work create Retrofit adapter like this:
adapter = new Retrofit.Builder() //in your case replace adapter with Retrofit retrofit
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
Also make sure to include these dependencies:
compile 'com.google.code.gson:gson:2.6.2'
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
Hope it works.You can refer to official retrofit docs,this guide and gson guide for more information.
I am using Retrofit library (version 2.0.2 as of this writing).
I am making a GET call to a service which responds a big JSON object but I am only interested in one key:value pair in it.
How can I get just that instead of writing a whole new POJO class that matches the JSON response?
Example -
{
status_code: 34,
status_message: "The resource you requested could not be found.",
...,
...
}
I need only status code value (34 here).
Please note, I am just giving an example of this JSON object here. The real one I am dealing with is huge and I care about only one key:value pair in it.
Thanks in advance.
You can refer to the following:
#GET("/files/jsonsample.json")
Call<JsonObject> readJsonFromFileUri();
and
class MyStatus{
int status_code;
}
...
Retrofit retrofit2 = new Retrofit.Builder()
.baseUrl("http://...")
.addConverterFactory(GsonConverterFactory.create())
.build();
WebAPIService apiService = retrofit2.create(WebAPIService.class);
Call<JsonObject> jsonCall = apiService.readJsonFromFileUri();
jsonCall.enqueue(new Callback<JsonObject>() {
#Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
String jsonString = response.body().toString();
Gson gson = new Gson();
MyStatus status = gson.fromJson(jsonString, MyStatus.class);
Log.i(LOG_TAG, String.valueOf(status.status_code));
}
#Override
public void onFailure(Call<JsonObject> call, Throwable t) {
Log.e(LOG_TAG, t.toString());
}
});
...
Debug screenshot