简体   繁体   English

Java opencsv 库:从空值(空值)中删除引号

[英]Java opencsv library: remove quotations from empty values(null values)

I use this library for exporting to CSV file我使用这个库导出到 CSV 文件

<dependency>
        <groupId>com.opencsv</groupId>
        <artifactId>opencsv</artifactId>
        <version>5.3</version>
    </dependency>

I created Builder:我创建了生成器:

writer = new StatefulBeanToCsvBuilder<T>(printWriter)
                .withQuotechar(CSVWriter.DEFAULT_QUOTE_CHARACTER)
                .withSeparator(CSVWriter.DEFAULT_SEPARATOR)
                .withOrderedResults(false)
                .withMappingStrategy(mappingStrategy)
                .build();

It Is my POJO:这是我的 POJO:

@Data
public class ReportCsvDto {

    @CsvBindByName(column = "NAME")
    @CsvBindByPosition(position = 0)
    private String name;

    @CsvBindByName(column = "ID")
    @CsvBindByPosition(position = 1)
    private String id;

    @CsvBindByName(column = "GENDER")
    @CsvBindByPosition(position = 3)
    private String gender;
}

How can I remove quotations from empty values?如何从空值中删除引号?

I have this: "Bill","","male" I want this: "Bill",,"male"我有这个: "Bill","","male"我想要这个: "Bill",,"male"

I want to remove quotations only from empty values我想删除值报价

I have looked through the code of opencsv library.我已经查看了 opencsv 库的代码。 And most simple decision which I can come up with now it is just override transmuteBean method in MappingStrategy and passing this new stategy to the builder.而最简单的决定,我可以想出现在它只是覆盖MappingStrategy transmuteBean方法,并把这种新的stategy的建设者。 For example for ColumnPositionMappingStrategy :例如对于ColumnPositionMappingStrategy

public class CustomColumnPositionMappingStrategy<T> extends ColumnPositionMappingStrategy<T> {
    @Override
    public String[] transmuteBean(T bean) throws CsvFieldAssignmentException, CsvChainedException {
        int numColumns = headerIndex.findMaxIndex()+1;
        BeanField<T, Integer> firstBeanField, subsequentBeanField;
        Integer firstIndex, subsequentIndex;
        List<String> contents = new ArrayList<>(Math.max(numColumns, 0));

        // Create a map of types to instances of subordinate beans
        Map<Class<?>, Object> instanceMap;
        try {
            instanceMap = indexBean(bean);
        }
        catch(IllegalAccessException | InvocationTargetException e) {
            // Our testing indicates these exceptions probably can't be thrown,
            // but they're declared, so we have to deal with them. It's an
            // alibi catch block.
            CsvBeanIntrospectionException csve = new CsvBeanIntrospectionException(
                    ResourceBundle.getBundle(
                            ICSVParser.DEFAULT_BUNDLE_NAME, errorLocale)
                            .getString("error.introspecting.beans"));
            csve.initCause(e);
            throw csve;
        }

        CsvChainedException chainedException = null;
        for(int i = 0; i < numColumns;) {

            // Determine the first value
            firstBeanField = findField(i);
            firstIndex = chooseMultivaluedFieldIndexFromHeaderIndex(i);
            String[] fields = ArrayUtils.EMPTY_STRING_ARRAY;
            if(firstBeanField != null) {
                try {
                    fields = firstBeanField.write(instanceMap.get(firstBeanField.getType()), firstIndex);
                }
                catch(CsvDataTypeMismatchException | CsvRequiredFieldEmptyException e) {
                    if(chainedException != null) {
                        chainedException.add(e);
                    }
                    else {
                        chainedException = new CsvChainedException(e);
                    }
                }
            }

            if(fields.length == 0) {

                // Write the only value
                contents.add(null);
                i++; // Advance the index
            }
            else {

                // Multiple values. Write the first.
                contents.add(fields[0]);

                // Now write the rest.
                // We must make certain that we don't write more fields
                // than we have columns of the correct type to cover them.
                int j = 1;
                int displacedIndex = i+j;
                subsequentBeanField = findField(displacedIndex);
                subsequentIndex = chooseMultivaluedFieldIndexFromHeaderIndex(displacedIndex);
                while(j < fields.length
                        && displacedIndex < numColumns
                        && Objects.equals(firstBeanField, subsequentBeanField)
                        && Objects.equals(firstIndex, subsequentIndex)) {
                    // This field still has a header, so add it
                    contents.add(fields[j]);

                    // Prepare for the next loop through
                    displacedIndex = i + (++j);
                    subsequentBeanField = findField(displacedIndex);
                    subsequentIndex = chooseMultivaluedFieldIndexFromHeaderIndex(displacedIndex);
                }

                i = displacedIndex; // Advance the index

                // And here's where we fill in any fields that are missing to
                // cover the number of columns of the same type
                if(i < numColumns) {
                    subsequentBeanField = findField(i);
                    subsequentIndex = chooseMultivaluedFieldIndexFromHeaderIndex(i);
                    while(Objects.equals(firstBeanField, subsequentBeanField)
                            && Objects.equals(firstIndex, subsequentIndex)
                            && i < numColumns) {
                        contents.add(null);
                        subsequentBeanField = findField(++i);
                        subsequentIndex = chooseMultivaluedFieldIndexFromHeaderIndex(i);
                    }
                }
            }
        }

        // If there were exceptions, throw them
        if(chainedException != null) {
            if (chainedException.hasOnlyOneException()) {
                throw chainedException.getFirstException();
            }
            throw chainedException;
        }

        return contents.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
    }
}

