json.csv.de

Ratgeber · Format & Standards

Verschachteltes JSON flachklopfen: von Nested zu Flat-CSV

JSON kennt Tiefe, CSV kennt nur Zeilen und Spalten. Dieser Leitfaden zeigt die vier zentralen Strategien, mit denen du jede Verschachtelung sauber in eine flache Tabelle überführst.

7 Min Lesezeit 1.633 Wörter 4 FAQs
Jan-Tristan Rudat
Jan-Tristan RudatRedakteur
Geprüft am

JSON und CSV beschreiben Daten auf grundlegend verschiedene Weise. CSV ist ein zweidimensionales Format: Zeilen und Spalten, jede Zelle hält genau einen Skalarwert. JSON dagegen kennt beliebig tiefe Verschachtelung. Ein Objekt kann Objekte enthalten, die wiederum Arrays enthalten, in denen erneut Objekte stehen. Sobald du diese Struktur in eine Tabelle pressen willst, musst du die Tiefe auflösen. Genau das nennt man Flachklopfen oder Flattening: Aus einem Baum wird eine Reihe von Spalten.

Es gibt dafür kein einziges richtiges Verfahren. Welche Strategie passt, hängt davon ab, was in der Verschachtelung steckt und was du danach mit der CSV machst. Dieser Artikel geht die vier zentralen Strategien durch und zeigt für jede ein konkretes JSON-Eingangsbeispiel und das resultierende CSV.

Warum CSV flach ist und JSON nicht

Eine CSV-Datei nach RFC 4180 besteht aus Datensätzen, die durch Zeilenumbrüche getrennt sind, und Feldern, die durch Kommas getrennt sind. Der erste Datensatz ist üblicherweise die Kopfzeile mit den Spaltennamen. Es gibt keinen Mechanismus, um in einer Zelle eine weitere Tabelle oder ein Objekt zu transportieren. Eine Zelle ist ein String, eine Zahl, ein Wahrheitswert oder leer.

JSON nach RFC 8259 hat diese Beschränkung nicht. Ein Wert kann selbst wieder ein Objekt oder Array sein, und das beliebig tief. Daraus ergibt sich das Kernproblem: Du musst entscheiden, wie ein Baum mit mehreren Ebenen auf eine einzige Ebene aus Spalten abgebildet wird. Drei Arten von Verschachtelung treten dabei auf, und jede braucht eine eigene Antwort:

  1. Verschachtelte Objekte, etwa user.adresse.stadt.
  2. Arrays von Skalaren, etwa tags.
  3. Arrays von Objekten, etwa eine Liste von Bestellpositionen.

Dazu kommt der Sonderfall heterogener Datensätze, bei denen nicht jedes Objekt dieselben Schlüssel besitzt.

Strategie 1: Dot-Notation für verschachtelte Objekte

Verschachtelte Objekte sind der einfachste Fall. Jeder Pfad von der Wurzel zu einem Skalarwert wird zu einem Spaltennamen, wobei die Ebenen durch einen Punkt verbunden werden. Aus dem Pfad user → adresse → stadt wird die Spalte user.adresse.stadt. Das ist eindeutig, verlustfrei und lässt sich problemlos wieder zurückbauen.

Betrachte folgendes JSON:

{
  "id": 1,
  "name": "Anna Berg",
  "adresse": {
    "stadt": "Hamburg",
    "plz": "20095",
    "geo": {
      "lat": 53.55,
      "lng": 9.99
    }
  }
}

Beim Flachklopfen wandert jede Blattebene in eine eigene Spalte. Die Zwischenobjekte adresse und geo erscheinen nicht selbst als Spalte, sondern nur als Präfix im Pfad:

id,name,adresse.stadt,adresse.plz,adresse.geo.lat,adresse.geo.lng
1,"Anna Berg",Hamburg,20095,53.55,9.99

Der Algorithmus dahinter ist eine rekursive Tiefensuche. Du läufst über alle Schlüssel eines Objekts. Ist der Wert ein Skalar, schreibst du den aktuellen Pfad als Spaltennamen. Ist der Wert ein Objekt, hängst du den Schlüssel an den Pfad an und steigst eine Ebene tiefer. So entsteht für jedes Blatt genau eine Spalte.

