How to strip null fields for gdpr data minimization
- Step 1Export a representative record to a file — Save a real (or realistic) record as a
.jsonfile. Pick one with several optional fields left unset so you can see exactly what minimisation removes. The file is parsed as one JSON document. - Step 2Drop the file and choose targets — Drag the file onto the dropzone.
null valuesis ticked by default. Addempty stringsandempty objectsif your write path stores""or{}for never-collected fields; addempty arraysonly if an empty list is truly 'never collected' rather than 'collected, none'. - Step 3Keep Deep on for nested personal data — Leave Deep (strip at all nesting levels) checked so nested objects like
addressorconsentHistoryare minimised too, not just top-level keys. - Step 4Run and read the stripped count — Click Strip Values. The 'N values stripped' figure confirms how many empty placeholders were removed; sanity-check it against how many fields you expected to be unset.
- Step 5Confirm no real value was lost — Diff the original against the output with json-diff. Verify only nulls/empties were removed and that
false/0consent answers and any deliberate tombstones survived. - Step 6Apply minimisation in the write path — Replicate the rule before persistence — strip unset fields before
prisma.create,insertOne, orINSERT. Document that absent means 'not collected' so later queries don't misread a missing key as a null answer.
Minimisation targets and the privacy meaning of each
Match each strip target to the storage pattern it cleans. Choose conservatively: stripping a value you can't reconstruct is irreversible for that file.
| Target | Storage pattern it removes | Privacy caution | Default |
|---|---|---|---|
null values | "phone": null placeholder for never-collected data | Safe to strip if null means 'not collected', not 'cleared' | On |
empty strings | "middleName": "" from a form field left blank | Distinguish 'blank' from 'intentionally empty' before stripping | Off |
empty arrays | "purchases": [] for a user with no history | [] can mean 'collected, none' — keep it if that's relevant | Off |
empty objects | "address": {} shell with no populated sub-fields | Cascades from null/empty-string children when those are also ticked | Off |
undefined values | Not applicable to files (JSON has no undefined) | No-op for file input; leave off | Off |
What minimisation does and does NOT do
Stripping reshapes a record; it is not a lawful-basis or erasure mechanism on its own.
| Concern | This tool's effect |
|---|---|
| Shape of new records | Removes unset fields so the document shows only collected data |
| Already-stored data | No effect — it processes the file you drop, not your database |
| Right to erasure (Art. 17) | Not a deletion log; use your DB's delete + audit trail |
Negative consent (false) | Preserved — false is a real answer, never stripped |
Deliberate null tombstone | Stripped if null is ticked — leave null off to keep tombstones |
File and tier limits
Pro tool. Numbers from lib/tier-limits.ts and lib/csv-utils.ts.
| Limit | Free | Pro |
|---|---|---|
| Max file size | 2 MB | 100 MB |
| Files per run | 1 | Up to 10 (batch) |
| Accepted extensions | .json .ndjson .jsonl .txt | .json .ndjson .jsonl .txt |
| Processing location | In-browser, no upload | In-browser, no upload |
Cookbook
Minimisation before/after on synthetic personal-data records. All names and emails are fictional.
Strip never-collected placeholders
ExampleA signup captured email only; phone, DOB, and marketing consent were never asked. Default strip (null, Deep on) leaves the minimal collected record.
Input (record.json):
{
"email": "sam@example.com",
"phone": null,
"dob": null,
"marketingConsent": null
}
Strip Values (targets: null · Deep on):
{
"email": "sam@example.com"
}
3 values stripped.Preserve a recorded 'no'
ExampleA user explicitly declined marketing. That false is real consent data and is preserved, while the unasked phone field is removed.
Input:
{
"email": "jo@example.com",
"marketingConsent": false,
"phone": null
}
Strip Values (targets: null · Deep on):
{
"email": "jo@example.com",
"marketingConsent": false
}
false survives; only the null phone is stripped.Collapse an empty address shell
ExampleAn address object exists but every sub-field is null. Ticking null + empty objects removes the children, then the empty address cascades away.
Input:
{
"email": "li@example.com",
"address": { "line1": null, "postcode": null }
}
Strip Values (targets: null + empty objects · Deep on):
{
"email": "li@example.com"
}
3 values stripped (line1, postcode, then empty address).Keep 'collected, none' lists
ExampleAn empty purchases array means the user was checked and has none — analytically different from never-checked. Leave empty arrays unticked.
Input:
{ "email": "ash@example.com", "purchases": [], "phone": null }
Strip Values (targets: null only · empty arrays UNCHECKED):
{ "email": "ash@example.com", "purchases": [] }
The meaningful empty list is preserved.Anonymise, then minimise
ExampleFor audit samples, mask identifiers first, then strip empties so the shared file is both pseudonymised and minimal.
# 1. Mask emails/names with the anonymiser tool # -> see /tool/json-anonymizer # 2. Drop the masked file here # 3. Strip Values (null, Deep on) -> Download # Result: minimal + pseudonymised record set for the DPIA appendix
Errors and edge cases
Real errors and silent failures sourced from each platform's own documentation. Match the wording to the row, fix what the row says to fix.
Tool does not delete stored data
By designStripping operates on the file you drop, not on your database. It models the minimised shape for new writes; it does not satisfy a right-to-erasure request for records already persisted. Use your database delete plus an audit log for that.
Stripping a deliberate `null` tombstone
CautionIf null is a meaningful 'this was cleared' marker rather than 'never collected', stripping it loses that signal. Leave the null checkbox off for those records, or use a sentinel value before stripping.
Negative consent stored as `false`
Preservedfalse is never stripped — there is no falsy mode. A recorded decline ("marketingConsent": false) survives, which is what you want for demonstrating you honoured the choice.
Empty array as 'collected, none'
Caution[] can mean the data was gathered and the result is empty, which is itself a fact. Don't tick empty arrays when that distinction matters to a controller or processor record.
Special-category data in the file
Local-onlyHealth, biometric, or other Article 9 data in the dropped file is processed entirely in your browser and never uploaded. Still follow your own handling policy for the local file (encryption at rest, etc.).
Multi-record NDJSON export
Parse errorA line-delimited export with one record per line is not a single JSON document and will not parse. Wrap records into a JSON array first, then strip the array.
Invalid JSON from a hand-edited record
Parse errorA trailing comma or BOM throws on JSON.parse and the run fails. Repair with json-format-fixer before stripping.
Whitespace placeholder instead of empty string
PreservedA field set to " " is not matched by empty strings (only zero-length "" is). Trim such values upstream if they represent 'not collected'.
Indentation is fixed
ExpectedOutput is 2-space pretty JSON; there is no indent control. If your storage layer wants compact JSON, minify the result afterward with json-minifier.
Frequently asked questions
Does stripping null fields make me GDPR-compliant?
No single tool does. Stripping unset fields supports the minimisation principle by keeping stored records limited to data actually collected, but compliance also needs lawful basis, retention rules, and erasure handling. Treat this as one practical step in a broader programme.
Is the personal data in my file uploaded?
No. The file is read and stripped entirely in your browser. Records with names, emails, or special-category data never reach JAD Apps servers.
Does this delete data already in my database?
No. It processes the file you drop, not your live store. Use it to design and verify the minimised write shape, then apply that rule in your persistence code; handle existing records with your normal delete/retention process.
Will it remove a 'no' consent stored as false?
No. false and 0 are preserved as real values. A recorded decline survives stripping, which is important evidence that you respected the user's choice.
What's the difference between null and an absent field for GDPR?
An absent field commits no value for that data point in the record; a null field stores an explicit empty placeholder. Sparse (absent) records make minimisation and subject-access exports clearer, which is why stripping unset nulls before storage helps.
Should I strip empty arrays from consent or purchase history?
Usually not. [] often means 'we checked and there are none', which is a fact you may need to demonstrate. Leave empty arrays unticked unless an empty list genuinely means 'never collected'.
Can I strip and pseudonymise in one step?
Not in one pass here. Mask identifiers first with json-anonymizer, then drop the masked file into this tool to remove empties — giving you a minimal, pseudonymised sample.
How do I prove what changed for an audit?
Keep the original and run both files through json-diff. The diff documents exactly which empty fields were removed, which is useful evidence in a DPIA appendix.
Does it work on nested address or consent objects?
Yes, with Deep mode on (the default). Nested objects are recursed and minimised; an address whose sub-fields are all stripped will itself collapse away if empty objects is also ticked.
What file size can I process?
Free allows 2 MB per file; Pro allows up to 100 MB and batch processing of up to 10 files. Larger files are blocked before processing.
Is the undefined option useful for records?
Not for files. JSON cannot contain undefined, so the checkbox is a no-op on dropped files. Use null (and optionally empty string/array/object).
Where do the limit numbers come from?
They are defined in the codebase — tier limits in lib/tier-limits.ts and the free file cap in lib/csv-utils.ts — so the 2 MB / 100 MB figures here reflect the tool as built.
Privacy first
Conversion runs locally in your browser. No file is uploaded — only metadata counters are saved for signed-in dashboard stats.