Export a small JSONL file with explicit columns, correct quoting and checks for invalid records.
Select the columns you need before converting. The script reads and validates the entire bounded input before emitting CSV, rejects duplicate JSON keys and non-finite numbers, quotes commas and line breaks, and prefixes formula-like strings with an apostrophe. Missing or null fields become blank cells.
Before you start
Python 3.10 or later; standard library only.
UTF-8 JSONL with one JSON object per nonblank line.
Choose 1–32 distinct column names; input is limited to 8 MiB and 10,000 records.
Build and check
Inspect the input and choose the desired column order.
Keep the output path different from the input path: shell redirection truncates an existing output before the script starts.
Open the CSV and verify its header, row count and expected cells.
Limits to understand
Only selected top-level fields are exported. Nested objects and lists become JSON text; the conversion is not a lossless JSON backup.
Strings beginning with formula markers after whitespace get an apostrophe. Actual negative numbers remain numeric. This can alter display and must be checked in the target spreadsheet.
The whole bounded input is validated before output. An output-stream failure can still interrupt a write.
INSPECT THE SCRIPT
Inspect, copy and run the documented checks.
This browser preview builds code locally. It does not contact an API or run the code.
Default example ready.
Python example
# SPDX-License-Identifier: MIT
# Copyright (c) 2026 OpenViST contributors
"""Convert a bounded UTF-8 JSON Lines file to selected CSV columns."""
import argparse
import csv
import json
import math
import re
import sys
MAX_BYTES = 8 * 1024 * 1024
MAX_RECORDS = 10000
def unique_object(pairs):
result = {}
for key, value in pairs:
if key in result:
raise ValueError(f"duplicate JSON key: {key}")
result[key] = value
return result
def reject_constant(value):
raise ValueError(f"non-finite JSON number: {value}")
def finite_float(value):
number = float(value)
if not math.isfinite(number):
raise ValueError("JSON number exceeds the finite float range")
return number
def cell(value):
if value is None:
return ""
if isinstance(value, (dict, list, bool)):
return json.dumps(value, ensure_ascii=False, sort_keys=True)
if isinstance(value, str) and value.lstrip().startswith(("=", "+", "-", "@")):
return "'" + value
return value
def convert(source, target, columns):
if not 1 <= len(columns) <= 32 or len(set(columns)) != len(columns):
raise ValueError("choose 1–32 distinct columns")
if any(not re.fullmatch(r"[A-Za-z_][A-Za-z0-9_. -]{0,63}", name) for name in columns):
raise ValueError("invalid column name")
raw = source.read(MAX_BYTES + 1)
if len(raw) > MAX_BYTES:
raise ValueError("input exceeds 8 MiB")
rows = []
for line_number, line in enumerate(raw.decode("utf-8-sig").splitlines(), 1):
if not line.strip():
continue
try:
row = json.loads(line, object_pairs_hook=unique_object,
parse_constant=reject_constant, parse_float=finite_float)
except (ValueError, RecursionError) as error:
raise ValueError(f"invalid JSON on line {line_number}: {error}") from error
if not isinstance(row, dict):
raise ValueError(f"line {line_number} must contain a JSON object")
rows.append(row)
if len(rows) > MAX_RECORDS:
raise ValueError("input exceeds 10,000 records")
# Validate the whole input before emitting a header or any partial CSV.
writer = csv.DictWriter(target, fieldnames=columns, lineterminator="\n")
writer.writeheader()
for row in rows:
writer.writerow({name: cell(row.get(name)) for name in columns})
return len(rows)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("input", help="UTF-8 JSONL file")
parser.add_argument("--columns", required=True, help="comma-separated column names")
args = parser.parse_args()
try:
with open(args.input, "rb") as source:
convert(source, sys.stdout, [name.strip() for name in args.columns.split(",")])
except (OSError, ValueError, UnicodeError) as error:
print(f"error: {error}", file=sys.stderr)
return 2
return 0
if __name__ == "__main__":
sys.exit(main())
What to verify after running
Test commas, newlines, Unicode, missing fields and nested JSON.
Malformed JSON, duplicate keys and non-object records must fail without partial CSV.
Non-finite or overflowing numbers, excessive records and large input must return a nonzero exit status.
Request or format preview
Illustrative preview. No provider response, price or balance is fetched.
Code license: MIT. Keep the license notice with your copy.
Example version 1.0.0 · Documentation reviewed 2026-10-06.
Executed locally with deterministic fixtures and CLI checks on Python 3.12.14. The three scripts pass 22 tests covering valid output, invalid input and size limits. No network or provider call was made.
Related browser tools
Inspect related data locally in your browser. These tools have their own input rules and limits; they do not execute this recipe.