Rand Stats

JSONL

zef:apogee

Actions Status

NAME

JSONL - Read, write, edit, and filter JSONL (JSON Lines) files

SYNOPSIS

use JSONL;

# Convenience functions
my @lines = read-jsonl('data.jsonl'.IO).list;
write-jsonl('output.jsonl'.IO, [%(:name("Alice")), %(:name("Bob"))]);
append-jsonl('output.jsonl'.IO, %(:name("Charlie")));

# Direct hash-like access on lines
for read-jsonl('data.jsonl'.IO) -> $line {
    say $line<name>;           # Hash-like access
    say $line<meta><score>;    # Nested access
    say $line.line-number;     # 1-based source line number
}

# Grep with clean syntax
my @adults = read-jsonl('data.jsonl'.IO).grep({ $_<age> > 18 }).list;

DESCRIPTION

JSONL provides a complete toolkit for working with JSONL (JSON Lines / NDJSON) files. Supports any JSON value type per line (objects, arrays, scalars).

JSONL::Line

A single parsed JSONL line. Pairs the parsed value with its source line number. Supports hash-like ($line<key>) and array-like ($line[0]) access.

my JSONL::Line $line = ...;
$line.value;                  # The parsed JSON value (Hash, Array, Str, Int, etc.)
$line.line-number;            # 1-based line number from source
$line.to-json;                # Serialize back to compact JSON string
$line.Str;                    # Same as to-json

# Hash-like access (when value is a Hash)
$line<name>;                  # AT-KEY
$line<name>:exists;           # EXISTS-KEY
$line.keys;                   # Key list
$line.values;                 # Value list
$line.kv;                     # Key-value pairs
$line.elems;                  # Number of elements

# Array-like access (when value is an Array)
$line[0];                     # AT-POS
$line[0]:exists;              # EXISTS-POS

JSONL::Reader

Lazy/streaming JSONL reader. Accepts file paths or IO handles.

# From file
my JSONL::Reader $r .= new(:path('data.jsonl'.IO));

# From handle (stdin, pipes)
my JSONL::Reader $r .= new(:handle($*IN));

# Lenient mode: skip blank lines, collect malformed as warnings
my JSONL::Reader $r .= new(:path($path), :lenient);
say $r.warnings;              # Lines that failed to parse

# Methods
$r.lines;                     # Lazy Seq of JSONL::Line
$r.list;                      # Eager List of all lines
$r.head(10);                  # First 10 lines
$r.tail(10);                  # Last 10 lines
$r.line-at(5);                # Line at 0-based index
$r.count;                     # Total line count
$r.grep({ $_<age> > 18 });   # Filter lines
$r.sample(100);               # Random sample (reservoir sampling)
$r.summary;                   # Stats: count, types, keys
$r.schema(:sample(100));      # Infer field=>type map

JSONL::Writer

Writes JSONL to files or handles. Always compact JSON, one value per line.

# To file
my JSONL::Writer $w .= new(:path('out.jsonl'.IO));
my JSONL::Writer $w .= new(:path($path), :!sorted-keys);  # Disable key sorting

# To handle
my JSONL::Writer $w .= new(:handle($fh));

# Methods
$w.write-line(%(:id(1)));     # Write single value + newline
$w.write-all(@values);        # Write all values (overwrites file)
$w.append(%(:id(2)));         # Open in append mode, write, close
$w.append-many(@values);      # Append multiple values
$w.close;                     # Close handle if we opened it

JSONL::Editor

In-place file editing. Reads the full file, applies the edit, writes it back.

my JSONL::Editor $e .= new;

$e.update-at($path, 0, %(:id(99)));       # Replace line at index
$e.delete-at($path, 2);                    # Remove line at index
$e.insert-at($path, 1, %(:id(50)));       # Insert at index
$e.transform($path, -> $line {            # Transform each line
    $line.value<id> > 5 ?? $line.value !! Nil  # Return Nil to delete
});

# All methods accept :lenient flag
$e.update-at($path, 0, $val, :lenient);

AUTHOR

Matt Doughty matt@apogee.guru

COPYRIGHT AND LICENSE

Copyright 2026 Matt Doughty

This library is free software; you can redistribute it and/or modify it under the Artistic License 2.0.