Ein praktischer Hinweis: Wenn deine Schlüssel selbst Punkte enthalten, etwa "version.major", wird der Punkt als Trenner mehrdeutig. In dem Fall lohnt sich ein anderes Trennzeichen wie __ oder /. Dokumentiere die Wahl, damit der Rückweg von CSV zu JSON eindeutig bleibt.

Strategie 2: Arrays als Index-Spalten

Arrays von Skalaren lassen sich über ihren Index ebenfalls in Dot-Notation auflösen. Aus dem Array tags werden die Spalten tags.0, tags.1, tags.2 und so weiter. Der Index tritt an die Stelle des Schlüssels.

{
  "id": 7,
  "name": "Projekt Nord",
  "tags": ["intern", "prio-hoch", "q3"]
}

Daraus wird:

id,name,tags.0,tags.1,tags.2
7,"Projekt Nord",intern,prio-hoch,q3

Diese Variante ist verlustfrei und behält die Reihenfolge der Elemente. Ihr Schwachpunkt liegt bei Arrays unterschiedlicher Länge. Die Spaltenzahl richtet sich nach dem längsten Array im gesamten Datensatz. Ein Objekt mit nur einem Tag erzeugt dann leere Zellen in den höheren Index-Spalten:

id,name,tags.0,tags.1,tags.2
7,"Projekt Nord",intern,prio-hoch,q3
8,"Projekt Süd",extern,,

Bei stark schwankenden Längen wird die Tabelle dünn besetzt und schwer lesbar. Für solche Fälle ist die nächste Strategie meist die bessere Wahl.

Strategie 3: Arrays als zusammengefasster String

Statt jedes Array-Element in eine eigene Spalte zu schreiben, kannst du das gesamte Array zu einem einzigen String zusammenfügen. Die Elemente werden mit einem Trennzeichen verbunden, klassisch mit Semikolon, da das Komma in CSV bereits Feldtrenner ist.

{
  "id": 7,
  "name": "Projekt Nord",
  "tags": ["intern", "prio-hoch", "q3"]
}

ergibt mit Join über Semikolon:

id,name,tags
7,"Projekt Nord","intern;prio-hoch;q3"

Diese Variante hält die Spaltenzahl stabil, egal wie lang die einzelnen Arrays sind. Sie eignet sich für Anzeige und Export, etwa wenn ein Mensch die CSV in einer Tabellenkalkulation öffnet und die Tags nur lesen, nicht einzeln auswerten will. Der Preis: Die einzelnen Elemente sind nicht mehr direkt adressierbar. Eine Pivot-Auswertung pro Tag oder ein Filter auf ein einzelnes Element ist nicht mehr ohne weiteres Parsen möglich. Achte zudem darauf, dass das gewählte Trennzeichen nicht selbst in den Werten vorkommt, sonst wird die spätere Rückumwandlung mehrdeutig.

Strategie 4: Arrays explodieren in mehrere Zeilen

Sobald ein Array nicht aus Skalaren, sondern aus Objekten besteht, stoßen Index-Spalten und Join-Strings an ihre Grenzen. Hier hilft das Explodieren, auch Unnesting oder Normalisieren genannt. Pro Array-Element entsteht eine eigene Zeile, während die Felder der äußeren Ebene in jeder dieser Zeilen wiederholt werden.

{
  "bestellung": 1001,
  "kunde": "Anna Berg",
  "positionen": [
    { "artikel": "Tastatur", "menge": 1, "preis": 49.9 },
    { "artikel": "Maus", "menge": 2, "preis": 19.5 }
  ]
}

Beim Explodieren wird die Bestellung auf zwei Zeilen verteilt, eine pro Position. Die Felder bestellung und kunde erscheinen in beiden Zeilen, die Positionsfelder werden über Dot-Notation flachgeklopft:

bestellung,kunde,positionen.artikel,positionen.menge,positionen.preis
1001,"Anna Berg",Tastatur,1,49.9
1001,"Anna Berg",Maus,2,19.5

