Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Faster json parsing #1980

Merged
merged 10 commits into from
May 17, 2017
4 changes: 2 additions & 2 deletions src/main/java/io/vertx/core/buffer/impl/BufferImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,12 @@ public String toString(Charset enc) {

@Override
public JsonObject toJsonObject() {
return new JsonObject(toString());
return new JsonObject(this);
}

@Override
public JsonArray toJsonArray() {
return new JsonArray(toString());
return new JsonArray(this);
}

public byte getByte(int pos) {
Expand Down
82 changes: 82 additions & 0 deletions src/main/java/io/vertx/core/json/Json.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.module.SimpleModule;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufOutputStream;
import io.vertx.core.buffer.Buffer;

import java.io.IOException;
import java.math.BigDecimal;
Expand Down Expand Up @@ -61,6 +64,13 @@ public class Json {
prettyMapper.registerModule(module);
}

/**
* Encode a POJO to JSON using the underlying Jackson mapper.
*
* @param obj a POJO
* @return a String containing the JSON representation of the given POJO.
* @throws EncodeException if a property cannot be encoded.
*/
public static String encode(Object obj) throws EncodeException {
try {
return mapper.writeValueAsString(obj);
Expand All @@ -69,6 +79,30 @@ public static String encode(Object obj) throws EncodeException {
}
}

/**
* Encode a POJO to JSON using the underlying Jackson mapper.
*
* @param obj a POJO
* @return a Buffer containing the JSON representation of the given POJO.
* @throws EncodeException if a property cannot be encoded.
*/
public static Buffer encodeToBuffer(Object obj) throws EncodeException {
try {
Buffer buf = Buffer.buffer();
mapper.writeValue(new ByteBufOutputStream(buf.getByteBuf()), obj);
return buf;
} catch (Exception e) {
throw new EncodeException("Failed to encode as JSON: " + e.getMessage());
}
}

/**
* Encode a POJO to JSON with pretty indentation, using the underlying Jackson mapper.
*
* @param obj a POJO
* @return a String containing the JSON representation of the given POJO.
* @throws EncodeException if a property cannot be encoded.
*/
public static String encodePrettily(Object obj) throws EncodeException {
try {
return prettyMapper.writeValueAsString(obj);
Expand All @@ -77,6 +111,14 @@ public static String encodePrettily(Object obj) throws EncodeException {
}
}

/**
* Decode a given JSON string to a POJO of the given class type.
* @param str the JSON string.
* @param clazz the class to map to.
* @param <T> the generic type.
* @return an instance of T
* @throws DecodeException when there is a parsing or invalid mapping.
*/
public static <T> T decodeValue(String str, Class<T> clazz) throws DecodeException {
try {
return mapper.readValue(str, clazz);
Expand All @@ -85,6 +127,14 @@ public static <T> T decodeValue(String str, Class<T> clazz) throws DecodeExcepti
}
}

/**
* Decode a given JSON string to a POJO of the given type.
* @param str the JSON string.
* @param type the type to map to.
* @param <T> the generic type.
* @return an instance of T
* @throws DecodeException when there is a parsing or invalid mapping.
*/
public static <T> T decodeValue(String str, TypeReference<T> type) throws DecodeException {
try {
return mapper.readValue(str, type);
Expand All @@ -93,6 +143,38 @@ public static <T> T decodeValue(String str, TypeReference<T> type) throws Decode
}
}

/**
* Decode a given JSON buffer to a POJO of the given class type.
* @param buf the JSON buffer.
* @param type the type to map to.
* @param <T> the generic type.
* @return an instance of T
* @throws DecodeException when there is a parsing or invalid mapping.
*/
public static <T> T decodeValue(Buffer buf, TypeReference<T> type) throws DecodeException {
try {
return mapper.readValue(new ByteBufInputStream(buf.getByteBuf()), type);
} catch (Exception e) {
throw new DecodeException("Failed to decode:" + e.getMessage(), e);
}
}

/**
* Decode a given JSON buffer to a POJO of the given class type.
* @param buf the JSON buffer.
* @param clazz the class to map to.
* @param <T> the generic type.
* @return an instance of T
* @throws DecodeException when there is a parsing or invalid mapping.
*/
public static <T> T decodeValue(Buffer buf, Class<T> clazz) throws DecodeException {
try {
return mapper.readValue(new ByteBufInputStream(buf.getByteBuf()), clazz);
} catch (Exception e) {
throw new DecodeException("Failed to decode:" + e.getMessage(), e);
}
}

@SuppressWarnings("unchecked")
static Object checkAndCopy(Object val, boolean copy) {
if (val == null) {
Expand Down
22 changes: 22 additions & 0 deletions src/main/java/io/vertx/core/json/JsonArray.java
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,15 @@ public JsonArray(List list) {
this.list = list;
}

/**
* Create an instance from a Buffer of JSON.
*
* @param buf the buffer of JSON.
*/
public JsonArray(Buffer buf) {
fromBuffer(buf);
}

/**
* Get the String at position {@code pos} in the array,
*
Expand Down Expand Up @@ -544,6 +553,15 @@ public String encode() {
return Json.encode(list);
}

/**
* Encode this JSON object as buffer.
*
* @return the buffer encoding.
*/
public Buffer toBuffer() {
return Json.encodeToBuffer(list);
}

/**
* Encode the JSON array prettily as a string
*
Expand Down Expand Up @@ -641,6 +659,10 @@ private void fromJson(String json) {
list = Json.decodeValue(json, List.class);
}

private void fromBuffer(Buffer buf) {
list = Json.decodeValue(buf, List.class);
}

private class Iter implements Iterator<Object> {

final Iterator<Object> listIter;
Expand Down
26 changes: 24 additions & 2 deletions src/main/java/io/vertx/core/json/JsonObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,19 @@ public JsonObject(Map<String, Object> map) {
this.map = map;
}

/**
* Create an instance from a buffer.
*
* @param buf the buffer to create the instance from.
*/
public JsonObject(Buffer buf) {
fromBuffer(buf);
}

/**
* Create a JsonObject from the fields of a Java object.
* Faster than calling `new JsonObject(Json.encode(obj))`.
*
*
* @param obj
* The object to convert to a JsonObject.
* @throws IllegalArgumentException
Expand All @@ -86,7 +95,7 @@ public static JsonObject mapFrom(Object obj) {
/**
* Instantiate a Java object from a JsonObject.
* Faster than calling `Json.decodeValue(Json.encode(jsonObject), type)`.
*
*
* @param type
* The type to instantiate from the JsonObject.
* @throws IllegalArgumentException
Expand Down Expand Up @@ -763,6 +772,15 @@ public String encodePrettily() {
return Json.encodePrettily(map);
}

/**
* Encode this JSON object as buffer.
*
* @return the buffer encoding.
*/
public Buffer toBuffer() {
return Json.encodeToBuffer(map);
}

/**
* Copy the JSON object
*
Expand Down Expand Up @@ -932,6 +950,10 @@ private void fromJson(String json) {
map = Json.decodeValue(json, Map.class);
}

private void fromBuffer(Buffer buf) {
map = Json.decodeValue(buf, Map.class);
}

private class Iter implements Iterator<Map.Entry<String, Object>> {

final Iterator<Map.Entry<String, Object>> mapIter;
Expand Down
30 changes: 29 additions & 1 deletion src/test/java/io/vertx/test/core/JsonArrayTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ public void testGetInstant() {

@Test
public void testGetJsonObject() {
JsonObject obj = new JsonObject().put("foo", "bar");
JsonObject obj = new JsonObject().put("foo", "bar");
jsonArray.add(obj);
assertEquals(obj, jsonArray.getJsonObject(0));
try {
Expand Down Expand Up @@ -846,6 +846,25 @@ public void testEncode() throws Exception {
assertEquals(expected, json);
}

@Test
public void testEncodeToBuffer() throws Exception {
jsonArray.add("foo");
jsonArray.add(123);
jsonArray.add(1234l);
jsonArray.add(1.23f);
jsonArray.add(2.34d);
jsonArray.add(true);
byte[] bytes = TestUtils.randomByteArray(10);
jsonArray.add(bytes);
jsonArray.addNull();
jsonArray.add(new JsonObject().put("foo", "bar"));
jsonArray.add(new JsonArray().add("foo").add(123));
String strBytes = Base64.getEncoder().encodeToString(bytes);
Buffer expected = Buffer.buffer("[\"foo\",123,1234,1.23,2.34,true,\"" + strBytes + "\",null,{\"foo\":\"bar\"},[\"foo\",123]]", "UTF-8");
Buffer json = jsonArray.toBuffer();
assertArrayEquals(expected.getBytes(), json.getBytes());
}

@Test
public void testDecode() {
byte[] bytes = TestUtils.randomByteArray(10);
Expand Down Expand Up @@ -1027,6 +1046,15 @@ public void testCreateFromListNestedList() {
assertSame(list2, arr2.getList());
}

@Test
public void testCreateFromBuffer() {
JsonArray excepted = new JsonArray();
excepted.add("foobar");
excepted.add(123);
Buffer buf = Buffer.buffer(excepted.encode());
assertEquals(excepted, new JsonArray(buf));
}

@Test
public void testClusterSerializable() {
jsonArray.add("foo").add(123);
Expand Down
17 changes: 16 additions & 1 deletion src/test/java/io/vertx/test/core/JsonMapperTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.json.Json;
import org.junit.Test;

Expand Down Expand Up @@ -91,14 +92,28 @@ public void encodeCustomTypeBinaryNull() {
assertEquals("null", json);
}

@Test
public void encodeToBuffer() {
Buffer json = Json.encodeToBuffer("Hello World!");
assertNotNull(json);
// json strings are always UTF8
assertEquals("\"Hello World!\"", json.toString("UTF-8"));
}

@Test
public void testGenericDecoding() {
Pojo original = new Pojo();
original.value = "test";

String json = Json.encode(Collections.singletonList(original));
List<Pojo> correct;

correct = Json.decodeValue(json, new TypeReference<List<Pojo>>() {});
assertTrue(((List)correct).get(0) instanceof Pojo);
assertEquals(original.value, correct.get(0).value);

List<Pojo> correct = Json.decodeValue(json, new TypeReference<List<Pojo>>() {});
// same must apply if instead of string we use a buffer
correct = Json.decodeValue(Buffer.buffer(json, "UTF8"), new TypeReference<List<Pojo>>() {});
assertTrue(((List)correct).get(0) instanceof Pojo);
assertEquals(original.value, correct.get(0).value);

Expand Down
34 changes: 34 additions & 0 deletions src/test/java/io/vertx/test/core/JsonObjectTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -1210,6 +1210,31 @@ public void testEncode() throws Exception {
assertEquals(expected, json);
}

@Test
public void testEncodeToBuffer() throws Exception {
jsonObject.put("mystr", "foo");
jsonObject.put("mycharsequence", new StringBuilder("oob"));
jsonObject.put("myint", 123);
jsonObject.put("mylong", 1234l);
jsonObject.put("myfloat", 1.23f);
jsonObject.put("mydouble", 2.34d);
jsonObject.put("myboolean", true);
byte[] bytes = TestUtils.randomByteArray(10);
jsonObject.put("mybinary", bytes);
Instant now = Instant.now();
jsonObject.put("myinstant", now);
jsonObject.putNull("mynull");
jsonObject.put("myobj", new JsonObject().put("foo", "bar"));
jsonObject.put("myarr", new JsonArray().add("foo").add(123));
String strBytes = Base64.getEncoder().encodeToString(bytes);

Buffer expected = Buffer.buffer("{\"mystr\":\"foo\",\"mycharsequence\":\"oob\",\"myint\":123,\"mylong\":1234,\"myfloat\":1.23,\"mydouble\":2.34,\"" +
"myboolean\":true,\"mybinary\":\"" + strBytes + "\",\"myinstant\":\"" + ISO_INSTANT.format(now) + "\",\"mynull\":null,\"myobj\":{\"foo\":\"bar\"},\"myarr\":[\"foo\",123]}", "UTF-8");

Buffer json = jsonObject.toBuffer();
assertArrayEquals(expected.getBytes(), json.getBytes());
}

@Test
public void testDecode() throws Exception {
byte[] bytes = TestUtils.randomByteArray(10);
Expand Down Expand Up @@ -1509,6 +1534,15 @@ public void testCreateFromMap() {
assertSame(map, obj.getMap());
}

@Test
public void testCreateFromBuffer() {
JsonObject excepted = new JsonObject();
excepted.put("foo", "bar");
excepted.put("quux", 123);
Buffer buf = Buffer.buffer(excepted.encode());
assertEquals(excepted, new JsonObject(buf));
}

@Test
public void testCreateFromMapCharSequence() {
Map<String, Object> map = new HashMap<>();
Expand Down