【问题标题】:Parse only one field in a large JSON string仅解析大型 JSON 字符串中的一个字段
【发布时间】:2019-07-18 01:02:04
【问题描述】:

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

{

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

}

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

MyClass {
  private String foo;
}

如果我确保 foo 出现在 JSON 字符串的第一个位置,Gson 是否仍会扫描整个字符串,或者它是否聪明到可以停止?

我应该改用 JsonPath 吗?

【问题讨论】:

  • 简单的方法,在 api 中接受 MyClass。并且你的 MyClass 只添加你想要的。

标签: java json gson jsonpath


【解决方案1】:

您的 json 文件无效。缺少逗号。它应该是这样的:

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

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

Jackson 的示例代码: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());

【讨论】:

    【解决方案2】:

    要回答这个问题,我们需要看看你如何解析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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-05
      • 1970-01-01
      • 1970-01-01
      • 2016-04-25
      • 2018-02-20
      • 2020-05-19
      相关资源
      最近更新 更多