use JsonDeserializer on GSON - android

I have a model that work with multiple JSON responses. However this a response :
items: [
{
kind: "youtube#playlistItem",
etag: ""fpJ9onbY0Rl_LqYLG6rOCJ9h9N8/jqbcTLu8tYm8b1FXGO14gNZrFG4"",
id: "PLUQ7I1jJqKB4lJarGcpWsP62l7iC06IkE2LDE0BxLe18",
That conflict with this one (notice the same id field with different type. The above id is String, the other is a Class) :
items: [
{
kind: "youtube#searchResult",
etag: ""fpJ9onbY0Rl_LqYLG6rOCJ9h9N8/hDIU49vmD5aPhKUN5Yz9gtljG9A"",
id: {
kind: "youtube#playlist",
playlistId: "PLh6xqIvLQJSf3ynKVEc1axUb1dQwvGWfO"
},
I want to read the id field using a single model class.
This is my model Response class :
public class Response {
private ArrayList<Item> items = new ArrayList<Item>();
And this is my model Item class :
public class Item {
private Id id;
//nested class inside Item
public class Id
{
private String id;
private String kind;
private String playlistId;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getKind() {
return kind;
}
public void setKind(String kind) {
this.kind = kind;
}
public String getPlaylistId() {
return playlistId;
}
public void setPlaylistId(String playlistId) {
this.playlistId = playlistId;
}
}
Notice that the Id class is inside Item class.
This is how i use registerTypeAdapter :
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Response.class,
new JsonDeserializer<Item.Id>() {
#Override
public Item.Id deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
Item.Id result = new Item().new Id();
if(jsonElement.isJsonPrimitive() == false)
{
result.setKind(jsonElement.getAsJsonObject().get("kind").getAsString());
result.setPlaylistId(jsonElement.getAsJsonObject().get("playlistId").getAsString());
//return new Item.Id(jsonElement.getAsJsonObject().get("kind").getAsString(), jsonElement.getAsJsonObject().get("playlistId").getAsString());
return result;
}
else
{
result.setId(jsonElement.getAsString());
//return new Item.Id(jsonElement.getAsString());
return result;
}
}
});
Gson gson = gsonBuilder.create();
Response result = Response.success(
gson.fromJson(json, gsonClass),
HttpHeaderParser.parseCacheHeaders(response));
However the above code throws java.lang.NullPointerException in this line :
if(jsonElement.isJsonPrimitive() == false)
What should i do?
Is this the correct way to use registerTypeAdapter?
Thanks for your time

Your main problem seems to be that you're registering a type adapter for Response class but you're giving a JsonDeserializer that handles Item.Id classes. Either you deserialize a full Response or limit yourself to Item or even Item.Id.
Another problem with deserializing Item.Id (as written in your question) directly is that it's a non-static inner class, which requires an instance of the parent class to be instantiated (as you do with the new Item().new Id()). I think that you would have to deserialize the whole Item manually if you want to keep it as such, frankly I don't see any reason not to make Item.Id static as it would simplify the problem.
Here's my solution with the static version of Item.Id
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Item.Id.class, new JsonDeserializer<Item.Id>() {
#Override
public Item.Id deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
Item.Id id = new Item.Id();
if (!json.isJsonPrimitive()) {
JsonObject jsonObject = json.getAsJsonObject();
id.setKind(jsonObject.get("kind").getAsString());
id.setPlaylistId(jsonObject.get("playlistId").getAsString());
} else {
id.setId(json.getAsString());
}
return id;
}
});
And some test snippets that seemed to work for me:
Gson gson = builder.create();
Response response = gson.fromJson("{\n" +
" \"items\": [\n" +
" {\n" +
" \"kind\": \"youtube#playlistItem\",\n" +
" \"etag\": \"fpJ9onbY0Rl_LqYLG6rOCJ9h9N8/jqbcTLu8tYm8b1FXGO14gNZrFG4\",\n" +
" \"id\": \"PLUQ7I1jJqKB4lJarGcpWsP62l7iC06IkE2LDE0BxLe18\"\n" +
" }\n" +
" ]\n" +
"}", Response.class);
Response response2 = gson.fromJson("{\n" +
" \"items\": [\n" +
" {\n" +
" \"kind\": \"youtube#searchResult\",\n" +
" \"etag\": \"fpJ9onbY0Rl_LqYLG6rOCJ9h9N8/hDIU49vmD5aPhKUN5Yz9gtljG9A\",\n" +
" \"id\": {\n" +
" \"kind\": \"youtube#playlist\",\n" +
" \"playlistId\": \"PLh6xqIvLQJSf3ynKVEc1axUb1dQwvGWfO\"\n" +
" }\n" +
" }\n" +
" ]\n" +
"}", Response.class);
If you want to keep Item.Id non-static, I think you need to write a deserializer for Item instead. Also keep in mind that json elements can be absent but the parser should still be able to handle it.

I had just the same problem long time ago, but i was working then with Json.Net, However json concept is (almost) the same so I hope I will succeed help.
I guess it might not work, but why try to not register with the Item class? I mean there is no 'Item.Id' in so it's obious get a null. BUT this: gsonBuilder.registerTypeAdapter(Item.class, likely to fail, becaus you dont parse the Item class explicitly (However it might work, I just never tried it in Gson. But in Json.Net similar way would work proper.)
So, how could you fix that? I'm too lazy to write an paraser but you could look here and read the surt summary summary from here (Search for Using the registerTypeAdapter().
I hope I helped.

Related

error while Deserialize the Google Direction json

I have this json
https://maps.googleapis.com/maps/api/directions/json?origin=-22.8895625,-47.0714089&destination=-22.892376,-47.027553&key=
And I need deserialize it
But I get this error
Additional text encountered after finished reading JSON content: ,. Path '', line 8, position 4.
What I am doing:
public static async Task<List<Model.Localizacao>> GetDirectionsAsync(Localizacao locUser, Localizacao locLoja)
{
using (var client = new HttpClient())
{
try
{
List<Model.Localizacao> lstLoc = new List<Model.Localizacao>();
var json = await client.GetStringAsync("https://maps.googleapis.com/maps/api/directions/json?origin=" + locUser.latitude + "," + locUser.longitude + "&destination="+ locLoja.latitude+","+locLoja.longitude+"&key=" + GOOGLEMAPSKEY);
json = json.Substring(json.IndexOf('['));
json = json.Substring(0, json.LastIndexOf(']') + 1);
lstLoc = JsonConvert.DeserializeObject<List<Model.Localizacao>>(json);
return lstLoc;
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
return null;
}
}
}
this is my class:
namespace neoFly_Montana.Model
{
class Localizacao
{
public double latitude { get; set; }
public double longitude { get; set; }
}
}
How can I solve that?
My key is the same for the maps of google
I believe the problem is in these lines:
json = json.Substring(json.IndexOf('['));
json = json.Substring(0, json.LastIndexOf(']') + 1);
This appears to set json to be all the text from the first [ to the last ]. That means that you're going to wind up with some malformed json.
geocoded_waypoints is an array, but so is routes, which means you're going to wind up with a String that looks like this:
[
{ "geocoder_status" : "OK" ... }
{ "geocoder_status" : "OK" ... }
], "routes": [
{ "bounds": { ... } ... }
]
That , "routes": [ will fail to parse.
Update
After some discussion in the comments, I think I'm at the end of the help I'm able to provide. I'm not familiar with C# or the particular JSON parsing library you're using.
However, I can offer some ideas as a starting point.
The JSON coming back from that Google call has a particular structure. I suspect you will have to create new model classes that match this structure. For example, the top-level object would have three fields, and might look like this in Java:
public class ApiResponse {
private List<Waypoint> geocoded_waypoints;
private List<Route> routes;
private String status;
}
Then you'd have to implement Waypoint and Route, again matching the structure of the Google response:
public class Waypoint {
private String geocoder_status;
private String place_id;
private List<String> types;
}
public class Route {
private Bounds bounds;
private String copyrights;
private List<Leg> legs;
private Polyline overview_polyline;
private String summary;
private List<String> warnings;
private List<String> waypoint_order;
}
And so on. Once you have a class to represent the top-level response as well as all the various sub-objects inside that response, you would probably be able to change this code:
List<Model.Localizacao> lstLoc = new List<Model.Localizacao>();
var json = await client.GetStringAsync("https://maps.googleapis.com/maps/api/directions/json?origin=" + locUser.latitude + "," + locUser.longitude + "&destination="+ locLoja.latitude+","+locLoja.longitude+"&key=" + GOOGLEMAPSKEY);
json = json.Substring(json.IndexOf('['));
json = json.Substring(0, json.LastIndexOf(']') + 1);
lstLoc = JsonConvert.DeserializeObject<List<Model.Localizacao>>(json);
return lstLoc;
to this:
var json = await client.GetStringAsync("https://maps.googleapis.com/maps/api/directions/json?origin=" + locUser.latitude + "," + locUser.longitude + "&destination="+ locLoja.latitude+","+locLoja.longitude+"&key=" + GOOGLEMAPSKEY);
return JsonConvert.DeserializeObject<ApiResponse>(json);

How to skip wrapper object in jackson json deserialization

I am trying to deserialize the following string using the jackson.
{
"roomName": "u8ec29p0j7q2m9f",
"broadcastPresenceRoles": {
"broadcastPresenceRole": [
"moderator",
"participant",
"visitor"
]
},
"owners": {
"owner": "anewuser#shahanshah"
},
"admins": {
"admin": "sanjeet#shahanshah"
},
"members": null,
"outcasts": {
"outcast": [
"sawan#shahanshah",
"sus#shahanshah"
]
},
"ownerGroups": {
"ownerGroup": "Friends"
}
}
This the response from the openfire rest apis.
I am having problem with the wrapper object wrapping the arrays.
Here
"broadcastPresenceRoles": {
"broadcastPresenceRole": [
"moderator",
"participant",
"visitor"
]
},
I tried this to unwrap the container but not getting success. I don't think writing wrapper classes is good idea (Since I will have to write several wrapper classes).Also I need the generalized solution so i can use it with other responses since the apis is having other responses in the similar wrapped objects format. Thanks in advance.
You can create a custom annotation with #JsonDeserialize inside and create a custom JsonDeserializer that implements ContextualDeserializer. The idea is inspired from the solution you mentioned but it is more general to unwrap any one property in a json object.
Following is the custom annotation using #JacksonAnnotationsInside as annotation container contains #JsonDeserialize:
#Retention(RetentionPolicy.RUNTIME)
#JacksonAnnotationsInside
#JsonDeserialize(using = JsonUnwrapPropertyDeserializer.class)
public #interface JsonUnwrapProperty {
}
and the custom JsonDeserializer that implements ContextualDeserializer:
public class JsonUnwrapPropertyDeserializer extends JsonDeserializer<Object> implements ContextualDeserializer {
private JavaType unwrappedJavaType;
private String unwrappedProperty;
#Override
public JsonDeserializer<?> createContextual(final DeserializationContext deserializationContext, final BeanProperty beanProperty) throws JsonMappingException {
unwrappedProperty = beanProperty.getMember().getName();
unwrappedJavaType = beanProperty.getType();
return this;
}
#Override
public Object deserialize(final JsonParser jsonParser, final DeserializationContext deserializationContext) throws IOException {
final TreeNode targetObjectNode = jsonParser.readValueAsTree().get(unwrappedProperty);
return jsonParser.getCodec().readValue(targetObjectNode.traverse(), unwrappedJavaType);
}
}
and the usage example:
public class MyBean {
#JsonProperty("broadcastPresenceRoles")
#JsonUnwrapProperty
private List<String> broadcastPresenceRole;
#JsonProperty("admins")
#JsonUnwrapProperty
private String admin;
// constructor, getter and setter
}
#JsonProperty is to locate the wrapper object and #JsonUnwrappProperty is to deserialize the json object and extract a property into the annotated field.
Edited:
Following is an example with ObjectMapper:
String json = "{\n" +
" \"broadcastPresenceRoles\": {\n" +
" \"broadcastPresenceRole\": [\n" +
" \"moderator\",\n" +
" \"participant\",\n" +
" \"visitor\"\n" +
" ]\n" +
" },\n" +
" \"admins\": {\n" +
" \"admin\": \"sanjeet#shahanshah\"\n" +
" }\n" +
"}";
final ObjectMapper mapper = new ObjectMapper();
final MyBean myBean = mapper.readValue(json, MyBean.class);
System.out.println(myBean.getBroadcastPresenceRole());
System.out.println(myBean.getAdmin());
Output:
[moderator, participant, visitor]
sanjeet#shahanshah
I created a variation that solved the NPE based on #wilson response
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
#Retention(RetentionPolicy.RUNTIME)
#JacksonAnnotationsInside
#JsonDeserialize(using = JsonUnwrapProperty.JsonUnwrapPropertyDeserializer.class)
public #interface JsonUnwrapProperty {
public static class JsonUnwrapPropertyDeserializer extends JsonDeserializer<Object>
implements ContextualDeserializer {
private JavaType unwrappedJavaType;
private String unwrappedProperty;
#Override
public JsonDeserializer<?> createContextual(final DeserializationContext deserializationContext,
final BeanProperty beanProperty) throws JsonMappingException {
unwrappedProperty = beanProperty.getMember().getName();
unwrappedJavaType = beanProperty.getType();
return this;
}
#Override
public Object deserialize(final JsonParser jsonParser, final DeserializationContext deserializationContext)
throws IOException {
System.out.println(String.format("Ignoring %s in %s/%s", unwrappedProperty,
jsonParser.currentName(), jsonParser.nextFieldName()));
JsonToken token = jsonParser.nextValue();
return jsonParser.getCodec().readValue(jsonParser, unwrappedJavaType);
}
}
}

From JSon array to custom data structure

Say I need to fill a Binary Search Tree with data obtained from Server. Say further that the data coming from server is a json array of nodes
"parts":[{"id":1,"name":"apple"},{"id":12,"name":"orange"},{"id":21,"name":"pen"},{"id":214,"name":"kite"}]//where each {} represents a node
How do I use GSon to read the array of Nodes into my BST?
If you recall a BST has two classes
public class BST{
private Note root;
}
public class Node{
String el;
Node left, right;
}
If BST is too hard, image something simpler
public class MyDataStructure{
private List<Part> partsList;
…
}
public class Part{
String el;
List<String> stuff;
}
How do I populate MyDataStructure with partsList using GSon on android? As a side note, I would rather help solving the MyDataStruction version of the problem.
ok.. you can use this as reference:
define a class pojo
and a Fruit(is the array/list/collection)
the pojo
class Pojo {
#Override
public String toString() {
return "Pojo [parts=" + parts + "]";
}
private List<Fruits> parts;
}
the fruit
class Fruits {
private int id;
private String name;
#Override
public String toString() {
return "[id=" + id + ", name=" + name + "]";
}
}
the implementation
String json = "{\"parts\": [{\"id\":1,\"name\":\"apple\"},{\"id\":2,\"name\":\"pear\"},{\"id\":3,\"name\":\"kiwi\"}]}";
Gson g = new Gson();
Pojo p = g.fromJson(json, Pojo.class);
System.out.println(p);
the MyDataStructure population
add to the pojo a setter getter so you can work with the list, add too setter and getter for the fruit class so you can get the id and the name..
so in the pojo object p you can do p.getList() and iterate over the elements
Something like:
Pojo p = g.fromJson(json, Pojo.class);
System.out.println(p);
for (Fruits f : p.getParts()) {
System.out.println(f.getId());
System.out.println(f.getName());
}

Json to POJO mapping in Android

What are good practice for handling json over a Rest Framework in Android. For instance, if I get a certain json result as follow (or any other, I'm just giving something more complex):
{"lifts":
[{
"id":26,
"time":"2012-11-21T12:00:00Z",
"capacity":4,
"price":10,
"from": {
"description":null,
"city": {
"name":"Montreal"
}
},
"to":{
"description":"24 rue de la ville",
"city":{
"name":"Sherbrooke"
}
},
"driver":{
"first_name": "Benjamin",
"image":"https://graph.facebook.com/693607843/picture?type=large"
}
}
]}
1) Should I handle the result manually and get each value to populate my ui... (Not really)
2) Should I create a POJO for each object (to handle the mapping, with JSONObject). In my example, I will have to create a lift object that handle all the parameters and even create more POJO, to use for instance image and probably locations. (so basically, I constantly need to check my api rest framework to see how my object are done on server side, I'm duplicating my models from server to the android client).
3) Is there any framework to handle the mapping (serialize and deserialization).
I'm currently using option number 2, but was wondering if there was something better out there. It's working for me so far, for receiving and sending.
I like to create a response object per api endpoint where i map the response of the call.
For the given example and using GSON, the response object would be something like the following
public class Test
{
static String jsonString =
"{\"lifts\":" +
" [{" +
" \"id\":26," +
" \"time\":\"2012-11-21T12:00:00Z\"," +
" \"capacity\":4," +
" \"price\":10," +
" \"from\": { " +
" \"description\":null," +
" \"city\": {" +
" \"name\":\"Montreal\"" +
" }" +
" }," +
" \"to\":{" +
" \"description\":\"24 rue de la ville\"," +
" \"city\":{" +
" \"name\":\"Sherbrooke\"" +
" }" +
" }," +
" \"driver\":{" +
" \"first_name\": \"Benjamin\"," +
" \"image\":\"https://graph.facebook.com/693607843/picture? type=large\"" +
" }" +
" }" +
" ]}";
public static void main( String[] args )
{
Gson gson = new Gson();
Response response = gson.fromJson( jsonString, Response.class );
System.out.println( gson.toJson( response ) );
}
public class Response
{
#SerializedName("lifts")
List<Lift> lifts;
}
class Lift
{
#SerializedName("id")
int id;
#SerializedName("time")
String time;
#SerializedName("capacity")
int capacity;
#SerializedName("price")
float price;
#SerializedName("from")
Address from;
#SerializedName("to")
Address to;
#SerializedName("driver")
Driver driver;
}
class Address
{
#SerializedName("description")
String description;
#SerializedName("city")
City city;
}
class City
{
#SerializedName("name")
String name;
}
class Driver
{
#SerializedName("first_name")
String firstName;
#SerializedName("image")
String image;
}
}

Using GSON in Android to parse a complex JSON object

I'm relatively new to Java programming and need to parse a complex JSON object across the wire. I've been reading documentation on GSON the past day and Haven't had much luck being able to fully parse this type of structure:
{
'Events' : [{
'name' : 'exp',
'date' : '10-10-2010',
'tags' : ["tag 1", "tag2", "tag3"]
},...more events...],
'Contacts' : [{
'name' : 'John Smith',
'date' : '10-10-2010',
'tags' : ["tag 1", "tag2", "tag3"]
},...more contacts...],
}
I've been able to get it to work similarly to this question but can't figure out how to get that additional array level to work.
The correct way to do it using GSON in the format I'm looking for is:
//somewhere after the web response:
Gson gson = new Gson();
Event[] events = gson.fromJson(webServiceResponse, Event[].class);
//somewhere nested in the class:
static class Event{
String name;
String date;
public String getName()
{
return name;
}
public String getDate()
{
return date;
}
public void setName(String name)
{
this.name = name;
}
public void setDate(String date)
{
this.date = date;
}
}
Java has his own JSON parser: we can use it on Android as well.
Below you can find how you can get all events from your String.
String TAG = "JSON EXAMPLE";
String jsonString = "{\"Events\" : [{\"name\" : \"exp\",\"date\" : \"10-10-2010\",\"tags\" : [\"tag 1\",\"tag 2\",\"tag 3\"]}],\"Contacts\" : [{\"name\" : \"John Smith\",\"date\" : \"10-10-2010\",\"tags\" : [\"tag 1\",\"tag 2\",\"tag 3\"]}]}";
try {
JSONObject jsonObj = new JSONObject(jsonString); // create a json object from a string
JSONArray jsonEvents = jsonObj.getJSONArray("Events"); // get all events as json objects from Events array
for(int i = 0; i < jsonEvents.length(); i++){
JSONObject event = jsonEvents.getJSONObject(i); // create a single event jsonObject
Log.e(TAG, "Event name:" + event.getString("name") + " date: " + event.getString("date"));
JSONArray eventTags = event.getJSONArray("tags");
for(int j = 0; j < eventTags.length(); j++){
Log.e(TAG, "Event tag: " + eventTags.getString(j));
}
}
} catch (JSONException e) {
e.printStackTrace();
}
Be aware: Your JSON Object(from your question) will throw a exception because it is not valid( I'm not sure, but it look like a javascript object). You have to add some quotes to each property(key) and ecape them with \ (\").
This tool is really nice to test if a JSON String is valid or not.

Categories

Resources