Excel-Datei auslesen
Der Service Task „Excel-Datei auslesen (xls/xlsx)“ liest ein Blatt einer Excel-Datei und gibt die Zeilen als Liste
von Datensätzen (records) zurück. Unterstützt werden .xls (Excel 97–2003), .xlsx und .xlsm. Das Format wird am
Inhalt der Datei erkannt, nicht an der Dateiendung.
Der Task eignet sich für zwei Fälle:
- Saubere Listen mit einer Kopfzeile, z. B. Artikel- oder Preislisten. Hier genügt
header_row. - Excel im Drucklayout, z. B. Rechnungen oder Leistungsverzeichnisse mit Seitenköpfen, Zwischensummen, Titeln und Texten über mehrere Zeilen. Hier beschreiben Regeln, was eine Zeile ist.
Werte kommen so, wie Excel sie anzeigt: Formeln liefern ihr in der Datei gespeichertes Ergebnis, Zahlen werden auf
Wunsch nach ihrem Zahlenformat gerundet (aus 113,9396 mit Format 0,00 wird 113,94).
Input-Parameter
{
"document": {
"referenceId": "string",
"filename": "rechnung.xls"
},
"options": {
"sheet": 0,
"header_row": 1
}
}
document: Referenz auf die hochgeladene Excel-Datei (fileReference). Pflicht.options: Optional. Beschreibt, welches Blatt und welche Zeilen gelesen werden und wie. Darf auch als JSON-Text übergeben werden. Ohneoptionswird das erste Blatt gelesen; jede nicht leere Zeile wird ein Datensatz mit den FeldernA,B,C, …
Einfache Liste
{
"document": { "referenceId": "..." },
"options": { "sheet": "Artikel", "header_row": 1 }
}
Ergebnis: je Zeile ab Zeile 2 ein Datensatz, Feldnamen aus der Kopfzeile, z. B.
{ "Artikel": "A-1", "Bezeichnung": "Schraube", "Preis": 0.15, "Datum": "2026-02-15" }.
Excel im Drucklayout
Beispiel: Rechnungspositionen ab der Kopfzeile „Bezeichnung“ bis „Nettosumme“, Seitenköpfe und Summen überspringen, Titel („3.0 Betonarbeiten“) und Zwischentexte („Regiebericht vom …“) an die folgenden Positionen hängen.
{
"sheet": 0,
"start_after": { "column": "B", "matches": "^(Artikel)?bezeichnung$", "flags": "i" },
"stop_at": { "any_column": "^Nettosumme$" },
"scan_columns": "A:F",
"columns": {
"oz": { "col": "A", "as": "text" },
"text": { "col": "B", "as": "text" },
"quantity": { "col": "C", "as": "number", "round": "display" },
"unit": { "col": "D", "as": "text" },
"price": { "col": "E", "as": "number" }
},
"rules": [
{ "name": "Seitenkopf", "type": "skip", "if": [{ "any_column": "Seite\\s*\\d+" }] },
{ "name": "Summe", "type": "skip", "if": [{ "any_column": "^(Summe|Übertrag)\\b", "flags": "i" }] },
{ "name": "Position", "type": "position",
"if": [{ "column": "quantity", "is": "numeric" }, { "column": "unit", "is": "filled" }] },
{ "name": "Titel", "type": "title", "if": [{ "column": "oz", "matches": "^\\d+\\.0$" }] },
{ "name": "Text", "type": "text", "if": [{ "column": "text", "is": "filled" }] }
],
"carry": [
{ "name": "title", "from_type": "title", "value": "{oz} {text}" },
{ "name": "context", "from_type": "text", "accumulate": true, "reset_on": ["empty_row", "type:title"] }
]
}
Ablauf
- Lesen: Für jede Zelle werden Typ, Wert, Zahlenformat und angezeigter Text ermittelt.
- Bereich: Wo die Tabelle beginnt und endet (
start_row,start_after,end_row,stop_at). Wiederkehrende Blöcke wie Seitenköpfe lassen sich mitskip_blocksüberspringen. - Spalten:
columnsvergibt Namen und legt Typ, Rundung und Ersetzungen fest. - Regeln: Für jede Zeile werden die
rulesder Reihe nach geprüft; die erste passende bestimmt den Typ der Zeile. Typen sind frei benennbar. Zeilen vom Typskipwerden verworfen; passt keine Regel, gilt der Typunclear. - Übertragen:
carryhängt Werte an die folgenden Zeilen an (z. B. den aktuellen Titel). - Zusammenführen:
mergehängt Textzeilen an die nächste oder vorige Zeile eines Typs an (z. B. Langtexte über mehrere Zeilen). - Ergebnis:
recordsmit allen Datensätzen (ohneskip).
Leere Zeilen werden nie zu Datensätzen. Sie können aber Übertragungen zurücksetzen und Zusammenführungen beenden
(empty_row). Leerzeilen direkt nach übersprungenen Zeilen zählen dabei nicht, damit Seitenumbrüche mitten in einer
Position nichts unterbrechen.
Optionen
| Option | Bedeutung |
|---|---|
sheet |
Blatt als Index (0 = erstes), Name ("Rechnung") oder Muster ({"matches": "^Angebot"}). Standard 0. |
header_row |
Zeilennummer der Kopfzeile. Die Spaltennamen werden zu Feldnamen, gelesen wird ab der Zeile darunter. |
start_row / end_row |
Feste erste / letzte Zeile (1-basiert wie in Excel). |
start_after |
Bedingung: Gelesen wird ab der Zeile nach dem ersten Treffer. Kein Treffer: keine Datensätze, Warnung. |
stop_at |
Bedingung: Gelesen wird bis vor den ersten Treffer. |
scan_columns |
Spaltenbereich für any_column und die Erkennung leerer Zeilen, z. B. "A:F". Standard: alle. |
columns |
Feldname → Spalte. Kurzform "B" oder Objekt { "col": "B", "as": …, "round": …, "replace": … }. |
rules |
Liste von Regeln { "name", "type", "if": [Bedingungen], "any": [Bedingungen], "extract": [...] }. |
default_type |
Typ, wenn keine Regel passt. Standard "unclear"; diese Zeilen stehen zusätzlich in unclear. |
skip_types |
Typen, die verworfen werden. Standard ["skip"]. |
skip_blocks |
Blöcke überspringen: { "start": Bedingung, "rows": 4 } oder { "start": Bedingung, "end": Bedingung }. |
carry |
Werte an folgende Zeilen hängen, siehe unten. |
lookups |
Nachschlagetabellen aus einem Zeilenbereich, z. B. Titel aus einem Deckblatt. |
merge |
Textzeilen zusammenführen, siehe unten. |
output_types |
Nur diese Typen in records, z. B. ["position"]. |
locale |
"de" (Standard) oder "en": Dezimal- und Tausendertrennzeichen für angezeigte Texte. |
include_rows |
true: zusätzlich rows mit jeder gelesenen Zeile und ihrer Einstufung (zur Kontrolle). |
detect_missing_formula_values |
Standard true: Formeln ohne gespeichertes Ergebnis erkennen (nur .xlsx). |
strict |
true: Task bricht ab, wenn unklare Zeilen oder Formeln ohne gespeichertes Ergebnis vorkommen. |
Spalten
| Eigenschaft | Bedeutung |
|---|---|
col |
Spaltenbuchstabe, z. B. "C". |
as |
text: angezeigter Text (z. B. Positionsnummer „3.091“, auch wenn Excel sie als Zahl gespeichert hat). number: Zahl, auch aus Text wie „1.234,56“. date: Datum als YYYY-MM-DD, auch aus Text wie „15.02.2026“. auto (Standard): Wert, wie er in der Zelle steht. |
round |
"display": runden wie angezeigt (Standard bei number). Zahl: feste Nachkommastellen. Der ungerundete Wert steht dann in _raw. |
replace |
Liste [[Regex, Ersatz], …] für Texte, z. B. [["^-$", ""]], damit ein Aufzählungsstrich nicht als Positionsnummer gilt. |
trim |
Standard true: Leerraum am Rand entfernen und mehrfachen Leerraum zusammenfassen. |
Bedingungen
Bedingungen stehen in if (alle müssen passen), any (eine muss passen), start_after, stop_at, skip_blocks,
only_if und only_if_target. Sie lassen sich mit all, any und not verschachteln.
| Form | prüft |
|---|---|
{ "column": "quantity", "is": "numeric" } |
Typ: empty, filled, number, numeric (Zahl oder Zahl als Text), text, date, error. Auch als Liste. |
{ "column": "oz", "matches": "^\\d+\\.0$" } |
Regulärer Ausdruck auf den angezeigten Text. flags: "i" ignoriert Groß-/Kleinschreibung. Ebenso not_matches, equals. |
{ "column": "C", "gt": 0 } |
Zahlvergleich gt / lt. |
{ "any_column": "Seite\\s*\\d+" } |
Irgendeine Spalte passt (optional eingeschränkt mit columns: "A:F"). |
{ "row": "114-200" } |
Zeilennummer oder Bereich. |
{ "not": { … } } |
Verneinung. |
column nimmt einen Feldnamen aus columns oder einen Spaltenbuchstaben.
extract zerlegt einen Wert per Regex mit benannten Gruppen in Felder, z. B. wird aus „A4.01Alternativposition zu
4.01“ mit { "field": "oz", "pattern": "^(?P<oz>A[\\d.]*\\d)(?P<text>.*)$" } die Positionsnummer A4.01 und der Text
„Alternativposition zu 4.01“.
Übertragen (carry)
{ "name": "context", "from_type": "text", "value": "{text}", "accumulate": true, "reset_on": ["empty_row", "type:title"] }
from_type: Zeilen dieses Typs setzen den Wert.value: Vorlage aus Feldern der Zeile, z. B."{oz} {text}".accumulate:truesammelt alle Werte in einer Liste, sonst gilt der letzte.reset_on:empty_rowund/odertype:<typ>setzen den Wert zurück.fallback_lookup/fallback_key: Fehlt der Wert, wird er in einer Nachschlagetabelle auslookupsgesucht.
Jeder Datensatz bekommt den aktuellen Wert unter name.
Zusammenführen (merge)
{ "from_type": "text", "into_type": "position", "direction": "next", "take_first": ["oz"], "break_on": ["empty_row", "type:title"], "dehyphenate": true }
direction: "next": Textzeilen werden vorgemerkt und der nächsten Zeile vom Typinto_typevorangestellt (Langtext über der Mengenzeile).direction: "previous": Textzeilen direkt nach einer Zeile vom Typinto_typewerden angehängt.fields: welche Felder verbunden werden. Standard["text"].separator: Standard Leerzeichen.take_first: Felder, die aus der ersten vorgemerkten Zeile übernommen werden, wenn die Zielzeile sie nicht hat.only_if(Bedingung an die Textzeile),only_if_target(Bedingung an die Zielzeile).break_on:empty_rowund/odertype:<typ>beenden das Vormerken.dehyphenate:trueverbindet getrennte Wörter („Hoch-“ + „lochziegeln“ → „Hochlochziegeln“), lässt „Beton- und“ aber stehen.
Vorgemerkte Zeilen, die nicht zusammengeführt werden, bleiben eigene Datensätze.
Output
{
"status": 200,
"format": "xls",
"sheet": "Rechnung",
"sheets": ["Rechnung", "Aufmaß"],
"start_row": 117,
"end_row": 325,
"stopped_at": null,
"records": [
{
"_row": 125, "_rows": [125], "_type": "position", "_rule": "Position",
"oz": "3.01", "text": "Frostriegel", "quantity": 3.97, "unit": "m³", "price": 85.5,
"title": "3.0 Beton- und Stahlbetonarbeiten", "context": [],
"_raw": { "quantity": 3.9699999999999998 }
}
],
"total": 1,
"counts": { "position": 1, "skip": 10 },
"unclear": [],
"warnings": [],
"metadata": { "filename": "rechnung.xls", "inputBytes": 173056, "processingTimeMs": 120, "engine": "xlrd" }
}
records: die Datensätze._rowist die Zeile in Excel,_rowsalle Zeilen, aus denen der Datensatz besteht,_typeund_rulezeigen, welche Regel gegriffen hat._rawenthält ungerundete Werte,_notesHinweise zur Zeile.counts: Anzahl Zeilen je Typ; zusammengeführte Zeilen erscheinen als „… (angehängt)“.unclear: Zeilen, auf die keine Regel passt, mit ihren Zellinhalten.warnings: z. B. „Startmarke nicht gefunden“ oder Formeln ohne gespeichertes Ergebnis.
In Rechnungspositionen übernehmen (JSONata)
{
"app_invoice_items_custom": [records[_type = "position"].{
"app_article_no": oz,
"app_description": text,
"app_amount": quantity,
"app_unit": unit,
"app_item_price_net": price,
"app_vat_percentage": 19
}]
}
Hinweise
- Formelergebnisse: Der Task rechnet keine Formeln nach, sondern liest das Ergebnis, das Excel beim Speichern in
der Datei ablegt. In
.xls-Dateien ist es immer vorhanden. Eine.xlsx, die nicht mit Excel gespeichert wurde (z. B. aus einem anderen Programm exportiert), kann Formeln ohne Ergebnis enthalten: Der Wert ist dannnull, dazu gibt es eine Warnung. Abhilfe: Datei einmal in Excel öffnen und speichern. - Rundung: Mit
round: "display"stimmt die Menge mit dem überein, was im Ausdruck steht. Rechnet die Excel-Datei intern mit dem ungerundeten Wert, kann eine daraus erstellte Rechnung um Cent-Beträge von der Excel-Summe abweichen. - Nicht unterstützt:
.xlsb,.odsund passwortgeschützte Dateien (Fehlermeldung „passwortgeschützt“). CSV-Dateien liest CSV-Datei strukturiert auslesen. - Bei einem Fehler in den Optionen (unbekanntes Blatt, unbekannte Spalte, ungültige Bedingung) bricht der Task mit einer Meldung ab, ohne erneuten Versuch.
- Zum Einrichten neuer Vorlagen
include_rows: truesetzen:rowszeigt jede Zeile mit ihrer Einstufung.