Diese Form entspricht der Denkweise relationaler Datenbanken: Eine Bestellung mit mehreren Positionen wird zur Detailtabelle, in der jede Zeile eine Position ist. Genau diese Struktur brauchst du, wenn du danach gruppieren, summieren oder per SQL importieren willst. Der Nachteil ist die Redundanz der äußeren Felder und die Tatsache, dass eine Zeile nicht mehr genau einem JSON-Objekt entspricht. Hast du mehrere Arrays auf derselben Ebene, die du gleichzeitig explodieren willst, entsteht ein Kreuzprodukt, das schnell groß wird. In solchen Fällen ist es sauberer, pro Array eine getrennte CSV mit einem Fremdschlüssel auf die Bestell-ID zu erzeugen.

Heterogene Schemata: die Spalten-Union

In der Praxis sind JSON-Arrays selten perfekt einheitlich. Ein Objekt hat ein Feld telefon, das nächste nicht, dafür ein Feld fax. Damit eine Tabelle entsteht, musst du die Vereinigungsmenge aller vorkommenden Schlüssel bilden. Jeder Schlüssel, der in mindestens einem Objekt auftaucht, wird zur Spalte. Objekte, denen dieser Schlüssel fehlt, bekommen an dieser Stelle eine leere Zelle.

[
  { "id": 1, "name": "Anna", "telefon": "040-111" },
  { "id": 2, "name": "Ben", "fax": "040-222" },
  { "id": 3, "name": "Cleo", "telefon": "040-333", "newsletter": true }
]

Die Spalten-Union umfasst id, name, telefon, fax und newsletter. Fehlende Felder werden zu leeren Zellen:

id,name,telefon,fax,newsletter
1,Anna,040-111,,
2,Ben,,040-222,
3,Cleo,040-333,,true

Wichtig ist hier die Reihenfolge: Du musst den gesamten Datensatz einmal durchlaufen, um alle Schlüssel zu sammeln, bevor du die Kopfzeile schreiben kannst. Ein einziger Durchlauf reicht für die Header-Ermittlung nicht, wenn neue Schlüssel erst in späteren Objekten auftauchen. Beachte außerdem die schon angesprochene Unterscheidung zwischen einem fehlenden Schlüssel und einem Schlüssel mit Wert null. In der CSV sehen beide gleich aus, nämlich als leere Zelle. Ist die Differenz fachlich relevant, kodiere null als expliziten Platzhalter.

Die Strategien im Vergleich

Die Wahl der Strategie folgt einer einfachen Logik: Was steckt in der Verschachtelung, und was willst du danach damit tun? Die folgende Tabelle fasst die Abwägung zusammen.

StrategieGeeignet fürVorteilNachteil
Dot-NotationVerschachtelte ObjekteVerlustfrei, eindeutig, rückbaubarBei tiefer Verschachtelung lange Spaltennamen
Index-SpaltenSkalar-Arrays fester LängeVerlustfrei, Reihenfolge bleibtViele leere Zellen bei variabler Länge
Join-StringSkalar-Arrays variabler LängeStabile Spaltenzahl, gut lesbarElemente nicht einzeln auswertbar
ExplodeArrays von ObjektenPro Element eine Zeile, DB-tauglichRedundanz, Kreuzprodukt bei mehreren Arrays
Spalten-UnionHeterogene ObjekteRobust gegen unterschiedliche SchemataDünn besetzte Tabelle, zwei Durchläufe nötig

In realen Daten kombinierst du diese Verfahren. Ein typischer Datensatz hat verschachtelte Objekte, die du per Dot-Notation auflöst, ein Skalar-Array, das du joinst, und ein Objekt-Array, das du entweder explodierst oder in eine eigene CSV auslagerst. Es gibt keinen Schalter, der dir alles abnimmt, weil die richtige Entscheidung von der fachlichen Bedeutung der Felder abhängt.

Verarbeitung in der Praxis

Egal welche Strategie, der erste Schritt ist immer dasselbe: das JSON zuverlässig parsen. In JavaScript erledigt das JSON.parse(), das aus einem String die entsprechende Objekt- oder Array-Struktur erzeugt. Erst auf dieser geparsten Struktur arbeitest du mit deinem Flattening-Algorithmus. Lies die Daten nie mit regulären Ausdrücken aus dem Rohstring, denn an Escapes, Unicode und verschachtelten Klammern scheitert jeder selbstgebaute Parser.

