An almost-AST is a tree nearly identical to an abstract syntax tree, except each reference object stores its target's identifier instead of a JavaScript object reference. Replacing { ref: targetObject } with { refId: targetObject.id } removes cycles, allowing the tree to be serialized with JSON.stringify. section 7.2
A practical serialization pipeline follows these steps:
- Traverse AST objects depth-first, copying each object's id, concept, and recursively transformed settings. section 7.2
- Convert reference objects into { refId: target.id }, while preserving an unresolved reference as { refId: undefined }. section 7.2
- Serialize the resulting cycle-free structure to JSON and write it to persistent storage. section 7.2
Rental AST
A Rental record can contain an Attribute Reference whose target is the rental price before discount. In the almost-AST, that relation stores the target attribute's ID, while the referenced attribute remains a separate tree object. section 7.2
Deserialization reverses the transformation. It copies the serialized tree, indexes AST objects by ID, records references needing resolution, and then replaces each refId with the corresponding AST object reference. An undefined refId becomes an unresolved { ref: undefined } reference. section 7.3
...
{
id: "<id1>",
concept: "<concept1>",
settings: {
// ...
}
}
...
{
id: "<id2>",
concept: "<concept2>",
settings: {
"<ref-property>": {
"refId": "<id1>"
},
// ...other settings...
}
}
...
Compare the serialized reference shape with the original AST reference shape. section 7.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
Inspect the recursive serialize function that produces a cycle-free almost-AST. section 7.2
