For example i have json looks like:
{
"data": [
{
"name": "one"
},
{
"name": "two"
}
]
}
For example i have object User with field name.
Is it possible write method which will parse data array to objects User?
something like
Call<List<User>> getUsers(#KeyPath("data"))
Now to do this, i need create a wrapper class something like
public class UsersWrapper {
#SerializeName("data")
public ArrayList<User> users;
}
and in service i do next
public interface Service {
#GET("users")
Call<UsersWrapper> getUsers()
}
But my all requests is just response with data but variable objects in array.
In this case i need create wrappers to any data requests. Pain :(
?
I'd do it this way:
Global class Wrapper<T> to parse the whole JSON
public class Wrapper<T> {
public List<T> data;
}
And User to parse actual array;
public class User {
public String name;
}
Then, the API interface:
#GET("/people")
Wrapper<User> getUsers();
And in DataSource class just do something like this:
#Override
public List<User> getUsers() {
Wrapper<User> usersWrapper = myApiInterface.getUsers();
return usersWrapper.data;
}
Upd1:
Another solution is to create custom JsonDeserializer (like described here) for List<User> type, register by registerTypeAdapter it with your custom Gson object and then you can deserialise your Json directly into List<User>. Though, this solution brings much more extra code and potential benefit is unclear for me.
Related
Well, I've seen alot of boilerplate code in my model classes when I need to parse json using GSON with Retrofit2. I would like to find out how to deal with it, because I'm pretty sure there is a way to make this look more elegant.
{
"data": [
{
"id": 2,
"price": 56,
"name": "Hello"
}
]
}
For parsing this json I would need to create 2 model classes. One would be for the inner object (id, price, name) and one would be Data.class which holds one attribute - List of this inner object.
It's totally okay to have different inner objects inside, but later on you will have many "Data.class" which has one attribute "data" which is List, but with different inner object type. How can I avoid this boiler-plate Data lookalike classes in my projects?
What I want:
Is to NOT create new Data class with "data" attribute changing inner object type whenever I create new "inner" object model class.
I had this problem and fix that with create one abstract class with name BaseResponse like this
public abstract class BaseResponseInterface2<T> {
#SerializedName("data")
private List<T> data;
public List<T> getData() {
return data;
}
public void setData(List<T> data) {
this.data = data;
}
}
And use that like this in api service interface
#GET("/api/")
Call<BaseResponseInterface2<innerClass>> getResponse(
#Path("id") int id
);
Hope it help
The API I'm working with returns objects (and their containing objects) in a "flat" format and I'm having trouble getting this to work elegantly with Retrofit and RxJava.
Consider this JSON response for an /employees/{id} endpoint:
{
"id": "123",
"id_to_name": {
"123" : "John Doe"
},
"id_to_age": {
"123" : 30
}
}
Using Retrofit and RxJava, how do I deserialize this to a Employee object with fields for name and age?
Ideally I'd like RxJava's onNext method to be called with an Employee object. Is this possible? Could this perhaps be done with some type of custom deserializer subclass (I'm using Gson at the moment)?
I realize I could create an EmployeeResponse object that maps directly to the JSON response, but having to map the EmployeeResponse to the Employee object every time I use this in an activity seems kind of unfortunate. It also gets much more complicated when the flat response also contains other objects that need to get deserialized and set as fields on the Employee.
Is there a better way?
The complete solution to this will seem like a lot, but this will let you write Retrofit interfaces with Employee instead of EmployeeResponse. Here's the game plan:
You will still need both EmployeeResponse and Employee objects, where EmployeeResponse just maps exactly to what you'd get from the API. Treat the response as a builder for Employee and write a static factory method that returns an Employee from an EmployeeResponse, ie. Employee employee = Employee.newInstance(response);
You will be creating a custom TypeAdapterFactory for Gson. When Gson sees you request a Employee object, we will have the TypeAdapter actually create an EmployeeResponse, then return the Employee via the static factory method described above.
Your TypeAdapterFactory will look something like this:
public class EmployeeAdapterFactory implements TypeAdapterFactory {
#Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
return type.getRawType() == Employee.class
? (TypeAdapter<T>) employeeAdapter(gson, (TypeToken<Employee>) type)
: null;
}
private TypeAdapter<Employee> employeeAdapter(Gson gson, TypeToken<Employee> type) {
return new TypeAdapter<Employee>() {
#Override public void write(JsonWriter out, Employee value) throws IOException {
// TODO serialization logic to go from an Employee back to EmployeeResponse structure, if necessary
}
#Override public Employee read(JsonReader in) throws IOException {
return Employee.newInstance(gson.fromJson(in, EmployeeResponse.class));
}
};
}
}
Register the factory when you make Gson:
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(new EmployeeAdapterFactory())
.build();
Retrofit retrofit = new Retrofit.Builder().baseUrl("https://foo.bar")
.addConverterFactory(GsonConverterFactory.create(gson))
... // your other Retrofit configs, like RxJava call adapter factory
.build();
And now you can safely define all your Retrofit interfaces with Employee instead of EmployeeResponse.
I have a problem pretty much the same as this: retrofit returning valid json but pojo is empty
But my variables are not declared as static. The are all declared like:
#SerializedName("name")
#Expose
private String name;
I have tried removing the annotations, but that doesn't work.
what could be the problem?
EDIT:
Interface:
#GET("/MyController/MyAction/{name}")
void getSomeData(#Path("name") String name, Callback<List<DataItem>> cb);
Can you show me the actual received data(JSON or XML)? It seems that your callback structure is not matching with your data. For example, it would be possible that your data may have array that have a name, and you ignored it.
In my case, I declared like this,
void getList(#Path("data") String data,//
Callback<OrderList> callback);
OrderList is:
public class OrderList {
List<Order> order_list;
}
And my data is:
{
"order_list":
[
{ "id": "1001", "data": "a" },
{ "id": "1002", "data": "b" }
]
}
I mean, it seems that your data may have nested structure and your class may not matching with that.
I designed a model with two entities : ParentEntity and ChildEntity. I use OrmLite to store them in a database.
I actually get my data from a remote webservice. This is the process I have :
Request the webservice (using retrofit)
Get JSON string in response
JSON string is parsed to JSON model by Gson (thank you again retrofit :))
I convert JSON model to an OrmLite model (no library, I make it myself)
The OrmLite model is given back to the callback waiting for the response of the request
The callback is in charge of calling the DAO to actually store the data
This process works perfectly for simple entities. But a problem appears when I try to manage more complex entities, with a ForeignCollection for example. Actually, I can't achieve the step 4 because I can't create a new ForeignCollection to put my child entities inside it.
I found some answers saying that I should store every child myself, but it will break the step 6 of my workflow.
So the question is :
How can I initialize the ForeignCollection before getting the object from the database ?
I could find a way to change the workflow. But it's only a kind of "work-around" and will create container objects just to achieve this...
The OrmLite schema (simplified)
(The class Entity just have a property id and its getter/setter.)
ParentEntity
#DatabaseTable(daoClass = ParentEntityDao.class)
public class ParentEntity extends Entity
{
#ForeignCollectionField(eager = true)
private ForeignCollection<TimesheetEntry> entries;
public Collection<TimesheetEntry> getEntries()
{
if(entries != null)
{
return Collections.unmodifiableCollection(entries);
}
else
{
return Collections.unmodifiableCollection(new ArrayList<TimesheetEntry>());
}
}
public void addEntry(ChildEntry childEntry)
{
childEntry.setParent(this);
entries.add(childEntry);
}
public void removeEntry(ChildEntry childEntry)
{
entries.remove(childEntry);
}
}
ChildEntity
#DatabaseTable(daoClass = ChildEntityDao.class)
public class ChildEntry extends Entity
{
#DatabaseField(foreign = true, canBeNull = false)
private ParentEntity parentEntity;
public void setParentEntity(ParentEntity parentEntity)
{
this.parentEntity = parentEntity;
}
public ParentEntity getParentEntity()
{
return parentEntity;
}
}
JSON schema (simplified)
Parent entity
public class JsonParentEntity
{
private List<JsonChildEntity> entries;
// Getter/setter
}
Child entity
public class JsonChildEntity
{
private String name;
// Getter/setter
}
I'm having some trouble with GSON, mainly deserializing from JSON to a POJO.
I have the following JSON:
{
"events":
[
{
"event":
{
"id": 628374485,
"title": "Developing for the Windows Phone"
}
},
{
"event":
{
"id": 765432,
"title": "Film Makers Meeting"
}
}
]
}
With the following POJO's ...
public class EventSearchResult {
private List<EventSearchEvent> events;
public List<EventSearchEvent> getEvents() {
return events;
}
}
public class EventSearchEvent {
private int id;
private String title;
public int getId() {
return id;
}
public String getTitle() {
return title;
}
}
... and I'm deserializing with the following code, where json input is the json above
Gson gson = new Gson();
return gson.fromJson(jsonInput, EventSearchResult.class);
However, I cannot get the list of events to populate correctly. The title and id are always null. I'm sure I'm missing something, but I'm not sure what. Any idea?
Thanks
OK, I figured this out. I attest this to a long day of coding with little sleep the night before!
The "events" data structure contained multiple "events", which each contain an "event" type. I had to move the EventSearchEvent under a new class called EventContainer. This event container contained one field "event". This "event" was the "EventSearchEvent". THerefore, when GSON iterated over the JSON array, it saw the Container (which is of type "events") and then inside of that object it looked for a "event" member. When it finally found that it loaded up the id and title appropriately.
The short of it: I didn't have my object hierarchy built correctly.