Python 3-based commandline converter YAML ↔ JSON ↔ PLIST
Yaplon is a versatile command-line tool for converting between various common structured data formats: JSON, YAML, Property Lists (PLIST, both binary and XML), XML, and CSV (read-only for CSV input). It’s designed for developers, data analysts, system administrators, or anyone who needs to quickly and easily transform data from one format to another.
In a world of diverse applications and services, data often needs to be exchanged and processed in different formats. Yaplon simplifies this by providing:
Install the latest stable release from the Python Package Index (PyPI):
pip3 install --user --upgrade yaplon
To install the latest development version directly from GitHub:
pip3 install --user --upgrade git+https://github.com/twardoch/yaplon
If you want to contribute to Yaplon or modify it:
git clone https://github.com/twardoch/yaplon.git
cd yaplon
python3 -m venv venv
source venv/bin/activate # On macOS/Linux
# Or: venv\Scripts\activate # On Windows
pip install -e .[dev]
This installs the package so that changes you make to the source code are immediately reflected. Development dependencies include tools for testing, linting, and packaging.
Yaplon can be invoked using the main yaplon
command followed by a conversion subcommand (e.g., j2y
for JSON to YAML), or by using dedicated shortcut scripts (e.g., json22yaml
).
yaplon <command> -i <input_file> -o <output_file> [options]
<command>
: Specifies the conversion type (see “Conversion Commands” below).-i <input_file>
: Input file. If omitted or specified as -
, Yaplon reads from standard input (stdin).-o <output_file>
: Output file. If omitted or specified as -
, Yaplon writes to standard output (stdout). This allows for easy piping.The following commands define the input and output formats:
c2j
: CSV to JSONc2p
: CSV to PLISTc2x
: CSV to XMLc2y
: CSV to YAMLj2p
: JSON to PLISTj2x
: JSON to XMLj2y
: JSON to YAMLp2j
: PLIST to JSONp2x
: PLIST to XMLp2y
: PLIST to YAMLx2j
: XML to JSONx2p
: XML to PLISTx2y
: XML to YAMLy2j
: YAML to JSONy2p
: YAML to PLISTy2x
: YAML to XMLYaplon supports JSON input with C-style comments (// ...
and /* ... */
) and trailing commas in objects/arrays (similar to JSON5).
These options are available for most conversion commands:
-i <input_file>, --in <input_file>
: Specify the input file. Defaults to stdin.-o <output_file>, --out <output_file>
: Specify the output file. Defaults to stdout.-s, --sort
: Sort data before conversion. For dictionaries/maps, keys are sorted alphabetically. This helps in producing consistent output, especially for version control.-m, --mini
: Minify output. The specific effect depends on the format (e.g., no indents or newlines in JSON/XML, flow style in YAML).*2p
(to Plist):
-b, --bin
: Output a binary Plist file. By default, an XML Plist is generated.p2j
, y2j
(from Plist/YAML to JSON):
-b, --bin
: Preserve binary data (e.g., from Plist <data>
or YAML !!binary
) as a simple base64 encoded string in JSON. The default behavior is to represent binary data as a special dictionary: {"__bytes__": true, "base64": "..."}
.*2x
(to XML):
-R <name>, --root <name>
: Specify the root tag name for the XML document. This is particularly useful if the input data is a list or if you want to override the default root tag (which is often ‘root’ or derived from a single top-level key). This option uses the xmltodict
backend for XML generation.-t <name>, --tag <name>
: Wrap the entire output in the specified tag. This option uses the dict2xml
backend, which may produce a simpler XML structure, especially for lists. If -t
is used, the -R
option is ignored.x2*
(from XML):
-N, --namespaces
: Process XML namespaces. If enabled, tag names in the resulting data structure will include namespace prefixes.c2*
(from CSV):
-H, --header
: Treat the first row of the CSV as a header row. The CSV data will be read as a list of dictionaries (where keys are header names) instead of a list of lists.-d <dialect>, --dialect <dialect>
: Specify the CSV dialect (e.g., ‘excel’, ‘excel-tab’, ‘unix’). If not specified, Yaplon attempts to sniff the dialect.-k <key_index>, --key <key_index>
: (Requires -H
or implies it) Use the values from the column specified by key_index
(a 1-based integer) as keys for a top-level dictionary. The values of this dictionary will be the row dictionaries (with the key column itself removed from the row dictionary).For convenience, Yaplon also installs shortcut scripts that correspond directly to the conversion commands:
csv22json
, csv22plist
, csv22xml
, csv22yaml
json22plist
, json22xml
, json22yaml
plist22json
, plist22xml
, plist22yaml
xml22json
, xml22plist
, xml22yaml
yaml22json
, yaml22plist
, yaml22xml
Note: These scripts use 22
(e.g., json22yaml
) instead of 2
to avoid potential conflicts with other similarly named conversion tools you might have installed.
JSON to YAML (file to file using dedicated tool):
json22yaml -i input.json -o output.yaml
JSON to YAML (using pipes and main yaplon
command):
cat input.json | yaplon j2y > output.yaml
PLIST to JSON (minified, preserving binary as base64 strings):
yaplon p2j -m -b -i input.plist -o output.json
CSV with header to XML (using a specific column as key and custom root tag):
yaplon c2x -H -k 1 --root "Entries" -i data.csv > data.xml
This section describes the internal workings of Yaplon.
Yaplon’s conversion process generally follows these steps:
reader.py
): The input data is parsed into a standardized Python data structure, primarily using collections.OrderedDict
. This preserves key order from the source format where applicable.
reader.sort_ordereddict
function can be invoked via the -s
/--sort
CLI option to recursively sort these OrderedDict
s by key.writer.py
): The Python OrderedDict
object is then serialized into the target output format.The Command Line Interface (CLI) is built using the Click library.
yaplon.ojson.read_json
. This module supports JSON5-like features such as C-style comments (//
, /* */
) and trailing commas. These are stripped by yaplon.file_strip.json.sanitize_json
before parsing with Python’s standard json
module (using object_pairs_hook=OrderedDict
).yaplon.ojson.json_dump
. Can produce regular or minified JSON. Binary data is handled based on the -b
flag (see “Binary Data Representation” below).yaplon.oplist.read_plist
, which wraps Python’s standard plistlib
. It parses both XML and binary Plists. Plist <data>
elements are converted to Python bytes
objects, and <date>
elements become datetime.datetime
objects.yaplon.oplist.plist_dumps
(for XML Plist) and yaplon.oplist.plist_binary_dumps
(for binary Plist, via -b
flag). These also use plistlib
.yaplon.oyaml.read_yaml
, which uses the PyYAML library. It employs custom constructors to load YAML !!binary
tags into Python bytes
objects and !!timestamp
tags into datetime.datetime
objects. Data is loaded into OrderedDict
to preserve order.yaplon.oyaml.yaml_dumps
. Uses a custom dumper for OrderedDict
to maintain key order and for bytes
to serialize them as !!binary
tags. Supports minified (flow style) output with the -m
flag.yaplon.reader.xml
, which uses the xmltodict library to parse XML into an OrderedDict
. Namespace processing can be enabled with the -N
flag.yaplon.writer.xml
. This function is noted as potentially “primitive and buggy” for complex cases. It can use two different backends:
xmltodict.unparse
: Used by default or when the -R
/--root
option is specified. This backend is generally more feature-rich for representing complex data structures.dict2xml.Converter
: Used when the -t
/--tag
option is specified. This backend might produce simpler XML, especially for lists, and wraps the entire structure in a single user-defined tag.bytes
are converted to base64 encoded strings, and datetime.datetime
objects are converted to ISO 8601 strings.yaplon.reader.csv
using Python’s standard csv
module.
-H
/--header
is used, a list of OrderedDict
s.-d
).-k
/--key
option allows restructuring the data into a top-level OrderedDict
keyed by values from a specified column.collections.OrderedDict
is used as the primary internal data structure after parsing to ensure that key order from formats like YAML or sorted JSON is preserved and can be maintained in the output.bytes
objects.<data>
tags.!!binary
tag.{"__bytes__": true, "base64": "..."}
(a dictionary indicating binary content).-b
flag (for p2j
, y2j
): A simple base64 encoded string.While not a formalized plugin system, adding support for a new format would conceptually involve:
yaplon/reader.py
and yaplon/writer.py
respectively.OrderedDict
(or list of OrderedDict
s, etc.).OrderedDict
and serialize it to the new format.yaplon/__main__.py
.Contributions to Yaplon are welcome! Whether it’s bug reports, feature suggestions, documentation improvements, or code contributions, your help is appreciated.
Please refer to the “Installation > For Development” section above for instructions on how to clone the repository and set up a development environment.
git checkout -b my-feature-branch
.tests/
directory.make test
or python3 -m pytest
. Ensure all tests pass.make lint
(uses Flake8 and Black to check style)make format
(uses Black to automatically format code)git push origin my-feature-branch
.main
branch of the twardoch/yaplon
repository.TODO.md
file in the repository for a list of known tasks and planned enhancements.For significant changes, please add an entry to the CHANGELOG.md
file, describing the change and referencing the relevant issue or PR if applicable.
Yaplon uses Black for code formatting and Flake8 for linting. Please ensure your contributions adhere to these standards by running make format
and make lint
.
The project uses pytest for testing. New features should include corresponding tests, and bug fixes should ideally include a test that demonstrates the bug and verifies the fix.
See CHANGELOG.md for a history of changes to the project.
Yaplon is distributed under the MIT License.
.github/FUNDING.yml
)