0

0

使用Gson解析和映射NDJSON文件中的多个记录

聖光之護

聖光之護

发布时间:2025-12-02 18:02:01

|

958人浏览过

|

来源于php中文网

原创

使用gson解析和映射ndjson文件中的多个记录

本文详细介绍了如何使用Gson库在Java中高效解析和映射NDJSON(Newline Delimited JSON)文件中的多个JSON记录。针对传统Gson `fromJson`方法仅能读取首个记录的问题,教程提出了基于`JsonReader`和`peek()`方法的迭代解析方案,并提供了完整的Java代码示例,确保能够成功读取并反序列化文件中所有独立的JSON对象。

在处理包含多个独立JSON对象的文本文件时,NDJSON(Newline Delimited JSON)是一种常见格式,其中每个JSON对象占据一行。然而,当尝试使用Gson库解析这类文件时,开发者常会遇到只能成功读取第一个JSON记录的问题。这是因为Gson的fromJson方法在默认情况下会将整个输入流视为一个完整的JSON结构,一旦解析完第一个顶级JSON对象,就会认为输入结束。

为了解决这一问题,我们需要利用Gson提供的JsonReader类进行迭代式解析,逐个读取文件中的JSON对象。

理解NDJSON格式与传统JSON解析的差异

NDJSON文件中的每个记录都是一个独立的、完整的JSON对象,它们之间通过换行符分隔。例如:

{"id": 1, "name": "Alice"}
{"id": 2, "name": "Bob"}
{"id": 3, "name": "Charlie"}

如果直接使用 gson.fromJson(reader, MyObject.class),Gson会读取第一个 {"id": 1, "name": "Alice"} 并成功映射,但接下来的内容会被忽略,因为Gson认为文件已经解析完毕。

解决方案:使用JsonReader进行迭代解析

要正确解析NDJSON文件中的所有记录,需要结合 JsonReader 和循环结构。JsonReader 提供了对JSON流更细粒度的控制。

Nanonets
Nanonets

基于AI的自学习OCR文档处理,自动捕获文档数据

下载

核心步骤

  1. 初始化 JsonReader: 从文件或输入流创建 JsonReader 实例。
  2. 设置宽松模式 (setLenient(true)): NDJSON文件中的每个JSON对象之间通常没有逗号分隔,JsonReader 默认情况下会期望一个单一的JSON文档或由逗号分隔的JSON数组。为了让 JsonReader 能够容忍这种非严格的JSON格式(即多个顶级JSON对象),需要将其设置为宽松模式。
  3. 迭代读取: 使用循环结构,结合 reader.peek() 方法来判断是否到达文件末尾。在每次循环中,调用 gson.fromJson(reader, YourDTO.class) 读取一个JSON对象,并将其添加到列表中。

示例代码

假设我们有一个 CustomerFeedDTO 类,用于映射NDJSON文件中的每个客户记录:

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

// 客户数据传输对象 (DTO)
class CustomerFeedDTO {
    private Map<String, ?> profile;
    private Map<String, ?> phone;
    private ArrayList<?> addresses;
    private Map<String, ?> orders;
    private ArrayList<?> customs;

    // 构造函数、Getter和Setter方法(此处省略,但实际应用中需要)
    public Map<String, ?> getProfile() { return profile; }
    public void setProfile(Map<String, ?> profile) { this.profile = profile; }

    public Map<String, ?> getPhone() { return phone; }
    public void setPhone(Map<String, ?> phone) { this.phone = phone; }

    public ArrayList<?> getAddresses() { return addresses; }
    public void setAddresses(ArrayList<?> addresses) { this.addresses = addresses; }

    public Map<String, ?> getOrders() { return orders; }
    public void setOrders(Map<String, ?> orders) { this.orders = orders; }

    public ArrayList<?> getCustoms() { return customs; }
    public void setCustoms(ArrayList<?> customs) { this.customs = customs; }

    @Override
    public String toString() {
        return "CustomerFeedDTO{" +
               "profile=" + profile +
               ", phone=" + phone +
               ", addresses=" + addresses +
               ", orders=" + orders +
               ", customs=" + customs +
               '}';
    }
}

使用上述 CustomerFeedDTO 和 NDJSON 文件,以下代码演示了如何解析所有记录:

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

import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class NdJsonParser {

    public static void main(String[] args) {
        List<CustomerFeedDTO> customerFeedDTOs = new ArrayList<>();
        Gson gson = new Gson();

        // 假设 customer.json 是你的 NDJSON 文件
        try (FileReader fileReader = new FileReader("customer.json");
             JsonReader reader = new JsonReader(fileReader)) {

            // 必须设置为宽松模式,以处理多个顶级JSON对象
            reader.setLenient(true);

            // 循环读取,直到文档结束
            while (reader.peek() != JsonToken.END_DOCUMENT) {
                CustomerFeedDTO customerFeedDTO = gson.fromJson(reader, CustomerFeedDTO.class);
                customerFeedDTOs.add(customerFeedDTO);
            }

            // 打印所有解析出的客户记录
            for (int i = 0; i < customerFeedDTOs.size(); i++) {
                System.out.println("Record " + (i + 1) + ": " + customerFeedDTOs.get(i));
            }

        } catch (IOException e) {
            System.err.println("读取文件时发生错误: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

reader.peek() 与 reader.hasNext() 的选择

在循环条件中,我们使用了 reader.peek() != JsonToken.END_DOCUMENT。peek() 方法返回下一个令牌的类型,而不会实际消耗它。当 peek() 返回 JsonToken.END_DOCUMENT 时,表示已经到达了JSON文档的末尾。

虽然 JsonReader 也有 hasNext() 方法,但它在NDJSON的场景下可能导致异常。hasNext() 主要用于检查JSON数组或对象中是否还有更多元素,当它尝试读取非预期的令牌(例如,当文档末尾没有更多结构化的JSON元素时)可能会抛出 IllegalStateException。因此,对于NDJSON这种由多个独立JSON对象组成的流,使用 peek() == JsonToken.END_DOCUMENT 来判断循环终止条件更为稳健和推荐。

注意事项与最佳实践

  1. 资源管理: 始终使用 try-with-resources 语句来确保 FileReader 和 JsonReader 在使用完毕后能够正确关闭,避免资源泄露。
  2. 错误处理: 在实际应用中,应添加更详细的异常处理逻辑,例如捕获 JsonSyntaxException 来处理格式错误的JSON记录。
  3. DTO设计: 示例中的 CustomerFeedDTO 使用了 Map<String, ?> 和 ArrayList<?> 来处理未知或动态的JSON结构。在已知JSON结构的情况下,建议使用具体的Java类型(如 String, Integer, 自定义对象等)来代替 Map 和 ArrayList<?>,以提高类型安全性和代码可读性。例如,profile 可以定义为一个具体的 ProfileDTO 类。
  4. 性能考虑: 对于非常大的NDJSON文件,将所有记录一次性加载到 List 中可能会消耗大量内存。在这种情况下,可以考虑逐条处理记录,或者使用流式API(如Java 8 Stream API)结合自定义的迭代器来处理数据,避免一次性加载所有数据。

总结

通过 JsonReader 的迭代解析机制,结合 reader.setLenient(true) 和 reader.peek() != JsonToken.END_DOCUMENT 作为循环条件,可以有效地解析和映射NDJSON文件中的所有JSON记录。这种方法提供了对JSON流的精细控制,是处理多行独立JSON数据时的标准和推荐实践。正确理解和应用这些技术,能够确保在Java应用程序中可靠地处理NDJSON数据。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
json数据格式
json数据格式

JSON是一种轻量级的数据交换格式。本专题为大家带来json数据格式相关文章,帮助大家解决问题。

457

2023.08.07

json是什么
json是什么

JSON是一种轻量级的数据交换格式,具有简洁、易读、跨平台和语言的特点,JSON数据是通过键值对的方式进行组织,其中键是字符串,值可以是字符串、数值、布尔值、数组、对象或者null,在Web开发、数据交换和配置文件等方面得到广泛应用。本专题为大家提供json相关的文章、下载、课程内容,供大家免费下载体验。

549

2023.08.23

jquery怎么操作json
jquery怎么操作json

操作的方法有:1、“$.parseJSON(jsonString)”2、“$.getJSON(url, data, success)”;3、“$.each(obj, callback)”;4、“$.ajax()”。更多jquery怎么操作json的详细内容,可以访问本专题下面的文章。

337

2023.10.13

go语言处理json数据方法
go语言处理json数据方法

本专题整合了go语言中处理json数据方法,阅读专题下面的文章了解更多详细内容。

82

2025.09.10

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

1031

2023.08.02

class在c语言中的意思
class在c语言中的意思

在C语言中,"class" 是一个关键字,用于定义一个类。想了解更多class的相关内容,可以阅读本专题下面的文章。

891

2024.01.03

python中class的含义
python中class的含义

本专题整合了python中class的相关内容,阅读专题下面的文章了解更多详细内容。

32

2025.12.06

golang map内存释放
golang map内存释放

本专题整合了golang map内存相关教程,阅读专题下面的文章了解更多相关内容。

77

2025.09.05

Python异步编程与Asyncio高并发应用实践
Python异步编程与Asyncio高并发应用实践

本专题围绕 Python 异步编程模型展开,深入讲解 Asyncio 框架的核心原理与应用实践。内容包括事件循环机制、协程任务调度、异步 IO 处理以及并发任务管理策略。通过构建高并发网络请求与异步数据处理案例,帮助开发者掌握 Python 在高并发场景中的高效开发方法,并提升系统资源利用率与整体运行性能。

37

2026.03.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 4.4万人学习

C# 教程
C# 教程

共94课时 | 11.2万人学习

Java 教程
Java 教程

共578课时 | 81.4万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号