Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Changing from Gson to Jackson for JSON parsing

Im currently using GSON to parse a pretty big JSONfile using an inputstream/reader. The parsing takes about 35 seconds on my android device, i understood from some benchmark test that Jackson performance is way better. But i can't find out how to parse my JSON file using jackson. Can anyone help me?

My JSON looks like this:

[

    {
        "venue": { … }
    },
    {
        "venue": {
            "venue_seasons": [ … ],
            "address": "the address",
            "city": "the city",
            "name": "the name",
            "created_at": "2011-05-31T07:55:33Z",
            "latitude": 00.000000,
            "country": "the country",
            "internal_link_en": null,
            "internal_link_nl": null,
            "updated_at": "2011-09-15T14:46:09Z",
            "zipcode": "the zipcode",
            "foursquare_link": "foursquare url",
            "url": null,
            "id": 3,
            "tip": "some tip",
            "uid": "4ab5e205f964a520317620e3",
            "phone": "phonenr",
            "recommended": null,
            "website": "someurl",
            "venue_photos": [ … ],  //array containing objects with urls of images
            "description": null,
            "longitude": 00.000000,
            "thumbnail_location": null,
            "subcategories": [ … ],
            "opening_en": null,
            "opening_nl": null,
            "hidden": false,
            "twitter": "thetwitteraccount",
            "themes": [ … ]
        }
    }, //more venues
]

My GSON code looks like this, it works:

AssetManager assetManager = getAssets();
InputStream inputStream = null;
try {
    inputStream = assetManager.open("filename.json");
} catch (IOException e) {
    Log.e("tag", e.getMessage());
}

Reader reader = new InputStreamReader(inputStream);

Gson gson = new Gson();
List<JsonResponse> venueList = gson.fromJson(reader, new TypeToken<List<JsonResponse>>() {}.getType());
JsonResponse naam = venueList.get(12);
String denaam = naam.venue.getName;
Log.i("nr12",denaam);
Log.i("timetracker","stop" ); 

// just some logging to test if the parser works 
for (JsonResponse venue : venueList) {
    String tijdel = String.valueOf(venue.venue.id);
    Log.i(venuetag,"name of venue"+ tijdel+ " is: " + venue.venue.getName);
}

...

class JsonResponse
{
    Venues venue;
}

class Venues
{
    public List<VenueSeasons> venue_seasons;

    public List<VenuePhotos> venue_photos;

    public List<SubCategories> subcategories;

    public List<Themes> themes;

    @SerializedName("address")
    public String getAdress;

    @SerializedName("city")
    public String getCity;

    @SerializedName("country")
    public String getCountry;

    @SerializedName("name")
    public String getName;

    @SerializedName("created_at")
    public Date getCreatedAt;

    @SerializedName("updated_at")
    public Date getUpdatedAt;

    @SerializedName("internal_link_nl")
    public String getInternalLinkNl;

    @SerializedName("internal_link_en")
    public String getInternalLinkEN;

    @SerializedName("latitude")
    public Double getLatitude;

    @SerializedName("longitude")
    public Double getLongitude;

    @SerializedName("foursquare_link")
    public String getFoursquareLink;

    @SerializedName("url")
    public String getURL;

    @SerializedName("phone")
    public String getPhone;

    @SerializedName("zipcode")
    public String getZipCode;

    public String tip;

    public String tip_en;

    public String uid;

    public int id;

    @SerializedName("website")
    public String getWebsite;

    @SerializedName("recommended")
    public Boolean getRecommended;

    @SerializedName("description")
    public String getDescription;

    @SerializedName("hidden")
    public Boolean getHidden;

    @SerializedName("opening_en")
    public String getOpeningEN;

    @SerializedName("opening_nl")
    public String getOpeningNL;

    @SerializedName("twitter")
    public String getTwitter;

    @SerializedName("thumbnail_location")
    public String getThumbnailLocation;
}


public class VenuePhotos 
{
    @SerializedName("large")
    public String getLargePhotoURL;

    @SerializedName("medium")
    public String getMediumPhotoURL;

    @SerializedName("small")
    public String getSmallPhotoURL; 

    @SerializedName("original")
    public String getOriginalPhotoURL;

    public String uid;

    public int id;

    public int venue_id;

    public boolean selected;

    @SerializedName("created_at")
    public Date getCreatedAt;

    @SerializedName("updated_at")
    public Date getUpdatedAt;
}

Now this works. I did some stuff with the data and after parsing it works all awesome, but I think it sucks the startup of my app takes so long.

My Jackson code (the failing one) is:

AssetManager assetManager = getAssets();
InputStream inputStream = null;
try {
    inputStream = assetManager.open("originalDelftJson.json");
} catch (IOException e) {
    Log.e("tag", e.getMessage());
}
Reader reader = new InputStreamReader(inputStream);

ObjectMapper objectMapper = new ObjectMapper();
JsonFactory jsonFactory = new JsonFactory();
JJsonResponse response = null;
try {
    JsonParser jp = jsonFactory.createJsonParser(reader);
    response = objectMapper.readValue(jp, JJsonResponse.class);
    String test = String.valueOf(response.venue.size());

With classes:

public class JJsonResponse
{
     public List<JVenue> venue;
}

class Venues
{
    public List<VenueSeasons> venue_seasons;

    public List<VenuePhotos> venue_photos;

    public List<SubCategories> subcategories;

    public List<Themes> themes;

    @SerializedName("address")
    public String getAdress;

    @SerializedName("city")
    public String getCity;

    @SerializedName("country")
    public String getCountry;

    @SerializedName("name")
    public String getName;

    @SerializedName("created_at")
    public Date getCreatedAt;

    @SerializedName("updated_at")
    public Date getUpdatedAt;

    @SerializedName("internal_link_nl")
    public String getInternalLinkNl;

    @SerializedName("internal_link_en")
    public String getInternalLinkEN;

    @SerializedName("latitude")
    public Double getLatitude;

    @SerializedName("longitude")
    public Double getLongitude;

    @SerializedName("foursquare_link")
    public String getFoursquareLink;

    @SerializedName("url")
    public String getURL;

    @SerializedName("phone")
    public String getPhone;

    @SerializedName("zipcode")
    public String getZipCode;

    public String tip;

    public String tip_en;

    public String uid;

    public int id;

    @SerializedName("website")
    public String getWebsite;

    @SerializedName("recommended")
    public Boolean getRecommended;

    @SerializedName("description")
    public String getDescription;

    @SerializedName("hidden")
    public Boolean getHidden;

    @SerializedName("opening_en")
    public String getOpeningEN;

    @SerializedName("opening_nl")
    public String getOpeningNL;

    @SerializedName("twitter")
    public String getTwitter;

    @SerializedName("thumbnail_location")
    public String getThumbnailLocation;
}

public class JVenue
{
    public String name;
    public int id;
    public String city;
    public String address;
    public String country;
    public String internal_link_nl;
    public String internal_link_en;
    public String zipcode;
    public String foursquare_link;
    public String tip_en;
    public String url;
    public Date created_at;
    public Date updated_at;
    public float latitude;
    public float longitude;
    public String tip;
    public String uid;
    public String phone;
    public String recommended;
    public String website;
    public String description;
    public String thumbnail_location;
    public boolean hidden;
    public String twitter;
    public String opening_en;
    public String opening_nl;
}

I think I am pretty close, but im doing something wrong because i get the error: org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of com.jacksonrecipes.testapp.model.JJsonResponse out of START_ARRAY token

I don't really get how Jackson works and how to implement it. Does anyone know how to change my Jackson implementation in my Android code so it will work and I can access the data?

edit: got my solution

With help of the answer of MH. i could find it. I now use:

List<JJsonResponse> venueCounter = objectMapper.readValue(inputStream, new TypeReference<List<JJsonResponse>>() { });
like image 546
Jasper Avatar asked Jul 08 '12 18:07

Jasper


2 Answers

Following is a working example using Jackson, with the JSON in the original question (corrected to be valid and to match the Java data structure), with the Java data structure from the original Gson example (corrected to use Jackson's @JsonProperty instead of Gson's @SerializedName).

import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.ANY;
import static com.fasterxml.jackson.annotation.PropertyAccessor.FIELD;

import java.io.File;
import java.util.Date;
import java.util.List;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

public class JacksonFoo
{
  public static void main(String[] args) throws Exception
  {
    ObjectMapper mapper = new ObjectMapper().setVisibility(FIELD, ANY);
    List<JsonResponse> responses = mapper.readValue(new File("input.json"), new TypeReference<List<JsonResponse>>() {});
    System.out.println(responses);
    System.out.println(mapper.writeValueAsString(responses));
  }
}

class JsonResponse
{
  Venues venue;

  @Override
  public String toString()
  {
    return venue.toString();
  }
}

class Venues
{
  List<VenueSeasons> venue_seasons;
  @JsonProperty("address") String getAdress;
  @JsonProperty("city") String getCity;
  @JsonProperty("name") String getName;
  @JsonProperty("created_at") Date getCreatedAt;
  @JsonProperty("latitude") Double getLatitude;
  @JsonProperty("country") String getCountry;
  @JsonProperty("internal_link_en") String getInternalLinkEN;
  @JsonProperty("internal_link_nl") String getInternalLinkNl;
  @JsonProperty("updated_at") Date getUpdatedAt;
  @JsonProperty("zipcode") String getZipCode;
  @JsonProperty("foursquare_link") String getFoursquareLink;
  @JsonProperty("url") String getURL;
  int id;
  String tip;
  String uid;
  @JsonProperty("phone") String getPhone;
  @JsonProperty("recommended") Boolean getRecommended;
  @JsonProperty("website") String getWebsite;
  List<VenuePhotos> venue_photos;
  @JsonProperty("description") String getDescription;
  @JsonProperty("longitude") Double getLongitude;
  @JsonProperty("thumbnail_location") String getThumbnailLocation;
  List<SubCategories> subcategories;
  @JsonProperty("opening_en") String getOpeningEN;
  @JsonProperty("opening_nl") String getOpeningNL;
  @JsonProperty("hidden") Boolean getHidden;
  @JsonProperty("twitter") String getTwitter;
  List<Themes> themes;
  String tip_en; // not in example JSON

  @Override
  public String toString()
  {
    return String.format("Venues: id=%d", id);
  }
}

class VenuePhotos
{
  @JsonProperty("large") String getLargePhotoURL;
  @JsonProperty("medium") String getMediumPhotoURL;
  @JsonProperty("small") String getSmallPhotoURL;
  @JsonProperty("original") String getOriginalPhotoURL;
  String uid;
  int id;
  int venue_id;
  boolean selected;
  @JsonProperty("created_at") Date getCreatedAt;
  @JsonProperty("updated_at") Date getUpdatedAt;
}

enum VenueSeasons
{
  Spring, Summer, Fall, Winter
}

enum SubCategories
{
  SubCat1, SubCat2, SubCat3, SubCat4 
}

enum Themes
{
  Theme1, Theme2, Theme3, Theme4
}
like image 115
Programmer Bruce Avatar answered Nov 04 '22 19:11

Programmer Bruce


One VERY IMPORTANT thing to keep in mind is that you always want to reuse ObjectMapper instances -- creating one per request slows things down a lot, possibly by an order of magnitude. Usually one either just creates a single static instance (configured right after being created), or injects one via DI framework like Guice.

Other performance best practice with Jackson can be found from: http://wiki.fasterxml.com/JacksonBestPracticesPerformance

like image 30
StaxMan Avatar answered Nov 04 '22 19:11

StaxMan