Skip to main content

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)
},
],
},
KeyMeaning
keyFieldThe field that says two entries are "the same". Default key.
pathWhere the list lives inside the document, as a path such as "#/groups/admin/entities". Default: the document itself is the list.
positionWhere a new entry goes. See Where a new entry goes.
wrapperPrefix, wrapperSuffixText around the JSON in the target, so the data can live inside a script, such as const ENTITIES = and ;.
seedOptional: 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/children is not supported: a path names keys, not positions.
  • Without a path the document itself must be the list (or empty). A document that is an object needs a path to 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).

positionWhere 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 path is 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;
  • position is 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 with wrapperPrefix: "const ENTITIES = " and wrapperSuffix: ";". 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 target at a different file.

Good to know​

  • The target is rewritten as plain, formatted JSON on every upsert, whatever its extension: comments in a .json5 target are lost.
  • The list can only hold objects, because an entry is recognized by its keyField.


Next​

  • Data Sources: read the file json-upsert wrote into another file of the same generation with $file.
  • Generating JSON: write a .json5 template and get plain JSON.
  • Advanced Templates: the mapping file, entry types and run order.