Skip to content

Add generic CSV export filter#15838

Merged
Siedlerchr merged 5 commits into
JabRef:mainfrom
joaohmalves:feature/csv-export-filter-15711
May 28, 2026
Merged

Add generic CSV export filter#15838
Siedlerchr merged 5 commits into
JabRef:mainfrom
joaohmalves:feature/csv-export-filter-15711

Conversation

@joaohmalves

@joaohmalves joaohmalves commented May 27, 2026

Copy link
Copy Markdown
Contributor

Adds a new CSV export filter that exports all standard BibTeX fields as-is, with a header row.

Related issues and pull requests

Closes #15711

PR Description

Fixes #15711

Adds a new generic CSV export filter based on the OpenOffice CSV layout.
The filter exports all standard BibTeX fields as-is with a header row,
making it suitable for use in spreadsheet applications like Excel or
LibreOffice Calc. The exporter is registered in ExporterFactory and
includes layout files and tests.

Steps to test

 1. Open JabRef and load a library with some entries
 2. Go to File → Export
 3. Select "CSV" as the export format
 4. Open the exported file in Excel or LibreOffice Calc
 5. Verify the first row contains the headers and subsequent rows contain entry data

AI usage

 Claude Sonnet 4.6 (claude-sonnet-4-6)

Checklist

  • I own the copyright of the code submitted and I license it under the MIT license
  • If AI tools were used, I disclosed them in the "AI usage" section and reviewed, understood, and take full ownership of all AI-generated code
  • I manually tested my changes in running JabRef (always required)
  • I added JUnit tests for changes (if applicable)
  • [/] I added screenshots in the PR description (if change is visible to the user)
  • [/] I added a screenshot in the PR description showing a library with a single entry with me as author and as title the issue number
  • I described the change in CHANGELOG.md in a way that can be understood by the average user (if change is visible to the user)
  • [/] I checked the user documentation for up to dateness and submitted a pull request to our user documentation repository

Adds a new CSV export filter that exports all standard BibTeX fields as-is, with a header row.

Fixes JabRef#15711
@github-actions

Copy link
Copy Markdown
Contributor

Hey @joaohmalves! 👋

Thank you for contributing to JabRef!

We have automated checks in place, based on which you will soon get feedback if any of them are failing. We also use Qodo for review assistance. It will update your pull request description with a review help and offer suggestions to improve the pull request.

After all automated checks pass, a maintainer will also review your contribution. Once that happens, you can go through their comments in the "Files changed" tab and act on them, or reply to the conversation if you have further inputs. You can read about the whole pull request process in our contribution guide.

Please ensure that your pull request is in line with our AI Usage Policy and make necessary disclosures.

@qodo-free-for-open-source-projects

Copy link
Copy Markdown
Contributor

Review Summary by Qodo

Add generic CSV export filter for BibTeX entries

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Adds generic CSV export filter for standard BibTeX fields
• Registers new "CSV" exporter in ExporterFactory
• Includes layout files with header row and field formatting
• Adds comprehensive unit tests for export functionality
Diagram
flowchart LR
  A["ExporterFactory"] -->|registers| B["CSV Exporter"]
  B -->|uses| C["csv.begin.layout"]
  B -->|uses| D["csv.layout"]
  E["GenericCsvExportFormatTest"] -->|tests| B
  B -->|exports to| F["CSV File with Headers"]

Loading

Grey Divider

File Changes

1. jablib/src/main/java/org/jabref/logic/exporter/ExporterFactory.java ✨ Enhancement +1/-0

Register generic CSV exporter in factory

• Registers new generic "CSV" exporter with TemplateExporter
• Added alongside existing "OpenOffice/LibreOffice CSV" exporter
• Uses StandardFileType.CSV and default save order configuration

jablib/src/main/java/org/jabref/logic/exporter/ExporterFactory.java


2. jablib/src/test/java/org/jabref/logic/exporter/GenericCsvExportFormatTest.java 🧪 Tests +112/-0

Add comprehensive CSV export format tests

