Quiero analizar una cadena en un objeto JSON interna (o equivalente) en Java. Las bibliotecas habituales, Gson
y Jackson
, son demasiado lentos para mis necesidades (> 100US para cada cuerda a JSON de análisis, de acuerdo con mis puntos de referencia). Sé que hay bibliotecas ligeramente más rápido, pero mirando a los puntos de referencia en línea, las ganancias disponibles serán pequeñas (menos de un orden de magnitud de mejora).
Si sé que el formato de la JSON con antelación, ¿hay alguna manera de analizarlo mucho más rápido? Por ejemplo, sé que la cadena será un JSON del formato:
{
"A" : 1.0 ,
"B" : "X"
}
es decir, sé que las dos claves serán "A" y "B", y los valores será un doble y una cadena, respectivamente. Teniendo en cuenta este conocimiento avanzado del formato, es que hay una biblioteca o algún enfoque para analizar el JSON mucho más rápido de lo habitual?
Si usted conoce a una JSON
estructura de carga útil se puede utilizar Streaming API
para leer los datos. Creé 4 métodos diferentes para leer dada JSON
la carga útil:
- Por defecto Gson - utilización
Gson
de clases. - Adaptador Gson - el uso
JsonReader
de la biblioteca Gson. - Por defecto Jackson - el uso
ObjectMapper
de Jackson. - Jackson en streaming API - utilización
JsonParser
de clases.
Para que sea comparable todos estos métodos lleve JSON
carga útil como String
y retorno Pojo
objeto que representa A
y B
propiedades. Siguiente gráfico representa las diferencias:
Como se puede notar, Jackson
's Streaming API
es la manera más rápida de deserialise su JSON
carga útil de estos 4 enfoques.
Para generar gráfico anterior, a continuación se utilizaron los datos:
1113 547 540 546 544 552 547 549 547 548 Promedio 603,3
940 455 452 456 465 459 457 458 455 455 Promedio 505,2
422 266 257 262 260 267 259 262 257 259 Promedio 277,1
202 186 184 189 185 188 182 186 187 183 Promedio 187,2
el código de prueba:
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;
public class JsonApp {
private static final String json = "{\"A\" : 1.0 ,\"B\" : \"X\"}";
private static final int MAX = 1_000_000;
private static List<List<Duration>> values = new ArrayList<>();
static {
IntStream.range(0, 4).forEach(i -> values.add(new ArrayList<>()));
}
public static void main(String[] args) throws Exception {
for (int i = 0; i < 10; i++) {
int v = 0;
values.get(v++).add(defaultGson());
values.get(v++).add(gsonAdapter());
values.get(v++).add(defaultJackson());
values.get(v).add(jacksonJsonFactory());
}
values.forEach(list -> {
list.forEach(d -> System.out.print(d.toMillis() + " "));
System.out.println(" avg " + list.stream()
.mapToLong(Duration::toMillis)
.average().getAsDouble());
});
}
static Duration defaultGson() {
Gson gson = new Gson();
long start = System.nanoTime();
for (int i = MAX; i > 0; i--) {
gson.fromJson(json, Pojo.class);
}
return Duration.ofNanos(System.nanoTime() - start);
}
static Duration gsonAdapter() throws IOException {
PojoTypeAdapter adapter = new PojoTypeAdapter();
long start = System.nanoTime();
for (int i = MAX; i > 0; i--) {
adapter.fromJson(json);
}
return Duration.ofNanos(System.nanoTime() - start);
}
static Duration defaultJackson() throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
long start = System.nanoTime();
for (int i = MAX; i > 0; i--) {
mapper.readValue(json, Pojo.class);
}
return Duration.ofNanos(System.nanoTime() - start);
}
static Duration jacksonJsonFactory() throws IOException {
JsonFactory jfactory = new JsonFactory();
long start = System.nanoTime();
for (int i = MAX; i > 0; i--) {
readPartially(jfactory);
}
return Duration.ofNanos(System.nanoTime() - start);
}
static Pojo readPartially(JsonFactory jfactory) throws IOException {
try (JsonParser parser = jfactory.createParser(json)) {
Pojo pojo = new Pojo();
parser.nextToken(); // skip START_OBJECT - {
parser.nextToken(); // skip A name
parser.nextToken();
pojo.A = parser.getDoubleValue();
parser.nextToken(); // skip B name
parser.nextToken();
pojo.B = parser.getValueAsString();
return pojo;
}
}
}
class PojoTypeAdapter extends TypeAdapter<Pojo> {
@Override
public void write(JsonWriter out, Pojo value) {
throw new IllegalStateException("Implement me!");
}
@Override
public Pojo read(JsonReader in) throws IOException {
if (in.peek() == com.google.gson.stream.JsonToken.NULL) {
in.nextNull();
return null;
}
Pojo pojo = new Pojo();
in.beginObject();
in.nextName();
pojo.A = in.nextDouble();
in.nextName();
pojo.B = in.nextString();
return pojo;
}
}
class Pojo {
double A;
String B;
@Override
public String toString() {
return "Pojo{" +
"A=" + A +
", B='" + B + '\'' +
'}';
}
}
Nota: si necesita datos muy precisos tratan de crear pruebas comparativas realizadas con excelente JMH paquete.