Why is my JSON object unset after I streamed values into it?
← JSON and Data Processing · Ref: Q1413
A JSON object can only take objects, and a single JSON value can take nothing. 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.
A MISMATCH IS INVALID
A scalar (a number, a String, a Boolean) or an array poured into an object has no key to merge under, so the operation is invalid. A single value has a nature and is not a container, so pouring anything into it is invalid too. EK9's answer to an invalid operation is UNSET - no exception, no half-merged object.
json? tells you whether the target survived:
settings <- JSON("theme", JSON("dark")) cat [JSON(7)] > settings stdout.println(`still set: ${settings?}`)
THE MISMATCH STICKS
Once a pour has gone wrong, the items after it are ignored - an object poured in after the mismatch does not start a new array - so a mismatch anywhere in the stream is still visible at the end. Only an explicit assignment ends it: ':=:', ':~:' (which takes its source, as it does into any unset JSON) or clear().
KEEP THE STREAM TO OBJECTS
Filter the stream down to objects before it reaches an object target:
isObject() as pure -> item as JSON <- rtn as Boolean: item.objectNature()
then cat items | filter by isObject > settings.
To collect values of any shape, pour them into an unset JSON or an array instead (Q1411). See Q1412 for merging objects.
Example
defines module qa.jsonanddata.jsonpourmismatchunset defines function isObject() as pure -> item as JSON <- rtn as Boolean: item.objectNature() defines program JsonPourMismatchUnsetDemo() stdout <- Stdout() //A scalar has no key to merge under: the object is left unset scalarInto <- JSON("colour", JSON("red")) cat [JSON(7)] > scalarInto stdout.println(`scalar into object, still set: ${scalarInto?}`) //So does an array arrayInto <- JSON("font", JSON("serif")) cat [$$ [1, 2]] > arrayInto stdout.println(`array into object, still set: ${arrayInto?}`) //A single value has a nature and is not a container: nothing can be poured into it count <- JSON(1) cat [JSON(2)] > count stdout.println(`into a single value, still set: ${count?}`) //The mismatch sticks: an object after it does not start a new array sticky <- JSON("size", JSON(10)) cat [JSON(7), JSON("weight", JSON(3))] > sticky stdout.println(`object after the mismatch, still set: ${sticky?}`) //Keep the stream to objects before it reaches an object target settings <- JSON("theme", JSON("dark")) cat [JSON(7), JSON("size", JSON(12)), $$ [1, 2]] | filter by isObject > settings stdout.println(`filtered: ${settings}`)
Other ways to ask this
- What happens when I stream a number into a JSON object or a JSON value?
- Can I pour a JSON array into a JSON object?
- How do I stop non-objects spoiling a JSON object I stream into?
- Why does json? stay false for the rest of a pipeline?
Coming from another language?
Java: Jackson ObjectNode.setAll() only accepts an ObjectNode, so a value of the wrong shape is a compile error or a ClassCastException. JavaScript: Object.assign({}, 7) silently ignores the number. EK9: a value of the wrong shape poured into a JSON object, or anything poured into a single JSON value, makes it unset for the rest of the pour, visible with json?, and a filter keeps the stream to objects.
Keywords: objectNature, filter, pour, unset, pipe, invalid, json, object, stream, mismatch