• Tests CSV export with basic fields (author, title, year)
• Tests export with multiple entries
• Tests empty entry list handling
• Verifies header row contains all standard BibTeX fields

jablib/src/test/java/org/jabref/logic/exporter/GenericCsvExportFormatTest.java


3. jablib/src/main/resources/resource/layout/csv/README 📝 Documentation +11/-0

Add CSV export filter documentation

• Documents generic CSV export filter purpose and usage
• Lists all standard BibTeX fields included as columns
• Describes intended use in spreadsheet applications

jablib/src/main/resources/resource/layout/csv/README


View more (3)
4. jablib/src/main/resources/resource/layout/csv/csv.begin.layout ⚙️ Configuration changes +1/-0

Define CSV header row layout

• Defines CSV header row with all standard BibTeX field names
• Includes 26 fields from Citation Key to ISSN
• Properly quoted field names for CSV compatibility

jablib/src/main/resources/resource/layout/csv/csv.begin.layout


5. jablib/src/main/resources/resource/layout/csv/csv.layout ⚙️ Configuration changes +1/-0

Define CSV data row layout template

• Defines CSV data row format with field placeholders
• Uses ReplaceWithEscapedDoubleQuotes formatter for proper escaping
• Maps all standard BibTeX fields to CSV columns

jablib/src/main/resources/resource/layout/csv/csv.layout


6. CHANGELOG.md 📝 Documentation +1/-0

Document CSV export filter addition

• Documents new generic CSV export filter feature
• References issue #15711
• Added to unreleased changes section

CHANGELOG.md


Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. csv.begin.layout missing Chapter header ✓ Resolved 📎 Requirement gap ≡ Correctness
Description
The new CSV header row omits standard BibTeX fields such as chapter and annote, so the exported
CSV cannot include headers for all standard fields as required. This makes the export incomplete and
inconsistent with the stated objective.
Code

jablib/src/main/resources/resource/layout/csv/csv.begin.layout[1]

Evidence
PR Compliance ID 3 requires headers for all standard fields, but csv.begin.layout only lists a
subset and does not contain headers like Chapter/Annote. StandardField defines ANNOTE and
CHAPTER as standard fields, demonstrating the header is incomplete.

Update csv.begin.layout to include headers for all standard fields
jablib/src/main/resources/resource/layout/csv/csv.begin.layout[1-1]
jablib/src/main/java/org/jabref/model/entry/field/StandardField.java[14-35]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The CSV header template does not include headers for all standard BibTeX fields (e.g., `chapter`, `annote`). This violates the requirement that `csv.begin.layout` emits a complete header row for all standard fields.
## Issue Context
`StandardField` includes standard BibTeX fields like `ANNOTE` and `CHAPTER`, but the current `csv.begin.layout` header line does not include corresponding columns.
## Fix Focus Areas
- jablib/src/main/resources/resource/layout/csv/csv.begin.layout[1-1]
- jablib/src/main/resources/resource/layout/csv/csv.layout[1-1]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. GenericCsvExportFormatTest lacks key test ✓ Resolved 📎 Requirement gap ☼ Reliability
Description
The added tests do not verify the full required header set and do not assert citation key export
behavior, so they don’t validate the key requirements of the new CSV exporter. This reduces
regression protection for the new export format.
Code

jablib/src/test/java/org/jabref/logic/exporter/GenericCsvExportFormatTest.java[R93-111]

Evidence
PR Compliance ID 6 requires tests asserting expected output including complete headers and citation
key behavior. The current tests only check a small subset of header labels and never set/assert a
citation key in exported output.

