已知格式时从String解析JSON的最快方法 [英] Fastest way to parse JSON from String when format is known
问题描述
我想将String解析为Java中的内部JSON对象(或等效对象).普通的库Gson
和Jackson
太慢了,无法满足我的需要(根据我的基准测试,每个要Json解析的String> 100us).我知道有一些更快的库,但是在线查看基准测试,可获得的收益将很小(不到一个数量级的提高).
I want to parse a String into an internal JSON object (or equivalent) in Java. The usual libraries, Gson
and Jackson
, are way too slow for my needs (> 100us for each String to Json parse, according to my benchmarks). I know there are slightly faster libraries, but looking at the benchmarks online, the gains available will be small (less than an order of magnitude improvement).
如果我事先知道JSON的格式,有什么方法可以更快地解析它?例如,我知道字符串将是以下格式的JSON:
If I know the format of the JSON in advance, is there a way I can parse it much faster? For example, I know the String will be a JSON of the format:
{
"A" : 1.0 ,
"B" : "X"
}
也就是说,我知道两个键分别是"A"和"B",其值分别是双精度型和字符串.有了这种格式的高级知识,是否有一个库或某种方法可以比平常更快地解析JSON?
i.e., I know the two keys will be "A" and "B", and the values will be a double and a string, respectively. Given this advanced knowledge of the format, is there a library or some approach to parse the JSON much faster than usual?
推荐答案
如果知道JSON
有效负载结构,则可以使用Streaming API
读取数据.我创建了4种不同的方法来读取给定的JSON
有效负载:
If you know a JSON
payload structure you can use Streaming API
to read data. I created 4 different methods to read given JSON
payload:
- 默认Gson-使用
Gson
类. - Gson适配器-使用Gson库中的
JsonReader
. - 默认杰克逊-使用杰克逊的
ObjectMapper
. - Jackson流式API-使用
JsonParser
类.
- Default Gson - use
Gson
class. - Gson Adapter - use
JsonReader
from Gson library. - Default Jackson - use
ObjectMapper
from Jackson. - Jackson streaming API - use
JsonParser
class.
为使它们具有可比性,所有这些方法都将JSON
有效负载作为String
,并返回表示A
和B
属性的Pojo
对象.下图表示差异:
To make it comparable all these methods take JSON
payload as String
and return Pojo
object which represents A
and B
properties. Below graph represents differences:
您会注意到,Jackson
的Streaming API
是从这4种方法反序列化JSON
有效负载的最快方法.
As you can notice, Jackson
's Streaming API
is the fastest way to deserialise your JSON
payload from these 4 approaches.
要生成上方图表,请使用以下数据:
To generate above graph, below data were used:
1113 547 540 546 544 552 547 549 547 548平均值603.3
940455452456465459457458455455平均505.2
422266257262260260267259262257259平均277.1
202186184189189185188182186187183183平均187.2
1113 547 540 546 544 552 547 549 547 548 avg 603.3
940 455 452 456 465 459 457 458 455 455 avg 505.2
422 266 257 262 260 267 259 262 257 259 avg 277.1
202 186 184 189 185 188 182 186 187 183 avg 187.2
基准代码:
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 + '\'' +
'}';
}
}
注意:如果您需要非常精确的数据,请尝试使用出色的 JMH 包.
Note: if you need really precise data try to create benchmark tests using excellent JMH package.
这篇关于已知格式时从String解析JSON的最快方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!