Skip to content

Instantly share code, notes, and snippets.

@casidiablo
Last active December 14, 2015 10:39
Show Gist options
  • Save casidiablo/5073496 to your computer and use it in GitHub Desktop.
Save casidiablo/5073496 to your computer and use it in GitHub Desktop.
Json Parse Benchmark

Correctness and Performance benchmark for various Java-based JSON libraries

Run the tests

You have to install vogar in order to run the tests againts an Android phone. Once you have set it up, you can run the tests like this:

vogar --benchmark \
  --classpath ParseBenchmarkData.zip \
  --classpath gson-2.2.2.jar \
  --classpath jackson-core-asl-1.9.12.jar \
  --classpath jackson-mapper-asl-1.9.12.jar \
  --classpath automated-inflater.jar \
  JsonParseBenchmark.java

Which parser is fastest on Android?

Here's a quick overview from the implementor of Android's two JSON parsers:

  • org.json The API requires you to load the entire document into a String. This is somewhat cumbersome to use and inefficient for large documents. But it's a DOM model so you can get a lot done with very little code.
  • Jackson is efficient and fully-featured. Jackson has a capable but large API. It accomplishes great efficiency by going to some extremes, like implementing its own floating point parsing and charset decoding. If you use Jackson, you may want to use ProGuard to shrink its 222 KiB jar.
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.caliper.Param;
import com.google.caliper.Runner;
import com.google.caliper.SimpleBenchmark;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;
import com.telly.api.inflate.AutomatedInflater;
import com.telly.api.inflate.JsonKey;
import org.json.JSONObject;
import org.json.JSONArray;
import java.io.*;
import java.lang.Class;
import java.lang.Exception;
import java.lang.Object;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
/**
* Measure Gson and Jackson parsing and binding performance.
* <p/>
* <p>This benchmark requires that ParseBenchmarkData.zip is on the classpath.
* That file contains Twitter feed data, which is representative of what
* applications will be parsing.
*/
public final class JsonParseBenchmark extends SimpleBenchmark {
@Param
Document document;
@Param
Api api;
private enum Document {
TWEETS(Constants.TWEET_LIST_TOKEN, Constants.TWEET_LIST_REFERENCE, Tweet.class, true),
READER_SHORT(Constants.FEED_TOKEN, Constants.FEED_TYPE_REFERENCE, Feed.class, false),
READER_LONG(Constants.FEED_TOKEN, Constants.FEED_TYPE_REFERENCE, Feed.class, false);
private final Type gsonType;
private final TypeReference<?> jacksonType;
private final Class<?> automatedClass;
private final boolean list;
private Document(TypeToken<?> typeToken, TypeReference<?> typeReference, Class<?> automatedClass, boolean list) {
this.gsonType = typeToken.getType();
this.jacksonType = typeReference;
this.automatedClass = automatedClass;
this.list = list;
}
private static class Constants {
public static final TypeToken<Feed> FEED_TOKEN = new TypeToken<Feed>() {
};
public static final TypeReference<Feed> FEED_TYPE_REFERENCE = new TypeReference<Feed>() {
};
public static final TypeToken<List<Tweet>> TWEET_LIST_TOKEN = new TypeToken<List<Tweet>>() {
};
public static final TypeReference<List<Tweet>> TWEET_LIST_REFERENCE = new TypeReference<List<Tweet>>() {
};
}
}
private enum Api {
JACKSON_BIND {
@Override
Parser newParser() {
return new JacksonBindParser();
}
},
GSON_BIND {
@Override
Parser newParser() {
return new GsonBindParser();
}
},
AUTOMATED_BIND {
@Override
Parser newParser() {
return new AutomatedInflaterParser();
}
};
abstract Parser newParser();
}
private char[] text;
private Parser parser;
@Override
protected void setUp() throws Exception {
text = resourceToString("/" + document.name() + ".json").toCharArray();
parser = api.newParser();
}
public void timeParse(int reps) throws Exception {
for (int i = 0; i < reps; i++) {
parser.parse(text, document);
}
}
private static String resourceToString(String path) throws Exception {
InputStream in = JsonParseBenchmark.class.getResourceAsStream(path);
if (in == null) {
throw new IllegalArgumentException("No such file: " + path);
}
Reader reader = new InputStreamReader(in, "UTF-8");
char[] buffer = new char[8192];
StringWriter writer = new StringWriter();
int count;
while ((count = reader.read(buffer)) != -1) {
writer.write(buffer, 0, count);
}
reader.close();
return writer.toString();
}
public static void main(String[] args) throws Exception {
Runner.main(JsonParseBenchmark.class, args);
}
interface Parser {
void parse(char[] data, Document document) throws Exception;
}
private static class GsonBindParser implements Parser {
private static Gson gson = new GsonBuilder()
.setDateFormat("EEE MMM dd HH:mm:ss Z yyyy")
.create();
public void parse(char[] data, Document document) throws Exception {
gson.fromJson(new CharArrayReader(data), document.gsonType);
}
}
private static class JacksonBindParser implements Parser {
private static ObjectMapper mapper = new ObjectMapper();
static {
mapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(DeserializationConfig.Feature.AUTO_DETECT_FIELDS, true);
mapper.setDateFormat(new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy"));
}
public void parse(char[] data, Document document) throws Exception {
mapper.readValue(new CharArrayReader(data), document.jacksonType);
}
}
private static class AutomatedInflaterParser implements Parser {
public void parse(char[] data, Document document) {
try {
if (document.list) {
JSONArray jsonResponse = new JSONArray(new String(data));
AutomatedInflater.getBeanList(jsonResponse, document.automatedClass);
} else {
JSONObject jsonResponse = new JSONObject(new String(data));
AutomatedInflater.getBean(document.automatedClass, jsonResponse);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
static class Tweet {
@JsonProperty
String coordinates;
@JsonProperty
boolean favorited;
@JsonProperty
Date created_at;
@JsonProperty
boolean truncated;
@JsonProperty
Tweet retweeted_status;
@JsonProperty
String id_str;
@JsonProperty
String in_reply_to_id_str;
@JsonProperty
String contributors;
@JsonProperty
String text;
@JsonProperty
long id;
@JsonProperty
String retweet_count;
@JsonProperty
String in_reply_to_status_id_str;
@JsonProperty
Object geo;
@JsonProperty
boolean retweeted;
@JsonProperty
String in_reply_to_user_id;
@JsonProperty
String in_reply_to_screen_name;
@JsonProperty
Object place;
@JsonProperty
User user;
@JsonProperty
String source;
@JsonProperty
String in_reply_to_user_id_str;
}
static class User {
@JsonProperty
String name;
@JsonProperty
String profile_sidebar_border_color;
@JsonProperty
boolean profile_background_tile;
@JsonProperty
String profile_sidebar_fill_color;
@JsonProperty
Date created_at;
@JsonProperty
String location;
@JsonProperty
String profile_image_url;
@JsonProperty
boolean follow_request_sent;
@JsonProperty
String profile_link_color;
@JsonProperty
boolean is_translator;
@JsonProperty
String id_str;
@JsonProperty
int favourites_count;
@JsonProperty
boolean contributors_enabled;
@JsonProperty
String url;
@JsonProperty
boolean default_profile;
@JsonProperty
long utc_offset;
@JsonProperty
long id;
@JsonProperty
boolean profile_use_background_image;
@JsonProperty
int listed_count;
@JsonProperty
String lang;
@JsonProperty("protected")
@SerializedName("protected")
boolean isProtected;
@JsonProperty
int followers_count;
@JsonProperty
String profile_text_color;
@JsonProperty
String profile_background_color;
@JsonProperty
String time_zone;
@JsonProperty
String description;
@JsonProperty
boolean notifications;
@JsonProperty
boolean geo_enabled;
@JsonProperty
boolean verified;
@JsonProperty
String profile_background_image_url;
@JsonProperty
boolean defalut_profile_image;
@JsonProperty
int friends_count;
@JsonProperty
int statuses_count;
@JsonProperty
String screen_name;
@JsonProperty
boolean following;
@JsonProperty
boolean show_all_inline_media;
}
static class Feed {
@JsonProperty
String id;
@JsonProperty
String title;
@JsonProperty
String description;
@JsonProperty("alternate")
@SerializedName("alternate")
List<Link> alternates;
@JsonProperty
long updated;
@JsonProperty
List<Item> items;
@Override
public String toString() {
StringBuilder result = new StringBuilder()
.append(id)
.append("\n").append(title)
.append("\n").append(description)
.append("\n").append(alternates)
.append("\n").append(updated);
int i = 1;
for (Item item : items) {
result.append(i++).append(": ").append(item).append("\n\n");
}
return result.toString();
}
}
static class Link {
@JsonProperty
String href;
@Override
public String toString() {
return href;
}
}
static class Item {
@JsonProperty
List<String> categories;
@JsonProperty
String title;
@JsonProperty
long published;
@JsonProperty
long updated;
@JsonProperty("alternate")
@SerializedName("alternate")
List<Link> alternates;
@JsonProperty
Content content;
@JsonProperty
String author;
@JsonProperty
List<ReaderUser> likingUsers;
@Override
public String toString() {
return title
+ "\nauthor: " + author
+ "\npublished: " + published
+ "\nupdated: " + updated
+ "\n" + content
+ "\nliking users: " + likingUsers
+ "\nalternates: " + alternates
+ "\ncategories: " + categories;
}
}
static class Content {
@JsonProperty
String content;
@Override
public String toString() {
return content;
}
}
static class ReaderUser {
@JsonProperty
String userId;
@Override
public String toString() {
return userId;
}
}
}
This file has been truncated, but you can view the full file.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment