讲师中心 微信公众号
AI工具推荐 视频效率加速

使用Gson解析NDJSON文件中的多个JSON记录

夏敏小哥_8732

夏敏小哥_8732

发布时间:2025-12-02 15:19:19

|

985人浏览过

|

来源于php中文网

原创

使用gson解析ndjson文件中的多个json记录

本文详细介绍了如何在Java中利用Gson库解析NDJSON(换行符分隔的JSON)文件,以读取并映射文件中的所有独立JSON记录到对应的DTO对象列表中。针对一次性只能读取一个记录的问题,文章提出了基于JsonReader的循环读取策略,并强调了reader.peek()方法在判断文件末尾时的关键作用,同时提供了完整的示例代码和重要注意事项。

理解NDJSON格式与Gson的初始挑战

NDJSON(Newline Delimited JSON)是一种特殊的数据格式,其中每行都是一个独立的、有效的JSON对象,各行之间通过换行符分隔。与传统的JSON数组不同,NDJSON文件本身并非一个单一的JSON数组,而是多个JSON对象的集合。

当尝试使用Gson库解析NDJSON文件时,常见的初学者误区是直接调用gson.fromJson(reader, YourDTO.class)。例如,对于一个包含多条客户记录的customer.json文件:

// Record # 1
{
 "profile":{
      "salutation":"Mr",
      "title":null,
      "company":null
   },
   "phone":{
      "home_phone":null
   },
   "addresses":[
      {
         "address_id":"1",
         "first_name":"Veronica"
      }
   ],
   "orders":{
      "placed_orders_count":2
   }
}
// Record # 2
{
    "profile":{
      "salutation":null,
      "title":null
   },
   "phone":{
      "home_phone":null
   },
   "addresses":[
      {
         "address_id":"2",
         "title":""
      }
   ],
   "orders":{
      "placed_orders_count":0
   }
}
// ... 更多记录

如果使用以下代码尝试读取:

import com.google.gson.Gson;
import com.google.gson.stream.JsonReader;
import java.io.FileReader;
import java.io.IOException;

// 假设 CustomerFeedDTO 已经定义
// ...

