# dynojson
[](https://github.com/cykruss/dynojson/actions/workflows/CI.yml)
[](https://pypi.org/project/dynojson/)
[](https://crates.io/crates/dynojson)
[](https://pypi.org/project/dynojson/)
[](LICENSE)
**Marshall / unmarshall JSON to and from DynamoDB JSON format** — a fast Python library backed by Rust.
Convert between regular JSON and [DynamoDB JSON format](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Programming.LowLevelAPI.html) on the terminal or in your Python code. Powered by a Rust core via [PyO3](https://pyo3.rs) for maximum performance.
- 🔥 Works on all platforms (Linux, macOS, Windows)
- 📄 Convert a file from a given file path
- ✏️ Convert a JSON string directly
- ⛓ Read from stdin — pipe in JSON from another command
- 🍰 Extract and convert only a subset of the JSON
- 🤝 Output can be piped or redirected to other commands
- 🧰 Integrate into your workflow with AWS DynamoDB CLI
## Installation
```sh
pip install dynojson
```
## Python API
All functions accept **JSON strings** or **Python dicts/lists** directly. The return type matches the input type.
```python
from dynojson import marshall, unmarshall, get_property
# ── With dicts (recommended) ──────────────────────────────────────
result = marshall({"name": "Alice", "age": 30})
# {"name": {"S": "Alice"}, "age": {"N": "30"}} ← returns a dict
result = unmarshall({"name": {"S": "Alice"}, "age": {"N": "30"}})
# {"name": "Alice", "age": 30} ← returns a dict
# ── With strings (also supported) ─────────────────────────────────
result = marshall('{"name": "Alice", "age": 30}')
# '{"name":{"S":"Alice"},"age":{"N":"30"}}' ← returns a string
result = unmarshall('{"name":{"S":"Alice"},"age":{"N":"30"}}')
# '{"name":"Alice","age":30}' ← returns a string
# ── Extract a property ────────────────────────────────────────────
subset = get_property({"Items": [{"type": {"S": "fruit"}}]}, "Items")
result = unmarshall(subset)
# [{"type": "fruit"}]
```
## CLI Usage
```sh
dynojson <command> [options] <json>
Commands:
dynojson unmarshall, u Convert DynamoDB JSON to regular JSON
dynojson marshall, m Convert regular JSON to DynamoDB JSON
Options:
-g <path> Extract a property before converting (dot-separated, supports *)
--version Show version
--help Show help
```
### Unmarshall (DynamoDB JSON → regular JSON)
```sh
# From a JSON string
$ dynojson u '{"name":{"S":"Alice"},"age":{"N":"30"}}'
{"name":"Alice","age":30}
# From a file
$ dynojson u data.json
# From stdin
# From AWS CLI
### Marshall (regular JSON → DynamoDB JSON)
```sh
# From a JSON string
$ dynojson m '{"name":"Alice","age":30}'
{"name":{"S":"Alice"},"age":{"N":"30"}}
# From a file
$ dynojson m data.json
# From stdin
### Extract a subset of JSON with `-g`
Use dot-notation to select a property. Supports numeric array indices and `*` to expand all array items.
```sh
# Get a specific property
$ dynojson m -g "fruits.0.benefits" food.json
# Expand all items
$ dynojson m -g "fruits.*.name" food.json
# With AWS CLI scan output
## DynamoDB type mapping
| String | `{"S": "…"}` |
| Number | `{"N": "…"}` |
| Boolean | `{"BOOL": …}` |
| Null | `{"NULL": true}` |
| Array | `{"L": […]}` |
| Object | `{"M": {…}}` |
String sets (`SS`), number sets (`NS`), and binary types (`B`, `BS`) are also supported during unmarshalling.
## Benchmarks
The dict path is faster than the string path because it skips two JSON serialization round-trips (`json.dumps` + `json.loads` on the Python side).
| Small (1 item) | 1.28x | 1.15x |
| Medium (100 items) | 1.20x | 1.22x |
| Large (10,000 items) | 1.14x | 1.22x |
Run the benchmark yourself:
```sh
maturin develop --release
python benchmarks/bench_marshall.py
```
## Development
### Prerequisites
- [Rust](https://rustup.rs/) (stable)
- Python 3.10+
- [uv](https://docs.astral.sh/uv/) (recommended) or pip
- [maturin](https://www.maturin.rs/)
### Setup
```sh
# Clone the repo
git clone https://github.com/cykruss/dynojson.git
cd dynojson
# Create a virtual environment
uv venv
source .venv/bin/activate
# Install build and test dependencies
uv pip install maturin pytest
# Build and install in development mode
maturin develop
# Run Rust tests
cargo test --lib
# Run Python tests
pytest tests/ -v
```
### Project structure
```
dynojson/
├── Cargo.toml # Rust package manifest
├── pyproject.toml # Python package manifest (maturin build)
├── README.md
├── LICENSE
├── CONTRIBUTING.md
├── src/
│ ├── lib.rs # PyO3 module + re-exports
│ ├── error.rs # Custom error types (thiserror)
│ ├── marshall.rs # Regular JSON → DynamoDB JSON
│ ├── unmarshall.rs # DynamoDB JSON → regular JSON
│ └── property.rs # Dot-path property extraction
├── python/
│ └── dynojson/
│ ├── __init__.py # Public Python API
│ ├── _dynojson.pyi # Type stubs
│ └── cli.py # CLI entry point
├── benchmarks/
│ └── bench_marshall.py # String vs dict path benchmark
└── tests/
├── test_marshall.py
├── test_unmarshall.py
├── test_property.py
└── test_cli.py
```
## Acknowledgements
This project was inspired by [ddbjson](https://github.com/duartealexf/ddbjson) by [Alexandre Duarte](https://github.com/duartealexf), an excellent Node.js CLI tool for converting DynamoDB JSON. `dynojson` is a ground-up Rust rewrite with Python bindings, building on the same ideas.
## License
[MIT](LICENSE)