Add automated test coverage for the new CSV export style
jablib/src/test/java/org/jabref/logic/exporter/GenericCsvExportFormatTest.java[49-62]
jablib/src/test/java/org/jabref/logic/exporter/GenericCsvExportFormatTest.java[93-111]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new CSV exporter tests only check that the header contains a handful of substrings and never assert citation key output behavior. The compliance requirement expects automated tests to validate headers (for all standard fields) and citation key behavior.
## Issue Context
`performExportContainsAllStandardFieldHeaders` currently asserts only a few header fragments, and the tests never set a citation key (e.g., via `withCitationKey(...)`) to validate how it is exported.
## Fix Focus Areas
- jablib/src/test/java/org/jabref/logic/exporter/GenericCsvExportFormatTest.java[46-62]
- jablib/src/test/java/org/jabref/logic/exporter/GenericCsvExportFormatTest.java[93-111]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Unescaped quotes in CSV ✓ Resolved 🐞 Bug ≡ Correctness
Description
The new csv.layout wraps most fields in double quotes but does not escape embedded " characters
for those fields, producing malformed CSV when values contain quotes. This can break spreadsheet
import and can also violate the template README’s “one row per entry” promise when fields contain
line breaks.
Code

jablib/src/main/resources/resource/layout/csv/csv.layout[1]

Evidence
csv.layout only applies the CSV-quote escaping formatter to citationkey; other fields are quoted
but not escaped, so embedded quotes will terminate the CSV field early. The formatter
ReplaceWithEscapedDoubleQuotes is implemented to double quotes (the standard CSV escaping). The
template README also states “one row per entry”, but the new template does not replace newlines in
any fields (unlike the existing OpenOffice CSV template which explicitly normalizes abstract).

jablib/src/main/resources/resource/layout/csv/csv.layout[1-1]
jablib/src/main/java/org/jabref/logic/layout/format/ReplaceWithEscapedDoubleQuotes.java[5-18]
jablib/src/main/resources/resource/layout/csv/README[1-4]
jablib/src/main/resources/resource/layout/openoffice/openoffice-csv.layout[1-1]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new generic CSV template quotes values (e.g., `"\author"`) but does not apply CSV escaping for embedded double quotes (and does not normalize newlines), so exported CSV can be malformed or span multiple lines per entry.
### Issue Context
CSV requires embedded `"` inside quoted fields to be escaped as `""`. JabRef already has `ReplaceWithEscapedDoubleQuotes` for exactly this purpose, but the new template only applies it to `\citationkey`.
### Fix Focus Areas
- jablib/src/main/resources/resource/layout/csv/csv.layout[1-1]
- jablib/src/main/resources/resource/layout/csv/README[1-4]
- jablib/src/main/java/org/jabref/logic/layout/format/ReplaceWithEscapedDoubleQuotes.java[5-18]
### Suggested fix
- Wrap *each* exported field in a formatter chain that at least applies `ReplaceWithEscapedDoubleQuotes` (and ideally also replaces `\n` with a space to keep one row per entry), e.g.:
- `"\format[ReplaceWithEscapedDoubleQuotes]{\author}"`
- or `"\format[Replace(\n, ),ReplaceWithEscapedDoubleQuotes]{\abstract}"`
- Consider also quoting/escaping the citation key consistently (currently it is escaped but not quoted).
- Add/extend a unit test to include a field value containing a double quote (and optionally a newline) and assert the output is valid (e.g., contains doubled quotes).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

4. Missing exporter test lock ✓ Resolved 🐞 Bug ☼ Reliability
Description
GenericCsvExportFormatTest lacks the @Execution(SAME_THREAD) / @ResourceLock("exporter")
guards used by other exporter tests, so it may run concurrently and become flaky. This is especially
risky because TemplateExporter mutates global Number.serialExportNumber during export.
Code

jablib/src/test/java/org/jabref/logic/exporter/GenericCsvExportFormatTest.java[R27-44]

Evidence
TemplateExporter resets/increments a shared static counter (Number.serialExportNumber) on every
export. Other exporter tests explicitly serialize exporter tests with @Execution(SAME_THREAD) and
@ResourceLock("exporter"), but the new test does not, so it can race under parallel test
execution.

