JSON Toolkit

JSON-Validierung in CI: eine Pipeline mit jsonlint, ajv und jq

Fazit: Syntaxvalidierung beweist, dass eine Datei parst; Schema-Validierung beweist, dass Felder dem Vertrag folgen. In CI zählen eindeutige Exit-Codes, Fehlerstellen und Reparaturwege.

Fortgeschritten~10 Min. LesezeitVeröffentlicht 2026-10-31Aktualisiert 2026-10-31
Titelbild für JSON-Validierung in CI

Kurz gesagt: In CI lässt man jq empty die Syntax erledigen, ajv-cli die Struktur (check-jsonschema für Teams mit mehreren Formaten) und Prettier den Stil; verketten Sie sie schichtweise und stoppen Sie beim ersten Fehlschlag.

Dass der Parser in Ihrem lokalen Editor durchgeht, heißt nicht, dass die Datei in CI gehört; dass CI besteht, heißt auch nicht, dass die Felder zum Vertrag passen.

1. Syntaxvalidierung von Schema-Validierung trennen

Der Ausdruck „JSON-Validierung“ wird viel zu weit verwendet, weshalb Teams oft ein Werkzeug wählen, um zwei verschiedene Probleme zu lösen:

Schicht Beantwortete Frage Typische Werkzeuge Was ein Fehlschlag bedeutet
Syntax Ist diese Datei gültiges JSON? jq empty, jsonlint, python -m json.tool Klammern, Kommas, Anführungszeichen oder Kodierung sind kaputt
Schema Passen Felder, Typen und Pflichteigenschaften zum Vertrag? ajv-cli, check-jsonschema Die Datei parst, aber die Geschäftsstruktur ist falsch
Stil Sind Einrückung und Schlüsselreihenfolge einheitlich? Prettier, jq -S Formatierung uneinheitlich und Diffs rauschend

Diese Schichten können einander nicht ersetzen. Eine Datei, die die Syntax besteht, kann trotzdem am Schema scheitern: {"price": "12.5"} ist gültiges JSON, verlangt das Schema aber price als Zahl, ist es nicht akzeptabel. Umgekehrt wird eine Datei mit fehlender schließender Klammer nie gegen ein noch so vollständiges Schema kompilieren.

Die Pipeline sollte also zuerst Syntax, dann Struktur und zuletzt Stil ausführen. Stoppen Sie, sobald eine Schicht scheitert, sonst tarnt sich ein Parse-Fehler als Feldfehler.

2. Syntaxschicht: jq empty ist die Option mit minimalen Abhängigkeiten

Ist jq bereits im CI-Image, kostet Syntaxvalidierung fast nichts:

jq empty data.json

jq empty erzeugt keine Ausgabe; es parst nur die Datei. Bei Erfolg endet es mit 0, bei Fehlschlag mit einem Nicht-Null-Code plus Zeilen-/Spalteninformation. Das macht es leichter als einen vollständigen Node-Abhängigkeitsbaum zu ziehen, und zu einem guten ersten Gate.

Beim Batch-Validieren von JSON-Dateien in einem Repository node_modules, Build-Ausgaben und Drittanbieter-Caches ausschließen:

find . -name '*.json' \
  -not -path './node_modules/*' \
  -not -path './dist/*' \
  -print0 | xargs -0 -n1 jq empty

-print0 und xargs -0 behandeln Leerzeichen in Dateinamen, während -n1 eine Datei pro Aufruf sicherstellt, damit die Fehlermeldung den Übeltäter nennt. Übergeben Sie nicht Hunderte von Dateien an einen einzigen Prozess; Fehlermeldungen überschreiben sich sonst.

jsonlint ist eine andere verbreitete Wahl, meist als npx jsonlint -q data.json aufgerufen, wobei -q nur Fehler ausgibt. Es meldet Zeilen- und Spaltennummern, aber seine Wartung ist weniger aktiv als bei jq, und verschiedene Versionen tolerieren BOM und Kommentare unterschiedlich. Behandeln Sie es als lesbareren Syntaxprüfer, nicht als Schema-Validator.

3. Schema-Schicht: die Grundlagen von ajv-cli validate

Ajv ist der am weitesten verbreitete Schema-Validator im JavaScript-Ökosystem, und ajv-cli exponiert ihn als Kommandozeilenwerkzeug:

ajv validate -s schema.json -d data.json

-s zeigt auf die Schemadatei, -d auf die zu validierenden Daten. Bei Fehlschlag endet der Prozess mit Nicht-Null, CI scheitert also sofort. Reale Projekte ergänzen meist zwei Flags:

npx ajv-cli validate \
  -s schemas/order.json \
  -d 'fixtures/orders/*.json' \
  --strict=false \
  --errors=text

--strict=false hilft bei älteren Schemas: Der Strict Mode behandelt unbekannte Schlüsselwörter und verdächtige required-Deklarationen als Fehler, was viele Legacy-Schemas scheitern lässt. --errors=text erzeugt menschenlesbare Ausgabe, während die standardmäßige JSON-Struktur besser für Maschinen ist. Wählen Sie danach, wer das Ergebnis konsumiert.

Auch die Draft-Version des Schemas muss passen. Ajv-Hauptversionen unterstützen standardmäßig verschiedene Drafts, und ajv-cli kann mit --spec=draft7, --spec=draft2019 oder --spec=draft2020 einen auswählen. Deklariert ein Schema $schema: "https://json-schema.org/draft/2020-12/schema", die CLI kompiliert es aber als draft-07, werden manche Schlüsselwörter stillschweigend ignoriert. Mehr zu Schemaerstellung und Versionsunterschieden siehe Erste Schritte mit JSON Schema.

Formatvalidierung ist eine weitere häufige Anforderung. Das Schlüsselwort format ist standardmäßig nur Annotation; damit email, date-time und uri tatsächlich etwas erzwingen, laden Sie ajv-formats:

npx ajv-cli validate -c ajv-formats -s schema.json -d data.json

Dieser Schritt wird leicht übersehen, und das Ergebnis ist, dass ein E-Mail-Feld not-an-email akzeptiert. Geben Sie der Pipeline vor der Auslieferung einen absichtlich ungültigen Datensatz; das beweist weit besser, dass die Regel aktiv ist, als die Dokumentation zehnmal zu lesen.

check-jsonschema: mehrere Formate und freundlichere Fehler

Verarbeitet das Team neben JSON auch YAML, TOML oder GitHub-Actions-Konfiguration, ist check-jsonschema bequemer:

check-jsonschema --schemafile schemas/config.json config/app.json
check-jsonschema --schemafile schemas/pipeline.json .github/workflows/ci.yml

Seine praktischen Vorteile:

  1. Native YAML-Unterstützung, die zu Dateien passt, die „JSON-förmig, aber in YAML geschrieben“ sind, etwa Kubernetes-Manifeste und CI-Konfiguration.
  2. Fehler enthalten Dateipfad, Zeilennummer und Feldpfad, was die Lokalisierungskosten niedrig hält.
  3. Ein pre-commit-Hook wird bereitgestellt, die Integration ist also billig.
  4. Es kann gängige Schemas aus SchemaStore ziehen, akzeptiert aber auch lokale Dateien, was eine Netzwerkabhängigkeit vermeidet.

In sprachübergreifenden Teams ist Python oft schon vorhanden, check-jsonschema zu installieren und zu aktualisieren ist also leichter, als eine Node-Toolchain zu pflegen. Lassen Sie jq die Syntax und dieses Werkzeug die Struktur erledigen; die Kombination hat sehr geringen Wartungsaufwand.

4. Exit-Codes sind das einzige verlässliche CI-Signal

So poliert die Ausgabe auch ist, CI liest nur Exit-Codes. Drei Fallen sind verbreitet:

