A reference object encodes a reference relation inside an AST setting. Its shape is `{ ref: <target AST object> }`: the `ref` field points to the target, while the target remains outside the source object's containment hierarchy. Unlike a contained AST object, following this link does not make the target a child to traverse into. section 3.4 section 3.3.1
In JavaScript, an encoded AST object has `concept` and `settings` fields, whereas a reference object has a `ref` field. A reliable recognizer first excludes null and arrays, then checks for `ref`; a complete reference relation additionally requires `ref` to be an AST object. section 3.2.2
Use a reference object when a setting must name another AST object without containing it:
- Read the target through `settings["attribute"].ref` after confirming that the setting exists. section 3.4 section 6.1.2
- Create the wrapper when a user chooses a target, assigning `{ ref: selectedAstObject }` rather than assigning the target directly. section 6.1.2
- During depth-first tree traversal, inspect `value.ref` but do not traverse into it; traverse only containment relations and array items. section 3.3.1
- For JSON persistence, replace the in-memory target with its ID as `{ refId: target.id }`, avoiding object-reference cycles. section 7.2
Referencing a rental attribute
An `Attribute Reference` used as a data attribute's initial value stores its chosen target in the `attribute` setting. The projection reads the referenced attribute's name through `settings["attribute"].ref.settings["name"]`. Before a selection exists, it guards against an undefined setting; once selected, it creates `{ ref: matchingAttribute }`, so later changes to that target remain visible through the reference. section 6.1.2
Listing 7.15 Transforming an AST into a cycle-free “almost-AST”
const serialize = (value) => {
if (isAstObject(value)) {
const serializedAstObject = {
id: value.id,
concept: value.concept,
settings: {}
}
for (const propertyName in value.settings) {
serializedAstObject.settings[propertyName] =
serialize(value.settings[propertyName])
}
return serializedAstObject
}
if (isAstReferenceObject(value)) {
return ({ #1
refId: isAstObject(value.ref) #2
? value.ref.id
: undefined
})
}
if (Array.isArray(value)) {
return value.map(serialize)
}
return value
}
module.exports.serialize = serialize
This serializer distinguishes AST objects, reference objects, arrays, and simple values, converting each reference target into a `refId` while recursively copying only contained structure. section 7.2
References complicate persistence because JavaScript object references are not directly representable in JSON and can create cycles. Give AST objects IDs, serialize each reference as `refId`, and later use that identifier to reconnect the reference to its target when reconstructing the AST. section 7.2
