I have a WCF Service that returns a List of objects to an Android app. One of the properties is a DateTime property. The return format is JSON and I am getting the date in this format /Date(1441117247253+0200)/ on the Android side. I am using com.squareup.retrofit:retrofit:1.9.0 to get the data from my service.
I have no clue how to use Retrofit to create a the string date into a Date object. I had a look at this http://square.github.io/retrofit/ under the Custom Converters section, but do not know how to go further. This is what I've tried but I do not know how to implement the converter class.
creating the restAdapter like so:
restAdapter = new RestAdapter.Builder()
.setConverter(new DotNetDateConverter())
.setLogLevel(RestAdapter.LogLevel.FULL)
.setEndpoint(API).build();
and the DotNetDateConverter Class which I do not know how to implement further:
public class DotNetDateConverter implements Converter
{
#Override
public Object fromBody(TypedInput body, Type type) throws ConversionException
{
return null;
}
#Override
public TypedOutput toBody(Object object)
{
return null;
}
}
there are other fields in the response which are fine, but how do I change the /Date(1441117247253+0200)/ to a proper java.util.Date object? Without the converter I get com.google.gson.JsonSyntaxException:/Date(1441117247253+0200)/ obviously because the string cannot be converter to a date.
Any assistance would greatly be appreciated.
Ok, so after some digging around, I came across this. My approach was wrong. So I ended up creating a Converter like this:
public class DotNetDateConverter implements JsonDeserializer<Date>
{
#Override
public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
{
String s = json.getAsJsonPrimitive().getAsString();
long l = Long.parseLong(s.substring(s.indexOf("(")+1, s.indexOf("+")));
Date d = new Date(l);
return d;
}
}
I also had to register it like so:
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Date.class, new DotNetDateConverter());
restAdapter = new RestAdapter.Builder()
.setConverter(new GsonConverter(gsonBuilder.create()))
.setLogLevel(RestAdapter.LogLevel.FULL)
.setEndpoint(API).build();
I had to change the code a bit to accommodate my specific scenario, that being dates come in as /Date(1441117247253+0200)/ with the time zone. Maybe someone else might find some use for this...
Related
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'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 :(
My webservice is giving me this format of Date: 1462575665220.
How can I deserialize this using GSON?
I think that I have to use something like this:
new GsonBuilder().setDateFormat("dd-MM-yyyy HH:mm:ss").create();
But I dont know what kind of format is it.
Can I deserialize it or I have to change something in the Webservice?
Sorry for bad english, I hope you understand.
Try this:
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Date.class, new JsonDeserializer<Date>() {
public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return new Date(json.getAsJsonPrimitive().getAsLong());
}
});
Gson gson = builder.create();
I have the following pojo:
public class PageRedirect implements Parcelable {
#SerializedName("method") private String method;
#SerializedName("url") private String url;
#SerializedName("parameters") private Parameters parameters;
//#SerializedName("parameters") private String params;
......}
The parameters field is changing depends on some parameter with the originating API. So sometimes it is {} "json object" and if it is empty, it is array []. I know this is a fault in the Backend. But I would like to find a quick work around... Instead of parsing the parameters, I just would like to get it as a string as the commented line and then I will process it. Any ideas?
When creating your instance of Gson, you can set a custom class deserializer as follows:
final GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Parameter.class, new ParameterTypeAdapter());
final Gson gson = gsonBuilder.create();
PageRedirect pageRedirect = gson.fromJson(yourJsonString, PageRedirect.class);
Then you can create your ParameterTypeAdapter as follows:
public class ParameterTypeAdapter extends TypeAdapter<Parameter> {
#Override
public void write(JsonWriter out, Calendar value) throws IOException {
}
#Override
public Calendar read(JsonReader in) throws IOException {
// do your parsing here
}
You can find more info on it here and here.
EDIT:
If you just want to defer parsing to another moment, you can store your "parameters" field as a JsonElement:
#SerializedName("parameters") private JsonElement parameters;
Afterwards, just convert it to String by using parameters.toString();
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;
...