MappingFacebook JSON response to POJO - android

I am making an API call to Facebook and receiving the following Json object:
{"first_name":"FirstName",
"last_name":"LastName",
"email":"email#email.com",
"picture":{"data":{"is_silhouette":true,"url":"pictureUrl"}},"id":"12345"}
Instead of deserializing the object manually, I am currently using Gson for it, like this:
FacebookProfileModel facebookProfileModel = new Gson().fromJson(object.toString(), FacebookProfileModel.class);
Here's how my POJO looks like:
#SerializedName("first_name")
String mFirstName;
#SerializedName("last_name")
String mLastName;
#SerializedName("email")
String mEmail;
#SerializedName("url")
String mUrl;
Obviously, I am receiving all the values except for the url, since the value is in 2 Json objects: picture and data. I guess one possible solution but not the best would be to create the Picture object within the Facebook Model and then the Data object within the Picture object but feels bad creating 2 more pojos for a String. Any other solutions?

There is no annotation based solution for this. However, the custom de-serializer would resolve this problem.
Custom Deserializer:-
public class FacebookProfileModelDeserializer implements JsonDeserializer<FacebookProfileModel> {
#Override
public FacebookProfileModel deserialize(JsonElement paramJsonElement, Type paramType,
JsonDeserializationContext paramJsonDeserializationContext) throws JsonParseException {
String url = paramJsonElement.getAsJsonObject().get("picture").getAsJsonObject().get("data").getAsJsonObject()
.get("url").getAsString();
FacebookProfileModel facebookProfileModel = new Gson().fromJson(paramJsonElement.getAsJsonObject(),
FacebookProfileModel.class);
facebookProfileModel.setmUrl(url);
return facebookProfileModel;
}
}
Main method:-
public static void main(String[] args) {
String jsonString = "{\"first_name\":\"FirstName\",\"last_name\":\"LastName\",\"email\":\"email#email.com\",\"picture\":{\"data\":{\"is_silhouette\":true,\"url\":\"pictureUrl\"}},\"id\":\"12345\"}";
Gson gson = new GsonBuilder()
.registerTypeAdapter(FacebookProfileModel.class, new FacebookProfileModelDeserializer())
.create();
FacebookProfileModel faceBookProfileModel = gson.fromJson(jsonString, FacebookProfileModel.class);
System.out.println(faceBookProfileModel.toString());
}

Related

GSON sorting keys automatically in toJson() [duplicate]

Seems like Gson.toJson(Object object) generates JSON code with randomly spread fields of the object. Is there way to fix fields order somehow?
public class Foo {
public String bar;
public String baz;
public Foo( String bar, String baz ) {
this.bar = bar;
this.baz = baz;
}
}
Gson gson = new Gson();
String jsonRequest = gson.toJson(new Foo("bar","baz"));
The string jsonRequest can be:
{ "bar":"bar", "baz":"baz" } (correct)
{ "baz":"baz", "bar":"bar" } (wrong sequence)
You'd need to create a custom JSON serializer.
E.g.
public class FooJsonSerializer implements JsonSerializer<Foo> {
#Override
public JsonElement serialize(Foo foo, Type type, JsonSerializationContext context) {
JsonObject object = new JsonObject();
object.add("bar", context.serialize(foo.getBar());
object.add("baz", context.serialize(foo.getBaz());
// ...
return object;
}
}
and use it as follows:
Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class, new FooJsonSerializer()).create();
String json = gson.toJson(foo);
// ...
This maintains the order as you've specified in the serializer.
See also:
Gson User Guide - Custom serializers and deserializers
If GSON doesn't support definition of field order, there are other libraries that do. Jackson allows definining this with #JsonPropertyOrder, for example. Having to specify one's own custom serializer seems like awful lot of work to me.
And yes, I agree in that as per JSON specification, application should not expect specific ordering of fields.
Actually Gson.toJson(Object object) doesn't generate fields in random order. The order of resulted json depends on literal sequence of the fields' names.
I had the same problem and it was solved by literal order of properties' names in the class.
The example in the question will always return the following jsonRequest:
{ "bar":"bar", "baz":"baz" }
In order to have a specific order you should modify fields' names, ex: if you want baz to be first in order then comes bar:
public class Foo {
public String f1_baz;
public String f2_bar;
public Foo ( String f1_baz, String f2_bar ) {
this.f1_baz = f1_baz;
this.f2_bar = f2_bar;
}
}
jsonRequest will be { "f1_baz ":"baz", "f2_bar":"bar" }
Here's my solution for looping over json text files in a given directory and writing over the top of them with sorted versions:
private void standardizeFormat(File dir) throws IOException {
File[] directoryListing = dir.listFiles();
if (directoryListing != null) {
for (File child : directoryListing) {
String path = child.getPath();
JsonReader jsonReader = new JsonReader(new FileReader(path));
Gson gson = new GsonBuilder().setPrettyPrinting().registerTypeAdapter(LinkedTreeMap.class, new SortedJsonSerializer()).create();
Object data = gson.fromJson(jsonReader, Object.class);
JsonWriter jsonWriter = new JsonWriter(new FileWriter(path));
jsonWriter.setIndent(" ");
gson.toJson(data, Object.class, jsonWriter);
jsonWriter.close();
}
}
}
private class SortedJsonSerializer implements JsonSerializer<LinkedTreeMap> {
#Override
public JsonElement serialize(LinkedTreeMap foo, Type type, JsonSerializationContext context) {
JsonObject object = new JsonObject();
TreeSet sorted = Sets.newTreeSet(foo.keySet());
for (Object key : sorted) {
object.add((String) key, context.serialize(foo.get(key)));
}
return object;
}
}
It's pretty hacky because it depends on the fact that Gson uses LinkedTreeMap when the Type is simply Object. This is an implementation details that is probably not guaranteed. Anyway, it's good enough for my short-lived purposes...

How do you parse json object inside a json array?

I am pretty weak with JSON, and probably have a silly question, and was wondering how to parse a JSON object placed inside a JSON array.
So, as of now, I have
public Single<Profile> doProfileApiCall() {
return Rx2AndroidNetworking.post(ApiEndPoint.ENDPOINT_PROFILE)
.addHeaders(mApiHeader.getProtectedApiHeader())
.build()
.getObjectSingle(Profile.class);
To retrieve my profile params, but in my endpoints I have :
[{"Name": "this", "Email","that#gmail.com"}]
I have my endpoint set up as :
public static final String ENDPOINT_PROFILE =
BuildConfig.BASE_URL
+ "/linktojson";
which gives me the above JSON.
but the issue is the [], how do I modify this with :
public Single<Profile> doProfileApiCall() {
return Rx2AndroidNetworking.post(ApiEndPoint.ENDPOINT_PROFILE)
.addHeaders(mApiHeader.getProtectedApiHeader())
.build()
.getObjectSingle(Profile.class);
such that I can use my profile.java model class which has
public class Profile {
#Expose
#SerializedName("Name")
private String name;
#Expose
#SerializedName("Email")
private String email;
etc...
}
Any idea how to go about this?
In the doProfileApiCall() method instead of .getObjectSingle use
.getJSONArraySingle(ProfileList.class)
Now create a new class ProfileList.java with the following code.
List<Profile> profileList = new ArrayList<>();
public List<Profile> getProfileList() {
return profileList;
}
public void setProfileList(List<Profile> profileList) {
this.profileList = profileList;
}
Change the returntype of the doProfileApiCall method to
public Single<ProfileList> doProfileApiCall()
Whenever you want to access the data use it with the list position 0, when in future you get more data, you can index the data accordingly.
Generally, if JSON root object is an array you should use List on Java side. In your case you have array so use related method:
return Rx2AndroidNetworking.post(ApiEndPoint.ENDPOINT_PROFILE)
.addHeaders(mApiHeader.getProtectedApiHeader())
.build()
.getObjectListSingle(Profile.class);
Rx2ANRequest source.

Custom GSON parser exclude object instances based on property value

When parsing JSON in Android using the GSON parser, I'd like to implement a rule that will exclude any objects from being created based on property value. For example:
{"people": [
{"first_name": "Bob"},
{"first_name": "Bob", "last_name": "Loblaw"}]}
I want to exclude the first person object because it doesn't have a last name property.
Is this possible at parse time?
It is possible with JsonDeserializer.
Suppose you would have POJOs like
public class Response {
#Getter
private List<Person> people = new ArrayList<>();
}
and
public class Person {
#Getter #Setter
private String first_name, last_name;
}
Creating JsonDeserializer like
public class PersonResponseDeserializer implements JsonDeserializer<Response> {
// Create a new gson to make the default parsing for response object
private final Gson gson = new Gson();
#Override
public Response deserialize(JsonElement json, Type typeOfT
, JsonDeserializationContext context) throws JsonParseException {
Response r = gson.fromJson(json, typeOfT);
// Remove all persons from R that have last name null
r.getPeople().removeAll(
r.getPeople().stream().filter( p -> p.getLast_name() == null )
.collect(Collectors.toSet())
);
return r;
}
}
could then be used like
Gson gson = new GsonBuilder()
.registerTypeAdapter(Response.class, new PersonResponseDeserializer())
.create();
Response r = gson.fromJson(s, Response.class);
So this is if it is required to be done at the parse time. Maybe it is otherwise better to loop the People after parsing and exclude Persons without last name then.

How Gson knows which json element is to use which Deserializer

I have the following json input. And know i am using Gson to parse.
{
“type”: “type1”,
“date”: “Tue, 16 May 2017 07:09:33 +0000”,
“body”:
{
“formatA_1”: “aaa”,
“formatA_2”: “bbbcccddd”
}
"other": "info"
}
public class Data {
private String type;
private Long date;
private Body body;
private String other;
...
}
As i want to convert the date to long, So i implement the custom DateDeserializer.
public class DateDeserializer implements JsonDeserializer<Long> {
#Override
public Long deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return DateConvertUtils.convertStringDatetoLong(json.getAsString(), DateConvertUtils.SERVER_DATE_FORMAT);
}
}
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Long.class, new DateDeserializer());
Gson gson = gsonBuilder.create();
Data data = gson.fromJson(json, Data.class);
This is working. But i wonder, how the Gson knows that only the "date" element needs to use the DateDeserializer? How it knows that other elements no need to use DateDeserializer?
If I put more other custom deserializers, how it would know which element is to use which deserializer?
Thanks a lot.
Gson uses the last added deserializer for given type and uses the deserializer for all cases. In your case, it will use your deserializer for any and all Longs

Using GSON to deserialize nested array

Giving
JSON
// imagine this is JSON of a city
{
"title" : "Troy"
"people" : [
{
{
"title" : "Hector",
"status" : "Dead"
},
{
"title" : "Paris",
"status" : "Run Away"
}
},
...
],
"location" : "Mediteranian",
"era" : "Ancient",
...
}
City
public class City {
#SerializeName("title")
String title;
#SerializeName("people")
List<Person> people;
#SerializeName("location")
String location;
#SerializeName("era")
String era;
...
}
Person
public class Person {
#SerializeName("title")
private String title;
#SerializeName("status")
private String status;
}
If having string of JSON above, it is possible to create list of person
A. without having to deserialize City first like following
City city = new Gson().fromJson(json, City.class)
ArrayList<Person> people = city.people;
And
B. without having to convert string to JSONObject, get JSONArray and then convert back to string like following
String peopleJsonString = json.optJSONArray("people").toString
ArrayList<Person> people = new Gson().fromJSON(peopleJsonString, Person.class);
You can use a custom JsonDeserializer, which is part of Gson (com.google.gson.JsonDeserializer).
Simple example:
public class WhateverDeserializer implements JsonDeserializer<Whatever> {
#Override
public Whatever deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException {
Whatever whatever = new Whatever();
// Fetch the needed object here
// whatever.setNeededObject(neededObject);
return whatever;
}
}
You can then apply this deserializer like this:
Gson gson = new GsonBuilder()
.registerTypeAdapter(Whatever.class, new WhateverDeserializer())
.create();
There is a full example of how to use a custom deserializer, including a super detailed explanation, on this page: http://www.javacreed.com/gson-deserialiser-example/
I don't think you can get the list directly without parsing the json array. You need to parse the array. And it would be faster via Gson;
If you strictly need (only array) and you won't be using any other json object . Simply delete them, so that gson won't parse them.

Categories

Resources