|
/* |
|
* 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; |
|
} |
|
} |
|
} |