Skip to content

Added reading and writing with JSON for save actions metadata#15159

Open
ZiadAbdElFatah wants to merge 16 commits intoJabRef:mainfrom
ZiadAbdElFatah:fix-for-issue-10371
Open

Added reading and writing with JSON for save actions metadata#15159
ZiadAbdElFatah wants to merge 16 commits intoJabRef:mainfrom
ZiadAbdElFatah:fix-for-issue-10371

Conversation

@ZiadAbdElFatah
Copy link
Copy Markdown
Collaborator

@ZiadAbdElFatah ZiadAbdElFatah commented Feb 19, 2026

Related issues and pull requests

Part of #10371

PR Description

Added reading and writing of save actions metadata using JSON with unit tests.
Now, save actions metadata is written using the old format and the new JSON format, and also, the two formats can be read for save actions.

Checklist

  • I own the copyright of the code submitted and I license it under the MIT license
  • 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

@ZiadAbdElFatah
Copy link
Copy Markdown
Collaborator Author

I think now step 1 and step 2 here #10371 (comment) are complete, if so I will move to big part in the issue which is step 3 @koppor.

  1. Write logic code to parse, read and write new JSON format.
  • I didn't find the proper place to put these logic code, maybe I should put them in MetaDataSerializer, >>MetaDataParser?
  • And I didn't find the old code to read @comment in this step now, maybe in BibtexDatabaseWriter?

The hole MetaDataParser can be "deleted" - and a new loading from JSON. I think, it is JSON -> DTO -> metadata. Maybe also directly from JSON to MetaData. -- "deleted" is not quite true, because JabRef should be able to read "old" files - and on version 7, the old metadata is not writtin any more. In version 6, both formats are read and written; with the new format taking predecdence)

@koppor
Copy link
Copy Markdown
Member

koppor commented Feb 19, 2026

think now step 1 and step 2 here #10371 (comment) are complete, if

I think its wrong. Because the testing needs to be redone. Based on the custom logic for each preference

@testlens-app

This comment has been minimized.

@ZiadAbdElFatah
Copy link
Copy Markdown
Collaborator Author

Because the testing needs to be redone. Based on the custom logic for each preference

Correct me if I am wrong, I know that there will be many tests generated for this feature as mentioned here #10371 (comment)

There will be many unit tests for that.

But I think step 3 must be implemented first

  1. Write logic code to parse, read and write new JSON format.

Then the unit tests will be generated for each preference.

@koppor
Copy link
Copy Markdown
Member

koppor commented Feb 20, 2026

But I think step 3 must be implemented first

Not for all at once. One after another. An easy one can be done here first to show all the impact.

Just reading and writing JSON is OK, but in the cutrent state it does not bring any value to the user.

If you feel unable to split down the work in small steps for yourself, maybe this is the wrong issue.

@ZiadAbdElFatah
Copy link
Copy Markdown
Collaborator Author

But I think step 3 must be implemented first

Not for all at once. One after another. An easy one can be done here first to show all the impact.

Just reading and writing JSON is OK, but in the cutrent state it does not bring any value to the user.

If you feel unable to split down the work in small steps for yourself, maybe this is the wrong issue.

I do already plan to finish one preference after another, logic, and unit tests for each one.
But I just wanted to make sure that this requested change https://github.com/JabRef/jabref/pull/12145/changes#r1944599873 is addressed. I will start working on one soon to get it reviewed.

@ZiadAbdElFatah
Copy link
Copy Markdown
Collaborator Author

I added writing and reading for JSON to save actions metadata.
Would you prefer to open a single PR for every metadata or all metadata in one PR @koppor?

@koppor
Copy link
Copy Markdown
Member

koppor commented Feb 24, 2026

I added writing and reading for JSON to save actions metadata.
Would you prefer to open a single PR for every metadata or all metadata in one PR @koppor?

What is not clear at

Not for all at once. One after another. An easy one can be done here first to show all the impact.

You need to be clear when writing "all". "all" matches "Not for all" in my comment.... Same letters!!

@ZiadAbdElFatah ZiadAbdElFatah changed the title Added parsing metadata using JSON with a unit test Added reading and writing with JSON for save actions metadata Feb 24, 2026
@ZiadAbdElFatah ZiadAbdElFatah marked this pull request as ready for review February 24, 2026 11:21
@qodo-free-for-open-source-projects
Copy link
Copy Markdown
Contributor

Review Summary by Qodo

Add JSON serialization for save actions metadata

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add JSON format support for save actions metadata
• Implement bidirectional reading/writing of save actions
• Maintain backward compatibility with legacy format
• Add comprehensive unit tests for JSON parsing
Diagram
flowchart LR
  A["BibDatabaseWriter"] -->|"serialize to JSON"| B["Save Actions JSON"]
  C["BibtexParser"] -->|"parse from JSON"| B
  B -->|"read/write"| D["MetaData"]
  E["Legacy Format"] -->|"backward compatible"| D
Loading

Grey Divider

File Changes

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

Add JSON serialization for save actions

• Added JSON serialization for save actions metadata
• Implemented serializeSaveActionsToJson() method to convert FieldFormatterCleanupActions to
 JSON format
• Implemented writeMetaDataJson() method to write metadata in new JSON format alongside legacy
 format
• Added imports for Gson, JsonObject, JsonArray, and LinkedHashMap

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


2. jablib/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java ✨ Enhancement +43/-0

Add JSON deserialization for save actions

• Added JSON parsing capability for save actions metadata
• Implemented parseCommentToJson() method to extract JSON from comments
• Implemented parseSaveActionsFromJson() method to deserialize JSON to
 FieldFormatterCleanupActions
• Added field to store parsed JSON metadata and integrate it into parser result
• Added imports for Gson, JsonObject, JsonElement, and StringJoiner

jablib/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java


3. jablib/src/main/java/org/jabref/model/metadata/MetaData.java ✨ Enhancement +1/-0

Add JSON metadata format flag constant

• Added new constant META_FLAG_V1 for JSON metadata format identifier
• Maintains existing META_FLAG for backward compatibility with legacy format

jablib/src/main/java/org/jabref/model/metadata/MetaData.java


View more (2)
4. jablib/src/test/java/org/jabref/logic/exporter/BibDatabaseWriterTest.java 🧪 Tests +21/-4

Update save actions test for dual format

• Updated writeSaveActions() test to verify both legacy and JSON formats are written
• Test now validates that save actions are written in both old format and new JSON format
• Verifies correct JSON structure with proper indentation and field ordering

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


5. jablib/src/test/java/org/jabref/logic/importer/fileformat/BibtexParserTest.java 🧪 Tests +43/-0

Add JSON parsing tests for save actions

• Added integrationTestSaveActionsJson() test to verify JSON parsing of save actions
• Added parseCommentToJson() unit test to validate JSON extraction from comments
• Tests verify correct deserialization of JSON metadata into FieldFormatterCleanupActions

jablib/src/test/java/org/jabref/logic/importer/fileformat/BibtexParserTest.java


Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects
Copy link
Copy Markdown
Contributor

qodo-free-for-open-source-projects Bot commented Feb 24, 2026

Code Review by Qodo

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

Grey Divider


Action required

1. parseCommentToJson uncaught JsonSyntaxException 📘 Rule violation ≡ Correctness
Description
JSON metadata parsing uses gson.fromJson(...) without handling malformed JSON, which can throw a
runtime exception and abort import. This violates the requirement for robust, contextual error
handling during parsing of external file content.
Code

jablib/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java[R423-427]

+    Optional<JsonObject> parseCommentToJson(String comment) {
+        Gson gson = new Gson();
+        String content = comment.substring(MetaData.META_FLAG_V1.length());
+        return Optional.ofNullable(gson.fromJson(content, JsonObject.class));
+    }
Evidence
The checklist requires handling potential failure points with meaningful context rather than
allowing crashes. The new parseCommentToJson method directly parses untrusted comment content via
Gson without any try/catch or parserResult error reporting, so malformed JSON can propagate as an
unchecked exception.

Rule 3: Generic: Robust Error Handling and Edge Case Management
AGENTS.md
jablib/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java[423-427]

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

## Issue description
`parseCommentToJson` parses JSON from untrusted file content using `gson.fromJson` without handling malformed JSON, which can throw a runtime exception and crash parsing.
## Issue Context
This is part of BibTeX import logic; invalid/malformed metadata comments should not abort import and should instead be reported as a parser exception/warning with location/context.
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java[415-427]

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


2. JSON meta duplicated on save 🐞 Bug ≡ Correctness
Description
META_FLAG_V1 (@Comment JSON) is parsed but not removed from the raw text buffer, so it can be stored
in the first entry’s parsed serialization and later written back verbatim. Since the writer also
always emits JSON metadata, saving can duplicate the JSON metadata block in the output file.
Code

jablib/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java[R418-420]

+        } else if (comment.startsWith(MetaData.META_FLAG_V1)) {
+            parsedJsonMetaData = parseCommentToJson(comment);
+        }
Evidence
Legacy metadata comments are explicitly dropped from the preserved text buffer via
dumpTextReadSoFarToString(), but the new JSON metadata branch does not. The preserved text
(including comments) is later attached to the first entry’s parsedSerialization/commentsBeforeEntry.
When saving without reformatting and the entry is unchanged, BibEntryWriter writes
parsedSerialization verbatim; additionally, BibDatabaseWriter always writes JSON metadata via
writeMetaDataJson, leading to duplicated JSON blocks (one before the first entry, one in the
metadata section).

jablib/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java[372-421]
jablib/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java[335-360]
jablib/src/main/java/org/jabref/logic/bibtex/BibEntryWriter.java[68-74]
jablib/src/main/java/org/jabref/logic/exporter/BibDatabaseWriter.java[275-327]

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

## Issue description
`META_FLAG_V1` JSON metadata comments are parsed but not removed from the parser’s preserved text buffer. This can cause the JSON `@Comment{jabref-meta-0.1.0 ...}` block to be stored as “user comments before the first entry” and later written back verbatim, while `BibDatabaseWriter` also writes a fresh JSON metadata block — resulting in duplicated JSON metadata after saving.
### Issue Context
Legacy JabRef metadata comments call `dumpTextReadSoFarToString()` to ensure metadata comments are rewritten by JabRef and not kept verbatim in the file. The JSON metadata branch should follow the same strategy.
### Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java[372-421]
- jablib/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java[335-360]
- jablib/src/main/java/org/jabref/logic/bibtex/BibEntryWriter.java[68-74]
### Suggested approach
1. In the `comment.startsWith(MetaData.META_FLAG_V1)` branch, after parsing (and/or even on parse failure, consistent with how entrytype comments are treated), call `dumpTextReadSoFarToString()` so the comment is not retained as user text.
2. Consider adding a regression test that:
- Parses a file containing only the JSON metadata comment + an entry.
- Saves with `shouldReformatFile() == false` and the entry unchanged.
- Asserts the output contains only one JSON metadata block (not one before the entry plus one in the metadata section).

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


Grey Divider

Qodo Logo

