使用 OpenCSV 将 CSV 解析为多个/嵌套 bean 类型?

2023-11-24

我有各种 CSV,其中包含一些标准列和一些完全随机的字段:

firstname, lastname, dog_name, fav_hat, fav_color
bill,smith,fido,porkpie,blue
james,smith,rover,bowler,purple


firstname, lastname, car_type, floor_number
tom, collins, ford, 14
jim, jones, toyota, 120

所以我试图将它们解析为 Person.class bean,它保存名字和姓氏,然后我有一个名为 PersonAttribute.class 的第二个类来保存......其他什么。

两个类的基本概要:

class Person {
 public String firstname;
 public String lastname;
 public List<PersonAttribute> attribs;
}

class PersonAttribute {
 public Person p;
 public String key; // header name, ex. 'car_type'
 public String value; // column value, ex. 'ford'
}

我一直在 opencsv 中使用 CsvToBean 函数:

public static List<Person> parseToBeans(File csvFile, HashMap<String, String> mapStrategy, Class beanClass) throws IOException {
    CSVReader reader = null;
    try {
        reader = new CSVReader(new BufferedReader(new FileReader(csvFile)));

        HeaderColumnNameTranslateMappingStrategy<Person> strategy = new HeaderColumnNameTranslateMappingStrategy<>();
        strategy.setType(beanClass);
        strategy.setColumnMapping(mapStrategy);

        final CsvToBean<Person> csv = new CsvToBean<Person>() {
            @Override
            protected Object convertValue(String value, PropertyDescriptor prop) throws InstantiationException, IllegalAccessException {
                value = value.trim().replaceAll(" +", " ");
                return super.convertValue(value, prop);
            }
        };
        return csv.parse(strategy, reader);
    }
...

但是,我不确定在解析 Person.class beans 的 csv 时如何处理创建 PersonAttribute.class beans。我碰到这个帖子我想知道我是否需要切换到 supercsv 才能轻松处理我想做的事情?


您当然可以使用 Super CSV 来实现这一目标。

您可以使用

  • CsvBeanReader- 不支持索引映射,因此您需要在 bean 中创建一个辅助方法才能使用它

  • CsvDozerBeanReader- 支持开箱即用的索引映射,因此将完全满足您的需求(需要最近发布的 Super CSV 2.1.0)

使用 CsvBeanReader

如果您不想使用 Dozer 并且能够修改您的 bean 类,最简单的选择是在您的 bean 上添加一个虚拟 setterCsvBeanReader将用于填充属性。我假设你的Person and PersonAttributebeans 有一个公共的无参数构造函数和为每个字段定义的 getters/setters(这是必需的)。

将以下虚拟设置器添加到您的Person bean:

public void setAddAttribute(PersonAttribute attribute){
    if (attribs == null){
        attribs = new ArrayList<PersonAttribute>();
    }
    attribs.add(attribute);
}

创建自定义细胞处理器这将填充一个PersonAttribute使用 CSV 标头中的相应键以及 CSV 列中的值。

package org.supercsv.example;

import org.supercsv.cellprocessor.CellProcessorAdaptor;
import org.supercsv.util.CsvContext;

/**
 * Creates a PersonAttribute using the corresponding header as the key.
 */
public class ParsePersonAttribute extends CellProcessorAdaptor {

    private final String[] header;

    public ParsePersonAttribute(final String[] header) {
        this.header = header;
    }

    public Object execute(Object value, CsvContext context) {

        if( value == null ) {
            return null;
        }

        PersonAttribute attribute = new PersonAttribute();
        // columns start at 1
        attribute.setKey(header[context.getColumnNumber() - 1]);
        attribute.setValue((String) value);
        return attribute;
    }

}

我认为下面的例子已经很能说明问题了,但我应该指出以下几点:

  • 我必须使用自定义首选项,因为您的 CSV 包含不属于数据的空格

  • 我必须动态组装字段映射和单元处理器数组,因为您的数据具有未知数量的属性(此设置通常并不复杂)

  • 属性使用的所有字段映射addAttribute,对应于setAddAttribute()我们添加到您的 bean 中的方法

  • 我使用我们的定制细胞处理器创建了一个PersonAttribute每个属性列的 bean

这是代码:

package org.supercsv.example;

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;

import org.supercsv.cellprocessor.Optional;
import org.supercsv.cellprocessor.constraint.NotNull;
import org.supercsv.cellprocessor.ift.CellProcessor;
import org.supercsv.io.CsvBeanReader;
import org.supercsv.io.ICsvBeanReader;
import org.supercsv.prefs.CsvPreference;

public class ReadWithCsvBeanReader {

    private static final String CSV = 
            "firstname, lastname, dog_name, fav_hat, fav_color\n"
            + "bill,smith,fido,porkpie,blue\n"
            + "james,smith,rover,bowler,purple";

    private static final String CSV2 = 
            "firstname, lastname, car_type, floor_number\n"
            + "tom, collins, ford, 14\n" + "jim, jones, toyota, 120";

    // attributes start at element 2 of the header array
    private static final int ATT_START_INDEX = 2;

    // custom preferences required because CSV contains 
    spaces that aren't part of the data
    private static final CsvPreference PREFS = 
        new CsvPreference.Builder(
            CsvPreference.STANDARD_PREFERENCE)
            .surroundingSpacesNeedQuotes(true).build();

    public static void main(String[] args) throws IOException {
        System.out.println("CsvBeanReader with first CSV input:");
        readWithCsvBeanReader(new StringReader(CSV));
        System.out.println("CsvBeanReader with second CSV input:");
        readWithCsvBeanReader(new StringReader(CSV2));
    }

    private static void readWithCsvBeanReader(final Reader reader)
            throws IOException {
        ICsvBeanReader beanReader = null;
        try {
            beanReader = new CsvBeanReader(reader, PREFS);

            final String[] header = beanReader.getHeader(true);

            // set up the field mapping and processors dynamically
            final String[] fieldMapping = new String[header.length];
            final CellProcessor[] processors = 
                    new CellProcessor[header.length];
            for (int i = 0; i < header.length; i++) {
                if (i < ATT_START_INDEX) {
                    // normal mappings
                    fieldMapping[i] = header[i];
                    processors[i] = new NotNull();
                } else {
                    // attribute mappings
                    fieldMapping[i] = "addAttribute";
                    processors[i] = 
                            new Optional(new ParsePersonAttribute(header));
                }
            }

            Person person;
            while ((person = beanReader.read(Person.class, fieldMapping,
                    processors)) != null) {
                System.out.println(String.format(
                        "lineNo=%s, rowNo=%s, person=%s",
                        beanReader.getLineNumber(), beanReader.getRowNumber(),
                        person));
            }

        } finally {
            if (beanReader != null) {
                beanReader.close();
            }
        }
    }

}

输出(我添加了toString()方法到你的豆子):

CsvBeanReader with first CSV input:
lineNo=2, rowNo=2, person=Person [firstname=bill, lastname=smith, attribs=[PersonAttribute [key=dog_name, value=fido], PersonAttribute [key=fav_hat, value=porkpie], PersonAttribute [key=fav_color, value=blue]]]
lineNo=3, rowNo=3, person=Person [firstname=james, lastname=smith, attribs=[PersonAttribute [key=dog_name, value=rover], PersonAttribute [key=fav_hat, value=bowler], PersonAttribute [key=fav_color, value=purple]]]
CsvBeanReader with second CSV input:
lineNo=2, rowNo=2, person=Person [firstname=tom, lastname=collins, attribs=[PersonAttribute [key=car_type, value=ford], PersonAttribute [key=floor_number, value=14]]]
lineNo=3, rowNo=3, person=Person [firstname=jim, lastname=jones, attribs=[PersonAttribute [key=car_type, value=toyota], PersonAttribute [key=floor_number, value=120]]]

使用 CsvDozerBeanReader

如果你不能,或者不想修改你的bean,那么我建议使用CsvDozerBeanReader in the 超级 CSV 推土机扩展装置项目,因为它支持嵌套和索引字段映射。查看一些正在使用的示例here.

下面是一个使用的示例CsvDozerBeanReader。你会发现它几乎与CsvBeanReader示例,但是:

  • 它使用不同的阅读器(废话!)

  • 它使用索引映射,例如attribs[0]

  • 它通过调用来设置映射configureBeanMapping()(而不是接受字符串数组read()方法就像CsvBeanReader

  • 它还设置了一些提示(更多内容见下文)

Code:

package org.supercsv.example;

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;

import org.supercsv.cellprocessor.Optional;
import org.supercsv.cellprocessor.constraint.NotNull;
import org.supercsv.cellprocessor.ift.CellProcessor;
import org.supercsv.io.dozer.CsvDozerBeanReader;
import org.supercsv.io.dozer.ICsvDozerBeanReader;
import org.supercsv.prefs.CsvPreference;

public class ReadWithCsvDozerBeanReader {

    private static final String CSV = 
            "firstname, lastname, dog_name, fav_hat, fav_color\n"
            + "bill,smith,fido,porkpie,blue\n" 
            + "james,smith,rover,bowler,purple";

    private static final String CSV2 = 
            "firstname, lastname, car_type, floor_number\n" 
            + "tom, collins, ford, 14\n"
            + "jim, jones, toyota, 120";

    // attributes start at element 2 of the header array
    private static final int ATT_START_INDEX = 2;

    // custom preferences required because CSV contains spaces that aren't part of the data
    private static final CsvPreference PREFS = new CsvPreference.Builder(CsvPreference.STANDARD_PREFERENCE)
        .surroundingSpacesNeedQuotes(true).build();

    public static void main(String[] args) throws IOException {
        System.out.println("CsvDozerBeanReader with first CSV input:");
        readWithCsvDozerBeanReader(new StringReader(CSV));
        System.out.println("CsvDozerBeanReader with second CSV input:");
        readWithCsvDozerBeanReader(new StringReader(CSV2));
    }

    private static void readWithCsvDozerBeanReader(final Reader reader) throws IOException {
        ICsvDozerBeanReader beanReader = null;
        try {
            beanReader = new CsvDozerBeanReader(reader, PREFS);

            final String[] header = beanReader.getHeader(true);

            // set up the field mapping, processors and hints dynamically
            final String[] fieldMapping = new String[header.length];
            final CellProcessor[] processors = new CellProcessor[header.length];
            final Class<?>[] hintTypes = new Class<?>[header.length];
            for( int i = 0; i < header.length; i++ ) {
                if( i < ATT_START_INDEX ) {
                    // normal mappings
                    fieldMapping[i] = header[i];
                    processors[i] = new NotNull();
                } else {
                    // attribute mappings
                    fieldMapping[i] = String.format("attribs[%d]", i - ATT_START_INDEX);
                    processors[i] = new Optional(new ParsePersonAttribute(header));
                    hintTypes[i] = PersonAttribute.class;
                }
            }

            beanReader.configureBeanMapping(Person.class, fieldMapping, hintTypes);

            Person person;
            while( (person = beanReader.read(Person.class, processors)) != null ) {
                System.out.println(String.format("lineNo=%s, rowNo=%s, person=%s", 
                    beanReader.getLineNumber(),
                    beanReader.getRowNumber(), person));
            }

        }
        finally {
            if( beanReader != null ) {
                beanReader.close();
            }
        }
    }

}

Output:

CsvDozerBeanReader with first CSV input:
lineNo=2, rowNo=2, person=Person [firstname=bill, lastname=smith, attribs=[PersonAttribute [key=dog_name, value=fido], PersonAttribute [key=fav_hat, value=porkpie], PersonAttribute [key=fav_color, value=blue]]]
lineNo=3, rowNo=3, person=Person [firstname=james, lastname=smith, attribs=[PersonAttribute [key=dog_name, value=rover], PersonAttribute [key=fav_hat, value=bowler], PersonAttribute [key=fav_color, value=purple]]]
CsvDozerBeanReader with second CSV input:
lineNo=2, rowNo=2, person=Person [firstname=tom, lastname=collins, attribs=[PersonAttribute [key=car_type, value=ford], PersonAttribute [key=floor_number, value=14]]]
lineNo=3, rowNo=3, person=Person [firstname=jim, lastname=jones, attribs=[PersonAttribute [key=car_type, value=toyota], PersonAttribute [key=floor_number, value=120]]]

在整理这个例子时,我发现了一个错误CsvDozerBeanReader在 Super CSV 2.0.1 中,当您组合细胞处理器(例如我在上面的示例中创建的用于解析每个人属性键/值的),具有索引映射,例如:

"firstname","lastname","attribs[0]","attribs[1]"

我刚刚发布了 Super CSV 2.1.0 修复了这个问题。事实证明,Dozer 需要配置一个提示才能使索引映射正常工作。我不是 100% 确定为什么,因为它完全有能力创建每个PersonAttribute并在摆脱自定义单元处理器并使用以下(深度)映射时将其添加到正确的索引:

"firstname","lastname","attribs[0].value","attribs[1].value"

我希望这有帮助 :)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 OpenCSV 将 CSV 解析为多个/嵌套 bean 类型? 的相关文章

  • SPNEGO 密码身份验证问题

    我已将我的应用程序配置为通过 SPNEGO 与 Websphere 使用 Kerberos 身份验证 这是详细信息 krb5 conf libdefaults default realm ABC MYCOMPANY COM default
  • 策略模式还是命令模式?

    假设我有一个金融交易列表 我需要针对这些交易执行一系列验证规则 一个例子是我有一笔购买产品的交易 但是首先我需要验证交易中的帐户是否有足够的可用资金 产品没有售完等 由于这些规则 交易将是标记为拒绝 并应指定错误代码 当然 我正在考虑用一个
  • 在 Java 正则表达式中获取多个模式的重叠匹配

    我有同样的问题这个链接 https stackoverflow com questions 18751486 matching one string multiple times using regex in java 但有多种模式 我的正
  • 在 jTextfield 中禁用“粘贴”

    我有一个用 Swing awt 编写的应用程序 我想阻止用户将值粘贴到文本字段中 有没有办法在不使用动作监听器的情况下做到这一点 您可以使用 null 参数调用 setTransferHandler 如下所示 textComponent s
  • 对象数组的数组(二维数组)JNI

    我正在努力创建自定义对象类型 ShareStruct 的二维数组 jobjectArray ret jobjectArray ins jobjectArray outs jclass myClass env gt FindClass env
  • 如何在url请求中发送数组

    我的要求如下 我想给出演员姓名 开始日期 结束日期并获取他在该时期出演的所有电影 因此 我的服务请求是这样的 http localhost 8080 MovieDB GetJson name Actor startDate 20120101
  • 查询 MongoDB 集合中的字段。

    我正在尝试查询 mongodb 集合中的特定字段 这是我的代码和输出 Mongo m new Mongo DB db m getDB mydb DBCollection coll db getCollection student addin
  • 迭代函数可以调用自身吗?

    当观看下面的 MIT 6 001 课程视频时 讲师在 28 00 将此算法标记为迭代 但是 在 30 27 他说这个算法和实际的 递归 算法都是递归的 该函数正在使用基本情况调用自身 那么这次迭代情况如何 private int itera
  • 如何在 MSSQL 中获取 CURRENT_DATE?

    我正在使用 jpa 3 o 和 Hibernate 我有一个命名查询 SELECT COUNT wt id FROM WPSTransaction wt WHERE wt createdDate gt CURRENT DATE WPSTra
  • 无法从资源加载图片

    So I am trying to load a image file from a resource so that when I export my application into a jar file it could be use
  • 具有 JPA 持久性的 Spring 状态机 - 存储库使用

    我试图弄清楚如何轻松使用 Spring 状态机 包括使用 JPA 进行持久化 这是我正在处理的问题 不兼容的数据类型 工厂和持久性 在程序的某个时刻 我想使用连接到用户的状态机 有用于此目的的存储库 项目spring statemachin
  • 在带有 Protocol Buffers 的项目中使用 Proguard 有什么特点?

    我有一个使用 Google Protocol Buffers 的项目 一旦我尝试用 ProGuard 对其进行混淆 似乎 protobuf 会导致问题 我将所有自己的类打包成mybuildedclasses jar 谷歌代码被打包成prot
  • 拆分/标记化/扫描字符串并注意引号

    Java中是否有默认 简单的方法来分割字符串 但要注意引号或其他符号 例如 给定以下文本 There s a man that live next door in my neighborhood and he gets me down Ob
  • 在 java 中运行外部应用程序但不要等待它完成

    我正在用java编写一个应用程序 允许我运行其他应用程序 为此 我使用了 Process 类对象 但当我这样做时 应用程序会等待进程结束 然后再退出 有没有办法在 Java 中运行外部应用程序 但不等待它完成 public static v
  • 如何从字符串中解析一个大整数? [复制]

    这个问题在这里已经有答案了 我有一个这样的方法 Integer parseInt myInt 不是这个整数变得很长 我得到以下异常 java lang NumberFormatException For input string 40001
  • 如何在不反编译的情况下更改已编译的.class文件?

    我想更改 class 文件方法 我安装 JD Eclipse Decompiler 并打开 class 文件 我添加了一些代码并保存 class 文件 但是 class 文件没有改变 我不知道如何使用反编译器 如果可能的话 如何在不使用反编
  • Java 中 JButton 的击键/热键

    最初我使用 JMenu 并建立热键以使用加速器工作 它运行得很好 现在我想在 JButton 中实现相同的行为 但我陷入困境 这是我编写的代码 请分享您的想法 以便我可以走上正确的道路 import javax swing import j
  • 如何制作一个makefile只用于编译一些java文件?

    我有三个java文件 名为A java B java C java A将创建对象B B将创建对象C 但我以前从未构建过makefile 有谁可以帮我构建一个 makefile 来编译这三个 java 文件吗 我应该使用什么工具来制作 mak
  • java.lang.IllegalStateException - 提交响应后无法创建会话

    我在我的项目中使用 JSF PrimeFaces 我为此准备了一个Maven项目 当我编译项目并加载主页后 我收到以下异常 java lang IllegalStateException Cannot create a session af
  • Axis2 错误:要输出的文本中的空白字符 (0x4) 无效

    我创建了一个 Java 客户端 使用 Axis2 1 7 6 作为代码生成器与 SOAP Web 服务进行交互 问题在于客户端的某些输入抛出异常并显示以下消息 org apache axis2 AxisFault Invalid white

随机推荐

  • 检索和修改 XMLHttpRequest 的内容

    我正在为 Firefox Safari Chrome 开发一个浏览器插件 它将拦截页面上的数据 针对正则表达式运行它 然后如果匹配 则重新格式化它 我使用以下方法在页面加载上进行此操作 var meth replaceInElement f
  • 调用未定义的函数 pg_connect() - Wamp

    我想连接到 PostgreSQL 我使用 wamp 64 位 我这里有 阿帕奇2 4 2 PHP 5 4 3 mysql 5 5 24 我还在 php ini 中取消注释 php pgsql 和 php pdo pgsql 但我无法连接 它
  • C# 验证电子邮件地址是否存在

    关于这个帖子关于电子邮件验证 使用 C 你会怎样 发出 VRFY 命令 发出 RCPT 命令 我想您会发现 在很多情况下 这些功能会故意对您撒谎 以击败垃圾邮件发送者 如果有一种方法可以确认电子邮件的真实性 而不是让用户点击验证 或取消订阅
  • atoi 是一个标准函数。但伊托亚不是。为什么?

    为什么会有这样的区别 我遇到了可怕的问题 假设itoa将在stdlib h最后链接了一个自定义版本itoa使用不同的原型 从而产生一些疯狂的错误 那么 为什么不是itoa不是标准函数 它出什么问题了 为什么标准偏向它的孪生兄弟atoi No
  • 为什么对 sysfs 设备属性文件上的“poll”调用没有正确阻止?

    我有一个简单的sysfs 设备属性它显示在我的下面sysfs目录 并调用read返回内核空间变量的值 我想打电话poll在此属性上允许我的用户空间线程阻塞 直到属性显示的值发生变化 我的问题是poll似乎并没有阻止我的属性 它不断返回POL
  • Angular 2:ngFor 完成时回调

    在 Angular 1 中 我编写了一个自定义指令 repeater ready 来使用ng repeat当迭代完成时调用回调方法 if scope last true timeout gt scope parent parent eval
  • 成员函数的部分特化[重复]

    这个问题在这里已经有答案了 可能的重复 部分模板专业化的 无效使用不完整类型 错误 为什么我可以这样做 template
  • Azure WebJobs SDK ServiceBus 连接字符串“AzureWebJobsAzureSBConnection”丢失或为空

    我在 Visual Studio 2015 中创建了一个 Azure Function App 该应用程序具有服务总线队列的触发器 当我在本地运行该应用程序时 它运行得很好 它能够从服务总线队列 通过名为 AzureSBConnection
  • Firebase 推送通知在 iOS 13 上不起作用

    Firebase 推送通知无法在 iOS 13 上运行 但在 iOS 12 4 中运行良好 有什么解决办法吗 编辑 2019 年 10 月 4 日 静默推送通知在 iOS 13 上不起作用 快速修复解决方案 如果您在 iOS 版本 13 2
  • reinterpret_cast 为 void* 不适用于函数指针

    我想将函数指针重新解释为 void 变量 函数指针的类型将是Class void 下面是示例代码 class Test int a int main Test p void a void f reinterpret cast
  • 自引用外键是什么意思?

    我检查了一个遗留数据库 发现了几个引用列自身的外键 引用的列是主键列 ALTER TABLE SchemaName TableName WITH CHECK ADD CONSTRAINT FK TableName TableName FOR
  • pythonnet 在 .net 中嵌入 Python 示例无法加载模块

    我正在尝试运行 Embedding Python in NET 示例https github com pythonnet pythonnet 我已按照故障排除文章在程序基目录中为我的 anaconda 环境设置正确的 PYTHONPATH
  • UIViewController init 方法中隐式解包的选项

    正如文件所说 如果您确定该选项确实包含一个值 您可以通过添加感叹号 来访问其基础值 那么为什么 UIViewController init 方法使用 init nibName nibName String bundle nibBundle
  • Java 中如何从另一个线程中杀死一个线程?

    我从主线程调用两个线程 将它们称为线程 1 和线程 2 当线程 1 停止时 我也想停止或终止线程 2 我该怎么做 我想要的实际输出发生了变化 那就是有一个主类 它也是线程 从主类我调用 thread1 和 thread2 我从主类向 thr
  • 将代码注入到没有自定义属性的所有方法和属性的最简单方法

    周围有很多问题和答案AOP in NETStack Overflow 上经常提到 PostSharp 和其他第三方产品 因此 NET 和 C 世界中似乎有相当多的 AOP 选项 但其中每一个都有其限制 在下载了有前途的 PostSharp
  • 如何查找和调用特定类型的 .Net TypeConverter?

    我想实现一个通用的运行时类型转换函数 它使用 Net TypeConverters 来进行转换 有谁知道如何查找和调用特定类型的 TypeConverter 考虑这个 C 示例 Convert obj to the type specifi
  • Numpy 使用索引数组将一个数组累加到另一个数组中

    我的问题是关于我想使用 numpy 表达的特定数组操作 我有一个浮点数数组w和一个索引数组idx与相同长度w我想总结一下w与相同的idx值并将它们收集在数组中v 作为一个循环 它看起来像这样 for i x in enumerate w v
  • 处理多个 NSURL 连接的最佳方式

    我正在尝试以编程方式创建 xls 工作表 为了填写表格 我正在制作倍数NSURLConnection大约100 现在 我的方法是 建立连接并将数据存储到数组中 该数组有 100 个对象 现在获取第一个对象并调用连接 存储数据 并与数组中的第
  • 与抽象类相比,使用分部类有什么好处?

    我一直在阅读 Programming Microsoft Visual C 2008 The Language 以便更好地了解 C 及其用途 我遇到了我在 ASP Net 的 Page 类中已经遇到过的部分类 在我看来 您似乎可以对抽象类和
  • 使用 OpenCSV 将 CSV 解析为多个/嵌套 bean 类型?

    我有各种 CSV 其中包含一些标准列和一些完全随机的字段 firstname lastname dog name fav hat fav color bill smith fido porkpie blue james smith rove