Symptom Ursache Behebung
Es gibt Fehler, aber CI ist grün Der Befehl endet mit `
Keine Fehler, aber CI ist rot diff behandelt „Unterschiede gefunden“ als Fehlschlag „Unterschied“ von „Ausführungsfehler“ unterscheiden
Fehlerausgabe ist abgeschnitten Parallele Mehrdatei-Ausgabe überschreibt sich Seriell laufen lassen oder pro Datei ausgeben

Merken Sie sich die Exit-Code-Semantik von jq: Normales Parsen ergibt 0; mit -e ergibt ein Ausdrucksergebnis false oder null den Wert 1; Nutzungsfehler und fehlende Dateien ergeben 2; Skriptkompilierungsfehler ergeben 3. Schema-Werkzeuge verwenden meist 0 für Erfolg, 1 für Validierungsfehler und 2 für Werkzeug- oder Argumentfehler. Kollabieren Sie in einem Wrapper nicht jeden Nicht-Null-Code zu „die Daten sind falsch“, sonst wird ein Umgebungsfehler als Inhaltsproblem fehldiagnostiziert.

5. pre-commit: Probleme vor dem Commit abfangen

Der Wert von pre-commit ist nicht „eine Prüfung mehr“; er ist, Entwicklern in einer Minute Feedback zu geben statt nach einem zehnminütigen CI-Lauf. Eine minimale Konfiguration sieht so aus:

repos:
  - repo: https://github.com/python-jsonschema/check-jsonschema
    rev: 0.33.0
    hooks:
      - id: check-jsonschema
        files: ^configs/.*\.json$
        args:
          - --schemafile
          - schemas/config.json
  - repo: local
    hooks:
      - id: jq-syntax
        name: jq syntax check
        entry: jq empty
        language: system
        files: ^configs/.*\.json$

Zwei Details zählen:

  • files sollte nur die Verzeichnisse treffen, die validiert werden müssen. Führen Sie Hooks nicht gegen node_modules oder generierte Dateien aus.
  • Der lokale Hook setzt voraus, dass jq auf dem Entwicklerrechner installiert ist. Dokumentieren Sie diese Anforderung oder wechseln Sie zu einem containerisierten Hook.

Verwendet das Projekt bereits Husky und lint-staged, kombinieren Sie sie direkt:

{
  "lint-staged": {
    "configs/*.json": ["jq empty", "prettier --check"]
  }
}

Denken Sie daran, dass lint-staged eine Liste gestagter Dateien übergibt, der Befehl also mehrere Pfade akzeptieren muss. Beides tun jq empty und prettier --check. Lassen Sie einen Hook nicht das ganze Repository scannen; Commits werden sonst merklich langsamer.

6. GitHub Actions: eine Pipeline zum Kopieren

Der folgende Workflow führt Syntaxvalidierung und Schema-Validierung aus und bewahrt den Bericht bei Fehlschlag:

name: json-validate

on:
  push:
    paths:
      - 'schemas/**'
      - 'fixtures/**'
  pull_request:

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - name: Syntax check
        run: |
          find fixtures -name '*.json' -print0 | xargs -0 -n1 jq empty
      - name: Schema check
        run: |
          npx --yes ajv-cli validate \
            -s schemas/order.json \
            -d 'fixtures/orders/*.json' \
            --strict=false \
            --errors=text

Der paths-Filter verhindert, dass reine Dokumentationsänderungen die Validierung auslösen. npx --yes passt zu Repositories ohne festgelegte Abhängigkeiten; Produktionsprojekte sollten ajv-cli in devDependencies aufnehmen und die Version festnageln. Ohne Lock kann ein Upstream-Release, das den Standard-Draft oder den Strict Mode ändert, die Pipeline über Nacht rot machen.

7. Fehlerlokalisierung: von Zeilennummern zu Feldpfaden

Die Hälfte des Werts eines Validators ist Erkennung; die andere Hälfte ist Lokalisierung. Lernen Sie, drei Arten von Ausgabe zu lesen:

  1. Syntaxfehler: jq meldet Zeile und Spalte, oft mit einem nahen Ausschnitt. Korrigieren Sie zuerst Klammern und Kommas; verdächtigen Sie nicht sofort die Kodierung.
  2. Schemafehler: Ajvs instancePath zeigt auf die Datenstelle (etwa /items/0/price), schemaPath zeigt auf die Regel (etwa #/properties/items/items/properties/price/type), und keyword nennt die verletzte Beschränkung.
  3. Formatfehler: Scheitert eine format-Prüfung, sagt die Meldung oft nur „must match format "email"“. Kombinieren Sie sie mit instancePath, um das Feld zu identifizieren.

Kopieren Sie instancePath in den Untersuchungsdatensatz; es ist weit nützlicher als eine Wand aus JSON. Bei Arrays sagt der numerische Index auch, welcher Datensatz gescheitert ist. Ist der Geschäftsschlüssel intuitiver, ergänzen Sie danach eine Abbildungszeile wie items[0].id = ORD-042.

Häufige Schemafehler, etwa additionalProperties: false zu überstrapazieren oder oneOf-Zweige zu schreiben, die sich tatsächlich überlappen, werden in Häufige Fallstricke der JSON-Schema-Generierung behandelt. Eine falsche Validierungsregel ist gefährlicher als keine Validierung, weil sie ein falsches Sicherheitsgefühl erzeugt.

8. Werkzeugauswahltabelle

Bedarf Empfohlenes Werkzeug Warum Worauf achten
Nur Syntax jq empty Null Abhängigkeiten, schnell, klare Exit-Codes Leerzeichen in Dateinamen bei Batch-Läufen
Freundlichere Zeilen-/Spaltenfehler jsonlint Lesbare Ausgabe Wartungsaktivität ist begrenzt
JSON-Schema-Validierung ajv-cli Ausgereiftes Ökosystem, strukturierte Fehler Draft-Versionen abstimmen und format-Unterstützung laden
Gemischtes JSON-/YAML-Repository check-jsonschema Mehrformate, pre-commit-freundlich Schemaquelle und Netzwerkzugriff
Schnelles Feedback vor dem Commit pre-commit / lint-staged Verarbeitet nur gestagte Dateien Hooks müssen mehrere Pfade akzeptieren
Einheitliche Formatierung Prettier Weit verbreitet --check verwenden, wenn Sie Dateien nicht neu schreiben wollen

Eine letzte Empfehlung: Prüfen Sie, dass die Pipeline tatsächlich scheitert, indem Sie ihr zuerst einen absichtlich ungültigen Datensatz geben. Das größte Risiko ist nicht eine komplizierte Konfiguration; es ist eine Konfiguration, die nie wirksam wird. „Sie kann scheitern“ als Abnahmekriterium zu behandeln ist verlässlicher als „sie besteht“. Zur breiteren Kommandozeilen-Toolchain lesen Sie weiter in Editoren, Kommandozeile und Automatisierung.

FAQ

Sollte ich jq empty oder jsonlint verwenden?

Bevorzugen Sie in CI jq empty: null Abhängigkeiten, schneller Start und klare Exit-Code-Semantik. Verwenden Sie jsonlint, wenn Sie freundlichere Zeilen-/Spaltenmeldungen brauchen oder lokal untersuchen. Beide führen nur Syntaxvalidierung aus; keines prüft die Feldstruktur.

Warum meldet ajv ein unbekanntes Schlüsselwort?

Bestätigen Sie zuerst, ob der $schema-Draft des Schemas zur Version passt, die die CLI verwendet. Viele „unbekanntes Schlüsselwort“-Fehler sind Schlüsselwörter neuer Drafts, auf die ein älterer Compiler stößt. Passen die Versionen, prüfen Sie auf einen Tippfehler. --strict=false kann Legacy-Schemas am Laufen halten, aber verlassen Sie sich nicht langfristig darauf, echte Probleme zu verbergen.

Schema-Validierung besteht, aber die API scheitert trotzdem. Warum?

Ein Schema validiert Struktur, nicht Geschäftssemantik. status mag etwa ein String mit zulässigem Enum sein, während die Zustandsmaschine verbietet, direkt von paid nach refunded zu gehen. Solche Regeln gehören in Code oder Datenbankbeschränkungen, nicht in JSON Schema.

Wie lokalisiere ich das scheiternde Feld schnell?

Lesen Sie instancePath oder die Zeilennummer aus der Werkzeugausgabe. Ajvs instancePath ist ein JSON Pointer, der direkt auf die Daten zeigt; check-jsonschema meldet Datei und Zeile. Notieren Sie den Pfad und bilden Sie ihn auf den Geschäftsschlüssel ab; die Lokalisierung dauert meist Minuten.

Sollte pre-commit eine vollständige Validierung ausführen?

Nein. pre-commit sollte nur gestagte Dateien verarbeiten, damit Commits schnell bleiben. Vollständige Scans gehören in CI oder geplante Jobs. Die Aufteilung ist einfach: lokale Läufe bevorzugen Geschwindigkeit, CI bevorzugt Vollständigkeit.