Rework JsonParserImpl to not depend anymore on jackson databind

This commit is contained in:
Julien Viet
2019-08-31 13:15:49 +02:00
parent 14293eda3e
commit 9c2a6accdf
3 changed files with 113 additions and 54 deletions

View File

@@ -12,16 +12,13 @@
package io.vertx.core.parsetools.impl;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.util.TokenBuffer;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.json.DecodeException;
import io.vertx.core.json.Json;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.core.json.impl.JacksonCodec;
import io.vertx.core.parsetools.JsonEvent;
import io.vertx.core.parsetools.JsonEventType;
import io.vertx.core.spi.json.JsonCodec;
import java.time.Instant;
import java.util.Base64;
@@ -36,17 +33,11 @@ public class JsonEventImpl implements JsonEvent {
private final JsonEventType type;
private final String field;
private final Object value;
private final TokenBuffer buffer;
public JsonEventImpl(JsonEventType type, String field, Object value) {
this(type, field, value, null);
}
public JsonEventImpl(JsonEventType type, String field, Object value, TokenBuffer buffer) {
this.type = type;
this.field = field;
this.value = value;
this.buffer = buffer;
}
@Override
@@ -96,27 +87,19 @@ public class JsonEventImpl implements JsonEvent {
@Override
public <T> T mapTo(Class<T> type) {
if (buffer != null) {
try {
return Json.mapper.readValue(buffer.asParser(), type);
} catch (Exception e) {
throw new DecodeException(e.getMessage());
}
} else {
return JsonCodec.INSTANCE.fromValue(value, type);
try {
return JacksonCodec.INSTANCE.fromValue(value, type);
} catch (Exception e) {
throw new DecodeException(e.getMessage(), e);
}
}
@Override
public <T> T mapTo(TypeReference<T> type) {
if (buffer != null) {
try {
return Json.mapper.readValue(buffer.asParser(), type);
} catch (Exception e) {
throw new DecodeException(e.getMessage());
}
} else {
try {
return JacksonCodec.fromValue(value, type);
} catch (Exception e) {
throw new DecodeException(e.getMessage(), e);
}
}

View File

@@ -13,8 +13,10 @@ package io.vertx.core.parsetools.impl;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.core.base.ParserBase;
import com.fasterxml.jackson.core.io.IOContext;
import com.fasterxml.jackson.core.json.async.NonBlockingJsonParser;
import com.fasterxml.jackson.databind.util.TokenBuffer;
import io.vertx.core.Handler;
import io.vertx.core.VertxException;
import io.vertx.core.buffer.Buffer;
@@ -29,6 +31,7 @@ import io.vertx.core.parsetools.JsonParser;
import io.vertx.core.streams.ReadStream;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.List;
import java.util.Map;
@@ -279,7 +282,7 @@ public class JsonParserImpl implements JsonParser {
if (objectHandler == null) {
BufferingHandler handler = new BufferingHandler();
handler.handler = buffer -> {
handleEvent(new JsonEventImpl(JsonEventType.VALUE, currentField, new JsonObject(handler.convert(Map.class)), handler.buffer));
handleEvent(new JsonEventImpl(JsonEventType.VALUE, currentField, new JsonObject(handler.convert(Map.class))));
};
objectHandler = handler;
}
@@ -300,18 +303,93 @@ public class JsonParserImpl implements JsonParser {
if (arrayHandler == null) {
BufferingHandler handler = new BufferingHandler();
handler.handler = buffer -> {
handleEvent(new JsonEventImpl(JsonEventType.VALUE, currentField, new JsonArray(handler.convert(List.class)), handler.buffer));
handleEvent(new JsonEventImpl(JsonEventType.VALUE, currentField, new JsonArray(handler.convert(List.class))));
};
arrayHandler = handler;
}
return this;
}
/**
* A parser implementation that feeds from a list of tokens instead of bytes.
*/
private static class TokenParser extends ParserBase {
private ArrayDeque<Object> tokens = new ArrayDeque<>();
private String text;
private TokenParser(IOContext ctxt, int features) {
super(ctxt, features);
}
@Override
public JsonToken nextToken() throws IOException {
if (tokens.isEmpty()) {
return JsonToken.NOT_AVAILABLE;
}
text = null;
_numTypesValid = NR_UNKNOWN;
_numberLong = 0L;
_numberDouble = 0L;
_currToken = (JsonToken) tokens.removeFirst();
if (_currToken == JsonToken.FIELD_NAME) {
String field = (String) tokens.removeFirst();
_parsingContext.setCurrentName(field);
text = field;
} else if (_currToken == JsonToken.VALUE_NUMBER_INT) {
Long v = (Long) tokens.removeFirst();
_numTypesValid = NR_LONG;
_numberLong = v;
} else if (_currToken == JsonToken.VALUE_NUMBER_FLOAT) {
Double v = (Double) tokens.removeFirst();
_numTypesValid = NR_DOUBLE;
_numberDouble = v;
} else if (_currToken == JsonToken.VALUE_STRING) {
text = (String) tokens.removeFirst();
}
return _currToken;
}
@Override
public String getText() {
return text;
}
@Override
public char[] getTextCharacters() {
throw new UnsupportedOperationException();
}
@Override
public int getTextLength() {
throw new UnsupportedOperationException();
}
@Override
public int getTextOffset() {
throw new UnsupportedOperationException();
}
@Override
public ObjectCodec getCodec() {
throw new UnsupportedOperationException();
}
@Override
public void setCodec(ObjectCodec c) {
throw new UnsupportedOperationException();
}
@Override
protected void _closeInput() {
}
}
private class BufferingHandler implements Handler<JsonToken> {
Handler<Void> handler;
int depth;
TokenBuffer buffer;
TokenParser buffer;
@Override
public void handle(JsonToken event) {
@@ -320,46 +398,40 @@ public class JsonParserImpl implements JsonParser {
case START_OBJECT:
case START_ARRAY:
if (depth++ == 0) {
buffer = new TokenBuffer(Json.mapper, false);
}
if (event == JsonToken.START_OBJECT) {
buffer.writeStartObject();
} else {
buffer.writeStartArray();
JsonFactory factory = new JsonFactory();
buffer = new TokenParser(new IOContext(factory._getBufferRecycler(), this, true), com.fasterxml.jackson.core.JsonParser.Feature.collectDefaults());
}
buffer.tokens.add(event);
break;
case FIELD_NAME:
buffer.writeFieldName(parser.getCurrentName());
buffer.tokens.add(event);
buffer.tokens.add(parser.currentName());
break;
case VALUE_NUMBER_INT:
buffer.writeNumber(parser.getLongValue());
buffer.tokens.add(event);
buffer.tokens.add(parser.getLongValue());
break;
case VALUE_NUMBER_FLOAT:
buffer.writeNumber(parser.getDoubleValue());
buffer.tokens.add(event);
buffer.tokens.add(parser.getDoubleValue());
break;
case VALUE_STRING:
buffer.writeString(parser.getText());
break;
case VALUE_TRUE:
buffer.writeBoolean(true);
buffer.tokens.add(event);
buffer.tokens.add(parser.getText());
break;
case VALUE_FALSE:
buffer.writeBoolean(false);
break;
case VALUE_TRUE:
case VALUE_NULL:
buffer.writeNull();
buffer.tokens.add(event);
break;
case END_OBJECT:
case END_ARRAY:
if (event == JsonToken.END_OBJECT) {
buffer.writeEndObject();
} else {
buffer.writeEndArray();
}
buffer.tokens.add(event);
if (--depth == 0) {
tokenHandler = JsonParserImpl.this::handleToken;
buffer.flush();
handler.handle(null);
buffer.close();
buffer = null;
}
break;
default:
@@ -373,9 +445,9 @@ public class JsonParserImpl implements JsonParser {
<T> T convert(Class<T> type) {
try {
return Json.mapper.readValue(buffer.asParser(), type);
return Json.mapper.readValue(buffer, type);
} catch (Exception e) {
throw new DecodeException(e.getMessage());
throw new DecodeException(e.getMessage(), e);
}
}
}

View File

@@ -584,7 +584,9 @@ public class JsonParserTest {
JsonParser parser = JsonParser.newParser();
List<Object> values = new ArrayList<>();
parser.arrayValueMode();
parser.handler(event -> values.add(event.mapTo(LinkedList.class)));
parser.handler(event -> {
values.add(event.mapTo(LinkedList.class));
});
parser.handle(new JsonArray().add(0).add(1).add(2).toBuffer());
assertEquals(Collections.singletonList(Arrays.asList(0L, 1L, 2L)), values);
assertEquals(LinkedList.class, values.get(0).getClass());
@@ -598,7 +600,9 @@ public class JsonParserTest {
assertEquals(Collections.emptyList(), values);
assertEquals(1, errors.size());
try {
JsonParser.newParser().arrayValueMode().handler(event -> values.add(event.mapTo(TheObject.class))).write(Buffer.buffer("[]")).end();
JsonParser.newParser().arrayValueMode().handler(event -> {
values.add(event.mapTo(TheObject.class));
}).write(Buffer.buffer("[]")).end();
fail();
} catch (DecodeException expected) {
}