仅解析大JSON字符串中的一个字段

时间:2019-02-24 13:33:34

标签: java json gson jsonpath

我有以下格式的JSON字符串:

{

  "foo": "small_vale"  
  "baz": "large_value"
  "bar": "another_large_value"

}

如何在忽略其余字段的同时有效地提取foo? 基本上,我使用的是Gson,我定义了一个像这样的“精简类”:

MyClass {
  private String foo;
}

如果我确保foo首先出现在JSON字符串中,那么Gson还是会扫描整个字符串,还是足够聪明以至于无法停止?

我应该改用JsonPath吗?

2 个答案:

答案 0 :(得分:1)

您的json文件无效。逗号不见了。它应该看起来像这样:

{
  "foo":"small_value",
  "baz":"large_value",
  "bar":"another_large_value"
}

This blog post说,Jackson或简单的JSON是解析大型JSON数据的最快方法。请参阅“大文件结果”一章以供参考。

杰克逊的示例代码:Jackson JSON – Read Specific JSON Key

它显示了如何解析json文件并获取特定元素的值。

//read json file data to String
byte[] jsonData = Files.readAllBytes(Paths.get("data.json"));

//create ObjectMapper instance
ObjectMapper objectMapper = new ObjectMapper();

//read JSON like DOM Parser
JsonNode rootNode = objectMapper.readTree(jsonData);
JsonNode fooNode = rootNode.path("foo");
System.out.println("foo value = "+fooNode.asText());

答案 1 :(得分:1)

要回答此问题,我们需要查看如何解析JSON。我认为您使用的是最简单的方法:

Test test = gson.fromJson(new FileReader(jsonFile), Test.class);

如果是这种情况,那么您的问题的答案是Gson不够聪明。如果检查此方法的实现,将发现:

public <T> T fromJson(Reader json, Class<T> classOfT) throws JsonSyntaxException, JsonIOException {
    JsonReader jsonReader = newJsonReader(json);
    Object object = fromJson(jsonReader, classOfT);
    assertFullConsumption(object, jsonReader);
    return Primitives.wrap(classOfT).cast(object);
}

在方法返回值之前,它将检查是否消耗了整个JSON,如果没有消耗,则抛出JsonIOExceptionGson在内部将TypeAdapter实现用于给定类型。对于您的自定义MyClass,它将使用ReflectiveTypeAdapterFactory.Adapter类,它将消耗整个JSON负载。为避免这种情况,您可以编写自己的TypeAdapter

class TestTypeAdapter extends TypeAdapter<Test> {

    @Override
    public void write(JsonWriter out, Test value) throws IOException {
        throw new IllegalStateException("Implement me!");
    }

    @Override
    public Test read(JsonReader in) throws IOException {
        if (in.peek() == JsonToken.NULL) {
            in.nextNull();
            return null;
        }

        Test test = new Test();

        try {
            in.beginObject();
            while (in.hasNext()) {
                String name = in.nextName();
                if (name.equals("foo")) {
                    test.setFoo(in.nextString());
                    break;
                }
            }
        } catch (IllegalStateException e) {
            throw new JsonSyntaxException(e);
        }

        return test;
    }
}

简单用法:

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class GsonApp {

    public static void main(String[] args) throws Exception {
        File jsonFile = new File("./resource/test.json").getAbsoluteFile();
        Gson gson = new GsonBuilder().create();

        Test test = gson.fromJson(new FileReader(jsonFile), Test.class);
        Test test1 = new TestTypeAdapter().fromJson(new FileReader(jsonFile));

        System.out.println(test);
        System.out.println(test1);
    }
}

class Test {

    private String foo;

    public String getFoo() {
        return foo;
    }

    public void setFoo(String foo) {
        this.foo = foo;
    }

    @Override
    public String toString() {
        return "Test{" +
                "foo='" + foo + '\'' +
                '}';
    }
}

上面的代码显示:

Test{foo='small_value'}
Test{foo='small_value'}

您可以看到,在两种情况下,我们都解析了small value。您可以测试此代码,并计算TypeAdapter有效负载的自定义JSON的运行速度。

但是,以防万一,您遇到的情况非常复杂,需要解析更多的JSON才能找到自己的价值,请尝试使用JSONPath解决方案。您可以从以下问题开始:how to parse a huge JSON file without loading it in memory