Comment on lines +423 to +427
Optional<JsonObject> parseCommentToJson(String comment) {
Gson gson = new Gson();
String content = comment.substring(MetaData.META_FLAG_V1.length());
return Optional.ofNullable(gson.fromJson(content, JsonObject.class));
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Action required

1. parsecommenttojson uncaught jsonsyntaxexception 📘 Rule violation ⛯ Reliability

JSON metadata parsing uses gson.fromJson(...) without handling malformed JSON, which can throw a
runtime exception and abort import. This violates the requirement for robust, contextual error
handling during parsing of external file content.
Agent Prompt
## Issue description
`parseCommentToJson` parses JSON from untrusted file content using `gson.fromJson` without handling malformed JSON, which can throw a runtime exception and crash parsing.

## Issue Context
This is part of BibTeX import logic; invalid/malformed metadata comments should not abort import and should instead be reported as a parser exception/warning with location/context.

## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java[415-427]

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

Comment on lines +418 to +420
} else if (comment.startsWith(MetaData.META_FLAG_V1)) {
parsedJsonMetaData = parseCommentToJson(comment);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Action required

2. Json meta duplicated on save 🐞 Bug ✓ Correctness

META_FLAG_V1 (@Comment JSON) is parsed but not removed from the raw text buffer, so it can be stored
in the first entry’s parsed serialization and later written back verbatim. Since the writer also
always emits JSON metadata, saving can duplicate the JSON metadata block in the output file.
Agent Prompt
### Issue description
`META_FLAG_V1` JSON metadata comments are parsed but not removed from the parser’s preserved text buffer. This can cause the JSON `@Comment{jabref-meta-0.1.0 ...}` block to be stored as “user comments before the first entry” and later written back verbatim, while `BibDatabaseWriter` also writes a fresh JSON metadata block — resulting in duplicated JSON metadata after saving.

### Issue Context
Legacy JabRef metadata comments call `dumpTextReadSoFarToString()` to ensure metadata comments are rewritten by JabRef and not kept verbatim in the file. The JSON metadata branch should follow the same strategy.

### Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java[372-421]
- jablib/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java[335-360]
- jablib/src/main/java/org/jabref/logic/bibtex/BibEntryWriter.java[68-74]

### Suggested approach
1. In the `comment.startsWith(MetaData.META_FLAG_V1)` branch, after parsing (and/or even on parse failure, consistent with how entrytype comments are treated), call `dumpTextReadSoFarToString()` so the comment is not retained as user text.
2. Consider adding a regression test that:
   - Parses a file containing only the JSON metadata comment + an entry.
   - Saves with `shouldReformatFile() == false` and the entry unchanged.
   - Asserts the output contains only one JSON metadata block (not one before the entry plus one in the metadata section).

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

@ZiadAbdElFatah
Copy link
Copy Markdown
Collaborator Author

I won't be able to continue working on the rest of the metadata until this one is reviewed, to be able to make the best out of the rest of the metadata. Also, I think there will be merge conflicts if I try to work on other metadata before this one is merged.

Copy link
Copy Markdown
Member

@koppor koppor left a comment

Choose a reason for hiding this comment

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

Wrong architecture.

Separate the convertes

We also miss some data models - and model convertion

Look for "Data Transfer Object" implementation pattern in the net.

Comment on lines +759 to +777
+ ";}" + OS.NEWLINE
+ OS.NEWLINE
+ "@Comment{jabref-meta-0.1.0" + OS.NEWLINE
+ "{" + OS.NEWLINE
+ " \"saveActions\": {" + OS.NEWLINE
+ " \"state\": true," + OS.NEWLINE
+ " \"title\": [" + OS.NEWLINE
+ " \"lower_case\"" + OS.NEWLINE
+ " ]," + OS.NEWLINE
+ " \"journal\": [" + OS.NEWLINE
+ " \"title_case\"" + OS.NEWLINE
+ " ]," + OS.NEWLINE
+ " \"day\": [" + OS.NEWLINE
+ " \"upper_case\"" + OS.NEWLINE
+ " ]" + OS.NEWLINE
+ " }" + OS.NEWLINE
+ "}" + OS.NEWLINE
+ "}" + OS.NEWLINE;

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.

Why no """ `?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I thought it would be better to follow the old style.

writeMetaDataJson(metaData);
}

private JsonObject serializeSaveActionsToJson(FieldFormatterCleanupActions saveActions) {
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.

Wrong place

See preferences how JabRef handles - see org.jabref.logic.preferences.JabRefCliPreferences and the sub preferences

It is a no go to create a monster class BibDatabaseWriter.

@testlens-app

This comment has been minimized.

@github-actions github-actions Bot added status: no-bot-comments and removed status: changes-required Pull requests that are not yet complete labels Mar 21, 2026
@testlens-app

This comment has been minimized.

@testlens-app

This comment has been minimized.

@testlens-app

This comment has been minimized.

@testlens-app
Copy link
Copy Markdown

testlens-app Bot commented Mar 25, 2026

✅ All tests passed ✅

🏷️ Commit: 7b1c83a
▶️ Tests: 10205 executed
⚪️ Checks: 50/50 completed


Learn more about TestLens at testlens.app.

@ZiadAbdElFatah
Copy link
Copy Markdown
Collaborator Author

ZiadAbdElFatah commented Mar 25, 2026

Wrong architecture.

Separate the convertes

We also miss some data models - and model convertion

Look for "Data Transfer Object" implementation pattern in the net.

Thanks for providing the correct pattern I studied it well and made the changes.

I removed the @ADR because there were 4 failing checks because of it, I don't know really if removing it is right or not but all the checks now pass, but if removing it is wrong I will restore it again.

@ZiadAbdElFatah ZiadAbdElFatah requested a review from koppor March 25, 2026 22:31
@calixtus
Copy link
Copy Markdown
Member

Please put the ADR annotations back in.
Was fixed in a recent PR by koppor.

ZiadAbdElFatah and others added 3 commits April 18, 2026 05:14
)

Bumps org.libreoffice:libreoffice from 24.8.4 to 25.2.7.

---
updated-dependencies:
- dependency-name: org.libreoffice:libreoffice
  dependency-version: 25.2.7
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
@ZiadAbdElFatah
Copy link
Copy Markdown
Collaborator Author

Please put the ADR annotations back in. Was fixed in a recent PR by koppor.

I updated the branch and added the ADR annotations back in, but the same checks still fail.

@calixtus
Copy link
Copy Markdown
Member

Will be fixed when #15559 is merged

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants