[杂谈]大型JSON数据切分(Java Jackson)

介绍

  最近遇到一个需求,期望将一个大型的json文件存储至Elasticsearch中,第一反应是期望将原始数据进行拆分,这样就可以在受限的内存空间中完成数据的导入。

思路

  本文使用 JAVA 语言进行切分,并且使用 jackson 组件。
  对 JSON 数据进行处理,首先想到的进行 反序列化 操作,但是这样的话,会将所有数据同时存储在内存中,在受限内存的环境下并不友好,故放弃此方法。
  改为使用 JsonParser 这个更底层的解析类进行数据处理,在设置 -Xmx10M 参数的前提下,能够正常解析 180M 的一个 JSON 文件。

代码

  分为四个例子,进行递进的理解

代码一:使用 JsonParser 解析JSON,根节点是对象。

{
	"name": "alexgaoyh",
	"attrs": [{
			"identifier": "100001",
			"address": ""
		},
		{
			"identifier": "100002",
			"address": ""
		},
		{
			"identifier": "100003",
			"address": "",
			"personal": [{
				"remark": "",
				"name": "alexgaoyh"
			}]
		},
		{
			"identifier": "100004",
			"address": ""
		}
	]
}
    public void processTopObject(String jsonFilePath) {
        File jsonFile = new File(jsonFilePath);
        JsonFactory jsonfactory = new JsonFactory();
        try {
            List<Map<String, Object>> allMapList = new ArrayList<>();
            Map<String, Object> currMap = new HashMap<>();
            JsonParser jsonParser = jsonfactory.createJsonParser(jsonFile);
            JsonToken jsonToken = jsonParser.nextToken();

            // String nameFieldText = null;

            while (jsonToken != JsonToken.END_OBJECT) {
                String fieldname = jsonParser.getCurrentName();
                if ("name".equals(fieldname)) {
                    jsonToken = jsonParser.nextToken();
                    // nameFieldText = jsonParser.getText();
                }
                if ("attrs".equals(fieldname)) {
                    jsonToken = jsonParser.nextToken();
                    if (jsonToken == JsonToken.START_ARRAY) {
                        jsonToken = jsonParser.nextToken();
                        while (jsonToken != JsonToken.END_ARRAY) {
                            String fieldnameDetail = jsonParser.getCurrentName();
                            if ("identifier".equals(fieldnameDetail)) {
                                jsonToken = jsonParser.nextToken();
                                // currMap.put("name", nameFieldText);
                                currMap.put(fieldnameDetail, jsonParser.getText());
                            }
                            // 无用的 personl 处理一下之后跳过
                            if ("personal".equals(fieldnameDetail)) {
                                jsonToken = jsonParser.nextToken();
                                while (jsonParser.nextToken() != JsonToken.END_ARRAY) {
                                }
                            }
                            if (jsonToken == JsonToken.END_OBJECT) {
                                allMapList.add(currMap);
                                currMap = new HashMap<>();
                            }
                            jsonToken = jsonParser.nextToken();
                        }
                    }
                }
                jsonToken = jsonParser.nextToken();
            }
            System.out.println("Total Records Found : " + allMapList.size());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

代码二:使用 JsonParser 解析JSON,根节点是数组

[{
		"identifier": "100001",
		"address": ""
	},
	{
		"identifier": "100002",
		"address": ""
	},
	{
		"identifier": "100003",
		"address": "",
		"personal": [{
			"remark": "",
			"name": "alexgaoyh"
		}]
	},
	{
		"identifier": "100004",
		"address": ""
	}
]
    public void processTopArray(String jsonFilePath) {
        File jsonFile = new File(jsonFilePath);
        JsonFactory jsonfactory = new JsonFactory();
        try {
            List<Map<String, String>> allMapList = new ArrayList<>();
            Map<String, String> currMap = new HashMap<>();
            JsonParser jsonParser = jsonfactory.createJsonParser(jsonFile);
            JsonToken jsonToken = jsonParser.nextToken();
            while (jsonToken != JsonToken.END_ARRAY) {
                String fieldname = jsonParser.getCurrentName();
                if ("identifier".equals(fieldname)) {
                    jsonToken = jsonParser.nextToken();
                    currMap.put(fieldname, jsonParser.getText());
                }
                // 无用的 personl 处理一下之后跳过
                if ("personal".equals(fieldname)) {
                    jsonToken = jsonParser.nextToken();
                    while (jsonParser.nextToken() != JsonToken.END_ARRAY) {
                    }
                }
                if (jsonToken == JsonToken.END_OBJECT) {
                    allMapList.add(currMap);
                    currMap = new HashMap<>();
                }
                jsonToken = jsonParser.nextToken();
            }
            System.out.println("Total Records Found : " + allMapList.size());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

代码三:使用 JsonParser 切分JSON,根节点是数组

   代码三使用的 json 数据同代码二一样,不同之处在于按照对象进行解析,便于后续的json结构切分。

    public void processTopArray(String jsonFilePath) {
        File jsonFile = new File(jsonFilePath);
        try {
            ObjectMapper mapper = new ObjectMapper();
            JsonParser parser = mapper.getFactory().createParser(jsonFile);
            if(parser.nextToken() != JsonToken.START_ARRAY) {
                throw new IllegalStateException("Expected an array");
            }
            while(parser.nextToken() == JsonToken.START_OBJECT) {
                ObjectNode node = mapper.readTree(parser);
                System.out.println(node);
            }

            parser.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

代码四:使用 JsonParser 切分JSON,根节点是数组

   大小为180M的JSON文件,使用互联网上的大型JSON文件,并对其进行解析,为切分做准备
   注意在进行解析的时候,增加了针对 BigDecimal 数据的处理,从而避免了精度丢失。
   按照代码逻辑,features 数组下的数据按照对象进行输出,从而就可以根据切分规则进行切分。
   比如定义一个 List tmpList = new ArrayList<>(); 将 ObjectNode node 对象添加进来,就可以理解为切分后的对象。

    public void parseObject(String jsonFilePath) {
        File jsonFile = new File(jsonFilePath);
        JsonFactory jsonfactory = new JsonFactory();

        ObjectMapper mapper = new ObjectMapper();
        mapper.setNodeFactory(JsonNodeFactory.withExactBigDecimals(true));
        mapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true);
        try {
            JsonParser jsonParser = jsonfactory.createJsonParser(jsonFile);
            JsonToken jsonToken = jsonParser.nextToken();

            while (jsonToken != JsonToken.END_OBJECT) {
                String fieldname = jsonParser.getCurrentName();
                if ("type".equals(fieldname)) {
                    jsonToken = jsonParser.nextToken();
                }
                if ("features".equals(fieldname)) {
                    jsonToken = jsonParser.nextToken();
                    if (jsonToken == JsonToken.START_ARRAY) {

                        while(jsonParser.nextToken() == JsonToken.START_OBJECT) {
                            ObjectNode node = mapper.readTree(jsonParser);
                            //System.out.println(node);
                        }
                    }
                }
                jsonToken = jsonParser.nextToken();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

总结

  将大量数据切分为相同结果的多组小数据,从而可以进行并行处理;

参考

  1. https://stackoverflow.com/questions/24835431/use-jackson-to-stream-parse-an-array-of-json-objects
  2. https://blog.csdn.net/weixin_42338707/article/details/111296652
  3. https://github.com/zemirco/sf-city-lots-json/blob/master/citylots.json
  • 18
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于Java对GPIB接口控制,可以使用NI-VISA库来进行控制。NI-VISA(National Instruments Virtual Instrument Software Architecture)是一种通用的VISA编程接口,可以用来控制各种类型的仪器和设备,包括GPIB、USB、以太网、串口等等。 以下是一个使用NI-VISA库来进行GPIB通信的Java示例代码: ```java import java.util.concurrent.TimeUnit; import com.sun.jna.Native; import com.sun.jna.Pointer; import com.sun.jna.ptr.IntByReference; import visa32.*; public class GPIBControl { private static final int GPIB_ADDRESS = 4; // GPIB地址 private static final int BUFFER_SIZE = 1024; // 缓冲区大小 public static void main(String[] args) { // 初始化NI-VISA库 VisaLibrary visaLibrary = (VisaLibrary) Native.loadLibrary("visa32", VisaLibrary.class); visaLibrary.viOpenDefaultRM(); // 打开GPIB设备 String deviceName = "GPIB0::" + GPIB_ADDRESS + "::INSTR"; Pointer instrument = new Memory(GpibLibrary.ViUInt32.SIZE); visaLibrary.viOpen(visaLibrary.getResourceManager(), deviceName, new GpibLibrary.ViAccessMode(GpibLibrary.VI_NO_LOCK), new GpibLibrary.ViUInt32(0), instrument); Pointer session = instrument.getPointer(0); // 设置超时时间 visaLibrary.viSetAttribute(new GpibLibrary.ViSession(session), new GpibLibrary.ViAttrState(GpibLibrary.VI_TMO_VALUE), new GpibLibrary.ViUInt32(5000)); // 写入命令 String command = "*IDN?"; visaLibrary.viWrite(new GpibLibrary.ViSession(session), command.getBytes(), new GpibLibrary.ViUInt32(command.length()), new IntByReference()); // 读取响应 byte[] buffer = new byte[BUFFER_SIZE]; visaLibrary.viRead(new GpibLibrary.ViSession(session), buffer, new GpibLibrary.ViUInt32(BUFFER_SIZE), new IntByReference()); String response = new String(buffer).trim(); System.out.println("Instrument response: " + response); // 关闭设备 visaLibrary.viClose(new GpibLibrary.ViSession(session)); visaLibrary.viClose(visaLibrary.getResourceManager()); } } ``` 在这个示例,我们使用NI-VISA库来打开GPIB设备,设置超时时间,写入命令并读取响应。需要注意的是,GPIB_ADDRESS变量需要设置为实际的GPIB地址,BUFFER_SIZE变量可以根据需要进行调整。 希望这个示例代码能够对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值