已知格式时从String解析JSON的最快方法 [英] Fastest way to parse JSON from String when format is known

查看:92
本文介绍了已知格式时从String解析JSON的最快方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想将String解析为Java中的内部JSON对象(或等效对象).普通的库GsonJackson太慢了,无法满足我的需要(根据我的基准测试,每个要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:

  1. 默认Gson-使用Gson类.
  2. Gson适配器-使用Gson库中的JsonReader.
  3. 默认杰克逊-使用杰克逊的ObjectMapper.
  4. Jackson流式API-使用JsonParser类.
  1. Default Gson - use Gson class.
  2. Gson Adapter - use JsonReader from Gson library.
  3. Default Jackson - use ObjectMapper from Jackson.
  4. Jackson streaming API - use JsonParser class.

为使它们具有可比性,所有这些方法都将JSON有效负载作为String,并返回表示AB属性的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:

您会注意到,JacksonStreaming 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