And for your example it will produce the following output:对于您的示例,它将产生以下输出:

"Bill",,"male"

This overridden method is a simple copy of the original method.这个被覆盖的方法是原始方法的简单副本。 But instead of writing empty string on null value it writes null value.但是不是在空值上写空字符串,而是写空值。 And CSVWriter.writeNext method then skips the output of the quotes for null value.然后CSVWriter.writeNext方法会跳过空值的引号输出。 This decision can be extended to handle blank lines in the original data too.这个决定也可以扩展到处理原始数据中的空行。

As an option you can implement MappingStrategy entirely of course.作为一种选择,您当然可以完全实现 MappingStrategy。 But I think this is not what you need.但我认为这不是你需要的。

Or you can just implement ICSVWriter for your case or redefine writeNext method for existing subclass.或者您可以为您的案例实现ICSVWriter或为现有子类重新定义writeNext方法。 And then you need to pass this CSVWriter to builder.然后您需要将此 CSVWriter 传递给构建器。 For example CSVWriter.writeNext :例如CSVWriter.writeNext

public class CustomCSVWriter extends CSVWriter {
    public CustomCSVWriter(Writer writer) {
        super(writer);
    }

    public CustomCSVWriter(Writer writer, char separator, char quotechar, char escapechar, String lineEnd) {
        super(writer, separator, quotechar, escapechar, lineEnd);
    }

    @Override
    protected void writeNext(String[] nextLine, boolean applyQuotesToAll, Appendable appendable) throws IOException {
        if (nextLine == null) {
            return;
        }

        for (int i = 0; i < nextLine.length; i++) {

            if (i != 0) {
                appendable.append(separator);
            }

            String nextElement = nextLine[i];

            if (StringUtils.isEmpty(nextElement)) {
                continue;
            }

            Boolean stringContainsSpecialCharacters = stringContainsSpecialCharacters(nextElement);

            appendQuoteCharacterIfNeeded(applyQuotesToAll, appendable, stringContainsSpecialCharacters);

            if (stringContainsSpecialCharacters) {
                processLine(nextElement, appendable);
            } else {
                appendable.append(nextElement);
            }

            appendQuoteCharacterIfNeeded(applyQuotesToAll, appendable, stringContainsSpecialCharacters);
        }

        appendable.append(lineEnd);
        writer.write(appendable.toString());
    }

    private void appendQuoteCharacterIfNeeded(boolean applyQuotesToAll, Appendable appendable, Boolean stringContainsSpecialCharacters) throws IOException {
        if ((applyQuotesToAll || stringContainsSpecialCharacters) && quotechar != NO_QUOTE_CHARACTER) {
            appendable.append(quotechar);
        }
    }
}

Overridden method is a simple copy of the original method again.重写方法是原始方法的简单副本。 But it skips processing of empty strings (StringUtils.isEmpty(nextElement) check instead of checking for null).但它跳过空字符串的处理(StringUtils.isEmpty(nextElement) 检查而不是检查 null)。

And, of course, you can redefine this behavior in the following way:而且,当然,您可以通过以下方式重新定义此行为:

public class CustomCSVWriter extends CSVWriter {
    public CustomCSVWriter(Writer writer) {
        super(writer);
    }

    public CustomCSVWriter(Writer writer, char separator, char quotechar, char escapechar, String lineEnd) {
        super(writer, separator, quotechar, escapechar, lineEnd);
    }

    @Override
    protected void writeNext(String[] nextLine, boolean applyQuotesToAll, Appendable appendable) throws IOException {
        if (nextLine != null) {
            for (int i = 0; i < nextLine.length; i++) {
                if (StringUtils.isEmpty(nextLine[i])) {
                    nextLine[i] = null;
                }
            }
        }
        super.writeNext(nextLine, applyQuotesToAll, appendable);
    }
}

Here empty strings are simply replaced with null values.这里空字符串简单地替换为空值。 And for me, this method would be more preferable if you do not need to separate empty strings and null values from the original data.对我来说,如果不需要从原始数据中分离空字符串和空值,这种方法会更可取。 Otherwise, the first option (with redefining MappingStrategy) is the only one possible.否则,第一个选项(重新定义 MappingStrategy)是唯一可能的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM