使用Gson以流式序列化/反序列化超级大体积的JSON数据

某些情况下,JSON体积可能非常大,例如,10个GB。这种情况下。不论是序列化还是反序列化,都不适合把完整JSON数加载到内存进行处理。这时就可以考虑使用Gson 提供的2个Api,可以以流式解析/序列化JSON数据

  • JsonWriter
  • JsonReader

JsonWriter

部分核心方法如下

// 构造函数,指定输出目的地
public JsonWriter(Writer out) 

// 开始一个数组
public JsonWriter beginArray() throws IOException
// 结束一个数组
public JsonWriter endArray() throws IOException
// 开始一个对象
public JsonWriter beginObject() throws IOException
// 结束一个对象
public JsonWriter endObject() throws IOException
// 输出一个key
public JsonWriter name(String name) throws IOException
// 输出一个value
public JsonWriter value(String value) throws IOException
// 输出一个null value
public JsonWriter nullValue() throws IOException
// 输出各个类型的value
public JsonWriter value(boolean value) throws IOException
public JsonWriter value(Boolean value) throws IOException
public JsonWriter value(double value) throws IOException 
public JsonWriter value(long value) throws IOException
public JsonWriter value(Number value) throws IOException

// 刷出
public void flush() throws IOException
// 关闭
public void close() throws IOException

很好理解,指定一个Out目的地,可以是磁盘,网络。然后就调用它的各个方法去“组装”JSON元素。JSON元素无非就是:对象,数组,KEY,VALUE,NULL。上面方法都有提供

JsonReader

核心方法如下

// 构造函数,指定Reader
public JsonReader(Reader in)

// 开始读取数组
public void beginArray() throws IOException
// 数组读取完毕
public void endArray() throws IOException
// 开始读取对象
public void beginObject() throws IOException
// 对象读取完毕
public void endObject() throws IOException
// 当前对象/数组是否还有下一个元素
public boolean hasNext() throws IOException
// 获取下一个“节点”类型
// 节点类型可能是:对象开始/对象结束/数组开始/数组结束/KEY/VALUE/JSON结束/...
public JsonToken peek() throws IOException
// 获取下一个KEY名称
public String nextName() throws IOException
// 获取下一个String VALUE
public String nextString() throws IOException
//获取下一个Boolean VALUE
public boolean nextBoolean() throws IOException
//获取下一个NULL VALUE
public void nextNull() throws IOException
//获取下一个Double VALUE
public double nextDouble() throws IOException
//获取下一个Long VALUE
public long nextLong() throws IOException
//获取下一个Int VALUE
public int nextInt() throws IOException
//跳过下一个Value
public void skipValue() throws IOException

一个Demo

把一个包含了1W个json对象的数组,先输出到磁盘。然后解析输出到控制台。

json的格式

{
  "users": [
    {
      "name": "ee434ba4-6c37-424b-b93e-952829b49fe4"
    },
   //  ....这个数组有1W个对象
  ]
}
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.UUID;

import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;

public class MainTest {

	public static void main(String[] args) throws Exception {
		write();
		read();
	}

	public static void write() throws Exception {
		try (BufferedWriter writer = Files.newBufferedWriter(Paths.get("D:\\sobig.json"), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {

			try (JsonWriter jsonWriter = new JsonWriter(writer)) {

				// 设置缩进,提高可读性
				jsonWriter.setIndent("  ");

				jsonWriter.beginObject(); // 输出:{
				jsonWriter.name("users"); // 输出:"name"

				jsonWriter.beginArray(); // 输出:[
				for (int i = 0; i < 10000; i++) {
					jsonWriter.beginObject(); // 输出:{
					jsonWriter.name("name"); // 输出:"name"
					jsonWriter.value(UUID.randomUUID().toString()); // 输出:"{uuid}"
					jsonWriter.endObject(); // 输出:}
				}

				jsonWriter.endArray(); // 输出:]

				jsonWriter.endObject(); // 输出:}
			}
		}
	}

	public static void read() throws Exception {
		try (BufferedReader reader = Files.newBufferedReader(Paths.get("D:\\sobig.json"))) {
			try (JsonReader jsonReader = new JsonReader(reader)) {
				while (true) {
					JsonToken jsonToken = jsonReader.peek();
					if (jsonToken == JsonToken.BEGIN_OBJECT) {
						System.out.println("开始解析对象");
						jsonReader.beginObject();
					} else if (jsonToken == JsonToken.END_OBJECT) {
						System.out.println("对象解析完毕");
						jsonReader.endObject();
					} else if (jsonToken == JsonToken.BEGIN_ARRAY) {
						System.out.println("开始解析数组");
						jsonReader.beginArray();
					} else if (jsonToken == JsonToken.END_ARRAY) {
						System.out.println("数组解析完毕");
						jsonReader.endArray();
					} else if (jsonToken == JsonToken.NAME) {
						System.out.println("解析到key:" + jsonReader.nextName());
					} else if (jsonToken == JsonToken.STRING) {
						System.out.println("解析到string value:" + jsonReader.nextString());
					} else if (jsonToken == JsonToken.NUMBER) {
						// jsonReader.nextDouble();
						// jsonReader.nextLong();
						System.out.println("解析到number vallue:" + jsonReader.nextInt());
					} else if (jsonToken == JsonToken.BOOLEAN) {
						System.out.println("解析到bool value:" + jsonReader.nextBoolean());
					} else if (jsonToken == JsonToken.NULL) {
						jsonReader.nextNull(); // void
						System.out.println("解析到null value: null");
					} else if (jsonToken == JsonToken.END_DOCUMENT) {
						System.out.println("JSON解析完毕");
						break;
					}
				}
			}
		}
	}
}

控制台输出:

开始解析对象
解析到key:users
开始解析数组
开始解析对象
解析到key:name
解析到string value:ee434ba4-6c37-424b-b93e-952829b49fe4
对象解析完毕

.... 解析出了1W个对象数据

数组解析完毕
对象解析完毕
JSON解析完毕

1 个赞

学会了