public class InitialNdjsonReaderExample {
    public static void main(String[] args) {
        Gson gson = new Gson();
        try (JsonReader reader = new JsonReader(new FileReader("customer.json"))) {
            // 这种方式只能读取第一个JSON对象
            CustomerFeedDTO customerFeedDTO = gson.fromJson(reader, CustomerFeedDTO.class);
            System.out.println("成功读取第一个记录: " + customerFeedDTO);
            // 后续的记录将无法读取到,因为fromJson方法在读取完一个完整的JSON对象后就会停止。
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

这段代码只会成功解析文件中的第一个JSON对象,因为gson.fromJson()方法在读取完一个完整的JSON对象后就会停止。为了读取所有记录,我们需要一种机制来逐个处理文件中的每个JSON对象。

Expanso json-flatten
Expanso json-flatten

通过 CLI 或 MCP 服务器,使用 Expanso Edge 管道扁平化嵌套 JSON 对象。

下载

解决方案:循环读取与JsonReader

要正确解析NDJSON文件中的所有记录,核心思想是利用JsonReader的流式读取特性,通过循环逐个解析JSON对象。

以下是实现这一目标的详细步骤和示例代码:

  1. 初始化JsonReader和Gson: 创建Gson实例和指向NDJSON文件的JsonReader。
  2. 设置宽松模式: 调用reader.setLenient(true)。这在处理一些非严格符合JSON规范的输入时非常有用,例如可能存在注释或未引用的字段名等。对于NDJSON文件,虽然通常是严格的,但开启此模式可以增加兼容性。
  3. 循环读取: 使用while (reader.peek() != JsonToken.END_DOCUMENT)作为循环条件。
    • reader.peek()方法用于查看下一个令牌的类型,而不会实际消耗它。
    • JsonToken.END_DOCUMENT表示已经到达JSON文档的末尾。通过检查这个令牌,我们可以确保在文件完全读取完毕后安全地退出循环。
  4. 解析并添加: 在循环内部,每次迭代都调用gson.fromJson(reader, CustomerFeedDTO.class)来解析当前行的JSON对象,并将其添加到预先创建的List中。
import com.google.gson.Gson;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken; // 导入 JsonToken
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

// CustomerFeedDTO 类定义 (详见下一节)
class CustomerFeedDTO {
    private Map<String, ?> profile;
    private Map<String, ?> phone;
    private ArrayList<?> addresses;
    private Map<String, ?> orders;
    private ArrayList<?> customs;

    // Getters and setters (省略具体实现,但实际应用中需要)
    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 +
               '}';
    }
}

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

        // 使用 try-with-resources 确保资源自动关闭
        try (JsonReader reader = new JsonReader(new FileReader("customer.json"))) {
            // 启用宽松模式,以处理可能存在的非严格JSON格式
            reader.setLenient(true);

            // 循环读取直到文档结束
            while (reader.peek() != JsonToken.END_DOCUMENT) {
                CustomerFeedDTO customerFeedDTO = gson.fromJson(reader, CustomerFeedDTO.class);
                customerFeedDTOs.add(customerFeedDTO);
                System.out.println("成功读取记录: " + customerFeedDTO); // 打印每条记录以验证
            }

            System.out.println("\n所有记录读取完毕。总计 " + customerFeedDTOs.size() + " 条记录。");

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

DTO类定义

为了成功映射NDJSON数据,需要一个对应的Java数据传输对象(DTO)类。根据提供的NDJSON结构,一个示例的CustomerFeedDTO可以定义如下:

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

public class CustomerFeedDTO {
    // 使用Map来灵活处理内部结构不固定的JSON对象
    private Map<String, ?> profile;
    private Map<String, ?> phone;
    // 使用ArrayList来处理JSON数组
    private ArrayList<?> addresses;
    private Map<String, ?> orders;
    private ArrayList<?> customs;

    // 构造函数 (可选)
    public CustomerFeedDTO() {}

    // Getters and Setters (必须提供,Gson通过反射调用它们进行数据绑定)
    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 +
               '}';
    }
}

注意: 实际应用中,Map 和 ArrayList> 可以被更具体的DTO类或泛型类型替换,以实现更强类型的数据访问和更清晰的代码结构。例如,profile可以是一个ProfileDTO对象,addresses可以是一个List,这样可以更好地封装数据并提供类型安全。

注意事项

  1. reader.setLenient(true)的重要性: 此方法允许JsonReader在解析JSON时更加宽容,例如接受未引用的名称、单引号字符串、C风格注释等。虽然NDJSON规范通常要求严格的JSON,但实际文件中可能存在一些不规范的格式。开启此模式可以提高解析的健壮性。

  2. reader.peek()与reader.hasNext()的比较:JsonReader确实有一个hasNext()方法,但它在到达文档末尾时可能会抛出IllegalStateException。相比之下,reader.peek() != JsonToken.END_DOCUMENT是一种更安全、更推荐的判断文件末尾的方式,它通过检查下一个令牌类型来避免异常。

  3. 资源管理:try-with-resources: 在处理文件I/O时,确保FileReader和JsonReader等资源被正确关闭至关重要,以防止资源泄露。Java 7及更高版本提供的try-with-resources语句是管理这些可关闭资源的最佳实践,它能确保在try块结束时(无论正常结束还是异常结束)自动关闭资源。

  4. 异常处理: 文件读取和JSON解析过程中可能会出现IOException或JsonSyntaxException等异常。在生产代码中,应提供健壮的异常处理机制,例如记录错误日志、向用户提供友好的错误信息或采取恢复措施。

热门AI工具

更多
SkildArt
SkildArt Hot

SkildArt是一款AI文本写作工具,一站式 AI 视觉创作平台。

PixPix
PixPix Hot

PixPix是一款面向电商视觉生产的AI商品图生成工具。

Laper
Laper Hot

Laper是专为编剧、导演和制片人推出的 AI 原生剧本创作工具。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

AionClaw
AionClaw Hot

AionClaw是一款面向办公、创作和编程任务的AI桌面智能体。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

PixTV
PixTV Hot

PixTV是一款面向AIGC内容创作的AI视频生成工具。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

讯飞绘文

讯飞绘文是一款由科大讯飞推出的一站式 AIGC 内容运营平台。

相关专题

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

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

2215

2023.08.07

json是什么
json是什么

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

3502

2023.08.23

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

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

1176

2023.10.13

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

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

4059

2025.09.10

string转int
string转int

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

6879

2023.08.02

while的用法
while的用法

while的用法是“while 条件: 代码块”,条件是一个表达式,当条件为真时,执行代码块,然后再次判断条件是否为真,如果为真则继续执行代码块,直到条件为假为止。本专题为大家提供while相关的文章、下载、课程内容,供大家免费下载体验。

354

2023.09.25

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

1818

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

2904

2023.09.04

Vite 项目如何打包和部署
Vite 项目如何打包和部署

Vite项目打包部署专题汇集生产构建、产物预览、输出目录、资源路径、Nginx与GitHub Pages部署及常见白屏和路由404问题的已审核文章,帮助读者完成上线流程。

60

2026.10.28

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
dev.java 官方:Learn Java
dev.java 官方:Learn Java

共0课时 | 0人学习

Java JDBC数据库连接官方教程
Java JDBC数据库连接官方教程

共0课时 | 0人学习

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

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn