CSVToCSVW¶
FastAPI microservice that reads a CSV file and produces a W3C CSVW metadata document that annotates every column with data types, measurement units, and semantic term links.
Repo: https://github.com/Mat-O-Lab/CSVToCSVW
Public API: https://csvtocsvw.matolab.org · API docs
Image: ghcr.io/mat-o-lab/csvtocsvw:latest
Version: v1.3.5
What is CSVW and why does it matter?¶
A plain CSV file is just text. The column header "Kraft [kN]" means something to a person who knows German and materials science — but to any downstream system it is an opaque string.
CSVW (CSV on the Web) solves this: it is a JSON sidecar file that annotates your CSV columns with data types, units, and ontology term links — turning a plain spreadsheet into self-describing data. When a system reads the CSVW alongside your CSV, it knows that the Kraft [kN] column contains force measurements in kilonewtons, expressed using the internationally agreed QUDT unit vocabulary. Any tool that understands QUDT can then compare, combine, or reason over that column correctly, without a human in the loop.
The CSVW file format is a W3C standard. CSVToCSVW automates its production: you give it a CSV URL, it gives you back the annotated CSVW — no manual JSON authoring required.
What is JSON-LD?¶
The CSVW file CSVToCSVW produces is written in JSON-LD — a format that embeds semantic meaning directly in a JSON file. Your CSV column "temperature" gets linked to a shared ontology term so any system knows exactly what it means, regardless of what the column was named locally.
Practically, JSON-LD looks like ordinary JSON with two extra conventions:
@context— a dictionary that maps short names (likequdt:unit) to full web URLs@id— a URL that unambiguously identifies the thing being described
You can read and edit a CSVW file with any JSON tool. The semantic meaning comes from the URLs, not from a special file format.
What CSVToCSVW produces¶
Given a CSV file, CSVToCSVW returns a CSVW JSON-LD document that:
- Annotates every data column with its inferred measurement unit (e.g.
Kraft [kN]→unit:KiloN) - Captures metadata rows found above the data table — common in lab instrument exports — as typed key-value annotations with their own unit links
- Records provenance (which service produced the file, at what time), so downstream systems can trace where the annotations came from
The CSVW output is Stage 1 of the default pipeline: it feeds directly into MapToMethod (mapping authoring) and into the CKAN automation layer.
For a full explanation of the ontology terms that appear in the CSVW output (QUDT, PROV-O, Open Annotation), see Semantic Foundation.
Supported CSV formats¶
CSVToCSVW handles the multi-block format common in lab instrument exports:
aktuelle Probe;17 ← metadata block (key-value rows)
Einspannlänge;832.756
Probenbreite b0;11.4
← blank separator line (auto-detected)
Zeit [s];Maschine [mm];Kraft [kN] ← data table header row
0.000;0.000;0.164
0.200;0.001;0.193
…
Both blocks are captured:
- Metadata rows (above the blank separator) become typed annotation entries — each key-value pair is preserved with its physical unit where one can be inferred from the value
- Data table columns (below the separator) become column descriptors with QUDT unit annotations derived from the header bracket notation
Column header notation — how units are detected¶
CSVToCSVW reads units from column names using two patterns:
1. Bracket notation: Column Name [unit]
The content inside square brackets is looked up against the QUDT unit vocabulary:
| Column header | Detected unit IRI |
|---|---|
Zeit [s] |
http://qudt.org/vocab/unit/SEC |
Maschine [mm] |
http://qudt.org/vocab/unit/MilliM |
Kraft [kN] |
http://qudt.org/vocab/unit/KiloN |
MX840A_0_CH 5 [°C] |
http://qudt.org/vocab/unit/DEG_C |
Druck [MPa] |
http://qudt.org/vocab/unit/MegaPA |
Weg [µm] |
http://qudt.org/vocab/unit/MicroM |
2. Suffix notation: common abbreviations in the column name itself
ForceMPa → unit:MegaPA, DisplacementMm → unit:MilliM
Columns without a recognizable unit pattern are typed but not unit-annotated. If your column headers use non-standard or institution-specific abbreviations, rename them to the bracket notation before calling the service for best results.
Output — CSVW JSON-LD in detail¶
Below is the real CSVW JSON-LD that CSVToCSVW produces for example2.csv. Two sections show the two kinds of content the format carries.
Data columns with unit annotations¶
Each column in the data table becomes a Column entry. Columns with recognized bracket notation get a qudt:unit link — a URL pointing to the internationally agreed definition of that unit:
{
"name": "MaschineMm",
"titles": ["Maschine [mm]", "MaschineMm"],
"@type": "Column",
"qudt:unit": {"@id": "http://qudt.org/vocab/unit/MilliM"}
},
{
"name": "KraftKn",
"titles": ["Kraft [kN]", "KraftKn"],
"@type": "Column",
"qudt:unit": {"@id": "http://qudt.org/vocab/unit/KiloN"}
},
{
"name": "Mx840A0Ch5C",
"titles": ["MX840A_0_CH 5 [°C]", "Mx840A0Ch5C"],
"@type": "Column",
"qudt:unit": {"@id": "http://qudt.org/vocab/unit/DEG_C"}
},
{
"name": "ZeitS",
"titles": ["Zeit [s]", "ZeitS"],
"@type": "Column",
"qudt:unit": {"@id": "http://qudt.org/vocab/unit/SEC"}
}
The name field is a CamelCase-normalized version of the original header. This is the key MapToMethod uses to refer to that column in mapping rules.
Metadata block as annotation entries¶
The preamble rows above the blank separator become Open Annotation entries. Each has a qudt:unit link when the value is a physical quantity:
{
"@id": "…/example.csv/Einspannlaenge1",
"label": "Einspannlänge",
"@type": "oa:Annotation",
"rownum": {"@value": 1, "@type": "xsd:integer"},
"oa:hasBody": [{
"@type": "qudt:QuantityValue",
"qudt:value": {"@value": 832.756, "@type": "xsd:double"},
"qudt:unit": {
"@id": "http://qudt.org/vocab/unit/MilliM",
"@type": "qudt:DerivedUnit"
}
}]
},
{
"@id": "…/example.csv/ProbenbreiteB03",
"label": "Probenbreite b0",
"@type": "oa:Annotation",
"oa:hasBody": [{
"@type": "qudt:QuantityValue",
"qudt:value": {"@value": 11.4, "@type": "xsd:double"},
"qudt:unit": {"@id": "http://qudt.org/vocab/unit/MilliM"}
}]
}
What oa:Annotation and qudt:QuantityValue mean, and how they fit into the broader ontology stack, is covered in Semantic Foundation.
API¶
Endpoints at a glance¶
| Method | Path | Input | Use when |
|---|---|---|---|
POST |
/api/annotate |
JSON body with data_url |
CSV is publicly accessible by URL |
POST |
/api/annotate_upload |
Multipart form with file |
CSV is private or on your local machine |
POST |
/api/rdf |
JSON body with metadata_url |
Re-serialize an existing CSVW to another RDF format |
GET |
/api/docs |
— | Interactive API explorer (Swagger UI) |
return_type options for all endpoints: json-ld · n3 · nt · hext · trig · turtle · longturtle · xml
POST /api/annotate — Annotate CSV from a public URL¶
Request¶
- Method:
POST - Path:
/api/annotate - Query param:
return_type=json-ld(or any format listed above) Content-Type: application/json- Body:
Response¶
The CSVW document as JSON-LD (or the requested serialization format).
Example — annotate example2.csv¶
curl -X POST "https://csvtocsvw.matolab.org/api/annotate?return_type=json-ld" \
-H "Content-Type: application/json" \
-d '{
"data_url": "https://github.com/Mat-O-Lab/CSVToCSVW/raw/main/examples/example2.csv",
"encoding": "auto"
}' \
--output example2-metadata.json
This is a real working call — the example CSV is publicly accessible on GitHub.
POST /api/annotate_upload — Annotate a local CSV (no public URL needed)¶
Request¶
- Method:
POST - Path:
/api/annotate_upload - Query param:
return_type=json-ld Content-Type: multipart/form-data- Form field:
file— your CSV file
Example¶
curl -X POST "https://csvtocsvw.matolab.org/api/annotate_upload?return_type=json-ld" \
-F "file=@/path/to/your/data.csv" \
--output data-metadata.json
Use this for private or intranet data that is not publicly accessible from the service host.
POST /api/rdf — Convert an existing CSVW to another RDF serialization¶
Request¶
- Method:
POST - Path:
/api/rdf - Query param:
return_type=turtle(or any format listed above) Content-Type: application/json- Body:
Example — get Turtle output from an existing CSVW¶
curl -X POST "https://csvtocsvw.matolab.org/api/rdf?return_type=turtle" \
-H "Content-Type: application/json" \
-d '{
"metadata_url": "https://raw.githubusercontent.com/Mat-O-Lab/CSVToCSVW/main/examples/example-metadata.json"
}' \
--output example.ttl
Try it with the example files¶
The examples/ folder in the CSVToCSVW repo contains eight real lab CSV files. Use them to explore the service output without needing your own data:
| File | Format | What it tests |
|---|---|---|
example.csv |
Multi-block (German lab, semicolon) | Metadata block + tensile test time series |
example2.csv |
Tab-separated, multi-channel INSTRON | Multiple measurement channels with units |
example3.csv |
Comma-separated | Simple tabular |
example5.csv |
Multi-block variant | Different separator and encoding |
Pre-computed CSVW outputs are in the same folder as *-metadata.json files — inspect them to see the full structure before calling the service.
Reproduce example2 output and inspect it:
curl -X POST "https://csvtocsvw.matolab.org/api/annotate?return_type=json-ld" \
-H "Content-Type: application/json" \
-d '{
"data_url": "https://github.com/Mat-O-Lab/CSVToCSVW/raw/main/examples/example2.csv",
"encoding": "auto"
}' \
| python3 -m json.tool | head -60
Compare your output to example2-metadata.json in the repo.
Real-world pipeline examples¶
BAMresearch DF-TEM-PAW (TEM microscopy)¶
The BAMresearch/DF-TEM-PAW repository shows a complete pipeline run using CSVToCSVW on TEM microscopy detection-run data:
| File | URL |
|---|---|
| Input CSV | detection_runs.csv |
| CSVW output | detection_runs-metadata.json |
| YARRRML mapping | detection_runs-map.yaml |
| Joined knowledge graph | detection_runs-joined.ttl |
Reproduce the annotation step:
curl -X POST "https://csvtocsvw.matolab.org/api/annotate?return_type=json-ld" \
-H "Content-Type: application/json" \
-d '{
"data_url": "https://raw.githubusercontent.com/BAMresearch/DF-TEM-PAW/main/detection_runs.csv",
"encoding": "auto"
}' \
--output detection_runs-metadata.json
This pipeline is documented in: Hanke et al. (2023)
Published tensile test example — Kupfer Digital (Nasrabadi et al. 2023)¶
The paper Toward a digital materials mechanical testing lab (Nasrabadi, Hanke et al., Computers in Industry, 2023) documents a complete CSVToCSVW → MapToMethod → RDFConverter pipeline run on real copper alloy tensile test data from an accredited BAM laboratory.
The source CSV contains German-language column headers — a typical real-world challenge. CSVToCSVW converts the column "Zugfestigkeit" (tensile strength, 314 MPa) into a JSON-LD annotation with QUDT unit semantics:
{
"@id": "Zugfestigkeit29",
"@type": "oa:Annotation",
"oa:hasBody": [{
"@type": "qudt:QuantityValue",
"qudt:value": { "@value": 314, "@type": "xsd:integer" },
"qudt:unit": { "@id": "http://qudt.org/vocab/unit/MegaPA" }
}]
}
After mapping through MapToMethod and conversion via RDFConverter, the same measurement becomes:
data:Zugfestigkeit29 a oa:Annotation ;
rdfs:label "Zugfestigkeit" ;
bfo:RO_0010002 <…#MeasurementOfUltimateTensileStrength> ;
csvw:rownum 29 ;
oa:hasBody [ a qudt:QuantityValue ;
qudt:unit qunit:MegaPA ;
qudt:value 314 ] .
The full dataset and paper are available via the publication DOI above.
IOFMaterialsTutorial (length measurements)¶
The IOFMaterialsTutorial repository provides a simpler worked example with five length measurements. Useful for exploring the API mechanics; note that it uses an older pattern format.
CKAN automation¶
When deployed in the DataStack, ckanext-csvtocsvw calls /api/annotate automatically on every CSV upload — no user action needed after initial setup. See Default Use Case for the full automation chain.
Trigger file extensions (configurable in .env): csv · txt · asc · tsv
Limitations¶
- Remote
/api/annotaterequires the CSV to be publicly accessible from the service host; use/api/annotate_uploadfor private data - QUDT unit matching is heuristic — based on header naming conventions; verify the output
qudt:unitIRIs match your intent - Multi-block CSV parsing targets lab instrument export formats; other multi-table layouts may need preprocessing