I make calls to an api that returns the following structure:
{
page: 1,
results: [
{
poster_path: "/6FxOPJ9Ysilpq0IgkrMJ7PubFhq.jpg",
adult: false,
overview: "Tarzan, having acclimated to life in London, is called back to his former home in the jungle to investigate the activities at a mining encampment.",
release_date: "2016-06-29",
genre_ids: [
28,
12
],
id: 258489,
original_title: "The Legend of Tarzan",
original_language: "en",
title: "The Legend of Tarzan",
backdrop_path: "/75GFqrnHMKqkcNZ2wWefWXfqtMV.jpg",
popularity: 27.931248,
vote_count: 655,
video: false,
vote_average: 4.6
},
... MORE MOVIES ...
}
The part that I am interested in is JUST the array of movie objects.
My movie class has the following method:
public static TypeAdapter<Movie> typeAdapter(Gson gson) {
return new AutoValue_Movie.GsonTypeAdapter(gson);
}
And this is how I build the Gson object that I pass to my retrofit instance:
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(new AutoValueGsonTypeAdapterFactory())
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
.create();
I use the following method in my DataManager to make the api call:
public Observable<Movie> syncMovies(int page) {
return mMovieService.getPlayingMovies(page)
.concatMap(new Func1<List<Movie>, Observable<Movie>>() {
#Override
public Observable<Movie> call(List<Movie> movies) {
return mDatabaseHelper.setMovies(movies);
}
});
}
Get playing movies returns an Observable< List< Movie > >. The problem is that I get an error message saying: Expected BEGIN_ARRAY but was BEGIN_OBJECT.
I think that the problem is that my JSON is really an object. How can I just extract the array and parse it into a List< Movie >?
Sorry for the ignorance but I am completely new to Gson.
Related
I am parsing the below sample json using retrofit in android:
{
"success": true,
"timestamp": 1664080564,
"base": "EUR",
"date": "2022-09-25",
"rates": {
"AED": 3.559105,
"AFN": 86.151217,
"ALL": 116.321643,
"AMD": 404.265711
}
}
As you can see there is no array in this json data, but I want the values of rates as a list or a map so that I can get "AED, AFN, ALL, AMD" as an array too. How can i achieve that using retrofit?
You can define rates as a Map<String, Double> in your data class and Retrofit will automatically parse the rates in form of a Map.
data class MyModel(
val success: Boolean,
val timestamp: Long,
val base: String,
val date: String,
val rates: Map<String, Double>
)
so that I can get "AED, AFN, ALL, AMD" as an array too.
For this you can simply use rates.keys to get all the keys.
I'm pretty new to Kotlin so here I go.
I have some JSON data that sometimes returns an object or it can return an array. See example:
Object:
"planograms": {
"planogram": {
"multiple-facings": 1,
"planogram-seq": 838,
"planogram-id": 252871,
"planogram-desc": "Bulk Hardware - No Springs"
}
},
Array:
"planograms": {
"planogram": [
{
"multiple-facings": 1,
"planogram-seq": 112,
"planogram-id": 262027,
"planogram-desc": "Cat Food - Cat Dry"
},
{
"multiple-facings": 1,
"planogram-seq": 94,
"planogram-id": 276508,
"planogram-desc": "Cat Food - Cat Dry - A"
}
]
},
I am using Retrofit2 for the parsing so here is the code that does that:
fun getItemInfoExtended(#Body itemId: ItemData): Call<DefaultResponse>
DefaultResponse has more data that includes the object/array.
My question is how can I get to return the object or array. currently, I have a list in my data class, so when the object is returned I get an error stating the issue of not an array:
data class Planograms (
val planogram: List<Planogram>?
)
data class Planogram (
val multipleFacings: Long,
val planogramSeq: Long,
val planogramID: Long,
val planogramDesc: String
)
Thanks in advance for your input.
I am accessing a API that has geometries object types in an JSON Object. The issue I have is in my Kotlin model class (below), I cannot have two Object Types for the node, as I get hit with the error
java.lang.IllegalStateException: Expected a double but was BEGIN_ARRAY at line 12 column 16 path $.features[0].geometry.geometries[0].coordinates[0];
because for the each inner node coordinates ofeach object, it can either return:
List<Double>
List<List<Double>>
API JSON
"geometries": [
{
"type": "Point",
"coordinates": [153.0533903,-26.7735391]
},
{
"type": "LineString",
"coordinates": [
[153.0258962, -27.3399828],
[153.02596, -27.34007],
[153.02602, -27.34015],
[153.026028, -27.3401745]
]
}
]
...
...
...
The external API response is passed through Retrofit models, however in my model class how do provide both List<Double> & List<List<Double>> processing
public class EventsGeometriesAPINodeModel {
#SerializedName("type")
#Expose
internal var type: String? = null
//how do provide both List<Double> List<List<Double>> processing
//API retruns either:
//List<Double>
//List<List<Double>>
#SerializedName("coordinates")
#Expose
internal var coordinates: List<Double>? = null
}
You have to make coordinates variable dynamic like JsonArray and whenever you receive data then you have to check type of that variable.
val listCoordinates = jsonObject.coordinates
if(listCoordinates is List<Double>){
} else if(listCoordinates is List<List<Double>>){
}
I'm trying to parse a JSON response using GSON after a Retrofit GET request. I don't need all the keys and values so I only #Expose the ones that I need and instructed the parser to do so. The request fires OK and the response come clean, but looking into logcat I found this error which evidently points me that POJO model is bad formatted or implemented:
04-09 12:16:01.679 5604-5604/? V/Retrofit error﹕ retrofit.converter.ConversionException: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $
This is the JSON response (which returns ok after GET request):
{
objects: [
{
rating: 0.97,
name: "High Line",
ranking: 0,
url: "http://www.thehighline.org",
price: null,
phone: "2125006035",
last_sync: 1428328869,
photos: [
"https://irs3.4sqi.net/img/general/original/11402168_2zKtnTfWXPJJJAaX7N6g1EMPTR7ahNqSAOsMotN-jNU.jpg"
],
local_id: 13778,
likes: 0,
city_id: 2621,
address: "btwn Gansevoort & W 34th St",
resource_uri: "/api/v1/venues/40f1d480f964a5206a0a1fe3/",
id: "40f1d480f964a5206a0a1fe3",
categories: [
{
name: "Park",
parent: {
local_id: 7,
name_id: "sights",
name: "Landmarks",
id: "4d4b7105d754a06377d81259"
},
local_id: 494,
name_id: "park",
category_id: 7,
id: "4bf58dd8d48988d163941735"
}
],
location: {
lat: 40.7470618874989,
lng: -74.0051937103271
}
},
{
rating: 0.97,
name: "Central Park",
ranking: 0,
url: "http://www.centralparknyc.org",
price: null,
phone: "2123106600",
last_sync: 1428521923,
photos: [
"https://irs2.4sqi.net/img/general/original/655018_Zp3vA90Sy4IIDApvfAo5KnDItoV0uEDZeST7bWT-qzk.jpg"
],
local_id: 13826,
likes: 0,
city_id: 2621,
address: "59th St to 110th St",
resource_uri: "/api/v1/venues/412d2800f964a520df0c1fe3/",
id: "412d2800f964a520df0c1fe3",
categories: [
{
name: "Park",
parent: {
local_id: 7,
name_id: "sights",
name: "Landmarks",
id: "4d4b7105d754a06377d81259"
},
local_id: 494,
name_id: "park",
category_id: 7,
id: "4bf58dd8d48988d163941735"
}
],
location: {
lat: 40.7888599444948,
lng: -73.9611625671387
}
}
],
meta: {
total_count: 1344,
next: "/api/v1/venues/?city_id=2621&category=topPicks&offset=2&limit=2&format=json",
limit: 2,
offset: 0
}
}
This is the main activity call to the Retrofit service:
Map<String, String> params = new HashMap<String, String>();
params.put("city_id", "2621");
params.put("offset", "0");
params.put("limit", "2");
params.put("category", "topPicks");
params.put("format", "json");
ApiClient.getApiClient().listVenues(params, new Callback<List<ApiResponse>>() {
#Override
public void success(List<ApiResponse> venues, Response response) {
//consumir venues
Log.v("RETROFIT SUCCESS", response.getBody().toString());
mAdapter = new MainCustomAdapter(venues);
mRecyclerView.setAdapter(mAdapter);
}
#Override
public void failure(RetrofitError retrofitError) {
if (retrofitError.getResponse() != null) {
Log.v("Retrofit error", retrofitError.getCause().toString());
}
//manejar el fallo
}
});
This is the Api client:
public class ApiClient {
private static ApiVenuesInterface apiVenues;
public static ApiVenuesInterface getApiClient() {
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
if (apiVenues == null) {
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("http://endpoint.com")
.setConverter(new GsonConverter(gson))
.setLogLevel(RestAdapter.LogLevel.FULL).setLog(new AndroidLog("RETROFIT"))
.build();
apiVenues = restAdapter.create(ApiVenuesInterface.class);
}
return apiVenues;
}
public interface ApiVenuesInterface {
//llamada asíncrona al querystring de venues
#GET("/api/v1/venues")
void listVenues(#QueryMap Map<String, String> params, Callback<List<ApiResponse>> callback);
}}
And finally this my POJO model (which I believe is the place where the main problem is):
public class ApiResponse {
private List<Object> objects = new ArrayList<Object>();
}
class Object {
#Expose
private String name;
#Expose
private List<String> photos = new ArrayList<String>();
#Expose
private String address;
#Expose
private List<Category> categories = new ArrayList<Category>();
/**
*
* #return
* The name
*/
public String getName() {
return name;
}
/**
*
* #return
* The photos
*/
public List<String> getPhotos() {
return photos;
}
/**
*
* #return
* The address
*/
public String getAddress() {
return address;
}
/**
*
* #return
* The categories
*/
public List<Category> getCategories() {
return categories;
}
class Category {
#Expose
private String name;
/**
*
* #return
* The name
*/
public String getName() {
return name;
}
}}
So, how did I must to model my POJO to parse the data I need? Thanks in advance.
IMPORTANT EDIT: This question is valid for Retrofit 1.x. Be aware that Retrofit 2.x is a little bit different than this because it uses annotations and Call methods.
As per earlier comment:
Based on a quick look, your Callback return type isn't of type List<ApiResponse>, but rather just ApiResponse. If you change the following, things should start working:
public interface ApiVenuesInterface {
//llamada asíncrona al querystring de venues
#GET("/api/v1/venues")
void listVenues(#QueryMap Map<String, String> params, Callback<ApiResponse> callback);
}
At the moment, you're telling Gson to convert the response into a list of ApiResponse objects, but in reality it's just a single ApiResponse wrapping a list/array of items. You modelled the POJOs correctly, but not so much the retrofit callback.
[edit] answering your follow up question: you can access the wrapped objects by simply adding a getter to your ApiResponse class:
public List<Object> getObjects() {
return objects;
}
A small tip: it would be good to come up with a different name for your Object class, since its name is identical to the 'mother' of all objects in Java: java.lang.Object. This is bound to lead to confusion and very prone to importing/referencing errors. Try to come up with something a little more descriptive, for example Venue (as it appears that's what you're dealing with, although I may be mistaken).
[edit2] Gson produces an ApiResponse object, which wraps around a List<Object>. Iterating over that list can be done like any other Java iteration; i.e. using the enhanced for-loop:
for (Object object : getObjects()) {
// get the name for this object
String name = object.getName();
// get the address for this object
String address = object.getAddress();
// get all the photo urls for this object
List<String> photos = object.getPhotos();
// etc...
}
The reason you're seeing i.e. myjavapackage.Object#3c4a86e1 is because Java doesn't know how to represent your objects as a string for printing. You can change this representation by overriding the following method in your Object class:
#Override public String toString() {
// return a string representation for this object; i.e. its name:
return name;
}
I'd still recommend to rename your Object class to something more sensible to avoid confusing it with the built-in java.lang.Object.
Try to remove this part from json string
{
objects:
and last curly bracket.
I have two jsons
{
success: 1,
camera: [
{
productid: 18486,
productkey: 509,
categoryid: 85,
categorykey: 2,
productname: "Samsung",
productimage: "samsung.jpg",
price: "10900"
},
{
productid: 18458,
productkey: 508,
categoryid: 85,
categorykey: 2,
productname: "Nikon Coolpix L29",
productimage: "nikoncoolpix.jpg",
price: "4446"
}]
}
Second Json
{
success: 1,
mobile: [
{
productid: 9999,
productkey: 519,
categoryid: 852,
categorykey: 21,
productname: "Samsung grand",
productimage: "samsung.jpg",
price: "10900"
},
{
productid: 1858,
productkey: 58,
categoryid: 5,
categorykey: 12,
productname: "nokia",
productimage: "nokia.jpg",
price: "44462"
}]
}
I need to parse the json using GSON,I tried these code
Gson mJson = new Gson();
PDProduct mObj = (PDProduct) mJson.fromJson(jsonStr,PDProduct.class);
List<ProductObj> mobiles = null;
mobiles = mObj.mobiles;
for (int i = 0; i < mobiles.size(); i++) {
HashMap<String, String> ProductDetails = new HashMap<String,
String>();
ProductDetails.put(Tags.PRODUCTTAG_CATGORY_ID,
mobiles.get(i).categoryid);
ProductDetails.put(Tags.PRODUCTTAG_CATGORY_KEY,
mobiles.get(i).categorykey);
ProductDetails.put(Tags.PRODUCTTAG_PRODUCT_ID,
mobiles.get(i).productid);
ProductDetails.put(Tags.PRODUCTTAG_PRODUCT_KEY,
mobiles.get(i).productkey);
ProductDetails.put(Tags.PRODUCTTAG_PRODUCT_NAME,
mobiles.get(i).productname);
ProductDetails.put(Tags.PRODUCTTAG_PRODUCT_PIC,
mobiles.get(i).productimage);
ProductDetails.put(Tags.PRODUCTTAG_PRODUCT_PRICE,
mobiles.get(i).price);
ProductDetail.add(ProductDetails);
}
public class PDProduct {
public String success = null;
public List<PDProductObj> mobiles = null;
}
For this I'm getting only mobile data,How could dynamically create key?
Is there any other method to parse this data?
Your two JSON objects are two different objects; one has a field camera, one has a field mobile.
If you're the one producing that JSON the easiest answer is: fix the JSON. Use one field name and include something in the objects to denote a category (or whatever that is).
If you can't fix the JSON, you can write a custom deserializer that deals with it by renaming the field:
class MyDeserializer implements JsonDeserializer<PDProduct> {
#Override
public PDProduct deserialize(JsonElement je, Type type, JsonDeserializationContext jdc) throws JsonParseException
{
JsonObject jo = je.getAsJsonObject();
if (jo.has("camera"))
{
JsonElement e = jo.remove("camera");
jo.add("mobile", e);
}
return new Gson().fromJson(jo, type);
}
}
Then use it with Gson:
Gson gson =
new GsonBuilder()
.registerTypeAdapter(PDProduct.class, new MyDeserializer())
.create();
The simplest solution is to change the json.
Add a new level "product" to the json and let it have name and data.
For example:
{
success: 1,
product:
{
name:camera,
data: [
{
productid: 18486,
productkey: 509,
categoryid: 85,
categorykey: 2,
...
Change PDIProduct class to reflect this change.
If you can't change the json (because you are getting it from an external source), change the JSON object to this structure before using GSON.