Property Node

PropertyNode is a facade that we wrap around the underlying data structures of RecordInFlight, allowing for easier, more intuitive interactions when both reading from and writing to the record.

Typically you won’t work with PropertyNode directly, but instead by invoking methods on RecordInFlight that accept Property Path parameters.

If your extension really needs to get into the guts of a property tree and either manipulate values in place or do some kind of heavy lifting, you can get an instance of PropertyNode to work with like so:

// RecordInFlight instance methods
global PropertyNode getOriginalPropertiesRoot();
global PropertyNode getPropertiesRoot();

Much like JSONParse, PropertyNode treats the underlying data as a tree and wraps each node in its own instance of PropertyNode (thus the class name). You can use property path notation to traverse the tree, retrieving specific nodes of interest. From a given node you can traverse further into that subtree.

A Common Use Case

One of the most likely reasons you’ll want to work with PropertyNode directly is when you want to manipulate a value at an arbitrary location in a record’s property tree, and you don’t actually care if value X is three layers down, or two, or has key A or key B.

Maybe the job of your extension in this moment is to capitalize stuff. So you have zero opinion about where to find stuff to capitalize, just that you capitalize whatever you’re told to. The user configures the “when” and “where”, and you handle the “what”.

// handed a property path value from Valence because of a configuration or mapping etc
String configuredPropertyPath = 'offices[*].employees[2,5].title';

PropertyNode root = record.getPropertiesRoot();

for(PropertyNode node : root.collectAllNodes(configuredPropertyPath)) {

// we capitalized the title on the third and sixth employees, and we did that on every office

This is really powerful because you were able to do your job and remain agnostic about the actual data shape of the record.


global Object getValue();

global void setValue(Object newValue);

global List<String> getPath(); // concrete property path to this exact node

global Boolean hasProperty(String concisePropertyPath); // true if a property exists at this path

global Boolean hasProperty(List<String> normalizedPropertyPath); // true if a property exists at this path

global Boolean hasProperty(String concisePropertyPath, Boolean notNull); // true if a property exists and its value is not null

global Boolean hasProperty(List<String> normalizedPropertyPath, Boolean notNull); // true if a property exists and its value is not null

global Object getPropertyValue(String concisePropertyPath);

global Object getPropertyValue(List<String> normalizedPropertyPath);

global List<Object> getPropertyValues(String concisePropertyPath);

global List<Object> getPropertyValues(List<String> normalizedPropertyPath);

global void setPropertyValue(String concisePropertyPath, Object value);

global void setPropertyValue(List<String> normalizedPropertyPath, Object value);

global void setPropertyValues(String concisePropertyPath, List<Object> values);

global void setPropertyValues(List<String> normalizedPropertyPath, List<Object> values);

global List<PropertyNode> collectAllNodes(String concisePropertyPath);

 * Recursively gather all nodes in the tree that the passed property path points to. Resolves indefinite
 * paths into potentially multiple nodes, each of which will be parsed and collected.
 * When this method is finished you will receive a final list of all nodes that matched the original path,
 * and you can go ahead and extract the values to get your real result.
 * @param propertyPath A property path using our property path syntax
 * @return All nodes at any level at or beneath this one in the tree that match the property path
global List<PropertyNode> collectAllNodes(List<String> normalizedPropertyPath);