JSON Upsert
type: "json-upsert" adds an entry to a JSON list, or replaces the entry that has the same key. Use it for a file that collects one item from each generation, such as a registry of entities or a menu.
json-upsert: add or update an entry in a JSON list
The source file holds one JSON5 object. It is merged into a JSON list in the target: an existing entry with the same key is replaced as a whole (not merged field by field), otherwise the entry is added.
init: {
"entity-record.json5": [
{
target: "entities.json5",
type: "json-upsert",
keyField: "key", // field that identifies "the same entry" (default "key")
// path: "#/items", // optional: where the list lives in the document (default: the root)
// position: "prepend", // optional: where a NEW entry goes (see below)
},
],
},
| Key | Meaning |
|---|---|
keyField | The field that says two entries are "the same". Default key. |
path | Where the list lives inside the document, as a path such as "#/groups/admin/entities". Default: the document itself is the list. |
position | Where a new entry goes. See Where a new entry goes. |
wrapperPrefix, wrapperSuffix | Text around the JSON in the target, so the data can live inside a script, such as const ENTITIES = and ;. |
seed | Optional: a template file that supplies a starting document. Not needed in most cases. |
You do not need a seed. Unlike an insert, which needs a slot to insert into, a
JSON list can always start empty. If the target does not exist (or is blank), it starts as an empty document and the first
entry creates the list. Add a seed only when the document must start with other data already in it.
path: a list inside a larger document
With a path, the list can sit anywhere in the document. Every part of the path that is missing is created, and
everything else in the file is left alone:
{ target: "data.json", type: "json-upsert", path: "#/groups/admin/entities" }
- A missing target becomes
{ "groups": { "admin": { "entities": [ ...the entry... ] } } }. - In an existing file only that list changes. Sibling keys (
version, other groups) stay exactly as they were. - Each step of the path must lead through an object, and the end must be a list of objects. An array position
such as
#/items/0/childrenis not supported: a path names keys, not positions. - Without a
paththe document itself must be the list (or empty). A document that is an object needs apathto the list inside it.
Where a new entry goes
position only matters for an entry that is not in the list yet. An entry that is already there stays where it is
(except with sortBy, which puts it back in order).
position | Where a new entry goes |
|---|---|
"append" (default) | At the end |
"prepend" | At the start |
{ sortBy: "label" } | In order by that field, ascending. Add order: "desc" for descending. |
{ after: "task" } | Right after the entry whose key is task |
{ before: "task" } | Right before that entry |
{ index: 0 } | At that exact position (clamped to the length of the list) |
If the anchor of after or before is not in the list, the new entry goes at the end.
What it refuses
A json-upsert that could only work by throwing existing data away stops with a message that names the file, and
the target is left unchanged. It refuses when:
- the document is an object and there is no
path(it would be replaced by a list); - a step of the
pathis something other than an object, or the end of it is something other than a list; - an entry in the list is not an object;
- the existing target is not valid JSON5 (see below), or the source file does not hold one JSON5 object;
positionis not one of the forms above (for example"middle",{ sortby: "label" }or{ index: "first" }).
An empty object ({}) or an empty list counts as an empty start, never as an obstacle.
If the target exists and is not JSON
json-upsert can only update a JSON or JSON5 document. If the file is something else (plain text, HTML, a script), the generation
stops with a message that names the file and says the content is not valid JSON5, and the file is left untouched.
- Data inside a script. If the JSON sits inside other text, such as
const ENTITIES = [ ... ];, declare that text withwrapperPrefix: "const ENTITIES = "andwrapperSuffix: ";". Then only the part between them is read and rewritten, and the surrounding text is kept exactly. - A starter file with only comments is not valid JSON5. Start from
[](or{}), or leave the file out and let CodeBaker create it. - Comments and trailing commas are fine inside the JSON itself, but they are not kept when the file is rewritten.
- If the file was not meant to be JSON at all, point
targetat a different file.
Good to know
- The target is rewritten as plain, formatted JSON on every upsert, whatever its extension: comments in a
.json5target are lost. - The list can only hold objects, because an entry is recognized by its
keyField.
Next
- Data Sources: read the file
json-upsertwrote into another file of the same generation with$file. - Generating JSON: write a
.json5template and get plain JSON. - Advanced Templates: the mapping file, entry types and run order.