jablib/src/test/java/org/jabref/logic/exporter/GenericCsvExportFormatTest.java[18-33]
jablib/src/main/java/org/jabref/logic/exporter/TemplateExporter.java[240-246]
jablib/src/main/java/org/jabref/logic/layout/format/Number.java[7-19]
jablib/src/test/java/org/jabref/logic/exporter/CsvExportFormatTest.java[31-33]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`GenericCsvExportFormatTest` can run in parallel with other exporter tests, but exporter code mutates global static state, which can lead to non-deterministic failures.
### Issue Context
`TemplateExporter` sets and increments `Number.serialExportNumber` (a public static int) during export. Existing exporter tests (e.g., `CsvExportFormatTest`) protect against this by forcing same-thread execution and applying a shared `ResourceLock("exporter")`.
### Fix Focus Areas
- jablib/src/test/java/org/jabref/logic/exporter/GenericCsvExportFormatTest.java[18-33]
- jablib/src/main/java/org/jabref/logic/exporter/TemplateExporter.java[240-246]
- jablib/src/main/java/org/jabref/logic/layout/format/Number.java[7-19]
- jablib/src/test/java/org/jabref/logic/exporter/CsvExportFormatTest.java[31-33]
### Suggested fix
- Add the same annotations used by `CsvExportFormatTest`:
- `@Execution(ExecutionMode.SAME_THREAD)`
- `@ResourceLock("exporter")`
- and the required imports.
- (Optional) Add an `@AfterEach` cleanup like the existing test class, for consistency.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread jablib/src/main/resources/resource/layout/csv/csv.begin.layout Outdated
Comment thread jablib/src/main/resources/resource/layout/csv/csv.layout Outdated
@github-actions github-actions Bot added good first issue An issue intended for project-newcomers. Varies in difficulty. status: changes-required Pull requests that are not yet complete labels May 27, 2026
- Apply OpenRewrite suggestions
- Add missing fields Chapter and Annote to header
- Apply ReplaceWithEscapedDoubleQuotes to all fields
- Add execution lock annotations to test class
- Add citation key assertion and quote escaping tests
@github-actions github-actions Bot added status: no-bot-comments and removed status: changes-required Pull requests that are not yet complete labels May 27, 2026
Comment on lines +120 to +128
assertTrue(header.contains("Citation Key"));
assertTrue(header.contains("Author"));
assertTrue(header.contains("Title"));
assertTrue(header.contains("Year"));
assertTrue(header.contains("Journal"));
assertTrue(header.contains("DOI"));
assertTrue(header.contains("Abstract"));
assertTrue(header.contains("Chapter"));
assertTrue(header.contains("Annote"));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you read other tests in the same folder, you will find they never make a wall of assertTrue

AI loves using it

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah sorry just checked that, will be fixing it

assertTrue(lines.get(1).contains("Doe2023"));
assertTrue(lines.get(1).contains("Doe, John"));
assertEquals(
"\"Citation Key\",\"Author\",\"Title\",\"Year\",\"Journal\",\"Booktitle\",\"Publisher\",\"Volume\",\"Number\",\"Pages\",\"Month\",\"Edition\",\"Address\",\"Editor\",\"Series\",\"Note\",\"HowPublished\",\"Organization\",\"Institution\",\"School\",\"Chapter\",\"Annote\",\"DOI\",\"URL\",\"Keywords\",\"Abstract\",\"ISBN\",\"ISSN\"",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better use three quotation marks, so you can use textblocks and do not escape the quotes https://www.baeldung.com/java-text-blocks

@github-actions github-actions Bot added status: changes-required Pull requests that are not yet complete and removed status: no-bot-comments labels May 27, 2026
@github-actions github-actions Bot added status: no-bot-comments and removed status: changes-required Pull requests that are not yet complete labels May 27, 2026
@joaohmalves joaohmalves requested a review from Siedlerchr May 28, 2026 07:06
@Siedlerchr Siedlerchr added this pull request to the merge queue May 28, 2026
@github-actions github-actions Bot added the status: to-be-merged PRs which are accepted and should go into the merge-queue. label May 28, 2026
Merged via the queue into JabRef:main with commit 2973a6c May 28, 2026
50 of 51 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

first contrib good first issue An issue intended for project-newcomers. Varies in difficulty. status: no-bot-comments status: to-be-merged PRs which are accepted and should go into the merge-queue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create a new CSV export filter based on openoffice-csv.layout which exports all standard fields as is

3 participants