只解析一个大 JSON 字符串中的一个字段
Parse only one field in a large JSON string
我有一个 JSON 字符串,格式如下:
{
"foo": "small_vale"
"baz": "large_value"
"bar": "another_large_value"
}
如何在忽略其余字段的同时有效地提取 foo
?
基本上,我使用的是 Gson,我定义了一个 "lean class" 这样的:
MyClass {
private String foo;
}
如果我确保 foo
首先出现在 JSON 字符串中,Gson 是否仍会扫描整个字符串,或者它是否足够聪明以停止?
我应该改用 JsonPath 吗?
您的 json 文件无效。缺少逗号。它应该是这样的:
{
"foo":"small_value",
"baz":"large_value",
"bar":"another_large_value"
}
This blog post 说 Jackson 或简单的 JSON 是解析大 json 数据的最快方法。请参阅第 "Big File Results" 章以供参考。
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());
要回答这个问题,我们需要了解您如何解析 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);
}
在方法 returns 值之前,它检查是否整个 JSON
被消耗,如果没有,则抛出 JsonIOException
。 Gson
在内部使用给定类型的 TypeAdapter
实现。对于您的自定义 MyClass
,它将使用 ReflectiveTypeAdapterFactory.Adapter
class,这将消耗整个 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.
我有一个 JSON 字符串,格式如下:
{
"foo": "small_vale"
"baz": "large_value"
"bar": "another_large_value"
}
如何在忽略其余字段的同时有效地提取 foo
?
基本上,我使用的是 Gson,我定义了一个 "lean class" 这样的:
MyClass {
private String foo;
}
如果我确保 foo
首先出现在 JSON 字符串中,Gson 是否仍会扫描整个字符串,或者它是否足够聪明以停止?
我应该改用 JsonPath 吗?
您的 json 文件无效。缺少逗号。它应该是这样的:
{
"foo":"small_value",
"baz":"large_value",
"bar":"another_large_value"
}
This blog post 说 Jackson 或简单的 JSON 是解析大 json 数据的最快方法。请参阅第 "Big File Results" 章以供参考。
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());
要回答这个问题,我们需要了解您如何解析 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);
}
在方法 returns 值之前,它检查是否整个 JSON
被消耗,如果没有,则抛出 JsonIOException
。 Gson
在内部使用给定类型的 TypeAdapter
实现。对于您的自定义 MyClass
,它将使用 ReflectiveTypeAdapterFactory.Adapter
class,这将消耗整个 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.