NCA Social Media
Erstellt:
Aktualisiert:
Autor:
Roland Golla
Grüner Güterzug mit Lok JSONL und Wagen mit JSON Würfeln

JSONL: Definition

JSONL (JSON Lines) ist ein Textformat, bei dem jede Zeile ein eigenständiges, vollständiges JSON Objekt enthält. Zeilenumbrüche trennen die Datensätze, ein umschließendes Array und Kommas zwischen den Objekten gibt es nicht.
Der Nutzen liegt im Streaming: Eine JSONL Datei lässt sich Zeile für Zeile lesen, ohne sie komplett in den Speicher zu laden. Deshalb ist das Format Standard für Logs, Event Streams, Datensätze für KI Modelle und seit PHPUnit 13.4 auch für Coverage Reports. Die Dateiendung ist .jsonl, der zweite Name lautet NDJSON (newline delimited JSON).
Inhalt

JSONL mit NCA: Schnelle Hilfe vom Experten

Wir arbeiten täglich mit PHPUnit, PHPStan, Psalm und Rector in Symfony Projekten und bauen Quality Gates in GitHub Actions und GitLab CI. Coverage Reports sind für uns kein Dashboard Schmuck, sondern Arbeitsmaterial: Sie zeigen, wo Tests fehlen. Mit JSONL wird dieses Material maschinenlesbar und passt direkt in die Werkzeuge, mit denen wir Tests schreiben, vom Shell Skript bis zum KI Coding Agent.
Teams begleiten wir im PHP Consulting, beim Refactoring Workshop und im Vibe Coding Consulting, wenn KI Agents Testlücken schließen sollen. Die Grundlagen stehen im Glossar: PHPUnit als Testframework, Code Coverage als Kennzahl, Xdebug als Treiber und Patch Coverage für neue Zeilen im CI.
NCA KI Workshop für Developer

Agentic Coding im Team, nicht nur im Glossar

Ein Tag inhouse mit Benjamin Klein für Developer, Tester und Admins. Context Engineering, Harness, ADRs und zwei Hands on Blöcke mit KI Agenten in eurem Setup.

  1. 1
    Kostenloses Vorgespräch
    Stand, Rollen und Ziele klären
  2. 2
    Workshoptag bei euch
    Praxis am Vormittag, Hands on danach
  3. 3
    Praxistag optional
    Agenten im eigenen Projekt
Benjamin Klein
Benjamin Klein
Senior Consultant
Zum Workshop für Developer arrow_forward

JSONL und JSON: Der Unterschied an einem Beispiel

Eine normale JSON Datei packt alle Datensätze in ein Array. Der Parser muss bis zur schließenden Klammer lesen, bevor er den ersten Eintrag zurückgeben kann. Bei großen Dateien heißt das: alles im Speicher, lange Wartezeit, und ein einziger Tippfehler macht die ganze Datei unbrauchbar.
Code:
          

[
  {"event": "login", "user": 42, "ts": "2026-10-14T09:12:00Z"},
  {"event": "checkout", "user": 42, "ts": "2026-10-14T09:15:31Z"}
]

Dieselben Daten als JSONL. Jede Zeile steht für sich, keine Klammern außen, keine Kommas zwischen den Objekten:
Code:
          

{"event": "login", "user": 42, "ts": "2026-10-14T09:12:00Z"}
{"event": "checkout", "user": 42, "ts": "2026-10-14T09:15:31Z"}

Das ändert alles bei der Verarbeitung. Ein neuer Datensatz wird einfach hinten angehängt. Ein Programm liest Zeile für Zeile und kann nach jeder Zeile schon reagieren. Eine kaputte Zeile betrifft nur diesen einen Eintrag, der Rest bleibt lesbar. Und weil Zeilen unabhängig sind, lassen sich Dateien ohne Umbau zusammenfügen oder parallel verarbeiten. Die Spezifikation auf jsonlines.org braucht dafür genau drei Regeln: UTF-8, jede Zeile ein gültiger JSON Wert, Zeilentrenner ist der Zeilenumbruch.

Wo JSONL hinpasst: Logs, Streams und KI Datensätze

JSONL ist die richtige Wahl, wenn Daten nacheinander entstehen oder nacheinander verarbeitet werden. Das trifft auf überraschend viele Alltagsfälle zu:
  • Logs und Events: Jeder Eintrag ist eine Zeile. tail -f zeigt live, was passiert, und Tools wie Grafana Loki oder Sentry Exporte liefern das Format direkt.
  • Streaming zwischen Diensten: Ein Prozess schreibt, der andere liest, ohne auf das Ende der Datei zu warten. Die Zeile ist die natürliche Nachrichtengrenze.
  • Datensätze für KI Modelle: Trainingsdaten, Evaluationen und Prompt Protokolle liegen praktisch überall als JSONL vor, bei Hugging Face genauso wie beim Fine Tuning über Ollama.
  • Exporte und Backups: Millionen Zeilen aus einer Tabelle lassen sich mit konstantem Speicherbedarf schreiben und wieder einlesen.
  • Werkzeug Ausgaben: Linter, Testrunner und Coverage Tools geben Befunde als JSONL aus, damit das nächste Werkzeug sie filtern kann, ohne den ganzen Bericht zu parsen.
Nicht hin passt JSONL, wenn ein Konsument das ganze Dokument auf einmal braucht: eine Konfigurationsdatei, eine API Antwort mit einem einzelnen Objekt, verschachtelte Strukturen mit Querverweisen. Dort bleibt normales JSON die bessere Wahl.

JSONL in PHP lesen und schreiben

PHP braucht keine Bibliothek dafür. Ein Generator liest die Datei zeilenweise und liefert jedes Objekt einzeln. So bleibt der Speicherbedarf konstant, egal ob die Datei zehn oder zehn Millionen Zeilen hat:
Code:
          

function readJsonl(string $path): Generator
{
    $handle = fopen($path, 'r');

    while (($line = fgets($handle)) !== false) {
        $line = trim($line);

        if ($line === '') {
            continue;
        }

        yield json_decode($line, true, 512, JSON_THROW_ON_ERROR);
    }

    fclose($handle);
}

foreach (readJsonl('events.jsonl') as $event) {
    echo $event['event'], PHP_EOL;
}

Beim Schreiben ist nur eine Regel wichtig: Jedes Objekt bekommt genau einen Zeilenumbruch hinten dran, und das JSON selbst darf keinen enthalten. json_encode kümmert sich um das zweite, weil es Zeilenumbrüche in Strings als \n escaped. JSON_UNESCAPED_UNICODE hält Umlaute lesbar:
Code:
          

function appendJsonl(string $path, array $record): void
{
    $line = json_encode(
        $record,
        JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
    );

    file_put_contents($path, $line . PHP_EOL, FILE_APPEND | LOCK_EX);
}

appendJsonl('events.jsonl', ['event' => 'login', 'user' => 42]);

Wer solche Helfer in einem Symfony Projekt braucht, legt sie als Service ab und lässt PHPStan die Typen prüfen. Für Tests reicht ein Fixture mit drei Zeilen und ein PHPUnit Data Provider.

PHPUnit 13.4: Coverage Report als JSONL

Seit PHPUnit 13.4 (Oktober 2026) gibt es die CLI Option --coverage-jsonl und das passende Element jsonl in der XML Konfiguration. Der Report ist anders gebaut als Clover oder HTML: Er listet nicht jede ausführbare Zeile, sondern nur den Code, der nicht abgedeckt wurde. Das Ergebnis ist kurz und direkt eine Aufgabenliste.
Code:
          

XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-jsonl build/coverage-jsonl

Dauerhaft landet das in der phpunit.xml unter dem Report Element:
Code:
          

<coverage>
    <report>
        <jsonl outputDirectory="build/coverage-jsonl"/>
    </report>
</coverage>

Das Ziel ist ein Verzeichnis, keine einzelne Datei. Die Bibliothek php-code-coverage 14.4 schreibt dort drei Dateien hinein:
  • meta.json: Schema Version, Generator, Zeitpunkt, Source Root, ob Branch Coverage erfasst wurde, und die Summen für Dateien, ausführbare und ausgeführte Zeilen.
  • coverage.jsonl: ein Objekt pro Quelldatei mit ausführbarem Code. Enthalten sind der Pfad relativ zum Source Root, die Zahl der ausführbaren und ausgeführten Zeilen, die Liste der nicht ausgeführten Zeilen und ein Eintrag pro Methode oder Funktion mit Status covered, partial oder uncovered.
  • tests.jsonl: ein Objekt pro Test, der mindestens eine Zeile ausgeführt hat, mit seinem Identifier und den Zeilen je Quelldatei.
Zwei Details machen den Report CI tauglich: Dateien ohne ausführbaren Code tauchen gar nicht auf, und die Einträge sind nach Pfad sortiert mit Slash als Trenner auf allen Plattformen. Dieselben Coverage Daten erzeugen so byteidentische Ausgaben. Ein Diff zwischen zwei Läufen zeigt damit exakt, welche Lücken neu dazugekommen sind. Was der Report als Zahl bedeutet und wo seine Grenzen liegen, steht auf der Seite zu Code Coverage. Wer Reports aus mehreren Läufen zusammenführt, bleibt bei phpcov und dem PHP Format.

Coverage JSONL als Input für KI Coding Agents

Hier zeigt sich, warum das Format gerade jetzt kommt. Ein KI Coding Agent wie OpenCode soll Testlücken schließen. Mit einem HTML Report kann er nichts anfangen, ein Clover XML mit jeder einzelnen Zeile sprengt sein Context Window. Die coverage.jsonl dagegen ist eine kompakte Liste: diese Datei, diese Methode, diese Zeilen fehlen.
Der Ablauf in der Praxis: Der Agent liest nur die Zeilen, deren Datei zur aktuellen Aufgabe passt, schreibt Tests für die gelisteten Methoden, lässt PHPUnit erneut laufen und vergleicht den neuen Report. Ein Verzeichnis, das nach dem zweiten Lauf weniger Zeilen in coverage.jsonl hat, ist der Nachweis, dass sich etwas getan hat. Mit tests.jsonl sieht der Agent zusätzlich, welcher Test welche Zeile berührt, und kann doppelte Tests vermeiden.
Eine Warnung gehört dazu: Ein Agent, der nur die Zahl nach oben treiben soll, produziert Tests ohne Assertion. PHPUnit markiert solche Tests als riskant, und Infection entlarvt sie beim Mutation Testing. Beides gehört in die Pipeline, bevor ein Agent an die Coverage geht. Wie wir solche Guardrails für Teams aufsetzen, beschreibt das Vibe Coding Consulting.

JSONL mit jq und Unix Tools auswerten

Der große Vorteil von JSONL ist, dass die normalen Kommandozeilenwerkzeuge funktionieren. Zeilen zählen, die letzten Einträge zeigen, nach einem Begriff filtern: alles ohne JSON Parser.
Code:
          

wc -l build/coverage-jsonl/coverage.jsonl

tail -n 5 build/coverage-jsonl/coverage.jsonl

grep '"src/Checkout/' build/coverage-jsonl/coverage.jsonl

Für alles, was in die Struktur hineingeht, ist jq das Werkzeug der Wahl. Es verarbeitet JSONL nativ, ein Objekt nach dem anderen:
Code:
          

# Dateien mit den meisten ungetesteten Zeilen zuerst
jq -c '{path, uncovered: (.uncoveredLines | length)}' coverage.jsonl \
  | sort -t: -k3 -nr | head -n 10

# Nur komplett ungetestete Methoden ausgeben
jq -c '.functions[] | select(.status == "uncovered") | .name' coverage.jsonl

Die Feldnamen in diesen Beispielen hängen vom Schema in meta.json ab, das die Bibliothek mit der Version pflegt. Vor dem ersten Skript lohnt ein Blick in eine echte Zeile mit jq '.' und head -n 1. Ein Fallstrick: jq --slurp liest alles in ein Array und verschenkt damit den Streaming Vorteil. Für große Dateien bleibt man bei der zeilenweisen Verarbeitung.

JSONL, JSON und CSV im Vergleich

