Recent chats
ASKGEEZ Q&A GUIDETechnology & devices

How can I read and write a JSON Lines file one record per line?

Quick answer

Treat a JSON Lines file as one complete JSON value per physical line, not as one JSON array. In Python, iterate over the file and call json.loads() on each nonblank line; catch json.JSONDecodeError and include the line number in the error. To write records, call json.dumps() once per value and add a newline. This assumes each record fits on one line; JSON Lines implementations may differ in their requirements. [S1]

LET’S WORK THROUGH IT

The question, the details, the next step.

Question

What does “one record per line” mean? Can I just write several objects one after another?

Geez

A JSON Lines file is a sequence of separate lines, with each line holding one complete JSON value. For example, a file might contain:

{"name": "Mina", "score": 8}
{"name": "Omar", "score": 6}

That is different from a single JSON array such as [{...}, {...}]. Python’s json module handles JSON documents, but repeatedly dumping separate values into one file does not make an ordinary valid JSON document. For line-oriented records, read and write one value at a time. [S1]

The example below assumes one complete value per physical line. It skips blank lines as an explicit convenience; if blank lines matter to your workflow, choose and document a different policy.

Follow-up question

How do I read the file and identify which record is malformed?

Geez

Use a line counter and catch json.JSONDecodeError for each line. This example keeps valid records and reports errors to the screen rather than stopping at the first bad record:

import json

records = []

with open("input.jsonl", "r", encoding="utf-8") as infile:
    for line_number, line in enumerate(infile, start=1):
        if not line.strip():
            continue  # Explicit policy: ignore blank lines.

        try:
            record = json.loads(line)
        except json.JSONDecodeError as exc:
            print(
                f"Malformed JSON on line {line_number}: "
                f"{exc.msg} (column {exc.colno})"
            )
            continue

        records.append(record)

print(f"Read {len(records)} valid records")

The counter identifies the physical line in the file, while the exception provides parsing details such as its message and position. [S1] If the file is large, you can process each record inside the loop instead of retaining every value in records.

Follow-up question

How do I write records back out, one per line?

Geez

Open the destination in write mode and dump each value separately, followed by a newline:

import json

records = [
    {"name": "Mina", "score": 8},
    {"name": "Omar", "score": 6},
]

with open("output.jsonl", "w", encoding="utf-8") as outfile:
    for record in records:
        outfile.write(json.dumps(record))
        outfile.write("\n")

The result has one serialized value per line. This example replaces the destination file because it uses "w"; use a separate output path if you need to preserve the input. The standard JSON module supplies the serialization, while the added newline supplies the record boundary for this line-oriented approach. [S1]

Follow-up question

Can I combine reading and writing, and what should I check before using the result?

Geez

Yes. Read each input line, parse it, and write valid values to a different output file. Keeping the paths separate avoids replacing the source while it is being read:

import json

with open("input.jsonl", "r", encoding="utf-8") as infile, \
     open("cleaned.jsonl", "w", encoding="utf-8") as outfile:
    for line_number, line in enumerate(infile, start=1):
        if not line.strip():
            continue  # This example ignores blank lines.

        try:
            value = json.loads(line)
        except json.JSONDecodeError as exc:
            print(f"Line {line_number}: {exc.msg} (column {exc.colno})")
            continue

        outfile.write(json.dumps(value) + "\n")

This copies valid JSON values into a new line-oriented file and reports skipped malformed lines. It does not establish requirements for every JSON Lines implementation, nor does it verify that values have the fields or types your application expects. Add application-specific validation if needed, and decide whether skipping blank or malformed lines is appropriate before relying on the output.

A practical checklist

  • Confirm the input uses one complete JSON value per physical line.

  • Choose and document how blank lines should be handled.

  • Track physical line numbers and report parse errors with useful details.

  • Write each serialized value followed by a newline.

  • Use a separate destination when you need to preserve the original file.

  • Check application-specific fields and types separately from JSON parsing.

A few more useful answers

Why not load the whole JSON Lines file with `json.load()`?

json.load() reads a JSON document, whereas this approach treats each physical line as a separate value. Iterate over the lines and parse them individually. [S1]

Does the example keep valid records when another line is malformed?

Yes. Its continue skips the malformed line and proceeds, while printing that line’s number and parse message. Remove that behavior or change it if your task should stop on an error.

Does a successful parse mean the record has the fields my program needs?

No. Parsing checks whether the line is valid JSON; it does not check application-specific requirements such as required keys or expected value types.

Sources

References used for this guide.

  1. [S1] Python json module: document framing and decode errors
    docs.python.org · Checked

YOUR QUESTION. YOUR DETAILS.

Let’s work through your situation.

Get a tailored answer or help with your next task. Starts at $1, with a quote before you pay.

Ask your own question
← Back to Geez at Work