How do I modify and combine JSON objects in EK9?

← JSON and Data Processing · Ref: Q192

EK9 provides operators for building and combining JSON objects. Use constructors to create objects and operators to merge, replace, or add properties.

BUILDING JSON OBJECTS

Create an empty JSON object:

  obj <- JSON().object()

Create with key-value pairs:

  person <- JSON("name", JSON("Alice"))

ADDING PROPERTIES (+)

Add a key-value pair to a JSON object:

  person + JSON("age", JSON(30))

Merge and replace work on JSON OBJECTS. Text becomes an object through JSON().parse(text), which returns a Result of (JSON, String) - check isOk() before ok(). The JSON(String) constructor does not parse: it makes a JSON string value, and merging another value into it keeps the first - a set value is never overwritten, and a value of another kind is never forced in.

MERGE OPERATOR (:~:)

Merge properties from one JSON object into another:

  base :~: extras

Merge never overwrites a set value: existing properties in base are kept, new properties from extras are added, a property base holds as null is filled, and nested objects are merged by the same rule (nested arrays append). A JSON null counts as unset, so it is filled; a top-level array appends what is merged into it.

REPLACE OPERATOR (:^:)

Replace matching properties:

  target :^: replacement

Properties in target that also exist in replacement are overwritten; properties only in replacement are not added.

See Q188 for JSON basics. See Q96 for operator semantics. See Q189 for converting to JSON.

Example

defines module qa.jsondata.manipulate

  defines function

    <?-
      Parses JSON text into an object; parse() returns Result of (JSON, String).
    -?>
    parsedObject()
      -> jsonText as String
      <- rtn as JSON: JSON().object()
      parseResult <- JSON().parse(jsonText)
      if parseResult.isOk()
        rtn := parseResult.ok()

  defines program

    JsonManipulateDemo()
      stdout <- Stdout()

      // === BUILDING JSON OBJECTS ===

      emptyObj <- JSON().object()
      stdout.println(`Empty object: ${emptyObj}`)

      person <- JSON("name", JSON("Alice"))
      stdout.println(`Person: ${person}`)

      // === ADDING PROPERTIES WITH + ===

      updated <- person + JSON("age", JSON(30))
      stdout.println(`With age: ${updated}`)

      // === MERGE OPERATOR :~: ===
      // new properties from extras are added, existing ones in base are kept

      base <- parsedObject(`{"name": "Alice", "role": "dev"}`)
      extras <- parsedObject(`{"team": "backend", "level": 5}`)
      base :~: extras
      stdout.println(`After merge: ${base}`)

      // === REPLACE OPERATOR :^: ===
      // age exists in both so it is overwritten; city is not added

      target <- parsedObject(`{"name": "Alice", "age": 30}`)
      replacement <- parsedObject(`{"age": 31, "city": "London"}`)
      target :^: replacement
      stdout.println(`After replace: ${target}`)

      // === A VALUE MERGED INTO A VALUE ===
      // a set value is never overwritten, so the first is kept

      single <- JSON("first")
      single :~: JSON("second")
      stdout.println(`Value merge: ${single}`)

Common mistakes

E50001 — The JSON type does not have a static 'createObject()' method. Create an empty JSON object by calling 'JSON().object()' which creates an unset JSON and then converts it to an empty object. See ek9 -h E50001 for details.

Incorrect:

emptyObj <- JSON.createObject()

Correct:

emptyObj <- JSON().object()
Other ways to ask this
  • How do I merge two JSON objects in EK9?
  • How do I build a JSON object from parts in EK9?
  • How do I add properties to JSON in EK9?

Coming from another language?

Java: Jackson ObjectNode.put()/set() for building, JsonNode merge via custom code. Python: dict.update() or {**a, **b} merge. Rust: serde_json::Map insert/extend. Go: manual map merging. Kotlin: mutableMapOf + putAll. EK9: :~: merge and :^: replace operators on JSON, + for adding properties.

Keywords: merge, property, combine, object, replace, json, manipulate, build, add, serialize, data