Format Stärken Typischer Einsatz
JSONL Streambar, anhängbar, verschachtelte Werte, fehlertolerant pro Zeile, grep und jq tauglich Logs, Event Streams, KI Datensätze, Coverage und Lint Reports, große Exporte
JSON Ein Dokument, beliebig tief verschachtelt, überall unterstützt Konfiguration, API Antworten, einzelne Objekte, Dokumente mit Querverweisen
CSV Klein, von jeder Tabellenkalkulation lesbar Flache Tabellen ohne Verschachtelung, Austausch mit Fachabteilungen
Clover XML Etabliert in CI Diensten und IDEs, jede Zeile mit Zähler Coverage Upload zu Codecov, Coveralls und SonarQube, IDE Anzeige

JSONL is one of those formats that looks almost too simple.

Flavio Copes, Entwickler und Autor, flaviocopes.com – JSONL: a practical guide to JSON Lines

Die NCA Erfahrung mit maschinenlesbaren Reports

Ein Bericht ist erst dann wertvoll, wenn das nächste Werkzeug ihn ohne Umweg lesen kann. Darauf achten wir in jeder Pipeline, die wir aufsetzen: Ausgaben von Linter, Testrunner und Coverage landen in Formaten, die ein Skript, ein Dashboard oder ein KI Agent direkt weiterverarbeitet. JSONL ist dafür in den letzten Jahren zum Standard geworden, und mit PHPUnit 13.4 kommt es nun auch in der PHP Welt an.
Die Basis bilden PHPUnit für Unit Tests, Functional Tests und Symfony KernelTestCase für das Zusammenspiel der Services. Coverage Daten sammeln Xdebug und PCOV, Paratest hält die Laufzeit kurz, und otr-report macht Testergebnisse lesbar. Vor dem Commit greift GrumPHP, im CI kommen PHPStan, Psalm und Rector dazu. Über die Oberfläche testen wir mit Cypress, und Workflows, die Reports einsammeln und verteilen, bauen wir mit n8n.
Alte Projekte sichern wir zuerst mit Characterization Tests ab. Erst wenn die Quality Gates stehen, darf ein Agent an die Testlücken, begleitet im PHP Consulting und im Vibe Coding Consulting.
CYPRESS.IO Ambassador und IT Consultant für QA Engenieering und Qualität in PHP Projekten.
Erreichen Sie unsere PHP Consultant Spezialisten

Wir sind Experten für PHP und helfen Ihnen, Ihre digitalen Herausforderungen zu meistern. Unser erfahrenes Team unterstützt Sie bei PHP Updates, PHP Refactoring und berät Sie remote zu allen Fragen rund um PHP. Mit unseren vollautomatischen CI/CD Deployments und einer robusten Docker-Infrastruktur bringen wir Ihre PHP-Projekte auf das nächste Level. Vertrauen Sie auf unsere Expertise für zuverlässige und skalierbare PHP-Lösungen.

Häufige Fragen zu JSONL

Die wichtigsten Fragen zu JSONL, JSON Lines in PHP und dem neuen Coverage Report in PHPUnit 13.4 kurz beantwortet.
Was ist eine JSONL Datei 2026?

Eine JSONL Datei (JSON Lines) enthält pro Zeile ein vollständiges JSON Objekt, getrennt durch Zeilenumbrüche. Es gibt kein umschließendes Array und keine Kommas zwischen den Einträgen. Dadurch lässt sich die Datei zeilenweise lesen und anhängen, ohne sie komplett in den Speicher zu laden. Die Endung ist .jsonl, der zweite Name ist NDJSON.

Was macht PHPUnit 13.4 mit JSONL 2026?

PHPUnit 13.4 bringt die Option --coverage-jsonl und das Element jsonl in der XML Konfiguration. Damit entsteht ein Coverage Report, der nur ungetesteten Code listet statt jeder ausführbaren Zeile. In das Zielverzeichnis kommen meta.json mit den Summen, coverage.jsonl mit einem Objekt pro Quelldatei und tests.jsonl mit einem Objekt pro Test.

Wann sollte ich JSONL statt JSON nutzen 2026?

