仅解析大JSON字符串中的一个字段 [英] Parse only one field in a large JSON string

查看:451
本文介绍了仅解析大JSON字符串中的一个字段的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

{

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

}

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

MyClass {
  private String foo;
}

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

我应该改用JsonPath吗?

解决方案

要回答此问题,我们需要了解如何解析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,如果没有消耗,则抛出JsonIOException. Gson在内部将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.您可以测试此代码,并计算针对JSON有效负载的自定义TypeAdapter有多快.

但是,如果情况复杂得多,需要解析更多的JSON来找到您的值,请尝试使用JSONPath解决方案.您可以从以下问题开始:如何解析巨大的JSON文件而不将其加载到内存中.

I have a JSON string of the following format:

{

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

}

How can I efficiently extract foo while ignoring the rest of the fields? Basically, I'm using Gson and I defined a "lean class" like that:

MyClass {
  private String foo;
}

If I'm ensuring that foo appears first in the JSON string, would Gson still scan the whole string, or is it smart enough to stop?

Should I use JsonPath instead?

解决方案

To answer on this question we need to see how do you parse JSON. I assume that you are using simplest:

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

If this is your case, answer for your question is Gson is not smart enough to do that. If you check implementation of this method, you will find out:

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);
}

Before method returns value, it checks whether whole JSON was consumed and in case not, JsonIOException is thrown. Gson internally uses TypeAdapter implementation for given type. For your custom MyClass it will use ReflectiveTypeAdapterFactory.Adapter class which will consume the whole JSON payload. To avoid this situation you can write your own 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;
    }
}

Simple usage:

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 + '\'' +
                '}';
    }
}

Above code prints:

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

As, you can see, in both cases we parsed our small value. You can test this code and calculate how faster custom TypeAdapter is for your JSON payload.

But in case, you have much complex situation and you need to parse much more JSON to find your value, try to use JSONPath solution. You can start from this question: how to parse a huge JSON file without loading it in memory.

这篇关于仅解析大JSON字符串中的一个字段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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