Skip to content

[ZEPPELIN-6634] Download service prepends a UTF-8 BOM to JSON files, breaking standard parsers - #5449

Open
xhaktm00 wants to merge 1 commit into
apache:masterfrom
xhaktm00:ZEPPELIN-6634
Open

[ZEPPELIN-6634] Download service prepends a UTF-8 BOM to JSON files, breaking standard parsers#5449
xhaktm00 wants to merge 1 commit into
apache:masterfrom
xhaktm00:ZEPPELIN-6634

Conversation

@xhaktm00

@xhaktm00 xhaktm00 commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

What is this PR for?

Downloaded .zpln and .ipynb files start with a UTF-8 BOM, which the JSON specification does not allow, so strict parsers refuse them:

Python json.load(utf-8)   -> FAIL: Unexpected UTF-8 BOM
nbformat (Jupyter)        -> FAIL: Notebook does not appear to be JSON
Zeppelin's own import     -> OK (Gson skips the BOM)

Zeppelin reads its own files back because Gson tolerates the BOM, which is why this went unnoticed until a downloaded .ipynb was opened in Jupyter.

The BOM was added in ZEPPELIN-672 so Excel reads a CSV export as UTF-8, but it lives in the shared download service, so the zpln and ipynb exports that later reused that service inherited it.

This makes the caller decide: saveAs() takes a bom flag defaulting to false, and only the CSV/TSV call site passes true. Of the six call sites, that is the only one that needs it — the new UI has none at all, since its CSV/TSV export goes through the xlsx library.

Call site Format BOM
result.controller.js CSV / TSV yes
notebook.controller.js (x2) zpln no
websocket-event.factory.js ipynb no
action-bar.component.ts (x2) zpln (new UI) no

Both UIs are fixed together, and the classic service drops the BOM in both its IE and standard branches.

What type of PR is it?

Bug Fix

Todos

  • Add a bom flag to both saveAs() services, defaulting to off
  • Opt the CSV/TSV call site in, leaving the JSON exports without a BOM
  • Add unit tests asserting the bytes on both paths

What is the Jira issue?

How should this be tested?

New spec save-as.service.spec.ts checks the raw bytes handed to createObjectURL, since Blob.text() decodes and strips a leading BOM: a zpln export has no BOM and parses as JSON, and a CSV export with bom: true keeps EF BB BF.

cd zeppelin-web-angular && npm run test:shell

Result: Tests 31 passed (31).

Manual, on a built server: export a note as .zpln and download a table result as CSV.

$ xxd note.zpln | head -1
00000000: 7b22 7061 7261 6772 6170 6873 223a 5b7b  {"paragraphs":[{
$ python3 -c "import json; json.load(open('note.zpln')); print('ok')"
ok

$ xxd export.csv | head -1
00000000: efbb bf6e 616d 652c 6369 7479 0aea b980  ...name,city....

The zpln no longer starts with a BOM and Python parses it, while the CSV still carries one. Korean, Chinese and emoji survived in both.

Screenshots (if appropriate)

N/A

Questions:

  • Does the license files need to update? No
  • Is there breaking changes for older versions? No — the flag defaults to off, and CSV/TSV keeps the BOM it had
  • Does this needs documentation? No

Downloaded zpln and ipynb files start with a UTF-8 BOM, which the JSON
specification does not allow, so strict parsers refuse them: Python's json
reports an unexpected BOM and nbformat reports the notebook is not JSON.
Zeppelin reads its own files back because Gson skips the BOM, which is why this
went unnoticed until a downloaded ipynb was opened in Jupyter.

The BOM was added in ZEPPELIN-672 so Excel reads a CSV export as UTF-8, but it
lives in the shared download service, so the zpln and ipynb exports that later
reused the service inherited it.

Let the call site decide instead. Only the CSV and TSV export asks for a BOM;
the JSON exports leave it off. The new UI has no caller that needs one, since
its CSV and TSV export goes through the xlsx library.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant