RETROFIT doesn't allow to do nested and data always null - android

This is my Json Data
[
{
"id": 1,
"name": "ANDY",
"Game": {
"car": "1 Item",
"plane": "1 Item"
},
"location": {
"home": 5.555,
"office": 150.316
}
}
}
Here is my calling API :
#GET("/sample.json")
Observable<Response> getAppTours(#Header("If-None-Match") String etag);
How can I access the game and get the car & plane location : home & office ?
I am using Retrofit and Ormlite. I keep getting error on Game and Location while I am adding the #DATABASEFIELD Game game;
The error is said :
Attempt to invoke interface method 'com.j256.ormlite.stmt.QueryBuilder com.j256.ormlite.dao.Dao.queryBuilder()' on a null object reference

It's simple to to get Nested objects if you use core class library to read Json. But Using retrofit you have to create another POJO (model) for game object. Have a brief look.

Copy/paste this JSON as input of this page http://www.jsonschema2pojo.org/:
[
{
"id": 1,
"name": "ANDY",
"Game": {
"car": "1 Item",
"plane": "1 Item"
},
"location": {
"home": 5.555,
"office": 150.316
}
}
]
You had a } instead of a ] to close it up, on biggies. Now change the source type to JSON and select your converter accordingly.
You can check the Retrofit's available converters in their main documentation almost at the bottom of the page http://square.github.io/retrofit/.
Make sure you add the needed line to your gradle, being that something similar to:
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
Now from jsonschema2pojo click on "Preview" or "Zip" you will then be able to download your new model. Copy those classes to your project.
Your new Retrofit interface will look something like:
public interface YourService {
#GET("your/url")
Call<Yourclass> listStuff();
}
Then, after that, you will be able to make the HTTP request with:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API_URL)
.addConverterFactory(GsonConverterFactory.create()) //Converter
.build();
// Create an instance of our GitHub API interface.
YourService service = retrofit.create(YourService.class);
// Create a call instance for looking up Retrofit contributors.
Call<Yourclass> call = service.listStuff();
And get your games and locations with:
Yourclass myclass = call.execute().body();
Game game = myclass.getGame();
Location location = myclass.getLocation();
Check these if you want to get more details:
https://github.com/square/retrofit/blob/master/samples/src/main/java/com/example/retrofit/SimpleService.java
http://square.github.io/retrofit/
https://github.com/joelittlejohn/jsonschema2pojo

Related

Json parsing using Gson and Retrofit (Custom Deserializer)

I need to build list of places (from response array create instance of place and finally receive list of places (place in json))?
How to parse it using Gson and Retrofit with custom deserializer?
I have following strucutre:
{
"success": true,
"error": null,
"response": [
{
"loc": {
"lat": 51.50853,
"long": -0.12574
},
"place": {
"name": "London",
"state": "",
"stateFull": "",
"country": "GB",
"countryFull": "United Kingdom",
"region": "",
"regionFull": "",
"continent": "eu",
"continentFull": "Europe"
},
"profile": {
"elevM": 21,
"elevFT": 69,
"pop": 7556900,
"tz": "Europe/London",
"tzname": "BST",
"tzoffset": 3600,
"isDST": true,
"wxzone": null,
"firezone": null,
"fips": null,
"countyid": null
}
},
.............
.............
]
}
You can use Android Studio plugin RoboPOJOGenerator. It is very easy to make model classes from data.
This answer tells how to handle List response in retrofit.
Update
I don't think it is good idea to make custom deserializer just to parse a list. When you can filter or map list after getting response. It will take upto 3-4 lines of code.
If you don't want many classes. then you can safely delete Profile.java and Loc.java in your case, Gson will parse only data that you have declared in your pojo.
Make generic response class
You can make single class for handling all response by using Java Generics. See example.
public class ApiResponse<T> {
#SerializedName("error")
#Expose
private String error;
#SerializedName("success")
#Expose
private boolean success;
#SerializedName("response")
#Expose
private T response;
// make getter setters
}
Now in ApiService you can define response type. Like this
#GET("api/users/login")
Call<ApiResponse<ModelUser>> getUser();
#GET("api/users/login")
Call<ApiResponse<ModelCity>> getCity();
Thus you don't have to create 3 classes every time. This generic class will do work for all response.

Moshi Custom Adapter isn't being called

TL;DR:
My questions are:
1 - How can I make an Adapter for the "timestamp": 1515375392.225 to ZonedDateTime.
2 - How can I register the List<Report> adapter in the moshi object Builder if I need the moshi object to get this adapter, according to the documentation?
My JSON string has the following structure:
[
{
"id": 0,
"location": {
"latitude": -22.967049,
"longitude": -43.19096
},
"timestamp": 1515375392.225
},
{
"id": 0,
"location": {
"latitude": -22.965845,
"longitude": -43.191102
},
"timestamp": 1515375392.225
},
.......
}]
The timestamp is an automatic conversion made by the Jackson JavaTimeModule, it converts a ZonedDateTime to a timestamp String in the form of a decimal number representing the seconds and nanoseconds from an Instant.
In order to parse the JSON timestamp String, I made the following Moshi adapter:
public class ZonedDateTimeAdapter {
#FromJson ZonedDateTime fromJson(String timestamp) {
int decimalIndex = timestamp.indexOf('.');
long seconds = Long.parseLong(timestamp.substring(0, decimalIndex));
long nanoseconds = Long.parseLong(timestamp.substring(decimalIndex));
return Instant.ofEpochSecond(seconds, nanoseconds).atZone(ZoneId.systemDefault());
}
#ToJson String toJson(ZonedDateTime zonedDateTime) {
Instant instant = zonedDateTime.toInstant();
return instant.getEpochSecond() + "." + instant.getNano();
}
}
And then I register this adapter as:
Type type = Types.newParameterizedType(List.class, Report.class);
Moshi moshi = new Moshi.Builder().add(new ZonedDateTimeAdapter()).build();
JsonAdapter<List<Report>> reportAdapter = moshi.adapter(type);
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(MoshiConverterFactory.create(moshi))
.build();
The problem is, when I call my webservice using Retrofit, I get the following Exception:
com.squareup.moshi.JsonDataException: java.lang.NumberFormatException:
For input string: ".067000000" at $[0].timestamp
(keep in mind the nanoseconds .067000000 here won't be the same as the JSON example that I gave before, since they called the webservice at different times).
I tried to place a breakpoint on my ZonedDateTimeAdapter, but it's never being called. But it's influencing Moshi, because if I remove it from the Moshi.Builder, the error changes to:
Caused by: java.lang.IllegalArgumentException: Cannot serialize
abstract class org.threeten.bp.ZoneId
I also tried to change the ZonedDateTimeAdapter to deal with Double instead of String, but it just changes the error message to:
com.squareup.moshi.JsonDataException: java.lang.NumberFormatException:
For input string: ".515376840747E9" at $[0].timestamp
So, basically, I have a bunch of changing error messages and no idea what am I doing wrong. I followed the Moshi documentation on Custom Adapters and I don't know what else to do.
Your JSON adapter’s #ToJson methods is accepting a String but the timestamp is a number. Either change this to be a number (ie. double) or pass a JsonReader instead of a String and read the number out yourself. In this case you can call reader.nextString().

Parse RequestBody as a request paramter in Retrofit 2.0

I have to parse the following web service:
http://api.mytracemobile.com/mobile.svc/getHotelApp?data={"AvailabilitySearch":
{"Authority": { "Currency": "USD" },"Cityname": "Jaipur, India", "CheckInDate": "2017-11-18","CheckOutDate": "2017-11-19", "Searchid": "111111", "Room": [{"Guests": {"Adult": [{ "Title": "Mr" },{ "Title": "Mr" }]}},{"Guests": {"Adult": { "Title": "Mr" }}}],"Nationality": "IN"}}
How can I create a RequestBody for it so ?data= also includes in that request body.
Please let me know how this will be implemented
Thanks in Advance
Read about GSON and Retrofit, there are sufficient articles on both.
Instead of above data sample, Check the dummy sample and Model class to understand the process
Sample: A:{B:"",C:"",D:[{D1:""},{D1:""}]}
Models:
Class1 {#SerializedName("A") private Class2 a; }
Class2 { #SerializedName("B") private String b;
#SerializedName("C") private String c;
#SerializedName("D") private Arraylist<Class3> d; }
Class3 { #SerializedName("D1") private String D1; }
Note: Add Getter setter or constructor in Models to access the values.
After this, You can decrypt the JSON response just by passing reference of Class1.
Read about GSON(for #SerializedName) and then about Retrofit to fetch JSON Response also check this StackOverflow post.

How to generate GSONFormat for a property that can either be an object or string?

I'm using GSON for parsing response from a Volley request and got stuck in creating a GSON format when the response has a property that can either be a string or an object or an array perhaps... e.g content
{
"data": {
"date_updated": "2016-12-21T03:55:29.955Z",
"date_created": "2016-12-21T03:55:29.955Z",
"content": "String here",
"content": {
"longitude": "",
"latitude": ""
},
"status": "PROC",
"_id": "5859fd31a93c7235575d62db"
}
}
My current process in creating a GSON model is:
Create a java class
Right click and select Generate > GSONFormat
Paste the object I'm trying to convert then use it in Volley.
I tried the above object but it doesn't proceed. I think it's because of same property name.
Thanks for your advice.
You can use GsonFormat, you can look this:
https://github.com/zzz40500/GsonFormat

Deserialise JSON with dynamic field

I'm loading JSON with a completely dynamic field from an API using Retrofit on Android.
This is an example:
{
"success": true,
"messages": {
"success": [
"SUCCESS_MESSAGE"
],
"error": {
"specific_error": {
…
}
}
},
"session": {
"id": "fj4qewqrewq43fdsa",
"expire": null,
"YII_CSRF_TOKEN": "fda432fdafasd78978fdas"
},
"metadata": {…}
}
The structure of the messages object can change with every request. Sometimes there is the success field and sometimes not. Same goes for the error field. The error field especially can be an array but also an object with several other objects and fields inside.
Does it make sense to deserialise the messages field into different POJOs and if so how would I do that? Would it make more sense to keep the field as a JSON object and get values from the object when they are needed?

Categories

Resources