Beim Schreiben der CSV solltest du RFC 4180 ernst nehmen. Felder, die Kommas, Anführungszeichen oder Zeilenumbrüche enthalten, müssen in doppelte Anführungszeichen gesetzt werden, und enthaltene Anführungszeichen werden verdoppelt. Aus dem Wert Berg, Anna wird in der CSV "Berg, Anna". Wer dieses Quoting auslässt, produziert Dateien, die in der ersten Tabellenkalkulation mit Komma im Datenfeld zerbrechen. Genau hier scheitern viele schnell zusammengeschriebene Exporter, weil sie nur Felder zusammenkleben, ohne die Sonderzeichen zu behandeln.

Ein letzter Hinweis zur Reihenfolge der Spalten. Sammle zuerst alle Pfade über den gesamten Datensatz ein, sortiere sie nach einer festen Regel und schreibe dann erst Header und Zeilen. So bleibt die Spaltenreihenfolge stabil und reproduzierbar, auch wenn die Eingabedaten in unterschiedlicher Reihenfolge ankommen. Das ist die Grundlage dafür, dass zwei Exporte derselben Daten auch byteweise vergleichbar bleiben.

Worauf es ankommt

Flachklopfen ist kein mechanischer Vorgang, sondern eine Reihe von Entscheidungen. Verschachtelte Objekte gehen verlustfrei per Dot-Notation in Spalten über. Skalar-Arrays werden entweder als Index-Spalten verlustfrei abgebildet oder als Join-String kompakt zusammengefasst, je nachdem ob du die Elemente später einzeln brauchst. Objekt-Arrays gehören explodiert, sobald du pro Element auswerten willst. Heterogene Datensätze verlangen eine Spalten-Union mit einem vollständigen Durchlauf vorab. Wer diese vier Muster sicher beherrscht und sauber nach RFC 4180 quotet, bekommt aus jedem noch so tief verschachtelten JSON eine flache, weiterverarbeitbare Tabelle.

FAQ

Häufige Fragen

Wie behandle ich null-Werte beim Flachklopfen?

Ein null im JSON wird in CSV meist zur leeren Zelle. Wichtig ist die Unterscheidung zwischen 'Schlüssel fehlt' und 'Schlüssel ist null'. Bei heterogenen Objekten entsteht durch fehlende Schlüssel ohnehin eine leere Zelle, sodass beide Fälle in der CSV gleich aussehen. Wenn die Differenz fachlich relevant ist, kodiere null als expliziten Platzhalter wie NULL.

Was passiert mit Arrays unterschiedlicher Länge bei der Index-Notation?

Bei tags.0, tags.1, tags.2 richtet sich die Spaltenzahl nach dem längsten Array im Datensatz. Objekte mit kürzeren Arrays bekommen in den überschüssigen Spalten leere Zellen. Das führt bei stark schwankenden Längen zu vielen leeren Feldern, weshalb sich dann eher der Join-String oder ein Explode anbietet.

Wann sollte ich Explode statt Join verwenden?

Explode lohnt sich, wenn jedes Array-Element selbst ein Objekt mit mehreren Feldern ist oder wenn du die Daten danach in einer Pivot-Tabelle oder Datenbank pro Element auswerten willst. Ein Join-String reicht, solange die Array-Elemente einfache Skalare sind und du sie nur anzeigen, nicht aggregieren möchtest.

Wie verhindere ich, dass Dot-Notation mit echten Punkten in Schlüsseln kollidiert?

Wenn deine JSON-Schlüssel selbst Punkte enthalten, wird der Punkt als Trennzeichen mehrdeutig. Wähle in diesem Fall ein anderes Trennzeichen wie Doppel-Unterstrich oder Slash für die Pfadbildung und dokumentiere es im Header oder einer Begleitnotiz.

Anzeige

Quellen

Worauf dieser Ratgeber sich stützt

Veröffentlicht · zuletzt geprüft
Verantwortlich: Jan-Tristan Rudat
Anzeige
Anzeige
Anzeige
Anzeige