需求:项目有个表(数据被其他服务实时更新)要解析里面一个字段,字段里面存储的 json 格式的字符串。
难点:研究发现,json 字符串内部,结构单一,但 json 内部包含数据量达到50多万,普通的json 解析方式肯定不是,会内存溢出的,只有用相应库中的流方式。
实现:这里不赘述其他实现,只把最核心的解析表中一行数据中那个存储 json 字段的解析过程的代码记录来下。
pom.xml
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
解析代码:
package test;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONReader;
import lombok.Data;
import java.io.CharArrayReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Test<O extends Keyable<String>> {
/**
* @param jsonStr
* @param clazz
* @return
* @apiNote 将 jsonStr 转换为 Entity 的方法。显然解析出来的数据可能包含多条
* @rule 既然是多条,也可能包含重复记录,默认不去重;
* 如果需要去重请重写 内部的 filterRedundenceInJson 方法
*/
private List<O> transfer2DTO(String jsonStr, Class<O> clazz) {
//json string -> dto
JSONObject root = null;
try{
root = JSON.parseObject(jsonStr);
}catch (OutOfMemoryError e){
System.err.println("内存溢出");
//采用流式 API
return this.transfer2DTOByStreamAPI(jsonStr,clazz);
}
if (root==null||!root.containsKey("tableModels")) {
return null;
}
JSONArray arr = root.getJSONArray("tableModels");
List<O> olist = arr.stream().map(e -> {
JSONObject e1 = ((JSONObject) e);
return e1.toJavaObject(clazz);
}).collect(Collectors.toList());
//去重
return this.filterRedundenceInJson(olist);
}
/**
* @param olist
* @return
* @apiNote 在相同 json 记录下的多余元素
* @rule 既然是多条,也可能包含重复记录,默认按照 O 的 pkVal() 去重;
* 如果需要去重请重写 内部的 filterRedundenceInJson 方法
*/
protected List<O> filterRedundenceInJson(List<O> olist) {
Map<String, O> map = new HashMap<>();
olist.forEach(o -> {
/**
* 当前的去重逻辑是:保留第一条记录
*/
if (!map.containsKey(o.getKey())) {
map.put(o.getKey(), o);
}
});
List<O> r = map.values().stream().collect(Collectors.toList());
return r;
}
private List<O> transfer2DTOByStreamAPI(String jsonStr, Class<O> clazz) {
List<O> olist = new ArrayList<>();
JSONReader jsonReader = new JSONReader(new CharArrayReader(jsonStr.toCharArray()));
jsonReader.startObject();
Object key = jsonReader.readObject();
/**
* key 对应 的 value,
* 由于我们需要进一步解析 value 内部细节,故不能直接简单获取其内容
*/
// Object value = jsonReader.readObject();
//进一步识别其为数组
jsonReader.startArray();
//遍历数组里面的内容
while (jsonReader.hasNext()){
//按整个对象进行读取
O o = jsonReader.readObject(clazz);
olist.add(o);
}
jsonReader.endArray();
jsonReader.endObject();
jsonReader.close();
return olist;
}
public static void main(String[] args) {
String jsonStr = "{\"tableModels\":[{\"id\":\"1\",\"code\":\"111\",\"name\":\"jiangxu\"},{\"id\":\"1\",\"code\":\"111\",\"name\":\"jiangxu\"}]}";
List<KeyValue> olist = new Test().transfer2DTO(jsonStr,KeyValue.class);
olist.forEach(e->System.out.println(e));
}
@Data
public static class KeyValue implements Keyable<String> {
private String id;
private String code;
private String name;
@Override
public String getKey() {
return this.getId();
}
}
}
package test;
import lombok.Data;
@Data
public class KeyValue implements Keyable<String> {
private String id;
private String code;
private String name;
@Override
public String getKey() {
return this.getId();
}
}
package test;
public interface Keyable<K extends Comparable<K>>{
K getKey();
}
说明:这里的 json 数据形如:
{"tableModels":[{"id":"1","code":"j1","name":"jiang"},{...},{...},{...},....50w条]