Convert data files between formats with optional Pydantic validation.
Supported formats: JSON, CSV, NDJSON (JSONL), YAML, XML
pip install -e .Or with uv:
uv run dataconv ...- PyYAML — for YAML support:
pip install pyyaml - Dev tools — for testing:
pip install -e ".[dev]"
dataconv input.json output.csv
dataconv input.csv output.jsonFormat is auto-detected from file extensions.
Use format names instead of file paths to read from stdin / write to stdout:
# Read JSON from stdin, write CSV to stdout
cat input.json | dataconv json csv
# Read file, write to stdout
dataconv input.json csv | less
# Read from stdin, write to file
cat input.json | dataconv json output.csv
# Chain conversions
cat data.json | dataconv json ndjson | dataconv ndjson yamldataconv input.json output.csv --schema examples/schema.pyRows that fail validation are excluded from the output file.
dataconv examples/input.json output.csv --schema examples/schema.py --errors errors.jsonInvalid rows are written to errors.json alongside validation error messages.
dataconv input.json output.csv --verboseShows warnings for schema load failures, validation errors, and key collisions.
A schema is a Python file defining a Pydantic model:
from pydantic import BaseModel
class RowSchema(BaseModel):
id: int
name: str
email: strThe first BaseModel subclass found in the file will be used for validation.
For nested JSON structures you can use nested Pydantic models:
from pydantic import BaseModel
class RowSchema(BaseModel):
class Address(BaseModel):
city: str | None = None
zip: str | None = ""
id: int
name: str
email: str
age: int
active: bool
address: Address | None = NoneSee examples/schema.py for a full example matching the sample data.
CSV files use flat dotted keys like address.city. Match them with a flat schema:
from pydantic import BaseModel
class RowSchema(BaseModel):
id: int
name: str
email: str
age: int
active: bool
address_city: str | None = ""
address_zip: str | None = ""dataconv [INPUT] [OUTPUT] [OPTIONS]
Positional arguments:
input Input file path (.json, .csv, .ndjson, .yaml, .xml) or format name (json, csv, ndjson, yaml, xml) for stdin
output Output file path or format name for stdout
Options:
--schema PATH Path to Pydantic schema file for validation
-e, --errors PATH Path to write invalid rows to
--flatten Flatten nested objects in output (default: keep nested)
--csv-keys FMT CSV column naming: dotted=address.city (default), flat=city
--xml-root NAME XML wrapper element for output (default: rows)
--xml-item NAME XML row element for output (default: row)
-v, --verbose Show warnings and debug info
-h, --help Show this help message
| Command | Input | Output |
|---|---|---|
dataconv input.json output.csv |
file → file | file |
dataconv input.json csv |
file | stdout (csv) |
dataconv json output.csv |
stdin (json) | file |
dataconv json csv |
stdin (json) | stdout (csv) |
dataconv input.json |
file | stdout (format guessed from input) |
Full JSON arrays or objects. Nested data is preserved on write.
Flat tabular data. Nested JSON is automatically flattened using dot-separated keys (e.g., address.city). Column order follows first-seen insertion order.
One JSON object per line. Comments (lines starting with #) are ignored. Malformed lines are skipped with a warning.
Full YAML documents or streams. Requires pyyaml installed.
Nested records wrapped as <rows><row>…</row></rows> by default — one <row>
element per record. Fields become child elements; repeated tags collapse to a
list, and nested dictionaries recurse into nested elements. Namespaces are
stripped and attribute names are merged under an @ prefix. Reads are strings
(like CSV), so type coercion (int/bool) only happens when a schema is supplied.
Requires no extra dependency (uses the stdlib xml.etree module).
On read the wrapper is auto-detected: a root whose children are all the same
element (e.g. <users><user>…</user></users>) is unwrapped into rows, while a
root with heterogeneous children (e.g. <person><name/><address/></person>) is
treated as a single nested record.
On write the wrapper names are fixed at rows/row unless overridden with
--xml-root / --xml-item (e.g. --xml-root users --xml-item user produces
<users><user>…</user></users>).
Convert JSON to CSV:
dataconv examples/input.json output.csvConvert JSON to CSV with validation:
dataconv examples/input.json output.csv --schema examples/schema.pyConvert CSV to JSON with validation and error output:
dataconv examples/input.csv output.json --schema examples/schema.py --errors errors.jsonStream JSON through to CSV:
cat examples/input.json | dataconv json csvConvert YAML to NDJSON:
dataconv data.yaml output.ndjsonConvert XML to JSON:
dataconv data.xml output.jsonWrite XML with a custom entity name:
dataconv data.json data.xml --xml-root users --xml-item user