Path syntax reference¶
This page explains how XmlExtractor turns a path in NodesToBeExtracted into
a value. Read it when a path doesn't match, or when you need to target a node
that the README examples don't cover.
The tree the macro reads¶
The macro doesn't read the channel XML directly. It calls the Linnworks API
method Orders/GetOrderXmlJSTree, which returns the XML rendered as a
jsTree-style HTML tree. In that tree, every node label sits in an <a>
element and every value sits in an <li> element beneath it. A simplified
example:
<ul>
<li><a>Order</a>
<ul>
<li><a>OrderNumber</a><ul><li>1001</li></ul></li>
<li><a>Buyer</a>
<ul>
<li><a>Email</a><ul><li>jane@example.com</li></ul></li>
<li><a>Phone</a><ul><li>020 7946 0100</li></ul></li>
</ul>
</li>
</ul>
</li>
</ul>
Linnworks shows this tree for any order in the order book, and Troubleshoot writes it to the macro's log for support to read; see Troubleshooting. Node
names differ by channel.
Plain paths¶
A plain path is one or more node labels separated by >.
Order>Buyer>Email
The macro resolves it like this:
- Trim spaces around each label.
Order > Buyer > EmailandOrder>Buyer>Emailare the same path. - Collect every
<a>anywhere in the document whose text equals the first label. Nodes whose text matches exactly come first, in document order, followed by nodes whose text matches ignoring case. - For each following label, collect the matching
<a>nodes at or below the previous match in the same way. With>, any depth counts, the previous match itself included. With>>, only direct children count. - Take the text of the first
<li>beneath the final match. That's the value. - When a match leads nowhere, because a later label finds nothing below it
or the value is empty, try the next match at that level. Only when every
match is exhausted, log
Did not find value for <path>and move on.
Because step 2 searches the whole document, a one-label path such as Email
finds the first node with that label wherever it sits. Add parent labels
when the same label appears in more than one place, for example a billing
and a shipping phone number.
Shadowed nodes and >>¶
Some channel XML lists a nested copy of a node before the node you want.
The Shopify GraphQL XML puts fulfillmentOrders before the order's own
id, name, and lineItems, and the fulfillment order carries its own
id, a location > id, and a partial lineItems. So id returns a
location id and name returns the billing name.
>> says the next label must be a direct child of the previous match:
| Path | Result on the GraphQL XML |
|---|---|
id |
gid://shopify/Location/..., the first id in the document |
>>id or Order>>id |
gid://shopify/Order/..., the order's own id |
name |
The billing name |
>>name or Order>>name |
#3104, the order name |
lineItems>nodes>sku |
The first line's SKU. The partial copy under fulfillmentOrders has no sku, so it's skipped. |
Order>>lineItems>>nodes>>sku |
The same, with every level pinned |
>> and > mix freely. At the start of a path, >> prefers the shallowest
match; at the same depth a node with the exact label wins over one that
differs only in case. So >>id and >>name reach the
order's own nodes without naming the root, and when an order has no
shallow match at all the path still falls back to a deeper one rather than
failing. Prefer that form over Order>>id: it doesn't depend on what the
root node is called. A single stray > at the start of a path is ignored.
Before 1.1.0, >> had no meaning and a path containing it never resolved.
Rules¶
| Rule | Detail |
|---|---|
| Separator | > between levels, , between paths. >> between levels when the next node must be a direct child. |
| Case | Exact case wins. email matches an email node when there is one, and otherwise the first Email or EMAIL. |
| Whitespace | Spaces around > and , are removed. Spaces inside a label are kept. |
| Depth | Up to 8 levels. A ninth level logs Failed processing Xml extraction only 8 levels of indentation are supported and skips that path. |
| Duplicates | A path listed twice, with or without different spacing, is written once. |
| Empty entries | A blank entry, such as the middle of Phone, , Email, logs Trying to extract and empty property and is skipped. A bare trailing comma produces no entry and no warning. |
| Repeated nodes | When a node repeats, for example one per order line, the first occurrence in document order whose value isn't empty, and under which the rest of the path resolves, is used. There's no index syntax. |
| Consecutive labels | With >, a level may match the same node as the previous one, so a doubled level such as nodes>nodes>id behaves like nodes>id. Use >> when the repeated label must be nested: nodes>>nodes>>id. |
| Non-leaf nodes | Pointing a path at a node that has children returns the concatenated text of its first child, label included. Point paths at leaf nodes. |
| Value text | The value is used as-is. It isn't trimmed. |
Key/value prefixes¶
Many channels send lists of key/value pairs rather than named nodes.
WooCommerce meta_data, Shopify note_attributes, and Etsy variations all
look like this in the tree:
<li><a>meta_data</a>
<ul>
<li><a>item</a>
<ul>
<li><a>key</a><ul><li>gift_message</li></ul></li>
<li><a>value</a><ul><li>Happy birthday</li></ul></li>
</ul>
</li>
</ul>
</li>
A plain path can't target "the value next to the key that says
gift_message". The four prefixes do exactly that.
| Prefix | Key label | Value label | Label match | Typical source |
|---|---|---|---|---|
WooMeta.<key> |
key |
value |
Case-insensitive | WooCommerce meta_data |
Metafield.<key> |
key |
value |
Case-insensitive | Shopify metafields, any other key/value list |
VariationSelectedOption.<key> |
formatted_name |
formatted_value |
Case-sensitive | Etsy variations and personalisation |
NoteAttribute.<key> |
name, then key |
value |
Case-insensitive | Shopify note_attributes and line item properties (REST), customAttributes (GraphQL) |
WooMeta. and Metafield. run the same lookup. Use whichever reads better
for your channel. NoteAttribute. tries a name/value pair first and a
key/value pair when none matches, so one configuration reads both the
REST and the GraphQL Shopify XML.
The macro resolves a prefix path like this:
- Take the text between the first and second dot as the key.
WooMeta.gift_messagegivesgift_message. - Find the first key-label node, for example
<a>key</a>, whose first<li>text equals the key. The key comparison is case-sensitive. - Find the value-label node, for example
<a>value</a>, among the key node's siblings. A value elsewhere in the tree doesn't count. - Take the text of the first
<li>beneath it.
Rules¶
| Rule | Detail |
|---|---|
| One level only | A prefix path must stand alone. WooMeta.gift_message>Something runs the lookup and then looks for Something beneath the value, which fails. A prefix in any level other than the first, such as Order>WooMeta.x, is not found. |
| Prefix match | The prefix is recognised case-insensitively, so woometa.gift_message works. The {{key}} placeholder keeps what you typed. |
| Keys with dots | Only the text between the first and second dot is used. WooMeta.a.b looks for the key a. |
| Keys with spaces | Allowed. NoteAttribute.Delivery instructions looks for the name Delivery instructions. |
| Repeated keys | When the same key appears more than once, for example the same meta key on every order line, only the first pair in document order is found. |
| No match | Nothing is written for that path. The macro's log, which support can read, records Did not find value for <path>. |
| Empty label nodes | Pairs whose key node is empty, such as <name />, are ignored. |
| Empty values | A pair whose value is empty is skipped in favour of the next pair with the same key. |
The Format template¶
Format is applied to every extracted value before it's written.
| Placeholder | Replaced with |
|---|---|
{{value}} |
The extracted value. |
{{key}} |
The path as you typed it, with spaces around > removed. |
Any other text in Format is kept verbatim. {{value}} is replaced before
{{key}}.
Where the value ends up¶
| Target | Name or text written |
|---|---|
ExtendedProperty |
Property name is the path as typed, for example WooMeta.gift_message. Property value is the formatted text. Type is String. |
InternalNote, ProcessingInternalNote, Note, ProcessingNote |
Note text is the formatted text. |
Extended properties are overwritten on every run, so the property always reflects the latest extraction. Notes are only added when no note of the same kind already has the same text, comparing case-insensitively, so running the macro twice doesn't duplicate them.
Characters that change on the way through¶
Before parsing, the macro escapes & and the literal tags <br> and <BR>
so that the tree parses as XML. Other forms such as <br/> aren't escaped
and drop out of the value. As a side effect, a value that the channel already sent
encoded, such as Tom & Jerry, is written out as Tom & Jerry
rather than Tom & Jerry. If a value must contain a plain ampersand, fix it
with a Rules Engine action or a template expression after extraction.