Skip to main content

json-source-edit

A Python package for surgical, byte-preserving JSON editing. Change only what you intend to change — everything else in the file survives byte-for-byte, so your diff shows your edit, not a full-file rewrite.

The problem

The obvious way to edit a JSON file is parse → modify → serialize:

data = json.loads(text)
data["steps"][1]["line"] = 433
text = json.dumps(data, indent=2)

This is correct, and it destroys your diff. Real evidence, from the project this package was extracted out of: three simple changes to one file — two field updates and one deletion — were expected to produce a ~10-line diff. The parse-modify-serialize round-trip produced a 500+ line diff instead.

That's because json.dumps doesn't know or care what your original formatting was — indentation, key order, quote style, all of it gets re-decided from scratch, every time, whether or not you touched it. Multiply that by a codebase-sized JSON file and code review becomes impossible: every line looks changed, and the actual edit is buried.

json-source-edit fixes this at the source: it never re-serializes the document. It computes exact byte positions for each change against the original text and splices replacement bytes into the gaps — the same operation a film or tape splicer performs, cut and rejoin at an exact point, everything else on the reel undisturbed.

Install

pip install json-source-edit

(Or clone the repo and pip install -e . for local development.)

Usage

from json_source_edit import JSONEditor

editor = JSONEditor.from_string("""{
  "title": "Rust Basics",
  "steps": [
    {"file": "src/main.rs", "line": 10},
    {"file": "src/lib.rs", "line": 20}
  ]
}""")
editor.replace("/steps/0/line", 433)
editor.remove("/steps/1")
result = editor.apply(validate=True)
print(editor.preview_diff())
--- original
+++ modified
@@ -1,7 +1,6 @@
 {
   "title": "Rust Basics",
   "steps": [
-    {"file": "src/main.rs", "line": 10},
-    {"file": "src/lib.rs", "line": 20}
+    {"file": "src/main.rs", "line": 433}
   ]
 }

validate=True catches mistakes before they reach you. It reconstructs the expected result independently — replaying your edits against a copy of the parsed document — and diffs that against what the surgical edit actually produced. If they disagree, it raises SemanticValidationError instead of silently returning something subtly wrong.

apply() defaults to validate=False; save() defaults to validate=True — writing to disk is where a mistake actually costs you something, so that path checks unless you opt out.

Three more methods help you inspect a pending batch before committing to it: get_value, get_modifications, and preview_diff.

Scope

JSON Patch operation Status
replace Supported — any path, any depth
remove Supported — array elements and object properties, any depth, except the document root itself (structurally undefined: the root has no parent container to apply a comma/whitespace rule against)
test Supported — not a text edit, an assertion. Evaluated against the document as it stood before this batch's own operations (not a naive sequential reading of RFC 6902 — see the docstring on operations.Test), with type-strict comparison (1 does not test-equal 1.0 or true)
add Supported — new array index (any position, or - to append) or object key. An existing object key is replaced instead, per RFC 6902 — an existing array index is always an insert, never a replace (see docs/adr/0002-*.md). The document root is out of scope, same posture as remove.
move Supported — RFC 6902 §4.4, a remove composed with an add. Rejects moving a location into one of its own children, and moving a location to itself.
copy Supported — RFC 6902 §4.5, an add using a value resolved (and deep-copied) from elsewhere in the document.

Every JSON Patch operation is implemented. Multiple replace/remove/ test/add/move/copy calls batch correctly against the same original document — every path resolves in original-document coordinates regardless of what else is in the batch, so edit order doesn't matter and one deletion or insertion can't corrupt another edit's position.

Benchmark (v0)

Reproduce with python benchmarks/throughput.py — a standalone script, no test framework required.

Methodology: replaces 5% of a flat array's elements at each size, median of 5 runs, reporting throughput against the original document size (the more relevant number for "can this handle my file" than edit count alone).

Single-machine numbers, not a controlled benchmark environment — expect real variance run to run and machine to machine; rerun locally before relying on any of this for capacity planning.

Size Elements Document Edits Median time Throughput
small 200 0.01MB 10 0.65ms 15,353 edits/sec, 13.7 MB/sec
medium 2,000 0.09MB 100 6.25ms 15,990 edits/sec, 14.9 MB/sec
large 20,000 0.97MB 1000 87.22ms 11,465 edits/sec, 11.1 MB/sec

(Measured on macOS/arm64, Python 3.12.4, 2026-08-01 — see the script's own output for full per-run samples; the "large" row in particular showed bimodal timing on this machine, a real observation, not smoothed over.)

Provenance

This package was designed and hardened inside codetour-cli, across four independent review gates, before being extracted here with no known remaining incompleteness. See PROVENANCE.md for the commit-by-commit history and docs/adr/0001-extraction-from-codetour-cli.md for the extraction decisions themselves (import strategy, the dependency verdict, the supported-Python-versions floor).

Credits

The position-mapping this package edits against — turning a JSON document into exact byte offsets for every value and key — is done by json-source-map, a small, dependency-free library by David Andersson. It's the one piece of this package that isn't ours: everything in src/json_source_edit/source_map.py is a thin seam around it, and everything else in this package builds on top of what it computes. Elegant, narrowly-scoped libraries like this are what make a package like this one feasible to write at all.

Status

Published: pypi.org/project/json-source-edit. pip install json-source-edit. 243 tests, 100% line coverage, full JSON Patch operation coverage. See CHANGELOG.md for release history. Design decisions beyond the original extraction live in docs/adr/ (0002: add's insertion doctrine, including the move/copy compositions; 0003: test's pristine-batch semantics).

License

MIT.

Metadata

Release files for json-source-edit 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for json-source-edit 0.4.0
File Size Uploaded
json_source_edit-0.4.0.tar.gz 66.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for json-source-edit 0.4.0
File Interpreter ABI Platform
json_source_edit-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 105.9 kB

Release files / json_source_edit-0.4.0.tar.gz

Download URL json_source_edit-0.4.0.tar.gz
Size 66.6 kB
Tags Source
SHA-256 checksum
How to use checksums
634f236bd866124c8b1581fc74642bdf1f361569d1e194d07c33b7559e479f81
BLAKE2b-256 checksum
How to use checksums
c98dfe047472dc55c3ed39b0d077b5de92e7b38d00941c681f54f3f0be6c84fc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 4, 2026.

Transparency log

Release files / json_source_edit-0.4.0-py3-none-any.whl

Download URL json_source_edit-0.4.0-py3-none-any.whl
Size 39.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8f2993df214e4d86369a10f71e506af584eabe6fae6c912d29db5c6dad1aeb6d
BLAKE2b-256 checksum
How to use checksums
d31df2085cd9a0e2bb649a198c790e65786431557712d419c6012156238fc1d6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 4, 2026.

Transparency log

Release history Release notifications | RSS feed

1.0.0

2 release files

0.6.0

2 release files

0.5.1

2 release files

0.5.0

2 release files

This release

0.4.0 This release

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page