Two exports from the same API, taken a day apart. A configuration file before and after a deployment. Yesterday's NDJSON log and today's. Sooner or later, everyone who works with JSON needs to answer the same question: what actually changed between these two files?
This guide covers why comparing JSON is harder than comparing ordinary text, how structural diffing solves it, what the Compare Files tool in GiantJSON Viewer+ can and cannot do (with real measured numbers), how to steer a comparison when the default answer is too noisy, and which desktop and command-line tools are worth knowing when a phone is not the right place for the job.
Why Ordinary Diff Tools Struggle With JSON
Classic diff tools compare files line by line. That works well for source code, where lines are a meaningful unit. JSON breaks that assumption in three ways:
Key Order Is Not Meaningful
Per the JSON specification, {"a": 1, "b": 2} and {"b": 2, "a": 1}
represent the same object. But many serializers do not guarantee key order, so two exports of
identical data can list keys in different order. A line-based diff flags every reordered key
as a change, even though nothing changed.
Formatting Is Noise
Pretty-printed with 2 spaces, pretty-printed with 4 spaces, minified onto a single line: all three are the same data. A text diff between a minified file and its formatted twin reports that every single line changed. Worse, a large minified file is often one line, so a line diff has exactly one unit to work with and its output is useless.
Moved Data Looks Like Delete + Add
If a record moves from position 12 to position 47,000 in an array, a text diff reports it as one deletion and one unrelated addition. In a big dataset where records get reordered (a common side effect of database exports), this turns a handful of real edits into thousands of phantom changes.
What a Structural JSON Diff Does Instead
A structural (or semantic) diff parses both files and compares the actual data: keys, values, arrays, nesting. The practical consequences:
- Key order differences are ignored.
{"a":1,"b":2}equals{"b":2,"a":1}. - Formatting differences are ignored. Minified and pretty-printed versions of the same data compare as identical.
- Changes are reported by path (for example
$.users[3].email), not by line number. - Good implementations recognize moved records as moves rather than delete/add pairs.
Most structural diff tools are desktop or command-line software, and most of them load both files fully into memory, which limits the file sizes they can handle. On mobile, structural diffing has been essentially unavailable for anything beyond small files.
Compare Files in GiantJSON Viewer+
GiantJSON Viewer+ now includes a Compare Files tool: a structural diff for JSON and NDJSON built on the same streaming philosophy as the large-file viewer. Both files are scanned in a streaming pass that writes a compact cache to disk; the comparison then runs against those caches instead of holding the files in memory. This means the tool is not limited by device RAM.
What It Does
- Structural comparison: key order and formatting are ignored; only real data differences are reported, each with its path and old/new values.
- Move detection: records that changed position are detected as moves, and each detected move is verified byte-for-byte against the source files rather than assumed from a hash match.
- NDJSON support: newline-delimited JSON (logs, pipeline exports) is a first-class input format alongside standard JSON.
- Grouped results: mass changes are grouped by where they occur in the file, so 50,000 additions to one array show up as one understandable group, not 50,000 rows to scroll through. A summary verdict comes first; the full breakdown, source excerpts, and a tappable minimap are there when you want detail.
- Steerable: four optional settings decide what counts as a change at all — how list items are paired, which part of the files to look at, which fields to ignore, and whether values are matched by meaning or byte for byte. See Controlling What Counts as a Change below.
- Source context on tap: every reported change opens the surrounding source lines from the actual file, syntax-coloured, with an A/B toggle to see the same spot on the other side. A long press copies the change's path, its source position, or the value itself.
- Sanity gate for unrelated files: if the two files share almost no content, the tool says so instead of producing a giant, meaningless diff. (Very small file pairs can still be force-compared.)
- Export: results can be saved or shared as a Markdown report or as NDJSON (one change per line, machine-readable), including old and new value excerpts.
- Offline: like the rest of the app, comparison runs entirely on the device. Nothing is uploaded.
Measured Performance
These are real numbers from the test rounds on a Samsung Galaxy S23 Ultra, using two 2 GB files (one per side):
- Identical pair: confirmed identical in roughly 4-8 seconds.
- 2 GB pair with 50,000 added records: compared in about 10 seconds, reporting exactly 50,000 additions in a single group.
- Repeat comparison: re-running a comparison whose caches already exist took 0.25 seconds, versus 52 seconds for the same pair cold (the first run includes building the index caches).
- Memory: stays below 400 MB even on the 2 GB runs (267 MB measured at report time on the 50,000-change case), because the engine streams from disk instead of loading the files.
Because both files are indexed to disk caches first, the expensive work happens once per file. Comparing the same files again, or comparing one file against several others, reuses the caches and is close to instant.
Pricing
Comparing files up to 1 MB is free. Comparing larger files is part of PRO. The free tier is enough to try the full workflow on typical config files and API responses; the large-file engine is what PRO pays for.
Honest Limitations
Being upfront about boundaries (see also Known Limitations):
- Heavily scattered edits in giant minified files: when thousands of tiny edits are spread randomly across a multi-gigabyte single-line file, the diff may fall back to reporting coarser changed regions instead of resolving every individual value. Typical real-world change patterns (appends, block edits, record changes) resolve exactly.
- No side-by-side split view in this first version. Results are a single unified breakdown with source excerpts, not two synchronized panes.
- No JSON Patch (RFC 6902) export. Exports are Markdown and NDJSON. If you need a machine-applyable patch, use a desktop tool from the list below.
- It compares, it does not merge. There is no three-way merge or conflict resolution; that remains desktop territory.
- Unrelated files are refused by design: if two large files share less than a small fraction of their content, the tool reports them as unrelated instead of rendering a diff that would be noise.
Controlling What Counts as a Change
A structural diff answers "what changed". On real data that answer is often technically correct
and practically useless: an export re-stamps updated_at on all 40,000 records, a
database dump lists the same rows in a different order, or you only care about one branch of a
file that happens to be two gigabytes. Compare Files has four settings for exactly these
situations. Each one is a separate screen with its own examples, and each is optional — leave
them alone and you get the plain structural comparison described above.
All four are configured after both files have been scanned, which matters: every value can be filled in by browsing the structure that was actually found in your files, and checked before anything is compared.
Match List Items: Pairing Records by Key
When a list is reordered, the tool has to decide which item in file B corresponds to which item in file A. That single decision separates "three records were edited" from "eleven thousand lines changed". There are three modes:
- Automatic (the default, and the recommended one): the engine works out
each list's identity field — an
idor something like it — on its own, and pairs items by it, so reordering is handled. Where it is not confident, it falls back to comparing by position instead of guessing. - By position only: item #1 is compared with item #1, #2 with #2. This is the right answer when position is itself meaningful — a time series, an ordered pipeline — and the noisy one when it is not.
- Custom keys: you name the identity field yourself, for the rare case where the automatic choice is wrong.
A custom key is a path that ends with the field identifying an item:
| Key | Meaning |
|---|---|
$.users[*].id |
items of the users list are paired by their id |
$.data.orders[*].sku |
works at any depth — plain names joined by dots |
$.accounts.{*}.orders[*].sku |
{*} stands for any one key, for levels that are a map of IDs |
$.grid[*][*].code |
[*] is the items of a list, and may repeat for nested lists |
You can also set a single key field that applies to every list — often just id —
and add per-list rules on top of it for the lists that use something else.
Compare Only a Part
This points the comparison at one exact place and ignores everything outside it. Useful when a
file is large but the interesting part is not: compare $.data.items and skip the
metadata, envelope, and everything else that surrounds it.
What goes in the box is an address, not a pattern — it names a single spot, such as
$.data.items or $.orders[128].shipping. Wildcards like
[*] deliberately do not work here; a scope that could match many places would make
the report ambiguous about what was actually compared. File A is the baseline for resolving
the address.
Ignore Fields
The answer to the re-stamped updated_at problem. An ignore entry takes one of two
forms:
| Entry | Meaning |
|---|---|
updated_at |
a bare field name — ignored everywhere in both files, at any depth |
$.data.items[*].meta.ts |
a path — ignores that field at this one spot in the structure, in every list item |
$.accounts.{*}.updated_at |
{*} again stands for any one key, when a level is a map of IDs |
Ignored differences are hidden but still counted. The report states how many were suppressed, and if filtering removed every difference there was, it says so in those words rather than showing a clean "no differences" screen. A comparison should never look quieter than the data actually is.
Smart vs Exact Text
Smart (the default) counts only content differences: 1.0 and
1.00 are the same number, and spacing, indentation, and key order are ignored.
This answers "what actually changed?".
Exact text compares the raw text as written, so 1.0 versus
1.00 and different spacing both count. It answers a narrower question — "are
these two files byte-for-byte the same?" — and is the right mode for verifying a copy or a
re-export that should be identical.
Browse and Verify Before You Run
Every one of these screens has the same two buttons, and they exist because typing a path from memory into a phone keyboard is a poor way to spend an afternoon.
BROWSE shows what the scan actually found in your two files — the lists that can be keyed, the addresses that exist, the field names that are present — and a tap fills the value in. VERIFY then checks what you have against both scanned files and answers in plain language: seen in both files, seen only in A, or not seen in the scanned structure at all. Nothing is compared until you press COMPARE, so a wrong path costs you a second, not a full run over two gigabytes.
Every setting narrows the question, and the report always says which narrowing was applied: the scope that was compared, the rules that paired list items, and the number of differences that were ignored. A filtered comparison stays auditable.
Desktop and Command-Line Alternatives
A phone is not always the right place for a diff, and the desktop ecosystem for this job is mature. Below is an honest overview of the common options, with their trade-offs. All of them are good tools; which one fits depends on whether you want a script, a GUI, or a quick paste box.
Command-Line Tools
jq + diff (Windows / macOS / Linux, free, open source). The classic recipe:
normalize both files with jq -S . (sort keys, consistent formatting), then run an
ordinary diff on the results. jq is actively maintained and available
practically everywhere, including CI images.
Pro: no new tool to learn if you already use jq; fully scriptable.
Con: the final comparison is still a line diff. Reordered array items, renamed keys,
or 1.0 vs 1 still show up as noisy line changes rather than
structural ones, and sorting keys can scatter a single rename across the diff output.
jd (Windows / macOS / Linux, free, MIT license). A dedicated structural diff-and-patch CLI for JSON and YAML, distributed as a single Go binary. Pro: a true structural diff, with output in its own format, JSON Merge Patch (RFC 7386), or a subset of JSON Patch (RFC 6902), which makes it a good fit for automation and CI. Con: CLI only, and a smaller community than jq; tagged releases are infrequent even though development continues.
json-diff (npm, free, MIT license). A simple structural diff for Node users:
json-diff a.json b.json prints a colorized structural comparison.
Pro: trivially easy in npm-based projects.
Con: release cadence has slowed in recent years, and it needs a Node runtime rather
than being a single static binary.
Graphtage (command-line Python tool from Trail of Bits). A semantic diff for tree-like formats: JSON, YAML, XML, and others, and it can even diff a JSON file against a YAML file. Pro: genuinely semantic matching of unordered keys. Con: more niche and research-flavored than jq or jd, with a steeper learning curve.
gjxdiff (Linux x86-64, free for individuals and organizations under 100 people). Our own desktop-scale sibling of Compare Files: a structural JSON and NDJSON diff CLI that memory-maps its inputs, so it handles files bigger than the machine's RAM. Measured numbers and setup are in How to Diff Large JSON Files on Linux.
GUI Compare Tools
Meld (Linux / Windows, macOS via community builds; free, GPL). A well-known open-source visual diff and merge tool, actively maintained by the GNOME project. Pro: clean two- and three-way visual comparison and merging, which the phone tool deliberately does not attempt. Con: it has no JSON-aware mode, so you should pretty-print (and ideally key-sort) both files first or the diff fills with formatting noise; Windows updates are manual.
WinMerge (Windows only, free, GPL). Actively maintained, and notably it ships JSON-related plugins: PrettifyJSON formats both sides via jq before comparing, and QueryJSON can extract sub-parts of a file for comparison. Pro: free and Windows-native, with the formatting-noise problem largely handled by the plugin. Con: underneath it is still a line diff; the plugin normalizes formatting but does not produce a structural change report.
Beyond Compare (Windows / macOS / Linux; commercial, Standard $35 / Pro $70 per user, perpetual license). A long-established commercial compare suite, currently in its 5.x line. Pro: polished side-by-side text compare plus strong folder-compare and merge workflows; one license covers all platforms. Con: paid; and in practice JSON comparison there is a text compare over pre-formatted, sorted JSON rather than a structural diff, so the same normalization caveats apply.
VS Code Built-In Diff
If VS Code is already your editor, right-click a file, choose "Select for Compare", then "Compare with Selected" on the second file (or use "File: Compare Active File With..." from the command palette). Pro: free, zero setup, and fine for quick line-level checks. Con: the diff editor is line-based with character-level highlighting; for JSON you will want to format and key-sort both files first, or install a marketplace extension that offers a more structural JSON view.
Online Diff Sites
Sites like jsondiff.com (a semantic JSON compare tool) and several similar services let you paste two documents into a browser and see the differences immediately. For small, non-sensitive snippets they are the fastest option of all.
Most online diff tools do not document whether your data stays in the browser or is sent to a server. For proprietary, personal, or otherwise sensitive JSON, assume the data may leave your machine unless the tool explicitly states it works offline, or use a local tool instead.
When Not to Use a Phone for This
A phone diff tool exists for the situations where the data is already on the phone, or where the phone is simply what you have in hand: checking an export on the go, verifying a backup, inspecting logs pulled from a device. It is not trying to replace a workstation. Reach for a desktop tool when:
- You need to merge changes, not just see them. Three-way merges and conflict editing need a desktop GUI.
- You need machine-readable patches (JSON Patch, jd format) to feed into automation or CI pipelines.
- You are reviewing a large refactor side by side with your editor open, making fixes as you go. A workstation with a split view is simply the better environment.
- The comparison is part of a scripted, repeated workflow. CLI tools compose; a touchscreen does not.
Frequently Asked Questions
How do I compare two JSON files on Android?
Open GiantJSON Viewer+, choose Compare Files from the Tools screen, pick the two files, and run the comparison. The result starts with a plain verdict and a summary of additions, removals, changes, and moves, with the full grouped breakdown below it. If the answer is noisier than you need, the four settings on the setup screen narrow it: how list items are paired, which part of the files to compare, which fields to ignore, and smart versus exact text matching. Files up to 1 MB compare free; larger files require PRO.
Can I ignore fields like updated_at?
Yes. An ignore entry is either a bare field name, which is ignored everywhere in both files, or
a path such as $.data.items[*].meta.ts, which ignores that field at one exact
spot in the structure. Ignored differences are hidden but still counted, and the report says
how many were suppressed.
How do I compare two arrays in a different order?
Pair the items by an identity field instead of by position. The Match list items setting
defaults to Automatic, which finds each list's identity field on its own and falls back to
position matching when it is not confident. Custom keys lets you name the field yourself, for
example $.users[*].id.
Why does a normal diff show thousands of changes between two JSON files with the same data?
Because line-based diffs compare text. Different key order, indentation, or line wrapping all count as changes to a text diff even when the data is identical. A structural diff parses both files and compares actual keys and values instead.
Can I diff really large JSON files on a phone?
With a streaming engine, yes. Compare Files was tested at 2 GB per side on a Galaxy S23 Ultra: identical-pair confirmation in roughly 4-8 seconds, a 50,000-change comparison in about 10 seconds, repeat runs in 0.25 seconds, with memory staying under 400 MB.
Does it work with NDJSON?
Yes. NDJSON (newline-delimited JSON) is supported as an input format, and comparison results can be exported as NDJSON as well, one change per line.
Conclusion
Comparing JSON files is a structural problem, and text diffs answer a different question than the one being asked. On a desktop there is a healthy ecosystem of structural diff tools, from one-line CLI recipes to full GUI compare suites, and for merges, patches, and scripted pipelines they remain the right choice.
What was missing was a way to do this on a phone at all, let alone on files bigger than the phone's RAM. That is the gap Compare Files fills: a structural JSON and NDJSON diff that streams from disk, verified on 2 GB inputs, with results built to be read by a person first and exported for the record second — and with enough control over the question that the answer stays short enough to read on a phone.
Compare JSON Files on Your Phone
Try Compare Files in GiantJSON Viewer+ — free for files up to 1 MB, structural diff at any size with PRO.
Get GiantJSON Viewer+