Skip to content

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:

  1. Trim spaces around each label. Order > Buyer > Email and Order>Buyer>Email are the same path.
  2. 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.
  3. 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.
  4. Take the text of the first <li> beneath the final match. That's the value.
  5. 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:

  1. Take the text between the first and second dot as the key. WooMeta.gift_message gives gift_message.
  2. Find the first key-label node, for example <a>key</a>, whose first <li> text equals the key. The key comparison is case-sensitive.
  3. 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.
  4. 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 &amp; Jerry, is written out as Tom &amp; 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.