OpenViSTAI · Agents · Blockchain · Crypto
Menu
OPENVIST RECIPES

Convert JSON Lines to a predictable CSV

Python

Export a small JSONL file with explicit columns, correct quoting and checks for invalid records.

Python code excerpt: Convert JSON Lines to a predictable CSV. Version 1.0.0. Read the full recipe for code, sources and recorded test scope.

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

  1. Inspect the input and choose the desired column order.
  2. Run: python jsonl_csv.py records.jsonl --columns name,url,category > records.csv
  3. Keep the output path different from the input path: shell redirection truncates an existing output before the script starts.
  4. 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.

Change a setting to inspect the preview.

Official sources and related profiles

    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.