How do I combine a stream of partial JSON objects into one JSON object?

← JSON and Data Processing · Ref: Q1412

Pour them into a JSON object with '>'. Pouring into JSON follows the target's shape: an unset or array target collects, an object target merges each streamed object in; anything else - a non-object into an object, or anything into a single value - is invalid, leaves the target unset, and later items do not revive it.

AN OBJECT TARGET MERGES

Each streamed object is merged in by the merge rule (the same rule as ':~:'):
- a key the target lacks, or holds as null, is filled;
- a key the target already holds is kept - the first recorded value wins;
- a nested object under a shared key is merged by this same rule, and a nested array appends.
What the target takes is a copy, so changing a source afterwards does not reach it.

START FROM AN OBJECT

The target must already be an object - an unset JSON collects the items into an array instead (Q1411). JSON().object() is an empty object:

  combined <- JSON().object()
  cat [JSON("name", JSON("Ann")), JSON("name", JSON("Bob"))] > combined
  stdout.println($combined)

See Q1411 for collecting into an array. See Q1413 for a stream that leaves the object unset. See Q192 for ':~:' on JSON.

Example

defines module qa.jsonanddata.jsonpourmergesobjects

  defines program

    JsonPourMergesObjectsDemo()
      stdout <- Stdout()

      //The first recorded value wins on a clashing key
      firstWins <- JSON().object()
      cat [JSON("name", JSON("Ann")), JSON("name", JSON("Bob"))] > firstWins
      stdout.println(`first wins: ${firstWins}`)

      //Nested objects merge by the same rule, nested arrays append, missing keys are filled
      person <- JSON("name", JSON("Ann")) + JSON("address", JSON("city", JSON("Leeds"))) + JSON("tags", $$ ["a"])
      fromTaskOne <- JSON("name", JSON("Bob")) + JSON("address", JSON("city", JSON("York")) + JSON("zip", JSON("LS1")))
      fromTaskTwo <- JSON("tags", $$ ["b"]) + JSON("age", JSON(30))
      cat [fromTaskOne, fromTaskTwo] > person
      stdout.println(`combined: ${person}`)
Other ways to ask this
  • What happens when I stream JSON objects into a JSON object?
  • Which value wins when two JSON objects streamed into one have the same key?
  • Does pouring JSON objects into an object merge nested objects?
  • How do I gather partial JSON results from several sources into one object?

Coming from another language?

Java: Jackson ObjectNode.setAll() overwrites (last wins); a first-wins deep merge is hand-written. Python: {**a, **b} and dict.update() are shallow and last-wins. JavaScript: Object.assign is shallow and last-wins; lodash.merge is deep but last-wins. EK9: '>' into a JSON object is a deep merge where the first recorded value wins, so partial results gathered from several sources never overwrite each other.

Keywords: pour, partial, nested, merge, combine, pipe, json, first wins, stream, object