JSONL passt, wenn Datensätze nacheinander entstehen oder verarbeitet werden: Logs, Event Streams, große Exporte, Trainingsdaten für KI Modelle und Berichte von Werkzeugen. Normales JSON bleibt besser, wenn ein Konsument das ganze Dokument auf einmal braucht, etwa bei Konfigurationsdateien oder einer API Antwort mit einem einzelnen Objekt.

Wie lese ich JSONL in PHP 2026?

Mit fopen und fgets Zeile für Zeile, jede Zeile durch json_decode. Ein Generator mit yield liefert die Objekte einzeln und hält den Speicherbedarf konstant. Leere Zeilen überspringst du mit trim, Fehler fängst du mit JSON_THROW_ON_ERROR ab. Eine Bibliothek ist dafür nicht nötig.

Warum ist JSONL für KI Coding Agents wichtig 2026?

Ein Agent kann JSONL zeilenweise filtern und muss nicht den ganzen Bericht ins Context Window laden. Der PHPUnit Coverage Report als JSONL ist dadurch direkt eine Aufgabenliste: diese Datei, diese Methode, diese Zeilen fehlen. Nach dem nächsten Testlauf zeigt ein Diff, ob die Lücke kleiner geworden ist.

Wie schreibe ich JSONL in PHP?

Jedes Objekt mit json_encode in einen String wandeln und mit PHP_EOL abschließen, dann per file_put_contents mit FILE_APPEND anhängen. Zeilenumbrüche innerhalb von Strings escaped json_encode automatisch. JSON_UNESCAPED_UNICODE sorgt dafür, dass Umlaute lesbar bleiben.

Worin unterscheiden sich JSONL und NDJSON?

Technisch sind beide dasselbe: ein JSON Wert pro Zeile, getrennt durch Zeilenumbruch, UTF-8 kodiert. JSONL ist der Name von jsonlines.org, NDJSON steht für newline delimited JSON. Die Dateiendungen .jsonl und .ndjson sind austauschbar, die meisten Werkzeuge akzeptieren beide.

Welche Werkzeuge verarbeiten JSONL auf der Kommandozeile?

Alles, was zeilenorientiert arbeitet: wc -l zählt Einträge, tail zeigt die letzten, grep filtert nach Text. Für Abfragen in die Struktur ist jq das Werkzeug der Wahl, es liest JSONL nativ Objekt für Objekt. Die Option --slurp lädt dagegen alles in ein Array und verschenkt den Streaming Vorteil.

Was passiert bei einer kaputten Zeile in JSONL?

Nur dieser eine Eintrag ist betroffen. Alle anderen Zeilen bleiben gültig und lesbar, weil jede Zeile für sich steht. Beim Einlesen entscheidest du, ob du die fehlerhafte Zeile überspringst oder den Lauf abbrichst. Bei einer normalen JSON Datei macht ein einzelner Syntaxfehler dagegen das ganze Dokument unbrauchbar.

Ersetzt der JSONL Report Clover oder HTML in PHPUnit?

Nein, er ergänzt sie. Clover XML bleibt das Format für Codecov, Coveralls, SonarQube und IDEs, HTML bleibt der Bericht für Menschen. JSONL ist für Skripte, CI Checks und KI Agents gedacht, die gezielt die Lücken brauchen. Mehrere Formate lassen sich in einem Lauf gleichzeitig erzeugen.

Brauche ich Xdebug oder PCOV für den JSONL Report?

Ja, wie für jeden Coverage Report. PHPUnit misst Coverage nicht selbst, sondern über eine PHP Extension. PCOV liefert Line Coverage und ist schnell, Xdebug im Coverage Modus liefert zusätzlich Branch Coverage, die dann auch in coverage.jsonl auftaucht. Ohne Treiber bleibt das Verzeichnis leer.

Warum sind die JSONL Coverage Dateien sortiert?

Die Bibliothek php-code-coverage sortiert Einträge nach Pfad und Test Identifier und nutzt auf allen Plattformen den Slash als Trenner. Dieselben Daten erzeugen so byteidentische Dateien. Das macht einen Diff zwischen zwei Läufen aussagekräftig und erlaubt Checks im CI, die neue Lücken erkennen.