Build a Simple ETL Pipeline in Python

A simple ETL pipeline that reads JSON Lines, transforms records with filtering and normalization, and writes the result to JSON.

Easy Python 3.9+ Aug 9, 2026 Files & data 13 views 0 copies

Python code

55 lines
Python 3.9+
import json
from pathlib import Path


def read_input(file_path: Path) -> list[dict]:
    """Read JSON lines file into list of dicts."""
    with file_path.open("r", encoding="utf-8") as f:
        return [json.loads(line) for line in f if line.strip()]


def transform(records: list[dict]) -> list[dict]:
    """Transform raw records: filter invalid, normalize fields."""
    cleaned = []
    for rec in records:
        if rec.get("value") is None:
            continue
        cleaned.append({
            "id": rec["id"],
            "value": float(rec["value"]),
            "status": rec.get("status", "unknown"),
        })
    return cleaned


def write_output(records: list[dict], output_path: Path) -> None:
    """Write transformed records to JSON file."""
    with output_path.open("w", encoding="utf-8") as f:
        json.dump(records, f, indent=2)


if __name__ == "__main__":
    input_file = Path("input.jsonl")
    output_file = Path("output.json")

    # Sample input data for demonstration
    sample_records = [
        {"id": 1, "value": "10.5", "status": "ok"},
        {"id": 2, "value": "20.0"},
        {"id": 3, "value": None},
        {"id": 4, "value": "30.25", "status": "flagged"},
    ]
    with input_file.open("w", encoding="utf-8") as f:
        for rec in sample_records:
            f.write(json.dumps(rec) + "\n")

    # ETL pipeline
    raw_data = read_input(input_file)
    transformed = transform(raw_data)
    write_output(transformed, output_file)

    # Verify and print result
    with output_file.open("r", encoding="utf-8") as f:
        result = json.load(f)
    print(f"Transformed {len(transformed)} records:")
    print(json.dumps(result, indent=2))

Output

stdout
Transformed 3 records:
[
  {
    "id": 1,
    "value": 10.5,
    "status": "ok"
  },
  {
    "id": 2,
    "value": 20.0,
    "status": "unknown"
  },
  {
    "id": 4,
    "value": 30.25,
    "status": "flagged"
  }
]

How it works

The pipeline uses pathlib.Path for cross-platform file handling and the standard json module for parsing and writing. read_input reads JSON Lines, skipping blank lines, while transform filters records without a value field and casts values to floats, defaulting status to 'unknown'. write_output writes the cleaned list as indented JSON. The if __name__ == '__main__' guard makes the functions importable and testable, while the demo creates sample data so you can run the script as-is. This design keeps each stage pure and easy to unit-test independently.

Common mistakes

  • Forgetting to skip blank lines in JSONL, causing parse errors
  • Assuming all records have a `status` key without using `.get()`
  • Not handling missing `value` fields before casting to float
  • Using `open()` without a context manager, leaking file handles

Variations

  1. Use `json.load()` directly on an open file handle for a single JSON document instead of JSONL
  2. Add `pandas.read_csv()` to ingest CSV input and convert to a DataFrame before transforming

Real-world use cases

  • Batch-processing raw API logs stored as JSONL into clean records for a data warehouse.
  • Normalizing and validating config dumps from multiple services before loading them into a feature store.
  • Moving CSV or JSON event files from an S3 bucket into a relational database with cleaning steps in between.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Files & data

Related tutorials and quizzes for this topic.